diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2ff7caef732a7..c9803d87e1319 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -238,12 +238,18 @@ pub fn check_crate(tcx: TyCtxt<'_>) { check::maybe_check_static_with_link_section(tcx, item_def_id); } DefKind::Const if !tcx.generics_of(item_def_id).own_requires_monomorphization() => { - // FIXME(generic_const_items): Passing empty instead of identity args is fishy but - // seems to be fine for now. Revisit this! - let instance = ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); - let cid = GlobalId { instance, promoted: None }; - let typing_env = ty::TypingEnv::fully_monomorphized(); - tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); + let predicates = tcx.predicates_of(item_def_id); + let predicates = predicates.instantiate_identity(tcx).predicates; + + if !traits::impossible_predicates(tcx, predicates) { + // FIXME(generic_const_items): Passing empty instead of identity args is fishy but + // seems to be fine for now. Revisit this! + let instance = + ty::Instance::new_raw(item_def_id.into(), ty::GenericArgs::empty()); + let cid = GlobalId { instance, promoted: None }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + tcx.ensure_ok().eval_to_const_value_raw(typing_env.as_query_input(cid)); + } } _ => (), } diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index e25f35c59c286..1b3ad188937b3 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -12,9 +12,11 @@ use crate::ty::{self, ConstToValTreeResult, GenericArgs, TyCtxt, TypeVisitableEx use crate::{error, mir}; impl<'tcx> TyCtxt<'tcx> { - /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts - /// that can't take any generic arguments like const items or enum discriminants. If a - /// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned. + /// Evaluates a constant without providing any generic parameters. + /// + /// This is useful to evaluate consts that can't take any generic arguments like enum + /// discriminants. If a non-region generic parameter is used within the constant + /// `ErrorHandled::TooGeneric` will be returned. #[instrument(skip(self), level = "debug")] pub fn const_eval_poly(self, def_id: DefId) -> EvalToConstValueResult<'tcx> { // In some situations def_id will have generic parameters within scope, but they aren't allowed @@ -28,9 +30,11 @@ impl<'tcx> TyCtxt<'tcx> { self.const_eval_global_id(typing_env, cid, DUMMY_SP) } - /// Evaluates a constant without providing any generic parameters. This is useful to evaluate consts - /// that can't take any generic arguments like const items or enum discriminants. If a - /// generic parameter is used within the constant `ErrorHandled::TooGeneric` will be returned. + /// Evaluates a constant without providing any generic parameters. + /// + /// This is useful to evaluate consts that can't take any generic arguments like enum + /// discriminants. If a non-region generic parameter is used within the constant + /// `ErrorHandled::TooGeneric` will be returned. #[instrument(skip(self), level = "debug")] pub fn const_eval_poly_to_alloc(self, def_id: DefId) -> EvalToAllocationRawResult<'tcx> { // In some situations def_id will have generic parameters within scope, but they aren't allowed diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 91e371d697dc6..ee498e8ca4c3e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1424,74 +1424,54 @@ struct RootCollector<'a, 'tcx> { entry_fn: Option<(DefId, EntryFnType)>, } -impl<'v> RootCollector<'_, 'v> { +impl<'tcx> RootCollector<'_, 'tcx> { fn process_item(&mut self, id: hir::ItemId) { - match self.tcx.def_kind(id.owner_id) { + let tcx = self.tcx; + let def_id = id.owner_id.to_def_id(); + + match tcx.def_kind(def_id) { DefKind::Enum | DefKind::Struct | DefKind::Union => { if self.strategy == MonoItemCollectionStrategy::Eager - && !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + && !tcx.generics_of(def_id).own_requires_monomorphization() + && let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| { + expect_and_erase_regions(tcx, param) + }) + && !tcx.instantiate_and_check_impossible_predicates((def_id, args)) { - debug!("RootCollector: ADT drop-glue for `{id:?}`",); - let id_args = - ty::GenericArgs::for_item(self.tcx, id.owner_id.to_def_id(), |param, _| { - match param.kind { - GenericParamDefKind::Lifetime => { - self.tcx.lifetimes.re_erased.into() - } - GenericParamDefKind::Type { .. } - | GenericParamDefKind::Const { .. } => { - unreachable!( - "`own_requires_monomorphization` check means that \ - we should have no type/const params" - ) - } - } - }); - - // This type is impossible to instantiate, so we should not try to - // generate a `drop_in_place` instance for it. - if self.tcx.instantiate_and_check_impossible_predicates(( - id.owner_id.to_def_id(), - id_args, - )) { - return; - } + let ty = tcx.type_of(def_id).instantiate(tcx, args); + debug_assert!(!ty.has_non_region_param()); - let ty = - self.tcx.type_of(id.owner_id.to_def_id()).instantiate(self.tcx, id_args); - assert!(!ty.has_non_region_param()); - visit_drop_use(self.tcx, ty, true, DUMMY_SP, self.output); + debug!("RootCollector: ADT drop-glue for `{id:?}`"); + visit_drop_use(tcx, ty, true, DUMMY_SP, self.output); } } DefKind::GlobalAsm => { - debug!( - "RootCollector: ItemKind::GlobalAsm({})", - self.tcx.def_path_str(id.owner_id) - ); + debug!("RootCollector: ItemKind::GlobalAsm({})", tcx.def_path_str(def_id)); self.output.push(dummy_spanned(MonoItem::GlobalAsm(id))); } DefKind::Static { .. } => { - let def_id = id.owner_id.to_def_id(); - debug!("RootCollector: ItemKind::Static({})", self.tcx.def_path_str(def_id)); + debug!("RootCollector: ItemKind::Static({})", tcx.def_path_str(def_id)); self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { // Const items only generate mono items if they are actually used somewhere. // Just declaring them is insufficient. - // If we're collecting items eagerly, then recurse into all constants. // Otherwise the value is only collected when explicitly mentioned in other items. - if self.strategy == MonoItemCollectionStrategy::Eager { - if !self.tcx.generics_of(id.owner_id).own_requires_monomorphization() - && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) - { - collect_const_value(self.tcx, val, self.output); - } + if self.strategy == MonoItemCollectionStrategy::Eager + && !tcx.generics_of(def_id).own_requires_monomorphization() + && let args = ty::GenericArgs::for_item(tcx, def_id, |param, _| { + expect_and_erase_regions(tcx, param) + }) + && !tcx.instantiate_and_check_impossible_predicates((def_id, args)) + && let Ok(val) = tcx.const_eval_poly(def_id) + { + collect_const_value(tcx, val, self.output); } } DefKind::Impl { .. } => { if self.strategy == MonoItemCollectionStrategy::Eager { - create_mono_items_for_default_impls(self.tcx, id, self.output); + create_mono_items_for_default_impls(tcx, id, self.output); } } DefKind::Fn => { @@ -1614,35 +1594,23 @@ fn create_mono_items_for_default_impls<'tcx>( item: hir::ItemId, output: &mut MonoItems<'tcx>, ) { - let Some(impl_) = tcx.impl_trait_header(item.owner_id) else { - return; - }; - + let impl_def_id = item.owner_id.to_def_id(); + let Some(impl_) = tcx.impl_trait_header(impl_def_id) else { return }; if matches!(impl_.polarity, ty::ImplPolarity::Negative) { return; } - if tcx.generics_of(item.owner_id).own_requires_monomorphization() { - return; - } - // Lifetimes never affect trait selection, so we are allowed to eagerly // instantiate an instance of an impl method if the impl (and method, // which we check below) is only parameterized over lifetime. In that case, // we use the ReErased, which has no lifetime information associated with // it, to validate whether or not the impl is legal to instantiate at all. - let only_region_params = |param: &ty::GenericParamDef, _: &_| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - unreachable!( - "`own_requires_monomorphization` check means that \ - we should have no type/const params" - ) - } - }; - let impl_args = GenericArgs::for_item(tcx, item.owner_id.to_def_id(), only_region_params); - let trait_ref = impl_.trait_ref.instantiate(tcx, impl_args); - + if tcx.generics_of(impl_def_id).own_requires_monomorphization() { + return; + } + let impl_args = ty::GenericArgs::for_item(tcx, impl_def_id, |param, _| { + expect_and_erase_regions(tcx, param) + }); // Unlike 'lazy' monomorphization that begins by collecting items transitively // called by `main` or other global items, when eagerly monomorphizing impl // items, we never actually check that the predicates of this impl are satisfied @@ -1652,13 +1620,15 @@ fn create_mono_items_for_default_impls<'tcx>( // consider higher-ranked predicates such as `for<'a> &'a mut [u8]: Copy` to // be trivially false. We must now check that the impl has no impossible-to-satisfy // predicates. - if tcx.instantiate_and_check_impossible_predicates((item.owner_id.to_def_id(), impl_args)) { + if tcx.instantiate_and_check_impossible_predicates((impl_def_id, impl_args)) { return; } + let trait_ref = impl_.trait_ref.instantiate(tcx, impl_args); + let typing_env = ty::TypingEnv::fully_monomorphized(); let trait_ref = tcx.normalize_erasing_regions(typing_env, trait_ref); - let overridden_methods = tcx.impl_item_implementor_ids(item.owner_id); + let overridden_methods = tcx.impl_item_implementor_ids(impl_def_id); for method in tcx.provided_trait_methods(trait_ref.def_id) { if overridden_methods.contains_key(&method.def_id) { continue; @@ -1671,7 +1641,9 @@ fn create_mono_items_for_default_impls<'tcx>( // As mentioned above, the method is legal to eagerly instantiate if it // only has lifetime generic parameters. This is validated by calling // `own_requires_monomorphization` on both the impl and method. - let args = trait_ref.args.extend_to(tcx, method.def_id, only_region_params); + let args = trait_ref + .args + .extend_to(tcx, method.def_id, |param, _| expect_and_erase_regions(tcx, param)); let instance = ty::Instance::expect_resolve(tcx, typing_env, method.def_id, args, DUMMY_SP); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); @@ -1681,6 +1653,18 @@ fn create_mono_items_for_default_impls<'tcx>( } } +fn expect_and_erase_regions<'tcx>( + tcx: TyCtxt<'tcx>, + param: &ty::GenericParamDef, +) -> ty::GenericArg<'tcx> { + match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + bug!("unexpected non-region param") + } + } +} + //=----------------------------------------------------------------------------- // Top-level entry point, tying it all together //=----------------------------------------------------------------------------- diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 7e15267a953ba..43902ff2cd0b4 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -37,6 +37,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt}; use rustc_privacy::DefIdVisitor; use rustc_session::config::CrateType; +use rustc_trait_selection::traits; use tracing::debug; /// Determines whether this item is recursive for reachability. See `is_recursively_reachable_local` @@ -205,19 +206,34 @@ impl<'tcx> ReachableContext<'tcx> { } hir::ItemKind::Const(_, _, _, init) => { - // Only things actually ending up in the final constant value are reachable - // for codegen. Everything else is only needed during const-eval, so even if - // const-eval happens in a downstream crate, all they need is - // `mir_for_ctfe`. + if self.tcx.generics_of(item.owner_id).own_requires_monomorphization() { + // In this case, we don't want to evaluate the const initializer. + // In lieu of that, we have to consider everything mentioned in it + // as reachable, since it *may* end up in the final value. + self.visit_nested_body(init); + return; + } + + let predicates = self.tcx.predicates_of(item.owner_id); + let predicates = predicates.instantiate_identity(self.tcx).predicates; + if traits::impossible_predicates(self.tcx, predicates) { + // The constant is impossible to reference. + // Therefore nothing can be reachable via it. + return; + } + match self.tcx.const_eval_poly_to_alloc(item.owner_id.def_id.into()) { Ok(alloc) => { + // Only things actually ending up in the final constant value are + // reachable for codegen. Everything else is only needed during + // const-eval, so even if const-eval happens in a downstream crate, + // all they need is `mir_for_ctfe`. let alloc = self.tcx.global_alloc(alloc.alloc_id).unwrap_memory(); self.propagate_from_alloc(alloc); } - // We can't figure out which value the constant will evaluate to. In - // lieu of that, we have to consider everything mentioned in the const - // initializer reachable, since it *may* end up in the final value. - Err(ErrorHandled::TooGeneric(_)) => self.visit_nested_body(init), + // We've checked at the start that there aren't any non-lifetime params + // in scope. The const initializer can't possibly be too generic. + Err(ErrorHandled::TooGeneric(_)) => bug!(), // If there was an error evaluating the const, nothing can be reachable // via it, and anyway compilation will fail. Err(ErrorHandled::Reported(..)) => {} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 43806d3977b94..3f852f55e52f1 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -721,8 +721,13 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. This should not be /// used during analysis. +#[instrument(level = "debug", skip(tcx))] pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec>) -> bool { - debug!("impossible_predicates(predicates={:?})", predicates); + debug_assert!(!predicates.has_non_region_param()); + if predicates.is_empty() { + return false; + } + let (infcx, param_env) = tcx .infer_ctxt() .with_next_trait_solver(true) @@ -749,26 +754,23 @@ pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec( tcx: TyCtxt<'tcx>, - key: (DefId, GenericArgsRef<'tcx>), + (def_id, args): (DefId, GenericArgsRef<'tcx>), ) -> bool { - debug!("instantiate_and_check_impossible_predicates(key={:?})", key); - - let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + let mut predicates = tcx.predicates_of(def_id).instantiate(tcx, args).predicates; // Specifically check trait fulfillment to avoid an error when trying to resolve // associated items. - if let Some(trait_def_id) = tcx.trait_of_item(key.0) { - let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); + if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, args); predicates.push(trait_ref.upcast(tcx)); } predicates.retain(|predicate| !predicate.has_param()); - let result = impossible_predicates(tcx, predicates); - debug!("instantiate_and_check_impossible_predicates(key={:?}) = {:?}", key, result); - result + impossible_predicates(tcx, predicates) } /// Checks whether a trait's associated item is impossible to reference on a given impl. diff --git a/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs b/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs deleted file mode 100644 index df50b4af80984..0000000000000 --- a/tests/codegen/dont_codegen_private_const_fn_only_used_in_const_eval.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! This test checks that we do not monomorphize functions that are only -//! used to evaluate static items, but never used in runtime code. - -//@compile-flags: --crate-type=lib -Copt-level=0 - -#![feature(generic_const_items)] - -const fn foo() {} - -pub static FOO: () = foo(); - -// CHECK-NOT: define{{.*}}foo{{.*}} - -const fn bar() {} - -pub const BAR: () = bar(); - -// CHECK-NOT: define{{.*}}bar{{.*}} - -const fn baz() {} - -#[rustfmt::skip] -pub const BAZ: () = if C { - baz() -}; - -// CHECK: define{{.*}}baz{{.*}} diff --git a/tests/codegen/private-const-fn-only-used-in-const-eval.rs b/tests/codegen/private-const-fn-only-used-in-const-eval.rs new file mode 100644 index 0000000000000..e97c570ccf3a3 --- /dev/null +++ b/tests/codegen/private-const-fn-only-used-in-const-eval.rs @@ -0,0 +1,45 @@ +//! Check that we — where possible — don't codegen functions that are only used to evaluate +//! static/const items, but never used in runtime code. + +//@ compile-flags: --crate-type=lib -Copt-level=0 + +#![feature(generic_const_items)] // only used in the last few test cases + +pub static STATIC: () = func0(); +const fn func0() {} +// CHECK-NOT: define{{.*}}func0{{.*}} + +pub const CONSTANT: () = func1(); +const fn func1() {} +// CHECK-NOT: define{{.*}}func1{{.*}} + +// The const item is impossible to reference and thus, we don't need to codegen `func2`. +pub const IMPOSS_TO_REF: fn() = func2 +where + for<'_delay> String: Copy; +const fn func2() {} +// CHECK-NOT: define{{.*}}func2{{.*}} + +// We generally don't want to evaluate the initializer of free const items if the latter have +// non-region params (and even if we did, const eval would fail anyway with "too polymorphic" +// if the initializer actually referenced such a param). +// +// As a result of not being able to look at the final value, during reachability analysis we +// can't tell for sure if for example certain functions end up in the final value or if they're +// only used during const eval. We fall back to a conservative HIR-based approach. + +// `func3` isn't needed at runtime but the compiler can't tell for the reason mentioned above. +pub const POLY_CONST_0: () = func3(); +const fn func3() {} +// CHECK: define{{.*}}func3{{.*}} + +// `func4` isn't needed at runtime but the compiler can't tell for the reason mentioned above. +pub const POLY_CONST_1: () = if C { func4() }; +const fn func4() {} +// CHECK: define{{.*}}func4{{.*}} + +// `func5` *is* needed at runtime (here, the HIR-based approach gets it right). +pub const POLY_CONST_2: Option = + if C { Some(func5) } else { None }; +const fn func5() {} +// CHECK: define{{.*}}func5{{.*}} diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs index fa3ef5907b26d..95a5b73f7875a 100644 --- a/tests/ui/generic-const-items/def-site-eval.rs +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -1,11 +1,11 @@ -//! Test that we only evaluate free const items (their def site to be clear) -//! whose generics don't require monomorphization. -#![feature(generic_const_items)] -#![expect(incomplete_features)] - +// Test that we don't evaluate the initializer of free const items if the latter +// have non-region generic params (i.e., ones that "require monomorphization"). //@ revisions: fail pass //@[pass] check-pass +#![feature(generic_const_items)] +#![expect(incomplete_features)] + const _<_T>: () = panic!(); const _: () = panic!(); diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs deleted file mode 100644 index 102c7b1e5f938..0000000000000 --- a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(generic_const_items, trivial_bounds)] -#![allow(incomplete_features)] - -// Ensure that we check if trivial bounds on const items hold or not. - -const UNUSABLE: () = () //~ ERROR entering unreachable code -where - String: Copy; - -fn main() { - let _ = UNUSABLE; //~ ERROR the trait bound `String: Copy` is not satisfied -} diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr deleted file mode 100644 index e5c3bbb0c7dba..0000000000000 --- a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error[E0080]: entering unreachable code - --> $DIR/trivially-unsatisfied-bounds-0.rs:6:1 - | -LL | / const UNUSABLE: () = () -LL | | where -LL | | String: Copy; - | |_________________^ evaluation of `UNUSABLE` failed here - -error[E0277]: the trait bound `String: Copy` is not satisfied - --> $DIR/trivially-unsatisfied-bounds-0.rs:11:13 - | -LL | let _ = UNUSABLE; - | ^^^^^^^^ the trait `Copy` is not implemented for `String` - | -note: required by a bound in `UNUSABLE` - --> $DIR/trivially-unsatisfied-bounds-0.rs:8:13 - | -LL | const UNUSABLE: () = () - | -------- required by a bound in this constant -LL | where -LL | String: Copy; - | ^^^^ required by this bound in `UNUSABLE` - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0080, E0277. -For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs deleted file mode 100644 index ebe97a65bbf31..0000000000000 --- a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(generic_const_items, trivial_bounds)] -#![allow(incomplete_features, dead_code, trivial_bounds)] - -// FIXME(generic_const_items): This looks like a bug to me. I expected that we wouldn't emit any -// errors. I thought we'd skip the evaluation of consts whose bounds don't hold. - -const UNUSED: () = () -where - String: Copy; -//~^^^ ERROR unreachable code - -fn main() {} diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr deleted file mode 100644 index a5f6dd980bdcb..0000000000000 --- a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: entering unreachable code - --> $DIR/trivially-unsatisfied-bounds-1.rs:7:1 - | -LL | / const UNUSED: () = () -LL | | where -LL | | String: Copy; - | |_________________^ evaluation of `UNUSED` failed here - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds.rs b/tests/ui/generic-const-items/trivially-unsatisfied-bounds.rs new file mode 100644 index 0000000000000..ca0898be57da5 --- /dev/null +++ b/tests/ui/generic-const-items/trivially-unsatisfied-bounds.rs @@ -0,0 +1,21 @@ +// Check that we don't evaluate the initializer of free const items if +// the latter have "impossible" (trivially unsatisfied) predicates. +//@ check-pass + +#![feature(generic_const_items)] +#![expect(incomplete_features)] +#![crate_type = "lib"] + +const _UNUSED: () = panic!() +where + for<'_delay> String: Copy; + +// Check *public* const items specifically to exercise reachability analysis which normally +// evaluates const initializers to look for function pointers in the final const value. +pub const PUBLIC: () = panic!() +where + for<'_delay> String: Copy; + +const REGION_POLY<'a>: () = panic!() +where + for<'_delay> [&'a ()]: Sized;