Skip to content

Commit c89c812

Browse files
committed
generic_const_args: allow paths to non type consts
1 parent cb40c25 commit c89c812

37 files changed

Lines changed: 566 additions & 45 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

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

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3064,7 +3064,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
30643064
span: Span,
30653065
) -> Result<(), ErrorGuaranteed> {
30663066
let tcx = self.tcx();
3067-
if tcx.is_type_const(def_id) {
3067+
// FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants
3068+
// until a refactoring for how generic args for IACs are represented has been landed.
3069+
let is_inherent_assoc_const = tcx.def_kind(def_id)
3070+
== DefKind::AssocConst { is_type_const: false }
3071+
&& tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false };
3072+
if tcx.is_type_const(def_id)
3073+
|| tcx.features().generic_const_args() && !is_inherent_assoc_const
3074+
{
30683075
Ok(())
30693076
} else {
30703077
let mut err = self.dcx().struct_span_err(

compiler/rustc_middle/src/ty/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
106106
self.generic_const_exprs()
107107
}
108108

109+
fn generic_const_args(self) -> bool {
110+
self.generic_const_args()
111+
}
112+
109113
fn coroutine_clone(self) -> bool {
110114
self.coroutine_clone()
111115
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
172172
fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
173173
self.type_of_opaque_hir_typeck(def_id)
174174
}
175+
fn is_type_const(self, def_id: DefId) -> bool {
176+
self.is_type_const(def_id)
177+
}
175178
fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> {
176179
self.const_of_item(def_id)
177180
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,6 +1196,44 @@ where
11961196
self.delegate.evaluate_const(param_env, uv)
11971197
}
11981198

1199+
pub(super) fn evaluate_const_and_instantiate_normalizes_to_term(
1200+
&mut self,
1201+
goal: Goal<I, ty::NormalizesTo<I>>,
1202+
uv: ty::UnevaluatedConst<I>,
1203+
) -> QueryResult<I> {
1204+
match self.evaluate_const(goal.param_env, uv) {
1205+
Some(evaluated) => {
1206+
self.instantiate_normalizes_to_term(goal, evaluated.into());
1207+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1208+
}
1209+
None if self.cx().features().generic_const_args() => {
1210+
// HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary,
1211+
// `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want
1212+
// to check `has_non_region_infer` against the type with vars resolved (i.e. check
1213+
// if there are vars we failed to resolve), so we need to call it again here.
1214+
// Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and
1215+
// HasInfers or something, make evaluate_const return that, and make this branch be
1216+
// based on that, rather than checking `has_non_region_infer`.
1217+
if self.resolve_vars_if_possible(uv).has_non_region_infer() {
1218+
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
1219+
} else {
1220+
// We do not instantiate to the `uv` passed in, but rather
1221+
// `goal.predicate.alias`. The `uv` passed in might correspond to the `impl`
1222+
// form of a constant (with generic arguments corresponding to the impl block),
1223+
// however, we want to structurally instantiate to the original, non-rebased,
1224+
// trait `Self` form of the constant (with generic arguments being the trait
1225+
// `Self` type).
1226+
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
1227+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1228+
}
1229+
}
1230+
None => {
1231+
// Legacy behavior: always treat as ambiguous
1232+
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
1233+
}
1234+
}
1235+
}
1236+
11991237
pub(super) fn is_transmutable(
12001238
&mut self,
12011239
src: I::Ty,

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use rustc_type_ir::{self as ty, Interner};
22
use tracing::instrument;
33

44
use crate::delegate::SolverDelegate;
5-
use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
5+
use crate::solve::{EvalCtxt, Goal, QueryResult};
66

77
impl<D, I> EvalCtxt<'_, D>
88
where
@@ -14,17 +14,7 @@ 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)
26-
} else {
27-
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
28-
}
17+
let uv = goal.predicate.alias.expect_ct(self.cx());
18+
self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv)
2919
}
3020
}

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
@@ -383,19 +383,30 @@ where
383383

384384
// Finally we construct the actual value of the associated type.
385385
let term = match goal.predicate.alias.kind(cx) {
386-
ty::AliasTermKind::ProjectionTy { .. } => {
387-
cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
386+
ty::AliasTermKind::ProjectionTy { .. } => cx
387+
.type_of(target_item_def_id)
388+
.instantiate(cx, target_args)
389+
.skip_norm_wip()
390+
.into(),
391+
ty::AliasTermKind::ProjectionConst { .. }
392+
if cx.is_type_const(target_item_def_id) =>
393+
{
394+
cx.const_of_item(target_item_def_id)
395+
.instantiate(cx, target_args)
396+
.skip_norm_wip()
397+
.into()
388398
}
389399
ty::AliasTermKind::ProjectionConst { .. } => {
390-
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
400+
let uv = ty::UnevaluatedConst::new(
401+
target_item_def_id.try_into().unwrap(),
402+
target_args,
403+
);
404+
return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv);
391405
}
392406
kind => panic!("expected projection, found {kind:?}"),
393407
};
394408

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

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,10 @@ pub fn try_evaluate_const<'tcx>(
701701
// logic does not go through type system normalization. If it did this would
702702
// be a backwards compatibility problem as we do not enforce "syntactic" non-
703703
// usage of generic parameters like we do here.
704-
if uv.args.has_non_region_param() || uv.args.has_non_region_infer() {
704+
if uv.args.has_non_region_param()
705+
|| uv.args.has_non_region_infer()
706+
|| uv.args.has_non_region_placeholders()
707+
{
705708
return Err(EvaluateConstErr::HasGenericsOrInfers);
706709
}
707710

0 commit comments

Comments
 (0)