From 8bb49e22b550b9ccdca3a1f9bc1e52532ec39635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 31 Dec 2023 05:06:53 +0100 Subject: [PATCH 1/3] Propagate the resolved type of assoc const bindings via query feeding --- .../rustc_hir_analysis/src/astconv/bounds.rs | 15 ++++++- compiler/rustc_hir_analysis/src/collect.rs | 1 + .../rustc_hir_analysis/src/collect/type_of.rs | 45 +++++++------------ compiler/rustc_middle/src/query/erase.rs | 4 ++ compiler/rustc_middle/src/query/mod.rs | 5 +++ compiler/rustc_middle/src/ty/context.rs | 17 +++++++ .../assoc-const-eq-supertraits.rs | 16 +++++++ .../assoc-const-eq-ty-alias-noninteracting.rs | 16 ++++--- .../associated-const-equality.rs | 11 ++++- 9 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 tests/ui/associated-consts/assoc-const-eq-supertraits.rs diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 6940b4a504584..a9b4dca08d48a 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -408,7 +408,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Create the generic arguments for the associated type or constant by joining the // parent arguments (the arguments of the trait) and the own arguments (the ones of // the associated item itself) and construct an alias type using them. - candidate.map_bound(|trait_ref| { + let alias_ty = candidate.map_bound(|trait_ref| { let ident = Ident::new(assoc_item.name, binding.ident.span); let item_segment = hir::PathSegment { ident, @@ -430,7 +430,18 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // *constants* to represent *const projections*. Alias *term* would be a more // appropriate name but alas. ty::AliasTy::new(tcx, assoc_item.def_id, alias_args) - }) + }); + + // Provide the resolved type of the associated constant to `type_of(AnonConst)`. + if !speculative && let ty::AssocKind::Const = assoc_kind { + let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); + // Since the arguments passed to the alias type above may contain early-bound + // generic parameters, the instantiated type may contain some as well. + // Therefore wrap it in `EarlyBinder`. + tcx.feed_type_of_assoc_const_binding(binding.hir_id, ty::EarlyBinder::bind(ty)); + } + + alias_ty }; match binding.kind { diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index a5ef1490bce97..d82d3eccfc683 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -63,6 +63,7 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { type_of: type_of::type_of, type_of_opaque: type_of::type_of_opaque, + type_of_assoc_const_binding: type_of::type_of_assoc_const_binding, type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index c0128afe2bf6c..57bcf7602eaa4 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -78,35 +78,10 @@ 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.parent_hir_node(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", - ); - }; - 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(), - ); - 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( - tcx, - tcx.def_span(def_id), - "Could not find associated const on trait", - ) - }; + Node::TypeBinding(&TypeBinding { hir_id, .. }) => { + // FIXME(fmease): Reject “escaping” early-bound generic parameters. + // FIXME(fmease): Reject escaping late-bound vars. + return tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder(); } // This match arm is for when the def_id appears in a GAT whose @@ -315,6 +290,18 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } } +pub(super) fn type_of_assoc_const_binding<'tcx>( + tcx: TyCtxt<'tcx>, + hir_id: HirId, +) -> ty::EarlyBinder>> { + let reported = tcx.dcx().delayed_bug(format!( + "attempt to obtain type of assoc const binding `{hir_id}` before \ + it was resolved by `add_predicates_for_ast_type_binding`" + )); + + ty::EarlyBinder::bind(ty::Binder::dummy(Ty::new_error(tcx, reported))) +} + fn get_path_containing_arg_in_pat<'hir>( pat: &'hir hir::Pat<'hir>, arg_id: HirId, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 2cdcdcb1492b0..82e0844a85c4c 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -193,6 +193,10 @@ impl EraseType for ty::EarlyBinder { type Result = T::Result; } +impl EraseType for ty::Binder<'_, Ty<'_>> { + type Result = [u8; size_of::>>()]; +} + impl EraseType for ty::Binder<'_, ty::FnSig<'_>> { type Result = [u8; size_of::>>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 5be45c33e1124..ce1e3484d69b0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -248,6 +248,11 @@ rustc_queries! { cycle_stash } + query type_of_assoc_const_binding(key: hir::HirId) -> ty::EarlyBinder>> { + desc { |tcx| "getting type of associated constant binding `{key:?}`" } + feedable + } + query type_alias_is_lazy(key: DefId) -> bool { desc { |tcx| "computing whether `{path}` is a lazy type alias", diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index cc734e7157fa4..acd68a5bd8700 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -71,6 +71,7 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; +use std::assert_matches::debug_assert_matches; use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt; @@ -540,6 +541,22 @@ impl<'tcx> TyCtxt<'tcx> { debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); TyCtxtFeed { tcx: self, key }.type_of(value) } + + pub fn feed_type_of_assoc_const_binding( + self, + key: hir::HirId, + value: ty::EarlyBinder>>, + ) { + debug_assert_matches!( + self.hir_node(key), + hir::Node::TypeBinding(hir::TypeBinding { + kind: hir::TypeBindingKind::Equality { term: hir::Term::Const(_) }, + .. + }) + ); + + TyCtxtFeed { tcx: self, key }.type_of_assoc_const_binding(value) + } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { 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..d5d724c9b1537 --- /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/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs b/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs index 4ff05112897c7..76df014ccd9bd 100644 --- a/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs +++ b/tests/ui/associated-consts/assoc-const-eq-ty-alias-noninteracting.rs @@ -1,21 +1,25 @@ // Regression test for issue #112560. // Respect the fact that (associated) types and constants live in different namespaces and // therefore equality bounds involving identically named associated items don't conflict if -// their kind (type vs. const) differs. - -// FIXME(fmease): Extend this test to cover supertraits again -// once #118040 is fixed. See initial version of PR #118360. +// their kind (type vs. const) differs. This obviously extends to supertraits. //@ check-pass #![feature(associated_const_equality)] -trait Trait { +trait Trait: SuperTrait { type N; + type Q; const N: usize; } -fn take(_: impl Trait) {} +trait SuperTrait { + const Q: &'static str; +} + +fn take0(_: impl Trait) {} + +fn take1(_: 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 3c727097e2b1c..c0179f02fd2eb 100644 --- a/tests/ui/generic-const-items/associated-const-equality.rs +++ b/tests/ui/generic-const-items/associated-const-equality.rs @@ -1,22 +1,31 @@ //@ check-pass -#![feature(generic_const_items, associated_const_equality)] +#![feature(generic_const_items, associated_const_equality, adt_const_params)] #![allow(incomplete_features)] trait Owner { const C: u32; const K: u32; + const Q: Maybe; } impl Owner for () { const C: u32 = N; const K: u32 = N + 1; + const Q: Maybe = Maybe::Nothing; } fn take0(_: impl Owner = { N }>) {} fn take1(_: impl Owner = 100>) {} +fn take2(_: impl Owner = { Maybe::Just(()) }>) {} fn main() { take0::<128>(()); take1(()); } + +#[derive(PartialEq, Eq, std::marker::ConstParamTy)] +enum Maybe { + Nothing, + Just(T), +} From b94498a3788699f839ee690d96d3288148c756d5 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 21 Feb 2024 10:28:20 +0000 Subject: [PATCH 2/3] Use existing query feeding workarounds --- .../rustc_hir_analysis/src/astconv/bounds.rs | 11 ++++++++++- compiler/rustc_hir_analysis/src/collect.rs | 1 - .../rustc_hir_analysis/src/collect/type_of.rs | 18 ------------------ compiler/rustc_middle/src/query/erase.rs | 4 ---- compiler/rustc_middle/src/query/mod.rs | 5 ----- compiler/rustc_middle/src/ty/context.rs | 17 ----------------- 6 files changed, 10 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index a9b4dca08d48a..117df5482e665 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -434,11 +434,20 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { // Provide the resolved type of the associated constant to `type_of(AnonConst)`. if !speculative && let ty::AssocKind::Const = assoc_kind { + let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = + binding.kind + else { + bug!() + }; let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); // Since the arguments passed to the alias type above may contain early-bound // generic parameters, the instantiated type may contain some as well. // Therefore wrap it in `EarlyBinder`. - tcx.feed_type_of_assoc_const_binding(binding.hir_id, ty::EarlyBinder::bind(ty)); + // FIXME(fmease): Reject escaping late-bound vars. + tcx.feed_anon_const_type( + anon_const.def_id, + ty::EarlyBinder::bind(ty.skip_binder()), + ); } alias_ty diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index d82d3eccfc683..a5ef1490bce97 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -63,7 +63,6 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { type_of: type_of::type_of, type_of_opaque: type_of::type_of_opaque, - type_of_assoc_const_binding: type_of::type_of_assoc_const_binding, type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 57bcf7602eaa4..ddb02247d168d 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -78,12 +78,6 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { .expect("const parameter types cannot be generic"); } - Node::TypeBinding(&TypeBinding { hir_id, .. }) => { - // FIXME(fmease): Reject “escaping” early-bound generic parameters. - // FIXME(fmease): Reject escaping late-bound vars. - return tcx.type_of_assoc_const_binding(hir_id).skip_binder().skip_binder(); - } - // This match arm is for when the def_id appears in a GAT whose // path can't be resolved without typechecking e.g. // @@ -290,18 +284,6 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> { } } -pub(super) fn type_of_assoc_const_binding<'tcx>( - tcx: TyCtxt<'tcx>, - hir_id: HirId, -) -> ty::EarlyBinder>> { - let reported = tcx.dcx().delayed_bug(format!( - "attempt to obtain type of assoc const binding `{hir_id}` before \ - it was resolved by `add_predicates_for_ast_type_binding`" - )); - - ty::EarlyBinder::bind(ty::Binder::dummy(Ty::new_error(tcx, reported))) -} - fn get_path_containing_arg_in_pat<'hir>( pat: &'hir hir::Pat<'hir>, arg_id: HirId, diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 82e0844a85c4c..2cdcdcb1492b0 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -193,10 +193,6 @@ impl EraseType for ty::EarlyBinder { type Result = T::Result; } -impl EraseType for ty::Binder<'_, Ty<'_>> { - type Result = [u8; size_of::>>()]; -} - impl EraseType for ty::Binder<'_, ty::FnSig<'_>> { type Result = [u8; size_of::>>()]; } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index ce1e3484d69b0..5be45c33e1124 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -248,11 +248,6 @@ rustc_queries! { cycle_stash } - query type_of_assoc_const_binding(key: hir::HirId) -> ty::EarlyBinder>> { - desc { |tcx| "getting type of associated constant binding `{key:?}`" } - feedable - } - query type_alias_is_lazy(key: DefId) -> bool { desc { |tcx| "computing whether `{path}` is a lazy type alias", diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index acd68a5bd8700..cc734e7157fa4 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -71,7 +71,6 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::WithCachedTypeInfo; use rustc_type_ir::{CollectAndApply, Interner, TypeFlags}; -use std::assert_matches::debug_assert_matches; use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt; @@ -541,22 +540,6 @@ impl<'tcx> TyCtxt<'tcx> { debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); TyCtxtFeed { tcx: self, key }.type_of(value) } - - pub fn feed_type_of_assoc_const_binding( - self, - key: hir::HirId, - value: ty::EarlyBinder>>, - ) { - debug_assert_matches!( - self.hir_node(key), - hir::Node::TypeBinding(hir::TypeBinding { - kind: hir::TypeBindingKind::Equality { term: hir::Term::Const(_) }, - .. - }) - ); - - TyCtxtFeed { tcx: self, key }.type_of_assoc_const_binding(value) - } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { From 858d3362095851bde4f9cc3c132542f2ce9e9a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Fri, 23 Feb 2024 06:47:09 +0100 Subject: [PATCH 3/3] Slightly simplify feeding of assoc const eq bounds --- compiler/rustc_hir_analysis/src/astconv/bounds.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs index 117df5482e665..c72096ea4652b 100644 --- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs +++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs @@ -433,12 +433,10 @@ impl<'tcx> dyn AstConv<'tcx> + '_ { }); // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if !speculative && let ty::AssocKind::Const = assoc_kind { - let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = + if !speculative + && let hir::TypeBindingKind::Equality { term: hir::Term::Const(anon_const) } = binding.kind - else { - bug!() - }; + { let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args)); // Since the arguments passed to the alias type above may contain early-bound // generic parameters, the instantiated type may contain some as well.