From: Philip Herron <[email protected]>

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]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/884c26f09db98cc5ac1477fc92bfaebd7e2a8aab

The commit has been mentioned in the following issue(s):
 - Rust-GCC/gccrs#4865: https://github.com/Rust-GCC/gccrs/issues/4865

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4921

 gcc/rust/typecheck/rust-hir-dot-operator.cc   |  6 +-
 .../typecheck/rust-hir-path-probe-expr.cc     |  2 +-
 .../typecheck/rust-hir-path-probe-type.cc     |  2 +-
 .../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(-)
 create mode 100644 gcc/testsuite/rust/compile/issues/4865-1.rs
 create mode 100644 gcc/testsuite/rust/compile/issues/4865-2.rs
 create mode 100644 gcc/testsuite/rust/compile/projection-bound-decl-id.rs

diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc 
b/gcc/rust/typecheck/rust-hir-dot-operator.cc
index 24b8b8ccc..9a125eb7b 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 d95c931b1..461f60eaa 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 79179627d..7b18bb80d 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 8bf304fa0..7ff160b14 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 &param)
 
   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 
&param,
       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 c9572b13c..41e02bb00 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 fe6bc888c..440b8b805 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 93061b1fc..a9b3dcc5e 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 c4f0ad59a..98daa831b 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 000000000..f6bdcb274
--- /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 000000000..a89bf6768
--- /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 000000000..96f057e23
--- /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" }
+}

base-commit: 215416773ce7d0dcd27a9593d05563facc5c7e56
-- 
2.55.0

Reply via email to