Skip to content

Commit 11679d0

Browse files
philbertydkm
authored andcommitted
Closure support at CallExpr
Closures's need to generate their specific function and setup their argument passing based on the signiture specified in libcore. We can get this information based on the specified bound on the closure. Addresses #195
1 parent d41bd48 commit 11679d0

File tree

8 files changed

+361
-12
lines changed

8 files changed

+361
-12
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,35 @@ class Context
147147
mono_fns[dId].push_back ({ref, fn});
148148
}
149149

150+
void insert_closure_decl (const TyTy::ClosureType *ref, tree fn)
151+
{
152+
auto dId = ref->get_def_id ();
153+
auto it = mono_closure_fns.find (dId);
154+
if (it == mono_closure_fns.end ())
155+
mono_closure_fns[dId] = {};
156+
157+
mono_closure_fns[dId].push_back ({ref, fn});
158+
}
159+
160+
tree lookup_closure_decl (const TyTy::ClosureType *ref)
161+
{
162+
auto dId = ref->get_def_id ();
163+
auto it = mono_closure_fns.find (dId);
164+
if (it == mono_closure_fns.end ())
165+
return error_mark_node;
166+
167+
for (auto &i : it->second)
168+
{
169+
const TyTy::ClosureType *t = i.first;
170+
tree fn = i.second;
171+
172+
if (ref->is_equal (*t))
173+
return fn;
174+
}
175+
176+
return error_mark_node;
177+
}
178+
150179
bool lookup_function_decl (HirId id, tree *fn, DefId dId = UNKNOWN_DEFID,
151180
const TyTy::BaseType *ref = nullptr,
152181
const std::string &asm_name = std::string ())
@@ -343,6 +372,8 @@ class Context
343372
std::vector<tree> loop_begin_labels;
344373
std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>>
345374
mono_fns;
375+
std::map<DefId, std::vector<std::pair<const TyTy::ClosureType *, tree>>>
376+
mono_closure_fns;
346377
std::map<HirId, tree> implicit_pattern_bindings;
347378
std::map<hashval_t, tree> main_variants;
348379

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

Lines changed: 272 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,9 +1589,7 @@ CompileExpr::visit (HIR::CallExpr &expr)
15891589
}
15901590

15911591
// must be a tuple constructor
1592-
bool is_fn = tyty->get_kind () == TyTy::TypeKind::FNDEF
1593-
|| tyty->get_kind () == TyTy::TypeKind::FNPTR;
1594-
bool is_adt_ctor = !is_fn;
1592+
bool is_adt_ctor = tyty->get_kind () == TyTy::TypeKind::ADT;
15951593
if (is_adt_ctor)
15961594
{
15971595
rust_assert (tyty->get_kind () == TyTy::TypeKind::ADT);
@@ -1692,20 +1690,71 @@ CompileExpr::visit (HIR::CallExpr &expr)
16921690
return true;
16931691
};
16941692

1693+
auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
1694+
1695+
// is this a closure call?
1696+
if (RS_CLOSURE_TYPE_P (TREE_TYPE (fn_address)))
1697+
{
1698+
rust_assert (tyty->get_kind () == TyTy::TypeKind::CLOSURE);
1699+
TyTy::ClosureType *closure = static_cast<TyTy::ClosureType *> (tyty);
1700+
1701+
std::vector<tree> tuple_arg_vals;
1702+
for (auto &argument : expr.get_arguments ())
1703+
{
1704+
auto rvalue = CompileExpr::Compile (argument.get (), ctx);
1705+
tuple_arg_vals.push_back (rvalue);
1706+
}
1707+
1708+
tree tuple_args_tyty
1709+
= TyTyResolveCompile::compile (ctx, &closure->get_parameters ());
1710+
tree tuple_args
1711+
= ctx->get_backend ()->constructor_expression (tuple_args_tyty, false,
1712+
tuple_arg_vals, -1,
1713+
expr.get_locus ());
1714+
1715+
// need to apply any autoderef's to the self argument
1716+
HirId autoderef_mappings_id = expr.get_mappings ().get_hirid ();
1717+
std::vector<Resolver::Adjustment> *adjustments = nullptr;
1718+
bool ok
1719+
= ctx->get_tyctx ()->lookup_autoderef_mappings (autoderef_mappings_id,
1720+
&adjustments);
1721+
rust_assert (ok);
1722+
1723+
// apply adjustments for the fn call
1724+
tree self
1725+
= resolve_adjustements (*adjustments, fn_address, expr.get_locus ());
1726+
1727+
// args are always self, and the tuple of the args we are passing where
1728+
// self is the path of the call-expr in this case the fn_address
1729+
std::vector<tree> args;
1730+
args.push_back (self);
1731+
args.push_back (tuple_args);
1732+
1733+
// get the fn call address
1734+
tree closure_call_site = ctx->lookup_closure_decl (closure);
1735+
tree closure_call_address
1736+
= address_expression (closure_call_site, expr.get_locus ());
1737+
translated
1738+
= ctx->get_backend ()->call_expression (closure_call_address, args,
1739+
nullptr /* static chain ?*/,
1740+
expr.get_locus ());
1741+
return;
1742+
}
1743+
16951744
bool is_varadic = false;
16961745
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
16971746
{
16981747
const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
16991748
is_varadic = fn->is_varadic ();
17001749
}
17011750

1702-
size_t required_num_args;
1751+
size_t required_num_args = expr.get_arguments ().size ();
17031752
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
17041753
{
17051754
const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
17061755
required_num_args = fn->num_params ();
17071756
}
1708-
else
1757+
else if (tyty->get_kind () == TyTy::TypeKind::FNPTR)
17091758
{
17101759
const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (tyty);
17111760
required_num_args = fn->num_params ();
@@ -1746,8 +1795,7 @@ CompileExpr::visit (HIR::CallExpr &expr)
17461795
args.push_back (rvalue);
17471796
}
17481797

1749-
// must be a call to a function
1750-
auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
1798+
// must be a regular call to a function
17511799
translated = ctx->get_backend ()->call_expression (fn_address, args, nullptr,
17521800
expr.get_locus ());
17531801
}
@@ -2806,7 +2854,223 @@ CompileExpr::visit (HIR::ArrayIndexExpr &expr)
28062854
void
28072855
CompileExpr::visit (HIR::ClosureExpr &expr)
28082856
{
2809-
gcc_unreachable ();
2857+
TyTy::BaseType *closure_expr_ty = nullptr;
2858+
if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
2859+
&closure_expr_ty))
2860+
{
2861+
rust_fatal_error (expr.get_locus (),
2862+
"did not resolve type for this ClosureExpr");
2863+
return;
2864+
}
2865+
rust_assert (closure_expr_ty->get_kind () == TyTy::TypeKind::CLOSURE);
2866+
TyTy::ClosureType *closure_tyty
2867+
= static_cast<TyTy::ClosureType *> (closure_expr_ty);
2868+
tree compiled_closure_tyty = TyTyResolveCompile::compile (ctx, closure_tyty);
2869+
2870+
// generate closure function
2871+
generate_closure_function (expr, *closure_tyty, compiled_closure_tyty);
2872+
2873+
// lets ignore state capture for now we need to instantiate the struct anyway
2874+
// then generate the function
2875+
2876+
std::vector<tree> vals;
2877+
// TODO
2878+
// setup argument captures based on the mode?
2879+
2880+
translated
2881+
= ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
2882+
vals, -1, expr.get_locus ());
2883+
}
2884+
2885+
tree
2886+
CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
2887+
TyTy::ClosureType &closure_tyty,
2888+
tree compiled_closure_tyty)
2889+
{
2890+
TyTy::FnType *fn_tyty = nullptr;
2891+
tree compiled_fn_type
2892+
= generate_closure_fntype (expr, closure_tyty, compiled_closure_tyty,
2893+
&fn_tyty);
2894+
if (compiled_fn_type == error_mark_node)
2895+
return error_mark_node;
2896+
2897+
const Resolver::CanonicalPath &parent_canonical_path
2898+
= closure_tyty.get_ident ().path;
2899+
Resolver::CanonicalPath path = parent_canonical_path.append (
2900+
Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, "{{closure}}"));
2901+
2902+
std::string ir_symbol_name = path.get ();
2903+
std::string asm_name = ctx->mangle_item (&closure_tyty, path);
2904+
2905+
unsigned int flags = 0;
2906+
tree fndecl
2907+
= ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name, asm_name,
2908+
flags, expr.get_locus ());
2909+
2910+
// insert into the context
2911+
ctx->insert_function_decl (fn_tyty, fndecl);
2912+
ctx->insert_closure_decl (&closure_tyty, fndecl);
2913+
2914+
// setup the parameters
2915+
std::vector<Bvariable *> param_vars;
2916+
2917+
// closure self
2918+
Bvariable *self_param
2919+
= ctx->get_backend ()->parameter_variable (fndecl, "$closure",
2920+
compiled_closure_tyty,
2921+
expr.get_locus ());
2922+
DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
2923+
param_vars.push_back (self_param);
2924+
2925+
// setup the implicit argument captures
2926+
// TODO
2927+
2928+
// args tuple
2929+
tree args_type
2930+
= TyTyResolveCompile::compile (ctx, &closure_tyty.get_parameters ());
2931+
Bvariable *args_param
2932+
= ctx->get_backend ()->parameter_variable (fndecl, "args", args_type,
2933+
expr.get_locus ());
2934+
param_vars.push_back (args_param);
2935+
2936+
// setup the implicit mappings for the arguments. Since argument passing to
2937+
// closure functions is done via passing a tuple but the closure body expects
2938+
// just normal arguments this means we need to destructure them similar to
2939+
// what we do in MatchExpr's. This means when we have a closure-param of a we
2940+
// actually setup the destructure to take from the args tuple
2941+
2942+
tree args_param_expr = args_param->get_tree (expr.get_locus ());
2943+
size_t i = 0;
2944+
for (auto &closure_param : expr.get_params ())
2945+
{
2946+
tree compiled_param_var = ctx->get_backend ()->struct_field_expression (
2947+
args_param_expr, i, closure_param.get_locus ());
2948+
2949+
const HIR::Pattern &param_pattern = *closure_param.get_pattern ();
2950+
ctx->insert_pattern_binding (
2951+
param_pattern.get_pattern_mappings ().get_hirid (), compiled_param_var);
2952+
i++;
2953+
}
2954+
2955+
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
2956+
return error_mark_node;
2957+
2958+
// lookup locals
2959+
HIR::Expr *function_body = expr.get_expr ().get ();
2960+
auto body_mappings = function_body->get_mappings ();
2961+
Resolver::Rib *rib = nullptr;
2962+
bool ok
2963+
= ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
2964+
rust_assert (ok);
2965+
2966+
std::vector<Bvariable *> locals
2967+
= compile_locals_for_block (ctx, *rib, fndecl);
2968+
2969+
tree enclosing_scope = NULL_TREE;
2970+
Location start_location = function_body->get_locus ();
2971+
Location end_location = function_body->get_locus ();
2972+
bool is_block_expr
2973+
= function_body->get_expression_type () == HIR::Expr::ExprType::Block;
2974+
if (is_block_expr)
2975+
{
2976+
HIR::BlockExpr *body = static_cast<HIR::BlockExpr *> (function_body);
2977+
start_location = body->get_locus ();
2978+
end_location = body->get_end_locus ();
2979+
}
2980+
2981+
tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
2982+
start_location, end_location);
2983+
ctx->push_block (code_block);
2984+
2985+
TyTy::BaseType *tyret = &closure_tyty.get_result_type ();
2986+
bool function_has_return = !closure_tyty.get_result_type ().is_unit ();
2987+
Bvariable *return_address = nullptr;
2988+
if (function_has_return)
2989+
{
2990+
tree return_type = TyTyResolveCompile::compile (ctx, tyret);
2991+
2992+
bool address_is_taken = false;
2993+
tree ret_var_stmt = NULL_TREE;
2994+
2995+
return_address = ctx->get_backend ()->temporary_variable (
2996+
fndecl, code_block, return_type, NULL, address_is_taken,
2997+
expr.get_locus (), &ret_var_stmt);
2998+
2999+
ctx->add_statement (ret_var_stmt);
3000+
}
3001+
3002+
ctx->push_fn (fndecl, return_address);
3003+
3004+
if (is_block_expr)
3005+
{
3006+
HIR::BlockExpr *body = static_cast<HIR::BlockExpr *> (function_body);
3007+
compile_function_body (ctx, fndecl, *body, true);
3008+
}
3009+
else
3010+
{
3011+
tree value = CompileExpr::Compile (function_body, ctx);
3012+
tree return_expr
3013+
= ctx->get_backend ()->return_statement (fndecl, {value},
3014+
function_body->get_locus ());
3015+
ctx->add_statement (return_expr);
3016+
}
3017+
3018+
tree bind_tree = ctx->pop_block ();
3019+
3020+
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
3021+
DECL_SAVED_TREE (fndecl) = bind_tree;
3022+
3023+
ctx->pop_fn ();
3024+
ctx->push_function (fndecl);
3025+
3026+
return fndecl;
3027+
}
3028+
3029+
tree
3030+
CompileExpr::generate_closure_fntype (HIR::ClosureExpr &expr,
3031+
const TyTy::ClosureType &closure_tyty,
3032+
tree compiled_closure_tyty,
3033+
TyTy::FnType **fn_tyty)
3034+
{
3035+
// grab the specified_bound
3036+
rust_assert (closure_tyty.num_specified_bounds () == 1);
3037+
const TyTy::TypeBoundPredicate &predicate
3038+
= *closure_tyty.get_specified_bounds ().begin ();
3039+
3040+
// ensure the fn_once_output associated type is set
3041+
closure_tyty.setup_fn_once_output ();
3042+
3043+
// the function signature is based on the trait bound that the closure
3044+
// implements which is determined at the type resolution time
3045+
//
3046+
// https://github.com/rust-lang/rust/blob/7807a694c2f079fd3f395821bcc357eee8650071/library/core/src/ops/function.rs#L54-L71
3047+
3048+
TyTy::TypeBoundPredicateItem item = TyTy::TypeBoundPredicateItem::error ();
3049+
if (predicate.get_name ().compare ("FnOnce") == 0)
3050+
{
3051+
item = predicate.lookup_associated_item ("call_once");
3052+
}
3053+
else if (predicate.get_name ().compare ("FnMut") == 0)
3054+
{
3055+
item = predicate.lookup_associated_item ("call_mut");
3056+
}
3057+
else if (predicate.get_name ().compare ("Fn") == 0)
3058+
{
3059+
item = predicate.lookup_associated_item ("call");
3060+
}
3061+
else
3062+
{
3063+
// FIXME error message?
3064+
gcc_unreachable ();
3065+
return error_mark_node;
3066+
}
3067+
3068+
rust_assert (!item.is_error ());
3069+
3070+
TyTy::BaseType *item_tyty = item.get_tyty_for_receiver (&closure_tyty);
3071+
rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF);
3072+
*fn_tyty = static_cast<TyTy::FnType *> (item_tyty);
3073+
return TyTyResolveCompile::compile (ctx, item_tyty);
28103074
}
28113075

28123076
} // namespace Compile

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,16 @@ class CompileExpr : private HIRCompileBase, protected HIR::HIRExpressionVisitor
142142
const TyTy::ArrayType &array_tyty, tree array_type,
143143
HIR::ArrayElemsCopied &elems);
144144

145+
protected:
146+
tree generate_closure_function (HIR::ClosureExpr &expr,
147+
TyTy::ClosureType &closure_tyty,
148+
tree compiled_closure_tyty);
149+
150+
tree generate_closure_fntype (HIR::ClosureExpr &expr,
151+
const TyTy::ClosureType &closure_tyty,
152+
tree compiled_closure_tyty,
153+
TyTy::FnType **fn_tyty);
154+
145155
private:
146156
CompileExpr (Context *ctx);
147157

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,15 @@ TyTyResolveCompile::visit (const TyTy::InferType &)
9797
}
9898

9999
void
100-
TyTyResolveCompile::visit (const TyTy::ClosureType &)
100+
TyTyResolveCompile::visit (const TyTy::ClosureType &type)
101101
{
102-
gcc_unreachable ();
102+
std::vector<Backend::typed_identifier> fields;
103+
tree type_record = ctx->get_backend ()->struct_type (fields);
104+
RS_CLOSURE_FLAG (type_record) = 1;
105+
106+
std::string named_struct_str = type.get_ident ().path.get () + "{{closure}}";
107+
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
108+
type.get_ident ().locus);
103109
}
104110

105111
void

0 commit comments

Comments
 (0)