aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gdcproject.org>2023-10-28 09:42:15 +0200
committerIain Buclaw <ibuclaw@gdcproject.org>2023-10-28 09:53:36 +0200
commit5d2a360f0a541646abb11efdbabc33c6a04de7ee (patch)
tree30f899e4220039d70a9c244498b54dbc0cee7931 /gcc
parent7f974c5fd4f59a9d8dd20c49a0e2909cb290f4b4 (diff)
downloadgcc-5d2a360f0a541646abb11efdbabc33c6a04de7ee.zip
gcc-5d2a360f0a541646abb11efdbabc33c6a04de7ee.tar.gz
gcc-5d2a360f0a541646abb11efdbabc33c6a04de7ee.tar.bz2
d: Add warning for call expression without side effects
In the last merge of the dmd front-end with upstream (r14-4830), this warning got removed from the semantic passes. Reimplement the warning for the code generation pass instead, where it cannot have an effect on conditional compilation. gcc/d/ChangeLog: * d-codegen.cc (call_side_effect_free_p): New function. * d-tree.h (CALL_EXPR_WARN_IF_UNUSED): New macro. (call_side_effect_free_p): New prototype. * expr.cc (ExprVisitor::visit (CallExp *)): Set CALL_EXPR_WARN_IF_UNUSED on matched call expressions. (ExprVisitor::visit (NewExp *)): Don't dereference the result of an allocation call here. * toir.cc (add_stmt): Emit warning when call expression added to statement list without being used. gcc/testsuite/ChangeLog: * gdc.dg/Wunused_value.d: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/d/d-codegen.cc54
-rw-r--r--gcc/d/d-tree.h7
-rw-r--r--gcc/d/expr.cc13
-rw-r--r--gcc/d/toir.cc32
-rw-r--r--gcc/testsuite/gdc.dg/Wunused_value.d29
5 files changed, 134 insertions, 1 deletions
diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc
index 155f5d0..91ddb1b 100644
--- a/gcc/d/d-codegen.cc
+++ b/gcc/d/d-codegen.cc
@@ -2102,6 +2102,60 @@ get_function_type (Type *t)
return tf;
}
+/* Returns TRUE if calling the function FUNC, or calling a function or delegate
+ object of type TYPE is be free of side effects. */
+
+bool
+call_side_effect_free_p (FuncDeclaration *func, Type *type)
+{
+ gcc_assert (func != NULL || type != NULL);
+
+ if (func != NULL)
+ {
+ /* Constructor and invariant calls can't be `pure'. */
+ if (func->isCtorDeclaration () || func->isInvariantDeclaration ())
+ return false;
+
+ /* Must be a `nothrow' function. */
+ TypeFunction *tf = func->type->toTypeFunction ();
+ if (!tf->isnothrow ())
+ return false;
+
+ /* Return type can't be `void' or `noreturn', as that implies all work is
+ done via side effects. */
+ if (tf->next->ty == TY::Tvoid || tf->next->ty == TY::Tnoreturn)
+ return false;
+
+ /* Only consider it as `pure' if it can't modify its arguments. */
+ if (func->isPure () == PURE::const_)
+ return true;
+ }
+
+ if (type != NULL)
+ {
+ TypeFunction *tf = get_function_type (type);
+
+ /* Must be a `nothrow` function type. */
+ if (tf == NULL || !tf->isnothrow ())
+ return false;
+
+ /* Return type can't be `void' or `noreturn', as that implies all work is
+ done via side effects. */
+ if (tf->next->ty == TY::Tvoid || tf->next->ty == TY::Tnoreturn)
+ return false;
+
+ /* Delegates that can modify its context can't be `pure'. */
+ if (type->isTypeDelegate () && tf->isMutable ())
+ return false;
+
+ /* Only consider it as `pure' if it can't modify its arguments. */
+ if (tf->purity == PURE::const_)
+ return true;
+ }
+
+ return false;
+}
+
/* Returns TRUE if CALLEE is a plain nested function outside the scope of
CALLER. In which case, CALLEE is being called through an alias that was
passed to CALLER. */
diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h
index 66c2f24..ed26533 100644
--- a/gcc/d/d-tree.h
+++ b/gcc/d/d-tree.h
@@ -47,6 +47,7 @@ typedef Array <Expression *> Expressions;
/* Usage of TREE_LANG_FLAG_?:
0: METHOD_CALL_EXPR
+ 1: CALL_EXPR_WARN_IF_UNUSED (in CALL_EXPR).
Usage of TYPE_LANG_FLAG_?:
0: TYPE_SHARED
@@ -357,6 +358,11 @@ lang_tree_node
#define METHOD_CALL_EXPR(NODE) \
(TREE_LANG_FLAG_0 (NODE))
+/* True if the CALL_EXPR is free of side effects, and its return value
+ should not be discarded. */
+#define CALL_EXPR_WARN_IF_UNUSED(NODE) \
+ (TREE_LANG_FLAG_1 (CALL_EXPR_CHECK (NODE)))
+
/* True if the type was declared 'shared'. */
#define TYPE_SHARED(NODE) \
(TYPE_LANG_FLAG_0 (NODE))
@@ -594,6 +600,7 @@ extern tree build_bounds_slice_condition (SliceExp *, tree, tree, tree);
extern bool array_bounds_check (void);
extern bool checkaction_trap_p (void);
extern TypeFunction *get_function_type (Type *);
+extern bool call_side_effect_free_p (FuncDeclaration *, Type *);
extern bool call_by_alias_p (FuncDeclaration *, FuncDeclaration *);
extern tree d_build_call_expr (FuncDeclaration *, tree, Expressions *);
extern tree d_build_call (TypeFunction *, tree, tree, Expressions *);
diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc
index 52243e6..72180b1 100644
--- a/gcc/d/expr.cc
+++ b/gcc/d/expr.cc
@@ -1714,6 +1714,12 @@ public:
build the call expression. */
tree exp = d_build_call (tf, callee, object, e->arguments);
+ /* Record whether the call expression has no side effects, so we can check
+ for an unused return value later. */
+ if (TREE_CODE (exp) == CALL_EXPR && CALL_EXPR_FN (exp) != NULL_TREE
+ && call_side_effect_free_p (e->f, e->e1->type))
+ CALL_EXPR_WARN_IF_UNUSED (exp) = 1;
+
if (returnvalue != NULL_TREE)
exp = compound_expr (exp, returnvalue);
@@ -2338,7 +2344,12 @@ public:
new_call = d_save_expr (new_call);
se->type = sd->type;
se->sym = new_call;
- result = compound_expr (build_expr (se), new_call);
+
+ /* Setting `se->sym' would mean that the result of the
+ constructed struct literal expression is `*(new_call)'.
+ Strip off the indirect reference, as we don't mean to
+ compute the value yet. */
+ result = build_address (build_expr (se));
}
else
result = new_call;
diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc
index db6f71b..f87e094 100644
--- a/gcc/d/toir.cc
+++ b/gcc/d/toir.cc
@@ -215,6 +215,38 @@ add_stmt (tree t)
if (!TREE_SIDE_EFFECTS (t))
return;
+ /* If a call expression has no side effects, and there's no explicit
+ `cast(void)', then issue a warning about the unused return value. */
+ if (TREE_CODE (t) == INDIRECT_REF)
+ {
+ t = TREE_OPERAND (t, 0);
+
+ if (TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE)
+ warning (OPT_Wunused_value, "value computed is not used");
+ }
+
+ if (TREE_CODE (t) == CALL_EXPR && CALL_EXPR_FN (t) != NULL_TREE
+ && CALL_EXPR_WARN_IF_UNUSED (t))
+ {
+ tree callee = CALL_EXPR_FN (t);
+
+ /* It's a call to a function or function pointer. */
+ if (TREE_CODE (callee) == ADDR_EXPR
+ && VAR_OR_FUNCTION_DECL_P (TREE_OPERAND (callee, 0)))
+ callee = TREE_OPERAND (callee, 0);
+
+ /* It's a call to a delegate object. */
+ if (TREE_CODE (callee) == COMPONENT_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (callee, 0))) == RECORD_TYPE
+ && TYPE_DELEGATE (TREE_TYPE (TREE_OPERAND (callee, 0))))
+ callee = TREE_OPERAND (callee, 0);
+
+ warning (OPT_Wunused_value,
+ "calling %qE without side effects discards return value "
+ "of type %qT; prepend a %<cast(void)%> if intentional",
+ callee, TREE_TYPE (t));
+ }
+
if (TREE_CODE (t) == COMPOUND_EXPR)
{
/* Push out each comma expressions as separate statements. */
diff --git a/gcc/testsuite/gdc.dg/Wunused_value.d b/gcc/testsuite/gdc.dg/Wunused_value.d
new file mode 100644
index 0000000..0afc881
--- /dev/null
+++ b/gcc/testsuite/gdc.dg/Wunused_value.d
@@ -0,0 +1,29 @@
+// { dg-do compile }
+// { dg-options "-Wunused-value" }
+
+@safe pure nothrow T t1(T)(T x)
+{
+ return x * x;
+}
+
+nothrow pure int f1(immutable(int)[] a)
+{
+ return 0;
+}
+
+nothrow pure int f2(immutable(int)* p)
+{
+ return 0;
+}
+
+void test()
+{
+ int x = 3;
+ t1(x); // { dg-warning "without side effects discards return value" }
+
+ auto fp = &t1!int;
+ fp(x); // { dg-warning "without side effects discards return value" }
+
+ f1([]); // { dg-warning "without side effects discards return value" }
+ f2(null); // { dg-warning "without side effects discards return value" }
+}