Skip to content

Commit 4eb272c

Browse files
authored
Unrolled build for #155341
Rollup merge of #155341 - khyperia:non-type-const, r=BoxyUwU generic_const_args: allow paths to non type consts tracking issue: #151972 Non type consts should be usable in the type system in `feature(generic_const_args)`. These are directly plugged into the constant evaluator, unlike type consts, which are attempted to be reasoned about by the type system. Inherent associated constants are not supported at this time, due to complications around how generic arguments are represented for them (it's currently a mess). The mess is being cleaned up (e.g. #154758), so instead of trying to hack support in before the refactoring is done, let's just wait to be able to implement it more cleanly. r? @BoxyUwU
2 parents e95e732 + cb2c5fc commit 4eb272c

38 files changed

Lines changed: 568 additions & 46 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
@@ -1200,6 +1200,44 @@ where
12001200
self.delegate.evaluate_const(param_env, uv)
12011201
}
12021202

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