diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 67398c80f360c..43900ba88993e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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)] diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index cff848eeb6a0f..147061dafeb1e 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -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>, diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 96af16c668781..a1a1168a21d2e 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -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 diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e7a8e71ce71b0..78878487a9b48 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -867,6 +867,9 @@ impl<'tcx> Term<'tcx> { pub fn ty(&self) -> Option> { 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 diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 687bd16ba30f4..6cb19416cd769 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -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; } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 8cde93ed9d82a..6634f3e364d32 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -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) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 346590a2de26f..e7897887df706 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -200,7 +200,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { debug!(?normalized_ty); - normalized_ty + normalized_ty.ty().unwrap() } fn register_predicate_obligation( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 087fc6034d98f..5e7d4c8b415c3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -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 `::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>, -) -> 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>, -) -> Result>, InProgress> { +) -> Result>, 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>, } 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>) -> 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, ProjectionTyError<'tcx>> { +) -> Result, 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>, 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 `>::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 ` as X>::A` using //`impl X for Vec { type A = Box; }`, 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 { 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) ) } diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index a8e376838e218..1de50bae31b8c 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -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() }) }, ) } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 16fc9a01a2707..3e2d7fc382050 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -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: // // `::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, diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index b99e44f2105ca..5cb0d309ff4ee 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -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:?}"), ), } } diff --git a/src/test/ui/associated-consts/assoc-const-eq-missing.rs b/src/test/ui/associated-consts/assoc-const-eq-missing.rs new file mode 100644 index 0000000000000..5e029a12df26f --- /dev/null +++ b/src/test/ui/associated-consts/assoc-const-eq-missing.rs @@ -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>() {} +//~^ ERROR associated type +fn foo2>() {} +//~^ ERROR associated type +fn foo3>() {} +//~^ ERROR associated type + +fn main() { + foo1::(); + foo2::(); + foo3::(); +} diff --git a/src/test/ui/associated-consts/assoc-const-eq-missing.stderr b/src/test/ui/associated-consts/assoc-const-eq-missing.stderr new file mode 100644 index 0000000000000..b4bd6456c8517 --- /dev/null +++ b/src/test/ui/associated-consts/assoc-const-eq-missing.stderr @@ -0,0 +1,21 @@ +error[E0220]: associated type `Z` not found for `Foo` + --> $DIR/assoc-const-eq-missing.rs:15:16 + | +LL | fn foo1>() {} + | ^ 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>() {} + | ^ 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>() {} + | ^ associated type `Z` not found + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/src/test/ui/associated-consts/assoc-const-ty-mismatch.rs b/src/test/ui/associated-consts/assoc-const-ty-mismatch.rs new file mode 100644 index 0000000000000..c3293156345a7 --- /dev/null +++ b/src/test/ui/associated-consts/assoc-const-ty-mismatch.rs @@ -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>() {} +//~^ ERROR mismatch in +fn foo2>() {} +//~^ ERROR mismatch in + +fn main() { + foo::(); + foo2::(); +} diff --git a/src/test/ui/associated-consts/assoc-const-ty-mismatch.stderr b/src/test/ui/associated-consts/assoc-const-ty-mismatch.stderr new file mode 100644 index 0000000000000..703245145ce4e --- /dev/null +++ b/src/test/ui/associated-consts/assoc-const-ty-mismatch.stderr @@ -0,0 +1,26 @@ +error: mismatch in bind of associated constant, got type + --> $DIR/assoc-const-ty-mismatch.rs:23:15 + | +LL | fn foo>() {} + | ^^^^^^^ + | +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>() {} + | ^^^^^^^^ + | +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 + diff --git a/src/test/ui/associated-consts/assoc-const.rs b/src/test/ui/associated-consts/assoc-const.rs index cd4b42f9f84c8..9c7884c80734c 100644 --- a/src/test/ui/associated-consts/assoc-const.rs +++ b/src/test/ui/associated-consts/assoc-const.rs @@ -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>() {} -//~^ ERROR associated const equality is incomplete -fn bar>() {} -//~^ ERROR associated const equality is incomplete +fn foo>() {} -fn main() {} +fn main() { + foo::() +} diff --git a/src/test/ui/associated-consts/assoc-const.stderr b/src/test/ui/associated-consts/assoc-const.stderr deleted file mode 100644 index ccaa6fa8ee884..0000000000000 --- a/src/test/ui/associated-consts/assoc-const.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: associated const equality is incomplete - --> $DIR/assoc-const.rs:16:15 - | -LL | fn foo>() {} - | ^^^ cannot yet relate associated const - -error: associated const equality is incomplete - --> $DIR/assoc-const.rs:18:15 - | -LL | fn bar>() {} - | ^^^^^^^^ cannot yet relate associated const - -error: aborting due to 2 previous errors - diff --git a/src/test/ui/feature-gates/feature-gate-associated_const_equality.rs b/src/test/ui/feature-gates/feature-gate-associated_const_equality.rs index b51ead2a18866..2534c527be463 100644 --- a/src/test/ui/feature-gates/feature-gate-associated_const_equality.rs +++ b/src/test/ui/feature-gates/feature-gate-associated_const_equality.rs @@ -9,7 +9,6 @@ impl TraitWAssocConst for Demo { fn foo>() {} //~^ ERROR associated const equality -//~| ERROR associated const equality fn main() { foo::(); diff --git a/src/test/ui/feature-gates/feature-gate-associated_const_equality.stderr b/src/test/ui/feature-gates/feature-gate-associated_const_equality.stderr index f4db49c4af884..6563fbcba2e4b 100644 --- a/src/test/ui/feature-gates/feature-gate-associated_const_equality.stderr +++ b/src/test/ui/feature-gates/feature-gate-associated_const_equality.stderr @@ -7,12 +7,6 @@ LL | fn foo>() {} = note: see issue #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>() {} - | ^^^^ 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`.