Skip to content

Commit 2f07148

Browse files
philbertydkm
authored andcommitted
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 734517f commit 2f07148

File tree

6 files changed

+172
-6
lines changed

6 files changed

+172
-6
lines changed

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

+47
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

+9
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

+45-5
Original file line numberDiff line numberDiff line change
@@ -2829,10 +2829,25 @@ CompileExpr::visit (HIR::ClosureExpr &expr)
28292829

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

28372852
translated
28382853
= ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
@@ -2879,8 +2894,29 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
28792894
DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
28802895
param_vars.push_back (self_param);
28812896

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

28852921
// args tuple
28862922
tree args_type
@@ -2910,7 +2946,10 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
29102946
}
29112947

29122948
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
2913-
return error_mark_node;
2949+
{
2950+
ctx->pop_closure_context ();
2951+
return error_mark_node;
2952+
}
29142953

29152954
// lookup locals
29162955
HIR::Expr *function_body = expr.get_expr ().get ();
@@ -2977,6 +3016,7 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
29773016
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
29783017
DECL_SAVED_TREE (fndecl) = bind_tree;
29793018

3019+
ctx->pop_closure_context ();
29803020
ctx->pop_fn ();
29813021
ctx->push_function (fndecl);
29823022

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

+8
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

+30-1
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
}
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)