diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 249a65e228245..d0740fe6049d3 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -35,6 +35,7 @@ use super::{ format_interp_error, }; use crate::enter_trace_span; +use crate::interpret::ensure_monomorphic_enough; // for the validation errors #[rustfmt::skip] @@ -734,11 +735,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) } // Do not allow references to uninhabited types. - if place.layout.is_uninhabited() { + if !place.layout.ty.is_opsem_inhabited(*self.ecx.tcx, self.ecx.typing_env) { let ty = place.layout.ty; throw_validation_failure!( self.path, - format!("encountered a {ptr_kind} pointing to uninhabited type {ty}") + format!("encountered a {ptr_kind} pointing to uninhabited type `{ty}`") ) } @@ -1568,8 +1569,9 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, } // Assert that we checked everything there is to check about this type. + // `is_opsem_inhabited` implies that the layout is inhabited (checked by layout invariants). assert!( - !val.layout.is_uninhabited(), + val.layout.ty.is_opsem_inhabited(*self.ecx.tcx, self.ecx.typing_env), "a value of type `{}` passed validation but that type is uninhabited", val.layout.ty ); @@ -1627,6 +1629,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx> { trace!("validate_operand_internal: {:?}, {:?}", *val, val.layout.ty); + // We can't check validity if there are any generics left. + ensure_monomorphic_enough(*self.tcx, val.layout.ty)?; + // Run the visitor. self.run_for_validation_mut(|ecx| { let reset_padding = reset_provenance_and_padding && { diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 5add2cf09b7b8..4a474d66ca6b1 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2145,6 +2145,11 @@ rustc_queries! { desc { "computing the uninhabited predicate of `{}`", key } } + /// Do not call this query directly: invoke `Ty::is_opsem_inhabited` instead. + query is_opsem_inhabited_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is inhabited on the opsem level", env.value } + } + query crate_dep_kind(_: CrateNum) -> CrateDepKind { eval_always desc { "fetching what a dependency looks like" } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 6aff85e77da5a..5a060a7d20ffd 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -43,6 +43,9 @@ //! This code should only compile in modules where the uninhabitedness of `Foo` //! is visible. +use std::assert_matches; + +use rustc_data_structures::fx::FxHashSet; use rustc_type_ir::TyKind::*; use tracing::instrument; @@ -54,7 +57,12 @@ pub mod inhabited_predicate; pub use inhabited_predicate::InhabitedPredicate; pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { inhabited_predicate_adt, inhabited_predicate_type, ..*providers }; + *providers = Providers { + inhabited_predicate_adt, + inhabited_predicate_type, + is_opsem_inhabited_raw, + ..*providers + }; } /// Returns an `InhabitedPredicate` that is generic over type parameters and @@ -186,7 +194,10 @@ impl<'tcx> Ty<'tcx> { self.inhabited_predicate(tcx).apply(tcx, typing_env, module) } - /// Returns true if the type is uninhabited without regard to visibility + /// Returns true if the type is uninhabited without regard to visibility. + /// + /// This is still conservative; for instance, a `#[non_exhaustive]` enum *in another crate* + /// is always considered inhabited. pub fn is_privately_uninhabited( self, tcx: TyCtxt<'tcx>, @@ -194,6 +205,16 @@ impl<'tcx> Ty<'tcx> { ) -> bool { !self.inhabited_predicate(tcx).apply_ignore_module(tcx, typing_env) } + + /// Returns whether `self` is considered inhabited on the opsem level, i.e., its validity + /// invariant might be satisfiable. `self` is expected to be monomorphic and normalized. + pub fn is_opsem_inhabited(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + // Handle simple cases directly, use the query with its cache for the rest. + is_opsem_inhabited_recursor(self, tcx, &mut (), /* stop_at_ref */ false, &|ty, _, _| { + // ADT handler: stop recursing, invoke the query. + tcx.is_opsem_inhabited_raw(typing_env.as_query_input(ty)) + }) + } } /// N.B. this query should only be called through `Ty::inhabited_predicate` @@ -216,3 +237,159 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP _ => bug!("unexpected TyKind, use `Ty::inhabited_predicate`"), } } + +/// Recurse over a type to determine whether it is inhabited on the opsem level. +/// Key constraints are: +/// - if a type's validity invariant is satisfiable, it must be opsem-inhabited. +/// - if a type's layout is marked uninhabited, it must be opsem-uninhabited. +/// +/// Beyond that, the value returned by this function is not a stable guarantee. +/// +/// When we encounter an ADT, we call `adt_handler`, giving it as its last argument a closure that +/// it can invoke to continue the recursion. This lets us share the logic for "simple" cases +/// (i.e., everything except for ADTs) between `Ty::is_opsem_inhabited` and the query. +/// +/// `seen` is used to detect infinite recursion: the set contains all ADTs that we encountered +/// on our path to the current type. +/// If `stop_at_ref` is true, we stop recursing at the next reference we encounter. +fn is_opsem_inhabited_recursor<'tcx, SEEN>( + ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + seen: &mut SEEN, + stop_at_ref: bool, + adt_handler: &impl Fn( + Ty<'tcx>, + &mut SEEN, + &dyn Fn(Ty<'tcx>, &mut SEEN, /* stop_at_ref */ bool) -> bool, + ) -> bool, +) -> bool { + match *ty.kind() { + // Trivially (un)inhabited types + ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Foreign(..) + | ty::RawPtr(..) + | ty::FnPtr(..) + | ty::FnDef(..) => true, + ty::Dynamic(..) => true, // We can't reason about traits, assume they are inhabited + ty::Slice(..) => true, // Slices can always be empty + ty::Never => false, + + // Types where we recurse + ty::Ref(_, pointee, _) => { + if stop_at_ref { + // Bailing out here is safe as the layout code always considers references + // inhabited, so the implication ("layout uninhabited => opsem uninhabited") + // is upheld. + return true; + } + is_opsem_inhabited_recursor(pointee, tcx, seen, stop_at_ref, adt_handler) + } + ty::Tuple(tys) => tys + .iter() + .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)), + ty::Array(elem, len) => { + len.try_to_target_usize(tcx).unwrap() == 0 + || is_opsem_inhabited_recursor(elem, tcx, seen, stop_at_ref, adt_handler) + } + ty::Pat(inner, _pat) => { + is_opsem_inhabited_recursor(inner, tcx, seen, stop_at_ref, adt_handler) + } + ty::Closure(_def, args) => { + let args = args.as_closure(); + args.upvar_tys() + .iter() + .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)) + } + ty::Coroutine(_def, args) => { + let args = args.as_coroutine(); + args.upvar_tys() + .iter() + .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)) + } + ty::CoroutineClosure(_def, args) => { + let args = args.as_coroutine_closure(); + args.upvar_tys() + .iter() + .all(|ty| is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler)) + } + ty::UnsafeBinder(base) => { + let base = tcx.instantiate_bound_regions_with_erased((*base).into()); + is_opsem_inhabited_recursor(base, tcx, seen, stop_at_ref, adt_handler) + } + ty::Adt(..) => { + // ADTs need a special handler to avoid infinite recursion. That handler is meant to + // call back into the recursor. Ideally it'd just call `is_opsem_inhabited_recursor` but + // then it would have to pass itself as the adt_handler argument which is not possible + // in Rust... so we provide the handler with a callback that it can use to continue the + // recursion with the same `adt_handler`. + adt_handler(ty, seen, &|ty, seen, stop_at_ref| { + is_opsem_inhabited_recursor(ty, tcx, seen, stop_at_ref, adt_handler) + }) + } + + ty::Error(_) + | ty::Infer(..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Param(..) + | ty::Alias(..) + | ty::CoroutineWitness(..) => { + bug!("non-normalized type in `is_opsem_uninhabited`: `{ty}`") + } + } +} + +fn is_opsem_inhabited_raw<'tcx>( + tcx: TyCtxt<'tcx>, + env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, +) -> bool { + let (ty, typing_env) = (env.value, env.typing_env); + assert_matches!( + ty.kind(), + ty::Adt(..), + "the query should only be invoked by `Ty::is_opsem_inhabited`" + ); + + is_opsem_inhabited_recursor( + ty, + tcx, + &mut FxHashSet::::default(), + /* stop_at_ref */ false, + &|ty, seen, rec| { + let ty::Adt(adt_def, adt_args) = *ty.kind() else { + unreachable! {} + }; + if adt_def.is_union() { + // Unions are always inhabited. + return true; + } + + let new_adt = seen.insert(adt_def.did()); + // If we have seen this ADT before, stop at the next reference to avoid infinite + // recursion. We can't stop here since we have to ensure that "layout inhabited" + // implies "opsem inhabited". + let stop_at_ref = !new_adt; + + // We are inhabited if in some variant all fields are inhabited. + let inhabited = adt_def.variants().iter().any(|variant| { + variant.fields.iter().all(|field| { + let ty = field.ty(tcx, adt_args); + let ty = tcx.normalize_erasing_regions(typing_env, ty); + rec(ty, seen, stop_at_ref) + }) + }); + + // Remove the type again so that we allow it to appear on other branches. + if new_adt { + seen.remove(&adt_def.did()); + } + + inhabited + }, + ) +} diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index 8c07a7ec3d2de..5ec90ac897052 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -1,6 +1,7 @@ use std::assert_matches; use rustc_abi::{BackendRepr, FieldsShape, Scalar, Size, TagEncoding, Variants}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, TyAndLayout}; use rustc_middle::{bug, ty}; @@ -34,6 +35,15 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou layout.ty ); } + // ABI uninhabitedness should imply opsem uninhabitedness. However, we can only check that if + // the type is really monomorphic (while we can compute a layout for some generic types). + if layout.is_uninhabited() && !layout.ty.has_param() { + assert!( + !layout.ty.is_opsem_inhabited(tcx, cx.typing_env), + "{:?} is ABI-uninhabited but not opsem-uninhabited?", + layout.ty + ); + } /// Yields non-ZST fields of the type fn non_zst_fields<'tcx, 'a>( diff --git a/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.rs b/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.rs index 2e6be8b971c64..25e21375cd746 100644 --- a/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.rs +++ b/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.rs @@ -3,7 +3,7 @@ use std::mem::{forget, transmute}; fn main() { unsafe { - let x: Box = transmute(&mut 42); //~ERROR: encountered a box pointing to uninhabited type ! + let x: Box = transmute(&mut 42); //~ERROR: encountered a box pointing to uninhabited type `!` forget(x); } } diff --git a/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.stderr b/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.stderr index 645a9789207a0..889596092de60 100644 --- a/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.stderr +++ b/src/tools/miri/tests/fail/validity/ref_to_uninhabited1.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: constructing invalid value of type std::boxed::Box: encountered a box pointing to uninhabited type ! +error: Undefined Behavior: constructing invalid value of type std::boxed::Box: encountered a box pointing to uninhabited type `!` --> tests/fail/validity/ref_to_uninhabited1.rs:LL:CC | LL | let x: Box = transmute(&mut 42); diff --git a/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.rs b/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.rs index 8934a06b5d73a..3ac987309f783 100644 --- a/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.rs +++ b/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.rs @@ -4,6 +4,6 @@ enum Void {} fn main() { unsafe { - let _x: &(i32, Void) = transmute(&42); //~ERROR: encountered a reference pointing to uninhabited type (i32, Void) + let _x: &&(i32, Void) = transmute(&&42); //~ERROR: encountered a reference pointing to uninhabited type `&(i32, Void)` } } diff --git a/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.stderr b/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.stderr index 37b265a771bc1..cba028eeca3fe 100644 --- a/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.stderr +++ b/src/tools/miri/tests/fail/validity/ref_to_uninhabited2.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: constructing invalid value of type &(i32, Void): encountered a reference pointing to uninhabited type (i32, Void) +error: Undefined Behavior: constructing invalid value of type &&(i32, Void): encountered a reference pointing to uninhabited type `&(i32, Void)` --> tests/fail/validity/ref_to_uninhabited2.rs:LL:CC | -LL | let _x: &(i32, Void) = transmute(&42); - | ^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | let _x: &&(i32, Void) = transmute(&&42); + | ^^^^^^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr index 4493067e9a8bb..a6045a686b397 100644 --- a/tests/ui/consts/const-eval/raw-bytes.32bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.32bit.stderr @@ -218,7 +218,7 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; ╾ALLOC_ID╼ │ ╾──╼ } -error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type Bar +error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type `Bar` --> $DIR/raw-bytes.rs:110:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; @@ -458,7 +458,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──╼╾──╼ } -error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type [!; 1] +error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type `[!; 1]` --> $DIR/raw-bytes.rs:188:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; diff --git a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr index 07deb955d24e7..095613b8b2f00 100644 --- a/tests/ui/consts/const-eval/raw-bytes.64bit.stderr +++ b/tests/ui/consts/const-eval/raw-bytes.64bit.stderr @@ -218,7 +218,7 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) }; ╾ALLOC_ID╼ │ ╾──────╼ } -error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type Bar +error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type `Bar` --> $DIR/raw-bytes.rs:110:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; @@ -458,7 +458,7 @@ LL | const RAW_TRAIT_OBJ_VTABLE_INVALID: *const dyn Trait = unsafe { mem::transm ╾ALLOC_ID╼ ╾ALLOC_ID╼ │ ╾──────╼╾──────╼ } -error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type [!; 1] +error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type `[!; 1]` --> $DIR/raw-bytes.rs:188:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) }; diff --git a/tests/ui/consts/const-eval/ub-uninhabit.rs b/tests/ui/consts/const-eval/ub-uninhabit.rs index 188ad768a0e98..7ec668855b092 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.rs +++ b/tests/ui/consts/const-eval/ub-uninhabit.rs @@ -18,19 +18,35 @@ union MaybeUninit { } const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; -//~^ ERROR constructing invalid value +//~^ ERROR value of zero-variant enum `Bar` const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; -//~^ ERROR constructing invalid value +//~^ ERROR reference pointing to uninhabited type `Bar` const BAD_BAD_ARRAY: [Bar; 1] = unsafe { MaybeUninit { uninit: () }.init }; -//~^ ERROR constructing invalid value +//~^ ERROR value of zero-variant enum `Bar` const READ_NEVER: () = unsafe { let mem = [0u32; 8]; let ptr = mem.as_ptr().cast::(); let _val = intrinsics::read_via_copy(ptr); - //~^ ERROR constructing invalid value + //~^ ERROR value of the never type }; +const BAD_NESTED_REF: &&! = unsafe { mem::transmute(&&0) }; +//~^ ERROR reference pointing to uninhabited type `&!` + +const BAD_NESTED_UNSIZED_REF: &&(!, [i32]) = unsafe { mem::transmute(&(&[0] as &[i32])) }; +//~^ ERROR reference pointing to uninhabited type `&(!, [i32])` + +const BAD_UNINHABITED_SLICE: &[!] = unsafe { mem::transmute(&[()] as &[()]) }; +//~^ ERROR value of the never type + +// This is an interesting type since it looks somewhat recursive even though it is not. +struct Wrap(T); +const WRAPPED_TWICE: Wrap> = unsafe { mem::transmute(()) }; +//~^ ERROR value of the never type +const WRAPPED_TWICE_REF: &Wrap> = unsafe { mem::transmute(&()) }; +//~^ ERROR reference pointing to uninhabited type `Wrap>` + fn main() {} diff --git a/tests/ui/consts/const-eval/ub-uninhabit.stderr b/tests/ui/consts/const-eval/ub-uninhabit.stderr index 64da16121b828..59c7e95adc7f7 100644 --- a/tests/ui/consts/const-eval/ub-uninhabit.stderr +++ b/tests/ui/consts/const-eval/ub-uninhabit.stderr @@ -4,7 +4,7 @@ error[E0080]: constructing invalid value of type Bar: encountered a value of zer LL | const BAD_BAD_BAD: Bar = unsafe { MaybeUninit { uninit: () }.init }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `BAD_BAD_BAD` failed here -error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type Bar +error[E0080]: constructing invalid value of type &Bar: encountered a reference pointing to uninhabited type `Bar` --> $DIR/ub-uninhabit.rs:23:1 | LL | const BAD_BAD_REF: &Bar = unsafe { mem::transmute(1usize) }; @@ -27,6 +27,56 @@ error[E0080]: constructing invalid value of type !: encountered a value of the n LL | let _val = intrinsics::read_via_copy(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `READ_NEVER` failed here -error: aborting due to 4 previous errors +error[E0080]: constructing invalid value of type &&!: encountered a reference pointing to uninhabited type `&!` + --> $DIR/ub-uninhabit.rs:36:1 + | +LL | const BAD_NESTED_REF: &&! = unsafe { mem::transmute(&&0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error[E0080]: constructing invalid value of type &&(!, [i32]): encountered a reference pointing to uninhabited type `&(!, [i32])` + --> $DIR/ub-uninhabit.rs:39:1 + | +LL | const BAD_NESTED_UNSIZED_REF: &&(!, [i32]) = unsafe { mem::transmute(&(&[0] as &[i32])) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error[E0080]: constructing invalid value of type &[!]: at .[0], encountered a value of the never type `!` + --> $DIR/ub-uninhabit.rs:42:1 + | +LL | const BAD_UNINHABITED_SLICE: &[!] = unsafe { mem::transmute(&[()] as &[()]) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error[E0080]: constructing invalid value of type Wrap>: at .0.0, encountered a value of the never type `!` + --> $DIR/ub-uninhabit.rs:47:47 + | +LL | const WRAPPED_TWICE: Wrap> = unsafe { mem::transmute(()) }; + | ^^^^^^^^^^^^^^^^^^ evaluation of `WRAPPED_TWICE` failed here + +error[E0080]: constructing invalid value of type &Wrap>: encountered a reference pointing to uninhabited type `Wrap>` + --> $DIR/ub-uninhabit.rs:49:1 + | +LL | const WRAPPED_TWICE_REF: &Wrap> = unsafe { mem::transmute(&()) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/cycle-static-promoted.rs b/tests/ui/consts/cycle-static-promoted.rs deleted file mode 100644 index d648d04861189..0000000000000 --- a/tests/ui/consts/cycle-static-promoted.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ check-pass - -struct Value { - values: &'static [&'static Value], -} - -// This `static` recursively points to itself through a promoted (the slice). -static VALUE: Value = Value { - values: &[&VALUE], -}; - -fn main() {} diff --git a/tests/ui/consts/recursive-type.rs b/tests/ui/consts/recursive-type.rs new file mode 100644 index 0000000000000..b0207733a2f3d --- /dev/null +++ b/tests/ui/consts/recursive-type.rs @@ -0,0 +1,18 @@ +//@ check-pass +use std::marker::PhantomData; + +struct Value { + values: &'static [&'static Value], +} + +// This `static` recursively points to itself through a promoted (the slice). +static VALUE: Value = Value { + values: &[&VALUE], +}; + +// If we just unfold this type going down the first variant of every enum, we'll never stop; we'll +// never even encounter the same type a second time. +struct S(&'static S<(T, T)>, PhantomData); +const C: &Result, ()> = &Err(()); + +fn main() {} diff --git a/tests/ui/consts/validate_never_arrays.stderr b/tests/ui/consts/validate_never_arrays.stderr index 517b632bdd7be..a006c19587f50 100644 --- a/tests/ui/consts/validate_never_arrays.stderr +++ b/tests/ui/consts/validate_never_arrays.stderr @@ -1,4 +1,4 @@ -error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type [!; 1] +error[E0080]: constructing invalid value of type &[!; 1]: encountered a reference pointing to uninhabited type `[!; 1]` --> $DIR/validate_never_arrays.rs:6:1 | LL | const _: &[!; 1] = unsafe { &*(1_usize as *const [!; 1]) };