public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Support computing the addresses of the object safe items in dyn objects
@ 2022-06-08 11:48 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-06-08 11:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:f4c200fcfec75b3841640263609d2133ead0c518

commit f4c200fcfec75b3841640263609d2133ead0c518
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Tue Nov 2 17:53:22 2021 +0000

    Support computing the addresses of the object safe items in dyn objects
    
    This changes the coercion code for the dyn vtable to compute addresses
    for more complex generic methods such as:
    
    ```
    impl<'a, T> FnLike<&'a T, &'a T> for Identity {
        fn call(&self, arg: &'a T) -> &'a T {
            arg
        }
    }
    ```
    
    In the above example the fntype for the bound FnLike is generic and
    bound to the ParamType 'T' from the generic impl-block. But the bound
    which has a signiture which is compatible for the impl block looks like:
    
    ```
    trait FnLike<A, R> {
        fn call(&self, arg: A) -> R;
    }
    ```
    
    This means when we have the trait-object bound:
    
    ```
    type FnObject<'b> = dyn for<'a> FnLike<&'a isize, &'a isize> + 'b
    ```
    
    We must be able to figure out that the signiture of the impl block item
    
    ```
    fn call(&self, arg: &'a T) -> &'a T;
    ```
    
    T must be substituted with &isize from the arguments used in the bound.
    
    Fixes #786

Diff:
---
 gcc/rust/backend/rust-compile-base.h          |   9 +-
 gcc/rust/backend/rust-compile.cc              | 193 ++++++++++++++++++++------
 gcc/rust/typecheck/rust-tyty-bounds.cc        |   6 +-
 gcc/rust/typecheck/rust-tyty.cc               |  64 ++++++++-
 gcc/rust/typecheck/rust-tyty.h                |  14 +-
 gcc/testsuite/rust/execute/torture/trait12.rs |  41 ++++++
 gcc/testsuite/rust/execute/torture/trait13.rs |  50 +++++++
 7 files changed, 325 insertions(+), 52 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h
index 87ac6ccb72d..3184e27654a 100644
--- a/gcc/rust/backend/rust-compile-base.h
+++ b/gcc/rust/backend/rust-compile-base.h
@@ -209,9 +209,12 @@ protected:
 				     const TyTy::DynamicObjectType *ty,
 				     Location locus);
 
-  Bexpression *
-  compute_address_for_trait_item (const Resolver::TraitItemReference *ref,
-				  const TyTy::BaseType *receiver);
+  Bexpression *compute_address_for_trait_item (
+    const Resolver::TraitItemReference *ref,
+    const TyTy::TypeBoundPredicate *predicate,
+    std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+      &receiver_bounds,
+    const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus);
 };
 
 } // namespace Compile
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index a5d32b191d3..e9aca2c34f1 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -22,6 +22,7 @@
 #include "rust-compile-struct-field-expr.h"
 #include "rust-hir-trait-resolve.h"
 #include "rust-hir-path-probe.h"
+#include "rust-hir-type-bounds.h"
 #include "rust-hir-dot-operator.h"
 
 namespace Rust {
@@ -243,8 +244,9 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
 
       size_t offs = 0;
       const Resolver::TraitItemReference *ref = nullptr;
-      for (auto &item : dyn->get_object_items ())
+      for (auto &bound : dyn->get_object_items ())
 	{
+	  const Resolver::TraitItemReference *item = bound.first;
 	  auto t = item->get_tyty ();
 	  rust_assert (t->get_kind () == TyTy::TypeKind::FNDEF);
 	  auto ft = static_cast<TyTy::FnType *> (t);
@@ -790,18 +792,25 @@ HIRCompileBase::coerce_to_dyn_object (Bexpression *compiled_ref,
   // __trait_object_ptr
   // [list of function ptrs]
 
+  auto root = actual->get_root ();
+  std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+    probed_bounds_for_receiver = Resolver::TypeBoundsProbe::Probe (root);
+
   std::vector<Bexpression *> vals;
   vals.push_back (compiled_ref);
-  for (auto &item : ty->get_object_items ())
+  for (auto &bound : ty->get_object_items ())
     {
-      // compute the address of each method item
-      auto address = compute_address_for_trait_item (item, actual->get_root ());
+      const Resolver::TraitItemReference *item = bound.first;
+      const TyTy::TypeBoundPredicate *predicate = bound.second;
+
+      auto address = compute_address_for_trait_item (item, predicate,
+						     probed_bounds_for_receiver,
+						     actual, root, locus);
       vals.push_back (address);
     }
 
   Bexpression *constructed_trait_object
     = ctx->get_backend ()->constructor_expression (dynamic_object, vals, -1,
-
 						   locus);
 
   fncontext fnctx = ctx->peek_fn ();
@@ -851,44 +860,148 @@ HIRCompileBase::coerce_to_dyn_object (Bexpression *compiled_ref,
 
 Bexpression *
 HIRCompileBase::compute_address_for_trait_item (
-  const Resolver::TraitItemReference *trait_item_ref,
-  const TyTy::BaseType *receiver)
+  const Resolver::TraitItemReference *ref,
+  const TyTy::TypeBoundPredicate *predicate,
+  std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+    &receiver_bounds,
+  const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus)
 {
-  TyTy::BaseType *item_type = trait_item_ref->get_tyty ();
-  rust_assert (item_type->get_kind () == TyTy::TypeKind::FNDEF);
-  TyTy::FnType *fntype = static_cast<TyTy::FnType *> (item_type);
-
-  auto root = receiver->get_root ();
-  HIR::PathIdentSegment segment_name (trait_item_ref->get_identifier ());
-  std::vector<Resolver::PathProbeCandidate> candidates
-    = Resolver::PathProbeType::Probe (root, segment_name, true, false, true);
-
-  // FIXME for default trait item resolution
+  // 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.
   //
-  // if (candidates.size () == 0)
-  //   {
-  //     rust_assert (trait_item_ref->is_optional ()); // has definition
+  // 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
   //
-  //     CompileTraitItem::Compile (self_type,
-  //       			 trait_item_ref->get_hir_trait_item (), ctx,
-  //       			 fntype);
-  //     if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
-  //       {
-  //         return ctx->get_backend ()->error_expression ();
-  //       }
-  //   }
-
-  rust_assert (!candidates.empty ());
-  rust_assert (candidates.size () == 1);
-
-  Resolver::PathProbeCandidate *candidate = &candidates.at (0);
-  rust_assert (candidate->is_impl_candidate ());
-
-  HIR::ImplItem *impl_item = candidate->item.impl.impl_item;
-
-  return CompileInherentImplItem::Compile (receiver->get_root (), impl_item,
-					   ctx, true, fntype, true,
-					   Location () /* FIXME */);
+  // Algo:
+  // check if there is an impl-item for this trait-item-ref first
+  // else assert that the trait-item-ref has an implementation
+
+  TyTy::TypeBoundPredicateItem predicate_item
+    = predicate->lookup_associated_item (ref->get_identifier ());
+  rust_assert (!predicate_item.is_error ());
+
+  // 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 ();
+  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))
+	{
+	  associated_impl_block = impl_block;
+	  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))
+	{
+	  self_bound = &bound;
+	  break;
+	}
+    }
+  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 ());
+
+  // apply any generic arguments from this predicate
+  TyTy::BaseType *mono1 = associated_self_item.get_tyty_for_receiver (self);
+  TyTy::BaseType *mono2 = nullptr;
+  if (predicate->has_generic_args ())
+    {
+      mono2 = associated_self_item.get_tyty_for_receiver (
+	self, predicate->get_generic_args ());
+    }
+  else
+    {
+      mono2 = associated_self_item.get_tyty_for_receiver (self);
+    }
+  rust_assert (mono1 != nullptr);
+  rust_assert (mono1->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *assocated_item_ty1 = static_cast<TyTy::FnType *> (mono1);
+
+  rust_assert (mono2 != nullptr);
+  rust_assert (mono2->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *assocated_item_ty2 = static_cast<TyTy::FnType *> (mono2);
+
+  // 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)
+	continue;
+
+      HIR::Function *fn = static_cast<HIR::Function *> (impl_item.get ());
+      bool found_associated_item
+	= fn->get_function_name ().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 (
+	associated_function->get_mappings ().get_hirid (), &lookup);
+      rust_assert (ok);
+      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+      TyTy::FnType *lookup_fntype = static_cast<TyTy::FnType *> (lookup);
+
+      if (lookup_fntype->needs_substitution ())
+	{
+	  TyTy::SubstitutionArgumentMappings mappings
+	    = assocated_item_ty1->solve_missing_mappings_from_this (
+	      *assocated_item_ty2, *lookup_fntype);
+	  lookup_fntype = lookup_fntype->handle_substitions (mappings);
+	}
+
+      return CompileInherentImplItem::Compile (root, associated_function, ctx,
+					       true, lookup_fntype, true,
+					       locus);
+    }
+
+  // we can only compile trait-items with a body
+  bool trait_item_has_definition = ref->is_optional ();
+  rust_assert (trait_item_has_definition);
+
+  HIR::TraitItem *trait_item = ref->get_hir_trait_item ();
+  return CompileTraitItem::Compile (root, trait_item, ctx, trait_item_fntype,
+				    true, locus);
 }
 
 } // namespace Compile
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
index c2c5e2e9100..cfb96bbd0e4 100644
--- a/gcc/rust/typecheck/rust-tyty-bounds.cc
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -138,7 +138,8 @@ TypeBoundPredicate::lookup_associated_item (const std::string &search) const
 }
 
 BaseType *
-TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
+TypeBoundPredicateItem::get_tyty_for_receiver (
+  const TyTy::BaseType *receiver, const HIR::GenericArgs *bound_args)
 {
   TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty ();
   if (trait_item_tyty->get_kind () == TyTy::TypeKind::FNDEF)
@@ -169,7 +170,8 @@ TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
     return trait_item_tyty;
 
   // FIXME LEAK this should really be const
-  const HIR::GenericArgs *args = parent->get_generic_args ();
+  const HIR::GenericArgs *args
+    = (bound_args != nullptr) ? bound_args : parent->get_generic_args ();
   HIR::GenericArgs *generic_args = new HIR::GenericArgs (*args);
   TyTy::BaseType *resolved
     = Resolver::SubstMapper::Resolve (trait_item_tyty, parent->get_locus (),
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index 184922857aa..03ba9fe82a7 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -585,6 +585,57 @@ SubstitutionRef::solve_mappings_from_receiver_for_self (
 				       mappings.get_locus ());
 }
 
+SubstitutionArgumentMappings
+SubstitutionRef::solve_missing_mappings_from_this (SubstitutionRef &ref,
+						   SubstitutionRef &to)
+{
+  rust_assert (!ref.needs_substitution ());
+  rust_assert (needs_substitution ());
+  rust_assert (get_num_substitutions () == ref.get_num_substitutions ());
+
+  Location locus = used_arguments.get_locus ();
+  std::vector<SubstitutionArg> resolved_mappings;
+
+  std::map<HirId, std::pair<ParamType *, BaseType *>> substs;
+  for (size_t i = 0; i < get_num_substitutions (); i++)
+    {
+      SubstitutionParamMapping &a = substitutions.at (i);
+      SubstitutionParamMapping &b = ref.substitutions.at (i);
+
+      if (a.need_substitution ())
+	{
+	  const BaseType *root = a.get_param_ty ()->resolve ()->get_root ();
+	  rust_assert (root->get_kind () == TyTy::TypeKind::PARAM);
+	  const ParamType *p = static_cast<const TyTy::ParamType *> (root);
+
+	  substs[p->get_ty_ref ()] = {static_cast<ParamType *> (p->clone ()),
+				      b.get_param_ty ()->resolve ()};
+	}
+    }
+
+  for (auto it = substs.begin (); it != substs.end (); it++)
+    {
+      HirId param_id = it->first;
+      BaseType *arg = it->second.second;
+
+      const SubstitutionParamMapping *associate_param = nullptr;
+      for (SubstitutionParamMapping &p : to.substitutions)
+	{
+	  if (p.get_param_ty ()->get_ty_ref () == param_id)
+	    {
+	      associate_param = &p;
+	      break;
+	    }
+	}
+
+      rust_assert (associate_param != nullptr);
+      SubstitutionArg argument (associate_param, arg);
+      resolved_mappings.push_back (std::move (argument));
+    }
+
+  return SubstitutionArgumentMappings (resolved_mappings, locus);
+}
+
 void
 ADTType::accept_vis (TyVisitor &vis)
 {
@@ -1988,7 +2039,7 @@ ParamType::as_string () const
   bool ok = context->lookup_type (get_ty_ref (), &lookup);
   rust_assert (ok);
 
-  return lookup->as_string ();
+  return get_symbol () + "=" + lookup->as_string ();
 }
 
 std::string
@@ -2513,10 +2564,13 @@ DynamicObjectType::is_equal (const BaseType &other) const
   return bounds_compatible (other, Location (), false);
 }
 
-const std::vector<const Resolver::TraitItemReference *>
+const std::vector<
+  std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
 DynamicObjectType::get_object_items () const
 {
-  std::vector<const Resolver::TraitItemReference *> items;
+  std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+    items;
   for (auto &bound : get_specified_bounds ())
     {
       const Resolver::TraitReference *trait = bound.get ();
@@ -2525,7 +2579,7 @@ DynamicObjectType::get_object_items () const
 	  if (item.get_trait_item_type ()
 		== Resolver::TraitItemReference::TraitItemType::FN
 	      && item.is_object_safe ())
-	    items.push_back (&item);
+	    items.push_back ({&item, &bound});
 	}
 
       for (auto &super_trait : trait->get_super_traits ())
@@ -2535,7 +2589,7 @@ DynamicObjectType::get_object_items () const
 	      if (item.get_trait_item_type ()
 		    == Resolver::TraitItemReference::TraitItemType::FN
 		  && item.is_object_safe ())
-		items.push_back (&item);
+		items.push_back ({&item, &bound});
 	    }
 	}
     }
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 5e26b3af549..10bb40d404c 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -166,12 +166,16 @@ public:
     return parent == nullptr || trait_item_ref == nullptr;
   }
 
-  BaseType *get_tyty_for_receiver (const TyTy::BaseType *receiver);
+  BaseType *get_tyty_for_receiver (const TyTy::BaseType *receiver,
+				   const HIR::GenericArgs *bound_args
+				   = nullptr);
 
   const Resolver::TraitItemReference *get_raw_item () const;
 
   bool needs_implementation () const;
 
+  const TypeBoundPredicate *get_parent () const { return parent; }
+
 private:
   const TypeBoundPredicate *parent;
   const Resolver::TraitItemReference *trait_item_ref;
@@ -992,6 +996,11 @@ public:
   SubstitutionArgumentMappings solve_mappings_from_receiver_for_self (
     SubstitutionArgumentMappings &mappings) const;
 
+  // TODO comment
+  SubstitutionArgumentMappings
+  solve_missing_mappings_from_this (SubstitutionRef &ref, SubstitutionRef &to);
+
+  // TODO comment
   BaseType *infer_substitions (Location locus)
   {
     std::vector<SubstitutionArg> args;
@@ -2228,7 +2237,8 @@ public:
   bool is_concrete () const override final { return true; }
 
   // this returns a flat list of items including super trait bounds
-  const std::vector<const Resolver::TraitItemReference *>
+  const std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
   get_object_items () const;
 };
 
diff --git a/gcc/testsuite/rust/execute/torture/trait12.rs b/gcc/testsuite/rust/execute/torture/trait12.rs
new file mode 100644
index 00000000000..f14a966bfba
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait12.rs
@@ -0,0 +1,41 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait FnLike<A, R> {
+    fn call(&self, arg: A) -> R;
+    // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+    // { dg-warning "unused name .arg." "" { target *-*-* } .-2 }
+}
+
+type FnObject<'b> = dyn for<'a> FnLike<&'a isize, &'a isize> + 'b;
+
+struct Identity;
+
+impl<'a, T> FnLike<&'a T, &'a T> for Identity {
+    fn call(&self, arg: &'a T) -> &'a T {
+        // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+        // { dg-warning "unused name" "" { target *-*-* } .-2 }
+        arg
+    }
+}
+
+fn call_repeatedly(f: &FnObject) {
+    let x = 3;
+    let y = f.call(&x);
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *y);
+    }
+}
+
+fn main() -> i32 {
+    call_repeatedly(&Identity);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait13.rs b/gcc/testsuite/rust/execute/torture/trait13.rs
new file mode 100644
index 00000000000..76fb09cae82
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait13.rs
@@ -0,0 +1,50 @@
+/* { dg-output "123\n456\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    fn qux(&self) {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, 456);
+        }
+    }
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn dynamic_dispatch(t: &dyn Bar) {
+    t.baz();
+    t.qux();
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(123);
+
+    let b: &dyn Bar;
+    b = &a;
+    dynamic_dispatch(b);
+
+    0
+}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-06-08 11:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-08 11:48 [gcc/devel/rust/master] Support computing the addresses of the object safe items in dyn objects Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).