Skip to content

Commit 9e4f9d2

Browse files
committed
generic_const_args: allow paths to non type consts
1 parent 50fd5b6 commit 9e4f9d2

23 files changed

Lines changed: 439 additions & 28 deletions

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3041,7 +3041,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
30413041
span: Span,
30423042
) -> Result<(), ErrorGuaranteed> {
30433043
let tcx = self.tcx();
3044-
if tcx.is_type_const(def_id) {
3044+
// FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants
3045+
// until a refactoring for how generic args for IACs are represented has been landed.
3046+
let is_inherent_assoc_const = tcx.def_kind(def_id)
3047+
== DefKind::AssocConst { is_type_const: false }
3048+
&& tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false };
3049+
if tcx.is_type_const(def_id)
3050+
|| tcx.features().generic_const_args() && !is_inherent_assoc_const
3051+
{
30453052
Ok(())
30463053
} else {
30473054
let mut err = self.dcx().struct_span_err(

compiler/rustc_middle/src/ty/context/impl_interner.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
177177
fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
178178
self.type_of_opaque_hir_typeck(def_id)
179179
}
180+
fn is_type_const(self, def_id: DefId) -> bool {
181+
self.is_type_const(def_id)
182+
}
180183
fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
181184
self.const_of_item(def_id)
182185
}

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1168,6 +1168,40 @@ where
11681168
self.delegate.evaluate_const(param_env, uv)
11691169
}
11701170

1171+
pub(super) fn evaluate_const_and_instantiate_normalizes_to_term(
1172+
&mut self,
1173+
goal: Goal<I, ty::NormalizesTo<I>>,
1174+
uv: ty::UnevaluatedConst<I>,
1175+
) -> QueryResult<I> {
1176+
match self.evaluate_const(goal.param_env, uv) {
1177+
Some(evaluated) => {
1178+
self.instantiate_normalizes_to_term(goal, evaluated.into());
1179+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1180+
}
1181+
None => {
1182+
// HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary,
1183+
// `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want
1184+
// to check `has_non_region_infer` against the type with vars resolved (i.e. check
1185+
// if there are vars we failed to resolve), so we need to call it again here.
1186+
// Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and
1187+
// HasInfers or something, make evaluate_const return that, and make this branch be
1188+
// based on that, rather than checking `has_non_region_infer`.
1189+
if self.resolve_vars_if_possible(uv).has_non_region_infer() {
1190+
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
1191+
} else {
1192+
// We do not instantiate to the `uv` passed in, but rather
1193+
// `goal.predicate.alias`. The `uv` passed in might correspond to the `impl`
1194+
// form of a constant (with generic arguments corresponding to the impl block),
1195+
// however, we want to structurally instantiate to the original, non-rebased,
1196+
// trait `Self` form of the constant (with generic arguments being the trait
1197+
// `Self` type).
1198+
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
1199+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1200+
}
1201+
}
1202+
}
1203+
}
1204+
11711205
pub(super) fn is_transmutable(
11721206
&mut self,
11731207
src: I::Ty,

compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,23 @@ where
1414
&mut self,
1515
goal: Goal<I, ty::NormalizesTo<I>>,
1616
) -> QueryResult<I> {
17-
if let Some(normalized_const) = self.evaluate_const(
18-
goal.param_env,
19-
ty::UnevaluatedConst::new(
20-
goal.predicate.alias.def_id().try_into().unwrap(),
21-
goal.predicate.alias.args,
22-
),
23-
) {
24-
self.instantiate_normalizes_to_term(goal, normalized_const.into());
25-
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
17+
let cx = self.cx();
18+
let uv = goal.predicate.alias.expect_ct(self.cx());
19+
// keep legacy behavior for array repeat expressions:
20+
// when a constant is too generic to be evaluated, the legacy behavior is to return
21+
// Ambiguous, whereas evaluate_const_and_instantiate_normalizes_to_term structurally
22+
// instantiates to itself and returns Yes (if there are no inference variables)
23+
let is_repeat_expr =
24+
cx.anon_const_kind(uv.def.into()) == ty::AnonConstKind::RepeatExprCount;
25+
if is_repeat_expr {
26+
if let Some(normalized_const) = self.evaluate_const(goal.param_env, uv) {
27+
self.instantiate_normalizes_to_term(goal, normalized_const.into());
28+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
29+
} else {
30+
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
31+
}
2632
} else {
27-
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
33+
self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv)
2834
}
2935
}
3036
}

compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,15 @@ where
3434
ty::AliasTermKind::FreeTy { def_id } => {
3535
cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
3636
}
37-
ty::AliasTermKind::FreeConst { def_id } => {
37+
ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => {
3838
cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
3939
}
40+
ty::AliasTermKind::FreeConst { .. } => {
41+
return self.evaluate_const_and_instantiate_normalizes_to_term(
42+
goal,
43+
free_alias.expect_ct(cx),
44+
);
45+
}
4046
kind => panic!("expected free alias, found {kind:?}"),
4147
};
4248

compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,18 @@ where
5656
ty::AliasTermKind::InherentTy { def_id } => {
5757
cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
5858
}
59-
ty::AliasTermKind::InherentConst { def_id } => {
59+
ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => {
6060
cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
6161
}
62+
ty::AliasTermKind::InherentConst { .. } => {
63+
// FIXME(gca): This is dead code at the moment. It should eventually call
64+
// self.evaluate_const like projected consts do in consider_impl_candidate in
65+
// normalizes_to/mod.rs. However, how generic args are represented for IACs is up in
66+
// the air right now.
67+
// Will self.evaluate_const eventually take the inherent_args or the impl_args form
68+
// of args? It might be either.
69+
panic!("References to inherent associated consts should have been blocked");
70+
}
6271
kind => panic!("expected inherent alias, found {kind:?}"),
6372
};
6473
self.instantiate_normalizes_to_term(goal, normalized);

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,28 @@ where
389389
.instantiate(cx, target_args)
390390
.skip_norm_wip()
391391
.into(),
392-
ty::AliasTermKind::ProjectionConst { .. } => cx
393-
.const_of_item(target_item_def_id)
394-
.instantiate(cx, target_args)
395-
.skip_norm_wip()
396-
.into(),
392+
ty::AliasTermKind::ProjectionConst { .. }
393+
if cx.is_type_const(target_item_def_id) =>
394+
{
395+
cx.const_of_item(target_item_def_id)
396+
.instantiate(cx, target_args)
397+
.skip_norm_wip()
398+
.into()
399+
}
400+
ty::AliasTermKind::ProjectionConst { .. } => {
401+
// target_args contains vars:
402+
// - the Self type of the impl block is instantiated with fresh vars
403+
// - the resulting type is eq'd against the actual Self type
404+
// - the fresh vars are then used as target_args
405+
// we need the actual args to run const eval, so we need to actually do the `eq`
406+
// and figure out the args, so, call try_evaluate_added_goals
407+
ecx.try_evaluate_added_goals()?;
408+
let uv = ty::UnevaluatedConst::new(
409+
target_item_def_id.try_into().unwrap(),
410+
target_args,
411+
);
412+
return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv);
413+
}
397414
kind => panic!("expected projection, found {kind:?}"),
398415
};
399416

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,10 @@ pub fn try_evaluate_const<'tcx>(
696696
// logic does not go through type system normalization. If it did this would
697697
// be a backwards compatibility problem as we do not enforce "syntactic" non-
698698
// usage of generic parameters like we do here.
699-
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
699+
if uv.args.has_non_region_param()
700+
|| uv.args.has_non_region_infer()
701+
|| uv.args.has_non_region_placeholders()
702+
{
700703
return Err(EvaluateConstErr::HasGenericsOrInfers);
701704
}
702705

compiler/rustc_trait_selection/src/traits/normalize.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,13 +347,17 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
347347
.skip_norm_wip()
348348
.fold_with(self)
349349
.into(),
350-
ty::AliasTermKind::FreeConst { def_id } => infcx
350+
ty::AliasTermKind::FreeConst { def_id } if infcx.tcx.is_type_const(def_id) => infcx
351351
.tcx
352352
.const_of_item(def_id)
353353
.instantiate(infcx.tcx, free.args)
354354
.skip_norm_wip()
355355
.fold_with(self)
356356
.into(),
357+
ty::AliasTermKind::FreeConst { .. } => {
358+
super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env)
359+
.into()
360+
}
357361
kind => panic!("expected free alias, found {kind:?}"),
358362
};
359363
self.depth -= 1;
@@ -376,6 +380,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
376380
t
377381
}
378382

383+
#[instrument(skip(self), level = "debug")]
379384
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
380385
if !needs_normalization(self.selcx.infcx, &ty) {
381386
return ty;

compiler/rustc_trait_selection/src/traits/project.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -549,9 +549,17 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
549549
ty::AliasTermKind::InherentTy { def_id } => {
550550
tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip().into()
551551
}
552-
ty::AliasTermKind::InherentConst { def_id } => {
552+
ty::AliasTermKind::InherentConst { def_id } if tcx.is_type_const(def_id) => {
553553
tcx.const_of_item(def_id).instantiate(tcx, args).skip_norm_wip().into()
554554
}
555+
ty::AliasTermKind::InherentConst { .. } => {
556+
// FIXME(gca): This is dead code at the moment. It should eventually call
557+
// super::evaluate_const like projected consts do in confirm_impl_candidate in this
558+
// file. However, how generic args are represented for IACs is up in the air right now.
559+
// Will super::evaluate_const eventually take the inherent_args or the impl_args form of
560+
// args? It might be either.
561+
panic!("References to inherent associated consts should have been blocked");
562+
}
555563
kind => panic!("expected inherent alias, found {kind:?}"),
556564
};
557565

@@ -617,11 +625,13 @@ pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>(
617625
alias_term.rebase_inherent_args_onto_impl(impl_args, tcx)
618626
}
619627

628+
#[derive(Debug)]
620629
enum Projected<'tcx> {
621630
Progress(Progress<'tcx>),
622631
NoProgress(ty::Term<'tcx>),
623632
}
624633

634+
#[derive(Debug)]
625635
struct Progress<'tcx> {
626636
term: ty::Term<'tcx>,
627637
obligations: PredicateObligations<'tcx>,
@@ -652,7 +662,7 @@ impl<'tcx> Progress<'tcx> {
652662
/// IMPORTANT:
653663
/// - `obligation` must be fully normalized
654664
// FIXME(mgca): While this supports constants, it is only used for types by default right now
655-
#[instrument(level = "info", skip(selcx))]
665+
#[instrument(level = "info", ret, skip(selcx))]
656666
fn project<'cx, 'tcx>(
657667
selcx: &mut SelectionContext<'cx, 'tcx>,
658668
obligation: &ProjectionTermObligation<'tcx>,
@@ -2060,7 +2070,21 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20602070
ty::AliasTermKind::ProjectionConst { .. } => {
20612071
let uv = ty::UnevaluatedConst::new(assoc_term.item.def_id, args);
20622072
let ct = ty::Const::new_unevaluated(tcx, uv);
2063-
super::evaluate_const(selcx.infcx, ct, param_env).into()
2073+
// We don't want to use super::evaluate_const, because that returns its parameter
2074+
// unchanged if it is too generic to evaluate. We are passing the `impl` form of the
2075+
// constant to evaluate_const (with generic arguments corresponding to the impl
2076+
// block), but we want to return the original, non-rebased, trait `Self` form of the
2077+
// constant (with generic arguments being the trait `Self` type) in Projected::NoProgress.
2078+
match super::try_evaluate_const(selcx.infcx, ct, param_env) {
2079+
Ok(evaluated) => evaluated.into(),
2080+
Err(
2081+
super::EvaluateConstErr::EvaluationFailure(e)
2082+
| super::EvaluateConstErr::InvalidConstParamTy(e),
2083+
) => ty::Const::new_error(tcx, e).into(),
2084+
Err(super::EvaluateConstErr::HasGenericsOrInfers) => {
2085+
return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
2086+
}
2087+
}
20642088
}
20652089
kind => panic!("expected projection alias, found {kind:?}"),
20662090
};

0 commit comments

Comments
 (0)