diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-03-04 11:58:45 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-04 11:58:45 +0000 |
commit | d89c8ccf3237e66029125c0fe007297bb86eca74 (patch) | |
tree | 24926ddec8217cdba774a50eaa06383c8a72d13e /gcc/rust/backend | |
parent | b4bd389c66a3e3bf0489626a1a70c2500d415ef8 (diff) | |
parent | d6e1771291c792f665f5b9ed7d065bf292051e6a (diff) | |
download | gcc-d89c8ccf3237e66029125c0fe007297bb86eca74.zip gcc-d89c8ccf3237e66029125c0fe007297bb86eca74.tar.gz gcc-d89c8ccf3237e66029125c0fe007297bb86eca74.tar.bz2 |
Merge #990
990: Add must use attribute support r=philberty a=philberty
This is a port of the CPP front-end nodiscard attribute to be used for
must_use. It contains a patch to clean up how we handle expressions vs
statements and removes more of the GCC abstraction. Its my hope that we
can leverage more and more existing code to get the static analysis where
we want it.
Fixes #856
Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Diffstat (limited to 'gcc/rust/backend')
-rw-r--r-- | gcc/rust/backend/rust-compile-base.cc | 43 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-base.h | 3 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-context.h | 2 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-expr.cc | 15 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-expr.h | 30 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-stmt.h | 5 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile.cc | 14 | ||||
-rw-r--r-- | gcc/rust/backend/rust-constexpr.cc | 47 | ||||
-rw-r--r-- | gcc/rust/backend/rust-tree.cc | 659 | ||||
-rw-r--r-- | gcc/rust/backend/rust-tree.h | 182 |
10 files changed, 904 insertions, 96 deletions
diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc index 33e4c26..a705da7 100644 --- a/gcc/rust/backend/rust-compile-base.cc +++ b/gcc/rust/backend/rust-compile-base.cc @@ -22,6 +22,8 @@ #include "rust-compile-fnparam.h" #include "rust-compile-var-decl.h" +#include "rust-expr.h" // for AST::AttrInputLiteral + #include "fold-const.h" #include "stringpool.h" @@ -52,10 +54,16 @@ HIRCompileBase::setup_attributes_on_fndecl ( 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; if (is_inline) { handle_inline_attribute_on_fndecl (fndecl, attr); } + else if (is_must_use) + { + handle_must_use_attribute_on_fndecl (fndecl, attr); + } } } @@ -108,6 +116,30 @@ HIRCompileBase::handle_inline_attribute_on_fndecl (tree fndecl, } 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) { switch (abi) @@ -262,9 +294,8 @@ HIRCompileBase::compile_function_body (Context *ctx, tree fndecl, auto compiled_expr = CompileStmt::Compile (s.get (), ctx); if (compiled_expr != nullptr) { - tree compiled_stmt - = ctx->get_backend ()->expression_statement (fndecl, compiled_expr); - ctx->add_statement (compiled_stmt); + tree s = convert_to_void (compiled_expr, ICV_STATEMENT); + ctx->add_statement (s); } } @@ -289,10 +320,8 @@ HIRCompileBase::compile_function_body (Context *ctx, tree fndecl, } else { - tree final_stmt - = ctx->get_backend ()->expression_statement (fndecl, - compiled_expr); - ctx->add_statement (final_stmt); + // FIXME can this actually happen? + ctx->add_statement (compiled_expr); } } } diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h index b434168..c30aa4d 100644 --- a/gcc/rust/backend/rust-compile-base.h +++ b/gcc/rust/backend/rust-compile-base.h @@ -79,6 +79,9 @@ protected: static void handle_inline_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 setup_abi_options (tree fndecl, ABI abi); static tree address_expression (tree, Location); diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 46f88b3..3fefd8d 100644 --- a/gcc/rust/backend/rust-compile-context.h +++ b/gcc/rust/backend/rust-compile-context.h @@ -25,9 +25,9 @@ #include "rust-hir-type-check.h" #include "rust-backend.h" #include "rust-compile-tyty.h" -#include "rust-ast-full.h" #include "rust-hir-full.h" #include "rust-mangle.h" +#include "rust-tree.h" namespace Rust { namespace Compile { diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index bf47661..6d50c3f 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -63,8 +63,6 @@ CompileExpr::visit (HIR::ArithmeticOrLogicalExpr &expr) void CompileExpr::visit (HIR::CompoundAssignmentExpr &expr) { - fncontext fn = ctx->peek_fn (); - auto op = expr.get_expr_type (); auto lhs = CompileExpr::Compile (expr.get_left_expr ().get (), ctx); auto rhs = CompileExpr::Compile (expr.get_right_expr ().get (), ctx); @@ -82,10 +80,7 @@ CompileExpr::visit (HIR::CompoundAssignmentExpr &expr) = resolve_operator_overload (lang_item_type, expr, lhs, rhs, expr.get_left_expr ().get (), expr.get_right_expr ().get ()); - auto assignment - = ctx->get_backend ()->expression_statement (fn.fndecl, - compound_assignment); - ctx->add_statement (assignment); + ctx->add_statement (compound_assignment); return; } @@ -94,7 +89,7 @@ CompileExpr::visit (HIR::CompoundAssignmentExpr &expr) = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs, expr.get_locus ()); tree assignment - = ctx->get_backend ()->assignment_statement (fn.fndecl, lhs, operator_expr, + = ctx->get_backend ()->assignment_statement (lhs, operator_expr, expr.get_locus ()); ctx->add_statement (assignment); } @@ -304,8 +299,10 @@ CompileExpr::visit (HIR::MatchExpr &expr) { tree result_reference = ctx->get_backend ()->var_expression (tmp, arm_locus); - tree assignment = ctx->get_backend ()->assignment_statement ( - fnctx.fndecl, result_reference, kase_expr_tree, arm_locus); + tree assignment + = ctx->get_backend ()->assignment_statement (result_reference, + kase_expr_tree, + arm_locus); ctx->add_statement (assignment); } diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h index 3cc51d4..8aeb703 100644 --- a/gcc/rust/backend/rust-compile-expr.h +++ b/gcc/rust/backend/rust-compile-expr.h @@ -169,7 +169,6 @@ public: void visit (HIR::AssignmentExpr &expr) override { - fncontext fn = ctx->peek_fn (); auto lvalue = CompileExpr::Compile (expr.get_lhs (), ctx); auto rvalue = CompileExpr::Compile (expr.get_rhs (), ctx); @@ -191,7 +190,7 @@ public: expr.get_rhs ()->get_locus ()); tree assignment - = ctx->get_backend ()->assignment_statement (fn.fndecl, lvalue, rvalue, + = ctx->get_backend ()->assignment_statement (lvalue, rvalue, expr.get_locus ()); ctx->add_statement (assignment); @@ -594,9 +593,7 @@ public: = CompileBlock::compile (expr.get_loop_block ().get (), ctx, nullptr); tree loop_expr = ctx->get_backend ()->loop_expression (code_block, expr.get_locus ()); - tree loop_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, loop_expr); - ctx->add_statement (loop_stmt); + ctx->add_statement (loop_expr); if (tmp != NULL) { @@ -645,9 +642,7 @@ public: = CompileExpr::Compile (expr.get_predicate_expr ().get (), ctx); tree exit_expr = ctx->get_backend ()->exit_expression (condition, expr.get_locus ()); - tree break_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, exit_expr); - ctx->add_statement (break_stmt); + ctx->add_statement (exit_expr); tree code_block_stmt = CompileBlock::compile (expr.get_loop_block ().get (), ctx, nullptr); @@ -659,14 +654,11 @@ public: tree loop_expr = ctx->get_backend ()->loop_expression (loop_block, expr.get_locus ()); - tree loop_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, loop_expr); - ctx->add_statement (loop_stmt); + ctx->add_statement (loop_expr); } void visit (HIR::BreakExpr &expr) override { - fncontext fnctx = ctx->peek_fn (); if (expr.has_break_expr ()) { tree compiled_expr @@ -676,8 +668,10 @@ public: tree result_reference = ctx->get_backend ()->var_expression ( loop_result_holder, expr.get_expr ()->get_locus ()); - tree assignment = ctx->get_backend ()->assignment_statement ( - fnctx.fndecl, result_reference, compiled_expr, expr.get_locus ()); + tree assignment + = ctx->get_backend ()->assignment_statement (result_reference, + compiled_expr, + expr.get_locus ()); ctx->add_statement (assignment); } @@ -721,9 +715,7 @@ public: tree exit_expr = ctx->get_backend ()->exit_expression ( ctx->get_backend ()->boolean_constant_expression (true), expr.get_locus ()); - tree break_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, exit_expr); - ctx->add_statement (break_stmt); + ctx->add_statement (exit_expr); } } @@ -761,9 +753,7 @@ public: } } - tree goto_label - = ctx->get_backend ()->goto_statement (label, expr.get_locus ()); - ctx->add_statement (goto_label); + translated = ctx->get_backend ()->goto_statement (label, expr.get_locus ()); } void visit (HIR::BorrowExpr &expr) override; diff --git a/gcc/rust/backend/rust-compile-stmt.h b/gcc/rust/backend/rust-compile-stmt.h index 2dfb520..0f69fb0 100644 --- a/gcc/rust/backend/rust-compile-stmt.h +++ b/gcc/rust/backend/rust-compile-stmt.h @@ -90,9 +90,8 @@ public: auto fnctx = ctx->peek_fn (); if (ty->is_unit ()) { - tree expr_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, init); - ctx->add_statement (expr_stmt); + // FIXME this feels wrong + ctx->add_statement (init); } else { diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc index fcbfc05..9e2c5b3 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -82,10 +82,8 @@ CompileBlock::visit (HIR::BlockExpr &expr) auto compiled_expr = CompileStmt::Compile (s.get (), ctx); if (compiled_expr != nullptr) { - tree compiled_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, - compiled_expr); - ctx->add_statement (compiled_stmt); + tree s = convert_to_void (compiled_expr, ICV_STATEMENT); + ctx->add_statement (s); } } @@ -98,10 +96,7 @@ CompileBlock::visit (HIR::BlockExpr &expr) { if (result == nullptr) { - tree final_stmt - = ctx->get_backend ()->expression_statement (fnctx.fndecl, - compiled_expr); - ctx->add_statement (final_stmt); + ctx->add_statement (compiled_expr); } else { @@ -109,8 +104,7 @@ CompileBlock::visit (HIR::BlockExpr &expr) result, expr.get_final_expr ()->get_locus ()); tree assignment - = ctx->get_backend ()->assignment_statement (fnctx.fndecl, - result_reference, + = ctx->get_backend ()->assignment_statement (result_reference, compiled_expr, expr.get_locus ()); ctx->add_statement (assignment); diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc index f1f969c..1b0515e 100644 --- a/gcc/rust/backend/rust-constexpr.cc +++ b/gcc/rust/backend/rust-constexpr.cc @@ -17,6 +17,7 @@ #include "rust-constexpr.h" #include "rust-location.h" #include "rust-diagnostics.h" +#include "rust-tree.h" #include "fold-const.h" #include "realmpfr.h" @@ -25,52 +26,6 @@ #include "gimplify.h" #include "tree-iterator.h" -/* Returns true if NODE is a pointer. */ -#define TYPE_PTR_P(NODE) (TREE_CODE (NODE) == POINTER_TYPE) - -/* Returns true if NODE is a reference. */ -#define TYPE_REF_P(NODE) (TREE_CODE (NODE) == REFERENCE_TYPE) - -/* Returns true if NODE is a pointer or a reference. */ -#define INDIRECT_TYPE_P(NODE) (TYPE_PTR_P (NODE) || TYPE_REF_P (NODE)) - -/* [basic.fundamental] - - Types bool, char, wchar_t, and the signed and unsigned integer types - are collectively called integral types. - - Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration - types as well, which is incorrect in C++. Keep these checks in - ascending code order. */ -#define RS_INTEGRAL_TYPE_P(TYPE) \ - (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE) - -/* [basic.fundamental] - - Integral and floating types are collectively called arithmetic - types. - - As a GNU extension, we also accept complex types. - - Keep these checks in ascending code order. */ -#define ARITHMETIC_TYPE_P(TYPE) \ - (RS_INTEGRAL_TYPE_P (TYPE) || TREE_CODE (TYPE) == REAL_TYPE \ - || TREE_CODE (TYPE) == COMPLEX_TYPE) - -/* True iff TYPE is cv decltype(nullptr). */ -#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE) - -/* [basic.types] - - Arithmetic types, enumeration types, pointer types, - pointer-to-member types, and std::nullptr_t are collectively called - scalar types. - - Keep these checks in ascending code order. */ -#define SCALAR_TYPE_P(TYPE) \ - (TREE_CODE (TYPE) == ENUMERAL_TYPE || ARITHMETIC_TYPE_P (TYPE) \ - || TYPE_PTR_P (TYPE) || NULLPTR_TYPE_P (TYPE)) - namespace Rust { namespace Compile { diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc new file mode 100644 index 0000000..8e39408 --- /dev/null +++ b/gcc/rust/backend/rust-tree.cc @@ -0,0 +1,659 @@ +// 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 "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; +} + +} // namespace Rust diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h new file mode 100644 index 0000000..c21bf4b --- /dev/null +++ b/gcc/rust/backend/rust-tree.h @@ -0,0 +1,182 @@ +// 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)))) + +namespace Rust { + +// 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 */ +}; + +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); + +} // namespace Rust + +#endif // RUST_TREE |