Skip to content

Commit 36a433a

Browse files
committed
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
1 parent d88526b commit 36a433a

File tree

6 files changed

+172
-6
lines changed

6 files changed

+172
-6
lines changed

gcc/rust/backend/rust-compile-context.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,52 @@ Context::type_hasher (tree type)
142142
return hstate.end ();
143143
}
144144

145+
void
146+
Context::push_closure_context (HirId id)
147+
{
148+
auto it = closure_bindings.find (id);
149+
rust_assert (it == closure_bindings.end ());
150+
151+
closure_bindings.insert ({id, {}});
152+
closure_scope_bindings.push_back (id);
153+
}
154+
155+
void
156+
Context::pop_closure_context ()
157+
{
158+
rust_assert (!closure_scope_bindings.empty ());
159+
160+
HirId ref = closure_scope_bindings.back ();
161+
closure_scope_bindings.pop_back ();
162+
closure_bindings.erase (ref);
163+
}
164+
165+
void
166+
Context::insert_closure_binding (HirId id, tree expr)
167+
{
168+
rust_assert (!closure_scope_bindings.empty ());
169+
170+
HirId ref = closure_scope_bindings.back ();
171+
closure_bindings[ref].insert ({id, expr});
172+
}
173+
174+
bool
175+
Context::lookup_closure_binding (HirId id, tree *expr)
176+
{
177+
if (closure_scope_bindings.empty ())
178+
return false;
179+
180+
HirId ref = closure_scope_bindings.back ();
181+
auto it = closure_bindings.find (ref);
182+
rust_assert (it != closure_bindings.end ());
183+
184+
auto iy = it->second.find (id);
185+
if (iy == it->second.end ())
186+
return false;
187+
188+
*expr = iy->second;
189+
return true;
190+
}
191+
145192
} // namespace Compile
146193
} // namespace Rust

gcc/rust/backend/rust-compile-context.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ class Context
345345
return mangler.mangle_item (ty, path);
346346
}
347347

348+
void push_closure_context (HirId id);
349+
void pop_closure_context ();
350+
void insert_closure_binding (HirId id, tree expr);
351+
bool lookup_closure_binding (HirId id, tree *expr);
352+
348353
std::vector<tree> &get_type_decls () { return type_decls; }
349354
std::vector<::Bvariable *> &get_var_decls () { return var_decls; }
350355
std::vector<tree> &get_const_decls () { return const_decls; }
@@ -377,6 +382,10 @@ class Context
377382
std::map<HirId, tree> implicit_pattern_bindings;
378383
std::map<hashval_t, tree> main_variants;
379384

385+
// closure bindings
386+
std::vector<HirId> closure_scope_bindings;
387+
std::map<HirId, std::map<HirId, tree>> closure_bindings;
388+
380389
// To GCC middle-end
381390
std::vector<tree> type_decls;
382391
std::vector<::Bvariable *> var_decls;

gcc/rust/backend/rust-compile-expr.cc

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2824,10 +2824,25 @@ CompileExpr::visit (HIR::ClosureExpr &expr)
28242824

28252825
// lets ignore state capture for now we need to instantiate the struct anyway
28262826
// then generate the function
2827-
28282827
std::vector<tree> vals;
2829-
// TODO
2830-
// setup argument captures based on the mode?
2828+
for (const auto &capture : closure_tyty->get_captures ())
2829+
{
2830+
// lookup the HirId
2831+
HirId ref = UNKNOWN_HIRID;
2832+
bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
2833+
rust_assert (ok);
2834+
2835+
// lookup the var decl
2836+
Bvariable *var = nullptr;
2837+
bool found = ctx->lookup_var_decl (ref, &var);
2838+
rust_assert (found);
2839+
2840+
// FIXME
2841+
// this should bes based on the closure move-ability
2842+
tree var_expr = var->get_tree (expr.get_locus ());
2843+
tree val = address_expression (var_expr, expr.get_locus ());
2844+
vals.push_back (val);
2845+
}
28312846

28322847
translated
28332848
= ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
@@ -2874,8 +2889,29 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
28742889
DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
28752890
param_vars.push_back (self_param);
28762891

2892+
// push a new context
2893+
ctx->push_closure_context (expr.get_mappings ().get_hirid ());
2894+
28772895
// setup the implicit argument captures
2878-
// TODO
2896+
size_t idx = 0;
2897+
for (const auto &capture : closure_tyty.get_captures ())
2898+
{
2899+
// lookup the HirId
2900+
HirId ref = UNKNOWN_HIRID;
2901+
bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
2902+
rust_assert (ok);
2903+
2904+
// get the assessor
2905+
tree binding = ctx->get_backend ()->struct_field_expression (
2906+
self_param->get_tree (expr.get_locus ()), idx, expr.get_locus ());
2907+
tree indirection = indirect_expression (binding, expr.get_locus ());
2908+
2909+
// insert bindings
2910+
ctx->insert_closure_binding (ref, indirection);
2911+
2912+
// continue
2913+
idx++;
2914+
}
28792915

28802916
// args tuple
28812917
tree args_type
@@ -2905,7 +2941,10 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
29052941
}
29062942

29072943
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
2908-
return error_mark_node;
2944+
{
2945+
ctx->pop_closure_context ();
2946+
return error_mark_node;
2947+
}
29092948

29102949
// lookup locals
29112950
HIR::Expr *function_body = expr.get_expr ().get ();
@@ -2972,6 +3011,7 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
29723011
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
29733012
DECL_SAVED_TREE (fndecl) = bind_tree;
29743013

3014+
ctx->pop_closure_context ();
29753015
ctx->pop_fn ();
29763016
ctx->push_function (fndecl);
29773017

gcc/rust/backend/rust-compile-resolve-path.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@ ResolvePathRef::resolve (const HIR::PathIdentSegment &final_segment,
121121
return constant_expr;
122122
}
123123

124+
// maybe closure binding
125+
tree closure_binding = error_mark_node;
126+
if (ctx->lookup_closure_binding (ref, &closure_binding))
127+
{
128+
TREE_USED (closure_binding) = 1;
129+
return closure_binding;
130+
}
131+
124132
// this might be a variable reference or a function reference
125133
Bvariable *var = nullptr;
126134
if (ctx->lookup_var_decl (ref, &var))

gcc/rust/backend/rust-compile-type.cc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "rust-compile-type.h"
2020
#include "rust-compile-expr.h"
2121
#include "rust-constexpr.h"
22+
#include "rust-gcc.h"
2223

2324
#include "tree.h"
2425

@@ -99,11 +100,39 @@ TyTyResolveCompile::visit (const TyTy::InferType &)
99100
void
100101
TyTyResolveCompile::visit (const TyTy::ClosureType &type)
101102
{
103+
auto mappings = ctx->get_mappings ();
104+
102105
std::vector<Backend::typed_identifier> fields;
106+
107+
size_t i = 0;
108+
for (const auto &capture : type.get_captures ())
109+
{
110+
// lookup the HirId
111+
HirId ref = UNKNOWN_HIRID;
112+
bool ok = mappings->lookup_node_to_hir (capture, &ref);
113+
rust_assert (ok);
114+
115+
// lookup the var decl type
116+
TyTy::BaseType *lookup = nullptr;
117+
bool found = ctx->get_tyctx ()->lookup_type (ref, &lookup);
118+
rust_assert (found);
119+
120+
// FIXME get the var pattern name
121+
std::string mappings_name = "capture_" + std::to_string (i);
122+
123+
// FIXME
124+
// this should be based on the closure move-ability
125+
tree decl_type = TyTyResolveCompile::compile (ctx, lookup);
126+
tree capture_type = build_reference_type (decl_type);
127+
fields.push_back (Backend::typed_identifier (mappings_name, capture_type,
128+
type.get_ident ().locus));
129+
}
130+
103131
tree type_record = ctx->get_backend ()->struct_type (fields);
104132
RS_CLOSURE_FLAG (type_record) = 1;
105133

106-
std::string named_struct_str = type.get_ident ().path.get () + "{{closure}}";
134+
std::string named_struct_str
135+
= type.get_ident ().path.get () + "::{{closure}}";
107136
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
108137
type.get_ident ().locus);
109138
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// { dg-output "3\n" }
2+
extern "C" {
3+
fn printf(s: *const i8, ...);
4+
}
5+
6+
#[lang = "fn_once"]
7+
pub trait FnOnce<Args> {
8+
#[lang = "fn_once_output"]
9+
type Output;
10+
11+
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
12+
}
13+
14+
fn f<F: FnOnce(i32) -> i32>(g: F) {
15+
let call = g(1);
16+
unsafe {
17+
let a = "%i\n\0";
18+
let b = a as *const str;
19+
let c = b as *const i8;
20+
21+
printf(c, call);
22+
}
23+
}
24+
25+
pub fn main() -> i32 {
26+
let capture = 2;
27+
let a = |i: i32| {
28+
let b = i + capture;
29+
b
30+
};
31+
f(a);
32+
0
33+
}

0 commit comments

Comments
 (0)