From 4c0addc80af4666f26d7ad51fe34a0e2dd0b8b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 19 Nov 2023 19:02:47 +0100 Subject: [PATCH] Check supertraits and gen args when resolving anon consts in assoc const eq bounds --- .../rustc_hir_analysis/src/astconv/mod.rs | 2 +- .../rustc_hir_analysis/src/collect/type_of.rs | 87 ++++++++++++++----- .../assoc-const-eq-supertraits.rs | 16 ++++ .../associated-const-equality.rs | 3 + 4 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 tests/ui/associated-consts/assoc-const-eq-supertraits.rs diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 94b182e09f680..a6269bc3af83a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -345,7 +345,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// type itself: `['a]`. The returned `GenericArgsRef` concatenates these two /// lists: `[Vec, u8, 'a]`. #[instrument(level = "debug", skip(self, span), ret)] - fn create_args_for_ast_path<'a>( + pub(crate) fn create_args_for_ast_path<'a>( &self, span: Span, def_id: DefId, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c4fc4dda069aa..f0be7363d5862 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -8,9 +8,10 @@ use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::symbol::Ident; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::traits; -use super::ItemCtxt; use super::{bad_placeholder, is_suggestable_infer_ty}; +use super::{AstConv, ItemCtxt}; pub use opaque::test_opaque_hidden_types; mod opaque; @@ -60,35 +61,78 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .expect("const parameter types cannot be generic"); } - Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) - if let Node::TraitRef(trait_ref) = tcx.hir().get(tcx.hir().parent_id(binding_id)) => - { + Node::TypeBinding( + binding @ &TypeBinding { hir_id: binding_id, ident, gen_args, span, .. }, + ) if let Node::TraitRef(trait_ref) = tcx.hir().get(tcx.hir().parent_id(binding_id)) => { let Some(trait_def_id) = trait_ref.trait_def_id() else { return Ty::new_error_with_message( tcx, tcx.def_span(def_id), - "Could not find trait", + "could not find trait", ); }; - 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(), + + // FIXME(associated_const_equality): We're now performing a full but ad-hoc type-based + // resolution of the associated constant. Doing all this work *here* isn't great. + // Ideally, we would've computed this already somewhere else (in a query?). + + let icx = ItemCtxt::new(tcx, def_id); + let trait_segment = trait_ref.path.segments.last().unwrap(); + let (trait_args, _) = icx.astconv().create_args_for_ast_path( + trait_ref.path.span, + trait_def_id, + &[], + trait_segment, + trait_segment.args(), + trait_segment.infer_args, + // FIXME(associated_const_equality): This isn't correct, it should be the concrete / + // instantiated self type. Theoretically, we could search for it in the HIR of the + // parent item but that's super fragile and hairy. + Some(tcx.types.self_param), + ty::BoundConstness::NotConst, ); - return if let Some(assoc_item) = assoc_item { - tcx.type_of(assoc_item.def_id) - .no_bound_vars() - .expect("const parameter types cannot be generic") - } else { - // FIXME(associated_const_equality): add a useful error message here. - Ty::new_error_with_message( + let trait_ref = ty::Binder::bind_with_vars( + ty::TraitRef::new(tcx, trait_def_id, trait_args), + tcx.late_bound_vars(trait_ref.hir_ref_id), + ); + + // We shouldn't need to deal with ambiguity since `add_predicates_for_ast_type_binding` + // should've already bailed out early in such case. + let Some((assoc_item, parent_args)) = + traits::supertraits(tcx, trait_ref).find_map(|trait_ref| { + tcx.associated_items(trait_ref.def_id()) + .find_by_name_and_kind( + tcx, + binding.ident, + ty::AssocKind::Const, + trait_ref.def_id(), + ) + // FIXME(fmease): `skip_binder` is fishy! + .map(|item| (item, trait_ref.skip_binder().args)) + }) + else { + return Ty::new_error_with_message( tcx, tcx.def_span(def_id), - "Could not find associated const on trait", - ) + "could not find associated const on trait", + ); }; + + let args = icx.astconv().create_args_for_associated_item( + span, + assoc_item.def_id, + // FIXME(fmease): This is gross as hell! + &hir::PathSegment { + ident, + hir_id, + res: def::Res::Def(def::DefKind::AssocConst, assoc_item.def_id), + args: Some(gen_args), + infer_args: false, + }, + parent_args, + ); + + return tcx.type_of(assoc_item.def_id).instantiate(tcx, args); } // This match arm is for when the def_id appears in a GAT whose @@ -120,8 +164,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .unwrap() .0 .def_id; - let item_ctxt = &ItemCtxt::new(tcx, item_def_id) as &dyn crate::astconv::AstConv<'_>; - let ty = item_ctxt.ast_ty_to_ty(hir_ty); + let ty = ItemCtxt::new(tcx, item_def_id).to_ty(hir_ty); // Iterate through the generics of the projection to find the one that corresponds to // the def_id that this query was called with. We filter to only type and const args here diff --git a/tests/ui/associated-consts/assoc-const-eq-supertraits.rs b/tests/ui/associated-consts/assoc-const-eq-supertraits.rs new file mode 100644 index 0000000000000..9cfef6a5f7131 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-eq-supertraits.rs @@ -0,0 +1,16 @@ +// Regression test for issue #118040. +// Ensure that we support assoc const eq bounds where the assoc const comes from a supertrait. + +// check-pass + +#![feature(associated_const_equality)] + +trait Trait: SuperTrait {} +trait SuperTrait: SuperSuperTrait {} +trait SuperSuperTrait { + const K: T; +} + +fn take(_: impl Trait) {} + +fn main() {} diff --git a/tests/ui/generic-const-items/associated-const-equality.rs b/tests/ui/generic-const-items/associated-const-equality.rs index 785d3aa5018ca..41e2078e31ce1 100644 --- a/tests/ui/generic-const-items/associated-const-equality.rs +++ b/tests/ui/generic-const-items/associated-const-equality.rs @@ -6,15 +6,18 @@ trait Owner { const C: u32; const K: u32; + const Q: Option; } impl Owner for () { const C: u32 = N; const K: u32 = N + 1; + const Q: Option = None; } fn take0(_: impl Owner = { N }>) {} fn take1(_: impl Owner = 100>) {} +fn take2(_: impl Owner = { Some(()) }>) {} fn main() { take0::<128>(());