aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/backend
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-01-14 15:49:07 +0000
committerGitHub <noreply@github.com>2022-01-14 15:49:07 +0000
commitb21caeb3af4313016afeb94a91956e8fc4c2656d (patch)
tree03b1cac2b3442af423cd565b0168682e6d4541b3 /gcc/rust/backend
parent03e56b5181506a4e6cdb9fe86c543a57840e54c3 (diff)
parent93554f3bce5bd42d8671559a774818767979cdae (diff)
downloadgcc-b21caeb3af4313016afeb94a91956e8fc4c2656d.zip
gcc-b21caeb3af4313016afeb94a91956e8fc4c2656d.tar.gz
gcc-b21caeb3af4313016afeb94a91956e8fc4c2656d.tar.bz2
Merge #870
870: Add constant folding to const functions r=philberty a=philberty In Rust the ArrayType has a constant capacity constraint, which means it allows for bounds checking at compile time as no variable-length arrays are allowed. In order to typecheck this case we had a constant folding pass as part of the type checking system which generated gcc tree's for the IR and enforced the constant checking along the way. GCC with optimizations turned on is capable of constant folding/propogating the compilation unit fully, but we need a method that works regardless of middlle-end optimizations to fold constant expressions at the front-end, turns out the CPP front-end already does this via its constexpr mechanism to ensure that these _do_ fold correctly. Another major reason to do this change is that the original const fold pass was a striped down copy of what the backend is _already_ doing which is creating a duplication of the code generation pass. With this all unified into the code generation pass all we need to do is port over gcc/cp/constexpr.c to enforce the const rules fully but at the GCC tree level not at the typed HIR level. Now that we have unified the pass when we hit a const function we can simply emit a normal GCC function and outside of const expressions GCC will simply emit a normal CallExpr and depending on optimization level fully optimize this. If we are in a ConstDecl we will follow the rust-constexpr.cc and fold the values or error_mark_node with an apropriate error. By reusing the CPP constexpr code we _know_ it works so reusing it as much as possible is a good idea in general for this front-end. Fixes #799 Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Diffstat (limited to 'gcc/rust/backend')
-rw-r--r--gcc/rust/backend/rust-compile-base.h11
-rw-r--r--gcc/rust/backend/rust-compile-context.h6
-rw-r--r--gcc/rust/backend/rust-compile-expr.cc235
-rw-r--r--gcc/rust/backend/rust-compile-expr.h81
-rw-r--r--gcc/rust/backend/rust-compile-implitem.h4
-rw-r--r--gcc/rust/backend/rust-compile-item.h89
-rw-r--r--gcc/rust/backend/rust-compile-pattern.cc33
-rw-r--r--gcc/rust/backend/rust-compile-resolve-path.cc23
-rw-r--r--gcc/rust/backend/rust-compile-stmt.h4
-rw-r--r--gcc/rust/backend/rust-compile-type.cc14
-rw-r--r--gcc/rust/backend/rust-compile-type.h5
-rw-r--r--gcc/rust/backend/rust-compile.cc90
-rw-r--r--gcc/rust/backend/rust-constexpr.cc428
-rw-r--r--gcc/rust/backend/rust-constexpr.h46
14 files changed, 956 insertions, 113 deletions
diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h
index b3ae1f6..e963176 100644
--- a/gcc/rust/backend/rust-compile-base.h
+++ b/gcc/rust/backend/rust-compile-base.h
@@ -37,15 +37,15 @@ protected:
Context *get_context () { return ctx; }
- void compile_function_body (tree fndecl,
- std::unique_ptr<HIR::BlockExpr> &function_body,
+ void compile_function_body (tree fndecl, HIR::BlockExpr &function_body,
bool has_return_type);
bool compile_locals_for_block (Resolver::Rib &rib, tree fndecl,
std::vector<Bvariable *> &locals);
- tree coercion_site (tree compiled_ref, TyTy::BaseType *actual,
- TyTy::BaseType *expected, Location locus);
+ tree coercion_site (tree rvalue, TyTy::BaseType *actual,
+ TyTy::BaseType *expected, Location lvalue_locus,
+ Location rvalue_locus);
tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
const TyTy::BaseType *expected,
@@ -57,6 +57,9 @@ protected:
std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
&receiver_bounds,
const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus);
+
+ bool verify_array_capacities (tree ltype, tree rtype, Location ltype_locus,
+ Location rtype_locus);
};
} // namespace Compile
diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
index 43c23dd..9be9801 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -27,7 +27,6 @@
#include "rust-compile-tyty.h"
#include "rust-ast-full.h"
#include "rust-hir-full.h"
-#include "rust-hir-const-fold-ctx.h"
#include "rust-mangle.h"
namespace Rust {
@@ -45,8 +44,7 @@ public:
Context (::Backend *backend)
: backend (backend), resolver (Resolver::Resolver::get ()),
tyctx (Resolver::TypeCheckContext::get ()),
- mappings (Analysis::Mappings::get ()),
- const_ctx (ConstFold::Context::get ()), mangler (Mangler ())
+ mappings (Analysis::Mappings::get ()), mangler (Mangler ())
{
// insert the builtins
auto builtins = resolver->get_builtin_types ();
@@ -110,7 +108,6 @@ public:
Resolver::Resolver *get_resolver () { return resolver; }
Resolver::TypeCheckContext *get_tyctx () { return tyctx; }
Analysis::Mappings *get_mappings () { return mappings; }
- ConstFold::Context *get_const_ctx () { return const_ctx; }
void push_block (tree scope)
{
@@ -315,7 +312,6 @@ private:
Resolver::Resolver *resolver;
Resolver::TypeCheckContext *tyctx;
Analysis::Mappings *mappings;
- ConstFold::Context *const_ctx;
std::set<HirId> builtin_range;
Mangler mangler;
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 9b04bbf..33237e5 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -25,10 +25,12 @@
#include "rust-hir-type-bounds.h"
#include "rust-hir-dot-operator.h"
#include "rust-compile-pattern.h"
+#include "rust-constexpr.h"
#include "fold-const.h"
#include "realmpfr.h"
#include "convert.h"
+#include "print-tree.h"
namespace Rust {
namespace Compile {
@@ -381,7 +383,11 @@ CompileExpr::visit (HIR::CallExpr &expr)
rust_assert (ok);
// coerce it if required
- rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());
+ Location lvalue_locus
+ = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+ Location rvalue_locus = argument->get_locus ();
+ rvalue = coercion_site (rvalue, actual, expected, lvalue_locus,
+ rvalue_locus);
// add it to the list
arguments.push_back (rvalue);
@@ -477,7 +483,11 @@ CompileExpr::visit (HIR::CallExpr &expr)
rust_assert (ok);
// coerce it if required
- rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());
+ Location lvalue_locus
+ = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+ Location rvalue_locus = argument->get_locus ();
+ rvalue
+ = coercion_site (rvalue, actual, expected, lvalue_locus, rvalue_locus);
// add it to the list
args.push_back (rvalue);
@@ -604,7 +614,11 @@ CompileExpr::visit (HIR::MethodCallExpr &expr)
rust_assert (ok);
// coerce it if required
- rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());
+ Location lvalue_locus
+ = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+ Location rvalue_locus = argument->get_locus ();
+ rvalue
+ = coercion_site (rvalue, actual, expected, lvalue_locus, rvalue_locus);
// add it to the list
args.push_back (rvalue);
@@ -1093,5 +1107,220 @@ CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
expr_tree);
}
+void
+CompileExpr::visit (HIR::ArrayExpr &expr)
+{
+ TyTy::BaseType *tyty = nullptr;
+ if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+ &tyty))
+ {
+ rust_fatal_error (expr.get_locus (),
+ "did not resolve type for this array expr");
+ return;
+ }
+
+ tree array_type = TyTyResolveCompile::compile (ctx, tyty);
+ if (TREE_CODE (array_type) != ARRAY_TYPE)
+ {
+ translated = error_mark_node;
+ return;
+ }
+
+ rust_assert (tyty->get_kind () == TyTy::TypeKind::ARRAY);
+ const TyTy::ArrayType &array_tyty
+ = static_cast<const TyTy::ArrayType &> (*tyty);
+
+ HIR::ArrayElems &elements = *expr.get_internal_elements ();
+ switch (elements.get_array_expr_type ())
+ {
+ case HIR::ArrayElems::ArrayExprType::VALUES: {
+ HIR::ArrayElemsValues &elems
+ = static_cast<HIR::ArrayElemsValues &> (elements);
+ translated
+ = array_value_expr (expr.get_locus (), array_tyty, array_type, elems);
+ }
+ return;
+
+ case HIR::ArrayElems::ArrayExprType::COPIED:
+ HIR::ArrayElemsCopied &elems
+ = static_cast<HIR::ArrayElemsCopied &> (elements);
+ translated
+ = array_copied_expr (expr.get_locus (), array_tyty, array_type, elems);
+ }
+}
+
+tree
+CompileExpr::array_value_expr (Location expr_locus,
+ const TyTy::ArrayType &array_tyty,
+ tree array_type, HIR::ArrayElemsValues &elems)
+{
+ std::vector<unsigned long> indexes;
+ std::vector<tree> constructor;
+ size_t i = 0;
+ for (auto &elem : elems.get_values ())
+ {
+ tree translated_expr = CompileExpr::Compile (elem.get (), ctx);
+ constructor.push_back (translated_expr);
+ indexes.push_back (i++);
+ }
+
+ return ctx->get_backend ()->array_constructor_expression (array_type, indexes,
+ constructor,
+ expr_locus);
+}
+
+tree
+CompileExpr::array_copied_expr (Location expr_locus,
+ const TyTy::ArrayType &array_tyty,
+ tree array_type, HIR::ArrayElemsCopied &elems)
+{
+ // see gcc/cp/typeck2.c:1369-1401
+ gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE);
+ tree domain = TYPE_DOMAIN (array_type);
+ if (!domain)
+ return error_mark_node;
+
+ if (!TREE_CONSTANT (TYPE_MAX_VALUE (domain)))
+ {
+ rust_error_at (expr_locus, "non const capacity domain %qT", array_type);
+ return error_mark_node;
+ }
+
+ tree capacity_expr = CompileExpr::Compile (elems.get_num_copies_expr (), ctx);
+ if (!TREE_CONSTANT (capacity_expr))
+ {
+ rust_error_at (expr_locus, "non const num copies %qT", array_type);
+ return error_mark_node;
+ }
+
+ // get the compiled value
+ tree translated_expr = CompileExpr::Compile (elems.get_elem_to_copy (), ctx);
+
+ tree max_domain = TYPE_MAX_VALUE (domain);
+ tree min_domain = TYPE_MIN_VALUE (domain);
+
+ auto max = wi::to_offset (max_domain);
+ auto min = wi::to_offset (min_domain);
+ auto precision = TYPE_PRECISION (TREE_TYPE (domain));
+ auto sign = TYPE_SIGN (TREE_TYPE (domain));
+ unsigned HOST_WIDE_INT len
+ = wi::ext (max - min + 1, precision, sign).to_uhwi ();
+
+ // create the constructor
+ size_t idx = 0;
+ std::vector<unsigned long> indexes;
+ std::vector<tree> constructor;
+ for (unsigned HOST_WIDE_INT i = 0; i < len; i++)
+ {
+ constructor.push_back (translated_expr);
+ indexes.push_back (idx++);
+ }
+
+ return ctx->get_backend ()->array_constructor_expression (array_type, indexes,
+ constructor,
+ expr_locus);
+}
+
+// tree
+// CompileExpr::array_copied_expr (Location expr_locus, tree array_type,
+// HIR::ArrayElemsCopied &elems)
+// {
+// // create tmp for the result
+// fncontext fnctx = ctx->peek_fn ();
+// Location start_location = expr_locus;
+// Location end_location = expr_locus;
+// tree fndecl = fnctx.fndecl;
+// tree enclosing_scope = ctx->peek_enclosing_scope ();
+
+// bool is_address_taken = false;
+// tree result_var_stmt = nullptr;
+// Bvariable *result
+// = ctx->get_backend ()->temporary_variable (fnctx.fndecl,
+// enclosing_scope,
+// array_type, NULL,
+// is_address_taken, expr_locus,
+// &result_var_stmt);
+// ctx->add_statement (result_var_stmt);
+
+// // get the compiled value
+// tree translated_expr = CompileExpr::Compile (elems.get_elem_to_copy (),
+// ctx);
+
+// // lets assign each index in the array
+// TyTy::BaseType *capacity_tyty = nullptr;
+// HirId capacity_ty_id
+// = elems.get_num_copies_expr ()->get_mappings ().get_hirid ();
+// bool ok = ctx->get_tyctx ()->lookup_type (capacity_ty_id,
+// &capacity_tyty); rust_assert (ok); tree capacity_type =
+// TyTyResolveCompile::compile (ctx, capacity_tyty); tree capacity_expr =
+// CompileExpr::Compile (elems.get_num_copies_expr (), ctx);
+
+// // create a loop for this with assignments to array_index exprs
+// tree index_type = capacity_type;
+// Bvariable *idx
+// = ctx->get_backend ()->temporary_variable (fnctx.fndecl,
+// enclosing_scope,
+// index_type, NULL,
+// is_address_taken, expr_locus,
+// &result_var_stmt);
+// ctx->add_statement (result_var_stmt);
+
+// // set index to zero
+// tree index_lvalue = error_mark_node;
+// tree zero = build_int_cst (index_type, 0);
+// tree index_assignment
+// = ctx->get_backend ()->assignment_statement (fnctx.fndecl,
+// index_lvalue,
+// zero, expr_locus);
+// ctx->add_statement (index_assignment);
+
+// // BEGIN loop block
+// tree loop_body = ctx->get_backend ()->block (fndecl, enclosing_scope, {},
+// start_location, end_location);
+// ctx->push_block (loop_body);
+
+// // loop predicate
+// tree loop_predicate
+// = fold_build2_loc (expr_locus.gcc_location (), GE_EXPR,
+// boolean_type_node,
+// ctx->get_backend ()->var_expression (idx, expr_locus),
+// capacity_expr);
+// tree exit_expr = fold_build1_loc (expr_locus.gcc_location (), EXIT_EXPR,
+// void_type_node, loop_predicate);
+// tree break_stmt
+// = ctx->get_backend ()->expression_statement (fnctx.fndecl, exit_expr);
+// ctx->add_statement (break_stmt);
+
+// // increment index
+// tree increment
+// = fold_build2_loc (expr_locus.gcc_location (), POSTINCREMENT_EXPR,
+// index_type,
+// ctx->get_backend ()->var_expression (idx, expr_locus),
+// build_int_cst (index_type, 1));
+
+// // create index_assess
+// tree index_access = ctx->get_backend ()->array_index_expression (
+// ctx->get_backend ()->var_expression (result, expr_locus), increment,
+// expr_locus);
+
+// // create assignment to index_access
+// tree array_assignment
+// = ctx->get_backend ()->assignment_statement (fnctx.fndecl,
+// index_access,
+// translated_expr, expr_locus);
+// ctx->add_statement (array_assignment);
+
+// // END loop block
+// ctx->pop_block ();
+
+// tree loop_expr = ctx->get_backend ()->loop_expression (loop_body,
+// expr_locus); tree loop_stmt
+// = ctx->get_backend ()->expression_statement (fnctx.fndecl, loop_expr);
+// ctx->add_statement (loop_stmt);
+
+// // result is the tmp
+// return ctx->get_backend ()->var_expression (result, expr_locus);
+// }
+
} // namespace Compile
} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 2f44621..b5047d0 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -290,7 +290,9 @@ public:
expr.get_rhs ()->get_mappings ().get_hirid (), &actual);
rust_assert (ok);
- rvalue = coercion_site (rvalue, actual, expected, expr.get_locus ());
+ rvalue
+ = coercion_site (rvalue, actual, expected, expr.get_lhs ()->get_locus (),
+ expr.get_rhs ()->get_locus ());
tree assignment
= ctx->get_backend ()->assignment_statement (fn.fndecl, lvalue, rvalue,
@@ -332,56 +334,7 @@ public:
expr.get_locus ());
}
- void visit (HIR::ArrayExpr &expr) override
- {
- TyTy::BaseType *tyty = nullptr;
- if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
- &tyty))
- {
- rust_fatal_error (expr.get_locus (),
- "did not resolve type for this array expr");
- return;
- }
-
- rust_assert (tyty->get_kind () == TyTy::TypeKind::ARRAY);
- TyTy::ArrayType *array_tyty = static_cast<TyTy::ArrayType *> (tyty);
- capacity_expr = array_tyty->get_capacity ();
-
- tree array_type = TyTyResolveCompile::compile (ctx, array_tyty);
- rust_assert (array_type != nullptr);
-
- expr.get_internal_elements ()->accept_vis (*this);
- std::vector<unsigned long> indexes;
- for (size_t i = 0; i < constructor.size (); i++)
- indexes.push_back (i);
-
- translated
- = ctx->get_backend ()->array_constructor_expression (array_type, indexes,
- constructor,
- expr.get_locus ());
- }
-
- void visit (HIR::ArrayElemsValues &elems) override
- {
- for (auto &elem : elems.get_values ())
- {
- tree translated_expr = CompileExpr::Compile (elem.get (), ctx);
- constructor.push_back (translated_expr);
- }
- }
-
- void visit (HIR::ArrayElemsCopied &elems) override
- {
- tree translated_expr
- = CompileExpr::Compile (elems.get_elem_to_copy (), ctx);
-
- size_t capacity;
- bool ok = ctx->get_backend ()->const_size_cast (capacity_expr, &capacity);
- rust_assert (ok);
-
- for (size_t i = 0; i < capacity; ++i)
- constructor.push_back (translated_expr);
- }
+ void visit (HIR::ArrayExpr &expr) override;
void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
@@ -605,14 +558,18 @@ public:
std::vector<tree> arguments;
for (size_t i = 0; i < struct_expr.get_fields ().size (); i++)
{
- auto &argument = struct_expr.get_fields ().at (i);
- auto rvalue = CompileStructExprField::Compile (argument.get (), ctx);
-
// assignments are coercion sites so lets convert the rvalue if
// necessary
auto respective_field = variant->get_field_at_index (i);
auto expected = respective_field->get_field_type ();
+ // process arguments
+ auto &argument = struct_expr.get_fields ().at (i);
+ auto lvalue_locus
+ = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+ auto rvalue_locus = argument->get_locus ();
+ auto rvalue = CompileStructExprField::Compile (argument.get (), ctx);
+
TyTy::BaseType *actual = nullptr;
bool ok = ctx->get_tyctx ()->lookup_type (
argument->get_mappings ().get_hirid (), &actual);
@@ -621,8 +578,8 @@ public:
// compile/torture/struct_base_init_1.rs
if (ok)
{
- rvalue = coercion_site (rvalue, actual, expected,
- argument->get_locus ());
+ rvalue = coercion_site (rvalue, actual, expected, lvalue_locus,
+ rvalue_locus);
}
// add it to the list
@@ -998,15 +955,19 @@ protected:
tree type_cast_expression (tree type_to_cast_to, tree expr, Location locus);
+ tree array_value_expr (Location expr_locus, const TyTy::ArrayType &array_tyty,
+ tree array_type, HIR::ArrayElemsValues &elems);
+
+ tree array_copied_expr (Location expr_locus,
+ const TyTy::ArrayType &array_tyty, tree array_type,
+ HIR::ArrayElemsCopied &elems);
+
private:
CompileExpr (Context *ctx)
- : HIRCompileBase (ctx), translated (error_mark_node),
- capacity_expr (nullptr)
+ : HIRCompileBase (ctx), translated (error_mark_node)
{}
tree translated;
- tree capacity_expr;
- std::vector<tree> constructor;
};
} // namespace Compile
diff --git a/gcc/rust/backend/rust-compile-implitem.h b/gcc/rust/backend/rust-compile-implitem.h
index 1da607a..5f4d879 100644
--- a/gcc/rust/backend/rust-compile-implitem.h
+++ b/gcc/rust/backend/rust-compile-implitem.h
@@ -288,7 +288,7 @@ public:
ctx->push_fn (fndecl, return_address);
- compile_function_body (fndecl, function.get_definition (),
+ compile_function_body (fndecl, *function.get_definition ().get (),
function.has_function_return_type ());
ctx->pop_block ();
@@ -548,7 +548,7 @@ public:
ctx->push_fn (fndecl, return_address);
- compile_function_body (fndecl, func.get_block_expr (),
+ compile_function_body (fndecl, *func.get_block_expr ().get (),
function.has_return_type ());
ctx->pop_block ();
diff --git a/gcc/rust/backend/rust-compile-item.h b/gcc/rust/backend/rust-compile-item.h
index 5af9ab3..208a2a5 100644
--- a/gcc/rust/backend/rust-compile-item.h
+++ b/gcc/rust/backend/rust-compile-item.h
@@ -27,6 +27,7 @@
#include "rust-compile-expr.h"
#include "rust-compile-fnparam.h"
#include "rust-compile-extern.h"
+#include "rust-constexpr.h"
namespace Rust {
namespace Compile {
@@ -90,24 +91,96 @@ public:
void visit (HIR::ConstantItem &constant) override
{
+ // resolve the type
TyTy::BaseType *resolved_type = nullptr;
bool ok
= ctx->get_tyctx ()->lookup_type (constant.get_mappings ().get_hirid (),
&resolved_type);
rust_assert (ok);
- tree type = TyTyResolveCompile::compile (ctx, resolved_type);
- tree value = CompileExpr::Compile (constant.get_expr (), ctx);
-
+ // canonical path
const Resolver::CanonicalPath *canonical_path = nullptr;
ok = ctx->get_mappings ()->lookup_canonical_path (
constant.get_mappings ().get_crate_num (),
constant.get_mappings ().get_nodeid (), &canonical_path);
rust_assert (ok);
-
std::string ident = canonical_path->get ();
+
+ // types
+ tree type = TyTyResolveCompile::compile (ctx, resolved_type);
+ tree const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+
+ HIR::Expr *const_value_expr = constant.get_expr ();
+ bool is_block_expr
+ = const_value_expr->get_expression_type () == HIR::Expr::ExprType::Block;
+
+ // compile the expression
+ tree folded_expr = error_mark_node;
+ if (!is_block_expr)
+ {
+ tree value = CompileExpr::Compile (constant.get_expr (), ctx);
+ folded_expr = ConstCtx::fold (value);
+ }
+ else
+ {
+ // in order to compile a block expr we want to reuse as much existing
+ // machineary that we already have. This means the best approach is to
+ // make a _fake_ function with a block so it can hold onto temps then
+ // use our constexpr code to fold it completely or error_mark_node
+ Backend::typed_identifier receiver;
+ tree compiled_fn_type = ctx->get_backend ()->function_type (
+ receiver, {},
+ {Backend::typed_identifier ("_", const_type, constant.get_locus ())},
+ NULL, constant.get_locus ());
+
+ tree fndecl
+ = ctx->get_backend ()->function (compiled_fn_type, ident, "",
+ Backend::function_read_only,
+ constant.get_locus ());
+
+ tree enclosing_scope = NULL_TREE;
+ HIR::BlockExpr *function_body
+ = static_cast<HIR::BlockExpr *> (constant.get_expr ());
+ Location start_location = function_body->get_locus ();
+ Location end_location = function_body->get_closing_locus ();
+
+ tree code_block
+ = ctx->get_backend ()->block (fndecl, enclosing_scope, {},
+ start_location, end_location);
+ ctx->push_block (code_block);
+
+ bool address_is_taken = false;
+ tree ret_var_stmt = NULL_TREE;
+ Bvariable *return_address = ctx->get_backend ()->temporary_variable (
+ fndecl, code_block, const_type, NULL, address_is_taken,
+ constant.get_locus (), &ret_var_stmt);
+
+ ctx->add_statement (ret_var_stmt);
+ ctx->push_fn (fndecl, return_address);
+
+ compile_function_body (fndecl, *function_body, true);
+
+ ctx->pop_block ();
+
+ auto body = ctx->get_backend ()->block_statement (code_block);
+ if (!ctx->get_backend ()->function_set_body (fndecl, body))
+ {
+ rust_error_at (constant.get_locus (),
+ "failed to set body to constant function");
+ return;
+ }
+
+ ctx->pop_fn ();
+
+ // lets fold it into a call expr
+ tree call = build_call_array_loc (constant.get_locus ().gcc_location (),
+ const_type, fndecl, 0, NULL);
+ folded_expr = ConstCtx::fold (call);
+ }
+
tree const_expr
- = ctx->get_backend ()->named_constant_expression (type, ident, value,
+ = ctx->get_backend ()->named_constant_expression (const_type, ident,
+ folded_expr,
constant.get_locus ());
ctx->push_const (const_expr);
@@ -183,6 +256,10 @@ public:
if (is_main_fn || function.has_visibility ())
flags |= Backend::function_is_visible;
+ // is it a const function?
+ if (function.get_qualifiers ().is_const ())
+ flags |= Backend::function_read_only;
+
const Resolver::CanonicalPath *canonical_path = nullptr;
bool ok = ctx->get_mappings ()->lookup_canonical_path (
function.get_mappings ().get_crate_num (),
@@ -288,7 +365,7 @@ public:
ctx->push_fn (fndecl, return_address);
- compile_function_body (fndecl, function.get_definition (),
+ compile_function_body (fndecl, *function.get_definition ().get (),
function.has_function_return_type ());
ctx->pop_block ();
diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc
index 27ee487..ffda652 100644
--- a/gcc/rust/backend/rust-compile-pattern.cc
+++ b/gcc/rust/backend/rust-compile-pattern.cc
@@ -17,6 +17,8 @@
// <http://www.gnu.org/licenses/>.
#include "rust-compile-pattern.h"
+#include "rust-compile-expr.h"
+#include "rust-constexpr.h"
namespace Rust {
namespace Compile {
@@ -46,20 +48,33 @@ CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern)
ok = adt->lookup_variant_by_id (variant_id, &variant);
rust_assert (ok);
- mpz_t disciminantl;
- if (variant->get_variant_type () == TyTy::VariantDef::VariantType::NUM)
+ tree case_low = error_mark_node;
+ if (variant->is_specified_discriminant_node ())
{
- mpz_init_set_ui (disciminantl, variant->get_discriminant ());
+ auto discrim_node = variant->get_discriminant_node ();
+ auto &discrim_expr = discrim_node->get_discriminant_expression ();
+
+ tree discrim_expr_node = CompileExpr::Compile (discrim_expr.get (), ctx);
+ tree folded_discrim_expr = ConstCtx::fold (discrim_expr_node);
+ case_low = folded_discrim_expr;
}
else
{
- HirId variant_id = variant->get_id ();
- mpz_init_set_ui (disciminantl, variant_id);
- }
+ mpz_t disciminantl;
+ if (variant->get_variant_type () == TyTy::VariantDef::VariantType::NUM)
+ {
+ mpz_init_set_ui (disciminantl, variant->get_discriminant ());
+ }
+ else
+ {
+ HirId variant_id = variant->get_id ();
+ mpz_init_set_ui (disciminantl, variant_id);
+ }
- tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
- tree case_low
- = double_int_to_tree (t, mpz_get_double_int (t, disciminantl, true));
+ tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
+ case_low
+ = double_int_to_tree (t, mpz_get_double_int (t, disciminantl, true));
+ }
case_label_expr
= build_case_label (case_low, NULL_TREE, associated_case_label);
diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc
index 07133b9..c1d0778 100644
--- a/gcc/rust/backend/rust-compile-resolve-path.cc
+++ b/gcc/rust/backend/rust-compile-resolve-path.cc
@@ -95,11 +95,24 @@ ResolvePathRef::resolve (const HIR::PathIdentSegment &final_segment,
tree compiled_adt_type = TyTyResolveCompile::compile (ctx, adt);
// make the ctor for the union
- mpz_t val;
- mpz_init_set_ui (val, variant->get_discriminant ());
- tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
- tree qualifier
- = double_int_to_tree (t, mpz_get_double_int (t, val, true));
+ tree qualifier = error_mark_node;
+ if (variant->is_specified_discriminant_node ())
+ {
+ auto discrim_node = variant->get_discriminant_node ();
+ auto &discrim_expr = discrim_node->get_discriminant_expression ();
+
+ tree discrim_expr_node
+ = CompileExpr::Compile (discrim_expr.get (), ctx);
+ tree folded_discrim_expr = ConstCtx::fold (discrim_expr_node);
+ qualifier = folded_discrim_expr;
+ }
+ else
+ {
+ mpz_t val;
+ mpz_init_set_ui (val, variant->get_discriminant ());
+ tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
+ qualifier = double_int_to_tree (t, mpz_get_double_int (t, val, true));
+ }
return ctx->get_backend ()->constructor_expression (compiled_adt_type,
true, {qualifier},
diff --git a/gcc/rust/backend/rust-compile-stmt.h b/gcc/rust/backend/rust-compile-stmt.h
index f3ee69d..3e73cce 100644
--- a/gcc/rust/backend/rust-compile-stmt.h
+++ b/gcc/rust/backend/rust-compile-stmt.h
@@ -112,8 +112,10 @@ public:
stmt.get_init_expr ()->get_mappings ().get_hirid (), &actual);
rust_assert (ok);
+ Location lvalue_locus = stmt.get_pattern ()->get_locus ();
+ Location rvalue_locus = stmt.get_init_expr ()->get_locus ();
TyTy::BaseType *expected = ty;
- init = coercion_site (init, actual, expected, stmt.get_locus ());
+ init = coercion_site (init, actual, expected, lvalue_locus, rvalue_locus);
auto fnctx = ctx->peek_fn ();
if (ty->is_unit ())
diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
index c147a51..d7a8ae5 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -17,8 +17,11 @@
// <http://www.gnu.org/licenses/>.
#include "rust-compile-type.h"
+#include "rust-compile-expr.h"
+#include "rust-constexpr.h"
#include "tree.h"
+#include "print-tree.h"
namespace Rust {
namespace Compile {
@@ -63,13 +66,15 @@ TyTyResolveCompile::get_implicit_enumeral_node_type (Context *ctx)
void
TyTyResolveCompile::visit (const TyTy::ErrorType &)
{
- gcc_unreachable ();
+ translated = error_mark_node;
}
+
void
TyTyResolveCompile::visit (const TyTy::InferType &)
{
- gcc_unreachable ();
+ translated = error_mark_node;
}
+
void
TyTyResolveCompile::visit (const TyTy::ClosureType &)
{
@@ -332,8 +337,11 @@ TyTyResolveCompile::visit (const TyTy::ArrayType &type)
{
tree element_type
= TyTyResolveCompile::compile (ctx, type.get_element_type ());
+ tree capacity_expr = CompileExpr::Compile (&type.get_capacity_expr (), ctx);
+ tree folded_capacity_expr = ConstCtx::fold (capacity_expr);
+
translated
- = ctx->get_backend ()->array_type (element_type, type.get_capacity ());
+ = ctx->get_backend ()->array_type (element_type, folded_capacity_expr);
}
void
diff --git a/gcc/rust/backend/rust-compile-type.h b/gcc/rust/backend/rust-compile-type.h
index 1e05e5d..4d9c780 100644
--- a/gcc/rust/backend/rust-compile-type.h
+++ b/gcc/rust/backend/rust-compile-type.h
@@ -63,13 +63,14 @@ public:
private:
TyTyResolveCompile (Context *ctx, bool trait_object_mode)
- : ctx (ctx), trait_object_mode (trait_object_mode), translated (nullptr),
- recursion_count (0)
+ : ctx (ctx), trait_object_mode (trait_object_mode),
+ translated (error_mark_node), recursion_count (0)
{}
Context *ctx;
bool trait_object_mode;
tree translated;
+
size_t recursion_count;
static const size_t kDefaultRecusionLimit = 5;
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index 610e3aa..7208ed0 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -207,11 +207,11 @@ CompileStructExprField::visit (HIR::StructExprFieldIdentifier &field)
// Shared methods in compilation
void
-HIRCompileBase::compile_function_body (
- tree fndecl, std::unique_ptr<HIR::BlockExpr> &function_body,
- bool has_return_type)
+HIRCompileBase::compile_function_body (tree fndecl,
+ HIR::BlockExpr &function_body,
+ bool has_return_type)
{
- for (auto &s : function_body->get_statements ())
+ for (auto &s : function_body.get_statements ())
{
auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
if (compiled_expr != nullptr)
@@ -222,12 +222,12 @@ HIRCompileBase::compile_function_body (
}
}
- if (function_body->has_expr ())
+ if (function_body.has_expr ())
{
// the previous passes will ensure this is a valid return
// or a valid trailing expression
tree compiled_expr
- = CompileExpr::Compile (function_body->expr.get (), ctx);
+ = CompileExpr::Compile (function_body.expr.get (), ctx);
if (compiled_expr != nullptr)
{
@@ -238,7 +238,7 @@ HIRCompileBase::compile_function_body (
auto ret = ctx->get_backend ()->return_statement (
fndecl, retstmts,
- function_body->get_final_expr ()->get_locus ());
+ function_body.get_final_expr ()->get_locus ());
ctx->add_statement (ret);
}
else
@@ -288,21 +288,33 @@ HIRCompileBase::compile_locals_for_block (Resolver::Rib &rib, tree fndecl,
}
tree
-HIRCompileBase::coercion_site (tree compiled_ref, TyTy::BaseType *actual,
- TyTy::BaseType *expected, Location locus)
+HIRCompileBase::coercion_site (tree rvalue, TyTy::BaseType *actual,
+ TyTy::BaseType *expected, Location lvalue_locus,
+ Location rvalue_locus)
{
auto root_actual_kind = actual->get_root ()->get_kind ();
auto root_expected_kind = expected->get_root ()->get_kind ();
- if (root_expected_kind == TyTy::TypeKind::DYNAMIC
- && root_actual_kind != TyTy::TypeKind::DYNAMIC)
+ if (root_expected_kind == TyTy::TypeKind::ARRAY
+ && root_actual_kind == TyTy::TypeKind::ARRAY)
+ {
+ tree tree_rval_type
+ = TyTyResolveCompile::compile (ctx, actual->get_root ());
+ tree tree_lval_type
+ = TyTyResolveCompile::compile (ctx, expected->get_root ());
+ if (!verify_array_capacities (tree_lval_type, tree_rval_type,
+ lvalue_locus, rvalue_locus))
+ return error_mark_node;
+ }
+ else if (root_expected_kind == TyTy::TypeKind::DYNAMIC
+ && root_actual_kind != TyTy::TypeKind::DYNAMIC)
{
const TyTy::DynamicObjectType *dyn
= static_cast<const TyTy::DynamicObjectType *> (expected->get_root ());
- return coerce_to_dyn_object (compiled_ref, actual, expected, dyn, locus);
+ return coerce_to_dyn_object (rvalue, actual, expected, dyn, rvalue_locus);
}
- return compiled_ref;
+ return rvalue;
}
tree
@@ -530,5 +542,57 @@ HIRCompileBase::compute_address_for_trait_item (
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;
+
+ auto 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;
+
+ auto 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 %lu "
+ "elements, found one with %lu elements",
+ ltype_length, rtype_length);
+ return false;
+ }
+
+ return true;
+}
+
} // namespace Compile
} // namespace Rust
diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc
new file mode 100644
index 0000000..293f110
--- /dev/null
+++ b/gcc/rust/backend/rust-constexpr.cc
@@ -0,0 +1,428 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-constexpr.h"
+#include "rust-location.h"
+#include "rust-diagnostics.h"
+
+#include "fold-const.h"
+#include "realmpfr.h"
+#include "convert.h"
+#include "print-tree.h"
+#include "gimplify.h"
+#include "tree-iterator.h"
+
+/* Returns true if NODE is a pointer. */
+#define TYPE_PTR_P(NODE) (TREE_CODE (NODE) == POINTER_TYPE)
+
+/* Returns true if NODE is a reference. */
+#define TYPE_REF_P(NODE) (TREE_CODE (NODE) == REFERENCE_TYPE)
+
+/* Returns true if NODE is a pointer or a reference. */
+#define INDIRECT_TYPE_P(NODE) (TYPE_PTR_P (NODE) || TYPE_REF_P (NODE))
+
+/* [basic.fundamental]
+
+ Types bool, char, wchar_t, and the signed and unsigned integer types
+ are collectively called integral types.
+
+ Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
+ types as well, which is incorrect in C++. Keep these checks in
+ ascending code order. */
+#define RS_INTEGRAL_TYPE_P(TYPE) \
+ (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
+
+/* [basic.fundamental]
+
+ Integral and floating types are collectively called arithmetic
+ types.
+
+ As a GNU extension, we also accept complex types.
+
+ Keep these checks in ascending code order. */
+#define ARITHMETIC_TYPE_P(TYPE) \
+ (RS_INTEGRAL_TYPE_P (TYPE) || TREE_CODE (TYPE) == REAL_TYPE \
+ || TREE_CODE (TYPE) == COMPLEX_TYPE)
+
+/* True iff TYPE is cv decltype(nullptr). */
+#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE)
+
+/* [basic.types]
+
+ Arithmetic types, enumeration types, pointer types,
+ pointer-to-member types, and std::nullptr_t are collectively called
+ scalar types.
+
+ Keep these checks in ascending code order. */
+#define SCALAR_TYPE_P(TYPE) \
+ (TREE_CODE (TYPE) == ENUMERAL_TYPE || ARITHMETIC_TYPE_P (TYPE) \
+ || TYPE_PTR_P (TYPE) || NULLPTR_TYPE_P (TYPE))
+
+namespace Rust {
+namespace Compile {
+
+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
+get_function_named_in_call (tree t);
+
+ConstCtx::ConstCtx () : constexpr_ops_count (0) {}
+
+tree
+ConstCtx::fold (tree expr)
+{
+ tree folded = ConstCtx ().constexpr_expression (expr);
+ rust_assert (folded != NULL_TREE);
+ return folded;
+}
+
+tree
+ConstCtx::constexpr_expression (tree t)
+{
+ location_t loc = EXPR_LOCATION (t);
+ if (CONSTANT_CLASS_P (t))
+ {
+ if (TREE_OVERFLOW (t))
+ {
+ rust_error_at (Location (loc), "overflow in constant expression");
+ return t;
+ }
+
+ if (TREE_CODE (t) == INTEGER_CST && TYPE_PTR_P (TREE_TYPE (t))
+ && !integer_zerop (t))
+ {
+ // FIXME check does this actually work to print out tree types
+ rust_error_at (Location (loc),
+ "value %qE of type %qT is not a constant expression",
+ t, TREE_TYPE (t));
+ return t;
+ }
+
+ return t;
+ }
+
+ // Avoid excessively long constexpr evaluations
+ if (++constexpr_ops_count >= constexpr_ops_limit)
+ {
+ rust_error_at (
+ Location (loc),
+ "%<constexpr%> 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 (t);
+ break;
+
+ case CALL_EXPR:
+ r = eval_call_expression (t);
+ break;
+
+ case RETURN_EXPR:
+ rust_assert (TREE_OPERAND (t, 0) != NULL_TREE);
+ r = constexpr_expression (TREE_OPERAND (t, 0));
+ break;
+
+ case MODIFY_EXPR:
+ r = eval_store_expression (t);
+ break;
+
+ default:
+ break;
+ }
+
+ return r;
+}
+
+tree
+ConstCtx::eval_store_expression (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 = ConstCtx::fold (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 (probe);
+ evaluated = true;
+ }
+ break;
+ }
+ }
+
+ return init;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Like cxx_eval_unary_expression, except for binary expressions. */
+
+tree
+ConstCtx::eval_binary_expression (tree t)
+{
+ tree orig_lhs = TREE_OPERAND (t, 0);
+ tree orig_rhs = TREE_OPERAND (t, 1);
+ tree lhs, rhs;
+
+ lhs = constexpr_expression (orig_lhs);
+ rhs = constexpr_expression (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.
+tree
+ConstCtx::eval_call_expression (tree t)
+{
+ tree fun = get_function_named_in_call (t);
+ return constexpr_fn_retval (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.
+tree
+ConstCtx::constexpr_fn_retval (tree body)
+{
+ switch (TREE_CODE (body))
+ {
+ case STATEMENT_LIST: {
+ tree expr = NULL_TREE;
+ for (tree stmt : tsi_range (body))
+ {
+ tree s = constexpr_fn_retval (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 (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 (TREE_OPERAND (body, 0));
+
+ case BIND_EXPR: {
+ tree b = BIND_EXPR_BODY (body);
+ return constexpr_fn_retval (b);
+ }
+ break;
+
+ default:
+ return error_mark_node;
+ }
+ return error_mark_node;
+}
+
+// Taken from cp/constexpr.c
+//
+// 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.c
+}
+
+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;
+}
+
+} // 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 0000000..40d9159
--- /dev/null
+++ b/gcc/rust/backend/rust-constexpr.h
@@ -0,0 +1,46 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_CONSTEXPR
+#define RUST_CONSTEXPR
+
+#include "rust-system.h"
+#include "tree.h"
+
+namespace Rust {
+namespace Compile {
+
+class ConstCtx
+{
+public:
+ static tree fold (tree);
+
+ tree constexpr_expression (tree);
+ tree eval_binary_expression (tree);
+ tree eval_call_expression (tree);
+ tree constexpr_fn_retval (tree);
+ tree eval_store_expression (tree);
+
+private:
+ ConstCtx ();
+
+ HOST_WIDE_INT constexpr_ops_count;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_CONSTEXPR