mirror of
https://gcc.gnu.org/git/gcc.git
synced 2026-02-22 20:01:22 -05:00
gccrs: Closure support at CallExpr
gcc/rust/ChangeLog: * backend/rust-compile-context.h: Add new functions: `insert_closure_decl` and `lookup_closure_decl`. * backend/rust-compile-expr.cc (CompileExpr::visit): Start compiling Closures properly. (CompileExpr::generate_closure_function): New function. (CompileExpr::generate_closure_fntype): Likewise. * backend/rust-compile-expr.h: Declare `generate_closure_function` and `generate_closure_fntype`. * backend/rust-compile-type.cc (TyTyResolveCompile::visit): Visit closure types properly. * backend/rust-mangle.cc (legacy_mangle_name): Add support for closures. * backend/rust-tree.h (RS_CLOSURE_FLAG): Add new tree macro. (RS_CLOSURE_TYPE_P): And checking for it on tree nodes. * typecheck/rust-tyty.cc (ClosureType::is_equal): Add implementation. gcc/testsuite/ChangeLog: * rust/execute/torture/closure1.rs: New test.
This commit is contained in:
committed by
Arthur Cohen
parent
740a199722
commit
699e7e8627
@@ -147,6 +147,35 @@ public:
|
||||
mono_fns[dId].push_back ({ref, fn});
|
||||
}
|
||||
|
||||
void insert_closure_decl (const TyTy::ClosureType *ref, tree fn)
|
||||
{
|
||||
auto dId = ref->get_def_id ();
|
||||
auto it = mono_closure_fns.find (dId);
|
||||
if (it == mono_closure_fns.end ())
|
||||
mono_closure_fns[dId] = {};
|
||||
|
||||
mono_closure_fns[dId].push_back ({ref, fn});
|
||||
}
|
||||
|
||||
tree lookup_closure_decl (const TyTy::ClosureType *ref)
|
||||
{
|
||||
auto dId = ref->get_def_id ();
|
||||
auto it = mono_closure_fns.find (dId);
|
||||
if (it == mono_closure_fns.end ())
|
||||
return error_mark_node;
|
||||
|
||||
for (auto &i : it->second)
|
||||
{
|
||||
const TyTy::ClosureType *t = i.first;
|
||||
tree fn = i.second;
|
||||
|
||||
if (ref->is_equal (*t))
|
||||
return fn;
|
||||
}
|
||||
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
bool lookup_function_decl (HirId id, tree *fn, DefId dId = UNKNOWN_DEFID,
|
||||
const TyTy::BaseType *ref = nullptr,
|
||||
const std::string &asm_name = std::string ())
|
||||
@@ -343,6 +372,8 @@ private:
|
||||
std::vector<tree> loop_begin_labels;
|
||||
std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>>
|
||||
mono_fns;
|
||||
std::map<DefId, std::vector<std::pair<const TyTy::ClosureType *, tree>>>
|
||||
mono_closure_fns;
|
||||
std::map<HirId, tree> implicit_pattern_bindings;
|
||||
std::map<hashval_t, tree> main_variants;
|
||||
|
||||
|
||||
@@ -1589,9 +1589,7 @@ CompileExpr::visit (HIR::CallExpr &expr)
|
||||
}
|
||||
|
||||
// must be a tuple constructor
|
||||
bool is_fn = tyty->get_kind () == TyTy::TypeKind::FNDEF
|
||||
|| tyty->get_kind () == TyTy::TypeKind::FNPTR;
|
||||
bool is_adt_ctor = !is_fn;
|
||||
bool is_adt_ctor = tyty->get_kind () == TyTy::TypeKind::ADT;
|
||||
if (is_adt_ctor)
|
||||
{
|
||||
rust_assert (tyty->get_kind () == TyTy::TypeKind::ADT);
|
||||
@@ -1692,6 +1690,57 @@ CompileExpr::visit (HIR::CallExpr &expr)
|
||||
return true;
|
||||
};
|
||||
|
||||
auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
|
||||
|
||||
// is this a closure call?
|
||||
if (RS_CLOSURE_TYPE_P (TREE_TYPE (fn_address)))
|
||||
{
|
||||
rust_assert (tyty->get_kind () == TyTy::TypeKind::CLOSURE);
|
||||
TyTy::ClosureType *closure = static_cast<TyTy::ClosureType *> (tyty);
|
||||
|
||||
std::vector<tree> tuple_arg_vals;
|
||||
for (auto &argument : expr.get_arguments ())
|
||||
{
|
||||
auto rvalue = CompileExpr::Compile (argument.get (), ctx);
|
||||
tuple_arg_vals.push_back (rvalue);
|
||||
}
|
||||
|
||||
tree tuple_args_tyty
|
||||
= TyTyResolveCompile::compile (ctx, &closure->get_parameters ());
|
||||
tree tuple_args
|
||||
= ctx->get_backend ()->constructor_expression (tuple_args_tyty, false,
|
||||
tuple_arg_vals, -1,
|
||||
expr.get_locus ());
|
||||
|
||||
// need to apply any autoderef's to the self argument
|
||||
HirId autoderef_mappings_id = expr.get_mappings ().get_hirid ();
|
||||
std::vector<Resolver::Adjustment> *adjustments = nullptr;
|
||||
bool ok
|
||||
= ctx->get_tyctx ()->lookup_autoderef_mappings (autoderef_mappings_id,
|
||||
&adjustments);
|
||||
rust_assert (ok);
|
||||
|
||||
// apply adjustments for the fn call
|
||||
tree self
|
||||
= resolve_adjustements (*adjustments, fn_address, expr.get_locus ());
|
||||
|
||||
// args are always self, and the tuple of the args we are passing where
|
||||
// self is the path of the call-expr in this case the fn_address
|
||||
std::vector<tree> args;
|
||||
args.push_back (self);
|
||||
args.push_back (tuple_args);
|
||||
|
||||
// get the fn call address
|
||||
tree closure_call_site = ctx->lookup_closure_decl (closure);
|
||||
tree closure_call_address
|
||||
= address_expression (closure_call_site, expr.get_locus ());
|
||||
translated
|
||||
= ctx->get_backend ()->call_expression (closure_call_address, args,
|
||||
nullptr /* static chain ?*/,
|
||||
expr.get_locus ());
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_varadic = false;
|
||||
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
|
||||
{
|
||||
@@ -1699,13 +1748,13 @@ CompileExpr::visit (HIR::CallExpr &expr)
|
||||
is_varadic = fn->is_varadic ();
|
||||
}
|
||||
|
||||
size_t required_num_args;
|
||||
size_t required_num_args = expr.get_arguments ().size ();
|
||||
if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
|
||||
{
|
||||
const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
|
||||
required_num_args = fn->num_params ();
|
||||
}
|
||||
else
|
||||
else if (tyty->get_kind () == TyTy::TypeKind::FNPTR)
|
||||
{
|
||||
const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (tyty);
|
||||
required_num_args = fn->num_params ();
|
||||
@@ -1746,8 +1795,7 @@ CompileExpr::visit (HIR::CallExpr &expr)
|
||||
args.push_back (rvalue);
|
||||
}
|
||||
|
||||
// must be a call to a function
|
||||
auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
|
||||
// must be a regular call to a function
|
||||
translated = ctx->get_backend ()->call_expression (fn_address, args, nullptr,
|
||||
expr.get_locus ());
|
||||
}
|
||||
@@ -2806,7 +2854,223 @@ CompileExpr::visit (HIR::ArrayIndexExpr &expr)
|
||||
void
|
||||
CompileExpr::visit (HIR::ClosureExpr &expr)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
TyTy::BaseType *closure_expr_ty = nullptr;
|
||||
if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
|
||||
&closure_expr_ty))
|
||||
{
|
||||
rust_fatal_error (expr.get_locus (),
|
||||
"did not resolve type for this ClosureExpr");
|
||||
return;
|
||||
}
|
||||
rust_assert (closure_expr_ty->get_kind () == TyTy::TypeKind::CLOSURE);
|
||||
TyTy::ClosureType *closure_tyty
|
||||
= static_cast<TyTy::ClosureType *> (closure_expr_ty);
|
||||
tree compiled_closure_tyty = TyTyResolveCompile::compile (ctx, closure_tyty);
|
||||
|
||||
// generate closure function
|
||||
generate_closure_function (expr, *closure_tyty, compiled_closure_tyty);
|
||||
|
||||
// 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?
|
||||
|
||||
translated
|
||||
= ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false,
|
||||
vals, -1, expr.get_locus ());
|
||||
}
|
||||
|
||||
tree
|
||||
CompileExpr::generate_closure_function (HIR::ClosureExpr &expr,
|
||||
TyTy::ClosureType &closure_tyty,
|
||||
tree compiled_closure_tyty)
|
||||
{
|
||||
TyTy::FnType *fn_tyty = nullptr;
|
||||
tree compiled_fn_type
|
||||
= generate_closure_fntype (expr, closure_tyty, compiled_closure_tyty,
|
||||
&fn_tyty);
|
||||
if (compiled_fn_type == error_mark_node)
|
||||
return error_mark_node;
|
||||
|
||||
const Resolver::CanonicalPath &parent_canonical_path
|
||||
= closure_tyty.get_ident ().path;
|
||||
Resolver::CanonicalPath path = parent_canonical_path.append (
|
||||
Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, "{{closure}}"));
|
||||
|
||||
std::string ir_symbol_name = path.get ();
|
||||
std::string asm_name = ctx->mangle_item (&closure_tyty, path);
|
||||
|
||||
unsigned int flags = 0;
|
||||
tree fndecl
|
||||
= ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name, asm_name,
|
||||
flags, expr.get_locus ());
|
||||
|
||||
// insert into the context
|
||||
ctx->insert_function_decl (fn_tyty, fndecl);
|
||||
ctx->insert_closure_decl (&closure_tyty, fndecl);
|
||||
|
||||
// setup the parameters
|
||||
std::vector<Bvariable *> param_vars;
|
||||
|
||||
// closure self
|
||||
Bvariable *self_param
|
||||
= ctx->get_backend ()->parameter_variable (fndecl, "$closure",
|
||||
compiled_closure_tyty,
|
||||
expr.get_locus ());
|
||||
DECL_ARTIFICIAL (self_param->get_decl ()) = 1;
|
||||
param_vars.push_back (self_param);
|
||||
|
||||
// setup the implicit argument captures
|
||||
// TODO
|
||||
|
||||
// args tuple
|
||||
tree args_type
|
||||
= TyTyResolveCompile::compile (ctx, &closure_tyty.get_parameters ());
|
||||
Bvariable *args_param
|
||||
= ctx->get_backend ()->parameter_variable (fndecl, "args", args_type,
|
||||
expr.get_locus ());
|
||||
param_vars.push_back (args_param);
|
||||
|
||||
// setup the implicit mappings for the arguments. Since argument passing to
|
||||
// closure functions is done via passing a tuple but the closure body expects
|
||||
// just normal arguments this means we need to destructure them similar to
|
||||
// what we do in MatchExpr's. This means when we have a closure-param of a we
|
||||
// actually setup the destructure to take from the args tuple
|
||||
|
||||
tree args_param_expr = args_param->get_tree (expr.get_locus ());
|
||||
size_t i = 0;
|
||||
for (auto &closure_param : expr.get_params ())
|
||||
{
|
||||
tree compiled_param_var = ctx->get_backend ()->struct_field_expression (
|
||||
args_param_expr, i, closure_param.get_locus ());
|
||||
|
||||
const HIR::Pattern ¶m_pattern = *closure_param.get_pattern ();
|
||||
ctx->insert_pattern_binding (
|
||||
param_pattern.get_pattern_mappings ().get_hirid (), compiled_param_var);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
|
||||
return error_mark_node;
|
||||
|
||||
// lookup locals
|
||||
HIR::Expr *function_body = expr.get_expr ().get ();
|
||||
auto body_mappings = function_body->get_mappings ();
|
||||
Resolver::Rib *rib = nullptr;
|
||||
bool ok
|
||||
= ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
|
||||
rust_assert (ok);
|
||||
|
||||
std::vector<Bvariable *> locals
|
||||
= compile_locals_for_block (ctx, *rib, fndecl);
|
||||
|
||||
tree enclosing_scope = NULL_TREE;
|
||||
Location start_location = function_body->get_locus ();
|
||||
Location end_location = function_body->get_locus ();
|
||||
bool is_block_expr
|
||||
= function_body->get_expression_type () == HIR::Expr::ExprType::Block;
|
||||
if (is_block_expr)
|
||||
{
|
||||
HIR::BlockExpr *body = static_cast<HIR::BlockExpr *> (function_body);
|
||||
start_location = body->get_locus ();
|
||||
end_location = body->get_end_locus ();
|
||||
}
|
||||
|
||||
tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
|
||||
start_location, end_location);
|
||||
ctx->push_block (code_block);
|
||||
|
||||
TyTy::BaseType *tyret = &closure_tyty.get_result_type ();
|
||||
bool function_has_return = !closure_tyty.get_result_type ().is_unit ();
|
||||
Bvariable *return_address = nullptr;
|
||||
if (function_has_return)
|
||||
{
|
||||
tree return_type = TyTyResolveCompile::compile (ctx, tyret);
|
||||
|
||||
bool address_is_taken = false;
|
||||
tree ret_var_stmt = NULL_TREE;
|
||||
|
||||
return_address = ctx->get_backend ()->temporary_variable (
|
||||
fndecl, code_block, return_type, NULL, address_is_taken,
|
||||
expr.get_locus (), &ret_var_stmt);
|
||||
|
||||
ctx->add_statement (ret_var_stmt);
|
||||
}
|
||||
|
||||
ctx->push_fn (fndecl, return_address);
|
||||
|
||||
if (is_block_expr)
|
||||
{
|
||||
HIR::BlockExpr *body = static_cast<HIR::BlockExpr *> (function_body);
|
||||
compile_function_body (ctx, fndecl, *body, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree value = CompileExpr::Compile (function_body, ctx);
|
||||
tree return_expr
|
||||
= ctx->get_backend ()->return_statement (fndecl, {value},
|
||||
function_body->get_locus ());
|
||||
ctx->add_statement (return_expr);
|
||||
}
|
||||
|
||||
tree bind_tree = ctx->pop_block ();
|
||||
|
||||
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
|
||||
DECL_SAVED_TREE (fndecl) = bind_tree;
|
||||
|
||||
ctx->pop_fn ();
|
||||
ctx->push_function (fndecl);
|
||||
|
||||
return fndecl;
|
||||
}
|
||||
|
||||
tree
|
||||
CompileExpr::generate_closure_fntype (HIR::ClosureExpr &expr,
|
||||
const TyTy::ClosureType &closure_tyty,
|
||||
tree compiled_closure_tyty,
|
||||
TyTy::FnType **fn_tyty)
|
||||
{
|
||||
// grab the specified_bound
|
||||
rust_assert (closure_tyty.num_specified_bounds () == 1);
|
||||
const TyTy::TypeBoundPredicate &predicate
|
||||
= *closure_tyty.get_specified_bounds ().begin ();
|
||||
|
||||
// ensure the fn_once_output associated type is set
|
||||
closure_tyty.setup_fn_once_output ();
|
||||
|
||||
// the function signature is based on the trait bound that the closure
|
||||
// implements which is determined at the type resolution time
|
||||
//
|
||||
// https://github.com/rust-lang/rust/blob/7807a694c2f079fd3f395821bcc357eee8650071/library/core/src/ops/function.rs#L54-L71
|
||||
|
||||
TyTy::TypeBoundPredicateItem item = TyTy::TypeBoundPredicateItem::error ();
|
||||
if (predicate.get_name ().compare ("FnOnce") == 0)
|
||||
{
|
||||
item = predicate.lookup_associated_item ("call_once");
|
||||
}
|
||||
else if (predicate.get_name ().compare ("FnMut") == 0)
|
||||
{
|
||||
item = predicate.lookup_associated_item ("call_mut");
|
||||
}
|
||||
else if (predicate.get_name ().compare ("Fn") == 0)
|
||||
{
|
||||
item = predicate.lookup_associated_item ("call");
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME error message?
|
||||
gcc_unreachable ();
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
rust_assert (!item.is_error ());
|
||||
|
||||
TyTy::BaseType *item_tyty = item.get_tyty_for_receiver (&closure_tyty);
|
||||
rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF);
|
||||
*fn_tyty = static_cast<TyTy::FnType *> (item_tyty);
|
||||
return TyTyResolveCompile::compile (ctx, item_tyty);
|
||||
}
|
||||
|
||||
} // namespace Compile
|
||||
|
||||
@@ -142,6 +142,16 @@ protected:
|
||||
const TyTy::ArrayType &array_tyty, tree array_type,
|
||||
HIR::ArrayElemsCopied &elems);
|
||||
|
||||
protected:
|
||||
tree generate_closure_function (HIR::ClosureExpr &expr,
|
||||
TyTy::ClosureType &closure_tyty,
|
||||
tree compiled_closure_tyty);
|
||||
|
||||
tree generate_closure_fntype (HIR::ClosureExpr &expr,
|
||||
const TyTy::ClosureType &closure_tyty,
|
||||
tree compiled_closure_tyty,
|
||||
TyTy::FnType **fn_tyty);
|
||||
|
||||
private:
|
||||
CompileExpr (Context *ctx);
|
||||
|
||||
|
||||
@@ -97,9 +97,15 @@ TyTyResolveCompile::visit (const TyTy::InferType &)
|
||||
}
|
||||
|
||||
void
|
||||
TyTyResolveCompile::visit (const TyTy::ClosureType &)
|
||||
TyTyResolveCompile::visit (const TyTy::ClosureType &type)
|
||||
{
|
||||
gcc_unreachable ();
|
||||
std::vector<Backend::typed_identifier> fields;
|
||||
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}}";
|
||||
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
|
||||
type.get_ident ().locus);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -13,6 +13,8 @@ static const std::string kMangledRef = "$RF$";
|
||||
static const std::string kMangledPtr = "$BP$";
|
||||
static const std::string kMangledLeftSqParen = "$u5b$"; // [
|
||||
static const std::string kMangledRightSqParen = "$u5d$"; // ]
|
||||
static const std::string kMangledLeftBrace = "$u7b$"; // {
|
||||
static const std::string kMangledRightBrace = "$u7d$"; // }
|
||||
static const std::string kQualPathBegin = "_" + kMangledSubstBegin;
|
||||
static const std::string kMangledComma = "$C$";
|
||||
|
||||
@@ -66,6 +68,10 @@ legacy_mangle_name (const std::string &name)
|
||||
m = kMangledLeftSqParen;
|
||||
else if (c == ']')
|
||||
m = kMangledRightSqParen;
|
||||
else if (c == '{')
|
||||
m = kMangledLeftBrace;
|
||||
else if (c == '}')
|
||||
m = kMangledRightBrace;
|
||||
else if (c == ',')
|
||||
m = kMangledComma;
|
||||
else if (c == ':')
|
||||
|
||||
@@ -82,6 +82,11 @@
|
||||
#define SLICE_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_0 (TYPE))
|
||||
|
||||
// lambda?
|
||||
#define RS_CLOSURE_FLAG TREE_LANG_FLAG_1
|
||||
#define RS_CLOSURE_TYPE_P(TYPE) \
|
||||
(TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_1 (TYPE))
|
||||
|
||||
/* Returns true if NODE is a pointer to member function type. */
|
||||
#define TYPE_PTRMEMFUNC_P(NODE) \
|
||||
(TREE_CODE (NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
|
||||
|
||||
@@ -1696,8 +1696,17 @@ ClosureType::can_eq (const BaseType *other, bool emit_errors) const
|
||||
bool
|
||||
ClosureType::is_equal (const BaseType &other) const
|
||||
{
|
||||
gcc_unreachable ();
|
||||
return false;
|
||||
if (other.get_kind () != TypeKind::CLOSURE)
|
||||
return false;
|
||||
|
||||
const ClosureType &other2 = static_cast<const ClosureType &> (other);
|
||||
if (get_def_id () != other2.get_def_id ())
|
||||
return false;
|
||||
|
||||
if (!get_parameters ().is_equal (other2.get_parameters ()))
|
||||
return false;
|
||||
|
||||
return get_result_type ().is_equal (other2.get_result_type ());
|
||||
}
|
||||
|
||||
BaseType *
|
||||
|
||||
18
gcc/testsuite/rust/execute/torture/closure1.rs
Normal file
18
gcc/testsuite/rust/execute/torture/closure1.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
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 main() -> i32 {
|
||||
let closure_annotated = |i: i32| -> i32 { i + 1 };
|
||||
|
||||
let i = 1;
|
||||
closure_annotated(i) - 2
|
||||
}
|
||||
Reference in New Issue
Block a user