about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkadmin <julianknodt@gmail.com>2022-01-27 14:40:38 +0000
committerkadmin <julianknodt@gmail.com>2022-01-27 14:40:55 +0000
commit1c4fe64bdcb0cc27c91c5405430f0ca189fcd065 (patch)
tree0c2acfdd301bd07f28eaa12edb016fbc7dcd27bf
parent21b4a9cfdcbb1e76f4b36b5c3cfd64d627285093 (diff)
downloadrust-1c4fe64bdcb0cc27c91c5405430f0ca189fcd065.tar.gz
rust-1c4fe64bdcb0cc27c91c5405430f0ca189fcd065.zip
Continue work on assoc const eq
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs475
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs35
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs85
-rw-r--r--src/test/ui/associated-consts/assoc-const.rs11
-rw-r--r--src/test/ui/associated-consts/assoc-const.stderr14
6 files changed, 471 insertions, 152 deletions
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6174c922e2d..05a210ef49e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -857,6 +857,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
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index e8b46e88a42..8f498f02169 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -44,7 +44,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 +53,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 +67,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,32 +196,67 @@ 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(
-        selcx,
-        obligation.param_env,
-        obligation.predicate.projection_ty,
-        obligation.cause.clone(),
-        obligation.recursion_depth,
-        &mut obligations,
-    ) {
-        Ok(Some(n)) => n,
-        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) {
-        Ok(InferOk { obligations: inferred_obligations, value: () }) => {
-            obligations.extend(inferred_obligations);
-            Ok(Ok(Some(obligations)))
+    match obligation.predicate.term {
+        ty::Term::Ty(obligation_pred_ty) => {
+            let normalized_ty = match opt_normalize_projection_type(
+                selcx,
+                obligation.param_env,
+                obligation.predicate.projection_ty,
+                obligation.cause.clone(),
+                obligation.recursion_depth,
+                &mut obligations,
+            ) {
+                Ok(Some(n)) => n,
+                Ok(None) => return Ok(Ok(None)),
+                Err(InProgress) => return Ok(Err(InProgress)),
+            };
+            debug!(?normalized_ty, ?obligations, "project_and_unify_type result");
+            match infcx
+                .at(&obligation.cause, obligation.param_env)
+                .eq(normalized_ty, obligation_pred_ty)
+            {
+                Ok(InferOk { obligations: inferred_obligations, value: () }) => {
+                    obligations.extend(inferred_obligations);
+                    Ok(Ok(Some(obligations)))
+                }
+                Err(err) => {
+                    debug!("project_and_unify_type: equating types encountered error {:?}", err);
+                    Err(MismatchedProjectionTypes { err })
+                }
+            }
         }
-        Err(err) => {
-            debug!("project_and_unify_type: equating types encountered error {:?}", err);
-            Err(MismatchedProjectionTypes { err })
+        ty::Term::Const(_obligation_pred_const) => {
+            let normalized_const = match opt_normalize_projection_const(
+                selcx,
+                obligation.param_env,
+                obligation.predicate.projection_ty,
+                obligation.cause.clone(),
+                obligation.recursion_depth,
+                &mut obligations,
+            ) {
+                Ok(Some(n)) => n,
+                Ok(None) => return Ok(Ok(None)),
+                Err(InProgress) => return Ok(Err(InProgress)),
+            };
+            println!("{:?}", normalized_const);
+            todo!();
+            /*
+            match infcx
+                .at(&obligation.cause, obligation.param_env)
+                .eq(normalized_ty, obligation_pred_const)
+            {
+                Ok(InferOk { obligations: inferred_obligations, value: () }) => {
+                    obligations.extend(inferred_obligations);
+                    Ok(Ok(Some(obligations)))
+                }
+                Err(err) => {
+                    debug!("project_and_unify_type: equating consts encountered error {:?}", err);
+                    Err(MismatchedProjectionTypes { err })
+                }
+            }
+            */
         }
     }
 }
@@ -813,6 +848,22 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
     })
 }
 
+fn opt_normalize_projection_const<'a, 'b, 'tcx>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    projection_const: ty::ProjectionTy<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    _obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> Result<Option<&'tcx ty::Const<'tcx>>, InProgress> {
+    let infcx = selcx.infcx();
+    let projection_const = infcx.resolve_vars_if_possible(projection_const);
+    let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_const);
+    // TODO continue working on below
+    let _projected_const = project_const(selcx, &obligation);
+    //println!("{:?}", projected_const);
+    todo!();
+}
 /// The guts of `normalize`: normalize a specific projection like `<T
 /// as Trait>::Item`. The result is always a type (and possibly
 /// additional obligations). Returns `None` in the case of ambiguity,
@@ -914,8 +965,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty);
 
     match project_type(selcx, &obligation) {
-        Ok(ProjectedTy::Progress(Progress {
-            ty: projected_ty,
+        Ok(Projected::Progress(Progress {
+            term: projected_ty,
             obligations: mut projected_obligations,
         })) => {
             // if projection succeeded, then what we get out of this
@@ -957,7 +1008,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             obligations.extend(result.obligations);
             Ok(Some(result.value))
         }
-        Ok(ProjectedTy::NoProgress(projected_ty)) => {
+        Ok(Projected::NoProgress(projected_ty)) => {
             debug!(?projected_ty, "opt_normalize_projection_type: no progress");
             let result = Normalized { value: projected_ty, obligations: vec![] };
             if use_cache {
@@ -966,14 +1017,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             // 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 :
@@ -1032,35 +1083,76 @@ fn normalize_to_error<'a, 'tcx>(
     Normalized { value: new_value, obligations: vec![trait_obligation] }
 }
 
-enum ProjectedTy<'tcx> {
-    Progress(Progress<'tcx>),
-    NoProgress(Ty<'tcx>),
+enum Projected<'tcx, T> {
+    Progress(Progress<'tcx, T>),
+    NoProgress(T),
 }
 
-struct Progress<'tcx> {
-    ty: Ty<'tcx>,
+struct Progress<'tcx, T>
+where
+    T: 'tcx,
+{
+    term: T,
     obligations: Vec<PredicateObligation<'tcx>>,
 }
 
-impl<'tcx> Progress<'tcx> {
+impl<'tcx> Progress<'tcx, Ty<'tcx>> {
     fn error(tcx: TyCtxt<'tcx>) -> Self {
-        Progress { ty: tcx.ty_error(), obligations: vec![] }
+        Progress { term: tcx.ty_error(), 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
     }
 }
 
+impl<'tcx> Progress<'tcx, &'tcx ty::Const<'tcx>> {
+    fn error_const(tcx: TyCtxt<'tcx>) -> Self {
+        Progress { term: tcx.const_error(tcx.ty_error()), obligations: vec![] }
+    }
+}
+
+fn project_const<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+) -> Result<Projected<'tcx, &'tcx ty::Const<'tcx>>, ProjectionError<'tcx>> {
+    if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) {
+        return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow));
+    }
+
+    if obligation.predicate.references_error() {
+        return Ok(Projected::Progress(Progress::error_const(selcx.tcx())));
+    }
+
+    let mut candidates = ProjectionCandidateSet::None;
+
+    assemble_candidates_from_param_env(selcx, obligation, &mut candidates);
+    assemble_candidates_from_trait_def(selcx, obligation, &mut candidates);
+    assemble_candidates_from_object_ty(selcx, obligation, &mut candidates);
+    if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates {
+    } else {
+        assemble_candidates_from_impls(selcx, obligation, &mut candidates);
+    };
+    match candidates {
+        ProjectionCandidateSet::Single(candidate) => {
+            Ok(Projected::Progress(confirm_candidate_const(selcx, obligation, candidate)))
+        }
+        ProjectionCandidateSet::None => todo!(),
+        /*
+        Ok(Projected::NoProgress(
+            selcx
+                .tcx()
+                .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs),
+        )),
+        */
+        // Error occurred while trying to processing impls.
+        ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)),
+        // Inherent ambiguity that prevents us from even enumerating the candidates.
+        ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates),
+    }
+}
+
 /// Computes the result of a projection type (if we can).
 ///
 /// IMPORTANT:
@@ -1069,19 +1161,18 @@ impl<'tcx> Progress<'tcx> {
 fn project_type<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-) -> Result<ProjectedTy<'tcx>, ProjectionTyError<'tcx>> {
+) -> Result<Projected<'tcx, Ty<'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 +1183,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 +1193,19 @@ 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(
             selcx
                 .tcx()
                 .mk_projection(obligation.predicate.item_def_id, obligation.predicate.substs),
         )),
         // 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 +1215,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 +1240,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 +1263,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>(
         selcx,
         obligation,
         candidate_set,
-        ProjectionTyCandidate::TraitDef,
+        ProjectionCandidate::TraitDef,
         bounds.iter(),
         true,
     )
@@ -1191,7 +1281,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 +1308,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
         selcx,
         obligation,
         candidate_set,
-        ProjectionTyCandidate::Object,
+        ProjectionCandidate::Object,
         env_predicates,
         false,
     );
@@ -1231,14 +1321,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 +1342,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 +1362,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<...>`:
@@ -1327,7 +1414,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 +1587,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 +1601,62 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
 fn confirm_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
-    candidate: ProjectionTyCandidate<'tcx>,
-) -> Progress<'tcx> {
+    candidate: ProjectionCandidate<'tcx>,
+) -> Progress<'tcx, Ty<'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.term.has_infer_regions() {
+        progress.term = OpportunisticRegionResolver::new(selcx.infcx()).fold_ty(progress.term);
+    }
+    progress
+}
+
+fn confirm_candidate_const<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    candidate: ProjectionCandidate<'tcx>,
+) -> Progress<'tcx, &'tcx ty::Const<'tcx>> {
+    let mut progress = match candidate {
+        ProjectionCandidate::ParamEnv(poly_projection)
+        | ProjectionCandidate::Object(poly_projection) => {
+            confirm_param_env_candidate_const(selcx, obligation, poly_projection, false)
+        }
+
+        ProjectionCandidate::TraitDef(poly_projection) => {
+            confirm_param_env_candidate_const(selcx, obligation, poly_projection, true)
+        }
+
+        ProjectionCandidate::Select(impl_source) => {
+            confirm_select_candidate_const(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 = OpportunisticRegionResolver::new(selcx.infcx()).fold_const(progress.term);
     }
     progress
 }
@@ -1546,7 +1665,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     impl_source: Selection<'tcx>,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     match impl_source {
         super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
         super::ImplSource::Generator(data) => confirm_generator_candidate(selcx, obligation, data),
@@ -1573,11 +1692,42 @@ fn confirm_select_candidate<'cx, 'tcx>(
     }
 }
 
+fn confirm_select_candidate_const<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    impl_source: Selection<'tcx>,
+) -> Progress<'tcx, &'tcx ty::Const<'tcx>> {
+    match impl_source {
+        super::ImplSource::UserDefined(data) => {
+            confirm_impl_candidate_const(selcx, obligation, data)
+        }
+        super::ImplSource::Generator(_)
+        | super::ImplSource::Closure(_)
+        | super::ImplSource::FnPointer(_)
+        | super::ImplSource::DiscriminantKind(_)
+        | super::ImplSource::Pointee(_) => todo!(),
+        super::ImplSource::Object(_)
+        | super::ImplSource::AutoImpl(..)
+        | super::ImplSource::Param(..)
+        | super::ImplSource::Builtin(..)
+        | super::ImplSource::TraitUpcasting(_)
+        | super::ImplSource::TraitAlias(..)
+        | super::ImplSource::ConstDrop(_) => {
+            // we don't create Select candidates with this kind of resolution
+            span_bug!(
+                obligation.cause.span,
+                "Cannot project an associated type from `{:?}`",
+                impl_source
+            )
+        }
+    }
+}
+
 fn confirm_generator_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     impl_source: ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let gen_sig = impl_source.substs.as_generator().poly_sig();
     let Normalized { value: gen_sig, obligations } = normalize_with_depth(
         selcx,
@@ -1627,7 +1777,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     _: ImplSourceDiscriminantKindData,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let tcx = selcx.tcx();
 
     let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
@@ -1652,7 +1802,7 @@ fn confirm_pointee_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     _: ImplSourcePointeeData,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let tcx = selcx.tcx();
     let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
 
@@ -1684,7 +1834,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let fn_type = selcx.infcx().shallow_resolve(fn_pointer_impl_source.fn_ty);
     let sig = fn_type.fn_sig(selcx.tcx());
     let Normalized { value: sig, obligations } = normalize_with_depth(
@@ -1704,7 +1854,7 @@ fn confirm_closure_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     impl_source: ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let closure_sig = impl_source.substs.as_closure().sig();
     let Normalized { value: closure_sig, obligations } = normalize_with_depth(
         selcx,
@@ -1726,7 +1876,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     fn_sig: ty::PolyFnSig<'tcx>,
     flag: util::TupleArgumentsFlag,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let tcx = selcx.tcx();
 
     debug!(?obligation, ?fn_sig, "confirm_callable_candidate");
@@ -1757,7 +1907,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
     obligation: &ProjectionTyObligation<'tcx>,
     poly_cache_entry: ty::PolyProjectionPredicate<'tcx>,
     potentially_unnormalized_candidate: bool,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let infcx = selcx.infcx();
     let cause = &obligation.cause;
     let param_env = obligation.param_env;
@@ -1804,7 +1954,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.ty().unwrap(), obligations: nested_obligations }
         }
         Err(e) => {
             let msg = format!(
@@ -1813,7 +1963,72 @@ 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, obligations: vec![] }
+        }
+    }
+}
+
+fn confirm_param_env_candidate_const<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    poly_cache_entry: ty::PolyProjectionPredicate<'tcx>,
+    potentially_unnormalized_candidate: bool,
+) -> Progress<'tcx, &'tcx ty::Const<'tcx>> {
+    let infcx = selcx.infcx();
+    let cause = &obligation.cause;
+    let param_env = obligation.param_env;
+
+    let (cache_entry, _) = infcx.replace_bound_vars_with_fresh_vars(
+        cause.span,
+        LateBoundRegionConversionTime::HigherRankedType,
+        poly_cache_entry,
+    );
+
+    let cache_projection = cache_entry.projection_ty;
+    let mut nested_obligations = Vec::new();
+    let obligation_projection = obligation.predicate;
+    let obligation_projection = ensure_sufficient_stack(|| {
+        normalize_with_depth_to(
+            selcx,
+            obligation.param_env,
+            obligation.cause.clone(),
+            obligation.recursion_depth + 1,
+            obligation_projection,
+            &mut nested_obligations,
+        )
+    });
+    let cache_projection = if potentially_unnormalized_candidate {
+        ensure_sufficient_stack(|| {
+            normalize_with_depth_to(
+                selcx,
+                obligation.param_env,
+                obligation.cause.clone(),
+                obligation.recursion_depth + 1,
+                cache_projection,
+                &mut nested_obligations,
+            )
+        })
+    } else {
+        cache_projection
+    };
+
+    match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) {
+        Ok(InferOk { value: _, obligations }) => {
+            nested_obligations.extend(obligations);
+            assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations);
+            Progress { term: cache_entry.term.ct().unwrap(), obligations: nested_obligations }
+        }
+        Err(e) => {
+            let msg = format!(
+                "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}",
+                obligation, poly_cache_entry, e,
+            );
+            let err = infcx.tcx.const_error_with_message(
+                infcx.tcx.ty_error(),
+                obligation.cause.span,
+                &msg,
+            );
+            Progress { term: err, obligations: vec![] }
         }
     }
 }
@@ -1822,7 +2037,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTyObligation<'tcx>,
     impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
+) -> Progress<'tcx, Ty<'tcx>> {
     let tcx = selcx.tcx();
 
     let ImplSourceUserDefinedData { impl_def_id, substs, mut nested } = impl_impl_source;
@@ -1830,9 +2045,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(), obligations: nested },
     };
 
     if !assoc_ty.item.defaultness.has_value() {
@@ -1844,7 +2059,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(), 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:
@@ -1861,10 +2076,54 @@ fn confirm_impl_candidate<'cx, 'tcx>(
             obligation.cause.span,
             "impl item and trait item have different parameter counts",
         );
-        Progress { ty: err, obligations: nested }
+        Progress { term: err, obligations: nested }
     } else {
         assoc_ty_own_obligations(selcx, obligation, &mut nested);
-        Progress { ty: ty.subst(tcx, substs), obligations: nested }
+        Progress { term: ty.subst(tcx, substs), obligations: nested }
+    }
+}
+
+fn confirm_impl_candidate_const<'cx, 'tcx>(
+    selcx: &mut SelectionContext<'cx, 'tcx>,
+    obligation: &ProjectionTyObligation<'tcx>,
+    impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
+) -> Progress<'tcx, &'tcx ty::Const<'tcx>> {
+    let tcx = selcx.tcx();
+
+    let ImplSourceUserDefinedData { impl_def_id, substs, nested } = impl_impl_source;
+    let assoc_item_id = obligation.predicate.item_def_id;
+    let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
+
+    let param_env = obligation.param_env;
+    let assoc_ct = match assoc_def(selcx, impl_def_id, assoc_item_id) {
+        Ok(assoc_ct) => assoc_ct,
+        Err(ErrorReported) => {
+            return Progress { term: tcx.const_error(tcx.ty_error()), obligations: nested };
+        }
+    };
+
+    if !assoc_ct.item.defaultness.has_value() {
+        return Progress { term: tcx.const_error(tcx.ty_error()), obligations: nested };
+    }
+    let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs);
+    let substs =
+        translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ct.defining_node);
+    let _ty = tcx.type_of(assoc_ct.item.def_id);
+    // TODO need to figure how to get the const of the assoc_ct.item.def_id
+    // I'm not sure if there's another tcx query for it.
+    let _ct = ();
+    if substs.len() != tcx.generics_of(assoc_ct.item.def_id).count() {
+        let err = tcx.const_error_with_message(
+            tcx.ty_error(),
+            obligation.cause.span,
+            "impl item and trait item have different parameter counts",
+        );
+        Progress { term: err, obligations: nested }
+    } else {
+        // There are no where-clauses on associated consts yet, but if that's
+        // ever added it would go here.
+        todo!();
+        //Progress { term: ct.subst(tcx, substs), obligations: nested }
     }
 }
 
@@ -1905,10 +2164,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 +2179,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 +2190,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 +2201,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_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 4cf8ddcb8f0..6124d2906ff 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -1132,7 +1132,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| {
@@ -1140,35 +1140,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);
@@ -1176,7 +1173,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),
@@ -1188,7 +1185,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,
             );
@@ -1199,14 +1196,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:
             //
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 63020b7f90f..d5430fefc53 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;
@@ -170,11 +171,40 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
                         .position(|arg| arg.id() == hir_id)
                         .map(|index| (index, seg))
                 });
+            // FIXME(associated_const_equality): recursively search through the bindings instead
+            // of just top level.
+
             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()
+                        .filter_map(|seg| seg.args.map(|args| (args.bindings, seg)))
+                        .find_map(|(bindings, seg)| {
+                            bindings
+                                .iter()
+                                .filter_map(|binding| {
+                                    if let hir::TypeBindingKind::Equality { term: Term::Const(c) } =
+                                        binding.kind
+                                    {
+                                        Some(c)
+                                    } else {
+                                        None
+                                    }
+                                })
+                                .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 +212,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 +512,59 @@ 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()
+                      .filter_map(|seg| seg.args.map(|args| (args.bindings, seg)))
+                      .find_map(|(bindings, seg)| {
+                          bindings
+                              .iter()
+                              .filter_map(|binding| {
+                                  if let hir::TypeBindingKind::Equality { term: Term::Const(c) } =
+                                      binding.kind
+                                  {
+                                          Some((binding, c))
+                                  } else {
+                                      None
+                                  }
+                              })
+                              .find_map(|(binding, ct)| if ct.hir_id == hir_id {
+                                Some((binding, seg))
+                              } else {
+                                None
+                              })
+                      }) =>
+                {
+                  // TODO 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 {
+                      // TODO 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.rs b/src/test/ui/associated-consts/assoc-const.rs
index cd4b42f9f84..9c7884c8073 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<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>()
+}
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 ccaa6fa8ee8..00000000000
--- 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<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
-