]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: support deref cycles to resolve to trait items
authorPhilip Herron <herron.philip@googlemail.com>
Tue, 23 May 2023 16:55:32 +0000 (17:55 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 16 Jan 2024 17:37:21 +0000 (18:37 +0100)
Fixes #2190

gcc/rust/ChangeLog:

* backend/rust-compile-expr.cc (HIRCompileBase::resolve_deref_adjustment):
reuse op overload code
* typecheck/rust-autoderef.cc (Adjuster::try_deref_type): update
(Adjuster::try_raw_deref_type): likewise
* typecheck/rust-autoderef.h: likewise
* typecheck/rust-hir-dot-operator.cc (MethodResolver::select):
improve debug loging

gcc/testsuite/ChangeLog:

* rust/compile/issue-2190-1.rs: New test.
* rust/compile/issue-2190-2.rs: New test.

Signed-off-by: Philip Herron <herron.philip@googlemail.com>
gcc/rust/backend/rust-compile-expr.cc
gcc/rust/typecheck/rust-autoderef.cc
gcc/rust/typecheck/rust-autoderef.h
gcc/rust/typecheck/rust-hir-dot-operator.cc
gcc/testsuite/rust/compile/issue-2190-1.rs [new file with mode: 0644]
gcc/testsuite/rust/compile/issue-2190-2.rs [new file with mode: 0644]

index 00b9a07000881a4d1da855443d3a011d9f45750a..93dd937da6f77ab99a6f265126f2facf0775bd49 100644 (file)
@@ -2367,15 +2367,8 @@ HIRCompileBase::resolve_deref_adjustment (Resolver::Adjustment &adjustment,
   rust_assert (adjustment.has_operator_overload ());
 
   TyTy::FnType *lookup = adjustment.get_deref_operator_fn ();
-  HIR::ImplItem *resolved_item = adjustment.get_deref_hir_item ();
-
-  tree fn_address = error_mark_node;
-  if (!lookup->has_subsititions_defined ())
-    fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, nullptr,
-                                                  true, locus);
-  else
-    fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, lookup,
-                                                  true, locus);
+  TyTy::BaseType *receiver = adjustment.get_actual ();
+  tree fn_address = resolve_method_address (lookup, receiver, locus);
 
   // does it need a reference to call
   tree adjusted_argument = expression;
index adaf575b2c15c39f98ca58394b8670177edcc9ff..2f21d2685e4f3c6f318a4c58f5992383ad41bc90 100644 (file)
@@ -28,7 +28,7 @@ namespace Resolver {
 static bool
 resolve_operator_overload_fn (
   Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *ty,
-  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  TyTy::FnType **resolved_fn,
   Adjustment::AdjustmentType *requires_ref_adjustment);
 
 TyTy::BaseType *
@@ -44,19 +44,18 @@ Adjustment
 Adjuster::try_deref_type (TyTy::BaseType *ty,
                          Analysis::RustLangItem::ItemType deref_lang_item)
 {
-  HIR::ImplItem *impl_item = nullptr;
   TyTy::FnType *fn = nullptr;
   Adjustment::AdjustmentType requires_ref_adjustment
     = Adjustment::AdjustmentType::ERROR;
   bool operator_overloaded
-    = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item,
+    = resolve_operator_overload_fn (deref_lang_item, ty, &fn,
                                    &requires_ref_adjustment);
   if (!operator_overloaded)
     {
       return Adjustment::get_error ();
     }
 
-  auto resolved_base = fn->get_return_type ()->clone ();
+  auto resolved_base = fn->get_return_type ()->destructure ();
   bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF;
   if (!is_valid_type)
     return Adjustment::get_error ();
@@ -81,7 +80,7 @@ Adjuster::try_deref_type (TyTy::BaseType *ty,
     }
 
   return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty,
-                                                      ref_base, fn, impl_item,
+                                                      ref_base, fn,
                                                       requires_ref_adjustment);
 }
 
@@ -94,7 +93,7 @@ Adjuster::try_raw_deref_type (TyTy::BaseType *ty)
 
   const TyTy::ReferenceType *ref_base
     = static_cast<const TyTy::ReferenceType *> (ty);
-  auto infered = ref_base->get_base ()->clone ();
+  auto infered = ref_base->get_base ()->destructure ();
 
   return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
 }
@@ -122,8 +121,8 @@ Adjuster::try_unsize_type (TyTy::BaseType *ty)
 
 static bool
 resolve_operator_overload_fn (
-  Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *ty,
-  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  Analysis::RustLangItem::ItemType lang_item_type, TyTy::BaseType *lhs,
+  TyTy::FnType **resolved_fn,
   Adjustment::AdjustmentType *requires_ref_adjustment)
 {
   auto context = TypeCheckContext::get ();
@@ -139,35 +138,69 @@ resolve_operator_overload_fn (
   if (!lang_item_defined)
     return false;
 
+  // we might be in a static or const context and unknown is fine
+  TypeCheckContextItem current_context = TypeCheckContextItem::get_error ();
+  if (context->have_function_context ())
+    {
+      current_context = context->peek_context ();
+    }
+
+  // this flags stops recurisve calls to try and deref when none is available
+  // which will cause an infinite loop
+  bool autoderef_flag = true;
   auto segment = HIR::PathIdentSegment (associated_item_name);
-  auto candidates
-    = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name),
-                            true);
+  auto candidates = MethodResolver::Probe (lhs, segment, autoderef_flag);
+
+  // remove any recursive candidates
+  std::set<MethodCandidate> resolved_candidates;
+  for (auto &c : candidates)
+    {
+      const TyTy::BaseType *candidate_type = c.candidate.ty;
+      rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF);
+
+      const TyTy::FnType &fn
+       = *static_cast<const TyTy::FnType *> (candidate_type);
 
-  bool have_implementation_for_lang_item = !candidates.empty ();
+      DefId current_fn_defid = current_context.get_defid ();
+      bool recursive_candidated = fn.get_id () == current_fn_defid;
+      if (!recursive_candidated)
+       {
+         resolved_candidates.insert (c);
+       }
+    }
+
+  bool have_implementation_for_lang_item = resolved_candidates.size () > 0;
   if (!have_implementation_for_lang_item)
     return false;
 
-  // multiple candidates?
-  if (candidates.size () > 1)
+  if (resolved_candidates.size () > 1)
     {
-      // error out? probably not for this case
+      // no need to error out as we are just trying to see if there is a fit
       return false;
     }
 
   // Get the adjusted self
-  auto candidate = *candidates.begin ();
-  Adjuster adj (ty);
+  MethodCandidate candidate = *resolved_candidates.begin ();
+  Adjuster adj (lhs);
   TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
 
-  // is this the case we are recursive
-  // handle the case where we are within the impl block for this
-  // lang_item otherwise we end up with a recursive operator overload
-  // such as the i32 operator overload trait
-  TypeCheckContextItem fn_context = context->peek_context ();
-  if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+  PathProbeCandidate &resolved_candidate = candidate.candidate;
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (fn->is_method ());
+
+  rust_debug ("is_impl_item_candidate: %s",
+             resolved_candidate.is_impl_candidate () ? "true" : "false");
+
+  // in the case where we resolve to a trait bound we have to be careful we are
+  // able to do so there is a case where we are currently resolving the deref
+  // operator overload function which is generic and this might resolve to the
+  // trait item of deref which is not valid as its just another recursive case
+  if (current_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
     {
-      auto &impl_item = fn_context.get_impl_item ();
+      auto &impl_item = current_context.get_impl_item ();
       HIR::ImplBlock *parent = impl_item.first;
       HIR::Function *fn = impl_item.second;
 
@@ -201,24 +234,17 @@ resolve_operator_overload_fn (
        }
     }
 
-  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
-
-  // rust only support impl item deref operator overloading ie you must have an
-  // impl block for it
-  rust_assert (candidate.candidate.type
-              == PathProbeCandidate::CandidateType::IMPL_FUNC);
-  *impl_item = candidate.candidate.item.impl.impl_item;
-
-  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
-  TyTy::BaseType *lookup = lookup_tyty;
-  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
-  rust_assert (fn->is_method ());
+  // we found a valid operator overload
+  fn->prepare_higher_ranked_bounds ();
+  rust_debug ("resolved operator overload to: {%u} {%s}",
+             candidate.candidate.ty->get_ref (),
+             candidate.candidate.ty->debug_str ().c_str ());
 
   if (fn->needs_substitution ())
     {
-      if (ty->get_kind () == TyTy::TypeKind::ADT)
+      if (lhs->get_kind () == TyTy::TypeKind::ADT)
        {
-         const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
+         const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (lhs);
 
          auto s = fn->get_self_type ()->get_root ();
          rust_assert (s->can_eq (adt, false));
@@ -265,8 +291,8 @@ resolve_operator_overload_fn (
          rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
          fn = static_cast<TyTy::FnType *> (lookup);
 
-         Location unify_locus = mappings->lookup_location (ty->get_ref ());
-         unify_site (ty->get_ref (),
+         Location unify_locus = mappings->lookup_location (lhs->get_ref ());
+         unify_site (lhs->get_ref (),
                      TyTy::TyWithLocation (fn->get_self_type ()),
                      TyTy::TyWithLocation (adjusted_self), unify_locus);
 
index 0c00c5a82cd8955c68ff70a9714d16bc1fa039df..bdb1abc92d2fc9e6bd031f20b5e8cc53fefc7800 100644 (file)
@@ -42,18 +42,15 @@ public:
   // ctor for all adjustments except derefs
   Adjustment (AdjustmentType type, TyTy::BaseType *actual,
              TyTy::BaseType *expected)
-    : Adjustment (type, actual, expected, nullptr, nullptr,
-                 AdjustmentType::ERROR)
+    : Adjustment (type, actual, expected, nullptr, AdjustmentType::ERROR)
   {}
 
   static Adjustment get_op_overload_deref_adjustment (
     AdjustmentType type, TyTy::BaseType *actual, TyTy::BaseType *expected,
-    TyTy::FnType *fn, HIR::ImplItem *deref_item,
-    Adjustment::AdjustmentType requires_ref_adjustment)
+    TyTy::FnType *fn, Adjustment::AdjustmentType requires_ref_adjustment)
   {
     rust_assert (type == DEREF || type == DEREF_MUT);
-    return Adjustment (type, actual, expected, fn, deref_item,
-                      requires_ref_adjustment);
+    return Adjustment (type, actual, expected, fn, requires_ref_adjustment);
   }
 
   AdjustmentType get_type () const { return type; }
@@ -107,15 +104,12 @@ public:
     return requires_ref_adjustment;
   }
 
-  HIR::ImplItem *get_deref_hir_item () const { return deref_item; }
-
 private:
   Adjustment (AdjustmentType type, TyTy::BaseType *actual,
              TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn,
-             HIR::ImplItem *deref_item,
              Adjustment::AdjustmentType requires_ref_adjustment)
     : type (type), actual (actual), expected (expected),
-      deref_operator_fn (deref_operator_fn), deref_item (deref_item),
+      deref_operator_fn (deref_operator_fn),
       requires_ref_adjustment (requires_ref_adjustment)
   {}
 
@@ -127,7 +121,6 @@ private:
   //
   // the fn that we are calling
   TyTy::FnType *deref_operator_fn;
-  HIR::ImplItem *deref_item;
   // operator overloads can requre a reference
   Adjustment::AdjustmentType requires_ref_adjustment;
 };
index 251cf8a02e5824cdcc90ca9b284f8aa7f4b5afee..28bc0c0c9a5e848e3d3b0c1b71c21a95a4e4a291 100644 (file)
@@ -66,8 +66,9 @@ MethodResolver::try_hook (const TyTy::BaseType &r)
 bool
 MethodResolver::select (TyTy::BaseType &receiver)
 {
-  rust_debug ("MethodResolver::select reciever=[%s]",
-             receiver.debug_str ().c_str ());
+  rust_debug ("MethodResolver::select reciever=[%s] path=[%s]",
+             receiver.debug_str ().c_str (),
+             segment_name.as_string ().c_str ());
 
   struct impl_item_candidate
   {
diff --git a/gcc/testsuite/rust/compile/issue-2190-1.rs b/gcc/testsuite/rust/compile/issue-2190-1.rs
new file mode 100644 (file)
index 0000000..85091b2
--- /dev/null
@@ -0,0 +1,11 @@
+#[lang = "deref"]
+trait Deref {
+    type Target;
+    fn deref(&self) -> &Self::Target;
+}
+
+fn foo<T: Deref<Target = i32>>(t: &T) -> i32 {
+    t.max(2)
+    // { dg-error "failed to resolve method for .max." "" { target *-*-* } .-1 }
+    // { dg-error "failed to type resolve expression" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/issue-2190-2.rs b/gcc/testsuite/rust/compile/issue-2190-2.rs
new file mode 100644 (file)
index 0000000..48ab72b
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-options "-w" }
+#[lang = "deref"]
+trait Deref {
+    type Target;
+    fn deref(&self) -> &Self::Target;
+}
+
+fn foo<T: Deref<Target = i32>>(t: &T) -> i32 {
+    t.max(2)
+}
+
+impl i32 {
+    fn max(self, other: i32) -> i32 {
+        if self > other {
+            self
+        } else {
+            other
+        }
+    }
+}