diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/Make-lang.in | 1 | ||||
-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 | ||||
-rw-r--r-- | gcc/rust/hir/tree/rust-hir-item.h | 21 | ||||
-rw-r--r-- | gcc/rust/hir/tree/rust-hir.h | 3 | ||||
-rw-r--r-- | gcc/rust/lang.opt | 8 | ||||
-rw-r--r-- | gcc/rust/rust-backend.h | 5 | ||||
-rw-r--r-- | gcc/rust/rust-gcc.cc | 21 | ||||
-rw-r--r-- | gcc/rust/typecheck/rust-hir-type-check-implitem.h | 29 | ||||
-rw-r--r-- | gcc/rust/util/rust-attributes.cc | 8 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/torture/must_use1.rs | 16 | ||||
-rw-r--r-- | gcc/testsuite/rust/compile/torture/must_use2.rs | 17 |
20 files changed, 1004 insertions, 125 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index b43f0f3..b33d90e 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -111,6 +111,7 @@ GRS_OBJS = \ rust/rust-compile-type.o \ rust/rust-constexpr.o \ rust/rust-compile-base.o \ + rust/rust-tree.o \ $(END) # removed object files from here 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 diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h index 54ba7d5..25e5681 100644 --- a/gcc/rust/hir/tree/rust-hir-item.h +++ b/gcc/rust/hir/tree/rust-hir-item.h @@ -2369,8 +2369,11 @@ public: return TraitItemKind::FUNC; } - AST::AttrVec &get_outer_attrs () { return outer_attrs; } - const AST::AttrVec &get_outer_attrs () const { return outer_attrs; } + AST::AttrVec &get_outer_attrs () override final { return outer_attrs; } + const AST::AttrVec &get_outer_attrs () const override final + { + return outer_attrs; + } protected: // Clone function implementation as (not pure) virtual method @@ -2452,6 +2455,12 @@ public: return TraitItemKind::CONST; } + AST::AttrVec &get_outer_attrs () override final { return outer_attrs; } + const AST::AttrVec &get_outer_attrs () const override final + { + return outer_attrs; + } + protected: // Clone function implementation as (not pure) virtual method TraitItemConst *clone_trait_item_impl () const override @@ -2533,6 +2542,12 @@ public: return TraitItemKind::TYPE; } + AST::AttrVec &get_outer_attrs () override final { return outer_attrs; } + const AST::AttrVec &get_outer_attrs () const override final + { + return outer_attrs; + } + protected: // Clone function implementation as (not pure) virtual method TraitItemType *clone_trait_item_impl () const override @@ -2821,6 +2836,8 @@ public: Identifier get_item_name () const { return item_name; } + AST::AttrVec &get_outer_attrs () { return outer_attrs; } + protected: ExternalItem (Analysis::NodeMapping mappings, Identifier item_name, Visibility vis, AST::AttrVec outer_attrs, Location locus) diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h index 760904b..fda7e1d 100644 --- a/gcc/rust/hir/tree/rust-hir.h +++ b/gcc/rust/hir/tree/rust-hir.h @@ -723,6 +723,9 @@ public: const Analysis::NodeMapping get_mappings () const { return mappings; } virtual TraitItemKind get_item_kind () const = 0; + + virtual AST::AttrVec &get_outer_attrs () = 0; + virtual const AST::AttrVec &get_outer_attrs () const = 0; }; class ImplItem diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index ddd2868..794a667 100644 --- a/gcc/rust/lang.opt +++ b/gcc/rust/lang.opt @@ -34,6 +34,14 @@ L Rust Joined Separate ; Not documented +Wall +Rust +; Documented in c.opt + +Wunused-result +Rust Var(warn_unused_result) Init(1) Warning +Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value. + frust-crate= Rust Joined RejectNegative -frust-crate=<name> Set the crate name for the compilation diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h index fe809c9..fca09b2 100644 --- a/gcc/rust/rust-backend.h +++ b/gcc/rust/rust-backend.h @@ -292,16 +292,13 @@ public: // Statements. - // Create an expression statement within the specified function. - virtual tree expression_statement (tree, tree) = 0; - // 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, tree lhs, tree rhs, Location) = 0; + 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. diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc index 812fd55..dfdfe8a5 100644 --- a/gcc/rust/rust-gcc.cc +++ b/gcc/rust/rust-gcc.cc @@ -249,11 +249,9 @@ public: // Statements. - tree expression_statement (tree, tree); - tree init_statement (tree, Bvariable *var, tree init); - tree assignment_statement (tree, tree lhs, tree rhs, Location); + tree assignment_statement (tree lhs, tree rhs, Location); tree return_statement (tree, const std::vector<tree> &, Location); @@ -1837,14 +1835,6 @@ Gcc_backend::call_expression (tree, // containing fcn for call return ret; } -// An expression as a statement. - -tree -Gcc_backend::expression_statement (tree, tree expr) -{ - return expr; -} - // Variable initialization. tree @@ -1880,8 +1870,7 @@ Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree) // Assignment. tree -Gcc_backend::assignment_statement (tree bfn, tree lhs, tree rhs, - Location location) +Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location) { if (lhs == error_mark_node || rhs == error_mark_node) return error_mark_node; @@ -1896,8 +1885,7 @@ Gcc_backend::assignment_statement (tree bfn, tree lhs, tree rhs, || int_size_in_bytes (TREE_TYPE (lhs)) == 0 || TREE_TYPE (rhs) == void_type_node || int_size_in_bytes (TREE_TYPE (rhs)) == 0) - return this->compound_statement (this->expression_statement (bfn, lhs), - this->expression_statement (bfn, rhs)); + return this->compound_statement (lhs, rhs); rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location); @@ -2527,8 +2515,7 @@ Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree, if (init_tree != NULL_TREE && (this->type_size (type_tree) == 0 || TREE_TYPE (init_tree) == void_type_node)) - *pstatement = this->compound_statement ( - this->expression_statement (fndecl, init_tree), *pstatement); + *pstatement = this->compound_statement (init_tree, *pstatement); return new Bvariable (var); } diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h index b5c0dbc..80fd0ec 100644 --- a/gcc/rust/typecheck/rust-hir-type-check-implitem.h +++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h @@ -404,8 +404,6 @@ public: void visit (HIR::ConstantItem &constant) override { - // resolved_trait_item = trait_reference.lookup_trait_item ( - // constant.get_identifier (), TraitItemReference::TraitItemType::CONST); trait_reference.lookup_trait_item_by_type ( constant.get_identifier (), TraitItemReference::TraitItemType::CONST, &resolved_trait_item); @@ -428,6 +426,11 @@ public: if (resolved_trait_item->is_error ()) return; + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item->get_hir_trait_item (); + merge_attributes (constant.get_outer_attrs (), *hir_trait_item); + // check the types are compatible if (!resolved_trait_item->get_tyty ()->can_eq (lookup, true)) { @@ -467,6 +470,11 @@ public: if (resolved_trait_item->is_error ()) return; + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item->get_hir_trait_item (); + merge_attributes (type.get_outer_attrs (), *hir_trait_item); + // check the types are compatible if (!resolved_trait_item->get_tyty ()->can_eq (lookup, true)) { @@ -515,6 +523,11 @@ public: if (resolved_trait_item->is_error ()) return; + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item->get_hir_trait_item (); + merge_attributes (function.get_outer_attrs (), *hir_trait_item); + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); rust_assert (resolved_trait_item->get_tyty ()->get_kind () == TyTy::TypeKind::FNDEF); @@ -557,6 +570,18 @@ public: } } +protected: + // this allows us to inherit the must_use specified on a trait definition onto + // its implementation + void merge_attributes (AST::AttrVec &impl_item_attrs, + const HIR::TraitItem &trait_item) + { + for (const auto &attr : trait_item.get_outer_attrs ()) + { + impl_item_attrs.push_back (attr); + } + } + private: TypeCheckImplItemWithTrait ( HIR::ImplBlock *parent, TyTy::BaseType *self, diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc index 3809ad7..4f3bd0b 100644 --- a/gcc/rust/util/rust-attributes.cc +++ b/gcc/rust/util/rust-attributes.cc @@ -22,10 +22,10 @@ namespace Rust { namespace Analysis { // https://doc.rust-lang.org/stable/nightly-rustc/src/rustc_feature/builtin_attrs.rs.html#256 -static const BuiltinAttrDefinition __definitions[] = { - {"inline", CODE_GENERATION}, {"cfg", EXPANSION}, {"cfg_attr", EXPANSION}, - {"allow", STATIC_ANALYSIS}, {"lang", HIR_LOWERING}, -}; +static const BuiltinAttrDefinition __definitions[] + = {{"inline", CODE_GENERATION}, {"cfg", EXPANSION}, + {"cfg_attr", EXPANSION}, {"allow", STATIC_ANALYSIS}, + {"lang", HIR_LOWERING}, {"must_use", STATIC_ANALYSIS}}; BuiltinAttributeMappings * BuiltinAttributeMappings::get () diff --git a/gcc/testsuite/rust/compile/torture/must_use1.rs b/gcc/testsuite/rust/compile/torture/must_use1.rs new file mode 100644 index 0000000..95a6657 --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/must_use1.rs @@ -0,0 +1,16 @@ +#[must_use = "TEST 1"] +fn test1() -> i32 { + 123 +} + +#[must_use = "TEST 2"] +fn test2() -> i32 { + 456 +} + +fn main() { + let _a = test1(); + + test2(); + // { dg-warning "ignoring return value of" "" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/rust/compile/torture/must_use2.rs b/gcc/testsuite/rust/compile/torture/must_use2.rs new file mode 100644 index 0000000..4c6d660 --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/must_use2.rs @@ -0,0 +1,17 @@ +trait A { + #[must_use] + fn test() -> i32; + // { dg-warning "unused name" "" { target *-*-* } .-1 } +} + +struct S; +impl A for S { + fn test() -> i32 { + 123 + } +} + +fn main() { + S::test(); + // { dg-warning "ignoring return value of" "" { target *-*-* } .-1 } +} |