gccrs: Add check for recursive trait cycles
authorPhilip Herron <philip.herron@embecosm.com>
Mon, 17 Oct 2022 10:35:26 +0000 (11:35 +0100)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 21 Feb 2023 11:36:36 +0000 (12:36 +0100)
gcc/rust/ChangeLog:

* typecheck/rust-hir-trait-resolve.cc (TraitResolver::resolve_trait): Check if a
trait query is currently in progress.
* typecheck/rust-hir-type-check.h (class TraitQueryGuard): Add helpers around
checking for trait queries and inserting them.

gcc/testsuite/ChangeLog:

* rust/compile/issue-1589.rs: New test.

gcc/rust/typecheck/rust-hir-trait-resolve.cc
gcc/rust/typecheck/rust-hir-type-check.h
gcc/testsuite/rust/compile/issue-1589.rs [new file with mode: 0644]

index 1b0bcaa..2ec9b2e 100644 (file)
@@ -141,6 +141,14 @@ TraitResolver::resolve_trait (HIR::Trait *trait_reference)
       return tref;
     }
 
+  DefId trait_id = trait_reference->get_mappings ().get_defid ();
+  if (context->trait_query_in_progress (trait_id))
+    {
+      rust_error_at (trait_reference->get_locus (), "trait cycle detected");
+      return &TraitReference::error_node ();
+    }
+
+  TraitQueryGuard guard (trait_id);
   TyTy::BaseType *self = nullptr;
   std::vector<TyTy::SubstitutionParamMapping> substitutions;
   for (auto &generic_param : trait_reference->get_generic_params ())
@@ -201,8 +209,10 @@ TraitResolver::resolve_trait (HIR::Trait *trait_reference)
              HIR::TraitBound *b
                = static_cast<HIR::TraitBound *> (bound.get ());
 
-             // FIXME this might be recursive we need a check for that
              auto predicate = get_predicate_from_bound (b->get_path ());
+             if (predicate.is_error ())
+               return &TraitReference::error_node ();
+
              specified_bounds.push_back (predicate);
              super_traits.push_back (predicate.get ());
            }
index a1dd805..2b47c67 100644 (file)
@@ -381,6 +381,19 @@ public:
     return querys_in_progress.find (id) != querys_in_progress.end ();
   }
 
+  void insert_trait_query (DefId id) { trait_queries_in_progress.insert (id); }
+
+  void trait_query_completed (DefId id)
+  {
+    trait_queries_in_progress.erase (id);
+  }
+
+  bool trait_query_in_progress (DefId id) const
+  {
+    return trait_queries_in_progress.find (id)
+          != trait_queries_in_progress.end ();
+  }
+
 private:
   TypeCheckContext ();
 
@@ -418,6 +431,7 @@ private:
 
   // query context lookups
   std::set<HirId> querys_in_progress;
+  std::set<DefId> trait_queries_in_progress;
 };
 
 class TypeResolution
@@ -426,6 +440,21 @@ public:
   static void Resolve (HIR::Crate &crate);
 };
 
+class TraitQueryGuard
+{
+public:
+  TraitQueryGuard (DefId id) : id (id), ctx (*TypeCheckContext::get ())
+  {
+    ctx.insert_trait_query (id);
+  }
+
+  ~TraitQueryGuard () { ctx.trait_query_completed (id); }
+
+private:
+  DefId id;
+  TypeCheckContext &ctx;
+};
+
 } // namespace Resolver
 } // namespace Rust
 
diff --git a/gcc/testsuite/rust/compile/issue-1589.rs b/gcc/testsuite/rust/compile/issue-1589.rs
new file mode 100644 (file)
index 0000000..79a5866
--- /dev/null
@@ -0,0 +1,5 @@
+pub trait A: B {}
+// { dg-error "trait cycle detected" "" { target *-*-* } .-1 }
+
+pub trait B: A {}
+// { dg-error "trait cycle detected" "" { target *-*-* } .-1 }