From patchwork Tue Dec 6 10:14:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Cohen X-Patchwork-Id: 61536 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5E91B38376B7 for ; Tue, 6 Dec 2022 10:25:56 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x42d.google.com (mail-wr1-x42d.google.com [IPv6:2a00:1450:4864:20::42d]) by sourceware.org (Postfix) with ESMTPS id 2A8FB3AA9812 for ; Tue, 6 Dec 2022 10:13:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2A8FB3AA9812 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-x42d.google.com with SMTP id bx10so22815580wrb.0 for ; Tue, 06 Dec 2022 02:13:49 -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=OHT44YXuZKf9ByZd2ZiCgsySYzxqF/qOmJ78bky5I6s=; b=Wpbz3NexkOdXcr2+U0xjMNKx+UdGygYphTQagpkMvPbiC+ojclKABqgn8kweijtFnM B/nCLqp5Fot8Owv8sjI7D8sl32LLuAc7GHX9j3nJFqQRF9oU0yj7xu2dGSFuxyNyXZoi TpgVoJDV/Ww3KjpogBBv9r8WV4Tj2LgeQOsGfi6Kq3G7llz2GH+to4Zk5jF44s0IfVFB U8nW/xb3m1aiXN0x/0wHmcRi+FCzHn3Kee6mM5kVffNmrbGTxiTbFPt4GpRn25T6anQ3 lvUMtFDL87V6sbgtuunQe8/T6BG6xgbyopUUD3cFQbVc/qznCx6qkWRO1cAy0yB5yVvc XEJg== 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=OHT44YXuZKf9ByZd2ZiCgsySYzxqF/qOmJ78bky5I6s=; b=QDwx06pg7Pe8NQR3Sm6raAqnD7Imx87vcZzuYRfAr95pbcKuggFrUcOGkrJSm10G+t n31y0nJBN0P8jWRO5fzP2IzuKySanmMIrB1Q+SZITXcA0PeSKq0U8NE3gxL4bI3Nytvs Hin2pJ+NX2AlM9h7Y54pQW1digBTbTUD3QAlHb4R55vmebHtgs8SpkluBGXaDZk7pZr6 b/xKKyJahtA+g4dXmpMu57/Zcoa/V9dgb//jTOQ617tCM+N4DauXejf5dAdYctGwClam kdksjzpdV27UepHL5+79pNe6m2c1lyjNfnPYEN1dVbT183+fAj+9oFd4iJUzRtlqoGvO +pWg== X-Gm-Message-State: ANoB5pnHDVM7dpkNt0LUXQpogZQ46ocTB2cBErwV5ugrGSOyl0bey0v5 HXv3+XHtNR1D9Dqg/7TebBaUCNUgQ5Ght/Z4PA== X-Google-Smtp-Source: AA0mqf6MVJKrORGdT/9OD/HqmgPU/CtWCf4EZecPz6UnhQrMd+jpF+2Qbbafmp9qGn4qw28FMN0uDQ== X-Received: by 2002:a5d:5702:0:b0:242:569:3028 with SMTP id a2-20020a5d5702000000b0024205693028mr10529921wrv.435.1670321627080; Tue, 06 Dec 2022 02:13:47 -0800 (PST) Received: from platypus.lan ([2001:861:5e4c:3bb0:6424:328a:1734:3249]) by smtp.googlemail.com with ESMTPSA id r10-20020a05600c458a00b003cfd4a50d5asm27052699wmo.34.2022.12.06.02.13.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Dec 2022 02:13:46 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, richard.guenther@gmail.com, jakub@redhat.com, Philip Herron , David Faust , Faisal Abbas <90.abbasfaisal@gmail.com> Subject: [PATCH Rust front-end v4 38/46] gccrs: Add HIR to GCC GENERIC lowering entry point Date: Tue, 6 Dec 2022 11:14:15 +0100 Message-Id: <20221206101417.778807-39-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221206101417.778807-1-arthur.cohen@embecosm.com> References: <20221206101417.778807-1-arthur.cohen@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-17.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham 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+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Philip Herron This patch contains the entry point and utilities used for the lowering of HIR nodes to `tree`s. It also contains a constant evaluator, ported over from the C++ frontend. Co-authored-by: David Faust Co-authored-by: Faisal Abbas <90.abbasfaisal@gmail.com> --- gcc/rust/backend/rust-compile-context.cc | 146 ++++++++ gcc/rust/backend/rust-compile-context.h | 343 ++++++++++++++++++ gcc/rust/backend/rust-compile.cc | 414 ++++++++++++++++++++++ gcc/rust/backend/rust-compile.h | 47 +++ gcc/rust/backend/rust-constexpr.cc | 433 +++++++++++++++++++++++ gcc/rust/backend/rust-constexpr.h | 31 ++ 6 files changed, 1414 insertions(+) create mode 100644 gcc/rust/backend/rust-compile-context.cc create mode 100644 gcc/rust/backend/rust-compile-context.h create mode 100644 gcc/rust/backend/rust-compile.cc create mode 100644 gcc/rust/backend/rust-compile.h create mode 100644 gcc/rust/backend/rust-constexpr.cc create mode 100644 gcc/rust/backend/rust-constexpr.h diff --git a/gcc/rust/backend/rust-compile-context.cc b/gcc/rust/backend/rust-compile-context.cc new file mode 100644 index 00000000000..cb2addf6c21 --- /dev/null +++ b/gcc/rust/backend/rust-compile-context.cc @@ -0,0 +1,146 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-compile-context.h" +#include "rust-compile-type.h" + +namespace Rust { +namespace Compile { + +Context::Context (::Backend *backend) + : backend (backend), resolver (Resolver::Resolver::get ()), + tyctx (Resolver::TypeCheckContext::get ()), + mappings (Analysis::Mappings::get ()), mangler (Mangler ()) +{ + setup_builtins (); +} + +void +Context::setup_builtins () +{ + auto builtins = resolver->get_builtin_types (); + for (auto it = builtins.begin (); it != builtins.end (); it++) + { + HirId ref; + bool ok = tyctx->lookup_type_by_node_id ((*it)->get_node_id (), &ref); + rust_assert (ok); + + TyTy::BaseType *lookup; + ok = tyctx->lookup_type (ref, &lookup); + rust_assert (ok); + + TyTyResolveCompile::compile (this, lookup); + } +} + +hashval_t +Context::type_hasher (tree type) +{ + inchash::hash hstate; + + hstate.add_int (TREE_CODE (type)); + + if (TYPE_NAME (type)) + { + hashval_t record_name_hash + = IDENTIFIER_HASH_VALUE (DECL_NAME (TYPE_NAME (type))); + hstate.add_object (record_name_hash); + } + + for (tree t = TYPE_ATTRIBUTES (type); t; t = TREE_CHAIN (t)) + /* Just the identifier is adequate to distinguish. */ + hstate.add_object (IDENTIFIER_HASH_VALUE (TREE_PURPOSE (t))); + + switch (TREE_CODE (type)) + { + case METHOD_TYPE: + hstate.add_object (TYPE_HASH (TYPE_METHOD_BASETYPE (type))); + /* FALLTHROUGH. */ + case FUNCTION_TYPE: + for (tree t = TYPE_ARG_TYPES (type); t; t = TREE_CHAIN (t)) + if (TREE_VALUE (t) != error_mark_node) + hstate.add_object (TYPE_HASH (TREE_VALUE (t))); + break; + + case OFFSET_TYPE: + hstate.add_object (TYPE_HASH (TYPE_OFFSET_BASETYPE (type))); + break; + + case ARRAY_TYPE: { + if (TYPE_DOMAIN (type)) + hstate.add_object (TYPE_HASH (TYPE_DOMAIN (type))); + if (!AGGREGATE_TYPE_P (TREE_TYPE (type))) + { + unsigned typeless = TYPE_TYPELESS_STORAGE (type); + hstate.add_object (typeless); + } + } + break; + + case INTEGER_TYPE: { + tree t = TYPE_MAX_VALUE (type); + if (!t) + t = TYPE_MIN_VALUE (type); + for (int i = 0; i < TREE_INT_CST_NUNITS (t); i++) + hstate.add_object (TREE_INT_CST_ELT (t, i)); + break; + } + + case REAL_TYPE: + case FIXED_POINT_TYPE: { + unsigned prec = TYPE_PRECISION (type); + hstate.add_object (prec); + break; + } + + case VECTOR_TYPE: + hstate.add_poly_int (TYPE_VECTOR_SUBPARTS (type)); + break; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: { + for (tree t = TYPE_FIELDS (type); t; t = TREE_CHAIN (t)) + { + hashval_t name_hash = IDENTIFIER_HASH_VALUE (DECL_NAME (t)); + hashval_t type_hash = type_hasher (TREE_TYPE (t)); + hstate.add_object (name_hash); + hstate.add_object (type_hash); + } + } + break; + + case BOOLEAN_TYPE: + break; + + case REFERENCE_TYPE: + case POINTER_TYPE: { + hashval_t type_hash = type_hasher (TREE_TYPE (type)); + hstate.add_object (type_hash); + } + break; + + default: + break; + } + + return hstate.end (); +} + +} // namespace Compile +} // namespace Rust diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h new file mode 100644 index 00000000000..096b65f8b39 --- /dev/null +++ b/gcc/rust/backend/rust-compile-context.h @@ -0,0 +1,343 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_COMPILE_CONTEXT +#define RUST_COMPILE_CONTEXT + +#include "rust-system.h" +#include "rust-hir-map.h" +#include "rust-name-resolver.h" +#include "rust-hir-type-check.h" +#include "rust-backend.h" +#include "rust-hir-full.h" +#include "rust-mangle.h" +#include "rust-tree.h" + +namespace Rust { +namespace Compile { + +struct fncontext +{ + tree fndecl; + ::Bvariable *ret_addr; +}; + +class Context +{ +public: + Context (::Backend *backend); + + void setup_builtins (); + + bool lookup_compiled_types (tree t, tree *type) + { + hashval_t h = type_hasher (t); + auto it = compiled_type_map.find (h); + if (it == compiled_type_map.end ()) + return false; + + *type = it->second; + return true; + } + + tree insert_compiled_type (tree type) + { + hashval_t h = type_hasher (type); + auto it = compiled_type_map.find (h); + if (it != compiled_type_map.end ()) + return it->second; + + compiled_type_map.insert ({h, type}); + push_type (type); + return type; + } + + tree insert_main_variant (tree type) + { + hashval_t h = type_hasher (type); + auto it = main_variants.find (h); + if (it != main_variants.end ()) + return it->second; + + main_variants.insert ({h, type}); + return type; + } + + ::Backend *get_backend () { return backend; } + Resolver::Resolver *get_resolver () { return resolver; } + Resolver::TypeCheckContext *get_tyctx () { return tyctx; } + Analysis::Mappings *get_mappings () { return mappings; } + + void push_block (tree scope) + { + scope_stack.push_back (scope); + statements.push_back ({}); + } + + tree pop_block () + { + auto block = scope_stack.back (); + scope_stack.pop_back (); + + auto stmts = statements.back (); + statements.pop_back (); + + backend->block_add_statements (block, stmts); + + return block; + } + + tree peek_enclosing_scope () + { + if (scope_stack.size () == 0) + return nullptr; + + return scope_stack.back (); + } + + void add_statement_to_enclosing_scope (tree stmt) + { + statements.at (statements.size () - 2).push_back (stmt); + } + + void add_statement (tree stmt) { statements.back ().push_back (stmt); } + + void insert_var_decl (HirId id, ::Bvariable *decl) + { + compiled_var_decls[id] = decl; + } + + bool lookup_var_decl (HirId id, ::Bvariable **decl) + { + auto it = compiled_var_decls.find (id); + if (it == compiled_var_decls.end ()) + return false; + + *decl = it->second; + return true; + } + + void insert_function_decl (const TyTy::FnType *ref, tree fn) + { + auto id = ref->get_ty_ref (); + auto dId = ref->get_id (); + + rust_assert (compiled_fn_map.find (id) == compiled_fn_map.end ()); + compiled_fn_map[id] = fn; + + auto it = mono_fns.find (dId); + if (it == mono_fns.end ()) + mono_fns[dId] = {}; + + mono_fns[dId].push_back ({ref, fn}); + } + + bool lookup_function_decl (HirId id, tree *fn, DefId dId = UNKNOWN_DEFID, + const TyTy::BaseType *ref = nullptr) + { + // for for any monomorphized fns + if (ref != nullptr) + { + rust_assert (dId != UNKNOWN_DEFID); + + auto it = mono_fns.find (dId); + if (it == mono_fns.end ()) + return false; + + for (auto &e : mono_fns[dId]) + { + const TyTy::BaseType *r = e.first; + tree f = e.second; + if (ref->is_equal (*r)) + { + *fn = f; + return true; + } + } + return false; + } + + auto it = compiled_fn_map.find (id); + if (it == compiled_fn_map.end ()) + return false; + + *fn = it->second; + return true; + } + + void insert_const_decl (HirId id, tree expr) { compiled_consts[id] = expr; } + + bool lookup_const_decl (HirId id, tree *expr) + { + auto it = compiled_consts.find (id); + if (it == compiled_consts.end ()) + return false; + + *expr = it->second; + return true; + } + + void insert_label_decl (HirId id, tree label) { compiled_labels[id] = label; } + + bool lookup_label_decl (HirId id, tree *label) + { + auto it = compiled_labels.find (id); + if (it == compiled_labels.end ()) + return false; + + *label = it->second; + return true; + } + + void insert_pattern_binding (HirId id, tree binding) + { + implicit_pattern_bindings[id] = binding; + } + + bool lookup_pattern_binding (HirId id, tree *binding) + { + auto it = implicit_pattern_bindings.find (id); + if (it == implicit_pattern_bindings.end ()) + return false; + + *binding = it->second; + return true; + } + + void push_fn (tree fn, ::Bvariable *ret_addr) + { + fn_stack.push_back (fncontext{fn, ret_addr}); + } + void pop_fn () { fn_stack.pop_back (); } + + bool in_fn () { return fn_stack.size () != 0; } + + // Note: it is undefined behavior to call peek_fn () if fn_stack is empty. + fncontext peek_fn () + { + rust_assert (!fn_stack.empty ()); + return fn_stack.back (); + } + + void push_type (tree t) { type_decls.push_back (t); } + void push_var (::Bvariable *v) { var_decls.push_back (v); } + void push_const (tree c) { const_decls.push_back (c); } + void push_function (tree f) { func_decls.push_back (f); } + + void write_to_backend () + { + backend->write_global_definitions (type_decls, const_decls, func_decls, + var_decls); + } + + bool function_completed (tree fn) + { + for (auto it = func_decls.begin (); it != func_decls.end (); it++) + { + tree i = (*it); + if (i == fn) + { + return true; + } + } + return false; + } + + void push_loop_context (Bvariable *var) { loop_value_stack.push_back (var); } + + Bvariable *peek_loop_context () { return loop_value_stack.back (); } + + Bvariable *pop_loop_context () + { + auto back = loop_value_stack.back (); + loop_value_stack.pop_back (); + return back; + } + + void push_loop_begin_label (tree label) + { + loop_begin_labels.push_back (label); + } + + tree peek_loop_begin_label () { return loop_begin_labels.back (); } + + tree pop_loop_begin_label () + { + tree pop = loop_begin_labels.back (); + loop_begin_labels.pop_back (); + return pop; + } + + void push_const_context (void) { const_context++; } + void pop_const_context (void) + { + if (const_context > 0) + const_context--; + } + bool const_context_p (void) { return (const_context > 0); } + + std::string mangle_item (const TyTy::BaseType *ty, + const Resolver::CanonicalPath &path) const + { + return mangler.mangle_item (ty, path); + } + + 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; } + std::vector &get_func_decls () { return func_decls; } + + static hashval_t type_hasher (tree type); + +private: + ::Backend *backend; + Resolver::Resolver *resolver; + Resolver::TypeCheckContext *tyctx; + Analysis::Mappings *mappings; + Mangler mangler; + + // state + std::vector fn_stack; + std::map compiled_var_decls; + std::map compiled_type_map; + std::map compiled_fn_map; + std::map compiled_consts; + std::map compiled_labels; + std::vector<::std::vector> statements; + std::vector scope_stack; + std::vector<::Bvariable *> loop_value_stack; + std::vector loop_begin_labels; + std::map>> + mono_fns; + std::map implicit_pattern_bindings; + std::map main_variants; + + // To GCC middle-end + std::vector type_decls; + std::vector<::Bvariable *> var_decls; + std::vector const_decls; + std::vector func_decls; + + // Nonzero iff we are currently compiling something inside a constant context. + unsigned int const_context = 0; +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_COMPILE_CONTEXT diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc new file mode 100644 index 00000000000..0ccb98d9e12 --- /dev/null +++ b/gcc/rust/backend/rust-compile.cc @@ -0,0 +1,414 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-compile.h" +#include "rust-compile-item.h" +#include "rust-compile-implitem.h" +#include "rust-compile-expr.h" +#include "rust-compile-struct-field-expr.h" +#include "rust-compile-stmt.h" +#include "rust-hir-trait-resolve.h" +#include "rust-hir-path-probe.h" +#include "rust-hir-type-bounds.h" +#include "rust-hir-dot-operator.h" +#include "rust-compile-block.h" + +namespace Rust { +namespace Compile { + +CompileCrate::CompileCrate (HIR::Crate &crate, Context *ctx) + : crate (crate), ctx (ctx) +{} + +CompileCrate::~CompileCrate () {} + +void +CompileCrate::Compile (HIR::Crate &crate, Context *ctx) +{ + CompileCrate c (crate, ctx); + c.go (); +} + +void +CompileCrate::go () +{ + for (auto &item : crate.items) + CompileItem::compile (item.get (), ctx); +} + +// Shared methods in compilation + +tree +HIRCompileBase::coercion_site (HirId id, tree rvalue, + const TyTy::BaseType *rval, + const TyTy::BaseType *lval, + Location lvalue_locus, Location rvalue_locus) +{ + std::vector *adjustments = nullptr; + bool ok = ctx->get_tyctx ()->lookup_autoderef_mappings (id, &adjustments); + if (ok) + { + rvalue = resolve_adjustements (*adjustments, rvalue, rvalue_locus); + } + + return coercion_site1 (rvalue, rval, lval, lvalue_locus, rvalue_locus); +} + +tree +HIRCompileBase::coercion_site1 (tree rvalue, const TyTy::BaseType *rval, + const TyTy::BaseType *lval, + Location lvalue_locus, Location rvalue_locus) +{ + if (rvalue == error_mark_node) + return error_mark_node; + + const TyTy::BaseType *actual = rval->destructure (); + const TyTy::BaseType *expected = lval->destructure (); + + if (expected->get_kind () == TyTy::TypeKind::REF) + { + // this is a dyn object + if (SLICE_TYPE_P (TREE_TYPE (rvalue))) + { + return rvalue; + } + + // bad coercion... of something to a reference + if (actual->get_kind () != TyTy::TypeKind::REF) + return error_mark_node; + + const TyTy::ReferenceType *exp + = static_cast (expected); + const TyTy::ReferenceType *act + = static_cast (actual); + + tree deref_rvalue = indirect_expression (rvalue, rvalue_locus); + tree coerced + = coercion_site1 (deref_rvalue, act->get_base (), exp->get_base (), + lvalue_locus, rvalue_locus); + if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced))) + return coerced; + + return address_expression (coerced, rvalue_locus); + } + else if (expected->get_kind () == TyTy::TypeKind::POINTER) + { + // this is a dyn object + if (SLICE_TYPE_P (TREE_TYPE (rvalue))) + { + return rvalue; + } + + // bad coercion... of something to a reference + bool valid_coercion = actual->get_kind () == TyTy::TypeKind::REF + || actual->get_kind () == TyTy::TypeKind::POINTER; + if (!valid_coercion) + return error_mark_node; + + const TyTy::ReferenceType *exp + = static_cast (expected); + + TyTy::BaseType *actual_base = nullptr; + if (actual->get_kind () == TyTy::TypeKind::REF) + { + const TyTy::ReferenceType *act + = static_cast (actual); + + actual_base = act->get_base (); + } + else if (actual->get_kind () == TyTy::TypeKind::POINTER) + { + const TyTy::PointerType *act + = static_cast (actual); + + actual_base = act->get_base (); + } + rust_assert (actual_base != nullptr); + + tree deref_rvalue = indirect_expression (rvalue, rvalue_locus); + tree coerced + = coercion_site1 (deref_rvalue, actual_base, exp->get_base (), + lvalue_locus, rvalue_locus); + + if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced))) + return coerced; + + return address_expression (coerced, rvalue_locus); + } + else if (expected->get_kind () == TyTy::TypeKind::ARRAY) + { + if (actual->get_kind () != TyTy::TypeKind::ARRAY) + return error_mark_node; + + tree tree_rval_type = TyTyResolveCompile::compile (ctx, actual); + tree tree_lval_type = TyTyResolveCompile::compile (ctx, expected); + if (!verify_array_capacities (tree_lval_type, tree_rval_type, + lvalue_locus, rvalue_locus)) + return error_mark_node; + } + else if (expected->get_kind () == TyTy::TypeKind::SLICE) + { + // bad coercion + bool valid_coercion = actual->get_kind () == TyTy::TypeKind::SLICE + || actual->get_kind () == TyTy::TypeKind::ARRAY; + if (!valid_coercion) + return error_mark_node; + + // nothing to do here + if (actual->get_kind () == TyTy::TypeKind::SLICE) + return rvalue; + + // return an unsized coercion + Resolver::Adjustment unsize_adj ( + Resolver::Adjustment::AdjustmentType::UNSIZE, actual, expected); + return resolve_unsized_adjustment (unsize_adj, rvalue, rvalue_locus); + } + + return rvalue; +} + +tree +HIRCompileBase::coerce_to_dyn_object (tree compiled_ref, + const TyTy::BaseType *actual, + const TyTy::DynamicObjectType *ty, + Location locus) +{ + tree dynamic_object = TyTyResolveCompile::compile (ctx, ty); + tree dynamic_object_fields = TYPE_FIELDS (dynamic_object); + tree vtable_field = DECL_CHAIN (dynamic_object_fields); + rust_assert (TREE_CODE (TREE_TYPE (vtable_field)) == ARRAY_TYPE); + + //' this assumes ordering and current the structure is + // __trait_object_ptr + // [list of function ptrs] + + std::vector> + probed_bounds_for_receiver = Resolver::TypeBoundsProbe::Probe (actual); + + tree address_of_compiled_ref = null_pointer_node; + if (!actual->is_unit ()) + address_of_compiled_ref = address_expression (compiled_ref, locus); + + std::vector vtable_ctor_elems; + std::vector vtable_ctor_idx; + unsigned long i = 0; + for (auto &bound : ty->get_object_items ()) + { + const Resolver::TraitItemReference *item = bound.first; + const TyTy::TypeBoundPredicate *predicate = bound.second; + + auto address = compute_address_for_trait_item (item, predicate, + probed_bounds_for_receiver, + actual, actual, locus); + vtable_ctor_elems.push_back (address); + vtable_ctor_idx.push_back (i++); + } + + tree vtable_ctor = ctx->get_backend ()->array_constructor_expression ( + TREE_TYPE (vtable_field), vtable_ctor_idx, vtable_ctor_elems, locus); + + std::vector dyn_ctor = {address_of_compiled_ref, vtable_ctor}; + return ctx->get_backend ()->constructor_expression (dynamic_object, false, + dyn_ctor, -1, locus); +} + +tree +HIRCompileBase::compute_address_for_trait_item ( + const Resolver::TraitItemReference *ref, + const TyTy::TypeBoundPredicate *predicate, + std::vector> + &receiver_bounds, + const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus) +{ + // There are two cases here one where its an item which has an implementation + // within a trait-impl-block. Then there is the case where there is a default + // implementation for this within the trait. + // + // The awkward part here is that this might be a generic trait and we need to + // figure out the correct monomorphized type for this so we can resolve the + // address of the function , this is stored as part of the + // type-bound-predicate + // + // Algo: + // check if there is an impl-item for this trait-item-ref first + // else assert that the trait-item-ref has an implementation + + TyTy::TypeBoundPredicateItem predicate_item + = predicate->lookup_associated_item (ref->get_identifier ()); + rust_assert (!predicate_item.is_error ()); + + // this is the expected end type + TyTy::BaseType *trait_item_type = predicate_item.get_tyty_for_receiver (root); + rust_assert (trait_item_type->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *trait_item_fntype + = static_cast (trait_item_type); + + // find impl-block for this trait-item-ref + HIR::ImplBlock *associated_impl_block = nullptr; + const Resolver::TraitReference *predicate_trait_ref = predicate->get (); + for (auto &item : receiver_bounds) + { + Resolver::TraitReference *trait_ref = item.first; + HIR::ImplBlock *impl_block = item.second; + if (predicate_trait_ref->is_equal (*trait_ref)) + { + associated_impl_block = impl_block; + break; + } + } + + // FIXME this probably should just return error_mark_node but this helps + // debug for now since we are wrongly returning early on type-resolution + // failures, until we take advantage of more error types and error_mark_node + rust_assert (associated_impl_block != nullptr); + + // lookup self for the associated impl + std::unique_ptr &self_type_path + = associated_impl_block->get_type (); + TyTy::BaseType *self = nullptr; + bool ok = ctx->get_tyctx ()->lookup_type ( + self_type_path->get_mappings ().get_hirid (), &self); + rust_assert (ok); + + // lookup the predicate item from the self + TyTy::TypeBoundPredicate *self_bound = nullptr; + for (auto &bound : self->get_specified_bounds ()) + { + const Resolver::TraitReference *bound_ref = bound.get (); + const Resolver::TraitReference *specified_ref = predicate->get (); + if (bound_ref->is_equal (*specified_ref)) + { + self_bound = &bound; + break; + } + } + rust_assert (self_bound != nullptr); + + // lookup the associated item from the associated impl block + TyTy::TypeBoundPredicateItem associated_self_item + = self_bound->lookup_associated_item (ref->get_identifier ()); + rust_assert (!associated_self_item.is_error ()); + + TyTy::BaseType *mono1 = associated_self_item.get_tyty_for_receiver (self); + rust_assert (mono1 != nullptr); + rust_assert (mono1->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *assocated_item_ty1 = static_cast (mono1); + + // Lookup the impl-block for the associated impl_item if it exists + HIR::Function *associated_function = nullptr; + for (auto &impl_item : associated_impl_block->get_impl_items ()) + { + bool is_function = impl_item->get_impl_item_type () + == HIR::ImplItem::ImplItemType::FUNCTION; + if (!is_function) + continue; + + HIR::Function *fn = static_cast (impl_item.get ()); + bool found_associated_item + = fn->get_function_name ().compare (ref->get_identifier ()) == 0; + if (found_associated_item) + associated_function = fn; + } + + // we found an impl_item for this + if (associated_function != nullptr) + { + // lookup the associated type for this item + TyTy::BaseType *lookup = nullptr; + bool ok = ctx->get_tyctx ()->lookup_type ( + associated_function->get_mappings ().get_hirid (), &lookup); + rust_assert (ok); + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *lookup_fntype = static_cast (lookup); + + if (lookup_fntype->needs_substitution ()) + { + TyTy::SubstitutionArgumentMappings mappings + = assocated_item_ty1->solve_missing_mappings_from_this ( + *trait_item_fntype, *lookup_fntype); + lookup_fntype = lookup_fntype->handle_substitions (mappings); + } + + return CompileInherentImplItem::Compile (associated_function, ctx, + lookup_fntype, true, locus); + } + + // we can only compile trait-items with a body + bool trait_item_has_definition = ref->is_optional (); + rust_assert (trait_item_has_definition); + + HIR::TraitItem *trait_item = ref->get_hir_trait_item (); + return CompileTraitItem::Compile (trait_item, ctx, trait_item_fntype, true, + locus); +} + +bool +HIRCompileBase::verify_array_capacities (tree ltype, tree rtype, + Location lvalue_locus, + Location rvalue_locus) +{ + rust_assert (ltype != NULL_TREE); + rust_assert (rtype != NULL_TREE); + + // lets just return ok as other errors have already occurred + if (ltype == error_mark_node || rtype == error_mark_node) + return true; + + tree ltype_domain = TYPE_DOMAIN (ltype); + if (!ltype_domain) + return false; + + if (!TREE_CONSTANT (TYPE_MAX_VALUE (ltype_domain))) + return false; + + unsigned HOST_WIDE_INT ltype_length + = wi::ext (wi::to_offset (TYPE_MAX_VALUE (ltype_domain)) + - wi::to_offset (TYPE_MIN_VALUE (ltype_domain)) + 1, + TYPE_PRECISION (TREE_TYPE (ltype_domain)), + TYPE_SIGN (TREE_TYPE (ltype_domain))) + .to_uhwi (); + + tree rtype_domain = TYPE_DOMAIN (rtype); + if (!rtype_domain) + return false; + + if (!TREE_CONSTANT (TYPE_MAX_VALUE (rtype_domain))) + return false; + + unsigned HOST_WIDE_INT rtype_length + = wi::ext (wi::to_offset (TYPE_MAX_VALUE (rtype_domain)) + - wi::to_offset (TYPE_MIN_VALUE (rtype_domain)) + 1, + TYPE_PRECISION (TREE_TYPE (rtype_domain)), + TYPE_SIGN (TREE_TYPE (rtype_domain))) + .to_uhwi (); + + if (ltype_length != rtype_length) + { + rust_error_at ( + rvalue_locus, + "expected an array with a fixed size of " HOST_WIDE_INT_PRINT_UNSIGNED + " elements, found one with " HOST_WIDE_INT_PRINT_UNSIGNED " elements", + ltype_length, rtype_length); + return false; + } + + return true; +} + +} // namespace Compile +} // namespace Rust diff --git a/gcc/rust/backend/rust-compile.h b/gcc/rust/backend/rust-compile.h new file mode 100644 index 00000000000..62ebac69cc1 --- /dev/null +++ b/gcc/rust/backend/rust-compile.h @@ -0,0 +1,47 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_COMPILE_H +#define RUST_COMPILE_H + +#include "rust-system.h" +#include "rust-hir-full.h" +#include "rust-compile-context.h" + +namespace Rust { +namespace Compile { + +class CompileCrate +{ +public: + static void Compile (HIR::Crate &crate, Context *ctx); + + ~CompileCrate (); + +private: + CompileCrate (HIR::Crate &crate, Context *ctx); + void go (); + + HIR::Crate &crate; + Context *ctx; +}; + +} // namespace Compile +} // namespace Rust + +#endif // RUST_COMPILE_H diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc new file mode 100644 index 00000000000..5aa10d92e15 --- /dev/null +++ b/gcc/rust/backend/rust-constexpr.cc @@ -0,0 +1,433 @@ +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-constexpr.h" +#include "rust-location.h" +#include "rust-diagnostics.h" +#include "rust-tree.h" + +#include "fold-const.h" +#include "realmpfr.h" +#include "convert.h" +#include "print-tree.h" +#include "gimplify.h" +#include "tree-iterator.h" + +namespace Rust { +namespace Compile { + +struct constexpr_global_ctx +{ + HOST_WIDE_INT constexpr_ops_count; + + constexpr_global_ctx () : constexpr_ops_count (0) {} +}; + +struct constexpr_ctx +{ + constexpr_global_ctx *global; +}; + +static tree +constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, + bool unshare_p); +tree +decl_constant_value (tree decl, bool unshare_p); + +static void +non_const_var_error (location_t loc, tree r); + +static tree +constexpr_expression (const constexpr_ctx *ctx, tree); + +static tree +constexpr_fn_retval (const constexpr_ctx *ctx, tree r); + +static tree +eval_store_expression (const constexpr_ctx *ctx, tree r); + +static tree +eval_call_expression (const constexpr_ctx *ctx, tree r); + +static tree +eval_binary_expression (const constexpr_ctx *ctx, tree r); + +static tree +get_function_named_in_call (tree t); + +tree +fold_expr (tree expr) +{ + constexpr_global_ctx global_ctx; + constexpr_ctx ctx = {&global_ctx}; + + tree folded = constexpr_expression (&ctx, expr); + rust_assert (folded != NULL_TREE); + return folded; +} + +static tree +constexpr_expression (const constexpr_ctx *ctx, tree t) +{ + location_t loc = EXPR_LOCATION (t); + + if (CONSTANT_CLASS_P (t)) + { + if (TREE_OVERFLOW (t)) + { + error_at (loc, "overflow in constant expression"); + return t; + } + + return t; + } + + // Avoid excessively long constexpr evaluations + if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit) + { + rust_error_at ( + Location (loc), + "% evaluation operation count exceeds limit of " + "%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)", + constexpr_ops_limit); + + return t; + } + + tree r = t; + tree_code tcode = TREE_CODE (t); + switch (tcode) + { + case CONST_DECL: { + r = decl_constant_value (t, /*unshare_p=*/false); + if (TREE_CODE (r) == TARGET_EXPR + && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR) + r = TARGET_EXPR_INITIAL (r); + if (DECL_P (r)) + { + non_const_var_error (loc, r); + return r; + } + } + break; + + case POINTER_PLUS_EXPR: + case POINTER_DIFF_EXPR: + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case TRUNC_MOD_EXPR: + case CEIL_MOD_EXPR: + case ROUND_MOD_EXPR: + case RDIV_EXPR: + case EXACT_DIV_EXPR: + case MIN_EXPR: + case MAX_EXPR: + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case TRUTH_XOR_EXPR: + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case EQ_EXPR: + case NE_EXPR: + case SPACESHIP_EXPR: + case UNORDERED_EXPR: + case ORDERED_EXPR: + case UNLT_EXPR: + case UNLE_EXPR: + case UNGT_EXPR: + case UNGE_EXPR: + case UNEQ_EXPR: + case LTGT_EXPR: + case RANGE_EXPR: + case COMPLEX_EXPR: + r = eval_binary_expression (ctx, t); + break; + + case CALL_EXPR: + r = eval_call_expression (ctx, t); + break; + + case RETURN_EXPR: + rust_assert (TREE_OPERAND (t, 0) != NULL_TREE); + r = constexpr_expression (ctx, TREE_OPERAND (t, 0)); + break; + + case MODIFY_EXPR: + r = eval_store_expression (ctx, t); + break; + + default: + break; + } + + return r; +} + +static tree +eval_store_expression (const constexpr_ctx *ctx, tree t) +{ + tree init = TREE_OPERAND (t, 1); + if (TREE_CLOBBER_P (init)) + /* Just ignore clobbers. */ + return void_node; + + /* First we figure out where we're storing to. */ + tree target = TREE_OPERAND (t, 0); + + tree type = TREE_TYPE (target); + bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR; + if (preeval) + { + /* Evaluate the value to be stored without knowing what object it will be + stored in, so that any side-effects happen first. */ + init = fold_expr (init); + } + + bool evaluated = false; + tree object = NULL_TREE; + for (tree probe = target; object == NULL_TREE;) + { + switch (TREE_CODE (probe)) + { + default: + if (evaluated) + object = probe; + else + { + probe = constexpr_expression (ctx, probe); + evaluated = true; + } + break; + } + } + + return init; +} + +/* Subroutine of cxx_eval_constant_expression. + Like cxx_eval_unary_expression, except for binary expressions. */ +static tree +eval_binary_expression (const constexpr_ctx *ctx, tree t) +{ + tree orig_lhs = TREE_OPERAND (t, 0); + tree orig_rhs = TREE_OPERAND (t, 1); + tree lhs, rhs; + + lhs = constexpr_expression (ctx, orig_lhs); + rhs = constexpr_expression (ctx, orig_rhs); + + location_t loc = EXPR_LOCATION (t); + enum tree_code code = TREE_CODE (t); + tree type = TREE_TYPE (t); + + return fold_binary_loc (loc, code, type, lhs, rhs); +} + +// Subroutine of cxx_eval_constant_expression. +// Evaluate the call expression tree T in the context of OLD_CALL expression +// evaluation. +static tree +eval_call_expression (const constexpr_ctx *ctx, tree t) +{ + tree fun = get_function_named_in_call (t); + return constexpr_fn_retval (ctx, DECL_SAVED_TREE (fun)); +} + +// Subroutine of check_constexpr_fundef. BODY is the body of a function +// declared to be constexpr, or a sub-statement thereof. Returns the +// return value if suitable, error_mark_node for a statement not allowed in +// a constexpr function, or NULL_TREE if no return value was found. +static tree +constexpr_fn_retval (const constexpr_ctx *ctx, tree body) +{ + switch (TREE_CODE (body)) + { + case STATEMENT_LIST: { + tree expr = NULL_TREE; + for (tree stmt : tsi_range (body)) + { + tree s = constexpr_fn_retval (ctx, stmt); + if (s == error_mark_node) + return error_mark_node; + else if (s == NULL_TREE) + /* Keep iterating. */; + else if (expr) + /* Multiple return statements. */ + return error_mark_node; + else + expr = s; + } + return expr; + } + + case RETURN_EXPR: + return constexpr_expression (ctx, body); + + case DECL_EXPR: { + tree decl = DECL_EXPR_DECL (body); + if (TREE_CODE (decl) == USING_DECL + /* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__. */ + || DECL_ARTIFICIAL (decl)) + return NULL_TREE; + return error_mark_node; + } + + case CLEANUP_POINT_EXPR: + return constexpr_fn_retval (ctx, TREE_OPERAND (body, 0)); + + case BIND_EXPR: { + tree b = BIND_EXPR_BODY (body); + return constexpr_fn_retval (ctx, b); + } + break; + + default: + return error_mark_node; + } + return error_mark_node; +} + +// Taken from cp/constexpr.cc +// +// If DECL is a scalar enumeration constant or variable with a +// constant initializer, return the initializer (or, its initializers, +// recursively); otherwise, return DECL. If STRICT_P, the +// initializer is only returned if DECL is a +// constant-expression. If RETURN_AGGREGATE_CST_OK_P, it is ok to +// return an aggregate constant. If UNSHARE_P, return an unshared +// copy of the initializer. +static tree +constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p, + bool unshare_p) +{ + while (TREE_CODE (decl) == CONST_DECL) + { + tree init; + /* If DECL is a static data member in a template + specialization, we must instantiate it here. The + initializer for the static data member is not processed + until needed; we need it now. */ + + init = DECL_INITIAL (decl); + if (init == error_mark_node) + { + if (TREE_CODE (decl) == CONST_DECL) + /* Treat the error as a constant to avoid cascading errors on + excessively recursive template instantiation (c++/9335). */ + return init; + else + return decl; + } + + decl = init; + } + return unshare_p ? unshare_expr (decl) : decl; +} + +// A more relaxed version of decl_really_constant_value, used by the +// common C/C++ code. +tree +decl_constant_value (tree decl, bool unshare_p) +{ + return constant_value_1 (decl, /*strict_p=*/false, + /*return_aggregate_cst_ok_p=*/true, + /*unshare_p=*/unshare_p); +} + +static void +non_const_var_error (location_t loc, tree r) +{ + error_at (loc, + "the value of %qD is not usable in a constant " + "expression", + r); + /* Avoid error cascade. */ + if (DECL_INITIAL (r) == error_mark_node) + return; + + // more in cp/constexpr.cc +} + +static tree +get_callee (tree call) +{ + if (call == NULL_TREE) + return call; + else if (TREE_CODE (call) == CALL_EXPR) + return CALL_EXPR_FN (call); + + return NULL_TREE; +} + +// We have an expression tree T that represents a call, either CALL_EXPR +// or AGGR_INIT_EXPR. If the call is lexically to a named function, +// return the _DECL for that function. +static tree +get_function_named_in_call (tree t) +{ + tree fun = get_callee (t); + if (fun && TREE_CODE (fun) == ADDR_EXPR + && TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL) + fun = TREE_OPERAND (fun, 0); + return fun; +} + +// forked from gcc/cp/constexpr.cc maybe_constexpr_fn + +/* True if a function might be declared constexpr */ + +bool +maybe_constexpr_fn (tree t) +{ + return (DECL_DECLARED_CONSTEXPR_P (t)); +} + +// forked from gcc/cp/constexpr.cc get_nth_callarg + +/* We have an expression tree T that represents a call, either CALL_EXPR. + Return the Nth argument. */ + +inline tree +get_nth_callarg (tree t, int n) +{ + return CALL_EXPR_ARG (t, n); +} + +// forked from gcc/cp/constexpr.cc var_in_maybe_constexpr_fn + +/* True if T was declared in a function that might be constexpr: either a + function that was declared constexpr. */ + +bool +var_in_maybe_constexpr_fn (tree t) +{ + return (DECL_FUNCTION_SCOPE_P (t) && maybe_constexpr_fn (DECL_CONTEXT (t))); +} + +} // namespace Compile +} // namespace Rust diff --git a/gcc/rust/backend/rust-constexpr.h b/gcc/rust/backend/rust-constexpr.h new file mode 100644 index 00000000000..3cfcec817a9 --- /dev/null +++ b/gcc/rust/backend/rust-constexpr.h @@ -0,0 +1,31 @@ +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_CONSTEXPR +#define RUST_CONSTEXPR + +#include "rust-system.h" +#include "tree.h" + +namespace Rust { +namespace Compile { + +extern tree fold_expr (tree); + +} // namespace Compile +} // namespace Rust + +#endif // RUST_CONSTEXPR