https://gcc.gnu.org/g:8f4718f0b5900d1743563a5c4e8f3dcc6cec3468

commit r15-8373-g8f4718f0b5900d1743563a5c4e8f3dcc6cec3468
Author: Liam Naddell <liam.nadd...@mail.utoronto.ca>
Date:   Thu Aug 8 05:31:58 2024 -0400

    gccrs: Dynamic dispatch with supertraits
    
    gcc/rust/ChangeLog:
    
            * backend/rust-compile.cc:
            Modify compute_address_for_trait_item to support supertraits
            * typecheck/rust-tyty.cc:
            Remove auto
    
    gcc/testsuite/ChangeLog:
    
            * rust/compile/trait13.rs:
            Add test for supertraits of supertraits
            * rust/compile/trait14.rs:
            Diamond problem with supertraits test
            * rust/execute/torture/trait14.rs:
            Add test for dynamic dispatch with supertraits
            * rust/execute/torture/trait15.rs:
            Add test for dynamic dispatch with generics
            * rust/execute/torture/trait16.rs:
            Add test for dynamic dispatch with lifetime params 1
            * rust/execute/torture/trait17.rs:
            Add test for dynamic dispatch with lifetime params 2
            * rust/execute/torture/trait18.rs:
            Add test for default implementations with dynamic dispatch and
            supertraits
    
    Signed-off-by: Liam Naddell <liam.nadd...@mail.utoronto.ca>

Diff:
---
 gcc/rust/backend/rust-compile.cc              | 128 +++++++++++---------------
 gcc/rust/typecheck/rust-tyty.cc               |   2 +-
 gcc/testsuite/rust/compile/trait13.rs         |  47 ++++++++++
 gcc/testsuite/rust/compile/trait14.rs         |  51 ++++++++++
 gcc/testsuite/rust/execute/torture/trait14.rs |  47 ++++++++++
 gcc/testsuite/rust/execute/torture/trait15.rs |  56 +++++++++++
 gcc/testsuite/rust/execute/torture/trait16.rs |  52 +++++++++++
 gcc/testsuite/rust/execute/torture/trait17.rs |  54 +++++++++++
 gcc/testsuite/rust/execute/torture/trait18.rs |  56 +++++++++++
 9 files changed, 420 insertions(+), 73 deletions(-)

diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index 20190c35e86f..7e0a9b6920f7 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -241,100 +241,84 @@ HIRCompileBase::compute_address_for_trait_item (
     &receiver_bounds,
   const TyTy::BaseType *receiver, const TyTy::BaseType *root, location_t locus)
 {
-  // There are two cases here one where its an item which has an implementation
-  // within a trait-impl-block. Then there is the case where there is a default
-  // implementation for this within the trait.
-  //
-  // The awkward part here is that this might be a generic trait and we need to
-  // figure out the correct monomorphized type for this so we can resolve the
-  // address of the function , this is stored as part of the
-  // type-bound-predicate
-  //
-  // Algo:
-  // check if there is an impl-item for this trait-item-ref first
-  // else assert that the trait-item-ref has an implementation
-  //
-  // FIXME this does not support super traits
-
   TyTy::TypeBoundPredicateItem predicate_item
     = predicate->lookup_associated_item (ref->get_identifier ());
   rust_assert (!predicate_item.is_error ());
 
-  // this is the expected end type
+  // This is the expected end type
   TyTy::BaseType *trait_item_type = predicate_item.get_tyty_for_receiver 
(root);
   rust_assert (trait_item_type->get_kind () == TyTy::TypeKind::FNDEF);
   TyTy::FnType *trait_item_fntype
     = static_cast<TyTy::FnType *> (trait_item_type);
 
-  // find impl-block for this trait-item-ref
-  HIR::ImplBlock *associated_impl_block = nullptr;
-  const Resolver::TraitReference *predicate_trait_ref = predicate->get ();
+  // Loop through the list of trait references and impls that we satisfy.
+  // We are looking for one that has an implementation for "ref", a trait
+  // item.
   for (auto &item : receiver_bounds)
     {
-      Resolver::TraitReference *trait_ref = item.first;
       HIR::ImplBlock *impl_block = item.second;
-      if (predicate_trait_ref->is_equal (*trait_ref))
+      rust_assert (impl_block != nullptr);
+
+      // Lookup type for potentially associated impl.
+      std::unique_ptr<HIR::Type> &self_type_path = impl_block->get_type ();
+
+      // Checks for empty impl blocks, triggered by Sized trait.
+      if (self_type_path == nullptr)
+       continue;
+
+      // Convert HIR::Type to TyTy::BaseType
+      TyTy::BaseType *self = nullptr;
+      bool ok = ctx->get_tyctx ()->lookup_type (
+       self_type_path->get_mappings ().get_hirid (), &self);
+
+      rust_assert (ok);
+
+      // Look through the relevant bounds on our type, and find which one our
+      // impl block satisfies
+      TyTy::TypeBoundPredicate *self_bound = nullptr;
+      for (auto &bound : self->get_specified_bounds ())
        {
-         associated_impl_block = impl_block;
-         break;
+         const Resolver::TraitReference *bound_ref = bound.get ();
+         const Resolver::TraitReference *specified_ref = predicate->get ();
+         // If this impl is for one of our types or supertypes
+         if (specified_ref->satisfies_bound (*bound_ref))
+           {
+             self_bound = &bound;
+             break;
+           }
        }
-    }
 
-  // FIXME this probably should just return error_mark_node but this helps
-  // debug for now since we are wrongly returning early on type-resolution
-  // failures, until we take advantage of more error types and error_mark_node
-  rust_assert (associated_impl_block != nullptr);
-
-  // lookup self for the associated impl
-  std::unique_ptr<HIR::Type> &self_type_path
-    = associated_impl_block->get_type ();
-  TyTy::BaseType *self = nullptr;
-  bool ok = ctx->get_tyctx ()->lookup_type (
-    self_type_path->get_mappings ().get_hirid (), &self);
-  rust_assert (ok);
-
-  // lookup the predicate item from the self
-  TyTy::TypeBoundPredicate *self_bound = nullptr;
-  for (auto &bound : self->get_specified_bounds ())
-    {
-      const Resolver::TraitReference *bound_ref = bound.get ();
-      const Resolver::TraitReference *specified_ref = predicate->get ();
-      if (bound_ref->is_equal (*specified_ref))
+      // This impl block doesn't help us
+      if (self_bound == nullptr)
+       continue;
+
+      // Find the specific function in the impl block that matches "ref".
+      // This is the one we want to compute the address for.
+      HIR::Function *associated_function = nullptr;
+      for (auto &impl_item : impl_block->get_impl_items ())
        {
-         self_bound = &bound;
-         break;
+         bool is_function = impl_item->get_impl_item_type ()
+                            == HIR::ImplItem::ImplItemType::FUNCTION;
+         if (!is_function)
+           continue;
+
+         HIR::Function *fn = static_cast<HIR::Function *> (impl_item.get ());
+         bool found_associated_item
+           = fn->get_function_name ().as_string ().compare (
+               ref->get_identifier ())
+             == 0;
+         if (found_associated_item)
+           associated_function = fn;
        }
-    }
-  rust_assert (self_bound != nullptr);
-
-  // lookup the associated item from the associated impl block
-  TyTy::TypeBoundPredicateItem associated_self_item
-    = self_bound->lookup_associated_item (ref->get_identifier ());
-  rust_assert (!associated_self_item.is_error ());
 
-  // Lookup the impl-block for the associated impl_item if it exists
-  HIR::Function *associated_function = nullptr;
-  for (auto &impl_item : associated_impl_block->get_impl_items ())
-    {
-      bool is_function = impl_item->get_impl_item_type ()
-                        == HIR::ImplItem::ImplItemType::FUNCTION;
-      if (!is_function)
+      // This impl block satisfies the bound, but doesn't contain the relevant
+      // function. This could happen because of supertraits.
+      if (associated_function == nullptr)
        continue;
 
-      HIR::Function *fn = static_cast<HIR::Function *> (impl_item.get ());
-      bool found_associated_item
-       = fn->get_function_name ().as_string ().compare (ref->get_identifier ())
-         == 0;
-      if (found_associated_item)
-       associated_function = fn;
-    }
-
-  // we found an impl_item for this
-  if (associated_function != nullptr)
-    {
       // lookup the associated type for this item
       TyTy::BaseType *lookup = nullptr;
-      bool ok = ctx->get_tyctx ()->lookup_type (
+      ok = ctx->get_tyctx ()->lookup_type (
        associated_function->get_mappings ().get_hirid (), &lookup);
       rust_assert (ok);
       rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index b877c0c740b1..e0a8745d3a39 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -3792,7 +3792,7 @@ DynamicObjectType::get_object_items () const
   std::vector<
     std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate 
*>>
     items;
-  for (auto &bound : get_specified_bounds ())
+  for (const TypeBoundPredicate &bound : get_specified_bounds ())
     {
       const Resolver::TraitReference *trait = bound.get ();
       std::vector<const Resolver::TraitItemReference *> trait_items;
diff --git a/gcc/testsuite/rust/compile/trait13.rs 
b/gcc/testsuite/rust/compile/trait13.rs
new file mode 100644
index 000000000000..af5f5a6fab40
--- /dev/null
+++ b/gcc/testsuite/rust/compile/trait13.rs
@@ -0,0 +1,47 @@
+// Testing multiple supertraits and calling supertrait methods
+
+struct Foo {
+    my_int: u32,
+}
+
+trait GrandParent {
+    fn grandparent(&self) -> u32;
+}
+
+trait Parent : GrandParent {
+    fn parent(&self) -> bool;
+}
+
+trait Child : Parent {
+    fn child(&self);
+}
+
+impl GrandParent for Foo {
+    fn grandparent(&self) -> u32 {
+        self.my_int
+    }
+}
+
+impl Parent for Foo {
+    fn parent(&self) -> bool {
+        // Call supertrait method
+        return self.grandparent() != 0;
+    }
+}
+
+impl Child for Foo {
+    fn child(&self) {
+        let _ = self;
+    }
+}
+
+pub fn main() {
+    let a = Foo{my_int: 0xfeedf00d};
+    let b: &dyn Child = &a;
+
+    b.parent();
+    b.child();
+
+    // Here to silence bogus compiler warning
+    let _ = a.my_int;
+}
diff --git a/gcc/testsuite/rust/compile/trait14.rs 
b/gcc/testsuite/rust/compile/trait14.rs
new file mode 100644
index 000000000000..c1d42b5d6bb9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/trait14.rs
@@ -0,0 +1,51 @@
+// Testing diamond problem with supertraits
+
+ 
+struct Foo {
+    my_int: u32,
+}
+
+trait GrandParent {
+    fn grandparent(&self);
+}
+
+trait Parent1 : GrandParent {
+    fn parent1(&self);
+}
+
+trait Parent2 : GrandParent {
+    fn parent2(&self);
+}
+
+trait Child : Parent1+Parent2 {
+    fn child(&self);
+}
+
+impl GrandParent for Foo {
+    fn grandparent(&self) { let _ = self; }
+}
+
+impl Parent1 for Foo {
+    fn parent1(&self) { let _ = self; }
+}
+
+impl Parent2 for Foo {
+    fn parent2(&self) { let _ = self; }
+}
+
+impl Child for Foo {
+    fn child(&self) {
+        let _ = self;
+    }
+}
+
+pub fn main() {
+    let a = Foo{my_int: 0xf00dfeed};
+    let b: &dyn Child = &a;
+
+    b.parent1();
+    b.child();
+
+    // Suppress bogus compile warning
+    let _ = a.my_int;
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait14.rs 
b/gcc/testsuite/rust/execute/torture/trait14.rs
new file mode 100644
index 000000000000..759950e6fe2b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait14.rs
@@ -0,0 +1,47 @@
+/* { dg-output "parent123\r*\nchild\r*\n" } */
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+ 
+#[lang = "sized"]
+pub trait Sized {}
+
+struct Foo(i32);
+trait Parent {
+    fn parent(&self);
+}
+
+trait Child : Parent {
+    fn child(&self);
+}
+
+impl Parent for Foo {
+    fn parent(&self) {
+        unsafe {
+            let parent = "parent%i\n\0";
+            let msg = parent as *const str;
+            printf(msg as *const i8,self.0);
+        }
+    }
+}
+
+impl Child for Foo {
+    fn child(&self) {
+        let _ = self;
+        unsafe {
+            let child = "child\n\0";
+            let msg = child as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo(123);
+    let b: &dyn Child = &a;
+
+    b.parent();
+    b.child();
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait15.rs 
b/gcc/testsuite/rust/execute/torture/trait15.rs
new file mode 100644
index 000000000000..53469d78f53a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait15.rs
@@ -0,0 +1,56 @@
+/* { dg-output "parent123\r*\nchild\r*\n" } */
+// Testing generics passing with supertraits
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+ 
+#[lang = "sized"]
+pub trait Sized {}
+
+struct Foo {
+    my_int: u32,
+}
+
+trait Parent<T> {
+    fn parent(&self) -> T;
+}
+
+trait Child<T> : Parent<T> {
+    fn child(&self);
+}
+
+impl Parent<u32> for Foo {
+    fn parent(&self) -> u32 {
+        unsafe {
+            let parent = "parent%i\n\0";
+            let msg = parent as *const str;
+            printf(msg as *const i8,self.my_int);
+            return self.my_int;
+        }
+    }
+}
+
+impl Child<u32> for Foo {
+    fn child(&self) {
+        let _ = self;
+        unsafe {
+            let child = "child\n\0";
+            let msg = child as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo{my_int: 123};
+    let b: &dyn Child<u32> = &a;
+
+    b.parent();
+    b.child();
+
+    //Silence bogus warning
+    let _ = a.my_int;
+    
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait16.rs 
b/gcc/testsuite/rust/execute/torture/trait16.rs
new file mode 100644
index 000000000000..95f48a1efd11
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait16.rs
@@ -0,0 +1,52 @@
+/* { dg-output "parent123\r*\nchild\r*\n" } */
+//Testing lifetimes with supertraits
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+ 
+struct Foo {
+    my_int: u32,
+}
+
+trait Parent {
+    fn parent(&self);
+}
+
+trait Child : Parent {
+    fn child(&self);
+}
+
+impl Parent for Foo {
+    fn parent(&self) {
+        unsafe {
+            let parent = "parent%i\n\0";
+            let msg = parent as *const str;
+            printf(msg as *const i8,self.my_int);
+            return;
+        }
+    }
+}
+
+impl Child for Foo {
+    fn child<'a>(&self) {
+        let _ = self;
+        unsafe {
+            let child = "child\n\0";
+            let msg = child as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo{ my_int: 123};
+    let b: &dyn Child = &a;
+
+    b.parent();
+    b.child();
+
+    let _ = a.my_int;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait17.rs 
b/gcc/testsuite/rust/execute/torture/trait17.rs
new file mode 100644
index 000000000000..a619ef8f62d8
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait17.rs
@@ -0,0 +1,54 @@
+/* { dg-output "parent123\r*\nchild\r*\n" } */
+
+//Second test for lifetimes in supertraits
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+ 
+struct Foo {
+    my_int: u32,
+}
+
+trait Parent {
+    fn parent(&self);
+}
+
+trait Child : Parent {
+    fn child(&self);
+}
+
+impl Parent for Foo {
+    fn parent<'b>(&self) {
+        unsafe {
+            let parent = "parent%i\n\0";
+            let msg = parent as *const str;
+            printf(msg as *const i8,self.my_int);
+            return;
+        }
+    }
+}
+
+impl Child for Foo {
+    fn child(&self) {
+        let _ = self;
+        unsafe {
+            let child = "child\n\0";
+            let msg = child as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo{ my_int: 123};
+    let b: &dyn Child = &a;
+
+    b.parent();
+    b.child();
+
+    // Silence bogus warning
+    let _ = a.my_int;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait18.rs 
b/gcc/testsuite/rust/execute/torture/trait18.rs
new file mode 100644
index 000000000000..46024d8fd6a9
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait18.rs
@@ -0,0 +1,56 @@
+/* { dg-output "parent\r*\nchild\r*\n" } */
+//Testing default implementations with supertraits.
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+ 
+struct Foo {
+    my_int: u32,
+}
+
+trait Parent {
+    fn parent_str(&self) -> &'static str;
+    fn parent(&self) {
+        unsafe {
+            let parent: &'static str = self.parent_str();
+            let msg = parent as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+trait Child : Parent {
+    fn child(&self);
+}
+
+impl Parent for Foo {
+    fn parent_str(&self) -> &'static str {
+        let _ = self;
+        return "parent\n\0";
+    }
+}
+
+impl Child for Foo {
+    fn child(&self) {
+        let _ = self;
+        unsafe {
+            let child = "child\n\0";
+            let msg = child as *const str;
+            printf(msg as *const i8);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo{ my_int: 0xfeedf00d};
+    let b: &dyn Child = &a;
+
+    b.parent();
+    b.child();
+
+    // Bogus warning silencer
+    let _ = a.my_int;
+
+    0
+}

Reply via email to