public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Add initial support for argument capture of closures
@ 2022-12-05  9:53 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-12-05  9:53 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:3053ec366093560a6269aaace61ce77fb8710b01

commit 3053ec366093560a6269aaace61ce77fb8710b01
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Fri Oct 21 15:43:54 2022 +0100

    Add initial support for argument capture of closures
    
    When we have a closure expression that captures a parent function's
    variable we must setup the closure data to contain this. Ignoring
    moveability and mutability requires for now, this patch creates the closure
    structure with fields for each of the captured variables. When it comes to
    compilation of the closure expression in order to support nested closures
    we must setup a context of implicit mappings so that for all path
    resolution we hit this implicit closure mappings lookups code before any
    lookup_var_decl as this decl will not exist so the order here is important
    during path resolution which is a similar problem to match expression
    destructuring.
    
    Fixes #195

Diff:
---
 gcc/rust/backend/rust-compile-context.cc       | 47 ++++++++++++++++++++++++
 gcc/rust/backend/rust-compile-context.h        |  9 +++++
 gcc/rust/backend/rust-compile-expr.cc          | 50 +++++++++++++++++++++++---
 gcc/rust/backend/rust-compile-resolve-path.cc  |  8 +++++
 gcc/rust/backend/rust-compile-type.cc          | 31 +++++++++++++++-
 gcc/testsuite/rust/execute/torture/closure3.rs | 33 +++++++++++++++++
 6 files changed, 172 insertions(+), 6 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-context.cc b/gcc/rust/backend/rust-compile-context.cc
index cb2addf6c21..0687398808d 100644
--- a/gcc/rust/backend/rust-compile-context.cc
+++ b/gcc/rust/backend/rust-compile-context.cc
@@ -142,5 +142,52 @@ Context::type_hasher (tree type)
   return hstate.end ();
 }
 
+void
+Context::push_closure_context (HirId id)
+{
+  auto it = closure_bindings.find (id);
+  rust_assert (it == closure_bindings.end ());
+
+  closure_bindings.insert ({id, {}});
+  closure_scope_bindings.push_back (id);
+}
+
+void
+Context::pop_closure_context ()
+{
+  rust_assert (!closure_scope_bindings.empty ());
+
+  HirId ref = closure_scope_bindings.back ();
+  closure_scope_bindings.pop_back ();
+  closure_bindings.erase (ref);
+}
+
+void
+Context::insert_closure_binding (HirId id, tree expr)
+{
+  rust_assert (!closure_scope_bindings.empty ());
+
+  HirId ref = closure_scope_bindings.back ();
+  closure_bindings[ref].insert ({id, expr});
+}
+
+bool
+Context::lookup_closure_binding (HirId id, tree *expr)
+{
+  if (closure_scope_bindings.empty ())
+    return false;
+
+  HirId ref = closure_scope_bindings.back ();
+  auto it = closure_bindings.find (ref);
+  rust_assert (it != closure_bindings.end ());
+
+  auto iy = it->second.find (id);
+  if (iy == it->second.end ())
+    return false;
+
+  *expr = iy->second;
+  return true;
+}
+
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 658b9a3f595..7744f01dca3 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -345,6 +345,11 @@ public:
     return mangler.mangle_item (ty, path);
   }
 
+  void push_closure_context (HirId id);
+  void pop_closure_context ();
+  void insert_closure_binding (HirId id, tree expr);
+  bool lookup_closure_binding (HirId id, tree *expr);
+
   std::vector<tree> &get_type_decls () { return type_decls; }
   std::vector<::Bvariable *> &get_var_decls () { return var_decls; }
   std::vector<tree> &get_const_decls () { return const_decls; }
@@ -377,6 +382,10 @@ private:
   std::map<HirId, tree> implicit_pattern_bindings;
   std::map<hashval_t, tree> main_variants;
 
+  // closure bindings
+  std::vector<HirId> closure_scope_bindings;
+  std::map<HirId, std::map<HirId, tree>> closure_bindings;
+
   // To GCC middle-end
   std::vector<tree> type_decls;
   std::vector<::Bvariable *> var_decls;
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index b077a12f7a3..e50df63821f 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -2824,10 +2824,25 @@ CompileExpr::visit (HIR::ClosureExpr &expr)
 
   // lets ignore state capture for now we need to instantiate the struct anyway
   // then generate the function
-
   std::vector<tree> vals;
-  // TODO
-  // setup argument captures based on the mode?
+  for (const auto &capture : closure_tyty->get_captures ())
+    {
+      // lookup the HirId
+      HirId ref = UNKNOWN_HIRID;
+      bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
+      rust_assert (ok);
+
+      // lookup the var decl
+      Bvariable *var = nullptr;
+      bool found = ctx->lookup_var_decl (ref, &var);
+      rust_assert (found);
+
+      // FIXME
+      // this should bes based on the closure move-ability
+      tree var_expr = var->get_tree (expr.get_locus ());
+      tree val = address_expression (var_expr, expr.get_locus ());
+      vals.push_back (val);
+    }
 
   translated
     = ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
@@ -2874,8 +2889,29 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
   DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
   param_vars.push_back (self_param);
 
+  // push a new context
+  ctx->push_closure_context (expr.get_mappings ().get_hirid ());
+
   // setup the implicit argument captures
-  // TODO
+  size_t idx = 0;
+  for (const auto &capture : closure_tyty.get_captures ())
+    {
+      // lookup the HirId
+      HirId ref = UNKNOWN_HIRID;
+      bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
+      rust_assert (ok);
+
+      // get the assessor
+      tree binding = ctx->get_backend ()->struct_field_expression (
+	self_param->get_tree (expr.get_locus ()), idx, expr.get_locus ());
+      tree indirection = indirect_expression (binding, expr.get_locus ());
+
+      // insert bindings
+      ctx->insert_closure_binding (ref, indirection);
+
+      // continue
+      idx++;
+    }
 
   // args tuple
   tree args_type
@@ -2905,7 +2941,10 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
     }
 
   if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
+    {
+      ctx->pop_closure_context ();
+      return error_mark_node;
+    }
 
   // lookup locals
   HIR::Expr *function_body = expr.get_expr ().get ();
@@ -2972,6 +3011,7 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
   gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
   DECL_SAVED_TREE (fndecl) = bind_tree;
 
+  ctx->pop_closure_context ();
   ctx->pop_fn ();
   ctx->push_function (fndecl);
 
diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc
index f89da2bdcd5..eaa748ada3e 100644
--- a/gcc/rust/backend/rust-compile-resolve-path.cc
+++ b/gcc/rust/backend/rust-compile-resolve-path.cc
@@ -121,6 +121,14 @@ ResolvePathRef::resolve (const HIR::PathIdentSegment &final_segment,
       return constant_expr;
     }
 
+  // maybe closure binding
+  tree closure_binding = error_mark_node;
+  if (ctx->lookup_closure_binding (ref, &closure_binding))
+    {
+      TREE_USED (closure_binding) = 1;
+      return closure_binding;
+    }
+
   // this might be a variable reference or a function reference
   Bvariable *var = nullptr;
   if (ctx->lookup_var_decl (ref, &var))
diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
index 5e56e0a0b5d..4c72d6f5685 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -19,6 +19,7 @@
 #include "rust-compile-type.h"
 #include "rust-compile-expr.h"
 #include "rust-constexpr.h"
+#include "rust-gcc.h"
 
 #include "tree.h"
 
@@ -99,11 +100,39 @@ TyTyResolveCompile::visit (const TyTy::InferType &)
 void
 TyTyResolveCompile::visit (const TyTy::ClosureType &type)
 {
+  auto mappings = ctx->get_mappings ();
+
   std::vector<Backend::typed_identifier> fields;
+
+  size_t i = 0;
+  for (const auto &capture : type.get_captures ())
+    {
+      // lookup the HirId
+      HirId ref = UNKNOWN_HIRID;
+      bool ok = mappings->lookup_node_to_hir (capture, &ref);
+      rust_assert (ok);
+
+      // lookup the var decl type
+      TyTy::BaseType *lookup = nullptr;
+      bool found = ctx->get_tyctx ()->lookup_type (ref, &lookup);
+      rust_assert (found);
+
+      // FIXME get the var pattern name
+      std::string mappings_name = "capture_" + std::to_string (i);
+
+      // FIXME
+      // this should be based on the closure move-ability
+      tree decl_type = TyTyResolveCompile::compile (ctx, lookup);
+      tree capture_type = build_reference_type (decl_type);
+      fields.push_back (Backend::typed_identifier (mappings_name, capture_type,
+						   type.get_ident ().locus));
+    }
+
   tree type_record = ctx->get_backend ()->struct_type (fields);
   RS_CLOSURE_FLAG (type_record) = 1;
 
-  std::string named_struct_str = type.get_ident ().path.get () + "{{closure}}";
+  std::string named_struct_str
+    = type.get_ident ().path.get () + "::{{closure}}";
   translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
 						type.get_ident ().locus);
 }
diff --git a/gcc/testsuite/rust/execute/torture/closure3.rs b/gcc/testsuite/rust/execute/torture/closure3.rs
new file mode 100644
index 00000000000..62cf3a082cf
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/closure3.rs
@@ -0,0 +1,33 @@
+// { dg-output "3\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "fn_once"]
+pub trait FnOnce<Args> {
+    #[lang = "fn_once_output"]
+    type Output;
+
+    extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+}
+
+fn f<F: FnOnce(i32) -> i32>(g: F) {
+    let call = g(1);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, call);
+    }
+}
+
+pub fn main() -> i32 {
+    let capture = 2;
+    let a = |i: i32| {
+        let b = i + capture;
+        b
+    };
+    f(a);
+    0
+}

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

only message in thread, other threads:[~2022-12-05  9:53 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-05  9:53 [gcc/devel/rust/master] Add initial support for argument capture of closures 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).