public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/rust/master] Closure support at CallExpr
@ 2022-10-22 10:48 Thomas Schwinge
  0 siblings, 0 replies; only message in thread
From: Thomas Schwinge @ 2022-10-22 10:48 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:678bf852435aca6de25704ca417fab257fe03da9

commit 678bf852435aca6de25704ca417fab257fe03da9
Author: Philip Herron <philip.herron@embecosm.com>
Date:   Wed Oct 19 18:02:36 2022 +0100

    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

Diff:
---
 gcc/rust/backend/rust-compile-context.h        |  31 +++
 gcc/rust/backend/rust-compile-expr.cc          | 280 ++++++++++++++++++++++++-
 gcc/rust/backend/rust-compile-expr.h           |  10 +
 gcc/rust/backend/rust-compile-type.cc          |  10 +-
 gcc/rust/backend/rust-mangle.cc                |   6 +
 gcc/rust/backend/rust-tree.h                   |   5 +
 gcc/rust/typecheck/rust-tyty.cc                |  13 +-
 gcc/testsuite/rust/execute/torture/closure1.rs |  18 ++
 8 files changed, 361 insertions(+), 12 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 415b13e5bd9..658b9a3f595 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -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;
 
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index d66aa5d89f3..738c7b10fa3 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -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 ());
 }
@@ -2801,7 +2849,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 &param_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
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index fee2ba73d70..6938c936240 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -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);
 
diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
index 77d84748eb5..5e56e0a0b5d 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -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
diff --git a/gcc/rust/backend/rust-mangle.cc b/gcc/rust/backend/rust-mangle.cc
index 4d202078a70..83aefa7997a 100644
--- a/gcc/rust/backend/rust-mangle.cc
+++ b/gcc/rust/backend/rust-mangle.cc
@@ -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 == ':')
diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
index 378254c59da..4a110163fd8 100644
--- a/gcc/rust/backend/rust-tree.h
+++ b/gcc/rust/backend/rust-tree.h
@@ -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))
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index 342fa76ad82..e56bdd17b87 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -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 *
diff --git a/gcc/testsuite/rust/execute/torture/closure1.rs b/gcc/testsuite/rust/execute/torture/closure1.rs
new file mode 100644
index 00000000000..62afa78a038
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/closure1.rs
@@ -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
+}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-22 10:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-22 10:48 [gcc/devel/rust/master] Closure support at CallExpr Thomas Schwinge

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).