https://gcc.gnu.org/g:aa1e1d2230afc1ea58ebf4b20093013c485c1b74
commit r17-5148-gaa1e1d2230afc1ea58ebf4b20093013c485c1b74 Author: Philip Herron <[email protected]> Date: Tue Sep 8 13:25:59 2026 +0100 gccrs: Add type bound predicate scan This patch is used to address where constraints on projections for example fn test<P: Deref) where P::Target: PartialOrd<Q::Target> When we do fn test<P: Deref) we can specify the bound of Deref on P directly so its directly available. But when we do where P::Target ... that P::Target resolves the trait position Target of Deref but thats a trait position decl so this information is lost during path resolution. This patch addes a scan so when we generate predicates we track them in the type check context so we can look them up so it adds a loop to get any other associcated predicates in this position. Its not performant but we can fix that later we just need to progress libcore for now. Fixes Rust-GCC/gccrs#4865 gcc/rust/ChangeLog: * typecheck/rust-hir-dot-operator.cc (MethodResolver::try_hook): use new helper * typecheck/rust-hir-path-probe-expr.cc (PathProbeExpr::probe_bounds): likewise * typecheck/rust-hir-path-probe-type.cc (TypePathProbe::probe_generic): likewise * typecheck/rust-hir-type-check-type.cc (TypeResolveGenericParam::visit): likewise (TypeResolveGenericParam::apply_trait_bounds): track new decl id * typecheck/rust-hir-type-check.h: new helper * typecheck/rust-typecheck-context.cc (TypeCheckContext::predicates_for_type): impl * typecheck/rust-tyty.cc (ParamType::ParamType): new decl id (ParamType::clone): likewise * typecheck/rust-tyty.h: likewise gcc/testsuite/ChangeLog: * rust/compile/issues/4865-1.rs: New test. * rust/compile/issues/4865-2.rs: New test. * rust/compile/projection-bound-decl-id.rs: New test. Signed-off-by: Philip Herron <[email protected]> Diff: --- gcc/rust/typecheck/rust-hir-dot-operator.cc | 6 +- gcc/rust/typecheck/rust-hir-path-probe-expr.cc | 2 +- gcc/rust/typecheck/rust-hir-path-probe-type.cc | 2 +- gcc/rust/typecheck/rust-hir-type-check-type.cc | 2 + gcc/rust/typecheck/rust-hir-type-check.h | 2 + gcc/rust/typecheck/rust-typecheck-context.cc | 81 ++++++++++++++++++++++ gcc/rust/typecheck/rust-tyty.cc | 12 ++-- gcc/rust/typecheck/rust-tyty.h | 8 ++- gcc/testsuite/rust/compile/issues/4865-1.rs | 16 +++++ gcc/testsuite/rust/compile/issues/4865-2.rs | 20 ++++++ .../rust/compile/projection-bound-decl-id.rs | 14 ++++ 11 files changed, 153 insertions(+), 12 deletions(-) diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc index 24b8b8ccc172..9a125eb7bbf7 100644 --- a/gcc/rust/typecheck/rust-hir-dot-operator.cc +++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc @@ -106,7 +106,7 @@ MethodResolver::try_hook (const TyTy::BaseType &r) { rust_debug ("MethodResolver::try_hook get_predicate_items: [%s]", r.debug_str ().c_str ()); - const auto &specified_bounds = r.get_specified_bounds (); + auto specified_bounds = context->predicates_for_type (&r); predicate_items = get_predicate_items (segment_name, r, specified_bounds); if (predicate_items.size () > 0) @@ -117,14 +117,14 @@ MethodResolver::try_hook (const TyTy::BaseType &r) const auto &ref = static_cast<const TyTy::ReferenceType &> (r); const auto &element = ref.get_var_element_type (); const auto &element_ty = *element.get_tyty (); - const auto &specified_bounds = element_ty.get_specified_bounds (); + auto specified_bounds = context->predicates_for_type (&element_ty); predicate_items = get_predicate_items (segment_name, element_ty, specified_bounds); } else if (auto inner = TyTy::try_get_box_inner_type (const_cast<TyTy::BaseType *> (&r))) { - const auto &specified_bounds = (*inner)->get_specified_bounds (); + auto specified_bounds = context->predicates_for_type (*inner); predicate_items = get_predicate_items (segment_name, **inner, specified_bounds); } diff --git a/gcc/rust/typecheck/rust-hir-path-probe-expr.cc b/gcc/rust/typecheck/rust-hir-path-probe-expr.cc index d95c931b1aa8..461f60eaa6c2 100644 --- a/gcc/rust/typecheck/rust-hir-path-probe-expr.cc +++ b/gcc/rust/typecheck/rust-hir-path-probe-expr.cc @@ -193,7 +193,7 @@ PathProbeExpr::probe_bounds () } for (const TyTy::TypeBoundPredicate &predicate : - receiver->get_specified_bounds ()) + context->predicates_for_type (receiver)) { process_predicate_for_candidates (predicate); } diff --git a/gcc/rust/typecheck/rust-hir-path-probe-type.cc b/gcc/rust/typecheck/rust-hir-path-probe-type.cc index 79179627daed..7b18bb80d5fb 100644 --- a/gcc/rust/typecheck/rust-hir-path-probe-type.cc +++ b/gcc/rust/typecheck/rust-hir-path-probe-type.cc @@ -60,7 +60,7 @@ void TypePathProbe::probe_generic () { for (const TyTy::TypeBoundPredicate &predicate : - receiver->get_specified_bounds ()) + TypeCheckContext::get ()->predicates_for_type (receiver)) { auto candidate = process_predicate_for_candidates (predicate); insert_candidate (std::move (candidate)); diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc index 8bf304fa0efc..7ff160b148b6 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-type.cc +++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc @@ -892,6 +892,7 @@ TypeResolveGenericParam::visit (HIR::TypeParam ¶m) resolved = new TyTy::ParamType (param.get_type_representation ().as_string (), param.get_locus (), + param.get_mappings ().get_hirid (), param.get_mappings ().get_hirid (), {}); if (resolve_trait_bounds) @@ -912,6 +913,7 @@ TypeResolveGenericParam::apply_trait_bounds (HIR::TypeParam ¶m, TyTy::ParamType *p = new TyTy::ParamType (param.get_type_representation ().as_string (), param.get_locus (), implicit_id, + pty->get_decl_id (), {} /*empty specified bounds*/); context->insert_implicit_type (implicit_id, p); diff --git a/gcc/rust/typecheck/rust-hir-type-check.h b/gcc/rust/typecheck/rust-hir-type-check.h index c9572b13c0b1..41e02bb00ffa 100644 --- a/gcc/rust/typecheck/rust-hir-type-check.h +++ b/gcc/rust/typecheck/rust-hir-type-check.h @@ -311,6 +311,8 @@ public: void insert_resolved_predicate (HirId id, const TyTy::TypeBoundPredicate &predicate); bool lookup_predicate (HirId id, TyTy::TypeBoundPredicate *result); + std::vector<TyTy::TypeBoundPredicate> + predicates_for_type (const TyTy::BaseType *receiver) const; void insert_query (HirId id); void query_completed (HirId id); diff --git a/gcc/rust/typecheck/rust-typecheck-context.cc b/gcc/rust/typecheck/rust-typecheck-context.cc index fe6bc888cc52..440b8b8050f3 100644 --- a/gcc/rust/typecheck/rust-typecheck-context.cc +++ b/gcc/rust/typecheck/rust-typecheck-context.cc @@ -644,6 +644,87 @@ TypeCheckContext::lookup_predicate (HirId id, TyTy::TypeBoundPredicate *result) return true; } +std::vector<TyTy::TypeBoundPredicate> +TypeCheckContext::predicates_for_type (const TyTy::BaseType *receiver) const +{ + std::vector<TyTy::TypeBoundPredicate> result + = receiver->get_specified_bounds (); + + for (const auto &entry : predicates) + { + const auto &predicate = entry.second; + const auto &args = predicate.get_substitution_arguments (); + if (args.get_mappings ().empty ()) + continue; + + TyTy::BaseType *bound_self = args.get_mappings ().front ().get_tyty (); + if (bound_self == nullptr + || bound_self->get_kind () == TyTy::TypeKind::ERROR) + continue; + + if (receiver->get_kind () != TyTy::TypeKind::PROJECTION + || bound_self->get_kind () != TyTy::TypeKind::PROJECTION) + continue; + + const auto *receiver_projection + = static_cast<const TyTy::ProjectionType *> (receiver); + const auto *bound_projection + = static_cast<const TyTy::ProjectionType *> (bound_self); + + DefId receiver_item_defid = receiver_projection->get_item_defid (); + DefId bound_item_defid = bound_projection->get_item_defid (); + bool same_item = receiver_item_defid == bound_item_defid; + if (!same_item) + continue; + + DefId receiver_trait_defid + = receiver_projection->get_trait_ref ()->get_mappings ().get_defid (); + DefId bound_trait_defid + = bound_projection->get_trait_ref ()->get_mappings ().get_defid (); + bool same_trait = receiver_trait_defid == bound_trait_defid; + if (!same_trait) + continue; + + // Compatibility can unify unrelated parameters. Match their declaration + // identities instead; copies and synthetic bound parameters retain them. + const TyTy::BaseType *receiver_self = receiver_projection->get_self (); + const TyTy::BaseType *bound_self_ty = bound_projection->get_self (); + const auto *receiver_param + = receiver_self->try_as<const TyTy::ParamType> (); + const auto *bound_param = bound_self_ty->try_as<const TyTy::ParamType> (); + + bool either_self_is_param + = receiver_param != nullptr || bound_param != nullptr; + if (either_self_is_param) + { + bool both_selfs_are_param + = receiver_param != nullptr && bound_param != nullptr; + bool same_declaration + = both_selfs_are_param + && receiver_param->get_decl_id () == bound_param->get_decl_id (); + if (!same_declaration) + continue; + } + else + { + bool selfs_compatible = types_compatable ( + TyTy::TyWithLocation (const_cast<TyTy::BaseType *> (receiver_self)), + TyTy::TyWithLocation (const_cast<TyTy::BaseType *> (bound_self_ty)), + UNKNOWN_LOCATION, false, false); + if (!selfs_compatible) + continue; + } + + bool projections_compatible = types_compatable ( + TyTy::TyWithLocation (const_cast<TyTy::BaseType *> (receiver)), + TyTy::TyWithLocation (bound_self), UNKNOWN_LOCATION, false, false); + if (projections_compatible) + result.push_back (predicate); + } + + return result; +} + void TypeCheckContext::insert_query (HirId id) { diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc index 93061b1fcf6f..a9b3dcc5e6ae 100644 --- a/gcc/rust/typecheck/rust-tyty.cc +++ b/gcc/rust/typecheck/rust-tyty.cc @@ -3742,24 +3742,25 @@ PointerType::handle_substitions (SubstitutionArgumentMappings &mappings) // PARAM Type ParamType::ParamType (std::string symbol, location_t locus, HirId ref, + HirId decl_id, std::vector<TypeBoundPredicate> specified_bounds, std::set<HirId> refs) : BaseGeneric (ref, ref, KIND, {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol), locus}, std::move (specified_bounds), refs), - is_trait_self (false), symbol (symbol) + decl_id (decl_id), is_trait_self (false), symbol (symbol) {} ParamType::ParamType (bool is_trait_self, std::string symbol, location_t locus, - HirId ref, HirId ty_ref, + HirId ref, HirId ty_ref, HirId decl_id, std::vector<TypeBoundPredicate> specified_bounds, std::set<HirId> refs) : BaseGeneric (ref, ty_ref, KIND, {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol), locus}, std::move (specified_bounds), refs), - is_trait_self (is_trait_self), symbol (symbol) + decl_id (decl_id), is_trait_self (is_trait_self), symbol (symbol) {} bool @@ -3814,11 +3815,12 @@ ParamType::clone () const bool cycle = Resolver::ScopedPush<HirId>::contains (active, get_ty_ref ()); if (cycle) return new ParamType (is_trait_self, get_symbol (), ident.locus, get_ref (), - get_ty_ref (), {}, get_combined_refs ()); + get_ty_ref (), get_decl_id (), {}, + get_combined_refs ()); Resolver::ScopedPush<HirId> guard (active, get_ty_ref ()); return new ParamType (is_trait_self, get_symbol (), ident.locus, get_ref (), - get_ty_ref (), get_specified_bounds (), + get_ty_ref (), get_decl_id (), get_specified_bounds (), get_combined_refs ()); } diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h index c4f0ad59a658..98daa831bab8 100644 --- a/gcc/rust/typecheck/rust-tyty.h +++ b/gcc/rust/typecheck/rust-tyty.h @@ -515,12 +515,12 @@ class ParamType : public BaseGeneric public: static constexpr auto KIND = TypeKind::PARAM; - ParamType (std::string symbol, location_t locus, HirId ref, + ParamType (std::string symbol, location_t locus, HirId ref, HirId decl_id, std::vector<TypeBoundPredicate> specified_bounds, std::set<HirId> refs = std::set<HirId> ()); ParamType (bool is_trait_self, std::string symbol, location_t locus, - HirId ref, HirId ty_ref, + HirId ref, HirId ty_ref, HirId decl_id, std::vector<TypeBoundPredicate> specified_bounds, std::set<HirId> refs = std::set<HirId> ()); @@ -546,7 +546,11 @@ public: void set_implicit_self_trait (); bool is_implicit_self_trait () const; + HirId get_decl_id () const { return decl_id; } + private: + // Identity of the original HIR parameter, preserved through substitution. + const HirId decl_id; bool is_trait_self; std::string symbol; }; diff --git a/gcc/testsuite/rust/compile/issues/4865-1.rs b/gcc/testsuite/rust/compile/issues/4865-1.rs new file mode 100644 index 000000000000..f6bdcb274bd8 --- /dev/null +++ b/gcc/testsuite/rust/compile/issues/4865-1.rs @@ -0,0 +1,16 @@ +#![feature(no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +trait Deref { + type Target: ?Sized; +} +trait PartialOrd<Rhs: ?Sized = Self> { + fn gt(&self, rhs: &Rhs) -> bool; +} +pub fn compare<P: Deref, Q: Deref>(x: &P::Target, y: &Q::Target) -> bool +where + P::Target: PartialOrd<Q::Target>, +{ + P::Target::gt(x, y) +} diff --git a/gcc/testsuite/rust/compile/issues/4865-2.rs b/gcc/testsuite/rust/compile/issues/4865-2.rs new file mode 100644 index 000000000000..a89bf67680ce --- /dev/null +++ b/gcc/testsuite/rust/compile/issues/4865-2.rs @@ -0,0 +1,20 @@ +#![feature(no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +trait Deref { + type Target: ?Sized; +} +trait PartialOrd<Rhs: ?Sized = Self> { + fn gt(&self, rhs: &Rhs) -> bool; +} +pub fn compare<P: Deref, Q: Deref>(x: &P::Target, y: &Q::Target) -> bool +where + P::Target: PartialOrd<Q::Target>, +{ + P::Target::gt(x, y) +} +pub fn compare_unbounded<R: Deref, S: Deref>(x: &R::Target, y: &S::Target) -> bool { + R::Target::gt(x, y) + // { dg-error "failed to resolve path segment using an impl Probe" "" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/rust/compile/projection-bound-decl-id.rs b/gcc/testsuite/rust/compile/projection-bound-decl-id.rs new file mode 100644 index 000000000000..96f057e23795 --- /dev/null +++ b/gcc/testsuite/rust/compile/projection-bound-decl-id.rs @@ -0,0 +1,14 @@ +// { dg-options "-w" } + +#![feature(no_core, lang_items)] +#![no_core] +#[lang = "sized"] +pub trait Sized {} +trait Source { type Item; } +trait Marker { fn check(&self); } +fn bounded<T: Source>(x: &T::Item) where T::Item: Marker { + T::Item::check(x); +} +fn unbounded<T: Source>(x: &T::Item) { + T::Item::check(x); // { dg-error "failed to resolve path segment" } +}
