From 50fd5b6a85058ff6c41a7454b879aebf432e0ce3 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Tue, 21 Apr 2026 07:49:31 +0200 Subject: [PATCH 1/2] expect specific alias kind in normalizes_to --- .../src/solve/normalizes_to/free_alias.rs | 15 +++---- .../src/solve/normalizes_to/inherent.rs | 15 +++---- .../src/solve/normalizes_to/mod.rs | 21 +++++----- .../src/traits/normalize.rs | 16 ++++---- .../src/traits/project.rs | 41 ++++++++++++++----- .../src/normalize_projection_ty.rs | 14 ++++--- 6 files changed, 74 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index f3004e4c8105b..acccf95831d68 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -30,13 +30,14 @@ where .map(|pred| goal.with(cx, pred)), ); - let actual = if free_alias.kind(cx).is_type() { - cx.type_of(free_alias.def_id()).instantiate(cx, free_alias.args).skip_norm_wip().into() - } else { - cx.const_of_item(free_alias.def_id()) - .instantiate(cx, free_alias.args) - .skip_norm_wip() - .into() + let actual = match free_alias.kind(cx) { + ty::AliasTermKind::FreeTy { def_id } => { + cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + } + ty::AliasTermKind::FreeConst { def_id } => { + cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + } + kind => panic!("expected free alias, found {kind:?}"), }; self.instantiate_normalizes_to_term(goal, actual); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 4d62407fe2995..ea942fd4adc77 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -52,13 +52,14 @@ where .map(|pred| goal.with(cx, pred)), ); - let normalized = if inherent.kind(cx).is_type() { - cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip().into() - } else { - cx.const_of_item(inherent.def_id()) - .instantiate(cx, inherent_args) - .skip_norm_wip() - .into() + let normalized = match inherent.kind(cx) { + ty::AliasTermKind::InherentTy { def_id } => { + cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + } + ty::AliasTermKind::InherentConst { def_id } => { + cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + } + kind => panic!("expected inherent alias, found {kind:?}"), }; self.instantiate_normalizes_to_term(goal, normalized); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 1d74f1efe9136..8ab68d4eb71d4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -384,19 +384,20 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind(cx) { - ty::AliasTermKind::ProjectionTy { .. } => { - cx.type_of(target_item_def_id).map_bound(|ty| ty.into()) - } - ty::AliasTermKind::ProjectionConst { .. } => { - cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into()) - } + ty::AliasTermKind::ProjectionTy { .. } => cx + .type_of(target_item_def_id) + .instantiate(cx, target_args) + .skip_norm_wip() + .into(), + ty::AliasTermKind::ProjectionConst { .. } => cx + .const_of_item(target_item_def_id) + .instantiate(cx, target_args) + .skip_norm_wip() + .into(), kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term( - goal, - term.instantiate(cx, target_args).skip_norm_wip(), - ); + ecx.instantiate_normalizes_to_term(goal, term); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 55f1f861921ea..eaea3bf14d402 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -339,22 +339,22 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { }), ); self.depth += 1; - let res = if free.kind(infcx.tcx).is_type() { - infcx + let res = match free.kind(infcx.tcx) { + ty::AliasTermKind::FreeTy { def_id } => infcx .tcx - .type_of(free.def_id()) + .type_of(def_id) .instantiate(infcx.tcx, free.args) .skip_norm_wip() .fold_with(self) - .into() - } else { - infcx + .into(), + ty::AliasTermKind::FreeConst { def_id } => infcx .tcx - .const_of_item(free.def_id()) + .const_of_item(def_id) .instantiate(infcx.tcx, free.args) .skip_norm_wip() .fold_with(self) - .into() + .into(), + kind => panic!("expected free alias, found {kind:?}"), }; self.depth -= 1; res diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 5423e394119c4..e7ffd7b4b6369 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -545,10 +545,14 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( )); } - let term: Term<'tcx> = if alias_term.kind(tcx).is_type() { - tcx.type_of(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() - } else { - tcx.const_of_item(alias_term.def_id()).instantiate(tcx, args).skip_norm_wip().into() + let term: Term<'tcx> = match alias_term.kind(tcx) { + ty::AliasTermKind::InherentTy { def_id } => { + tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip().into() + } + ty::AliasTermKind::InherentConst { def_id } => { + tcx.const_of_item(def_id).instantiate(tcx, args).skip_norm_wip().into() + } + kind => panic!("expected inherent alias, found {kind:?}"), }; let mut term = selcx.infcx.resolve_vars_if_possible(term); @@ -2029,12 +2033,6 @@ fn confirm_impl_candidate<'cx, 'tcx>( let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node); - let term = if obligation.predicate.kind(tcx).is_type() { - tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into()) - } else { - tcx.const_of_item(assoc_term.item.def_id).map_bound(|ct| ct.into()) - }; - let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) { let msg = "impl item and trait item have different parameters"; let span = obligation.cause.span; @@ -2046,7 +2044,28 @@ fn confirm_impl_candidate<'cx, 'tcx>( Progress { term: err, obligations: nested } } else { assoc_term_own_obligations(selcx, obligation, &mut nested); - Progress { term: term.instantiate(tcx, args).skip_norm_wip(), obligations: nested } + + let term = match obligation.predicate.kind(tcx) { + ty::AliasTermKind::ProjectionTy { .. } => { + tcx.type_of(assoc_term.item.def_id).instantiate(tcx, args).skip_norm_wip().into() + } + ty::AliasTermKind::ProjectionConst { .. } + if tcx.is_type_const(assoc_term.item.def_id) => + { + tcx.const_of_item(assoc_term.item.def_id) + .instantiate(tcx, args) + .skip_norm_wip() + .into() + } + ty::AliasTermKind::ProjectionConst { .. } => { + let uv = ty::UnevaluatedConst::new(assoc_term.item.def_id, args); + let ct = ty::Const::new_unevaluated(tcx, uv); + super::evaluate_const(selcx.infcx, ct, param_env).into() + } + kind => panic!("expected projection alias, found {kind:?}"), + }; + + Progress { term, obligations: nested } }; Ok(Projected::Progress(progress)) } diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 16d5c20c87fe2..6a8ddb79a9826 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -2,7 +2,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::traits::PredicateObligations; use rustc_middle::query::Providers; -use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::normalize::NormalizationResult; @@ -86,10 +86,14 @@ fn normalize_canonicalized_free_alias<'tcx>( }, ); ocx.register_obligations(obligations); - let normalized_term = if goal.kind(tcx).is_type() { - tcx.type_of(goal.def_id()).instantiate(tcx, goal.args).skip_norm_wip().into() - } else { - tcx.const_of_item(goal.def_id()).instantiate(tcx, goal.args).skip_norm_wip().into() + let normalized_term = match goal.kind(tcx) { + ty::AliasTermKind::FreeTy { def_id } => { + tcx.type_of(def_id).instantiate(tcx, goal.args).skip_norm_wip().into() + } + ty::AliasTermKind::FreeConst { def_id } => { + tcx.const_of_item(def_id).instantiate(tcx, goal.args).skip_norm_wip().into() + } + kind => panic!("expected free alias, found {kind:?}"), }; Ok(NormalizationResult { normalized_term }) }, From 11032904999f4a3c0e0c2e754429751631240463 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Wed, 22 Apr 2026 20:28:54 +0200 Subject: [PATCH 2/2] generic_const_args: allow paths to non type consts --- .../src/hir_ty_lowering/mod.rs | 9 +++- .../src/ty/context/impl_interner.rs | 3 ++ .../src/solve/eval_ctxt/mod.rs | 21 ++++++++ .../src/solve/normalizes_to/anon_const.rs | 24 ++++++---- .../src/solve/normalizes_to/free_alias.rs | 5 +- .../src/solve/normalizes_to/inherent.rs | 11 ++++- .../src/solve/normalizes_to/mod.rs | 36 ++++++++++++-- .../rustc_trait_selection/src/traits/mod.rs | 5 +- .../src/traits/normalize.rs | 7 ++- .../src/traits/project.rs | 25 ++++++++-- .../rustc_trait_selection/src/traits/wf.rs | 2 +- .../src/normalize_projection_ty.rs | 6 ++- compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/predicate.rs | 22 +++++++-- .../const-generics/gca/generic-free-const.rs | 20 ++++++++ .../gca/non-type-equality-fail.next.stderr | 25 ++++++++++ .../gca/non-type-equality-fail.old.stderr | 25 ++++++++++ .../gca/non-type-equality-fail.rs | 45 +++++++++++++++++ .../gca/non-type-equality-ok.rs | 48 +++++++++++++++++++ .../gca/path-to-non-type-const.rs | 36 ++++++++++++++ ...type-inherent-associated-const.next.stderr | 24 ++++++++++ ...-type-inherent-associated-const.old.stderr | 24 ++++++++++ ...h-to-non-type-inherent-associated-const.rs | 29 +++++++++++ 23 files changed, 425 insertions(+), 28 deletions(-) create mode 100644 tests/ui/const-generics/gca/generic-free-const.rs create mode 100644 tests/ui/const-generics/gca/non-type-equality-fail.next.stderr create mode 100644 tests/ui/const-generics/gca/non-type-equality-fail.old.stderr create mode 100644 tests/ui/const-generics/gca/non-type-equality-fail.rs create mode 100644 tests/ui/const-generics/gca/non-type-equality-ok.rs create mode 100644 tests/ui/const-generics/gca/path-to-non-type-const.rs create mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.next.stderr create mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.old.stderr create mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index a43e373415ab7..324de1f9f707e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3041,7 +3041,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - if tcx.is_type_const(def_id) { + // FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants + // until a refactoring for how generic args for IACs are represented has been landed. + let is_inherent_assoc_const = tcx.def_kind(def_id) + == DefKind::AssocConst { is_type_const: false } + && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false }; + if tcx.is_type_const(def_id) + || tcx.features().generic_const_args() && !is_inherent_assoc_const + { Ok(()) } else { let mut err = self.dcx().struct_span_err( diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index ccda7aeb910ca..a16c06a369661 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -177,6 +177,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of_opaque_hir_typeck(def_id) } + fn is_type_const(self, def_id: DefId) -> bool { + self.is_type_const(def_id) + } fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> { self.const_of_item(def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index e224febd3c927..f84ba816d4355 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1168,6 +1168,27 @@ where self.delegate.evaluate_const(param_env, uv) } + pub(super) fn evaluate_const_and_instantiate( + &mut self, + goal: Goal>, + uv: ty::UnevaluatedConst, + ) -> QueryResult { + match self.delegate.evaluate_const(goal.param_env, uv) { + Some(evaluated) => { + self.instantiate_normalizes_to_term(goal, evaluated.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + None => { + if uv.has_non_region_infer() { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else { + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + } + } + pub(super) fn is_transmutable( &mut self, src: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index b3703639d99ef..f2b274d56c064 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -14,17 +14,21 @@ where &mut self, goal: Goal>, ) -> QueryResult { - if let Some(normalized_const) = self.evaluate_const( - goal.param_env, - ty::UnevaluatedConst::new( - goal.predicate.alias.def_id().try_into().unwrap(), - goal.predicate.alias.args, - ), - ) { - self.instantiate_normalizes_to_term(goal, normalized_const.into()); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + let cx = self.cx(); + let uv = goal.predicate.alias.expect_ct(self.cx()); + // keep legacy behavior for array repeat expressions (return Ambiguous instead of a + // structural relation and Yes) + let is_repeat_expr = + cx.anon_const_kind(uv.def.into()) == ty::AnonConstKind::RepeatExprCount; + if is_repeat_expr { + if let Some(normalized_const) = self.evaluate_const(goal.param_env, uv) { + self.instantiate_normalizes_to_term(goal, normalized_const.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + self.evaluate_const_and_instantiate(goal, uv) } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index acccf95831d68..c80ff3b39d200 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -34,9 +34,12 @@ where ty::AliasTermKind::FreeTy { def_id } => { cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() } - ty::AliasTermKind::FreeConst { def_id } => { + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => { cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() } + ty::AliasTermKind::FreeConst { .. } => { + return self.evaluate_const_and_instantiate(goal, free_alias.expect_ct(cx)); + } kind => panic!("expected free alias, found {kind:?}"), }; diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index ea942fd4adc77..00b5fd7fdcbce 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -56,9 +56,18 @@ where ty::AliasTermKind::InherentTy { def_id } => { cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() } - ty::AliasTermKind::InherentConst { def_id } => { + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => { cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() } + ty::AliasTermKind::InherentConst { .. } => { + // FIXME(gca): This is dead code at the moment. It should eventually call + // self.evaluate_const like projected consts do in consider_impl_candidate in + // normalizes_to/mod.rs. However, how generic args are represented for IACs is up in + // the air right now. + // Will self.evaluate_const eventually take the inherent_args or the impl_args form + // of args? It might be either. + panic!("References to inherent associated consts should have been blocked"); + } kind => panic!("expected inherent alias, found {kind:?}"), }; self.instantiate_normalizes_to_term(goal, normalized); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 8ab68d4eb71d4..9bc8133e5bb74 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -389,11 +389,37 @@ where .instantiate(cx, target_args) .skip_norm_wip() .into(), - ty::AliasTermKind::ProjectionConst { .. } => cx - .const_of_item(target_item_def_id) - .instantiate(cx, target_args) - .skip_norm_wip() - .into(), + ty::AliasTermKind::ProjectionConst { .. } + if cx.is_type_const(target_item_def_id) => + { + cx.const_of_item(target_item_def_id) + .instantiate(cx, target_args) + .skip_norm_wip() + .into() + } + ty::AliasTermKind::ProjectionConst { .. } => { + // target_args contains vars: + // - the Self type of the impl block is instantiated with fresh vars + // - the resulting type is eq'd against the actual Self type + // - the fresh vars are then used as target_args + // we need the actual args to run const eval, so we need to actually do the `eq` + // and figure out the args, so, call try_evaluate_added_goals + ecx.try_evaluate_added_goals()?; + // HACK(khyperia): this shouldn't be necessary, `try_evaluate_const` calls + // `resolve_vars_if_possible`. However, on failure, + // `evaluate_const_and_instantiate` then checks `has_non_region_infer` on the + // *pre*-`resolve_vars_if_possible` args, which, we want to return false if we + // successfully identified the actual type. + // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics + // and HasInfers or something, and make evaluate_const_and_instantiate change + // its behavior based on that, rather than it checking `has_non_region_infer`. + let target_args = ecx.resolve_vars_if_possible(target_args); + let uv = ty::UnevaluatedConst::new( + target_item_def_id.try_into().unwrap(), + target_args, + ); + return ecx.evaluate_const_and_instantiate(goal, uv); + } kind => panic!("expected projection, found {kind:?}"), }; diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 0196b8bee7cca..a4be589172f6f 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -696,7 +696,10 @@ pub fn try_evaluate_const<'tcx>( // 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() { + if uv.args.has_non_region_param() + || uv.args.has_non_region_infer() + || uv.args.has_non_region_placeholders() + { return Err(EvaluateConstErr::HasGenericsOrInfers); } diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index eaea3bf14d402..f739f69e9cad5 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -347,13 +347,17 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { .skip_norm_wip() .fold_with(self) .into(), - ty::AliasTermKind::FreeConst { def_id } => infcx + ty::AliasTermKind::FreeConst { def_id } if infcx.tcx.is_type_const(def_id) => infcx .tcx .const_of_item(def_id) .instantiate(infcx.tcx, free.args) .skip_norm_wip() .fold_with(self) .into(), + ty::AliasTermKind::FreeConst { .. } => { + super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env) + .into() + } kind => panic!("expected free alias, found {kind:?}"), }; self.depth -= 1; @@ -376,6 +380,7 @@ impl<'a, 'b, 'tcx> TypeFolder> for AssocTypeNormalizer<'a, 'b, 'tcx t } + #[instrument(skip(self), level = "debug")] fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { if !needs_normalization(self.selcx.infcx, &ty) { return ty; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e7ffd7b4b6369..0375d29dc8755 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -549,9 +549,17 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>( ty::AliasTermKind::InherentTy { def_id } => { tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip().into() } - ty::AliasTermKind::InherentConst { def_id } => { + ty::AliasTermKind::InherentConst { def_id } if tcx.is_type_const(def_id) => { tcx.const_of_item(def_id).instantiate(tcx, args).skip_norm_wip().into() } + ty::AliasTermKind::InherentConst { .. } => { + // FIXME(gca): This is dead code at the moment. It should eventually call + // super::evaluate_const like projected consts do in confirm_impl_candidate in this + // file. However, how generic args are represented for IACs is up in the air right now. + // Will super::evaluate_const eventually take the inherent_args or the impl_args form of + // args? It might be either. + panic!("References to inherent associated consts should have been blocked"); + } kind => panic!("expected inherent alias, found {kind:?}"), }; @@ -617,11 +625,13 @@ pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>( alias_term.rebase_inherent_args_onto_impl(impl_args, tcx) } +#[derive(Debug)] enum Projected<'tcx> { Progress(Progress<'tcx>), NoProgress(ty::Term<'tcx>), } +#[derive(Debug)] struct Progress<'tcx> { term: ty::Term<'tcx>, obligations: PredicateObligations<'tcx>, @@ -652,7 +662,7 @@ impl<'tcx> Progress<'tcx> { /// IMPORTANT: /// - `obligation` must be fully normalized // FIXME(mgca): While this supports constants, it is only used for types by default right now -#[instrument(level = "info", skip(selcx))] +#[instrument(level = "info", ret, skip(selcx))] fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTermObligation<'tcx>, @@ -2060,7 +2070,16 @@ fn confirm_impl_candidate<'cx, 'tcx>( ty::AliasTermKind::ProjectionConst { .. } => { let uv = ty::UnevaluatedConst::new(assoc_term.item.def_id, args); let ct = ty::Const::new_unevaluated(tcx, uv); - super::evaluate_const(selcx.infcx, ct, param_env).into() + match super::try_evaluate_const(selcx.infcx, ct, param_env) { + Ok(evaluated) => evaluated.into(), + Err( + super::EvaluateConstErr::EvaluationFailure(e) + | super::EvaluateConstErr::InvalidConstParamTy(e), + ) => ty::Const::new_error(tcx, e).into(), + Err(super::EvaluateConstErr::HasGenericsOrInfers) => { + return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx))); + } + } } kind => panic!("expected projection alias, found {kind:?}"), }; diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 66a2fcaa562d1..258eed52e167b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1060,7 +1060,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses - if !tcx.is_type_const(uv.def) { + if !tcx.is_type_const(uv.def) && !tcx.features().generic_const_args() { let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 6a8ddb79a9826..a9914bb71d95d 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -90,9 +90,13 @@ fn normalize_canonicalized_free_alias<'tcx>( ty::AliasTermKind::FreeTy { def_id } => { tcx.type_of(def_id).instantiate(tcx, goal.args).skip_norm_wip().into() } - ty::AliasTermKind::FreeConst { def_id } => { + ty::AliasTermKind::FreeConst { def_id } if tcx.is_type_const(def_id) => { tcx.const_of_item(def_id).instantiate(tcx, goal.args).skip_norm_wip().into() } + ty::AliasTermKind::FreeConst { .. } => { + traits::evaluate_const(ocx.infcx, goal.to_term(tcx).expect_const(), param_env) + .into() + } kind => panic!("expected free alias, found {kind:?}"), }; Ok(NormalizationResult { normalized_term }) diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index f350e5da9654c..1ef1a40479197 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -205,6 +205,7 @@ pub trait Interner: fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> ty::EarlyBinder; + fn is_type_const(self, def_id: Self::DefId) -> bool; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 0545fbfda6e41..7a48cfb23d6a2 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -732,16 +732,32 @@ impl AliasTerm { AliasTermKind::InherentTy { def_id } => AliasTyKind::Inherent { def_id }, AliasTermKind::OpaqueTy { def_id } => AliasTyKind::Opaque { def_id }, AliasTermKind::FreeTy { def_id } => AliasTyKind::Free { def_id }, - AliasTermKind::InherentConst { .. } + kind @ (AliasTermKind::InherentConst { .. } | AliasTermKind::FreeConst { .. } | AliasTermKind::UnevaluatedConst { .. } - | AliasTermKind::ProjectionConst { .. } => { - panic!("Cannot turn `UnevaluatedConst` into `AliasTy`") + | AliasTermKind::ProjectionConst { .. }) => { + panic!("Cannot turn `{}` into `AliasTy`", kind.descr()) } }; ty::AliasTy { kind, args: self.args, _use_alias_ty_new_instead: () } } + pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { + let def_id = match self.kind(interner) { + AliasTermKind::InherentConst { def_id } + | AliasTermKind::FreeConst { def_id } + | AliasTermKind::UnevaluatedConst { def_id } + | AliasTermKind::ProjectionConst { def_id } => def_id, + kind @ (AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. }) => { + panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) + } + }; + ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args } + } + // FIXME: remove this function (access the field instead) pub fn kind(self, _interner: I) -> AliasTermKind { self.kind diff --git a/tests/ui/const-generics/gca/generic-free-const.rs b/tests/ui/const-generics/gca/generic-free-const.rs new file mode 100644 index 0000000000000..176a7973da941 --- /dev/null +++ b/tests/ui/const-generics/gca/generic-free-const.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +const ADD1: usize = N + 1; + +fn a() -> [usize; ADD1::] { + [ADD1::; ADD1::] +} + +fn main() { + let _: [(); ADD1::<1>] = [(); ADD1::<1>]; + let _: [(); ADD1::<{ ADD1::<1> }>] = [(); ADD1::<2>]; + a::<2>(); +} diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.next.stderr b/tests/ui/const-generics/gca/non-type-equality-fail.next.stderr new file mode 100644 index 0000000000000..55b955506c2dd --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-fail.next.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:33:9 + | +LL | let _: Struct<{ as Trait>::PROJECTED_A }> = + | -------------------------------------------------------- expected due to this +LL | Struct::<{ as Trait>::PROJECTED_B }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Struct< as Trait>::PROJECTED_A>` + found struct `Struct< as Trait>::PROJECTED_B>` + +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:38:41 + | +LL | let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | | + | expected due to this + | + = note: expected struct `Struct<::PROJECTED_A>` + found struct `Struct<::PROJECTED_B>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.old.stderr b/tests/ui/const-generics/gca/non-type-equality-fail.old.stderr new file mode 100644 index 0000000000000..a29bd5d05c227 --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-fail.old.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:33:9 + | +LL | let _: Struct<{ as Trait>::PROJECTED_A }> = + | -------------------------------------------------------- expected due to this +LL | Struct::<{ as Trait>::PROJECTED_B }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected ` as Trait>::PROJECTED_A`, found ` as Trait>::PROJECTED_B` + | + = note: expected struct `Struct< as Trait>::PROJECTED_A>` + found struct `Struct< as Trait>::PROJECTED_B>` + +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:38:41 + | +LL | let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `::PROJECTED_A`, found `::PROJECTED_B` + | | + | expected due to this + | + = note: expected struct `Struct<::PROJECTED_A>` + found struct `Struct<::PROJECTED_B>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.rs b/tests/ui/const-generics/gca/non-type-equality-fail.rs new file mode 100644 index 0000000000000..348585bedffd7 --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-fail.rs @@ -0,0 +1,45 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED_A: usize; + const PROJECTED_B: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +impl Trait for StructImpl { + const PROJECTED_A: usize = 1; + const PROJECTED_B: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED_A: usize = N; + const PROJECTED_B: usize = N; +} + +const FREE_A: usize = 1; +const FREE_B: usize = 1; + +struct Struct; + +fn f() { + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_B }>; + //~^ ERROR mismatched types +} + +fn g() { + let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; + //~^ ERROR mismatched types +} + +fn main() { + f::<2>(); + g::(); +} diff --git a/tests/ui/const-generics/gca/non-type-equality-ok.rs b/tests/ui/const-generics/gca/non-type-equality-ok.rs new file mode 100644 index 0000000000000..cf543d77fe425 --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-ok.rs @@ -0,0 +1,48 @@ +//@ check-pass +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED_A: usize; + const PROJECTED_B: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +impl Trait for StructImpl { + const PROJECTED_A: usize = 1; + const PROJECTED_B: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED_A: usize = N; + const PROJECTED_B: usize = N; +} + +const FREE_A: usize = 1; +const FREE_B: usize = 1; + +struct Struct; + +fn f() { + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_A }>; +} + +fn g() { + let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_A }>; +} + +fn main() { + let _: Struct = Struct::; + let _: Struct<{ ::PROJECTED_A }> = + Struct::<{ ::PROJECTED_B }>; + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_B }>; +} diff --git a/tests/ui/const-generics/gca/path-to-non-type-const.rs b/tests/ui/const-generics/gca/path-to-non-type-const.rs new file mode 100644 index 0000000000000..f5bdeebb3c88e --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-const.rs @@ -0,0 +1,36 @@ +//@ check-pass +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +const FREE: usize = 1; + +impl Trait for StructImpl { + const PROJECTED: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED: usize = A; +} + +struct Struct; + +fn f() { + let _ = Struct::<{ T::PROJECTED }>; +} + +fn main() { + let _ = Struct::; + let _ = Struct::<{ ::PROJECTED }>; + let _ = Struct::<{ as Trait>::PROJECTED }>; +} diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.next.stderr b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.next.stderr new file mode 100644 index 0000000000000..df4be9bb17c76 --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.next.stderr @@ -0,0 +1,24 @@ +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:25:24 + | +LL | let _ = Struct::<{ StructImpl::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `StructImpl::INHERENT` + | +LL | type const INHERENT: usize = 1; + | ++++ + +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:27:24 + | +LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `GenericStructImpl::::INHERENT` + | +LL | type const INHERENT: usize = A; + | ++++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.old.stderr b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.old.stderr new file mode 100644 index 0000000000000..df4be9bb17c76 --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.old.stderr @@ -0,0 +1,24 @@ +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:25:24 + | +LL | let _ = Struct::<{ StructImpl::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `StructImpl::INHERENT` + | +LL | type const INHERENT: usize = 1; + | ++++ + +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:27:24 + | +LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `GenericStructImpl::::INHERENT` + | +LL | type const INHERENT: usize = A; + | ++++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs new file mode 100644 index 0000000000000..819fd74b9f121 --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs @@ -0,0 +1,29 @@ +//! This test should be part of path-to-non-type-const.rs, and should pass. However, we are holding +//! off on implementing paths to IACs until a refactoring of how IAC generics are represented. +//@ revisions: old next +//@[next] compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(inherent_associated_types)] +#![expect(incomplete_features)] + +struct StructImpl; +struct GenericStructImpl; + +impl StructImpl { + const INHERENT: usize = 1; +} + +impl GenericStructImpl { + const INHERENT: usize = A; +} + +struct Struct; + +fn main() { + let _ = Struct::<{ StructImpl::INHERENT }>; + //~^ ERROR use of `const` in the type system not defined as `type const` + let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; + //~^ ERROR use of `const` in the type system not defined as `type const` +}