Auto merge of #140549 - BoxyUwU:proper_const_norm, r=lcnr

Set groundwork for proper const normalization

r? lcnr

Updates a lot of our normalization/alias infrastructure to be setup to handle mgca aliases and normalization once const items are represented more like aliases than bodies. Inherent associated consts are still super busted, I didn't update the assertions that IACs the right arg setup because that winds up being somewhat involved to do *before* proper support for normalizing const aliases is implemented.

I dont *intend* for this to have any effect on stable. We continue normalizing via ctfe on stable and the codepaths in `project` for consts should only be reachable with mgca or ace.
This commit is contained in:
bors
2025-05-04 03:12:41 +00:00
20 changed files with 472 additions and 345 deletions

View File

@@ -51,7 +51,7 @@ pub use self::dyn_compatibility::{
pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
pub use self::normalize::NormalizeExt;
pub use self::project::{normalize_inherent_projection, normalize_projection_ty};
pub use self::project::{normalize_inherent_projection, normalize_projection_term};
pub use self::select::{
EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache,
SelectionContext,

View File

@@ -1,6 +1,7 @@
//! Deeply normalize types using the old trait solver.
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def::DefKind;
use rustc_infer::infer::at::At;
use rustc_infer::infer::{InferCtxt, InferOk};
use rustc_infer::traits::{
@@ -10,15 +11,12 @@ use rustc_macros::extension;
use rustc_middle::span_bug;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt,
TypingMode,
self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
TypeVisitableExt, TypingMode,
};
use tracing::{debug, instrument};
use super::{
BoundVarReplacer, PlaceholderReplacer, SelectionContext, project,
with_replaced_escaping_bound_vars,
};
use super::{BoundVarReplacer, PlaceholderReplacer, SelectionContext, project};
use crate::error_reporting::InferCtxtErrorExt;
use crate::error_reporting::traits::OverflowCause;
use crate::solve::NextSolverError;
@@ -178,6 +176,163 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) }
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_trait_projection(&mut self, proj: AliasTerm<'tcx>) -> Term<'tcx> {
if !proj.has_escaping_bound_vars() {
// When we don't have escaping bound vars we can normalize ambig aliases
// to inference variables (done in `normalize_projection_ty`). This would
// be wrong if there were escaping bound vars as even if we instantiated
// the bound vars with placeholders, we wouldn't be able to map them back
// after normalization succeeded.
//
// Also, as an optimization: when we don't have escaping bound vars, we don't
// need to replace them with placeholders (see branch below).
let proj = proj.fold_with(self);
project::normalize_projection_term(
self.selcx,
self.param_env,
proj,
self.cause.clone(),
self.depth,
self.obligations,
)
} else {
// If there are escaping bound vars, we temporarily replace the
// bound vars with placeholders. Note though, that in the case
// that we still can't project for whatever reason (e.g. self
// type isn't known enough), we *can't* register an obligation
// and return an inference variable (since then that obligation
// would have bound vars and that's a can of worms). Instead,
// we just give up and fall back to pretending like we never tried!
//
// Note: this isn't necessarily the final approach here; we may
// want to figure out how to register obligations with escaping vars
// or handle this some other way.
let infcx = self.selcx.infcx;
let (proj, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, proj);
let proj = proj.fold_with(self);
let normalized_term = project::opt_normalize_projection_term(
self.selcx,
self.param_env,
proj,
self.cause.clone(),
self.depth,
self.obligations,
)
.ok()
.flatten()
.unwrap_or(proj.to_term(infcx.tcx));
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
normalized_term,
)
}
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_inherent_projection(&mut self, inherent: AliasTerm<'tcx>) -> Term<'tcx> {
if !inherent.has_escaping_bound_vars() {
// When we don't have escaping bound vars we can normalize ambig aliases
// to inference variables (done in `normalize_projection_ty`). This would
// be wrong if there were escaping bound vars as even if we instantiated
// the bound vars with placeholders, we wouldn't be able to map them back
// after normalization succeeded.
//
// Also, as an optimization: when we don't have escaping bound vars, we don't
// need to replace them with placeholders (see branch below).
let inherent = inherent.fold_with(self);
project::normalize_inherent_projection(
self.selcx,
self.param_env,
inherent,
self.cause.clone(),
self.depth,
self.obligations,
)
} else {
let infcx = self.selcx.infcx;
let (inherent, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, inherent);
let inherent = inherent.fold_with(self);
let inherent = project::normalize_inherent_projection(
self.selcx,
self.param_env,
inherent,
self.cause.clone(),
self.depth,
self.obligations,
);
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
inherent,
)
}
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(self), ret)]
fn normalize_free_alias(&mut self, free: AliasTerm<'tcx>) -> Term<'tcx> {
let recursion_limit = self.cx().recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
self.selcx.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(free.into()),
self.cause.span,
false,
|diag| {
diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
},
);
}
let infcx = self.selcx.infcx;
self.obligations.extend(
// FIXME(BoxyUwU):
// FIXME(lazy_type_alias):
// It seems suspicious to instantiate the predicates with arguments that might be bound vars,
// we might wind up instantiating one of these bound vars underneath a hrtb.
infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map(
|(mut predicate, span)| {
if free.has_escaping_bound_vars() {
(predicate, ..) = BoundVarReplacer::replace_bound_vars(
infcx,
&mut self.universes,
predicate,
);
}
let mut cause = self.cause.clone();
cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, free.def_id));
Obligation::new(infcx.tcx, cause, self.param_env, predicate)
},
),
);
self.depth += 1;
let res = if free.kind(infcx.tcx).is_type() {
infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into()
} else {
// FIXME(mgca): once const items are actual aliases defined as equal to type system consts
// this should instead use that rather than evaluating.
super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env)
.super_fold_with(self)
.into()
};
self.depth -= 1;
res
}
}
impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
@@ -259,186 +414,63 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
}
}
ty::Projection if !data.has_escaping_bound_vars() => {
// When we don't have escaping bound vars we can normalize ambig aliases
// to inference variables (done in `normalize_projection_ty`). This would
// be wrong if there were escaping bound vars as even if we instantiated
// the bound vars with placeholders, we wouldn't be able to map them back
// after normalization succeeded.
//
// Also, as an optimization: when we don't have escaping bound vars, we don't
// need to replace them with placeholders (see branch below).
let data = data.fold_with(self);
let normalized_ty = project::normalize_projection_ty(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
self.obligations,
);
debug!(
?self.depth,
?ty,
?normalized_ty,
obligations.len = ?self.obligations.len(),
"AssocTypeNormalizer: normalized type"
);
normalized_ty.expect_type()
}
ty::Projection => {
// If there are escaping bound vars, we temporarily replace the
// bound vars with placeholders. Note though, that in the case
// that we still can't project for whatever reason (e.g. self
// type isn't known enough), we *can't* register an obligation
// and return an inference variable (since then that obligation
// would have bound vars and that's a can of worms). Instead,
// we just give up and fall back to pretending like we never tried!
//
// Note: this isn't necessarily the final approach here; we may
// want to figure out how to register obligations with escaping vars
// or handle this some other way.
let infcx = self.selcx.infcx;
let (data, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
let data = data.fold_with(self);
let normalized_ty = project::opt_normalize_projection_term(
self.selcx,
self.param_env,
data.into(),
self.cause.clone(),
self.depth,
self.obligations,
)
.ok()
.flatten()
.map(|term| term.expect_type())
.map(|normalized_ty| {
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
normalized_ty,
)
})
.unwrap_or_else(|| ty.super_fold_with(self));
debug!(
?self.depth,
?ty,
?normalized_ty,
obligations.len = ?self.obligations.len(),
"AssocTypeNormalizer: normalized type"
);
normalized_ty
}
ty::Free => {
let recursion_limit = self.cx().recursion_limit();
if !recursion_limit.value_within_limit(self.depth) {
self.selcx.infcx.err_ctxt().report_overflow_error(
OverflowCause::DeeplyNormalize(data.into()),
self.cause.span,
false,
|diag| {
diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
},
);
}
let infcx = self.selcx.infcx;
self.obligations.extend(
infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
|(mut predicate, span)| {
if data.has_escaping_bound_vars() {
(predicate, ..) = BoundVarReplacer::replace_bound_vars(
infcx,
&mut self.universes,
predicate,
);
}
let mut cause = self.cause.clone();
cause.map_code(|code| {
ObligationCauseCode::TypeAlias(code, span, data.def_id)
});
Obligation::new(infcx.tcx, cause, self.param_env, predicate)
},
),
);
self.depth += 1;
let res = infcx
.tcx
.type_of(data.def_id)
.instantiate(infcx.tcx, data.args)
.fold_with(self);
self.depth -= 1;
res
}
ty::Inherent if !data.has_escaping_bound_vars() => {
// This branch is *mostly* just an optimization: when we don't
// have escaping bound vars, we don't need to replace them with
// placeholders (see branch below). *Also*, we know that we can
// register an obligation to *later* project, since we know
// there won't be bound vars there.
let data = data.fold_with(self);
project::normalize_inherent_projection(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
self.obligations,
)
}
ty::Inherent => {
let infcx = self.selcx.infcx;
let (data, mapped_regions, mapped_types, mapped_consts) =
BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
let data = data.fold_with(self);
let ty = project::normalize_inherent_projection(
self.selcx,
self.param_env,
data,
self.cause.clone(),
self.depth,
self.obligations,
);
PlaceholderReplacer::replace_placeholders(
infcx,
mapped_regions,
mapped_types,
mapped_consts,
&self.universes,
ty,
)
}
ty::Projection => self.normalize_trait_projection(data.into()).expect_type(),
ty::Inherent => self.normalize_inherent_projection(data.into()).expect_type(),
ty::Free => self.normalize_free_alias(data.into()).expect_type(),
}
}
#[instrument(skip(self), level = "debug")]
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
let tcx = self.selcx.tcx();
if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &constant)
{
constant
if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &ct) {
return ct;
}
// Doing "proper" normalization of const aliases is inherently cyclic until const items
// are real aliases instead of having bodies. We gate proper const alias handling behind
// mgca to avoid breaking stable code, though this should become the "main" codepath long
// before mgca is stabilized.
//
// FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items
// are represented.
if tcx.features().min_generic_const_args() {
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
_ => return ct.super_fold_with(self),
};
let ct = match tcx.def_kind(uv.def) {
DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) {
DefKind::Trait => self.normalize_trait_projection(uv.into()),
DefKind::Impl { of_trait: false } => {
self.normalize_inherent_projection(uv.into())
}
kind => unreachable!(
"unexpected `DefKind` for const alias' resolution's parent def: {:?}",
kind
),
},
DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()),
kind => {
unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind)
}
};
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
ct.expect_const().super_fold_with(self)
} else {
let constant = constant.super_fold_with(self);
debug!(?constant, ?self.param_env);
with_replaced_escaping_bound_vars(
let ct = ct.super_fold_with(self);
return super::with_replaced_escaping_bound_vars(
self.selcx.infcx,
&mut self.universes,
constant,
|constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env),
ct,
|ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env),
)
.super_fold_with(self)
.super_fold_with(self);
// We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
// unnormalized after const evaluation returns.
}
}

View File

@@ -5,7 +5,6 @@ use std::ops::ControlFlow;
use rustc_data_structures::sso::SsoHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
@@ -172,6 +171,7 @@ pub(super) enum ProjectAndUnifyResult<'tcx> {
/// ```
/// If successful, this may result in additional obligations. Also returns
/// the projection cache key used to track these additional obligations.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx))]
pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -201,6 +201,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
/// If successful, this may result in additional obligations.
///
/// See [poly_project_and_unify_term] for an explanation of the return value.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx))]
fn project_and_unify_term<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -258,34 +259,28 @@ fn project_and_unify_term<'cx, 'tcx>(
/// there are unresolved type variables in the projection, we will
/// instantiate it with a fresh type variable `$X` and generate a new
/// obligation `<T as Trait>::Item == $X` for later.
pub fn normalize_projection_ty<'a, 'b, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
pub fn normalize_projection_term<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> Term<'tcx> {
opt_normalize_projection_term(
selcx,
param_env,
projection_ty.into(),
cause.clone(),
depth,
obligations,
)
.ok()
.flatten()
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
// information is available.
opt_normalize_projection_term(selcx, param_env, alias_term, cause.clone(), depth, obligations)
.ok()
.flatten()
.unwrap_or_else(move || {
// if we bottom out in ambiguity, create a type variable
// and a deferred predicate to resolve this when more type
// information is available.
selcx
.infcx
.projection_ty_to_infer(param_env, projection_ty, cause, depth + 1, obligations)
.into()
})
selcx
.infcx
.projection_term_to_infer(param_env, alias_term, cause, depth + 1, obligations)
.into()
})
}
/// The guts of `normalize`: normalize a specific projection like `<T
@@ -298,6 +293,7 @@ pub fn normalize_projection_ty<'a, 'b, 'tcx>(
/// often immediately appended to another obligations vector. So now this
/// function takes an obligations vector and appends to it directly, which is
/// slightly uglier but avoids the need for an extra short-lived allocation.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
@@ -456,6 +452,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
/// an error for this obligation, but we legitimately should not,
/// because it contains `[type error]`. Yuck! (See issue #29857 for
/// one case where this arose.)
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn normalize_to_error<'a, 'tcx>(
selcx: &SelectionContext<'a, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
@@ -469,9 +466,10 @@ fn normalize_to_error<'a, 'tcx>(
| ty::AliasTermKind::InherentTy
| ty::AliasTermKind::OpaqueTy
| ty::AliasTermKind::FreeTy => selcx.infcx.next_ty_var(cause.span).into(),
ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => {
selcx.infcx.next_const_var(cause.span).into()
}
ty::AliasTermKind::FreeConst
| ty::AliasTermKind::InherentConst
| ty::AliasTermKind::UnevaluatedConst
| ty::AliasTermKind::ProjectionConst => selcx.infcx.next_const_var(cause.span).into(),
};
let mut obligations = PredicateObligations::new();
obligations.push(Obligation {
@@ -484,36 +482,37 @@ fn normalize_to_error<'a, 'tcx>(
}
/// Confirm and normalize the given inherent projection.
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
alias_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> Ty<'tcx> {
) -> ty::Term<'tcx> {
let tcx = selcx.tcx();
if !tcx.recursion_limit().value_within_limit(depth) {
// Halt compilation because it is important that overflows never be masked.
tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow {
span: cause.span,
ty: alias_ty.to_string(),
ty: alias_term.to_string(),
});
}
let args = compute_inherent_assoc_ty_args(
let args = compute_inherent_assoc_term_args(
selcx,
param_env,
alias_ty,
alias_term,
cause.clone(),
depth,
obligations,
);
// Register the obligations arising from the impl and from the associated type itself.
let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args);
let predicates = tcx.predicates_of(alias_term.def_id).instantiate(tcx, args);
for (predicate, span) in predicates {
let predicate = normalize_with_depth_to(
selcx,
@@ -531,7 +530,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
// cause code, inherent projections will be printed with identity instantiation in
// diagnostics which is not ideal.
// Consider creating separate cause codes for this specific situation.
ObligationCauseCode::WhereClause(alias_ty.def_id, span),
ObligationCauseCode::WhereClause(alias_term.def_id, span),
);
obligations.push(Obligation::with_depth(
@@ -543,27 +542,33 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
));
}
let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
let term: Term<'tcx> = if alias_term.kind(tcx).is_type() {
tcx.type_of(alias_term.def_id).instantiate(tcx, args).into()
} else {
get_associated_const_value(selcx, alias_term.to_term(tcx).expect_const(), param_env).into()
};
let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
if ty.has_aliases() {
ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
let mut term = selcx.infcx.resolve_vars_if_possible(term);
if term.has_aliases() {
term =
normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, term, obligations);
}
ty
term
}
pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>(
selcx: &'a mut SelectionContext<'b, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
alias_ty: ty::AliasTy<'tcx>,
alias_term: ty::AliasTerm<'tcx>,
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut PredicateObligations<'tcx>,
) -> ty::GenericArgsRef<'tcx> {
let tcx = selcx.tcx();
let impl_def_id = tcx.parent(alias_ty.def_id);
let impl_def_id = tcx.parent(alias_term.def_id);
let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id);
let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
@@ -580,7 +585,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
// Infer the generic parameters of the impl by unifying the
// impl type with the self type of the projection.
let mut self_ty = alias_ty.self_ty();
let mut self_ty = alias_term.self_ty();
if !selcx.infcx.next_trait_solver() {
self_ty = normalize_with_depth_to(
selcx,
@@ -602,7 +607,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
}
}
alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
alias_term.rebase_inherent_args_onto_impl(impl_args, tcx)
}
enum Projected<'tcx> {
@@ -630,6 +635,7 @@ impl<'tcx> Progress<'tcx> {
///
/// IMPORTANT:
/// - `obligation` must be fully normalized
// FIXME(mgca): While this supports constants, it is only used for types by default right now
#[instrument(level = "info", skip(selcx))]
fn project<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -896,7 +902,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
// codegen (i.e., projection mode is not "any"), and the
// codegen (i.e., `TypingMode` is not `PostAnalysis`), and the
// impl's type is declared as default, then we disable
// projection (even if the trait ref is fully
// monomorphic). In the case where trait ref is not
@@ -1189,6 +1195,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
});
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@@ -1222,6 +1229,7 @@ fn confirm_candidate<'cx, 'tcx>(
result
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_select_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@@ -1873,6 +1881,7 @@ fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
.with_addl_obligations(nested)
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_param_env_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@@ -1926,9 +1935,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
) {
Ok(InferOk { value: _, obligations }) => {
nested_obligations.extend(obligations);
assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations);
// FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take
// a term instead.
assoc_term_own_obligations(selcx, obligation, &mut nested_obligations);
Progress { term: cache_entry.term, obligations: nested_obligations }
}
Err(e) => {
@@ -1942,6 +1949,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
}
}
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn confirm_impl_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
@@ -1955,8 +1963,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let param_env = obligation.param_env;
let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_ty) => assoc_ty,
let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
Ok(assoc_term) => assoc_term,
Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
};
@@ -1965,10 +1973,10 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// has impossible-to-satisfy predicates (since those were
// allowed in <https://github.com/rust-lang/rust/pull/135480>),
// or because the impl is literally missing the definition.
if !assoc_ty.item.defaultness(tcx).has_value() {
if !assoc_term.item.defaultness(tcx).has_value() {
debug!(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name(),
assoc_term.item.name(),
obligation.predicate
);
if tcx.impl_self_is_guaranteed_unsized(impl_def_id) {
@@ -1979,7 +1987,11 @@ fn confirm_impl_candidate<'cx, 'tcx>(
return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
} else {
return Ok(Projected::Progress(Progress {
term: Ty::new_misc_error(tcx).into(),
term: if obligation.predicate.kind(tcx).is_type() {
Ty::new_misc_error(tcx).into()
} else {
ty::Const::new_misc_error(tcx).into()
},
obligations: nested,
}));
}
@@ -1992,27 +2004,32 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// * `args` is `[u32]`
// * `args` ends up as `[u32, S]`
let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node);
let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
let did = assoc_ty.item.def_id;
let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
let uv = ty::UnevaluatedConst::new(did, identity_args);
ty::EarlyBinder::bind(ty::Const::new_unevaluated(tcx, uv).into())
let term = if obligation.predicate.kind(tcx).is_type() {
tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into())
} else {
tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
ty::EarlyBinder::bind(
get_associated_const_value(
selcx,
obligation.predicate.to_term(tcx).expect_const(),
param_env,
)
.into(),
)
};
let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
let err = Ty::new_error_with_message(
tcx,
obligation.cause.span,
"impl item and trait item have different parameters",
);
Progress { term: err.into(), obligations: nested }
let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) {
let msg = "impl item and trait item have different parameters";
let span = obligation.cause.span;
let err = if obligation.predicate.kind(tcx).is_type() {
Ty::new_error_with_message(tcx, span, msg).into()
} else {
ty::Const::new_error_with_message(tcx, span, msg).into()
};
Progress { term: err, obligations: nested }
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
assoc_term_own_obligations(selcx, obligation, &mut nested);
Progress { term: term.instantiate(tcx, args), obligations: nested }
};
Ok(Projected::Progress(progress))
@@ -2020,7 +2037,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
// Get obligations corresponding to the predicates from the where-clause of the
// associated type itself.
fn assoc_ty_own_obligations<'cx, 'tcx>(
// FIXME(mgca): While this supports constants, it is only used for types by default right now
fn assoc_term_own_obligations<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTermObligation<'tcx>,
nested: &mut PredicateObligations<'tcx>,
@@ -2090,3 +2108,15 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
})
}
}
fn get_associated_const_value<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
alias_ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> ty::Const<'tcx> {
// FIXME(mgca): We shouldn't be invoking ctfe here, instead const items should be aliases to type
// system consts that we can retrieve with some `query const_arg_of_alias` query. Evaluating the
// constant is "close enough" to getting the actual rhs of the const item for now even if it might
// lead to some cycles
super::evaluate_const(selcx.infcx, alias_ct, param_env)
}

View File

@@ -406,7 +406,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
let mut assume = predicate.trait_ref.args.const_at(2);
// FIXME(mgca): We should shallowly normalize this.
if self.tcx().features().generic_const_exprs() {
assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env)
}

View File

@@ -6,6 +6,7 @@
use std::iter;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
use rustc_middle::bug;
@@ -486,7 +487,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
/// Pushes the obligations required for an inherent alias to be WF
/// into `self.out`.
// FIXME(inherent_associated_types): Merge this function with `fn compute_alias`.
fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTerm<'tcx>) {
// An inherent projection is well-formed if
//
// (a) its predicates hold (*)
@@ -498,7 +499,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
if !data.self_ty().has_escaping_bound_vars() {
// FIXME(inherent_associated_types): Should this happen inside of a snapshot?
// FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm!
let args = traits::project::compute_inherent_assoc_ty_args(
let args = traits::project::compute_inherent_assoc_term_args(
&mut traits::SelectionContext::new(self.infcx),
self.param_env,
data,
@@ -776,7 +777,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
self.out.extend(obligations);
}
ty::Alias(ty::Inherent, data) => {
self.add_wf_preds_for_inherent_projection(data);
self.add_wf_preds_for_inherent_projection(data.into());
return; // Subtree handled by compute_inherent_projection.
}
@@ -961,9 +962,6 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
match c.kind() {
ty::ConstKind::Unevaluated(uv) => {
if !c.has_escaping_bound_vars() {
let obligations = self.nominal_obligations(uv.def, uv.args);
self.out.extend(obligations);
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::ClauseKind::ConstEvaluatable(c),
));
@@ -975,6 +973,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
self.param_env,
predicate,
));
if tcx.def_kind(uv.def) == DefKind::AssocConst
&& tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false })
{
self.add_wf_preds_for_inherent_projection(uv.into());
return; // Subtree is handled by above function
} else {
let obligations = self.nominal_obligations(uv.def, uv.args);
self.out.extend(obligations);
}
}
}
ty::ConstKind::Infer(_) => {