gccrs: Add base for HIR to GCC GENERIC lowering

This pass walks the HIR crate and turns them into GCC `tree`s. We do not have
any Rust specific tree's. We are slowly removing the backend abstraction
which was ported over from gccgo in favour of using `tree`s directly.

	gcc/rust/
	* backend/rust-builtins.h: New.
	* backend/rust-compile-base.cc: New.
	* backend/rust-compile-base.h: New.
	* backend/rust-mangle.cc: New.
	* backend/rust-mangle.h: New.
	* backend/rust-tree.cc: New.
	* backend/rust-tree.h: New.
	* rust-backend.h: New.
	* rust-gcc.cc: New.

Co-authored-by: David Faust <david.faust@oracle.com>
This commit is contained in:
Philip Herron
2022-10-21 14:01:04 +02:00
committed by Arthur Cohen
parent 509e4c32c6
commit 15f04af347
9 changed files with 6114 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
// 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_BUILTINS_H
#define RUST_BUILTINS_H
#include "rust-system.h"
#include "tree.h"
#include "langhooks.h"
namespace Rust {
namespace Compile {
// https://github.com/rust-lang/rust/blob/master/library/core/src/intrinsics.rs
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs
// https://github.com/Rust-GCC/gccrs/issues/658
//
// let llvm_name = match name {
// sym::sqrtf32 => "llvm.sqrt.f32",
// sym::sqrtf64 => "llvm.sqrt.f64",
// sym::powif32 => "llvm.powi.f32",
// sym::powif64 => "llvm.powi.f64",
// sym::sinf32 => "llvm.sin.f32",
// sym::sinf64 => "llvm.sin.f64",
// sym::cosf32 => "llvm.cos.f32",
// sym::cosf64 => "llvm.cos.f64",
// sym::powf32 => "llvm.pow.f32",
// sym::powf64 => "llvm.pow.f64",
// sym::expf32 => "llvm.exp.f32",
// sym::expf64 => "llvm.exp.f64",
// sym::exp2f32 => "llvm.exp2.f32",
// sym::exp2f64 => "llvm.exp2.f64",
// sym::logf32 => "llvm.log.f32",
// sym::logf64 => "llvm.log.f64",
// sym::log10f32 => "llvm.log10.f32",
// sym::log10f64 => "llvm.log10.f64",
// sym::log2f32 => "llvm.log2.f32",
// sym::log2f64 => "llvm.log2.f64",
// sym::fmaf32 => "llvm.fma.f32",
// sym::fmaf64 => "llvm.fma.f64",
// sym::fabsf32 => "llvm.fabs.f32",
// sym::fabsf64 => "llvm.fabs.f64",
// sym::minnumf32 => "llvm.minnum.f32",
// sym::minnumf64 => "llvm.minnum.f64",
// sym::maxnumf32 => "llvm.maxnum.f32",
// sym::maxnumf64 => "llvm.maxnum.f64",
// sym::copysignf32 => "llvm.copysign.f32",
// sym::copysignf64 => "llvm.copysign.f64",
// sym::floorf32 => "llvm.floor.f32",
// sym::floorf64 => "llvm.floor.f64",
// sym::ceilf32 => "llvm.ceil.f32",
// sym::ceilf64 => "llvm.ceil.f64",
// sym::truncf32 => "llvm.trunc.f32",
// sym::truncf64 => "llvm.trunc.f64",
// sym::rintf32 => "llvm.rint.f32",
// sym::rintf64 => "llvm.rint.f64",
// sym::nearbyintf32 => "llvm.nearbyint.f32",
// sym::nearbyintf64 => "llvm.nearbyint.f64",
// sym::roundf32 => "llvm.round.f32",
// sym::roundf64 => "llvm.round.f64",
// _ => return None,
// };
// Some(cx.get_intrinsic(&llvm_name))
class BuiltinsContext
{
public:
static BuiltinsContext &get ()
{
static BuiltinsContext instance;
return instance;
}
bool lookup_simple_builtin (const std::string &name, tree *builtin)
{
auto it = rust_intrinsic_to_gcc_builtin.find (name);
if (it == rust_intrinsic_to_gcc_builtin.end ())
return false;
return lookup_gcc_builtin (it->second, builtin);
}
private:
static const int builtin_const = 1 << 0;
static const int builtin_noreturn = 1 << 1;
static const int builtin_novops = 1 << 2;
BuiltinsContext () { setup (); }
void setup ()
{
tree math_function_type_f32
= build_function_type_list (float_type_node, float_type_node, NULL_TREE);
define_builtin ("sinf32", BUILT_IN_SINF, "__builtin_sinf", "sinf",
math_function_type_f32, builtin_const);
define_builtin ("sqrtf32", BUILT_IN_SQRTF, "__builtin_sqrtf", "sqrtf",
math_function_type_f32, builtin_const);
define_builtin ("unreachable", BUILT_IN_UNREACHABLE,
"__builtin_unreachable", NULL,
build_function_type (void_type_node, void_list_node),
builtin_const | builtin_noreturn);
define_builtin ("abort", BUILT_IN_ABORT, "__builtin_abort", "abort",
build_function_type (void_type_node, void_list_node),
builtin_const | builtin_noreturn);
define_builtin ("breakpoint", BUILT_IN_TRAP, "__builtin_trap", "breakpoint",
build_function_type (void_type_node, void_list_node),
builtin_const | builtin_noreturn);
define_builtin (
"memcpy", BUILT_IN_MEMCPY, "__builtin_memcpy", "memcpy",
build_function_type_list (build_pointer_type (void_type_node),
build_pointer_type (void_type_node),
build_pointer_type (void_type_node),
size_type_node, NULL_TREE),
0);
}
// Define a builtin function. BCODE is the builtin function code
// defined by builtins.def. NAME is the name of the builtin function.
// LIBNAME is the name of the corresponding library function, and is
// NULL if there isn't one. FNTYPE is the type of the function.
// CONST_P is true if the function has the const attribute.
// NORETURN_P is true if the function has the noreturn attribute.
void define_builtin (const std::string rust_name, built_in_function bcode,
const char *name, const char *libname, tree fntype,
int flags)
{
tree decl = add_builtin_function (name, fntype, bcode, BUILT_IN_NORMAL,
libname, NULL_TREE);
if ((flags & builtin_const) != 0)
TREE_READONLY (decl) = 1;
if ((flags & builtin_noreturn) != 0)
TREE_THIS_VOLATILE (decl) = 1;
if ((flags & builtin_novops) != 0)
DECL_IS_NOVOPS (decl) = 1;
set_builtin_decl (bcode, decl, true);
this->builtin_functions_[name] = decl;
if (libname != NULL)
{
decl = add_builtin_function (libname, fntype, bcode, BUILT_IN_NORMAL,
NULL, NULL_TREE);
if ((flags & builtin_const) != 0)
TREE_READONLY (decl) = 1;
if ((flags & builtin_noreturn) != 0)
TREE_THIS_VOLATILE (decl) = 1;
if ((flags & builtin_novops) != 0)
DECL_IS_NOVOPS (decl) = 1;
this->builtin_functions_[libname] = decl;
}
rust_intrinsic_to_gcc_builtin[rust_name] = name;
}
bool lookup_gcc_builtin (const std::string &name, tree *builtin)
{
auto it = builtin_functions_.find (name);
if (it == builtin_functions_.end ())
return false;
*builtin = it->second;
return true;
}
// A mapping of the GCC built-ins exposed to GCC Rust.
std::map<std::string, tree> builtin_functions_;
std::map<std::string, std::string> rust_intrinsic_to_gcc_builtin;
};
} // namespace Compile
} // namespace Rust
#endif // RUST_BUILTINS_H

View File

@@ -0,0 +1,730 @@
// 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
// <http://www.gnu.org/licenses/>.
#include "rust-compile-base.h"
#include "rust-abi.h"
#include "rust-compile-item.h"
#include "rust-compile-stmt.h"
#include "rust-compile-expr.h"
#include "rust-compile-fnparam.h"
#include "rust-compile-var-decl.h"
#include "rust-constexpr.h"
#include "rust-diagnostics.h"
#include "rust-expr.h" // for AST::AttrInputLiteral
#include "rust-macro.h" // for AST::MetaNameValueStr
#include "fold-const.h"
#include "stringpool.h"
#include "attribs.h"
#include "tree.h"
namespace Rust {
namespace Compile {
bool inline should_mangle_item (const tree fndecl)
{
return lookup_attribute ("no_mangle", DECL_ATTRIBUTES (fndecl)) == NULL_TREE;
}
void
HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point,
bool is_generic_fn, HIR::Visibility &visibility,
const HIR::FunctionQualifiers &qualifiers,
const AST::AttrVec &attrs)
{
// if its the main fn or pub visibility mark its as DECL_PUBLIC
// please see https://github.com/Rust-GCC/gccrs/pull/137
bool is_pub = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
if (is_main_entry_point || (is_pub && !is_generic_fn))
{
TREE_PUBLIC (fndecl) = 1;
}
// is it a const fn
if (qualifiers.is_const ())
{
TREE_READONLY (fndecl) = 1;
}
// is it inline?
for (const auto &attr : attrs)
{
bool is_inline = attr.get_path ().as_string ().compare ("inline") == 0;
bool is_must_use
= attr.get_path ().as_string ().compare ("must_use") == 0;
bool is_cold = attr.get_path ().as_string ().compare ("cold") == 0;
bool is_link_section
= attr.get_path ().as_string ().compare ("link_section") == 0;
bool no_mangle = attr.get_path ().as_string ().compare ("no_mangle") == 0;
bool is_deprecated
= attr.get_path ().as_string ().compare ("deprecated") == 0;
if (is_inline)
{
handle_inline_attribute_on_fndecl (fndecl, attr);
}
else if (is_must_use)
{
handle_must_use_attribute_on_fndecl (fndecl, attr);
}
else if (is_cold)
{
handle_cold_attribute_on_fndecl (fndecl, attr);
}
else if (is_link_section)
{
handle_link_section_attribute_on_fndecl (fndecl, attr);
}
else if (is_deprecated)
{
handle_deprecated_attribute_on_fndecl (fndecl, attr);
}
else if (no_mangle)
{
handle_no_mangle_attribute_on_fndecl (fndecl, attr);
}
}
}
void
HIRCompileBase::handle_cold_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr)
{
// simple #[cold]
if (!attr.has_attr_input ())
{
tree cold = get_identifier ("cold");
// this will get handled by the GCC backend later
DECL_ATTRIBUTES (fndecl)
= tree_cons (cold, NULL_TREE, DECL_ATTRIBUTES (fndecl));
return;
}
rust_error_at (attr.get_locus (),
"attribute %<cold%> does not accept any arguments");
}
void
HIRCompileBase::handle_link_section_attribute_on_fndecl (
tree fndecl, const AST::Attribute &attr)
{
if (!attr.has_attr_input ())
{
rust_error_at (attr.get_locus (),
"%<link_section%> expects exactly one argment");
return;
}
rust_assert (attr.get_attr_input ().get_attr_input_type ()
== AST::AttrInput::AttrInputType::LITERAL);
auto &literal = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
const auto &msg_str = literal.get_literal ().as_string ();
if (decl_section_name (fndecl))
{
rust_warning_at (attr.get_locus (), 0, "section name redefined");
}
set_decl_section_name (fndecl, msg_str.c_str ());
}
void
HIRCompileBase::handle_no_mangle_attribute_on_fndecl (
tree fndecl, const AST::Attribute &attr)
{
if (attr.has_attr_input ())
{
rust_error_at (attr.get_locus (),
"attribute %<no_mangle%> does not accept any arguments");
return;
}
DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("no_mangle"), NULL_TREE,
DECL_ATTRIBUTES (fndecl));
}
void
HIRCompileBase::handle_deprecated_attribute_on_fndecl (
tree fndecl, const AST::Attribute &attr)
{
tree value = NULL_TREE;
TREE_DEPRECATED (fndecl) = 1;
// simple #[deprecated]
if (!attr.has_attr_input ())
return;
const AST::AttrInput &input = attr.get_attr_input ();
auto input_type = input.get_attr_input_type ();
if (input_type == AST::AttrInput::AttrInputType::LITERAL)
{
// handle #[deprecated = "message"]
auto &literal
= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
const auto &msg_str = literal.get_literal ().as_string ();
value = build_string (msg_str.size (), msg_str.c_str ());
}
else if (input_type == AST::AttrInput::AttrInputType::TOKEN_TREE)
{
// handle #[deprecated(since = "...", note = "...")]
const auto &option = static_cast<const AST::DelimTokenTree &> (input);
AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
for (const auto &item : meta_item->get_items ())
{
auto converted_item = item->to_meta_name_value_str ();
if (!converted_item)
continue;
auto key_value = converted_item->get_name_value_pair ();
if (key_value.first.compare ("since") == 0)
{
// valid, but this is handled by Cargo and some third-party audit
// tools
continue;
}
else if (key_value.first.compare ("note") == 0)
{
const auto &msg_str = key_value.second;
if (value)
rust_error_at (attr.get_locus (), "multiple %<note%> items");
value = build_string (msg_str.size (), msg_str.c_str ());
}
else
{
rust_error_at (attr.get_locus (), "unknown meta item %qs",
key_value.first.c_str ());
}
}
}
if (value)
{
tree attr_list = build_tree_list (NULL_TREE, value);
DECL_ATTRIBUTES (fndecl)
= tree_cons (get_identifier ("deprecated"), attr_list,
DECL_ATTRIBUTES (fndecl));
}
}
void
HIRCompileBase::handle_inline_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr)
{
// simple #[inline]
if (!attr.has_attr_input ())
{
DECL_DECLARED_INLINE_P (fndecl) = 1;
return;
}
const AST::AttrInput &input = attr.get_attr_input ();
bool is_token_tree
= input.get_attr_input_type () == AST::AttrInput::AttrInputType::TOKEN_TREE;
rust_assert (is_token_tree);
const auto &option = static_cast<const AST::DelimTokenTree &> (input);
AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
if (meta_item->get_items ().size () != 1)
{
rust_error_at (attr.get_locus (), "invalid number of arguments");
return;
}
const std::string inline_option
= meta_item->get_items ().at (0)->as_string ();
// we only care about NEVER and ALWAYS else its an error
bool is_always = inline_option.compare ("always") == 0;
bool is_never = inline_option.compare ("never") == 0;
// #[inline(never)]
if (is_never)
{
DECL_UNINLINABLE (fndecl) = 1;
}
// #[inline(always)]
else if (is_always)
{
DECL_DECLARED_INLINE_P (fndecl) = 1;
DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
NULL, DECL_ATTRIBUTES (fndecl));
}
else
{
rust_error_at (attr.get_locus (), "unknown inline option");
}
}
void
HIRCompileBase::handle_must_use_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr)
{
tree nodiscard = get_identifier ("nodiscard");
tree value = NULL_TREE;
if (attr.has_attr_input ())
{
rust_assert (attr.get_attr_input ().get_attr_input_type ()
== AST::AttrInput::AttrInputType::LITERAL);
auto &literal
= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
const auto &msg_str = literal.get_literal ().as_string ();
tree message = build_string (msg_str.size (), msg_str.c_str ());
value = tree_cons (nodiscard, message, NULL_TREE);
}
DECL_ATTRIBUTES (fndecl)
= tree_cons (nodiscard, value, DECL_ATTRIBUTES (fndecl));
}
void
HIRCompileBase::setup_abi_options (tree fndecl, ABI abi)
{
tree abi_tree = NULL_TREE;
switch (abi)
{
case Rust::ABI::RUST:
case Rust::ABI::INTRINSIC:
case Rust::ABI::C:
case Rust::ABI::CDECL:
// `decl_attributes` function (not the macro) has the side-effect of
// actually switching the codegen backend to use the ABI we annotated.
// However, since `cdecl` is the default ABI GCC will be using, explicitly
// specifying that ABI will cause GCC to emit a warning saying the
// attribute is useless (which is confusing to the user as the attribute
// is added by us).
DECL_ATTRIBUTES (fndecl)
= tree_cons (get_identifier ("cdecl"), NULL, DECL_ATTRIBUTES (fndecl));
return;
case Rust::ABI::STDCALL:
abi_tree = get_identifier ("stdcall");
break;
case Rust::ABI::FASTCALL:
abi_tree = get_identifier ("fastcall");
break;
case Rust::ABI::SYSV64:
abi_tree = get_identifier ("sysv_abi");
break;
case Rust::ABI::WIN64:
abi_tree = get_identifier ("ms_abi");
break;
default:
break;
}
decl_attributes (&fndecl, build_tree_list (abi_tree, NULL_TREE), 0);
}
// ported from gcc/c/c-typecheck.c
//
// Mark EXP saying that we need to be able to take the
// address of it; it should not be allocated in a register.
// Returns true if successful. ARRAY_REF_P is true if this
// is for ARRAY_REF construction - in that case we don't want
// to look through VIEW_CONVERT_EXPR from VECTOR_TYPE to ARRAY_TYPE,
// it is fine to use ARRAY_REFs for vector subscripts on vector
// register variables.
bool
HIRCompileBase::mark_addressable (tree exp, Location locus)
{
tree x = exp;
while (1)
switch (TREE_CODE (x))
{
case VIEW_CONVERT_EXPR:
if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
&& VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0))))
return true;
x = TREE_OPERAND (x, 0);
break;
case COMPONENT_REF:
// TODO
// if (DECL_C_BIT_FIELD (TREE_OPERAND (x, 1)))
// {
// error ("cannot take address of bit-field %qD", TREE_OPERAND (x,
// 1)); return false;
// }
/* FALLTHRU */
case ADDR_EXPR:
case ARRAY_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
x = TREE_OPERAND (x, 0);
break;
case COMPOUND_LITERAL_EXPR:
TREE_ADDRESSABLE (x) = 1;
TREE_ADDRESSABLE (COMPOUND_LITERAL_EXPR_DECL (x)) = 1;
return true;
case CONSTRUCTOR:
TREE_ADDRESSABLE (x) = 1;
return true;
case VAR_DECL:
case CONST_DECL:
case PARM_DECL:
case RESULT_DECL:
// (we don't have a concept of a "register" declaration)
// fallthrough */
/* FALLTHRU */
case FUNCTION_DECL:
TREE_ADDRESSABLE (x) = 1;
/* FALLTHRU */
default:
return true;
}
return false;
}
tree
HIRCompileBase::address_expression (tree expr, Location location)
{
if (expr == error_mark_node)
return error_mark_node;
if (!mark_addressable (expr, location))
return error_mark_node;
return build_fold_addr_expr_loc (location.gcc_location (), expr);
}
tree
HIRCompileBase::indirect_expression (tree expr, Location locus)
{
if (expr == error_mark_node)
return error_mark_node;
return build_fold_indirect_ref_loc (locus.gcc_location (), expr);
}
std::vector<Bvariable *>
HIRCompileBase::compile_locals_for_block (Context *ctx, Resolver::Rib &rib,
tree fndecl)
{
std::vector<Bvariable *> locals;
for (auto it : rib.get_declarations ())
{
NodeId node_id = it.first;
HirId ref = UNKNOWN_HIRID;
if (!ctx->get_mappings ()->lookup_node_to_hir (node_id, &ref))
continue;
// we only care about local patterns
HIR::Pattern *pattern = ctx->get_mappings ()->lookup_hir_pattern (ref);
if (pattern == nullptr)
continue;
// lookup the type
TyTy::BaseType *tyty = nullptr;
if (!ctx->get_tyctx ()->lookup_type (ref, &tyty))
continue;
// compile the local
tree type = TyTyResolveCompile::compile (ctx, tyty);
Bvariable *compiled
= CompileVarDecl::compile (fndecl, type, pattern, ctx);
locals.push_back (compiled);
}
return locals;
}
void
HIRCompileBase::compile_function_body (Context *ctx, tree fndecl,
HIR::BlockExpr &function_body,
bool has_return_type)
{
for (auto &s : function_body.get_statements ())
{
auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
if (compiled_expr != nullptr)
{
tree s = convert_to_void (compiled_expr, ICV_STATEMENT);
ctx->add_statement (s);
}
}
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);
if (compiled_expr != nullptr)
{
if (has_return_type)
{
std::vector<tree> retstmts;
retstmts.push_back (compiled_expr);
auto ret = ctx->get_backend ()->return_statement (
fndecl, retstmts,
function_body.get_final_expr ()->get_locus ());
ctx->add_statement (ret);
}
else
{
// FIXME can this actually happen?
ctx->add_statement (compiled_expr);
}
}
}
}
tree
HIRCompileBase::compile_function (
Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
std::vector<HIR::FunctionParam> &function_params,
const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
bool function_has_return)
{
tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
std::string ir_symbol_name
= canonical_path->get () + fntype->subst_as_string ();
// we don't mangle the main fn since we haven't implemented the main shim
bool is_main_fn = fn_name.compare ("main") == 0;
std::string asm_name = fn_name;
unsigned int flags = 0;
tree fndecl = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name,
"" /* asm_name */, flags, locus);
setup_fndecl (fndecl, is_main_fn, fntype->has_subsititions_defined (),
visibility, qualifiers, outer_attrs);
setup_abi_options (fndecl, qualifiers.get_abi ());
// conditionally mangle the function name
bool should_mangle = should_mangle_item (fndecl);
if (!is_main_fn && should_mangle)
asm_name = ctx->mangle_item (fntype, *canonical_path);
SET_DECL_ASSEMBLER_NAME (fndecl,
get_identifier_with_length (asm_name.data (),
asm_name.length ()));
// insert into the context
ctx->insert_function_decl (fntype, fndecl);
// setup the params
TyTy::BaseType *tyret = fntype->get_return_type ();
std::vector<Bvariable *> param_vars;
if (!self_param.is_error ())
{
rust_assert (fntype->is_method ());
TyTy::BaseType *self_tyty_lookup = fntype->get_self_type ();
tree self_type = TyTyResolveCompile::compile (ctx, self_tyty_lookup);
Bvariable *compiled_self_param
= CompileSelfParam::compile (ctx, fndecl, self_param, self_type,
self_param.get_locus ());
param_vars.push_back (compiled_self_param);
ctx->insert_var_decl (self_param.get_mappings ().get_hirid (),
compiled_self_param);
}
// offset from + 1 for the TyTy::FnType being used when this is a method to
// skip over Self on the FnType
bool is_method = !self_param.is_error ();
size_t i = is_method ? 1 : 0;
for (auto &referenced_param : function_params)
{
auto tyty_param = fntype->param_at (i++);
auto param_tyty = tyty_param.second;
auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
Location param_locus = referenced_param.get_locus ();
Bvariable *compiled_param_var
= CompileFnParam::compile (ctx, fndecl, &referenced_param,
compiled_param_type, param_locus);
param_vars.push_back (compiled_param_var);
const HIR::Pattern &param_pattern = *referenced_param.get_param_name ();
ctx->insert_var_decl (param_pattern.get_pattern_mappings ().get_hirid (),
compiled_param_var);
}
if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
return error_mark_node;
// lookup locals
auto body_mappings = function_body->get_mappings ();
Resolver::Rib *rib = nullptr;
bool ok
= ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
rust_assert (ok);
std::vector<Bvariable *> locals
= compile_locals_for_block (ctx, *rib, fndecl);
tree enclosing_scope = NULL_TREE;
Location start_location = function_body->get_locus ();
Location end_location = function_body->get_end_locus ();
tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
start_location, end_location);
ctx->push_block (code_block);
Bvariable *return_address = nullptr;
if (function_has_return)
{
tree return_type = TyTyResolveCompile::compile (ctx, tyret);
bool address_is_taken = false;
tree ret_var_stmt = NULL_TREE;
return_address
= ctx->get_backend ()->temporary_variable (fndecl, code_block,
return_type, NULL,
address_is_taken, locus,
&ret_var_stmt);
ctx->add_statement (ret_var_stmt);
}
ctx->push_fn (fndecl, return_address);
compile_function_body (ctx, fndecl, *function_body, function_has_return);
tree bind_tree = ctx->pop_block ();
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
DECL_SAVED_TREE (fndecl) = bind_tree;
ctx->pop_fn ();
ctx->push_function (fndecl);
return fndecl;
}
tree
HIRCompileBase::compile_constant_item (
Context *ctx, TyTy::BaseType *resolved_type,
const Resolver::CanonicalPath *canonical_path, HIR::Expr *const_value_expr,
Location locus)
{
const std::string &ident = canonical_path->get ();
tree type = TyTyResolveCompile::compile (ctx, resolved_type);
tree const_type = build_qualified_type (type, TYPE_QUAL_CONST);
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 (const_value_expr, ctx);
folded_expr = fold_expr (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, locus)},
NULL, locus);
tree fndecl
= ctx->get_backend ()->function (compiled_fn_type, ident, "", 0, locus);
TREE_READONLY (fndecl) = 1;
tree enclosing_scope = NULL_TREE;
HIR::BlockExpr *function_body
= static_cast<HIR::BlockExpr *> (const_value_expr);
Location start_location = function_body->get_locus ();
Location end_location = function_body->get_end_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, locus,
&ret_var_stmt);
ctx->add_statement (ret_var_stmt);
ctx->push_fn (fndecl, return_address);
compile_function_body (ctx, fndecl, *function_body, true);
tree bind_tree = ctx->pop_block ();
gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
DECL_SAVED_TREE (fndecl) = bind_tree;
ctx->pop_fn ();
// lets fold it into a call expr
tree call = build_call_array_loc (locus.gcc_location (), const_type,
fndecl, 0, NULL);
folded_expr = fold_expr (call);
}
return named_constant_expression (const_type, ident, folded_expr, locus);
}
tree
HIRCompileBase::named_constant_expression (tree type_tree,
const std::string &name,
tree const_val, Location location)
{
if (type_tree == error_mark_node || const_val == error_mark_node)
return error_mark_node;
tree name_tree = get_identifier_with_length (name.data (), name.length ());
tree decl
= build_decl (location.gcc_location (), CONST_DECL, name_tree, type_tree);
DECL_INITIAL (decl) = const_val;
TREE_CONSTANT (decl) = 1;
TREE_READONLY (decl) = 1;
rust_preserve_from_gc (decl);
return decl;
}
} // namespace Compile
} // namespace Rust

View File

@@ -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
// <http://www.gnu.org/licenses/>.
#ifndef RUST_COMPILE_BASE
#define RUST_COMPILE_BASE
#include "rust-compile-context.h"
#include "rust-compile-type.h"
#include "rust-hir-visitor.h"
#include "rust-hir-full.h"
namespace Rust {
namespace Compile {
class HIRCompileBase
{
public:
virtual ~HIRCompileBase () {}
protected:
HIRCompileBase (Context *ctx) : ctx (ctx) {}
Context *ctx;
protected:
Context *get_context () { return ctx; }
tree coercion_site (HirId id, tree rvalue, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, Location lvalue_locus,
Location rvalue_locus);
tree coercion_site1 (tree rvalue, const TyTy::BaseType *actual,
const TyTy::BaseType *expected, Location lvalue_locus,
Location rvalue_locus);
tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
const TyTy::DynamicObjectType *ty, Location locus);
tree compute_address_for_trait_item (
const Resolver::TraitItemReference *ref,
const TyTy::TypeBoundPredicate *predicate,
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);
tree query_compile (HirId ref, TyTy::BaseType *lookup,
const HIR::PathIdentSegment &final_segment,
const Analysis::NodeMapping &mappings,
Location expr_locus, bool is_qualified_path);
tree resolve_adjustements (std::vector<Resolver::Adjustment> &adjustments,
tree expression, Location locus);
tree resolve_deref_adjustment (Resolver::Adjustment &adjustment,
tree expression, Location locus);
tree resolve_indirection_adjustment (Resolver::Adjustment &adjustment,
tree expression, Location locus);
tree resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
tree expression, Location locus);
tree resolve_unsized_slice_adjustment (Resolver::Adjustment &adjustment,
tree expression, Location locus);
tree resolve_unsized_dyn_adjustment (Resolver::Adjustment &adjustment,
tree expression, Location locus);
static void setup_fndecl (tree fndecl, bool is_main_entry_point,
bool is_generic_fn, HIR::Visibility &visibility,
const HIR::FunctionQualifiers &qualifiers,
const AST::AttrVec &attrs);
static void handle_inline_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void handle_cold_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void handle_must_use_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void
handle_link_section_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void
handle_deprecated_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void handle_no_mangle_attribute_on_fndecl (tree fndecl,
const AST::Attribute &attr);
static void setup_abi_options (tree fndecl, ABI abi);
static tree address_expression (tree expr, Location locus);
static tree indirect_expression (tree expr, Location locus);
static bool mark_addressable (tree, Location);
static std::vector<Bvariable *>
compile_locals_for_block (Context *ctx, Resolver::Rib &rib, tree fndecl);
static void compile_function_body (Context *ctx, tree fndecl,
HIR::BlockExpr &function_body,
bool has_return_type);
static tree compile_function (
Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
std::vector<HIR::FunctionParam> &function_params,
const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
bool function_has_return);
static tree
compile_constant_item (Context *ctx, TyTy::BaseType *resolved_type,
const Resolver::CanonicalPath *canonical_path,
HIR::Expr *const_value_expr, Location locus);
static tree named_constant_expression (tree type_tree,
const std::string &name,
tree const_val, Location location);
};
} // namespace Compile
} // namespace Rust
#endif // RUST_COMPILE_BASE

View File

@@ -0,0 +1,307 @@
#include "rust-mangle.h"
#include "fnv-hash.h"
#include "rust-base62.h"
// FIXME: Rename those to legacy_*
static const std::string kMangledSymbolPrefix = "_ZN";
static const std::string kMangledSymbolDelim = "E";
static const std::string kMangledGenericDelim = "$C$";
static const std::string kMangledSubstBegin = "$LT$";
static const std::string kMangledSubstEnd = "$GT$";
static const std::string kMangledSpace = "$u20$";
static const std::string kMangledRef = "$RF$";
static const std::string kMangledPtr = "$BP$";
static const std::string kMangledLeftSqParen = "$u5b$"; // [
static const std::string kMangledRightSqParen = "$u5d$"; // ]
static const std::string kQualPathBegin = "_" + kMangledSubstBegin;
static const std::string kMangledComma = "$C$";
namespace Rust {
namespace Compile {
Mangler::MangleVersion Mangler::version = MangleVersion::LEGACY;
static std::string
legacy_mangle_name (const std::string &name)
{
// example
// <&T as core::fmt::Debug>::fmt:
// _ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h6dac924c0051eef7E
// replace all white space with $ and & with RF
//
// <example::Bar as example::A>::fooA:
// _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E:
//
// core::ptr::const_ptr::<impl *const T>::cast:
// _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E:
//
// core::ptr::const_ptr::<impl *const [T]>::as_ptr:
// _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE:
//
// example::Foo<T>::new:
// _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E:
//
// <example::Identity as example::FnLike<&T,&T>>::call
// _ZN74_$LT$example..Identity$u20$as$u20$example..FnLike$LT$$RF$T$C$$RF$T$GT$$GT$4call17ha9ee58935895acb3E
std::string buffer;
for (size_t i = 0; i < name.size (); i++)
{
std::string m;
char c = name.at (i);
if (c == ' ')
m = kMangledSpace;
else if (c == '&')
m = kMangledRef;
else if (i == 0 && c == '<')
m = kQualPathBegin;
else if (c == '<')
m = kMangledSubstBegin;
else if (c == '>')
m = kMangledSubstEnd;
else if (c == '*')
m = kMangledPtr;
else if (c == '[')
m = kMangledLeftSqParen;
else if (c == ']')
m = kMangledRightSqParen;
else if (c == ',')
m = kMangledComma;
else if (c == ':')
{
rust_assert (i + 1 < name.size ());
rust_assert (name.at (i + 1) == ':');
i++;
m = "..";
}
else
m.push_back (c);
buffer += m;
}
return std::to_string (buffer.size ()) + buffer;
}
static std::string
legacy_mangle_canonical_path (const Resolver::CanonicalPath &path)
{
std::string buffer;
for (size_t i = 0; i < path.size (); i++)
{
auto &seg = path.get_seg_at (i);
buffer += legacy_mangle_name (seg.second);
}
return buffer;
}
// rustc uses a sip128 hash for legacy mangling, but an fnv 128 was quicker to
// implement for now
static std::string
legacy_hash (const std::string &fingerprint)
{
Hash::FNV128 hasher;
hasher.write ((const unsigned char *) fingerprint.c_str (),
fingerprint.size ());
uint64_t hi, lo;
hasher.sum (&hi, &lo);
char hex[16 + 1];
memset (hex, 0, sizeof hex);
snprintf (hex, sizeof hex, "%08" PRIx64 "%08" PRIx64, lo, hi);
return "h" + std::string (hex, sizeof (hex) - 1);
}
static std::string
v0_tuple_prefix (const TyTy::BaseType *ty)
{
if (ty->is_unit ())
return "u";
// FIXME: ARTHUR: Add rest of algorithm
return "";
}
static std::string
v0_numeric_prefix (const TyTy::BaseType *ty)
{
static const std::map<std::string, std::string> num_prefixes = {
{"[i8]", "a"}, {"[u8]", "h"}, {"[i16]", "s"}, {"[u16]", "t"},
{"[i32]", "l"}, {"[u32]", "m"}, {"[i64]", "x"}, {"[u64]", "y"},
{"[isize]", "i"}, {"[usize]", "j"}, {"[f32]", "f"}, {"[f64]", "d"},
};
auto ty_kind = ty->get_kind ();
auto ty_str = ty->as_string ();
auto numeric_iter = num_prefixes.end ();
// Special numeric types
if (ty_kind == TyTy::TypeKind::ISIZE)
return "i";
else if (ty_kind == TyTy::TypeKind::USIZE)
return "j";
numeric_iter = num_prefixes.find (ty_str);
if (numeric_iter != num_prefixes.end ())
return numeric_iter->second;
return "";
}
static std::string
v0_simple_type_prefix (const TyTy::BaseType *ty)
{
switch (ty->get_kind ())
{
case TyTy::TypeKind::BOOL:
return "b";
case TyTy::TypeKind::CHAR:
return "c";
case TyTy::TypeKind::STR:
return "e";
case TyTy::TypeKind::NEVER:
return "z";
// Placeholder types
case TyTy::TypeKind::ERROR: // Fallthrough
case TyTy::TypeKind::INFER: // Fallthrough
case TyTy::TypeKind::PLACEHOLDER: // Fallthrough
case TyTy::TypeKind::PARAM:
// FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc
return "p";
case TyTy::TypeKind::TUPLE:
return v0_tuple_prefix (ty);
case TyTy::TypeKind::UINT: // Fallthrough
case TyTy::TypeKind::INT: // Fallthrough
case TyTy::TypeKind::FLOAT: // Fallthrough
case TyTy::TypeKind::ISIZE: // Fallthrough
case TyTy::TypeKind::USIZE: // Fallthrough
return v0_numeric_prefix (ty);
default:
return "";
}
gcc_unreachable ();
}
// Add an underscore-terminated base62 integer to the mangling string.
// This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC:
// - 0 is encoded as "_"
// - any other value is encoded as itself minus one in base 62, followed by
// "_"
static void
v0_add_integer_62 (std::string &mangled, uint64_t x)
{
if (x > 0)
mangled.append (base62_integer (x - 1));
mangled.append ("_");
}
// Add a tag-prefixed base62 integer to the mangling string when the
// integer is greater than 0:
// - 0 is encoded as "" (nothing)
// - any other value is encoded as <tag> + v0_add_integer_62(itself), that is
// <tag> + base62(itself - 1) + '_'
static void
v0_add_opt_integer_62 (std::string &mangled, std::string tag, uint64_t x)
{
if (x > 0)
{
mangled.append (tag);
v0_add_integer_62 (mangled, x);
}
}
static void
v0_add_disambiguator (std::string &mangled, uint64_t dis)
{
v0_add_opt_integer_62 (mangled, "s", dis);
}
// Add an identifier to the mangled string. This corresponds to the
// `<identifier>` grammar in the v0 mangling RFC.
static void
v0_add_identifier (std::string &mangled, const std::string &identifier)
{
// FIXME: gccrs cannot handle unicode identifiers yet, so we never have to
// create mangling for unicode values for now. However, this is handled
// by the v0 mangling scheme. The grammar for unicode identifier is
// contained in <undisambiguated-identifier>, right under the <identifier>
// one. If the identifier contains unicode values, then an extra "u" needs
// to be added to the mangling string and `punycode` must be used to encode
// the characters.
mangled += std::to_string (identifier.size ());
// If the first character of the identifier is a digit or an underscore, we
// add an extra underscore
if (identifier[0] == '_')
mangled.append ("_");
mangled.append (identifier);
}
static std::string
v0_type_prefix (const TyTy::BaseType *ty)
{
auto ty_prefix = v0_simple_type_prefix (ty);
if (!ty_prefix.empty ())
return ty_prefix;
// FIXME: We need to fetch more type prefixes
gcc_unreachable ();
}
static std::string
legacy_mangle_item (const TyTy::BaseType *ty,
const Resolver::CanonicalPath &path)
{
const std::string hash = legacy_hash (ty->as_string ());
const std::string hash_sig = legacy_mangle_name (hash);
return kMangledSymbolPrefix + legacy_mangle_canonical_path (path) + hash_sig
+ kMangledSymbolDelim;
}
static std::string
v0_mangle_item (const TyTy::BaseType *ty, const Resolver::CanonicalPath &path)
{
// we can get this from the canonical_path
auto mappings = Analysis::Mappings::get ();
std::string crate_name;
bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name);
rust_assert (ok);
std::string mangled;
// FIXME: Add real algorithm once all pieces are implemented
auto ty_prefix = v0_type_prefix (ty);
v0_add_identifier (mangled, crate_name);
v0_add_disambiguator (mangled, 62);
gcc_unreachable ();
}
std::string
Mangler::mangle_item (const TyTy::BaseType *ty,
const Resolver::CanonicalPath &path) const
{
switch (version)
{
case Mangler::MangleVersion::LEGACY:
return legacy_mangle_item (ty, path);
case Mangler::MangleVersion::V0:
return v0_mangle_item (ty, path);
default:
gcc_unreachable ();
}
}
} // namespace Compile
} // namespace Rust

View File

@@ -0,0 +1,52 @@
// 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_MANGLE_H
#define RUST_MANGLE_H
#include "rust-system.h"
#include "rust-tyty.h"
namespace Rust {
namespace Compile {
class Mangler
{
public:
enum MangleVersion
{
// Values defined in rust/lang.opt
LEGACY = 0,
V0 = 1,
};
// this needs to support Legacy and V0 see github #429 or #305
std::string mangle_item (const TyTy::BaseType *ty,
const Resolver::CanonicalPath &path) const;
static void set_mangling (int frust_mangling_value)
{
version = static_cast<MangleVersion> (frust_mangling_value);
}
private:
static enum MangleVersion version;
};
} // namespace Compile
} // namespace Rust
#endif // RUST_MANGLE_H

View File

@@ -0,0 +1,958 @@
// 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
// <http://www.gnu.org/licenses/>.
#include "rust-tree.h"
#include "fold-const.h"
#include "stringpool.h"
#include "attribs.h"
#include "escaped_string.h"
namespace Rust {
void
mark_exp_read (tree exp)
{
if (exp == NULL)
return;
switch (TREE_CODE (exp))
{
case VAR_DECL:
gcc_fallthrough ();
case PARM_DECL:
DECL_READ_P (exp) = 1;
break;
case ARRAY_REF:
case COMPONENT_REF:
case MODIFY_EXPR:
case REALPART_EXPR:
case IMAGPART_EXPR:
CASE_CONVERT:
case ADDR_EXPR:
case INDIRECT_REF:
case FLOAT_EXPR:
case NON_DEPENDENT_EXPR:
case VIEW_CONVERT_EXPR:
mark_exp_read (TREE_OPERAND (exp, 0));
break;
case COMPOUND_EXPR:
mark_exp_read (TREE_OPERAND (exp, 1));
break;
case COND_EXPR:
if (TREE_OPERAND (exp, 1))
mark_exp_read (TREE_OPERAND (exp, 1));
if (TREE_OPERAND (exp, 2))
mark_exp_read (TREE_OPERAND (exp, 2));
break;
default:
break;
}
}
tree
convert_from_reference (tree val)
{
if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val)))
{
tree t = TREE_TYPE (TREE_TYPE (val));
tree ref = build1 (INDIRECT_REF, t, val);
mark_exp_read (val);
TREE_SIDE_EFFECTS (ref)
= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
val = ref;
}
return val;
}
tree
mark_use (tree expr, bool rvalue_p, bool read_p,
location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
if (expr == NULL_TREE || error_operand_p (expr))
return expr;
if (reject_builtin)
return error_mark_node;
if (read_p)
mark_exp_read (expr);
bool recurse_op[3] = {false, false, false};
switch (TREE_CODE (expr))
{
case COMPONENT_REF:
case NON_DEPENDENT_EXPR:
recurse_op[0] = true;
break;
case COMPOUND_EXPR:
recurse_op[1] = true;
break;
case COND_EXPR:
recurse_op[2] = true;
if (TREE_OPERAND (expr, 1))
recurse_op[1] = true;
break;
case INDIRECT_REF:
if (REFERENCE_REF_P (expr))
{
/* Try to look through the reference. */
tree ref = TREE_OPERAND (expr, 0);
tree r = mark_rvalue_use (ref, loc, reject_builtin);
if (r != ref)
expr = convert_from_reference (r);
}
break;
case VIEW_CONVERT_EXPR:
if (location_wrapper_p (expr))
{
loc = EXPR_LOCATION (expr);
tree op = TREE_OPERAND (expr, 0);
tree nop = RECUR (op);
if (nop == error_mark_node)
return error_mark_node;
else if (op == nop)
/* No change. */;
else if (DECL_P (nop) || CONSTANT_CLASS_P (nop))
{
/* Reuse the location wrapper. */
TREE_OPERAND (expr, 0) = nop;
/* If we're replacing a DECL with a constant, we also need to
change the TREE_CODE of the location wrapper. */
if (rvalue_p)
TREE_SET_CODE (expr, NON_LVALUE_EXPR);
}
else
{
/* Drop the location wrapper. */
expr = nop;
protected_set_expr_location (expr, loc);
}
return expr;
}
gcc_fallthrough ();
CASE_CONVERT:
recurse_op[0] = true;
break;
default:
break;
}
for (int i = 0; i < 3; ++i)
if (recurse_op[i])
{
tree op = TREE_OPERAND (expr, i);
op = RECUR (op);
if (op == error_mark_node)
return error_mark_node;
TREE_OPERAND (expr, i) = op;
}
return expr;
#undef RECUR
}
tree
mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */)
{
return mark_use (e, true, true, loc, reject_builtin);
}
tree
mark_lvalue_use (tree expr)
{
return mark_use (expr, false, true, input_location, false);
}
tree
mark_lvalue_use_nonread (tree expr)
{
return mark_use (expr, false, false, input_location, false);
}
tree
mark_discarded_use (tree expr)
{
if (expr == NULL_TREE)
return expr;
STRIP_ANY_LOCATION_WRAPPER (expr);
switch (TREE_CODE (expr))
{
case COND_EXPR:
TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2));
gcc_fallthrough ();
case COMPOUND_EXPR:
TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1));
return expr;
case COMPONENT_REF:
case ARRAY_REF:
case INDIRECT_REF:
case MEMBER_REF:
break;
default:
if (DECL_P (expr))
break;
else
return expr;
}
return mark_use (expr, true, true, input_location, false);
}
tree
convert_to_void (tree expr, impl_conv_void implicit)
{
location_t loc = expr_loc_or_input_loc (expr);
if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node)
return error_mark_node;
expr = mark_discarded_use (expr);
if (implicit == ICV_CAST)
/* An explicit cast to void avoids all -Wunused-but-set* warnings. */
mark_exp_read (expr);
if (!TREE_TYPE (expr))
return expr;
if (VOID_TYPE_P (TREE_TYPE (expr)))
return expr;
switch (TREE_CODE (expr))
{
case COND_EXPR: {
/* The two parts of a cond expr might be separate lvalues. */
tree op1 = TREE_OPERAND (expr, 1);
tree op2 = TREE_OPERAND (expr, 2);
bool side_effects
= ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2));
tree new_op1, new_op2;
new_op1 = NULL_TREE;
if (implicit != ICV_CAST && !side_effects)
{
if (op1)
new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND);
new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND);
}
else
{
if (op1)
new_op1 = convert_to_void (op1, ICV_CAST);
new_op2 = convert_to_void (op2, ICV_CAST);
}
expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2),
TREE_OPERAND (expr, 0), new_op1, new_op2);
break;
}
case COMPOUND_EXPR: {
/* The second part of a compound expr contains the value. */
tree op1 = TREE_OPERAND (expr, 1);
tree new_op1;
if (implicit != ICV_CAST
&& !warning_suppressed_p (expr /* What warning? */))
new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA);
else
new_op1 = convert_to_void (op1, ICV_CAST);
if (new_op1 != op1)
{
tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1),
TREE_OPERAND (expr, 0), new_op1);
expr = t;
}
break;
}
case NON_LVALUE_EXPR:
case NOP_EXPR:
/* These have already decayed to rvalue. */
break;
case CALL_EXPR:
maybe_warn_nodiscard (expr, implicit);
break;
case INDIRECT_REF: {
tree type = TREE_TYPE (expr);
int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
int is_volatile = TYPE_VOLATILE (type);
int is_complete = COMPLETE_TYPE_P (type);
/* Can't load the value if we don't know the type. */
if (is_volatile && !is_complete)
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of incomplete type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in second operand "
"of conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in third operand "
"of conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in right operand of "
"comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in left operand of "
"comma operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"indirection will not access object of "
"incomplete type %qT in for increment "
"expression",
type);
break;
default:
gcc_unreachable ();
}
}
/* Don't load the value if this is an implicit dereference, or if
the type needs to be handled by ctors/dtors. */
else if (is_volatile && is_reference)
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in second operand of "
"conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in third operand of "
"conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in right operand of "
"comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in left operand of comma "
"operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"implicit dereference will not access "
"object of type %qT in for increment expression",
type);
break;
default:
gcc_unreachable ();
}
}
else if (is_volatile && TREE_ADDRESSABLE (type))
{
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object of non-trivially-copyable type %qT",
type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in second "
"operand of conditional expression",
type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in third "
"operand of conditional expression",
type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in right "
"operand of comma operator",
type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in left "
"operand of comma operator",
type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in statement",
type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"indirection will not access object of "
"non-trivially-copyable type %qT in for "
"increment expression",
type);
break;
default:
gcc_unreachable ();
}
}
if (is_reference || !is_volatile || !is_complete
|| TREE_ADDRESSABLE (type))
{
/* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
operation is stripped off. Note that we don't warn about
- an expression with TREE_NO_WARNING set. (For an example of
such expressions, see build_over_call in call.cc.)
- automatic dereferencing of references, since the user cannot
control it. (See also warn_if_unused_value() in c-common.cc.)
*/
if (warn_unused_value && implicit != ICV_CAST
&& !warning_suppressed_p (expr, OPT_Wunused_value)
&& !is_reference)
warning_at (loc, OPT_Wunused_value, "value computed is not used");
expr = TREE_OPERAND (expr, 0);
if (TREE_CODE (expr) == CALL_EXPR)
maybe_warn_nodiscard (expr, implicit);
}
break;
}
case VAR_DECL: {
/* External variables might be incomplete. */
tree type = TREE_TYPE (expr);
int is_complete = COMPLETE_TYPE_P (type);
if (TYPE_VOLATILE (type) && !is_complete)
switch (implicit)
{
case ICV_CAST:
warning_at (loc, 0,
"conversion to void will not access "
"object %qE of incomplete type %qT",
expr, type);
break;
case ICV_SECOND_OF_COND:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in second operand of "
"conditional expression",
expr, type);
break;
case ICV_THIRD_OF_COND:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in third operand of "
"conditional expression",
expr, type);
break;
case ICV_RIGHT_OF_COMMA:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in right operand of comma operator",
expr, type);
break;
case ICV_LEFT_OF_COMMA:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in left operand of comma operator",
expr, type);
break;
case ICV_STATEMENT:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in statement",
expr, type);
break;
case ICV_THIRD_IN_FOR:
warning_at (loc, 0,
"variable %qE of incomplete type %qT will "
"not be accessed in for increment expression",
expr, type);
break;
default:
gcc_unreachable ();
}
break;
}
default:;
}
if (!TREE_SIDE_EFFECTS (expr))
expr = void_node;
return expr;
}
void
maybe_warn_nodiscard (tree expr, impl_conv_void implicit)
{
tree call = expr;
if (TREE_CODE (expr) == TARGET_EXPR)
call = TARGET_EXPR_INITIAL (expr);
location_t loc = expr_loc_or_input_loc (call);
tree callee = CALL_EXPR_FN (call);
if (!callee)
return;
tree type = TREE_TYPE (callee);
if (INDIRECT_TYPE_P (type))
type = TREE_TYPE (type);
tree rettype = TREE_TYPE (type);
tree fn = get_fndecl_from_callee (callee);
tree attr;
if (implicit != ICV_CAST && fn
&& (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn))))
{
escaped_string msg;
tree args = TREE_VALUE (attr);
if (args)
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
const char *format
= (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>")
: G_ ("ignoring return value of %qD, that must be used"));
const char *raw_msg = msg ? (const char *) msg : "";
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg))
inform (DECL_SOURCE_LOCATION (fn), "declared here");
}
else if (implicit != ICV_CAST
&& (attr
= lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype))))
{
escaped_string msg;
tree args = TREE_VALUE (attr);
if (args)
msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
const char *format
= (msg ? G_ (
"ignoring returned value of type %qT, that must be used: %<%s%>")
: G_ ("ignoring returned value of type %qT, that must be used"));
const char *raw_msg = msg ? (const char *) msg : "";
auto_diagnostic_group d;
if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg))
{
if (fn)
inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here",
fn);
inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
"%qT declared here", rettype);
}
}
}
location_t
expr_loc_or_loc (const_tree t, location_t or_loc)
{
location_t loc = EXPR_LOCATION (t);
if (loc == UNKNOWN_LOCATION)
loc = or_loc;
return loc;
}
location_t
expr_loc_or_input_loc (const_tree t)
{
return expr_loc_or_loc (t, input_location);
}
// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
// if we can.
tree
get_fndecl_from_callee (tree fn)
{
if (fn == NULL_TREE)
return fn;
if (TREE_CODE (fn) == FUNCTION_DECL)
return fn;
tree type = TREE_TYPE (fn);
if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
return NULL_TREE;
STRIP_NOPS (fn);
if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
fn = TREE_OPERAND (fn, 0);
if (TREE_CODE (fn) == FUNCTION_DECL)
return fn;
return NULL_TREE;
}
tree
pointer_offset_expression (tree base_tree, tree index_tree, location_t location)
{
tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree));
if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node
|| index_tree == error_mark_node || element_type_tree == error_mark_node)
return error_mark_node;
tree element_size = TYPE_SIZE_UNIT (element_type_tree);
index_tree = fold_convert_loc (location, sizetype, index_tree);
tree offset
= fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size);
return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree),
base_tree, offset);
}
// forked from gcc/cp/tree.cc cp_walk_subtrees
/* Apply FUNC to all language-specific sub-trees of TP in a pre-order
traversal. Called from walk_tree. */
tree
rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data,
hash_set<tree> *pset)
{
enum tree_code code = TREE_CODE (*tp);
tree result;
#define WALK_SUBTREE(NODE) \
do \
{ \
result = rs_walk_tree (&(NODE), func, data, pset); \
if (result) \
goto out; \
} \
while (0)
if (TYPE_P (*tp))
{
/* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of
the argument, so don't look through typedefs, but do walk into
template arguments for alias templates (and non-typedefed classes).
If *WALK_SUBTREES_P > 1, we're interested in type identity or
equivalence, so look through typedefs, ignoring template arguments for
alias templates, and walk into template args of classes.
See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2
when that's the behavior the walk_tree_fn wants. */
if (*walk_subtrees_p == 1 && typedef_variant_p (*tp))
{
*walk_subtrees_p = 0;
return NULL_TREE;
}
}
/* Not one of the easy cases. We must explicitly go through the
children. */
result = NULL_TREE;
switch (code)
{
case TREE_LIST:
WALK_SUBTREE (TREE_PURPOSE (*tp));
break;
case RECORD_TYPE:
if (TYPE_PTRMEMFUNC_P (*tp))
WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp));
break;
case CONSTRUCTOR:
if (COMPOUND_LITERAL_P (*tp))
WALK_SUBTREE (TREE_TYPE (*tp));
break;
case DECL_EXPR:
/* User variables should be mentioned in BIND_EXPR_VARS
and their initializers and sizes walked when walking
the containing BIND_EXPR. Compiler temporaries are
handled here. And also normal variables in templates,
since do_poplevel doesn't build a BIND_EXPR then. */
if (VAR_P (TREE_OPERAND (*tp, 0))
&& (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
&& !TREE_STATIC (TREE_OPERAND (*tp, 0))))
{
tree decl = TREE_OPERAND (*tp, 0);
WALK_SUBTREE (DECL_INITIAL (decl));
WALK_SUBTREE (DECL_SIZE (decl));
WALK_SUBTREE (DECL_SIZE_UNIT (decl));
}
break;
default:
return NULL_TREE;
}
/* We didn't find what we were looking for. */
out:
return result;
#undef WALK_SUBTREE
}
// forked from gcc/cp/tree.cc cp_expr_location
/* Like EXPR_LOCATION, but also handle some tcc_exceptional that have
locations. */
location_t
rs_expr_location (const_tree t_)
{
tree t = CONST_CAST_TREE (t_);
if (t == NULL_TREE)
return UNKNOWN_LOCATION;
return EXPR_LOCATION (t);
}
// forked from gcc/cp/class.cc is_really_empty_class
/* Returns true if TYPE contains no actual data, just various
possible combinations of empty classes. If IGNORE_VPTR is true,
a vptr doesn't prevent the class from being considered empty. Typically
we want to ignore the vptr on assignment, and not on initialization. */
bool
is_really_empty_class (tree type, bool ignore_vptr)
{
if (CLASS_TYPE_P (type))
{
tree field;
tree binfo;
tree base_binfo;
int i;
/* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
out, but we'd like to be able to check this before then. */
if (COMPLETE_TYPE_P (type) && is_empty_class (type))
return true;
if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type))
return false;
for (binfo = TYPE_BINFO (type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr))
return false;
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& !DECL_ARTIFICIAL (field)
/* An unnamed bit-field is not a data member. */
&& !DECL_UNNAMED_BIT_FIELD (field)
&& !is_really_empty_class (TREE_TYPE (field), ignore_vptr))
return false;
return true;
}
else if (TREE_CODE (type) == ARRAY_TYPE)
return (integer_zerop (array_type_nelts_top (type))
|| is_really_empty_class (TREE_TYPE (type), ignore_vptr));
return false;
}
// forked from gcc/cp/class.cc is_empty_class
/* Returns 1 if TYPE contains only padding bytes. */
int
is_empty_class (tree type)
{
if (type == error_mark_node)
return 0;
if (!CLASS_TYPE_P (type))
return 0;
return CLASSTYPE_EMPTY_P (type);
}
// forked from gcc/cp/tree.cc array_type_nelts_top
/* Return, as an INTEGER_CST node, the number of elements for TYPE
(which is an ARRAY_TYPE). This counts only elements of the top
array. */
tree
array_type_nelts_top (tree type)
{
return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
array_type_nelts (type), size_one_node);
}
// forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
/* Test whether DECL is a builtin that may appear in a
constant-expression. */
bool
builtin_valid_in_constant_expr_p (const_tree decl)
{
STRIP_ANY_LOCATION_WRAPPER (decl);
if (TREE_CODE (decl) != FUNCTION_DECL)
/* Not a function. */
return false;
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
{
if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
switch (DECL_FE_FUNCTION_CODE (decl))
{
case RS_BUILT_IN_IS_CONSTANT_EVALUATED:
case RS_BUILT_IN_SOURCE_LOCATION:
case RS_BUILT_IN_IS_CORRESPONDING_MEMBER:
case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
return true;
default:
break;
}
/* Not a built-in. */
return false;
}
switch (DECL_FUNCTION_CODE (decl))
{
/* These always have constant results like the corresponding
macros/symbol. */
case BUILT_IN_FILE:
case BUILT_IN_FUNCTION:
case BUILT_IN_LINE:
/* The following built-ins are valid in constant expressions
when their arguments are. */
case BUILT_IN_ADD_OVERFLOW_P:
case BUILT_IN_SUB_OVERFLOW_P:
case BUILT_IN_MUL_OVERFLOW_P:
/* These have constant results even if their operands are
non-constant. */
case BUILT_IN_CONSTANT_P:
case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
return true;
default:
return false;
}
}
// forked from gcc/cp/decl2.cc decl_maybe_constant_var_p
/* Returns true if DECL could be a symbolic constant variable, depending on
its initializer. */
bool
decl_maybe_constant_var_p (tree decl)
{
tree type = TREE_TYPE (decl);
if (!VAR_P (decl))
return false;
if (DECL_DECLARED_CONSTEXPR_P (decl))
return true;
if (DECL_HAS_VALUE_EXPR_P (decl))
/* A proxy isn't constant. */
return false;
if (TYPE_REF_P (type))
/* References can be constant. */;
else if (RS_TYPE_CONST_NON_VOLATILE_P (type)
&& INTEGRAL_OR_ENUMERATION_TYPE_P (type))
/* And const integers. */;
else
return false;
if (DECL_INITIAL (decl) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
/* We know the initializer, and it isn't constant. */
return false;
else
return true;
}
// forked from gcc/cp/typeck.cc cp_type_quals
/* Returns the type qualifiers for this type, including the qualifiers on the
elements for an array type. */
int
rs_type_quals (const_tree type)
{
int quals;
/* This CONST_CAST is okay because strip_array_types returns its
argument unmodified and we assign it to a const_tree. */
type = strip_array_types (CONST_CAST_TREE (type));
if (type == error_mark_node
/* Quals on a FUNCTION_TYPE are memfn quals. */
|| TREE_CODE (type) == FUNCTION_TYPE)
return TYPE_UNQUALIFIED;
quals = TYPE_QUALS (type);
/* METHOD and REFERENCE_TYPEs should never have quals. */
gcc_assert (
(TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
|| ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == TYPE_UNQUALIFIED));
return quals;
}
} // namespace Rust

View File

@@ -0,0 +1,508 @@
// 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
// <http://www.gnu.org/licenses/>.
#ifndef RUST_TREE
#define RUST_TREE
#include "rust-system.h"
#include "coretypes.h"
#include "tree.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))
/* True if NODE is an implicit INDIRECT_REF from convert_from_reference. */
#define REFERENCE_REF_P(NODE) \
(INDIRECT_REF_P (NODE) && TREE_TYPE (TREE_OPERAND (NODE, 0)) \
&& TYPE_REF_P (TREE_TYPE (TREE_OPERAND ((NODE), 0))))
// this is a helper to differentiate RECORD types between actual records and
// slices
#define SLICE_FLAG TREE_LANG_FLAG_0
#define SLICE_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_0 (TYPE))
/* Returns true if NODE is a pointer to member function type. */
#define TYPE_PTRMEMFUNC_P(NODE) \
(TREE_CODE (NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
#define TYPE_PTRMEMFUNC_FLAG(NODE) (TYPE_LANG_FLAG_2 (RECORD_TYPE_CHECK (NODE)))
#define TYPE_PTRMEMFUNC_FN_TYPE_RAW(NODE) (TREE_TYPE (TYPE_FIELDS (NODE)))
/* True if NODE is a compound-literal, i.e., a brace-enclosed
initializer cast to a particular type. This is mostly only set during
template parsing; once the initializer has been digested into an actual
value of the type, the expression is represented by a TARGET_EXPR. */
#define COMPOUND_LITERAL_P(NODE) \
(TREE_CODE (NODE) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (NODE))
/* When appearing in an INDIRECT_REF, it means that the tree structure
underneath is actually a call to a constructor. This is needed
when the constructor must initialize local storage (which can
be automatically destroyed), rather than allowing it to allocate
space from the heap.
When appearing in a SAVE_EXPR, it means that underneath
is a call to a constructor.
When appearing in a CONSTRUCTOR, the expression is an unconverted
compound literal.
When appearing in a FIELD_DECL, it means that this field
has been duly initialized in its constructor. */
#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE))
/* Nonzero if T is a class type. Zero for template type parameters,
typename types, and so forth. */
#define CLASS_TYPE_P(T) \
(RECORD_OR_UNION_CODE_P (TREE_CODE (T)) && TYPE_LANG_FLAG_5 (T))
/* [class.virtual]
A class that declares or inherits a virtual function is called a
polymorphic class. */
#define TYPE_POLYMORPHIC_P(NODE) (TREE_LANG_FLAG_2 (NODE))
/* Nonzero if this class has a virtual function table pointer. */
#define TYPE_CONTAINS_VPTR_P(NODE) \
(TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
/* A vector of BINFOs for the direct and indirect virtual base classes
that this type uses in a post-order depth-first left-to-right
order. (In other words, these bases appear in the order that they
should be initialized.) */
#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
/* A vector of BINFOs for the direct and indirect virtual base classes
that this type uses in a post-order depth-first left-to-right
order. (In other words, these bases appear in the order that they
should be initialized.) */
#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
/* We used to have a variant type for lang_type. Keep the name of the
checking accessor for the sole survivor. */
#define LANG_TYPE_CLASS_CHECK(NODE) (TYPE_LANG_SPECIFIC (NODE))
/* Keep these checks in ascending code order. */
#define RECORD_OR_UNION_CODE_P(T) ((T) == RECORD_TYPE || (T) == UNION_TYPE)
#define OVERLOAD_TYPE_P(T) (CLASS_TYPE_P (T) || TREE_CODE (T) == ENUMERAL_TYPE)
/* Nonzero if this class is "empty" in the sense of the C++ ABI. */
#define CLASSTYPE_EMPTY_P(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->empty_p)
/* True if DECL is declared 'constexpr'. */
#define DECL_DECLARED_CONSTEXPR_P(DECL) \
DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (DECL))
#define VAR_OR_FUNCTION_DECL_CHECK(NODE) \
TREE_CHECK2 (NODE, VAR_DECL, FUNCTION_DECL)
// Below macros are copied from gcc/c-family/c-common.h
/* In a FIELD_DECL, nonzero if the decl was originally a bitfield. */
#define DECL_C_BIT_FIELD(NODE) (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
#define SET_DECL_C_BIT_FIELD(NODE) \
(DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1)
#define CLEAR_DECL_C_BIT_FIELD(NODE) \
(DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 0)
/* True if the decl was an unnamed bitfield. */
#define DECL_UNNAMED_BIT_FIELD(NODE) \
(DECL_C_BIT_FIELD (NODE) && !DECL_NAME (NODE))
/* 1 iff NODE is function-local. */
#define DECL_FUNCTION_SCOPE_P(NODE) \
(DECL_CONTEXT (NODE) && TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
/* Nonzero if this type is const-qualified, but not
volatile-qualified. Other qualifiers are ignored. This macro is
used to test whether or not it is OK to bind an rvalue to a
reference. */
#define RS_TYPE_CONST_NON_VOLATILE_P(NODE) \
((rs_type_quals (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) \
== TYPE_QUAL_CONST)
/* [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)
/* Returns true if TYPE is an integral or enumeration name. Keep
these checks in ascending code order. */
#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE) \
(TREE_CODE (TYPE) == ENUMERAL_TYPE || RS_INTEGRAL_TYPE_P (TYPE))
/* Nonzero for a VAR_DECL that was initialized with a
constant-expression. */
#define DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P(NODE) \
(TREE_LANG_FLAG_2 (VAR_DECL_CHECK (NODE)))
// Above macros are copied from gcc/c-family/c-common.h
// forked from gcc/cp/cp-tree.h treee_pair_s
struct GTY (()) tree_pair_s
{
tree purpose;
tree value;
};
// forked from gcc/cp/cp-tree.h tree_pair_p
typedef tree_pair_s *tree_pair_p;
// forked from gcc/cp/cp-tree.h lang_type
/* This structure provides additional information above and beyond
what is provide in the ordinary tree_type. In the past, we used it
for the types of class types, template parameters types, typename
types, and so forth. However, there can be many (tens to hundreds
of thousands) of template parameter types in a compilation, and
there's no need for this additional information in that case.
Therefore, we now use this data structure only for class types.
In the past, it was thought that there would be relatively few
class types. However, in the presence of heavy use of templates,
many (i.e., thousands) of classes can easily be generated.
Therefore, we should endeavor to keep the size of this structure to
a minimum. */
struct GTY (()) lang_type
{
unsigned char align;
unsigned has_type_conversion : 1;
unsigned has_copy_ctor : 1;
unsigned has_default_ctor : 1;
unsigned const_needs_init : 1;
unsigned ref_needs_init : 1;
unsigned has_const_copy_assign : 1;
unsigned use_template : 2;
unsigned has_mutable : 1;
unsigned com_interface : 1;
unsigned non_pod_class : 1;
unsigned nearly_empty_p : 1;
unsigned user_align : 1;
unsigned has_copy_assign : 1;
unsigned has_new : 1;
unsigned has_array_new : 1;
unsigned gets_delete : 2;
unsigned interface_only : 1;
unsigned interface_unknown : 1;
unsigned contains_empty_class_p : 1;
unsigned anon_aggr : 1;
unsigned non_zero_init : 1;
unsigned empty_p : 1;
/* 32 bits allocated. */
unsigned vec_new_uses_cookie : 1;
unsigned declared_class : 1;
unsigned diamond_shaped : 1;
unsigned repeated_base : 1;
unsigned being_defined : 1;
unsigned debug_requested : 1;
unsigned fields_readonly : 1;
unsigned ptrmemfunc_flag : 1;
unsigned lazy_default_ctor : 1;
unsigned lazy_copy_ctor : 1;
unsigned lazy_copy_assign : 1;
unsigned lazy_destructor : 1;
unsigned has_const_copy_ctor : 1;
unsigned has_complex_copy_ctor : 1;
unsigned has_complex_copy_assign : 1;
unsigned non_aggregate : 1;
unsigned has_complex_dflt : 1;
unsigned has_list_ctor : 1;
unsigned non_std_layout : 1;
unsigned is_literal : 1;
unsigned lazy_move_ctor : 1;
unsigned lazy_move_assign : 1;
unsigned has_complex_move_ctor : 1;
unsigned has_complex_move_assign : 1;
unsigned has_constexpr_ctor : 1;
unsigned unique_obj_representations : 1;
unsigned unique_obj_representations_set : 1;
bool erroneous : 1;
bool non_pod_aggregate : 1;
/* When adding a flag here, consider whether or not it ought to
apply to a template instance if it applies to the template. If
so, make sure to copy it in instantiate_class_template! */
/* There are some bits left to fill out a 32-bit word. Keep track
of this by updating the size of this bitfield whenever you add or
remove a flag. */
unsigned dummy : 3;
tree primary_base;
vec<tree_pair_s, va_gc> *vcall_indices;
tree vtables;
tree typeinfo_var;
vec<tree, va_gc> *vbases;
tree as_base;
vec<tree, va_gc> *pure_virtuals;
tree friend_classes;
vec<tree, va_gc> *GTY ((reorder ("resort_type_member_vec"))) members;
tree key_method;
tree decl_list;
tree befriending_classes;
/* In a RECORD_TYPE, information specific to Objective-C++, such
as a list of adopted protocols or a pointer to a corresponding
@interface. See objc/objc-act.h for details. */
tree objc_info;
/* FIXME reuse another field? */
tree lambda_expr;
};
namespace Rust {
// forked from gcc/cp/cp-tree.h tsubst_flags_t
/* This type is used for parameters and variables which hold
combinations of the flags in enum tsubst_flags. */
typedef int tsubst_flags_t;
// forked from gcc/cp/cvt.cc convert_to_void
//
// When an expression is used in a void context, its value is discarded and
// no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
// stmt.expr/1, expr.comma/1]. This permits dereferencing an incomplete type
// in a void context. The C++ standard does not define what an `access' to an
// object is, but there is reason to believe that it is the lvalue to rvalue
// conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
// accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
// indicates that volatile semantics should be the same between C and C++
// where ever possible. C leaves it implementation defined as to what
// constitutes an access to a volatile. So, we interpret `*vp' as a read of
// the volatile object `vp' points to, unless that is an incomplete type. For
// volatile references we do not do this interpretation, because that would
// make it impossible to ignore the reference return value from functions. We
// issue warnings in the confusing cases.
//
// The IMPLICIT is ICV_CAST when the user is explicitly converting an
// expression to void via a cast. If an expression is being implicitly
// converted, IMPLICIT indicates the context of the implicit conversion.
/* Possible cases of implicit or explicit bad conversions to void. */
enum impl_conv_void
{
ICV_CAST, /* (explicit) conversion to void */
ICV_SECOND_OF_COND, /* second operand of conditional expression */
ICV_THIRD_OF_COND, /* third operand of conditional expression */
ICV_RIGHT_OF_COMMA, /* right operand of comma operator */
ICV_LEFT_OF_COMMA, /* left operand of comma operator */
ICV_STATEMENT, /* statement */
ICV_THIRD_IN_FOR /* for increment expression */
};
/* BUILT_IN_FRONTEND function codes. */
enum rs_built_in_function
{
RS_BUILT_IN_IS_CONSTANT_EVALUATED,
RS_BUILT_IN_INTEGER_PACK,
RS_BUILT_IN_IS_CORRESPONDING_MEMBER,
RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
RS_BUILT_IN_SOURCE_LOCATION,
RS_BUILT_IN_LAST
};
extern tree
convert_to_void (tree expr, impl_conv_void implicit);
// The lvalue-to-rvalue conversion (7.1) is applied if and only if the
// expression is a glvalue of volatile-qualified type and it is one of the
// following:
// * ( expression ), where expression is one of these expressions,
// * id-expression (8.1.4),
// * subscripting (8.2.1),
// * class member access (8.2.5),
// * indirection (8.3.1),
// * pointer-to-member operation (8.5),
// * conditional expression (8.16) where both the second and the third
// operands are one of these expressions, or
// * comma expression (8.19) where the right operand is one of these
// expressions.
extern tree
mark_discarded_use (tree expr);
// Mark EXP as read, not just set, for set but not used -Wunused warning
// purposes.
extern void
mark_exp_read (tree exp);
// We've seen an actual use of EXPR. Possibly replace an outer variable
// reference inside with its constant value or a lambda capture.
extern tree
mark_use (tree expr, bool rvalue_p, bool read_p, location_t loc,
bool reject_builtin);
// Called whenever the expression EXPR is used in an rvalue context.
// When REJECT_BUILTIN is true the expression is checked to make sure
// it doesn't make it possible to obtain the address of a GCC built-in
// function with no library fallback (or any of its bits, such as in
// a conversion to bool).
extern tree
mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
bool reject_builtin /* = true */);
// Called whenever an expression is used in an lvalue context.
extern tree
mark_lvalue_use (tree expr);
// As above, but don't consider this use a read.
extern tree
mark_lvalue_use_nonread (tree expr);
// We are using a reference VAL for its value. Bash that reference all the way
// down to its lowest form.
extern tree
convert_from_reference (tree val);
// Subroutine of convert_to_void. Warn if we're discarding something with
// attribute [[nodiscard]].
extern void
maybe_warn_nodiscard (tree expr, impl_conv_void implicit);
extern location_t
expr_loc_or_loc (const_tree t, location_t or_loc);
extern location_t
expr_loc_or_input_loc (const_tree t);
// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
// if we can.
extern tree
get_fndecl_from_callee (tree fn);
// FIXME some helpers from HIRCompileBase could probably be moved here over time
// Return an expression for the address of BASE[INDEX], used in offset intrinsic
extern tree
pointer_offset_expression (tree base_tree, tree index_tree, location_t locus);
/* A tree node, together with a location, so that we can track locations
(and ranges) during parsing.
The location is redundant for node kinds that have locations,
but not all node kinds do (e.g. constants, and references to
params, locals, etc), so we stash a copy here. */
extern location_t rs_expr_location (const_tree);
extern int
is_empty_class (tree type);
extern tree array_type_nelts_top (tree);
extern bool
is_really_empty_class (tree, bool);
extern bool builtin_valid_in_constant_expr_p (const_tree);
extern bool maybe_constexpr_fn (tree);
extern bool var_in_maybe_constexpr_fn (tree);
extern int
rs_type_quals (const_tree type);
extern bool decl_maybe_constant_var_p (tree);
extern tree
rs_walk_subtrees (tree *, int *, walk_tree_fn, void *, hash_set<tree> *);
#define rs_walk_tree(tp, func, data, pset) \
walk_tree_1 (tp, func, data, pset, rs_walk_subtrees)
#define rs_walk_tree_without_duplicates(tp, func, data) \
walk_tree_without_duplicates_1 (tp, func, data, rs_walk_subtrees)
// forked from gcc/cp/cp-tree.h cp_expr_loc_or_loc
inline location_t
rs_expr_loc_or_loc (const_tree t, location_t or_loc)
{
location_t loc = rs_expr_location (t);
if (loc == UNKNOWN_LOCATION)
loc = or_loc;
return loc;
}
// forked from gcc/cp/cp-tree.h cp_expr_loc_or_input_loc
inline location_t
rs_expr_loc_or_input_loc (const_tree t)
{
return rs_expr_loc_or_loc (t, input_location);
}
} // namespace Rust
#endif // RUST_TREE

506
gcc/rust/rust-backend.h Normal file
View File

@@ -0,0 +1,506 @@
// 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
// <http://www.gnu.org/licenses/>.
#ifndef RUST_BACKEND_H
#define RUST_BACKEND_H
#include <gmp.h>
#include <mpfr.h>
#include <mpc.h>
#include "rust-location.h"
#include "rust-linemap.h"
#include "rust-diagnostics.h"
#include "operator.h"
#include "tree.h"
// Pointers to these types are created by the backend, passed to the
// frontend, and passed back to the backend. The types must be
// defined by the backend using these names.
// The backend representation of a variable.
class Bvariable;
// The backend interface. This is a pure abstract class that a
// specific backend will implement.
class Backend
{
public:
virtual ~Backend () {}
// Name/type/location. Used for function parameters, struct fields,
// interface methods.
struct typed_identifier
{
std::string name;
tree type;
Location location;
typed_identifier ()
: name (), type (NULL_TREE), location (Linemap::unknown_location ())
{}
typed_identifier (const std::string &a_name, tree a_type,
Location a_location)
: name (a_name), type (a_type), location (a_location)
{}
};
// debug
virtual void debug (tree) = 0;
virtual void debug (Bvariable *) = 0;
virtual tree get_identifier_node (const std::string &str) = 0;
// Types.
// get unit-type
virtual tree unit_type () = 0;
// Get the unnamed boolean type.
virtual tree bool_type () = 0;
// Get the char type
virtual tree char_type () = 0;
// Get the wchar type
virtual tree wchar_type () = 0;
// Get the Host pointer size in bits
virtual int get_pointer_size () = 0;
// Get the raw str type const char*
virtual tree raw_str_type () = 0;
// Get an unnamed integer type with the given signedness and number
// of bits.
virtual tree integer_type (bool is_unsigned, int bits) = 0;
// Get an unnamed floating point type with the given number of bits
// (32 or 64).
virtual tree float_type (int bits) = 0;
// Get an unnamed complex type with the given number of bits (64 or 128).
virtual tree complex_type (int bits) = 0;
// Get a pointer type.
virtual tree pointer_type (tree to_type) = 0;
// Get a reference type.
virtual tree reference_type (tree to_type) = 0;
// make type immutable
virtual tree immutable_type (tree base) = 0;
// Get a function type. The receiver, parameter, and results are
// generated from the types in the Function_type. The Function_type
// is provided so that the names are available. This should return
// not the type of a Go function (which is a pointer to a struct)
// but the type of a C function pointer (which will be used as the
// type of the first field of the struct). If there is more than
// one result, RESULT_STRUCT is a struct type to hold the results,
// and RESULTS may be ignored; if there are zero or one results,
// RESULT_STRUCT is NULL.
virtual tree function_type (const typed_identifier &receiver,
const std::vector<typed_identifier> &parameters,
const std::vector<typed_identifier> &results,
tree result_struct, Location location)
= 0;
virtual tree
function_type_varadic (const typed_identifier &receiver,
const std::vector<typed_identifier> &parameters,
const std::vector<typed_identifier> &results,
tree result_struct, Location location)
= 0;
virtual tree function_ptr_type (tree result,
const std::vector<tree> &praameters,
Location location)
= 0;
// Get a struct type.
virtual tree struct_type (const std::vector<typed_identifier> &fields) = 0;
// Get a union type.
virtual tree union_type (const std::vector<typed_identifier> &fields) = 0;
// Get an array type.
virtual tree array_type (tree element_type, tree length) = 0;
// Return a named version of a type. The location is the location
// of the type definition. This will not be called for a type
// created via placeholder_pointer_type, placeholder_struct_type, or
// placeholder_array_type.. (It may be called for a pointer,
// struct, or array type in a case like "type P *byte; type Q P".)
virtual tree named_type (const std::string &name, tree, Location) = 0;
// Return the size of a type.
virtual int64_t type_size (tree) = 0;
// Return the alignment of a type.
virtual int64_t type_alignment (tree) = 0;
// Return the alignment of a struct field of this type. This is
// normally the same as type_alignment, but not always.
virtual int64_t type_field_alignment (tree) = 0;
// Return the offset of field INDEX in a struct type. INDEX is the
// entry in the FIELDS std::vector parameter of struct_type or
// set_placeholder_struct_type.
virtual int64_t type_field_offset (tree, size_t index) = 0;
// Expressions.
// Return an expression for a zero value of the given type. This is
// used for cases such as local variable initialization and
// converting nil to other types.
virtual tree zero_expression (tree) = 0;
virtual tree unit_expression () = 0;
// Create a reference to a variable.
virtual tree var_expression (Bvariable *var, Location) = 0;
// Return an expression for the multi-precision integer VAL in BTYPE.
virtual tree integer_constant_expression (tree btype, mpz_t val) = 0;
// Return an expression for the floating point value VAL in BTYPE.
virtual tree float_constant_expression (tree btype, mpfr_t val) = 0;
// Return an expression for the complex value VAL in BTYPE.
virtual tree complex_constant_expression (tree btype, mpc_t val) = 0;
// Return an expression for the string value VAL.
virtual tree string_constant_expression (const std::string &val) = 0;
// Get a char literal
virtual tree char_constant_expression (char c) = 0;
// Get a char literal
virtual tree wchar_constant_expression (wchar_t c) = 0;
// Return an expression for the boolean value VAL.
virtual tree boolean_constant_expression (bool val) = 0;
// Return an expression for the real part of BCOMPLEX.
virtual tree real_part_expression (tree bcomplex, Location) = 0;
// Return an expression for the imaginary part of BCOMPLEX.
virtual tree imag_part_expression (tree bcomplex, Location) = 0;
// Return an expression for the complex number (BREAL, BIMAG).
virtual tree complex_expression (tree breal, tree bimag, Location) = 0;
// Return an expression that converts EXPR to TYPE.
virtual tree convert_expression (tree type, tree expr, Location) = 0;
// Return an expression for the field at INDEX in BSTRUCT.
virtual tree struct_field_expression (tree bstruct, size_t index, Location)
= 0;
// Create an expression that executes BSTAT before BEXPR.
virtual tree compound_expression (tree bstat, tree bexpr, Location) = 0;
// Return an expression that executes THEN_EXPR if CONDITION is true, or
// ELSE_EXPR otherwise and returns the result as type BTYPE, within the
// specified function FUNCTION. ELSE_EXPR may be NULL. BTYPE may be NULL.
virtual tree conditional_expression (tree function, tree btype,
tree condition, tree then_expr,
tree else_expr, Location)
= 0;
// Return an expression for the negation operation OP EXPR.
// Supported values of OP are enumerated in NegationOperator.
virtual tree negation_expression (NegationOperator op, tree expr, Location)
= 0;
// Return an expression for the operation LEFT OP RIGHT.
// Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
virtual tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
tree left, tree right,
Location)
= 0;
// Return an expression for the operation LEFT OP RIGHT.
// Supported values of OP are enumerated in ComparisonOperator.
virtual tree comparison_expression (ComparisonOperator op, tree left,
tree right, Location)
= 0;
// Return an expression for the operation LEFT OP RIGHT.
// Supported values of OP are enumerated in LazyBooleanOperator.
virtual tree lazy_boolean_expression (LazyBooleanOperator op, tree left,
tree right, Location)
= 0;
// Return an expression that constructs BTYPE with VALS. BTYPE must be the
// backend representation a of struct. VALS must be in the same order as the
// corresponding fields in BTYPE.
virtual tree constructor_expression (tree btype, bool is_variant,
const std::vector<tree> &vals, int,
Location)
= 0;
// Return an expression that constructs an array of BTYPE with INDEXES and
// VALS. INDEXES and VALS must have the same amount of elements. Each index
// in INDEXES must be in the same order as the corresponding value in VALS.
virtual tree
array_constructor_expression (tree btype,
const std::vector<unsigned long> &indexes,
const std::vector<tree> &vals, Location)
= 0;
virtual tree array_initializer (tree, tree, tree, tree, tree, tree *,
Location)
= 0;
// Return an expression for ARRAY[INDEX] as an l-value. ARRAY is a valid
// fixed-length array, not a slice.
virtual tree array_index_expression (tree array, tree index, Location) = 0;
// Create an expression for a call to FN with ARGS, taking place within
// caller CALLER.
virtual tree call_expression (tree fn, const std::vector<tree> &args,
tree static_chain, Location)
= 0;
// Statements.
// Create a variable initialization statement in the specified
// function. This initializes a local variable at the point in the
// program flow where it is declared.
virtual tree init_statement (tree, Bvariable *var, tree init) = 0;
// Create an assignment statement within the specified function.
virtual tree assignment_statement (tree lhs, tree rhs, Location) = 0;
// Create a return statement, passing the representation of the
// function and the list of values to return.
virtual tree return_statement (tree, const std::vector<tree> &, Location) = 0;
// Create an if statement within a function. ELSE_BLOCK may be NULL.
virtual tree if_statement (tree, tree condition, tree then_block,
tree else_block, Location)
= 0;
// infinite loop expressions
virtual tree loop_expression (tree body, Location) = 0;
// exit expressions
virtual tree exit_expression (tree condition, Location) = 0;
// Create a single statement from two statements.
virtual tree compound_statement (tree, tree) = 0;
// Create a single statement from a list of statements.
virtual tree statement_list (const std::vector<tree> &) = 0;
// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if
// an exception occurs. EXCEPT_STMT may be NULL. FINALLY_STMT may be NULL and
// if not NULL, it will always be executed. This is used for handling defers
// in Go functions. In C++, the resulting code is of this form:
// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
virtual tree exception_handler_statement (tree bstat, tree except_stmt,
tree finally_stmt, Location)
= 0;
// Blocks.
// Create a block. The frontend will call this function when it
// starts converting a block within a function. FUNCTION is the
// current function. ENCLOSING is the enclosing block; it will be
// NULL for the top-level block in a function. VARS is the list of
// local variables defined within this block; each entry will be
// created by the local_variable function. START_LOCATION is the
// location of the start of the block, more or less the location of
// the initial curly brace. END_LOCATION is the location of the end
// of the block, more or less the location of the final curly brace.
// The statements will be added after the block is created.
virtual tree block (tree function, tree enclosing,
const std::vector<Bvariable *> &vars,
Location start_location, Location end_location)
= 0;
// Add the statements to a block. The block is created first. Then
// the statements are created. Then the statements are added to the
// block. This will called exactly once per block. The vector may
// be empty if there are no statements.
virtual void block_add_statements (tree, const std::vector<tree> &) = 0;
// Variables.
// Create an error variable. This is used for cases which should
// not occur in a correct program, in order to keep the compilation
// going without crashing.
virtual Bvariable *error_variable () = 0;
// Create a global variable. NAME is the package-qualified name of
// the variable. ASM_NAME is the encoded identifier for the
// variable, incorporating the package, and made safe for the
// assembler. BTYPE is the type of the variable. IS_EXTERNAL is
// true if the variable is defined in some other package. IS_HIDDEN
// is true if the variable is not exported (name begins with a lower
// case letter). IN_UNIQUE_SECTION is true if the variable should
// be put into a unique section if possible; this is intended to
// permit the linker to garbage collect the variable if it is not
// referenced. LOCATION is where the variable was defined.
virtual Bvariable *global_variable (const std::string &name,
const std::string &asm_name, tree btype,
bool is_external, bool is_hidden,
bool in_unique_section, Location location)
= 0;
// A global variable will 1) be initialized to zero, or 2) be
// initialized to a constant value, or 3) be initialized in the init
// function. In case 2, the frontend will call
// global_variable_set_init to set the initial value. If this is
// not called, the backend should initialize a global variable to 0.
// The init function may then assign a value to it.
virtual void global_variable_set_init (Bvariable *, tree) = 0;
// Create a local variable. The frontend will create the local
// variables first, and then create the block which contains them.
// FUNCTION is the function in which the variable is defined. NAME
// is the name of the variable. TYPE is the type. DECL_VAR, if not
// null, gives the location at which the value of this variable may
// be found, typically used to create an inner-scope reference to an
// outer-scope variable, to extend the lifetime of the variable beyond
// the inner scope. IS_ADDRESS_TAKEN is true if the address of this
// variable is taken (this implies that the address does not escape
// the function, as otherwise the variable would be on the heap).
// LOCATION is where the variable is defined. For each local variable
// the frontend will call init_statement to set the initial value.
virtual Bvariable *local_variable (tree function, const std::string &name,
tree type, Bvariable *decl_var,
Location location)
= 0;
// Create a function parameter. This is an incoming parameter, not
// a result parameter (result parameters are treated as local
// variables). The arguments are as for local_variable.
virtual Bvariable *parameter_variable (tree function, const std::string &name,
tree type, Location location)
= 0;
// Create a static chain parameter. This is the closure parameter.
virtual Bvariable *static_chain_variable (tree function,
const std::string &name, tree type,
Location location)
= 0;
// Create a temporary variable. A temporary variable has no name,
// just a type. We pass in FUNCTION and BLOCK in case they are
// needed. If INIT is not NULL, the variable should be initialized
// to that value. Otherwise the initial value is irrelevant--the
// backend does not have to explicitly initialize it to zero.
// ADDRESS_IS_TAKEN is true if the programs needs to take the
// address of this temporary variable. LOCATION is the location of
// the statement or expression which requires creating the temporary
// variable, and may not be very useful. This function should
// return a variable which can be referenced later and should set
// *PSTATEMENT to a statement which initializes the variable.
virtual Bvariable *temporary_variable (tree, tree, tree, tree init,
bool address_is_taken,
Location location, tree *pstatement)
= 0;
// Labels.
// Create a new label. NAME will be empty if this is a label
// created by the frontend for a loop construct. The location is
// where the label is defined.
virtual tree label (tree, const std::string &name, Location) = 0;
// Create a statement which defines a label. This statement will be
// put into the codestream at the point where the label should be
// defined.
virtual tree label_definition_statement (tree) = 0;
// Create a goto statement to a label.
virtual tree goto_statement (tree, Location) = 0;
// Create an expression for the address of a label. This is used to
// get the return address of a deferred function which may call
// recover.
virtual tree label_address (tree, Location) = 0;
// Functions.
// Bit flags to pass to the function method.
// Set if this is a function declaration rather than a definition;
// the definition will be in another compilation unit.
static const unsigned int function_is_declaration = 1 << 0;
// Set if the function should never be inlined because they call
// recover and must be visible for correct panic recovery.
static const unsigned int function_is_uninlinable = 1 << 1;
// Set if the function does not return. This is set for the
// implementation of panic.
static const unsigned int function_does_not_return = 1 << 2;
// Set if the function should be put in a unique section if
// possible. This is used for field tracking.
static const unsigned int function_in_unique_section = 1 << 3;
// Declare or define a function of FNTYPE.
// NAME is the Go name of the function. ASM_NAME, if not the empty
// string, is the name that should be used in the symbol table; this
// will be non-empty if a magic extern comment is used. FLAGS is
// bit flags described above.
virtual tree function (tree fntype, const std::string &name,
const std::string &asm_name, unsigned int flags,
Location)
= 0;
// Create a statement that runs all deferred calls for FUNCTION. This should
// be a statement that looks like this in C++:
// finish:
// try { DEFER_RETURN; } catch { CHECK_DEFER; goto finish; }
virtual tree function_defer_statement (tree function, tree undefer,
tree check_defer, Location)
= 0;
// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
// This will only be called for a function definition. Returns true on
// success, false on failure.
virtual bool
function_set_parameters (tree function,
const std::vector<Bvariable *> &param_vars)
= 0;
// Utility.
// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
// FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
virtual void
write_global_definitions (const std::vector<tree> &type_decls,
const std::vector<tree> &constant_decls,
const std::vector<tree> &function_decls,
const std::vector<Bvariable *> &variable_decls)
= 0;
// Write SIZE bytes of export data from BYTES to the proper
// section in the output object file.
virtual void write_export_data (const char *bytes, unsigned int size) = 0;
};
#endif // RUST_BACKEND_H

2718
gcc/rust/rust-gcc.cc Normal file

File diff suppressed because it is too large Load Diff