Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
check_incompatible_features(sess, features);
check_dependent_features(sess, features);
check_new_solver_banned_features(sess, features);
check_features_requiring_new_solver(sess, features);

let mut visitor = PostExpansionVisitor { sess, features };

Expand Down Expand Up @@ -738,3 +739,25 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) {
});
}
}

fn check_features_requiring_new_solver(sess: &Session, features: &Features) {
if sess.opts.unstable_opts.next_solver.globally {
return;
}

// Require the new solver with GCA, because the old solver can't implement GCA correctly as it
// does not support normalization obligations for free and inherent consts.
if let Some(gca_span) = features
.enabled_lang_features()
.iter()
.find(|feat| feat.gate_name == sym::generic_const_args)
.map(|feat| feat.attr_sp)
{
#[allow(rustc::symbol_intern_string_literal)]
sess.dcx().emit_err(errors::MissingDependentFeatures {
parent_span: gca_span,
parent: sym::generic_const_exprs,
missing: String::from("-Znext-solver=globally"),
});
}
}
9 changes: 8 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
34 changes: 34 additions & 0 deletions compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,40 @@ where
self.delegate.evaluate_const(param_env, uv)
}

pub(super) fn evaluate_const_and_instantiate_normalizes_to_term(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
uv: ty::UnevaluatedConst<I>,
) -> QueryResult<I> {
match self.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 => {
// HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary,
// `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want
// to check `has_non_region_infer` against the type with vars resolved (i.e. check
// if there are vars we failed to resolve), so we need to call it again here.
// Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and
// HasInfers or something, make evaluate_const return that, and make this branch be
// based on that, rather than checking `has_non_region_infer`.
if self.resolve_vars_if_possible(uv).has_non_region_infer() {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
} else {
// We do not instantiate to the `uv` passed in, but rather
// `goal.predicate.alias`. The `uv` passed in might correspond to the `impl`
// form of a constant (with generic arguments corresponding to the impl block),
// however, we want to structurally instantiate to the original, non-rebased,
// trait `Self` form of the constant (with generic arguments being the trait
// `Self` type).
self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
Comment thread
BoxyUwU marked this conversation as resolved.
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
}
}

pub(super) fn is_transmutable(
&mut self,
src: I::Ty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,23 @@ where
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
) -> QueryResult<I> {
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:
// when a constant is too generic to be evaluated, the legacy behavior is to return
// Ambiguous, whereas evaluate_const_and_instantiate_normalizes_to_term structurally
// instantiates to itself and returns Yes (if there are no inference variables)
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_normalizes_to_term(goal, uv)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,20 @@ 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 } 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_normalizes_to_term(
goal,
free_alias.expect_ct(cx),
);
}
kind => panic!("expected free alias, found {kind:?}"),
};

self.instantiate_normalizes_to_term(goal, actual);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,23 @@ 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 } 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);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
Expand Down
25 changes: 18 additions & 7 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,19 +384,30 @@ 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::ProjectionTy { .. } => cx
.type_of(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 { .. } => {
cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into())
let uv = ty::UnevaluatedConst::new(
target_item_def_id.try_into().unwrap(),
target_args,
);
return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv);
}
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)
})
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1060,7 +1060,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> 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),
));
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ pub trait Interner:
fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId)
-> ty::EarlyBinder<Self, Self::Ty>;
fn is_type_const(self, def_id: Self::DefId) -> bool;
fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Const>;
fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind;

Expand Down
22 changes: 19 additions & 3 deletions compiler/rustc_type_ir/src/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -732,16 +732,32 @@ impl<I: Interner> AliasTerm<I> {
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<I> {
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<I> {
self.kind
Expand Down
2 changes: 2 additions & 0 deletions src/doc/rustc-dev-guide/src/feature-gate-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ in `check_crate` and its AST visitor.
(declared in `rustc_feature::INCOMPATIBLE_FEATURES`) are not used together.
- `check_new_solver_banned_features`: Bans features incompatible with
compiler mode for the next trait solver.
- `check_features_requiring_new_solver`: Requires the new trait solver for
features incompatible with the old solver.
- **Parser-gated spans**: Processes the `GatedSpans` recorded during parsing
(see [Checking `GatedSpans`](#checking-gatedspans)).

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
error[E0284]: type annotations needed for `([(); _], [(); 10])`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:29:9
|
LL | let (mut arr, mut arr_with_weird_len) = free();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point
|
note: required by a const generic parameter in `free`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:24:9
|
LL | fn free<const N: usize>() -> ([(); N], [(); FREE::<N>]) {
| ^^^^^^^^^^^^^^ required by this const generic parameter in `free`
help: consider giving this pattern a type, where the value of const parameter `N` is specified
|
LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = free();
| +++++++++++++

error[E0271]: type mismatch resolving `2 == 10`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:35:45
|
LL | let (mut arr, mut arr_with_weird_len) = free();
| ^^^^^^ types differ

error[E0284]: type annotations needed for `([(); _], [(); 10])`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:46:9
|
LL | let (mut arr, mut arr_with_weird_len) = proj();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point
|
note: required by a const generic parameter in `proj`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:41:9
|
LL | fn proj<const N: usize>() -> ([(); N], [(); <S as Trait>::PROJ::<N>]) {
| ^^^^^^^^^^^^^^ required by this const generic parameter in `proj`
help: consider giving this pattern a type, where the value of const parameter `N` is specified
|
LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = proj();
| +++++++++++++

error[E0271]: type mismatch resolving `2 == 10`
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:52:45
|
LL | let (mut arr, mut arr_with_weird_len) = proj();
| ^^^^^^ types differ

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0271, E0284.
For more information about an error, try `rustc --explain E0271`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: `generic_const_exprs` requires -Znext-solver=globally to be enabled
--> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:9:12
|
LL | #![feature(generic_const_args)]
| ^^^^^^^^^^^^^^^^^^
|
= help: enable all of these features

error: aborting due to 1 previous error

Loading
Loading