diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 08f1d947dfb5c..c2279aebcec72 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -26,6 +26,7 @@ pub mod wf; use std::fmt::Debug; use std::ops::ControlFlow; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; pub use rustc_infer::traits::*; @@ -592,159 +593,206 @@ pub fn try_evaluate_const<'tcx>( ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Result, EvaluateConstErr> { - let tcx = infcx.tcx; - let ct = infcx.resolve_vars_if_possible(ct); - debug!(?ct); - - match ct.kind() { - ty::ConstKind::Value(..) => Ok(ct), - ty::ConstKind::Error(e) => Err(EvaluateConstErr::EvaluationFailure(e)), - ty::ConstKind::Param(_) - | ty::ConstKind::Infer(_) - | ty::ConstKind::Bound(_, _) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), - ty::ConstKind::Unevaluated(uv) => { - let opt_anon_const_kind = - (tcx.def_kind(uv.def) == DefKind::AnonConst).then(|| tcx.anon_const_kind(uv.def)); - - // Postpone evaluation of constants that depend on generic parameters or - // inference variables. - // - // We use `TypingMode::PostAnalysis` here which is not *technically* correct - // to be revealing opaque types here as borrowcheck has not run yet. However, - // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during - // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). - // As a result we always use a revealed env when resolving the instance to evaluate. - // - // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself - // instead of having this logic here - let (args, typing_env) = match opt_anon_const_kind { - // We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system - // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason - // about if you have to consider gce whatsoever. - Some(ty::AnonConstKind::GCE) => { - if uv.has_non_region_infer() || uv.has_non_region_param() { - // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause - // inference variables and generic parameters to show up in `ty::Const` even though the anon const - // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. - match tcx.thir_abstract_const(uv.def) { - Ok(Some(ct)) => { - let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); - if let Err(e) = ct.error_reported() { - return Err(EvaluateConstErr::EvaluationFailure(e)); - } else if ct.has_non_region_infer() || ct.has_non_region_param() { - // If the anon const *does* actually use generic parameters or inference variables from - // the generic arguments provided for it, then we should *not* attempt to evaluate it. - return Err(EvaluateConstErr::HasGenericsOrInfers); - } else { - let args = - replace_param_and_infer_args_with_placeholder(tcx, uv.args); - let typing_env = infcx - .typing_env(tcx.erase_and_anonymize_regions(param_env)) - .with_post_analysis_normalized(tcx); + fn try_evaluate_const_inner<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + type_const_stack: &mut FxHashSet, + ) -> Result, EvaluateConstErr> { + let tcx = infcx.tcx; + let ct = infcx.resolve_vars_if_possible(ct); + debug!(?ct); + + match ct.kind() { + ty::ConstKind::Value(..) => Ok(ct), + ty::ConstKind::Error(e) => Err(EvaluateConstErr::EvaluationFailure(e)), + ty::ConstKind::Param(_) + | ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Expr(_) => Err(EvaluateConstErr::HasGenericsOrInfers), + ty::ConstKind::Unevaluated(uv) => { + let opt_anon_const_kind = (tcx.def_kind(uv.def) == DefKind::AnonConst) + .then(|| tcx.anon_const_kind(uv.def)); + + // Postpone evaluation of constants that depend on generic parameters or + // inference variables. + // + // We use `TypingMode::PostAnalysis` here which is not *technically* correct + // to be revealing opaque types here as borrowcheck has not run yet. However, + // CTFE itself uses `TypingMode::PostAnalysis` unconditionally even during + // typeck and not doing so has a lot of (undesirable) fallout (#101478, #119821). + // As a result we always use a revealed env when resolving the instance to evaluate. + // + // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself + // instead of having this logic here + let (args, typing_env) = match opt_anon_const_kind { + // We handle `generic_const_exprs` separately as reasonable ways of handling constants in the type system + // completely fall apart under `generic_const_exprs` and makes this whole function Really hard to reason + // about if you have to consider gce whatsoever. + Some(ty::AnonConstKind::GCE) => { + if uv.has_non_region_infer() || uv.has_non_region_param() { + // `feature(generic_const_exprs)` causes anon consts to inherit all parent generics. This can cause + // inference variables and generic parameters to show up in `ty::Const` even though the anon const + // does not actually make use of them. We handle this case specially and attempt to evaluate anyway. + match tcx.thir_abstract_const(uv.def) { + Ok(Some(ct)) => { + let ct = + tcx.expand_abstract_consts(ct.instantiate(tcx, uv.args)); + if let Err(e) = ct.error_reported() { + return Err(EvaluateConstErr::EvaluationFailure(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() + { + // If the anon const *does* actually use generic parameters or inference variables from + // the generic arguments provided for it, then we should *not* attempt to evaluate it. + return Err(EvaluateConstErr::HasGenericsOrInfers); + } else { + let args = replace_param_and_infer_args_with_placeholder( + tcx, uv.args, + ); + let typing_env = infcx + .typing_env(tcx.erase_and_anonymize_regions(param_env)) + .with_post_analysis_normalized(tcx); + (args, typing_env) + } + } + Err(_) | Ok(None) => { + let args = GenericArgs::identity_for_item(tcx, uv.def); + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); (args, typing_env) } } - Err(_) | Ok(None) => { - let args = GenericArgs::identity_for_item(tcx, uv.def); - let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); - (args, typing_env) - } + } else { + let typing_env = infcx + .typing_env(tcx.erase_and_anonymize_regions(param_env)) + .with_post_analysis_normalized(tcx); + (uv.args, typing_env) } - } else { - let typing_env = infcx - .typing_env(tcx.erase_and_anonymize_regions(param_env)) - .with_post_analysis_normalized(tcx); - (uv.args, typing_env) - } - } - Some(ty::AnonConstKind::RepeatExprCount) => { - if uv.has_non_region_infer() { - // Diagnostics will sometimes replace the identity args of anon consts in - // array repeat expr counts with inference variables so we have to handle this - // even though it is not something we should ever actually encounter. - // - // Array repeat expr counts are allowed to syntactically use generic parameters - // but must not actually depend on them in order to evalaute successfully. This means - // that it is actually fine to evalaute them in their own environment rather than with - // the actually provided generic arguments. - tcx.dcx().delayed_bug("AnonConst with infer args but no error reported"); } + Some(ty::AnonConstKind::RepeatExprCount) => { + if uv.has_non_region_infer() { + // Diagnostics will sometimes replace the identity args of anon consts in + // array repeat expr counts with inference variables so we have to handle this + // even though it is not something we should ever actually encounter. + // + // Array repeat expr counts are allowed to syntactically use generic parameters + // but must not actually depend on them in order to evalaute successfully. This means + // that it is actually fine to evalaute them in their own environment rather than with + // the actually provided generic arguments. + tcx.dcx() + .delayed_bug("AnonConst with infer args but no error reported"); + } - // The generic args of repeat expr counts under `min_const_generics` are not supposed to - // affect evaluation of the constant as this would make it a "truly" generic const arg. - // To prevent this we discard all the generic arguments and evalaute with identity args - // and in its own environment instead of the current environment we are normalizing in. - let args = GenericArgs::identity_for_item(tcx, uv.def); - let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); + // The generic args of repeat expr counts under `min_const_generics` are not supposed to + // affect evaluation of the constant as this would make it a "truly" generic const arg. + // To prevent this we discard all the generic arguments and evalaute with identity args + // and in its own environment instead of the current environment we are normalizing in. + let args = GenericArgs::identity_for_item(tcx, uv.def); + let typing_env = ty::TypingEnv::post_analysis(tcx, uv.def); - (args, typing_env) - } - Some(ty::AnonConstKind::OGCA) => { - if infcx.typing_mode() != TypingMode::PostAnalysis { - // OGCA anon consts should be treated as always having generics - // during anything before codegen (or maybe MIR opts too). - return Err(EvaluateConstErr::HasGenericsOrInfers); + (args, typing_env) } + Some(ty::AnonConstKind::OGCA) => { + if infcx.typing_mode() != TypingMode::PostAnalysis { + // OGCA anon consts should be treated as always having generics + // during anything before codegen (or maybe MIR opts too). + return Err(EvaluateConstErr::HasGenericsOrInfers); + } - if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { - return Err(EvaluateConstErr::HasGenericsOrInfers); - } + if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { + return Err(EvaluateConstErr::HasGenericsOrInfers); + } - let typing_env = ty::TypingEnv::fully_monomorphized(); - (uv.args, typing_env) - } - Some(ty::AnonConstKind::MCG) | Some(ty::AnonConstKind::NonTypeSystem) | None => { - // We are only dealing with "truly" generic/uninferred constants here: - // - GCEConsts have been handled separately - // - Repeat expr count back compat consts have also been handled separately - // So we are free to simply defer evaluation here. - // - // FIXME: This assumes that `args` are normalized which is not necessarily true - // - // Const patterns are converted to type system constants before being - // evaluated. However, we don't care about them here as pattern evaluation - // logic does not go through type system normalization. If it did this would - // be a backwards compatibility problem as we do not enforce "syntactic" non- - // usage of generic parameters like we do here. - if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { - return Err(EvaluateConstErr::HasGenericsOrInfers); + let typing_env = ty::TypingEnv::fully_monomorphized(); + (uv.args, typing_env) } + Some(ty::AnonConstKind::MCG) + | Some(ty::AnonConstKind::NonTypeSystem) + | None => { + // We are only dealing with "truly" generic/uninferred constants here: + // - GCEConsts have been handled separately + // - Repeat expr count back compat consts have also been handled separately + // So we are free to simply defer evaluation here. + // + // FIXME: This assumes that `args` are normalized which is not necessarily true + // + // Const patterns are converted to type system constants before being + // evaluated. However, we don't care about them here as pattern evaluation + // logic does not go through type system normalization. If it did this would + // be a backwards compatibility problem as we do not enforce "syntactic" non- + // usage of generic parameters like we do here. + if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { + return Err(EvaluateConstErr::HasGenericsOrInfers); + } - // Since there is no generic parameter, we can just drop the environment - // to prevent query cycle. - let typing_env = ty::TypingEnv::fully_monomorphized(); + // Since there is no generic parameter, we can just drop the environment + // to prevent query cycle. + let typing_env = ty::TypingEnv::fully_monomorphized(); - (uv.args, typing_env) - } - }; - - let uv = ty::UnevaluatedConst::new(uv.def, args); - let erased_uv = tcx.erase_and_anonymize_regions(uv); - - use rustc_middle::mir::interpret::ErrorHandled; - // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at - // where it gets used as a const generic. - match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, tcx.def_span(uv.def)) { - Ok(Ok(val)) => Ok(ty::Const::new_value( - tcx, - val, - tcx.type_of(uv.def).instantiate(tcx, uv.args), - )), - Ok(Err(_)) => { - let e = tcx.dcx().delayed_bug( - "Type system constant with non valtree'able type evaluated but no error emitted", - ); - Err(EvaluateConstErr::InvalidConstParamTy(e)) + (uv.args, typing_env) + } + }; + + if tcx.is_type_const(uv.def) { + // Guard against self-referential type consts which would otherwise recurse. + if !type_const_stack.insert(uv.def) { + let guar = tcx.dcx().span_err( + tcx.def_span(uv.def), + "cycle detected when evaluating type-level constant", + ); + return Err(EvaluateConstErr::EvaluationFailure(guar)); + } + + let result = match ty::Instance::try_resolve(tcx, typing_env, uv.def, args) { + Ok(Some(instance)) => { + let ct = tcx + .const_of_item(instance.def_id()) + .instantiate(tcx, instance.args); + try_evaluate_const_inner(infcx, ct, param_env, type_const_stack) + } + Ok(None) => Err(EvaluateConstErr::HasGenericsOrInfers), + Err(err) => Err(EvaluateConstErr::EvaluationFailure( + rustc_middle::mir::interpret::ReportedErrorInfo::non_const_eval_error( + err, + ) + .into(), + )), + }; + + type_const_stack.remove(&uv.def); + return result; } - Err(ErrorHandled::Reported(info, _)) => { - Err(EvaluateConstErr::EvaluationFailure(info.into())) + + let uv = ty::UnevaluatedConst::new(uv.def, args); + let erased_uv = tcx.erase_and_anonymize_regions(uv); + + use rustc_middle::mir::interpret::ErrorHandled; + // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at + // where it gets used as a const generic. + match tcx.const_eval_resolve_for_typeck(typing_env, erased_uv, tcx.def_span(uv.def)) + { + Ok(Ok(val)) => Ok(ty::Const::new_value( + tcx, + val, + tcx.type_of(uv.def).instantiate(tcx, uv.args), + )), + Ok(Err(_)) => { + let e = tcx.dcx().delayed_bug( + "Type system constant with non valtree'able type evaluated but no error emitted", + ); + Err(EvaluateConstErr::InvalidConstParamTy(e)) + } + Err(ErrorHandled::Reported(info, _)) => { + Err(EvaluateConstErr::EvaluationFailure(info.into())) + } + Err(ErrorHandled::TooGeneric(_)) => Err(EvaluateConstErr::HasGenericsOrInfers), } - Err(ErrorHandled::TooGeneric(_)) => Err(EvaluateConstErr::HasGenericsOrInfers), } } } + + let mut type_const_stack = FxHashSet::default(); + try_evaluate_const_inner(infcx, ct, param_env, &mut type_const_stack) } /// Replaces args that reference param or infer variables with suitable diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.current.stderr similarity index 89% rename from tests/ui/const-generics/mgca/type_const-recursive.stderr rename to tests/ui/const-generics/mgca/type_const-recursive.current.stderr index d21ccb22bc90b..4df39fce0bc80 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.stderr +++ b/tests/ui/const-generics/mgca/type_const-recursive.current.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow normalizing the unevaluated constant `A` - --> $DIR/type_const-recursive.rs:5:1 + --> $DIR/type_const-recursive.rs:8:1 | LL | type const A: u8 = A; | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/const-generics/mgca/type_const-recursive.next.stderr b/tests/ui/const-generics/mgca/type_const-recursive.next.stderr new file mode 100644 index 0000000000000..b4cd0fc3798f5 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-recursive.next.stderr @@ -0,0 +1,8 @@ +error: cycle detected when evaluating type-level constant + --> $DIR/type_const-recursive.rs:8:1 + | +LL | type const A: u8 = A; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs index ebaab51bbc3b6..ce70fa146c655 100644 --- a/tests/ui/const-generics/mgca/type_const-recursive.rs +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -1,8 +1,12 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + #![expect(incomplete_features)] #![feature(min_generic_const_args)] - type const A: u8 = A; -//~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] +//[current]~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] +//[next]~^^ ERROR cycle detected when evaluating type-level constant fn main() {} diff --git a/tests/ui/const-generics/type-const-cycle.rs b/tests/ui/const-generics/type-const-cycle.rs new file mode 100644 index 0000000000000..0d8d4df68fae4 --- /dev/null +++ b/tests/ui/const-generics/type-const-cycle.rs @@ -0,0 +1,16 @@ +//@ compile-flags: -Znext-solver=globally +#![feature(generic_const_items, min_generic_const_args)] +#![allow(incomplete_features)] + +trait Owner { + type const C: u32; + //~^ ERROR cycle detected when evaluating type-level constant +} + +impl Owner for () { + type const C: u32 = { <() as Owner>::C:: }; +} + +type Arr = [u8; <() as Owner>::C::<0>]; + +fn main() {} diff --git a/tests/ui/const-generics/type-const-cycle.stderr b/tests/ui/const-generics/type-const-cycle.stderr new file mode 100644 index 0000000000000..41987f465d171 --- /dev/null +++ b/tests/ui/const-generics/type-const-cycle.stderr @@ -0,0 +1,8 @@ +error: cycle detected when evaluating type-level constant + --> $DIR/type-const-cycle.rs:6:5 + | +LL | type const C: u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/type-const-ice-issue-151631.rs b/tests/ui/const-generics/type-const-ice-issue-151631.rs new file mode 100644 index 0000000000000..53c6d0c6dd6d4 --- /dev/null +++ b/tests/ui/const-generics/type-const-ice-issue-151631.rs @@ -0,0 +1,18 @@ +// issue: +//@ compile-flags: -Znext-solver +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait SuperTrait {} +trait Trait: SuperTrait { + type const K: u32; +} +impl Trait for () { //~ ERROR: the trait bound `(): SuperTrait` is not satisfied + type const K: u32 = const { 1 }; +} + +fn check(_: impl Trait) {} + +fn main() { + check(()); //~ ERROR: the trait bound `(): SuperTrait` is not satisfied +} diff --git a/tests/ui/const-generics/type-const-ice-issue-151631.stderr b/tests/ui/const-generics/type-const-ice-issue-151631.stderr new file mode 100644 index 0000000000000..6f287293e27cf --- /dev/null +++ b/tests/ui/const-generics/type-const-ice-issue-151631.stderr @@ -0,0 +1,37 @@ +error[E0277]: the trait bound `(): SuperTrait` is not satisfied + --> $DIR/type-const-ice-issue-151631.rs:10:16 + | +LL | impl Trait for () { + | ^^ the trait `SuperTrait` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/type-const-ice-issue-151631.rs:6:1 + | +LL | trait SuperTrait {} + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/type-const-ice-issue-151631.rs:7:14 + | +LL | trait Trait: SuperTrait { + | ^^^^^^^^^^ required by this bound in `Trait` + +error[E0277]: the trait bound `(): SuperTrait` is not satisfied + --> $DIR/type-const-ice-issue-151631.rs:17:5 + | +LL | check(()); + | ^^^^^^^^^ the trait `SuperTrait` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/type-const-ice-issue-151631.rs:6:1 + | +LL | trait SuperTrait {} + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/type-const-ice-issue-151631.rs:7:14 + | +LL | trait Trait: SuperTrait { + | ^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`.