diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index d6dc1a62f4d0d..76bd9a8283681 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -97,11 +97,27 @@ pub(crate) fn const_to_valtree_inner<'tcx>( Ok(ty::ValTree::Leaf(val.assert_int())) } - // Raw pointers are not allowed in type level constants, as we cannot properly test them for - // equality at compile-time (see `ptr_guaranteed_cmp`). + ty::RawPtr(_) => { + // Not all raw pointers are allowed, as we cannot properly test them for + // equality at compile-time (see `ptr_guaranteed_cmp`). + // However we allow those that are just integers in disguise. + // (We could allow wide raw pointers where both sides are integers in the future, + // but for now we reject them.) + let Ok(val) = ecx.read_scalar(place) else { + return Err(ValTreeCreationError::Other); + }; + // We are in the CTFE machine, so ptr-to-int casts will fail. + // This can only be `Ok` if `val` already is an integer. + let Ok(val) = val.try_to_int() else { + return Err(ValTreeCreationError::Other); + }; + // It's just a ScalarInt! + Ok(ty::ValTree::Leaf(val)) + } + // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to // agree with runtime equality tests. - ty::FnPtr(_) | ty::RawPtr(_) => Err(ValTreeCreationError::NonSupportedType), + ty::FnPtr(_) => Err(ValTreeCreationError::NonSupportedType), ty::Ref(_, _, _) => { let Ok(derefd_place)= ecx.deref_pointer(place) else { @@ -222,12 +238,14 @@ pub fn valtree_to_const_value<'tcx>( assert!(valtree.unwrap_branch().is_empty()); mir::ConstValue::ZeroSized } - ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree { - ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), - ty::ValTree::Branch(_) => bug!( - "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf" - ), - }, + ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_) => { + match valtree { + ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), + ty::ValTree::Branch(_) => bug!( + "ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf" + ), + } + } ty::Ref(_, inner_ty, _) => { let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No); let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty); @@ -281,7 +299,6 @@ pub fn valtree_to_const_value<'tcx>( | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::FnPtr(_) - | ty::RawPtr(_) | ty::Str | ty::Slice(_) | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()), diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 918e484b9f44d..f033504c52383 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2217,13 +2217,14 @@ declare_lint! { /// /// ### Explanation /// - /// Previous versions of Rust allowed function pointers and wide raw pointers in patterns. + /// Previous versions of Rust allowed function pointers and all raw pointers in patterns. /// While these work in many cases as expected by users, it is possible that due to /// optimizations pointers are "not equal to themselves" or pointers to different functions /// compare as equal during runtime. This is because LLVM optimizations can deduplicate /// functions if their bodies are the same, thus also making pointers to these functions point /// to the same location. Additionally functions may get duplicated if they are instantiated - /// in different crates and not deduplicated again via LTO. + /// in different crates and not deduplicated again via LTO. Pointer identity for memory + /// created by `const` is similarly unreliable. pub POINTER_STRUCTURAL_MATCH, Allow, "pointers are not structural-match", diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 32711c23dc4e4..dfd0bc238c198 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -242,7 +242,7 @@ mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpo mir_build_pattern_not_covered = refutable pattern in {$origin} .pattern_ty = the matched value is of type `{$pattern_ty}` -mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index fc03f7891a86f..ad386b129ce80 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -123,6 +123,8 @@ impl<'tcx> ConstToPat<'tcx> { }); debug!(?check_body_for_struct_match_violation, ?mir_structural_match_violation); + let have_valtree = + matches!(cv, mir::Const::Ty(c) if matches!(c.kind(), ty::ConstKind::Value(_))); let inlined_const_as_pat = match cv { mir::Const::Ty(c) => match c.kind() { ty::ConstKind::Param(_) @@ -238,7 +240,9 @@ impl<'tcx> ConstToPat<'tcx> { } } else if !self.saw_const_match_lint.get() { match cv.ty().kind() { - ty::RawPtr(pointee) if pointee.ty.is_sized(self.tcx(), self.param_env) => {} + ty::RawPtr(..) if have_valtree => { + // This is a good raw pointer, it was accepted by valtree construction. + } ty::FnPtr(..) | ty::RawPtr(..) => { self.tcx().emit_spanned_lint( lint::builtin::POINTER_STRUCTURAL_MATCH, @@ -389,11 +393,19 @@ impl<'tcx> ConstToPat<'tcx> { subpatterns: self .field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?, }, - ty::Adt(def, args) => PatKind::Leaf { - subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip( - def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), args)), - ))?, - }, + ty::Adt(def, args) => { + assert!(!def.is_union()); // Valtree construction would never succeed for unions. + PatKind::Leaf { + subpatterns: self.field_pats( + cv.unwrap_branch().iter().copied().zip( + def.non_enum_variant() + .fields + .iter() + .map(|field| field.ty(self.tcx(), args)), + ), + )?, + } + } ty::Slice(elem_ty) => PatKind::Slice { prefix: cv .unwrap_branch() @@ -480,10 +492,15 @@ impl<'tcx> ConstToPat<'tcx> { } } }, - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => { + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { + // The raw pointers we see here have been "vetted" by valtree construction to be + // just integers, so we simply allow them. PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } } - ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(), + ty::FnPtr(..) => { + // Valtree construction would never succeed for these, so this is unreachable. + unreachable!() + } _ => { let err = InvalidPattern { span, non_sm_ty: ty }; let e = tcx.sess.emit_err(err); diff --git a/tests/ui/consts/issue-34784.rs b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs similarity index 50% rename from tests/ui/consts/issue-34784.rs rename to tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs index 98d943470a7f9..bbb98a162c339 100644 --- a/tests/ui/consts/issue-34784.rs +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.rs @@ -1,12 +1,11 @@ -// run-pass - -#![warn(pointer_structural_match)] +#![deny(pointer_structural_match)] #![allow(dead_code)] const C: *const u8 = &0; fn foo(x: *const u8) { match x { - C => {} + C => {} //~ERROR: behave unpredictably + //~| previously accepted _ => {} } } @@ -15,7 +14,8 @@ const D: *const [u8; 4] = b"abcd"; fn main() { match D { - D => {} + D => {} //~ERROR: behave unpredictably + //~| previously accepted _ => {} } } diff --git a/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr new file mode 100644 index 0000000000000..af7caaf5b742f --- /dev/null +++ b/tests/ui/consts/const_in_pattern/issue-34784-match-on-non-int-raw-ptr.stderr @@ -0,0 +1,25 @@ +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:7:9 + | +LL | C => {} + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 +note: the lint level is defined here + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:1:9 + | +LL | #![deny(pointer_structural_match)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/issue-34784-match-on-non-int-raw-ptr.rs:17:9 + | +LL | D => {} + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #62411 + +error: aborting due to 2 previous errors + diff --git a/tests/ui/consts/const_in_pattern/issue-44333.rs b/tests/ui/consts/const_in_pattern/issue-44333.rs index 96e8795e52d56..aaf1edb6fe6e4 100644 --- a/tests/ui/consts/const_in_pattern/issue-44333.rs +++ b/tests/ui/consts/const_in_pattern/issue-44333.rs @@ -16,9 +16,9 @@ const BAR: Func = bar; fn main() { match test(std::env::consts::ARCH.len()) { - FOO => println!("foo"), //~ WARN pointers in patterns behave unpredictably + FOO => println!("foo"), //~ WARN behave unpredictably //~^ WARN will become a hard error - BAR => println!("bar"), //~ WARN pointers in patterns behave unpredictably + BAR => println!("bar"), //~ WARN behave unpredictably //~^ WARN will become a hard error _ => unreachable!(), } diff --git a/tests/ui/consts/const_in_pattern/issue-44333.stderr b/tests/ui/consts/const_in_pattern/issue-44333.stderr index 731ef509ccad4..441aeecbc6d95 100644 --- a/tests/ui/consts/const_in_pattern/issue-44333.stderr +++ b/tests/ui/consts/const_in_pattern/issue-44333.stderr @@ -1,4 +1,4 @@ -warning: function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. --> $DIR/issue-44333.rs:19:9 | LL | FOO => println!("foo"), @@ -12,7 +12,7 @@ note: the lint level is defined here LL | #![warn(pointer_structural_match)] | ^^^^^^^^^^^^^^^^^^^^^^^^ -warning: function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. --> $DIR/issue-44333.rs:21:9 | LL | BAR => println!("bar"), diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs index 567685950e9e4..767d9754299fe 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.rs @@ -33,7 +33,7 @@ fn main() { let s = B(my_fn); match s { B(TEST) => println!("matched"), - //~^ WARN pointers in patterns behave unpredictably + //~^ WARN behave unpredictably //~| WARN this was previously accepted by the compiler but is being phased out _ => panic!("didn't match") }; diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr index d6afc0255ec4c..1aa627a803501 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-63479-match-fnptr.stderr @@ -1,4 +1,4 @@ -warning: function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. --> $DIR/issue-63479-match-fnptr.rs:35:7 | LL | B(TEST) => println!("matched"),