Auto merge of #93285 - JulianKnodt:const_eq_2, r=oli-obk

Continue work on associated const equality

This actually implements some more complex logic for assigning associated consts to values.
Inside of projection candidates, it now defers to a separate function for either consts or
types. To reduce amount of code, projections are now generic over T, where T is either a Type or
a Const. I can add some comments back later, but this was the fastest way to implement it.

It also now finds the correct type of consts in type_of.

---

The current main TODO is finding the const of the def id for the LeafDef.

Right now it works if the function isn't called, but once you use the trait impl with the bound it fails inside projection.
I was hoping to get some help in getting the `&'tcx ty::Const<'tcx>`, in addition to a bunch of other `todo!()`s which I think may not be hit.

r? `@oli-obk`

Updates #92827
This commit is contained in:
bors 2022-02-01 23:18:01 +00:00
commit 1ea4851715
19 changed files with 379 additions and 181 deletions

View File

@ -2203,6 +2203,12 @@ impl TypeBinding<'_> {
_ => panic!("expected equality type binding for parenthesized generic args"),
}
}
pub fn opt_const(&self) -> Option<&'_ AnonConst> {
match self.kind {
TypeBindingKind::Equality { term: Term::Const(ref c) } => Some(c),
_ => None,
}
}
}
#[derive(Debug)]

View File

@ -286,6 +286,26 @@ impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> {
}
}
impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> {
fn to_trace(
tcx: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
match (a, b) {
(ty::Term::Ty(a), ty::Term::Ty(b)) => {
ToTrace::to_trace(tcx, cause, a_is_expected, a, b)
}
(ty::Term::Const(a), ty::Term::Const(b)) => {
ToTrace::to_trace(tcx, cause, a_is_expected, a, b)
}
(_, _) => todo!(),
}
}
}
impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,

View File

@ -93,7 +93,7 @@ pub enum ProjectionCacheEntry<'tcx> {
Recur,
Error,
NormalizedTy {
ty: NormalizedTy<'tcx>,
ty: Normalized<'tcx, ty::Term<'tcx>>,
/// If we were able to successfully evaluate the
/// corresponding cache entry key during predicate
/// evaluation, then this field stores the final
@ -174,7 +174,11 @@ impl<'tcx> ProjectionCache<'_, 'tcx> {
}
/// Indicates that `key` was normalized to `value`.
pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
pub fn insert_term(
&mut self,
key: ProjectionCacheKey<'tcx>,
value: Normalized<'tcx, ty::Term<'tcx>>,
) {
debug!(
"ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
key, value

View File

@ -867,6 +867,9 @@ impl<'tcx> Term<'tcx> {
pub fn ty(&self) -> Option<Ty<'tcx>> {
if let Term::Ty(ty) = self { Some(ty) } else { None }
}
pub fn ct(&self) -> Option<&'tcx Const<'tcx>> {
if let Term::Const(c) = self { Some(c) } else { None }
}
}
/// This kind of predicate has no *direct* correspondent in the

View File

@ -1373,19 +1373,31 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
| ObligationCauseCode::ObjectCastObligation(_)
| ObligationCauseCode::OpaqueType
);
// FIXME(associated_const_equality): Handle Consts here
let data_ty = data.term.ty().unwrap();
if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp(
is_normalized_ty_expected,
normalized_ty,
data_ty,
data.term,
) {
values = Some(infer::ValuePairs::Types(ExpectedFound::new(
is_normalized_ty_expected,
normalized_ty,
data_ty,
)));
values = Some(match (normalized_ty, data.term) {
(ty::Term::Ty(normalized_ty), ty::Term::Ty(ty)) => {
infer::ValuePairs::Types(ExpectedFound::new(
is_normalized_ty_expected,
normalized_ty,
ty,
))
}
(ty::Term::Const(normalized_ct), ty::Term::Const(ct)) => {
infer::ValuePairs::Consts(ExpectedFound::new(
is_normalized_ty_expected,
normalized_ct,
ct,
))
}
(_, _) => span_bug!(
obligation.cause.span,
"found const or type where other expected"
),
});
err_buf = error;
err = &err_buf;
}

View File

@ -2496,7 +2496,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
trait_pred,
normalized_ty,
normalized_ty.ty().unwrap(),
);
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
if self.predicate_may_hold(&try_obligation)

View File

@ -200,7 +200,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
debug!(?normalized_ty);
normalized_ty
normalized_ty.ty().unwrap()
}
fn register_predicate_obligation(

View File

@ -22,12 +22,13 @@ use crate::traits::error_reporting::InferCtxtExt as _;
use rustc_data_structures::sso::SsoHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorReported;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::sym;
use std::collections::BTreeMap;
@ -44,7 +45,7 @@ pub(super) struct InProgress;
/// When attempting to resolve `<T as TraitRef>::Name` ...
#[derive(Debug)]
pub enum ProjectionTyError<'tcx> {
pub enum ProjectionError<'tcx> {
/// ...we found multiple sources of information and couldn't resolve the ambiguity.
TooManyCandidates,
@ -53,7 +54,7 @@ pub enum ProjectionTyError<'tcx> {
}
#[derive(PartialEq, Eq, Debug)]
enum ProjectionTyCandidate<'tcx> {
enum ProjectionCandidate<'tcx> {
/// From a where-clause in the env or object type
ParamEnv(ty::PolyProjectionPredicate<'tcx>),
@ -67,28 +68,28 @@ enum ProjectionTyCandidate<'tcx> {
Select(Selection<'tcx>),
}
enum ProjectionTyCandidateSet<'tcx> {
enum ProjectionCandidateSet<'tcx> {
None,
Single(ProjectionTyCandidate<'tcx>),
Single(ProjectionCandidate<'tcx>),
Ambiguous,
Error(SelectionError<'tcx>),
}
impl<'tcx> ProjectionTyCandidateSet<'tcx> {
impl<'tcx> ProjectionCandidateSet<'tcx> {
fn mark_ambiguous(&mut self) {
*self = ProjectionTyCandidateSet::Ambiguous;
*self = ProjectionCandidateSet::Ambiguous;
}
fn mark_error(&mut self, err: SelectionError<'tcx>) {
*self = ProjectionTyCandidateSet::Error(err);
*self = ProjectionCandidateSet::Error(err);
}
// Returns true if the push was successful, or false if the candidate
// was discarded -- this could be because of ambiguity, or because
// a higher-priority candidate is already there.
fn push_candidate(&mut self, candidate: ProjectionTyCandidate<'tcx>) -> bool {
use self::ProjectionTyCandidate::*;
use self::ProjectionTyCandidateSet::*;
fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool {
use self::ProjectionCandidate::*;
use self::ProjectionCandidateSet::*;
// This wacky variable is just used to try and
// make code readable and avoid confusing paths.
@ -196,7 +197,9 @@ fn project_and_unify_type<'cx, 'tcx>(
debug!(?obligation, "project_and_unify_type");
let mut obligations = vec![];
let normalized_ty = match opt_normalize_projection_type(
let infcx = selcx.infcx();
let normalized = match opt_normalize_projection_type(
selcx,
obligation.param_env,
obligation.predicate.projection_ty,
@ -208,13 +211,11 @@ fn project_and_unify_type<'cx, 'tcx>(
Ok(None) => return Ok(Ok(None)),
Err(InProgress) => return Ok(Err(InProgress)),
};
debug!(?normalized_ty, ?obligations, "project_and_unify_type result");
let infcx = selcx.infcx();
// FIXME(associated_const_equality): Handle consts here as well as types.
let obligation_pred_ty = obligation.predicate.term.ty().unwrap();
match infcx.at(&obligation.cause, obligation.param_env).eq(normalized_ty, obligation_pred_ty) {
debug!(?normalized, ?obligations, "project_and_unify_type result");
match infcx
.at(&obligation.cause, obligation.param_env)
.eq(normalized, obligation.predicate.term)
{
Ok(InferOk { obligations: inferred_obligations, value: () }) => {
obligations.extend(inferred_obligations);
Ok(Ok(Some(obligations)))
@ -441,7 +442,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
obligations.len = ?self.obligations.len(),
"AssocTypeNormalizer: normalized type"
);
normalized_ty
normalized_ty.ty().unwrap()
}
ty::Projection(data) => {
@ -471,6 +472,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
)
.ok()
.flatten()
.map(|term| term.ty().unwrap())
.map(|normalized_ty| {
PlaceholderReplacer::replace_placeholders(
infcx,
@ -793,7 +795,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Ty<'tcx> {
) -> Term<'tcx> {
opt_normalize_projection_type(
selcx,
param_env,
@ -809,7 +811,10 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
// and a deferred predicate to resolve this when more type
// information is available.
selcx.infcx().infer_projection(param_env, projection_ty, cause, depth + 1, obligations)
selcx
.infcx()
.infer_projection(param_env, projection_ty, cause, depth + 1, obligations)
.into()
})
}
@ -831,7 +836,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
cause: ObligationCause<'tcx>,
depth: usize,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Result<Option<Ty<'tcx>>, InProgress> {
) -> Result<Option<Term<'tcx>>, InProgress> {
let infcx = selcx.infcx();
// Don't use the projection cache in intercrate mode -
// the `infcx` may be re-used between intercrate in non-intercrate
@ -907,15 +912,15 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
debug!("opt_normalize_projection_type: found error");
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
obligations.extend(result.obligations);
return Ok(Some(result.value));
return Ok(Some(result.value.into()));
}
}
let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
match project_type(selcx, &obligation) {
Ok(ProjectedTy::Progress(Progress {
ty: projected_ty,
match project(selcx, &obligation) {
Ok(Projected::Progress(Progress {
term: projected_term,
obligations: mut projected_obligations,
})) => {
// if projection succeeded, then what we get out of this
@ -923,10 +928,9 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
// an impl, where-clause etc) and hence we must
// re-normalize it
let projected_ty = selcx.infcx().resolve_vars_if_possible(projected_ty);
debug!(?projected_ty, ?depth, ?projected_obligations);
let projected_term = selcx.infcx().resolve_vars_if_possible(projected_term);
let mut result = if projected_ty.has_projections() {
let mut result = if projected_term.has_projections() {
let mut normalizer = AssocTypeNormalizer::new(
selcx,
param_env,
@ -934,13 +938,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
depth + 1,
&mut projected_obligations,
);
let normalized_ty = normalizer.fold(projected_ty);
debug!(?normalized_ty, ?depth);
let normalized_ty = normalizer.fold(projected_term);
Normalized { value: normalized_ty, obligations: projected_obligations }
} else {
Normalized { value: projected_ty, obligations: projected_obligations }
Normalized { value: projected_term, obligations: projected_obligations }
};
let mut deduped: SsoHashSet<_> = Default::default();
@ -952,28 +954,27 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
});
if use_cache {
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
}
obligations.extend(result.obligations);
Ok(Some(result.value))
Ok(Some(result.value.into()))
}
Ok(ProjectedTy::NoProgress(projected_ty)) => {
debug!(?projected_ty, "opt_normalize_projection_type: no progress");
Ok(Projected::NoProgress(projected_ty)) => {
let result = Normalized { value: projected_ty, obligations: vec![] };
if use_cache {
infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone());
infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone());
}
// No need to extend `obligations`.
Ok(Some(result.value))
}
Err(ProjectionTyError::TooManyCandidates) => {
Err(ProjectionError::TooManyCandidates) => {
debug!("opt_normalize_projection_type: too many candidates");
if use_cache {
infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key);
}
Ok(None)
}
Err(ProjectionTyError::TraitSelectionError(_)) => {
Err(ProjectionError::TraitSelectionError(_)) => {
debug!("opt_normalize_projection_type: ERROR");
// if we got an error processing the `T as Trait` part,
// just return `ty::err` but add the obligation `T :
@ -985,7 +986,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
}
let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth);
obligations.extend(result.obligations);
Ok(Some(result.value))
Ok(Some(result.value.into()))
}
}
}
@ -1032,30 +1033,22 @@ fn normalize_to_error<'a, 'tcx>(
Normalized { value: new_value, obligations: vec![trait_obligation] }
}
enum ProjectedTy<'tcx> {
enum Projected<'tcx> {
Progress(Progress<'tcx>),
NoProgress(Ty<'tcx>),
NoProgress(ty::Term<'tcx>),
}
struct Progress<'tcx> {
ty: Ty<'tcx>,
term: ty::Term<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
}
impl<'tcx> Progress<'tcx> {
fn error(tcx: TyCtxt<'tcx>) -> Self {
Progress { ty: tcx.ty_error(), obligations: vec![] }
Progress { term: tcx.ty_error().into(), obligations: vec![] }
}
fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
debug!(
self.obligations.len = ?self.obligations.len(),
obligations.len = obligations.len(),
"with_addl_obligations"
);
debug!(?self.obligations, ?obligations, "with_addl_obligations");
self.obligations.append(&mut obligations);
self
}
@ -1066,22 +1059,21 @@ impl<'tcx> Progress<'tcx> {
/// IMPORTANT:
/// - `obligation` must be fully normalized
#[tracing::instrument(level = "info", skip(selcx))]
fn project_type<'cx, 'tcx>(
fn project<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
) -> Result<ProjectedTy<'tcx>, ProjectionTyError<'tcx>> {
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) {
debug!("project: overflow!");
// This should really be an immediate error, but some existing code
// relies on being able to recover from this.
return Err(ProjectionTyError::TraitSelectionError(SelectionError::Overflow));
return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow));
}
if obligation.predicate.references_error() {
return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx())));
return Ok(Projected::Progress(Progress::error(selcx.tcx())));
}
let mut candidates = ProjectionTyCandidateSet::None;
let mut candidates = ProjectionCandidateSet::None;
// Make sure that the following procedures are kept in order. ParamEnv
// needs to be first because it has highest priority, and Select checks
@ -1092,7 +1084,7 @@ fn project_type<'cx, 'tcx>(
assemble_candidates_from_object_ty(selcx, obligation, &mut candidates);
if let ProjectionTyCandidateSet::Single(ProjectionTyCandidate::Object(_)) = candidates {
if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates {
// Avoid normalization cycle from selection (see
// `assemble_candidates_from_object_ty`).
// FIXME(lazy_normalization): Lazy normalization should save us from
@ -1102,19 +1094,22 @@ fn project_type<'cx, 'tcx>(
};
match candidates {
ProjectionTyCandidateSet::Single(candidate) => {
Ok(ProjectedTy::Progress(confirm_candidate(selcx, obligation, candidate)))
ProjectionCandidateSet::Single(candidate) => {
Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
}
ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress(
ProjectionCandidateSet::None => Ok(Projected::NoProgress(
// FIXME(associated_const_generics): this may need to change in the future?
// need to investigate whether or not this is fine.
selcx
.tcx()
.mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs),
.mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs)
.into(),
)),
// Error occurred while trying to processing impls.
ProjectionTyCandidateSet::Error(e) => Err(ProjectionTyError::TraitSelectionError(e)),
ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)),
// Inherent ambiguity that prevents us from even enumerating the
// candidates.
ProjectionTyCandidateSet::Ambiguous => Err(ProjectionTyError::TooManyCandidates),
ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates),
}
}
@ -1124,14 +1119,13 @@ fn project_type<'cx, 'tcx>(
fn assemble_candidates_from_param_env<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
) {
debug!("assemble_candidates_from_param_env(..)");
assemble_candidates_from_predicates(
selcx,
obligation,
candidate_set,
ProjectionTyCandidate::ParamEnv,
ProjectionCandidate::ParamEnv,
obligation.param_env.caller_bounds().iter(),
false,
);
@ -1150,7 +1144,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>(
fn assemble_candidates_from_trait_def<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
) {
debug!("assemble_candidates_from_trait_def(..)");
@ -1173,7 +1167,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
selcx,
obligation,
candidate_set,
ProjectionTyCandidate::TraitDef,
ProjectionCandidate::TraitDef,
bounds.iter(),
true,
)
@ -1191,7 +1185,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
fn assemble_candidates_from_object_ty<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
) {
debug!("assemble_candidates_from_object_ty(..)");
@ -1218,7 +1212,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
selcx,
obligation,
candidate_set,
ProjectionTyCandidate::Object,
ProjectionCandidate::Object,
env_predicates,
false,
);
@ -1231,14 +1225,13 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
fn assemble_candidates_from_predicates<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>,
env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
potentially_unnormalized_candidates: bool,
) {
let infcx = selcx.infcx();
for predicate in env_predicates {
debug!(?predicate);
let bound_predicate = predicate.kind();
if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() {
let data = bound_predicate.rebind(data);
@ -1253,8 +1246,6 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
)
});
debug!(?data, ?is_match, ?same_def_id);
if is_match {
candidate_set.push_candidate(ctor(data));
@ -1275,7 +1266,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
fn assemble_candidates_from_impls<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionTyCandidateSet<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
) {
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
// start out by selecting the predicate `T as TraitRef<...>`:
@ -1299,10 +1290,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
super::ImplSource::Closure(_)
| super::ImplSource::Generator(_)
| super::ImplSource::FnPointer(_)
| super::ImplSource::TraitAlias(_) => {
debug!(?impl_source);
true
}
| super::ImplSource::TraitAlias(_) => true,
super::ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
@ -1327,7 +1315,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// NOTE: This should be kept in sync with the similar code in
// `rustc_ty_utils::instance::resolve_associated_item()`.
let node_item =
assoc_ty_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id)
assoc_def(selcx, impl_data.impl_def_id, obligation.predicate.item_def_id)
.map_err(|ErrorReported| ())?;
if node_item.is_final() {
@ -1500,7 +1488,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
};
if eligible {
if candidate_set.push_candidate(ProjectionTyCandidate::Select(impl_source)) {
if candidate_set.push_candidate(ProjectionCandidate::Select(impl_source)) {
Ok(())
} else {
Err(())
@ -1514,30 +1502,32 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
fn confirm_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
candidate: ProjectionTyCandidate<'tcx>,
candidate: ProjectionCandidate<'tcx>,
) -> Progress<'tcx> {
debug!(?obligation, ?candidate, "confirm_candidate");
let mut progress = match candidate {
ProjectionTyCandidate::ParamEnv(poly_projection)
| ProjectionTyCandidate::Object(poly_projection) => {
ProjectionCandidate::ParamEnv(poly_projection)
| ProjectionCandidate::Object(poly_projection) => {
confirm_param_env_candidate(selcx, obligation, poly_projection, false)
}
ProjectionTyCandidate::TraitDef(poly_projection) => {
ProjectionCandidate::TraitDef(poly_projection) => {
confirm_param_env_candidate(selcx, obligation, poly_projection, true)
}
ProjectionTyCandidate::Select(impl_source) => {
ProjectionCandidate::Select(impl_source) => {
confirm_select_candidate(selcx, obligation, impl_source)
}
};
// When checking for cycle during evaluation, we compare predicates with
// "syntactic" equality. Since normalization generally introduces a type
// with new region variables, we need to resolve them to existing variables
// when possible for this to work. See `auto-trait-projection-recursion.rs`
// for a case where this matters.
if progress.ty.has_infer_regions() {
progress.ty = OpportunisticRegionResolver::new(selcx.infcx()).fold_ty(progress.ty);
if progress.term.has_infer_regions() {
progress.term =
progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx()));
}
progress
}
@ -1804,7 +1794,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
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.
Progress { ty: cache_entry.term.ty().unwrap(), obligations: nested_obligations }
Progress { term: cache_entry.term, obligations: nested_obligations }
}
Err(e) => {
let msg = format!(
@ -1813,7 +1803,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
);
debug!("confirm_param_env_candidate: {}", msg);
let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg);
Progress { ty: err, obligations: vec![] }
Progress { term: err.into(), obligations: vec![] }
}
}
}
@ -1830,9 +1820,9 @@ 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 assoc_ty_def(selcx, impl_def_id, assoc_item_id) {
let assoc_ty = match assoc_def(selcx, impl_def_id, assoc_item_id) {
Ok(assoc_ty) => assoc_ty,
Err(ErrorReported) => return Progress { ty: tcx.ty_error(), obligations: nested },
Err(ErrorReported) => return Progress { term: tcx.ty_error().into(), obligations: nested },
};
if !assoc_ty.item.defaultness.has_value() {
@ -1844,7 +1834,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name, obligation.predicate
);
return Progress { ty: tcx.ty_error(), obligations: nested };
return Progress { term: tcx.ty_error().into(), obligations: nested };
}
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
@ -1856,15 +1846,25 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let substs =
translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node);
let ty = tcx.type_of(assoc_ty.item.def_id);
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
let term: ty::Term<'tcx> = if is_const {
let identity_substs =
crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id);
let val = ty::ConstKind::Unevaluated(ty::Unevaluated::new(did, identity_substs));
tcx.mk_const(ty::Const { ty, val }).into()
} else {
ty.into()
};
if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() {
let err = tcx.ty_error_with_message(
obligation.cause.span,
"impl item and trait item have different parameter counts",
);
Progress { ty: err, obligations: nested }
Progress { term: err.into(), obligations: nested }
} else {
assoc_ty_own_obligations(selcx, obligation, &mut nested);
Progress { ty: ty.subst(tcx, substs), obligations: nested }
Progress { term: term.subst(tcx, substs), obligations: nested }
}
}
@ -1905,10 +1905,10 @@ fn assoc_ty_own_obligations<'cx, 'tcx>(
///
/// Based on the "projection mode", this lookup may in fact only examine the
/// topmost impl. See the comments for `Reveal` for more details.
fn assoc_ty_def(
fn assoc_def(
selcx: &SelectionContext<'_, '_>,
impl_def_id: DefId,
assoc_ty_def_id: DefId,
assoc_def_id: DefId,
) -> Result<specialization_graph::LeafDef, ErrorReported> {
let tcx = selcx.tcx();
let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
@ -1920,7 +1920,7 @@ fn assoc_ty_def(
// for the associated item at the given impl.
// If there is no such item in that impl, this function will fail with a
// cycle error if the specialization graph is currently being built.
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_ty_def_id) {
if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
let item = tcx.associated_item(impl_item_id);
let impl_node = specialization_graph::Node::Impl(impl_def_id);
return Ok(specialization_graph::LeafDef {
@ -1931,7 +1931,7 @@ fn assoc_ty_def(
}
let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_ty_def_id) {
if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
Ok(assoc_item)
} else {
// This is saying that neither the trait nor
@ -1942,7 +1942,7 @@ fn assoc_ty_def(
// should have failed in astconv.
bug!(
"No associated type `{}` for {}",
tcx.item_name(assoc_ty_def_id),
tcx.item_name(assoc_def_id),
tcx.def_path_str(impl_def_id)
)
}

View File

@ -36,7 +36,10 @@ fn normalize_projection_ty<'tcx>(
&mut obligations,
);
fulfill_cx.register_predicate_obligations(infcx, obligations);
Ok(NormalizationResult { normalized_ty: answer })
// FIXME(associated_const_equality): All users of normalize_projection_ty expected
// a type, but there is the possibility it could've been a const now. Maybe change
// it to a Term later?
Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() })
},
)
}

View File

@ -1145,7 +1145,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
let assoc_ty = tcx
let assoc_item = tcx
.associated_items(candidate.def_id())
.filter_by_name_unhygienic(assoc_ident.name)
.find(|i| {
@ -1153,35 +1153,32 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
})
.expect("missing associated type");
// FIXME(associated_const_equality): need to handle assoc_consts here as well.
if assoc_ty.kind == ty::AssocKind::Const {
tcx.sess
.struct_span_err(path_span, &format!("associated const equality is incomplete"))
.span_label(path_span, "cannot yet relate associated const")
.emit();
return Err(ErrorReported);
}
if !assoc_ty.vis.is_accessible_from(def_scope, tcx) {
if !assoc_item.vis.is_accessible_from(def_scope, tcx) {
let kind = match assoc_item.kind {
ty::AssocKind::Type => "type",
ty::AssocKind::Const => "const",
_ => unreachable!(),
};
tcx.sess
.struct_span_err(
binding.span,
&format!("associated type `{}` is private", binding.item_name),
&format!("associated {kind} `{}` is private", binding.item_name),
)
.span_label(binding.span, "private associated type")
.span_label(binding.span, &format!("private associated {kind}"))
.emit();
}
tcx.check_stability(assoc_ty.def_id, Some(hir_ref_id), binding.span, None);
tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
if !speculative {
dup_bindings
.entry(assoc_ty.def_id)
.entry(assoc_item.def_id)
.and_modify(|prev_span| {
self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
span: binding.span,
prev_span: *prev_span,
item_name: binding.item_name,
def_path: tcx.def_path_str(assoc_ty.container.id()),
def_path: tcx.def_path_str(assoc_item.container.id()),
});
})
.or_insert(binding.span);
@ -1189,7 +1186,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// Include substitutions for generic parameters of associated types
let projection_ty = candidate.map_bound(|trait_ref| {
let ident = Ident::new(assoc_ty.name, binding.item_name.span);
let ident = Ident::new(assoc_item.name, binding.item_name.span);
let item_segment = hir::PathSegment {
ident,
hir_id: Some(binding.hir_id),
@ -1201,7 +1198,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
tcx,
path_span,
assoc_ty.def_id,
assoc_item.def_id,
&item_segment,
trait_ref.substs,
);
@ -1212,14 +1209,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
);
ty::ProjectionTy {
item_def_id: assoc_ty.def_id,
item_def_id: assoc_item.def_id,
substs: substs_trait_ref_and_assoc_item,
}
});
if !speculative {
// Find any late-bound regions declared in `ty` that are not
// declared in the trait-ref or assoc_ty. These are not well-formed.
// declared in the trait-ref or assoc_item. These are not well-formed.
//
// Example:
//
@ -1260,6 +1257,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// the "projection predicate" for:
//
// `<T as Iterator>::Item = u32`
let assoc_item_def_id = projection_ty.skip_binder().item_def_id;
let def_kind = tcx.def_kind(assoc_item_def_id);
match (def_kind, term) {
(hir::def::DefKind::AssocTy, ty::Term::Ty(_))
| (hir::def::DefKind::AssocConst, ty::Term::Const(_)) => (),
(_, _) => {
let got = if let ty::Term::Ty(_) = term { "type" } else { "const" };
let expected = def_kind.descr(assoc_item_def_id);
tcx.sess
.struct_span_err(
binding.span,
&format!("mismatch in bind of {expected}, got {got}"),
)
.span_note(
tcx.def_span(assoc_item_def_id),
&format!("{expected} defined here does not match {got}"),
)
.emit();
}
}
bounds.projection_bounds.push((
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
projection_ty,

View File

@ -1,5 +1,6 @@
use rustc_errors::{Applicability, ErrorReported, StashKey};
use rustc_hir as hir;
use rustc_hir::def::CtorOf;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit;
@ -160,21 +161,36 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
// We've encountered an `AnonConst` in some path, so we need to
// figure out which generic parameter it corresponds to and return
// the relevant type.
let filtered = path
.segments
.iter()
.filter_map(|seg| seg.args.map(|args| (args.args, seg)))
.find_map(|(args, seg)| {
args.iter()
.filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.id() == hir_id)
.map(|index| (index, seg))
});
let filtered = path.segments.iter().find_map(|seg| {
seg.args?
.args
.iter()
.filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.id() == hir_id)
.map(|index| (index, seg))
});
// FIXME(associated_const_generics): can we blend this with iteration above?
let (arg_index, segment) = match filtered {
None => {
tcx.sess
.delay_span_bug(tcx.def_span(def_id), "no arg matching AnonConst in path");
return None;
let binding_filtered = path.segments.iter().find_map(|seg| {
seg.args?
.bindings
.iter()
.filter_map(TypeBinding::opt_const)
.position(|ct| ct.hir_id == hir_id)
.map(|idx| (idx, seg))
});
match binding_filtered {
Some(inner) => inner,
None => {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
"no arg matching AnonConst in path",
);
return None;
}
}
}
Some(inner) => inner,
};
@ -182,7 +198,6 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
// Try to use the segment resolution if it is valid, otherwise we
// default to the path resolution.
let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res);
use def::CtorOf;
let generics = match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx
.generics_of(tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap()),
@ -483,15 +498,49 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
.discr_type()
.to_ty(tcx),
Node::TraitRef(trait_ref @ &TraitRef {
path, ..
}) if let Some((binding, seg)) =
path
.segments
.iter()
.find_map(|seg| {
seg.args?.bindings
.iter()
.find_map(|binding| if binding.opt_const()?.hir_id == hir_id {
Some((binding, seg))
} else {
None
})
}) =>
{
// FIXME(associated_const_equality) when does this unwrap fail? I have no idea what case it would.
let trait_def_id = trait_ref.trait_def_id().unwrap();
let assoc_items = tcx.associated_items(trait_def_id);
let assoc_item = assoc_items.find_by_name_and_kind(
tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(),
);
if let Some(assoc_item) = assoc_item {
tcx.type_of(assoc_item.def_id)
} else {
// FIXME(associated_const_equality): add a useful error message here.
tcx.ty_error_with_message(
DUMMY_SP,
&format!("Could not find associated const on trait"),
)
}
}
Node::GenericParam(&GenericParam {
hir_id: param_hir_id,
kind: GenericParamKind::Const { default: Some(ct), .. },
..
}) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)),
x => tcx.ty_error_with_message(
x =>
tcx.ty_error_with_message(
DUMMY_SP,
&format!("unexpected const parent in type_of(): {:?}", x),
&format!("unexpected const parent in type_of(): {x:?}"),
),
}
}

View File

@ -0,0 +1,26 @@
#![feature(associated_const_equality)]
#![allow(unused)]
pub trait Foo {
const N: usize;
}
pub struct Bar;
impl Foo for Bar {
const N: usize = 3;
}
fn foo1<F: Foo<Z=3>>() {}
//~^ ERROR associated type
fn foo2<F: Foo<Z=usize>>() {}
//~^ ERROR associated type
fn foo3<F: Foo<Z=5>>() {}
//~^ ERROR associated type
fn main() {
foo1::<Bar>();
foo2::<Bar>();
foo3::<Bar>();
}

View File

@ -0,0 +1,21 @@
error[E0220]: associated type `Z` not found for `Foo`
--> $DIR/assoc-const-eq-missing.rs:15:16
|
LL | fn foo1<F: Foo<Z=3>>() {}
| ^ associated type `Z` not found
error[E0220]: associated type `Z` not found for `Foo`
--> $DIR/assoc-const-eq-missing.rs:17:16
|
LL | fn foo2<F: Foo<Z=usize>>() {}
| ^ associated type `Z` not found
error[E0220]: associated type `Z` not found for `Foo`
--> $DIR/assoc-const-eq-missing.rs:19:16
|
LL | fn foo3<F: Foo<Z=5>>() {}
| ^ associated type `Z` not found
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0220`.

View File

@ -0,0 +1,31 @@
#![feature(associated_const_equality)]
#![allow(unused)]
pub trait Foo {
const N: usize;
}
pub trait FooTy {
type T;
}
pub struct Bar;
impl Foo for Bar {
const N: usize = 3;
}
impl FooTy for Bar {
type T = usize;
}
fn foo<F: Foo<N=usize>>() {}
//~^ ERROR mismatch in
fn foo2<F: FooTy<T=3usize>>() {}
//~^ ERROR mismatch in
fn main() {
foo::<Bar>();
foo2::<Bar>();
}

View File

@ -0,0 +1,26 @@
error: mismatch in bind of associated constant, got type
--> $DIR/assoc-const-ty-mismatch.rs:23:15
|
LL | fn foo<F: Foo<N=usize>>() {}
| ^^^^^^^
|
note: associated constant defined here does not match type
--> $DIR/assoc-const-ty-mismatch.rs:5:3
|
LL | const N: usize;
| ^^^^^^^^^^^^^^^
error: mismatch in bind of associated type, got const
--> $DIR/assoc-const-ty-mismatch.rs:25:18
|
LL | fn foo2<F: FooTy<T=3usize>>() {}
| ^^^^^^^^
|
note: associated type defined here does not match const
--> $DIR/assoc-const-ty-mismatch.rs:9:3
|
LL | type T;
| ^^^^^^^
error: aborting due to 2 previous errors

View File

@ -1,4 +1,6 @@
// run-pass
#![feature(associated_const_equality)]
#![allow(unused)]
pub trait Foo {
const N: usize;
@ -13,9 +15,8 @@ impl Foo for Bar {
const TEST:usize = 3;
fn foo<F: Foo<N=3>>() {}
//~^ ERROR associated const equality is incomplete
fn bar<F: Foo<N={TEST}>>() {}
//~^ ERROR associated const equality is incomplete
fn foo<F: Foo<N=3usize>>() {}
fn main() {}
fn main() {
foo::<Bar>()
}

View File

@ -1,14 +0,0 @@
error: associated const equality is incomplete
--> $DIR/assoc-const.rs:16:15
|
LL | fn foo<F: Foo<N=3>>() {}
| ^^^ cannot yet relate associated const
error: associated const equality is incomplete
--> $DIR/assoc-const.rs:18:15
|
LL | fn bar<F: Foo<N={TEST}>>() {}
| ^^^^^^^^ cannot yet relate associated const
error: aborting due to 2 previous errors

View File

@ -9,7 +9,6 @@ impl TraitWAssocConst for Demo {
fn foo<A: TraitWAssocConst<A=32>>() {}
//~^ ERROR associated const equality
//~| ERROR associated const equality
fn main() {
foo::<Demo>();

View File

@ -7,12 +7,6 @@ LL | fn foo<A: TraitWAssocConst<A=32>>() {}
= note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
= help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
error: associated const equality is incomplete
--> $DIR/feature-gate-associated_const_equality.rs:10:28
|
LL | fn foo<A: TraitWAssocConst<A=32>>() {}
| ^^^^ cannot yet relate associated const
error: aborting due to 2 previous errors
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.