Skip to content

Commit 9de17ad

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 93ddab3 commit 9de17ad

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
@@ -2816,10 +2816,25 @@ CompileExpr::visit (HIR::ClosureExpr &expr)
28162816

28172817
// lets ignore state capture for now we need to instantiate the struct anyway
28182818
// then generate the function
2819-
28202819
std::vector<tree> vals;
2821-
// TODO
2822-
// setup argument captures based on the mode?
2820+
for (const auto &capture : closure_tyty->get_captures ())
2821+
{
2822+
// lookup the HirId
2823+
HirId ref = UNKNOWN_HIRID;
2824+
bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
2825+
rust_assert (ok);
2826+
2827+
// lookup the var decl
2828+
Bvariable *var = nullptr;
2829+
bool found = ctx->lookup_var_decl (ref, &var);
2830+
rust_assert (found);
2831+
2832+
// FIXME
2833+
// this should bes based on the closure move-ability
2834+
tree var_expr = var->get_tree (expr.get_locus ());
2835+
tree val = address_expression (var_expr, expr.get_locus ());
2836+
vals.push_back (val);
2837+
}
28232838

28242839
translated
28252840
= ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
@@ -2866,8 +2881,29 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
28662881
DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
28672882
param_vars.push_back (self_param);
28682883

2884+
// push a new context
2885+
ctx->push_closure_context (expr.get_mappings ().get_hirid ());
2886+
28692887
// setup the implicit argument captures
2870-
// TODO
2888+
size_t idx = 0;
2889+
for (const auto &capture : closure_tyty.get_captures ())
2890+
{
2891+
// lookup the HirId
2892+
HirId ref = UNKNOWN_HIRID;
2893+
bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref);
2894+
rust_assert (ok);
2895+
2896+
// get the assessor
2897+
tree binding = ctx->get_backend ()->struct_field_expression (
2898+
self_param->get_tree (expr.get_locus ()), idx, expr.get_locus ());
2899+
tree indirection = indirect_expression (binding, expr.get_locus ());
2900+
2901+
// insert bindings
2902+
ctx->insert_closure_binding (ref, indirection);
2903+
2904+
// continue
2905+
idx++;
2906+
}
28712907

28722908
// args tuple
28732909
tree args_type
@@ -2897,7 +2933,10 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
28972933
}
28982934

28992935
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
2900-
return error_mark_node;
2936+
{
2937+
ctx->pop_closure_context ();
2938+
return error_mark_node;
2939+
}
29012940

29022941
// lookup locals
29032942
HIR::Expr *function_body = expr.get_expr ().get ();
@@ -2964,6 +3003,7 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
29643003
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
29653004
DECL_SAVED_TREE (fndecl) = bind_tree;
29663005

3006+
ctx->pop_closure_context ();
29673007
ctx->pop_fn ();
29683008
ctx->push_function (fndecl);
29693009

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)