===================================================================
@@ -4936,11 +4936,11 @@ infer_value_range (gimple stmt, tree op,
break;
if (e == NULL)
return false;
}
- if (infer_nonnull_range (stmt, op, true, true))
+ if (infer_nonnull_range (stmt, op))
{
*val_p = build_int_cst (TREE_TYPE (op), 0);
*comp_code_p = NE_EXPR;
return true;
}
===================================================================
@@ -258,10 +258,11 @@ Objective-C and Objective-C++ Dialects}.
-Wframe-larger-than=@var{len} -Wno-free-nonheap-object -Wjump-misses-init @gol
-Wignored-qualifiers -Wincompatible-pointer-types @gol
-Wimplicit -Wimplicit-function-declaration -Wimplicit-int @gol
-Winit-self -Winline -Wno-int-conversion @gol
-Wno-int-to-pointer-cast -Wno-invalid-offsetof @gol
+-Wnull-dereference @gol
-Winvalid-pch -Wlarger-than=@var{len} -Wunsafe-loop-optimizations @gol
-Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
-Wmain -Wmaybe-uninitialized -Wmemset-transposed-args @gol
-Wmisleading-indentation -Wmissing-braces @gol
-Wmissing-field-initializers -Wmissing-include-dirs @gol
@@ -4130,10 +4133,20 @@ All the above @option{-Wunused} options
In order to get a warning about an unused function parameter, you must
either specify @option{-Wextra -Wunused} (note that @option{-Wall} implies
@option{-Wunused}), or separately specify @option{-Wunused-parameter}.
+@item -Wnull-dereference
+@opindex Wnull-dereference
+@opindex Wno-null-dereference
+Warn if the compiler detects paths which trigger erroneous or
+undefined behaviour due to dereferencing a NULL pointer. This option
+is only active when @option{-fdelete-null-pointer-checks} is active,
+which is enabled by optimizations in most targets. The precision of
+the warnings depends on the optimization options used. This option is
+enabled by @option{-Wall}.
+
@item -Wuninitialized
@opindex Wuninitialized
@opindex Wno-uninitialized
Warn if an automatic variable is used without first being initialized
or if a variable may be clobbered by a @code{setjmp} call. In C++,
===================================================================
@@ -33,10 +33,10 @@ bar (void)
returns non-null attribute to isolate a path where NULL flows into
a return statement. We test this twice, once where the NULL flows
from a PHI, the second with an explicit return 0 in the IL.
We also verify that after isolation phi-cprop simplifies the
- return statement so that it returns &z directly.
+ return statement so that it returns &z directly. */
/* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */
/* { dg-final { scan-tree-dump-times "return &z;" 1 "phicprop1"} } */
===================================================================
@@ -22,10 +22,10 @@ bar (void)
/* We testing that the path isolation code can take advantage of the
returns non-null attribute to isolate a path where NULL flows into
a return statement.
We also verify that after isolation phi-cprop simplifies the
- return statement so that it returns &z directly.
+ return statement so that it returns &z directly. */
/* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */
/* { dg-final { scan-tree-dump-times "foo .&z.;" 1 "phicprop1"} } */
===================================================================
@@ -0,0 +1,82 @@
+/* { dg-do compile } */
+/* PR c/16531 */
+/* { dg-options "-O2 -fdelete-null-pointer-checks -Wnull-dereference" } */
+/* { dg-skip-if "" keeps_null_pointer_checks } */
+
+#ifndef __cplusplus
+#define NULL (void *)0
+#else
+#define NULL nullptr
+#endif
+
+struct t
+{
+ int bar;
+};
+
+struct t2
+{
+ struct t *s;
+};
+
+void test1 ()
+{
+ struct t *s = NULL;
+ s->bar = 1; /* { dg-warning "null" } */
+}
+
+void test2 (struct t *s)
+{
+ if (s == NULL && s->bar > 2) /* { dg-warning "null" } */
+ return;
+
+ s->bar = 3;
+}
+
+void test3 (struct t *s)
+{
+ if (s != NULL || s->bar > 2) /* { dg-warning "null" } */
+ return;
+
+ s->bar = 3; /* { dg-warning "null" } */
+}
+
+int test4 (struct t *s)
+{
+ if (s != NULL && s->bar > 2) /* { dg-bogus "null" } */
+ return 1;
+ return 0;
+}
+
+int test5 (struct t *s)
+{
+ if (s == NULL || s->bar > 2) /* { dg-bogus "null" } */
+ return 1;
+ return 0;
+}
+
+int test6 (struct t2 *s)
+{
+ if (s->s == 0 && s->s->bar == 0) /* { dg-warning "null" } */
+ return 1;
+ return 0;
+}
+
+int test7 (struct t *s)
+{
+ s = 0;
+ return s->bar; /* { dg-warning "null" } */
+}
+
+int test8 ()
+{
+ return ((struct t *)0)->bar; /* { dg-warning "null" } */
+}
+
+void test9 (struct t **s)
+{
+ if (s == 0)
+ *s = 0; /* { dg-warning "null" } */
+}
+
+
===================================================================
@@ -1,8 +1,7 @@
-
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks -Wnull-dereference" } */
/* { dg-skip-if "" keeps_null_pointer_checks } */
struct demangle_component
{
@@ -37,12 +36,12 @@ d_make_empty (struct d_info *di)
struct demangle_component *
d_type (struct d_info *di)
{
struct demangle_component *ret;
ret = d_make_empty (di);
- ret->type = 42;
- ret->zzz = -1;
+ ret->type = 42; /* { dg-warning "null pointer dereference" } */
+ ret->zzz = -1; /* { dg-warning "null pointer dereference" } */
return ret;
}
/* We're testing three aspects of isolation here. First that isolation
occurs, second that if we have two null dereferences in a block that
@@ -51,10 +50,5 @@ d_type (struct d_info *di)
and finally that we set the RHS of the store to zero. */
/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "isolate-paths"} } */
/* { dg-final { scan-tree-dump-times "->type = 42" 1 "isolate-paths"} } */
/* { dg-final { scan-tree-dump-times "->type ={v} 0" 1 "isolate-paths"} } */
/* { dg-final { scan-tree-dump-times "->zzz" 1 "isolate-paths"} } */
-
-
-
-
-
===================================================================
@@ -1,7 +1,7 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks -Wnull-dereference" } */
/* { dg-skip-if "" keeps_null_pointer_checks } */
typedef __SIZE_TYPE__ size_t;
extern void *memset (void *__s, int __c, size_t __n)
@@ -27,11 +27,11 @@ typedef struct VEC_rtx_gc
static __inline__ void
VEC_rtx_gc_safe_grow (VEC_rtx_gc ** vec_, int size_, const char *file_,
unsigned line_, const char *function_)
{
- ((*vec_) ? &(*vec_)->base : 0)->num = size_;
+ ((*vec_) ? &(*vec_)->base : 0)->num = size_; /* { dg-warning "null pointer dereference" } */
}
static __inline__ void
VEC_rtx_gc_safe_grow_cleared (VEC_rtx_gc ** vec_, int size_,
const char *file_, unsigned line_,
===================================================================
@@ -1,7 +1,7 @@
/* { dg-do compile } */
-/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-isolate-paths -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-isolate-paths -fdump-tree-optimized -Wnull-dereference" } */
/* { dg-skip-if "" keeps_null_pointer_checks } */
struct demangle_component
{
@@ -34,12 +34,12 @@ d_make_empty (struct d_info *di)
struct demangle_component *
d_type (struct d_info *di)
{
struct demangle_component *ret;
ret = d_make_empty (di);
- foo (ret->type);
- bar (ret->zzz);
+ foo (ret->type); /* { dg-warning "null pointer dereference" } */
+ bar (ret->zzz); /* { dg-warning "null pointer dereference" } */
return ret;
}
/* We're testing two aspects of isolation here. First that isolation
occurs, second that if we have two null dereferences in a block that
===================================================================
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-Wnonnull" } */
+
+
+extern void foo(void *) __attribute__ ((__nonnull__ (1)));
+
+int z;
+int y;
+
+void
+com (int a)
+{
+ foo (a == 42 ? &z : (void *) 0); /* { dg-warning "null" } */
+}
+
+void
+bar (void)
+{
+ foo ((void *)0); /* { dg-warning "null" } */
+}
+
+int * foo_r(int a) __attribute__((returns_nonnull));
+int * bar_r(void) __attribute__((returns_nonnull));
+
+int *
+foo_r(int a)
+{
+ switch (a)
+ {
+ case 0:
+ return &z;
+ default:
+ return (int *)0; /* { dg-warning "null" } */
+ }
+}
+
+int *
+bar_r (void)
+{
+ return 0; /* { dg-warning "null" } */
+}
+
===================================================================
@@ -329,15 +329,33 @@ find_implicit_erroneous_behaviour (void)
in other blocks would require more complex path
isolation code. */
if (gimple_bb (use_stmt) != bb)
continue;
- if (infer_nonnull_range (use_stmt, lhs,
- flag_isolate_erroneous_paths_dereference,
- flag_isolate_erroneous_paths_attribute))
+ bool by_dereference
+ = infer_nonnull_range_by_dereference (use_stmt, lhs);
+ if (by_dereference
+ || infer_nonnull_range_by_attribute (use_stmt, lhs))
{
+ location_t loc = gimple_location (use_stmt)
+ ? gimple_location (use_stmt)
+ : gimple_phi_arg_location (phi, i);
+
+ if (by_dereference)
+ {
+ warning_at (loc, OPT_Wnull_dereference,
+ "potential null pointer dereference");
+ if (!flag_isolate_erroneous_paths_dereference)
+ continue;
+ }
+ else
+ {
+ if (!flag_isolate_erroneous_paths_attribute)
+ continue;
+ }
+
duplicate = isolate_path (bb, duplicate, e,
use_stmt, lhs, false);
/* When we remove an incoming edge, we need to
reprocess the Ith element. */
@@ -379,17 +397,33 @@ find_explicit_erroneous_behaviour (void)
because of jump threading and constant propagation. */
for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
{
gimple stmt = gsi_stmt (si);
- /* By passing null_pointer_node, we can use infer_nonnull_range
- to detect explicit NULL pointer dereferences and other uses
- where a non-NULL value is required. */
- if (infer_nonnull_range (stmt, null_pointer_node,
- flag_isolate_erroneous_paths_dereference,
- flag_isolate_erroneous_paths_attribute))
+ /* By passing null_pointer_node, we can use the
+ infer_nonnull_range functions to detect explicit NULL
+ pointer dereferences and other uses where a non-NULL
+ value is required. */
+
+ bool by_dereference
+ = infer_nonnull_range_by_dereference (stmt, null_pointer_node);
+ if (by_dereference
+ || infer_nonnull_range_by_attribute (stmt, null_pointer_node))
{
+ if (by_dereference)
+ {
+ warning_at (gimple_location (stmt), OPT_Wnull_dereference,
+ "null pointer dereference");
+ if (!flag_isolate_erroneous_paths_dereference)
+ continue;
+ }
+ else
+ {
+ if (!flag_isolate_erroneous_paths_attribute)
+ continue;
+ }
+
insert_trap_and_remove_trailing_statements (&si,
null_pointer_node);
/* And finally, remove all outgoing edges from BB. */
edge e;
@@ -532,11 +566,12 @@ public:
virtual bool gate (function *)
{
/* If we do not have a suitable builtin function for the trap statement,
then do not perform the optimization. */
return (flag_isolate_erroneous_paths_dereference != 0
- || flag_isolate_erroneous_paths_attribute != 0);
+ || flag_isolate_erroneous_paths_attribute != 0
+ || warn_null_dereference);
}
virtual unsigned int execute (function *)
{
return gimple_ssa_isolate_erroneous_paths ();
===================================================================
@@ -1613,11 +1613,11 @@ instrument_nonnull_arg (gimple_stmt_iter
loc[1] = UNKNOWN_LOCATION;
for (unsigned int i = 0; i < gimple_call_num_args (stmt); i++)
{
tree arg = gimple_call_arg (stmt, i);
if (POINTER_TYPE_P (TREE_TYPE (arg))
- && infer_nonnull_range (stmt, arg, false, true))
+ && infer_nonnull_range_by_attribute (stmt, arg))
{
gimple g;
if (!is_gimple_val (arg))
{
g = gimple_build_assign (make_ssa_name (TREE_TYPE (arg)), arg);
@@ -1678,11 +1678,11 @@ instrument_nonnull_return (gimple_stmt_i
loc[0] = gimple_location (stmt);
loc[1] = UNKNOWN_LOCATION;
if (arg
&& POINTER_TYPE_P (TREE_TYPE (arg))
&& is_gimple_val (arg)
- && infer_nonnull_range (stmt, arg, false, true))
+ && infer_nonnull_range_by_attribute (stmt, arg))
{
basic_block then_bb, fallthru_bb;
*gsi = create_cond_insert_point (gsi, true, false, true,
&then_bb, &fallthru_bb);
gimple g = gimple_build_cond (EQ_EXPR, arg,
===================================================================
@@ -590,10 +590,15 @@ Common RejectNegative Joined Warning Und
Wlarger-than=
Common RejectNegative Joined UInteger Warning
-Wlarger-than=<number> Warn if an object is larger than <number> bytes
+Wnull-dereference
+Common Var(warn_null_dereference) Warning EnabledBy(Wall)
+Warn if the compiler detects paths which trigger erroneous or undefined
+behaviour due to dereferencing a NULL pointer.
+
Wunsafe-loop-optimizations
Common Var(warn_unsafe_loop_optimizations) Warning
Warn if the loop cannot be optimized due to nontrivial assumptions.
Wmissing-noreturn
===================================================================
@@ -2616,35 +2616,52 @@ check_loadstore (gimple, tree op, tree,
&& operand_equal_p (TREE_OPERAND (op, 0), (tree)data, 0))
return true;
return false;
}
-/* If OP can be inferred to be non-NULL after STMT executes, return true.
- DEREFERENCE is TRUE if we can use a pointer dereference to infer a
- non-NULL range, FALSE otherwise.
-
- ATTRIBUTE is TRUE if we can use attributes to infer a non-NULL range
- for function arguments and return values. FALSE otherwise. */
+/* Return true if OP can be inferred to be non-NULL after STMT executes,
+ either by using a pointer dereference or attributes. */
+bool
+infer_nonnull_range (gimple stmt, tree op)
+{
+ return infer_nonnull_range_by_dereference (stmt, op)
+ || infer_nonnull_range_by_attribute (stmt, op);
+}
+/* Return true if OP can be inferred to be non-NULL after STMT
+ executes by using a pointer dereference. */
bool
-infer_nonnull_range (gimple stmt, tree op, bool dereference, bool attribute)
+infer_nonnull_range_by_dereference (gimple stmt, tree op)
{
/* We can only assume that a pointer dereference will yield
non-NULL if -fdelete-null-pointer-checks is enabled. */
if (!flag_delete_null_pointer_checks
|| !POINTER_TYPE_P (TREE_TYPE (op))
|| gimple_code (stmt) == GIMPLE_ASM)
return false;
- if (dereference
- && walk_stmt_load_store_ops (stmt, (void *)op,
- check_loadstore, check_loadstore))
+ if (walk_stmt_load_store_ops (stmt, (void *)op,
+ check_loadstore, check_loadstore))
return true;
- if (attribute
- && is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
+ return false;
+}
+
+/* Return true if OP can be inferred to be a non-NULL after STMT
+ executes by using attributes. */
+bool
+infer_nonnull_range_by_attribute (gimple stmt, tree op)
+{
+ /* We can only assume that a pointer dereference will yield
+ non-NULL if -fdelete-null-pointer-checks is enabled. */
+ if (!flag_delete_null_pointer_checks
+ || !POINTER_TYPE_P (TREE_TYPE (op))
+ || gimple_code (stmt) == GIMPLE_ASM)
+ return false;
+
+ if (is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
{
tree fntype = gimple_call_fntype (stmt);
tree attrs = TYPE_ATTRIBUTES (fntype);
for (; attrs; attrs = TREE_CHAIN (attrs))
{
@@ -2679,17 +2696,16 @@ infer_nonnull_range (gimple stmt, tree o
}
}
/* If this function is marked as returning non-null, then we can
infer OP is non-null if it is used in the return statement. */
- if (attribute)
- if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
- if (gimple_return_retval (return_stmt)
- && operand_equal_p (gimple_return_retval (return_stmt), op, 0)
- && lookup_attribute ("returns_nonnull",
- TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
- return true;
+ if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
+ if (gimple_return_retval (return_stmt)
+ && operand_equal_p (gimple_return_retval (return_stmt), op, 0)
+ && lookup_attribute ("returns_nonnull",
+ TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
+ return true;
return false;
}
/* Compare two case labels. Because the front end should already have
===================================================================
@@ -1399,11 +1399,13 @@ extern bool gimple_call_builtin_p (const
extern bool gimple_call_builtin_p (const_gimple, enum built_in_class);
extern bool gimple_call_builtin_p (const_gimple, enum built_in_function);
extern bool gimple_asm_clobbers_memory_p (const gasm *);
extern void dump_decl_set (FILE *, bitmap);
extern bool nonfreeing_call_p (gimple);
-extern bool infer_nonnull_range (gimple, tree, bool, bool);
+extern bool infer_nonnull_range (gimple, tree);
+extern bool infer_nonnull_range_by_dereference (gimple, tree);
+extern bool infer_nonnull_range_by_attribute (gimple, tree);
extern void sort_case_labels (vec<tree>);
extern void preprocess_case_label_vec_for_gimple (vec<tree>, tree, tree *);
extern void gimple_seq_set_location (gimple_seq, location_t);
extern void gimple_seq_discard (gimple_seq);
extern void maybe_remove_unused_call_args (struct function *, gimple);