aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2022-10-21 15:43:54 +0100
committerArthur Cohen <arthur.cohen@embecosm.com>2023-02-21 12:36:52 +0100
commit92389b46a96ce2274f5d9a3658d771b8b0ad99c9 (patch)
tree10ad93f9c578725e9249f1303f096da0b0c5c016
parenteb1202224f8e9be687589d66011485b5fc582eb5 (diff)
downloadgcc-92389b46a96ce2274f5d9a3658d771b8b0ad99c9.zip
gcc-92389b46a96ce2274f5d9a3658d771b8b0ad99c9.tar.gz
gcc-92389b46a96ce2274f5d9a3658d771b8b0ad99c9.tar.bz2
gccrs: 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 gcc/rust/ChangeLog: * backend/rust-compile-context.cc (Context::push_closure_context): New function. (Context::pop_closure_context): Likewise. (Context::insert_closure_binding): Likewise. (Context::lookup_closure_binding): Likewise. * backend/rust-compile-context.h: Declare new functions and closure mappings. * backend/rust-compile-expr.cc (CompileExpr::visit): Visit captures properly. (CompileExpr::generate_closure_function): Compile captures properly. * backend/rust-compile-resolve-path.cc (ResolvePathRef::resolve): Check for closure bindings. * backend/rust-compile-type.cc (TyTyResolveCompile::visit): Compile capture list's types as well. gcc/testsuite/ChangeLog: * rust/execute/torture/closure3.rs: New test.
-rw-r--r--gcc/rust/backend/rust-compile-context.cc47
-rw-r--r--gcc/rust/backend/rust-compile-context.h9
-rw-r--r--gcc/rust/backend/rust-compile-expr.cc50
-rw-r--r--gcc/rust/backend/rust-compile-resolve-path.cc8
-rw-r--r--gcc/rust/backend/rust-compile-type.cc31
-rw-r--r--gcc/testsuite/rust/execute/torture/closure3.rs33
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 b989741..018897e 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 d2d3a53..8e8fac8 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 8169ba0..436fc92 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -2829,10 +2829,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,
@@ -2879,8 +2894,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
@@ -2910,7 +2946,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 ();
@@ -2977,6 +3016,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 ab8e628..8857df2 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 824cb3a..c8e1d3b 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 0000000..62cf3a0
--- /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
+}