From patchwork Tue Feb 21 12:02:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Cohen X-Patchwork-Id: 60052 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:eb09:0:0:0:0:0 with SMTP id s9csp1844273wrn; Tue, 21 Feb 2023 04:54:18 -0800 (PST) X-Google-Smtp-Source: AK7set/eCjy3zTljB+bW0sUEplhxT5KtLGRl4ZCS01+CR3fcIhAh5cccg8PpWDNcyNHf0Jw0Th7u X-Received: by 2002:a05:6402:789:b0:4ac:b602:3fa4 with SMTP id d9-20020a056402078900b004acb6023fa4mr4579343edy.17.1676984058418; Tue, 21 Feb 2023 04:54:18 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1676984058; cv=none; d=google.com; s=arc-20160816; b=Bp7S/ocqzOdNtXC9z1R3umbjoMY5yMRY6BaoMVxnP+X7vqUgsedJw1DNkUddhwqBtP /ViPkj4o6wo17PQAmLJzdl5wkU4qtgXqFUYZBN9kojpyTK/Bb/uC4pSouw8L85bb6t2l PxaAuJslGeClOX/esLs+qBoiM9EfLy2/JeOzTvKU7ci4VxswbCxeJXTPdobDSCb5sTqb gOaasN9pZYkxydRIlgxBd84nNckYtGbZJtcRSgV4Go4xruIEv8NIFOFNewv7oKrrpGTp rNWGwl2OLcy2xZ87gd3sojb3rztlOR3bGuPHAF4i6iKCVquCuIDGcznYh8/oC5DvZkTT s1qA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature:dmarc-filter :delivered-to; bh=z9AvgW17NvT7ot9Owzmvx1hcTo625u6Y6O6ERkvImTk=; b=O0EO2MjjTOPGWczfKqi0BCAZX73GErpYmrXSXaIaU7Ncd+dm1X6dhY4wySvhU8cUDN XfaPuG+oIiBXdl/M9DWI2AOBAWp4q2tevnKPCI+V/g3xT6rrMmeC+NKxgUGaAxZiEZpO DQv0IYq5ijpn3hErnHRbbEwcq8NQ6OKfh+wrDXNp5SJ8ng7Dk9Z9+vHOGj+xpenCLbOC W7ZQEA9Jv6GJVw5NBKZKyf1ftyIEInZbubAfcPUyM6Tyxm4ABHIVYwmWtfsFTXoPNvwr G+q11Nqo2ftNyVWkmiVF6+CYvOynQs2S/yN/091MJ+o8/l0gAi/b3Lgq9KSRTifV+MDi Cggw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@embecosm.com header.s=google header.b="H/710H0S"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org" Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id z11-20020aa7c64b000000b004ad119f4af3si3346847edr.412.2023.02.21.04.54.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Feb 2023 04:54:18 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@embecosm.com header.s=google header.b="H/710H0S"; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org" Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DBA3F3894C1F for ; Tue, 21 Feb 2023 12:18:40 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by sourceware.org (Postfix) with ESMTPS id C7A1D3841832 for ; Tue, 21 Feb 2023 12:04:40 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C7A1D3841832 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wr1-x435.google.com with SMTP id t13so4091690wrv.13 for ; Tue, 21 Feb 2023 04:04:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=z9AvgW17NvT7ot9Owzmvx1hcTo625u6Y6O6ERkvImTk=; b=H/710H0SXK3sof9diqlPxisO04ZdOoeamBacGC8dWNoS2LX7kV4cvm9CuDAIeJkX/H v7jE752tIK7g+AqS9x/v2SFStt+A90Q3xpc0Zpi0RemQ0OEsza4pITCz1zKcGUDGgK3i GfYi0JdXPbesVSWjVHzCGuZW29cMsyjD4C3DYbPppI9HAVhQqL19k4rsFTZu/oCJqiJM ZIAMALVzZBgJ14FXkrY9ntui2BjKO0B20Uq8CX9k5k6kL+rGzKP3TdC+o5fUApJWA92Q euP5sE6TZdF7YGdqhNaRoxdN4xBLw1M3SE1JZssqsSoYRqVbRYVfQtEb6fc2xKr4phlu kZoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=z9AvgW17NvT7ot9Owzmvx1hcTo625u6Y6O6ERkvImTk=; b=4V8sd3DoBPLQaTP9dvOKRA0AuBDJSFfmW5/eCxvjyTnwmu/35eaJo2ply8Hp/VSPbC W5pTRjfdu39fCJ4TaKc6VE0kz5V3QciTkXRXSZ4/GDR3Mll7zGYh1h5w+Eyc9TSLuVjJ gONqOnSP8P3MXYkS1kQG04IdGJ/FdvRIS8YLuzhojGjXlptjJ7aDt4UVf+qAGYh+pkTB Msfgsv+M36Xed3RsfX0Z9geBcmgqFz6hqu7ftit3g5jH8edlW57OMQyLSIXNe8SrVN8h VwAJT5qX/JUwM2RYyJqXnMWGHPKWDvUDUH0OBeHqpGzQy+pvLzRHZ1ZFkGhFYLXLG/1+ DWmA== X-Gm-Message-State: AO0yUKU/bA9jr6k2kupre32eHVrg9dLLTvvCicVX9YxhKuKPZ+tx3sSG FwqR33Ebt8W0a+TmGHub3X9Kth8Q86EUCQfQNQ== X-Received: by 2002:a5d:4851:0:b0:2c5:9cb8:d314 with SMTP id n17-20020a5d4851000000b002c59cb8d314mr3499811wrs.56.1676981080156; Tue, 21 Feb 2023 04:04:40 -0800 (PST) Received: from platypus.localdomain ([62.23.166.218]) by smtp.gmail.com with ESMTPSA id c15-20020adffb4f000000b002c55b0e6ef1sm5013811wrs.4.2023.02.21.04.04.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Feb 2023 04:04:39 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, Philip Herron Subject: [committed 097/103] gccrs: Add initial support for argument capture of closures Date: Tue, 21 Feb 2023 13:02:27 +0100 Message-Id: <20230221120230.596966-98-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230221120230.596966-1-arthur.cohen@embecosm.com> References: <20230221120230.596966-1-arthur.cohen@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-14.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: arthur.cohen@embecosm.com Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1758445236341290043?= X-GMAIL-MSGID: =?utf-8?q?1758445236341290043?= From: Philip Herron 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 gcc/rust/ChangeLog: * backend/rust-compile-context.cc (Context::push_closure_context): New function. (Context::pop_closure_context): Likewise. (Context::insert_closure_binding): Likewise. (Context::lookup_closure_binding): Likewise. * backend/rust-compile-context.h: Declare new functions and closure mappings. * backend/rust-compile-expr.cc (CompileExpr::visit): Visit captures properly. (CompileExpr::generate_closure_function): Compile captures properly. * backend/rust-compile-resolve-path.cc (ResolvePathRef::resolve): Check for closure bindings. * backend/rust-compile-type.cc (TyTyResolveCompile::visit): Compile capture list's types as well. gcc/testsuite/ChangeLog: * rust/execute/torture/closure3.rs: New test. --- gcc/rust/backend/rust-compile-context.cc | 47 +++++++++++++++++ gcc/rust/backend/rust-compile-context.h | 9 ++++ gcc/rust/backend/rust-compile-expr.cc | 50 +++++++++++++++++-- gcc/rust/backend/rust-compile-resolve-path.cc | 8 +++ gcc/rust/backend/rust-compile-type.cc | 31 +++++++++++- .../rust/execute/torture/closure3.rs | 33 ++++++++++++ 6 files changed, 172 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/rust/execute/torture/closure3.rs diff --git a/gcc/rust/backend/rust-compile-context.cc b/gcc/rust/backend/rust-compile-context.cc index b989741fe4b..018897e39b2 100644 --- a/gcc/rust/backend/rust-compile-context.cc +++ b/gcc/rust/backend/rust-compile-context.cc @@ -142,5 +142,52 @@ Context::type_hasher (tree type) return hstate.end (); } +void +Context::push_closure_context (HirId id) +{ + auto it = closure_bindings.find (id); + rust_assert (it == closure_bindings.end ()); + + closure_bindings.insert ({id, {}}); + closure_scope_bindings.push_back (id); +} + +void +Context::pop_closure_context () +{ + rust_assert (!closure_scope_bindings.empty ()); + + HirId ref = closure_scope_bindings.back (); + closure_scope_bindings.pop_back (); + closure_bindings.erase (ref); +} + +void +Context::insert_closure_binding (HirId id, tree expr) +{ + rust_assert (!closure_scope_bindings.empty ()); + + HirId ref = closure_scope_bindings.back (); + closure_bindings[ref].insert ({id, expr}); +} + +bool +Context::lookup_closure_binding (HirId id, tree *expr) +{ + if (closure_scope_bindings.empty ()) + return false; + + HirId ref = closure_scope_bindings.back (); + auto it = closure_bindings.find (ref); + rust_assert (it != closure_bindings.end ()); + + auto iy = it->second.find (id); + if (iy == it->second.end ()) + return false; + + *expr = iy->second; + return true; +} + } // namespace Compile } // namespace Rust diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index d2d3a53f182..8e8fac80d59 100644 --- a/gcc/rust/backend/rust-compile-context.h +++ b/gcc/rust/backend/rust-compile-context.h @@ -345,6 +345,11 @@ public: return mangler.mangle_item (ty, path); } + void push_closure_context (HirId id); + void pop_closure_context (); + void insert_closure_binding (HirId id, tree expr); + bool lookup_closure_binding (HirId id, tree *expr); + std::vector &get_type_decls () { return type_decls; } std::vector<::Bvariable *> &get_var_decls () { return var_decls; } std::vector &get_const_decls () { return const_decls; } @@ -377,6 +382,10 @@ private: std::map implicit_pattern_bindings; std::map main_variants; + // closure bindings + std::vector closure_scope_bindings; + std::map> closure_bindings; + // To GCC middle-end std::vector type_decls; std::vector<::Bvariable *> var_decls; diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 8169ba02b84..436fc924a13 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -2829,10 +2829,25 @@ CompileExpr::visit (HIR::ClosureExpr &expr) // lets ignore state capture for now we need to instantiate the struct anyway // then generate the function - std::vector vals; - // TODO - // setup argument captures based on the mode? + for (const auto &capture : closure_tyty->get_captures ()) + { + // lookup the HirId + HirId ref = UNKNOWN_HIRID; + bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref); + rust_assert (ok); + + // lookup the var decl + Bvariable *var = nullptr; + bool found = ctx->lookup_var_decl (ref, &var); + rust_assert (found); + + // FIXME + // this should bes based on the closure move-ability + tree var_expr = var->get_tree (expr.get_locus ()); + tree val = address_expression (var_expr, expr.get_locus ()); + vals.push_back (val); + } translated = ctx->get_backend ()->constructor_expression (compiled_closure_tyty, false, @@ -2879,8 +2894,29 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr, DECL_ARTIFICIAL (self_param->get_decl ()) = 1; param_vars.push_back (self_param); + // push a new context + ctx->push_closure_context (expr.get_mappings ().get_hirid ()); + // setup the implicit argument captures - // TODO + size_t idx = 0; + for (const auto &capture : closure_tyty.get_captures ()) + { + // lookup the HirId + HirId ref = UNKNOWN_HIRID; + bool ok = ctx->get_mappings ()->lookup_node_to_hir (capture, &ref); + rust_assert (ok); + + // get the assessor + tree binding = ctx->get_backend ()->struct_field_expression ( + self_param->get_tree (expr.get_locus ()), idx, expr.get_locus ()); + tree indirection = indirect_expression (binding, expr.get_locus ()); + + // insert bindings + ctx->insert_closure_binding (ref, indirection); + + // continue + idx++; + } // args tuple tree args_type @@ -2910,7 +2946,10 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr, } if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) - return error_mark_node; + { + ctx->pop_closure_context (); + return error_mark_node; + } // lookup locals HIR::Expr *function_body = expr.get_expr ().get (); @@ -2977,6 +3016,7 @@ CompileExpr::generate_closure_function (HIR::ClosureExpr &expr, gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); DECL_SAVED_TREE (fndecl) = bind_tree; + ctx->pop_closure_context (); ctx->pop_fn (); ctx->push_function (fndecl); diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc index ab8e628c75c..8857df2f3a5 100644 --- a/gcc/rust/backend/rust-compile-resolve-path.cc +++ b/gcc/rust/backend/rust-compile-resolve-path.cc @@ -121,6 +121,14 @@ ResolvePathRef::resolve (const HIR::PathIdentSegment &final_segment, return constant_expr; } + // maybe closure binding + tree closure_binding = error_mark_node; + if (ctx->lookup_closure_binding (ref, &closure_binding)) + { + TREE_USED (closure_binding) = 1; + return closure_binding; + } + // this might be a variable reference or a function reference Bvariable *var = nullptr; if (ctx->lookup_var_decl (ref, &var)) diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc index 824cb3a56ef..c8e1d3b4036 100644 --- a/gcc/rust/backend/rust-compile-type.cc +++ b/gcc/rust/backend/rust-compile-type.cc @@ -19,6 +19,7 @@ #include "rust-compile-type.h" #include "rust-compile-expr.h" #include "rust-constexpr.h" +#include "rust-gcc.h" #include "tree.h" @@ -99,11 +100,39 @@ TyTyResolveCompile::visit (const TyTy::InferType &) void TyTyResolveCompile::visit (const TyTy::ClosureType &type) { + auto mappings = ctx->get_mappings (); + std::vector fields; + + size_t i = 0; + for (const auto &capture : type.get_captures ()) + { + // lookup the HirId + HirId ref = UNKNOWN_HIRID; + bool ok = mappings->lookup_node_to_hir (capture, &ref); + rust_assert (ok); + + // lookup the var decl type + TyTy::BaseType *lookup = nullptr; + bool found = ctx->get_tyctx ()->lookup_type (ref, &lookup); + rust_assert (found); + + // FIXME get the var pattern name + std::string mappings_name = "capture_" + std::to_string (i); + + // FIXME + // this should be based on the closure move-ability + tree decl_type = TyTyResolveCompile::compile (ctx, lookup); + tree capture_type = build_reference_type (decl_type); + fields.push_back (Backend::typed_identifier (mappings_name, capture_type, + type.get_ident ().locus)); + } + 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}}"; + 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); } diff --git a/gcc/testsuite/rust/execute/torture/closure3.rs b/gcc/testsuite/rust/execute/torture/closure3.rs new file mode 100644 index 00000000000..62cf3a082cf --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/closure3.rs @@ -0,0 +1,33 @@ +// { dg-output "3\n" } +extern "C" { + fn printf(s: *const i8, ...); +} + +#[lang = "fn_once"] +pub trait FnOnce { + #[lang = "fn_once_output"] + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +fn f i32>(g: F) { + let call = g(1); + unsafe { + let a = "%i\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c, call); + } +} + +pub fn main() -> i32 { + let capture = 2; + let a = |i: i32| { + let b = i + capture; + b + }; + f(a); + 0 +}