Skip to content

Commit 10217a1

Browse files
committed
generic_const_args: allow paths to non type consts
1 parent cf1817b commit 10217a1

36 files changed

Lines changed: 602 additions & 42 deletions

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
464464
check_incompatible_features(sess, features);
465465
check_dependent_features(sess, features);
466466
check_new_solver_banned_features(sess, features);
467+
check_features_requiring_new_solver(sess, features);
467468

468469
let mut visitor = PostExpansionVisitor { sess, features };
469470

@@ -738,3 +739,25 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
738739
});
739740
}
740741
}
742+
743+
fn check_features_requiring_new_solver(sess: &Session, features: &Features) {
744+
if sess.opts.unstable_opts.next_solver.globally {
745+
return;
746+
}
747+
748+
// Require the new solver with GCA, because the old solver can't implement GCA correctly as it
749+
// does not support normalization obligations for free and inherent consts.
750+
if let Some(gca_span) = features
751+
.enabled_lang_features()
752+
.iter()
753+
.find(|feat| feat.gate_name == sym::generic_const_args)
754+
.map(|feat| feat.attr_sp)
755+
{
756+
#[allow(rustc::symbol_intern_string_literal)]
757+
sess.dcx().emit_err(errors::MissingDependentFeatures {
758+
parent_span: gca_span,
759+
parent: sym::generic_const_exprs,
760+
missing: String::from("-Znext-solver=globally"),
761+
});
762+
}
763+
}

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: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,20 @@ where
3030
.map(|pred| goal.with(cx, pred)),
3131
);
3232

33-
let actual = if free_alias.kind(cx).is_type() {
34-
cx.type_of(free_alias.def_id()).instantiate(cx, free_alias.args).skip_norm_wip().into()
35-
} else {
36-
cx.const_of_item(free_alias.def_id())
37-
.instantiate(cx, free_alias.args)
38-
.skip_norm_wip()
39-
.into()
33+
let actual = match free_alias.kind(cx) {
34+
ty::AliasTermKind::FreeTy { def_id } => {
35+
cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
36+
}
37+
ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => {
38+
cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into()
39+
}
40+
ty::AliasTermKind::FreeConst { .. } => {
41+
return self.evaluate_const_and_instantiate_normalizes_to_term(
42+
goal,
43+
free_alias.expect_ct(cx),
44+
);
45+
}
46+
kind => panic!("expected free alias, found {kind:?}"),
4047
};
4148

4249
self.instantiate_normalizes_to_term(goal, actual);

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,23 @@ where
5252
.map(|pred| goal.with(cx, pred)),
5353
);
5454

55-
let normalized = if inherent.kind(cx).is_type() {
56-
cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip().into()
57-
} else {
58-
cx.const_of_item(inherent.def_id())
59-
.instantiate(cx, inherent_args)
60-
.skip_norm_wip()
61-
.into()
55+
let normalized = match inherent.kind(cx) {
56+
ty::AliasTermKind::InherentTy { def_id } => {
57+
cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
58+
}
59+
ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => {
60+
cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into()
61+
}
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+
}
71+
kind => panic!("expected inherent alias, found {kind:?}"),
6272
};
6373
self.instantiate_normalizes_to_term(goal, normalized);
6474
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -384,19 +384,30 @@ where
384384

385385
// Finally we construct the actual value of the associated type.
386386
let term = match goal.predicate.alias.kind(cx) {
387-
ty::AliasTermKind::ProjectionTy { .. } => {
388-
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
387+
ty::AliasTermKind::ProjectionTy { .. } => cx
388+
.type_of(target_item_def_id)
389+
.instantiate(cx, target_args)
390+
.skip_norm_wip()
391+
.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()
389399
}
390400
ty::AliasTermKind::ProjectionConst { .. } => {
391-
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
401+
let uv = ty::UnevaluatedConst::new(
402+
target_item_def_id.try_into().unwrap(),
403+
target_args,
404+
);
405+
return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv);
392406
}
393407
kind => panic!("expected projection, found {kind:?}"),
394408
};
395409

396-
ecx.instantiate_normalizes_to_term(
397-
goal,
398-
term.instantiate(cx, target_args).skip_norm_wip(),
399-
);
410+
ecx.instantiate_normalizes_to_term(goal, term);
400411
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
401412
})
402413
}

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/wf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
10601060
ty::ConstKind::Unevaluated(uv) => {
10611061
if !c.has_escaping_bound_vars() {
10621062
// Skip type consts as mGCA doesn't support evaluatable clauses
1063-
if !tcx.is_type_const(uv.def) {
1063+
if !tcx.is_type_const(uv.def) && !tcx.features().generic_const_args() {
10641064
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
10651065
ty::ClauseKind::ConstEvaluatable(c),
10661066
));

0 commit comments

Comments
 (0)