c-typeck.c (check_modify_expr): New function.
* c-typeck.c (check_modify_expr): New function. (build_modify_expr): Call it if warn_sequence_point. * c-decl.c (warn_sequence_point): New variable. (c_decode_option): Handle -Wsequence-point and -Wno-sequence-point. Enable -Wsequence-point as part of -Wall. * c-tree.h (warn_sequence_point): Declare. * invoke.texi (-Wsequence-point): Document. * toplev.c (documented_lang_options): Add -Wsequence-point and -Wno-sequence-point. Original work by Michael Meeks, 16 Jun 1998. testsuite: * gcc.dg/sequence-pt-1.c: New test. From-SVN: r36840
This commit is contained in:
parent
d0b43a40b5
commit
bb58bec5cf
|
@ -1,3 +1,17 @@
|
|||
2000-10-11 Michael Meeks <mmeeks@gnu.org>
|
||||
Joseph S. Myers <jsm28@cam.ac.uk>
|
||||
|
||||
* c-typeck.c (check_modify_expr): New function.
|
||||
(build_modify_expr): Call it if warn_sequence_point.
|
||||
* c-decl.c (warn_sequence_point): New variable.
|
||||
(c_decode_option): Handle -Wsequence-point and
|
||||
-Wno-sequence-point. Enable -Wsequence-point as part of -Wall.
|
||||
* c-tree.h (warn_sequence_point): Declare.
|
||||
* invoke.texi (-Wsequence-point): Document.
|
||||
* toplev.c (documented_lang_options): Add -Wsequence-point and
|
||||
-Wno-sequence-point.
|
||||
Original work by Michael Meeks, 16 Jun 1998.
|
||||
|
||||
Wed Oct 11 06:15:41 2000 Richard Kenner <kenner@vlsi1.ultra.nyu.edu>
|
||||
|
||||
* tree.c (get_narrower): Don't look at precision of field if
|
||||
|
|
|
@ -497,6 +497,10 @@ int warn_float_equal = 0;
|
|||
|
||||
int warn_multichar = 1;
|
||||
|
||||
/* Nonzero means warn about possible violations of sequence point rules. */
|
||||
|
||||
int warn_sequence_point;
|
||||
|
||||
/* The variant of the C language being processed. */
|
||||
|
||||
c_language_kind c_language = clk_c;
|
||||
|
@ -765,6 +769,10 @@ c_decode_option (argc, argv)
|
|||
warn_return_type = 1;
|
||||
else if (!strcmp (p, "-Wno-return-type"))
|
||||
warn_return_type = 0;
|
||||
else if (!strcmp (p, "-Wsequence-point"))
|
||||
warn_sequence_point = 1;
|
||||
else if (!strcmp (p, "-Wno-sequence-point"))
|
||||
warn_sequence_point = 0;
|
||||
else if (!strcmp (p, "-Wcomment"))
|
||||
; /* cpp handles this one. */
|
||||
else if (!strcmp (p, "-Wno-comment"))
|
||||
|
@ -826,6 +834,7 @@ c_decode_option (argc, argv)
|
|||
warn_format = 1;
|
||||
warn_char_subscripts = 1;
|
||||
warn_parentheses = 1;
|
||||
warn_sequence_point = 1;
|
||||
warn_missing_braces = 1;
|
||||
/* We set this to 2 here, but 1 in -Wmain, so -ffreestanding can turn
|
||||
it off only if it's not explicit. */
|
||||
|
|
|
@ -366,6 +366,10 @@ extern int warn_missing_braces;
|
|||
|
||||
extern int warn_sign_compare;
|
||||
|
||||
/* Warn about possible violations of sequence point rules. */
|
||||
|
||||
extern int warn_sequence_point;
|
||||
|
||||
/* Warn about testing equality of floating point numbers. */
|
||||
|
||||
extern int warn_float_equal;
|
||||
|
|
132
gcc/c-typeck.c
132
gcc/c-typeck.c
|
@ -61,6 +61,7 @@ static tree pointer_diff PARAMS ((tree, tree));
|
|||
static tree unary_complex_lvalue PARAMS ((enum tree_code, tree));
|
||||
static void pedantic_lvalue_warning PARAMS ((enum tree_code));
|
||||
static tree internal_build_compound_expr PARAMS ((tree, int));
|
||||
static void check_modify_expr PARAMS ((tree, tree));
|
||||
static tree convert_for_assignment PARAMS ((tree, tree, const char *,
|
||||
tree, tree, int));
|
||||
static void warn_for_assignment PARAMS ((const char *, const char *,
|
||||
|
@ -3814,6 +3815,132 @@ build_c_cast (type, expr)
|
|||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Recursive check for expressions that break the sequence point rules
|
||||
and so have undefined semantics (e.g. n = n++). FIXME: if walk_tree
|
||||
gets moved out of the C++ front end, this should probably be moved
|
||||
to code shared between the front ends and use walk_tree. */
|
||||
static void
|
||||
check_modify_expr (lhs, rhs)
|
||||
tree lhs, rhs;
|
||||
{
|
||||
tree identifier_name; /* A VAR_DECL name on the LHS that could
|
||||
be the same as one on the RHS. */
|
||||
identifier_name = NULL_TREE;
|
||||
|
||||
if ((lhs == NULL_TREE) || (rhs == NULL_TREE))
|
||||
return;
|
||||
|
||||
switch (TREE_CODE (rhs))
|
||||
{
|
||||
case ERROR_MARK:
|
||||
return;
|
||||
case VAR_DECL:
|
||||
case PARM_DECL:
|
||||
identifier_name = DECL_NAME (rhs);
|
||||
break;
|
||||
case PREDECREMENT_EXPR:
|
||||
case PREINCREMENT_EXPR:
|
||||
case POSTDECREMENT_EXPR:
|
||||
case POSTINCREMENT_EXPR:
|
||||
{
|
||||
tree var_decl = TREE_OPERAND (rhs, 0);
|
||||
if (TREE_CODE (var_decl) == VAR_DECL
|
||||
|| TREE_CODE (var_decl) == PARM_DECL)
|
||||
identifier_name = DECL_NAME (var_decl);
|
||||
}
|
||||
break;
|
||||
case TREE_LIST:
|
||||
{
|
||||
tree parm = TREE_CHAIN (rhs);
|
||||
/* Now scan all the list, e.g. indices of multi dimensional array. */
|
||||
while (parm)
|
||||
{
|
||||
check_modify_expr (lhs, TREE_VALUE (parm));
|
||||
parm = TREE_CHAIN (parm);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case NOP_EXPR:
|
||||
case CONVERT_EXPR:
|
||||
case NON_LVALUE_EXPR:
|
||||
check_modify_expr (lhs, TREE_OPERAND (rhs, 0));
|
||||
return;
|
||||
case MODIFY_EXPR:
|
||||
/* First check for form a = b = a++ by checking RHS. */
|
||||
check_modify_expr (lhs, TREE_OPERAND (rhs, 1));
|
||||
/* Then check for a = (a = 1) + 2 and a = b[a++] = c. */
|
||||
if (TREE_CODE (TREE_OPERAND (rhs, 0)) == VAR_DECL
|
||||
|| TREE_CODE (TREE_OPERAND (rhs, 0)) == PARM_DECL)
|
||||
{
|
||||
identifier_name = DECL_NAME (TREE_OPERAND (rhs, 0));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
check_modify_expr (lhs, TREE_OPERAND (rhs, 0));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
/* We don't know what to do... pray check_modify_expr removes
|
||||
loops in the tree. */
|
||||
switch (TREE_CODE_CLASS (TREE_CODE (rhs)))
|
||||
{
|
||||
case 'r':
|
||||
case '<':
|
||||
case '2':
|
||||
case 'b':
|
||||
case '1':
|
||||
case 'e':
|
||||
case 's':
|
||||
case 'x':
|
||||
{
|
||||
int lp;
|
||||
int max = first_rtl_op (TREE_CODE (rhs));
|
||||
for (lp = 0; lp < max; lp++)
|
||||
check_modify_expr (lhs, TREE_OPERAND (rhs, lp));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (identifier_name != NULL_TREE)
|
||||
{
|
||||
switch (TREE_CODE (lhs))
|
||||
{
|
||||
case ERROR_MARK:
|
||||
return;
|
||||
/* Perhaps this variable was incremented on the RHS. */
|
||||
case VAR_DECL:
|
||||
case PARM_DECL:
|
||||
if (TREE_CODE (rhs) != VAR_DECL && TREE_CODE (rhs) != PARM_DECL)
|
||||
if (DECL_NAME (lhs) == identifier_name)
|
||||
warning ("operation on `%s' may be undefined",
|
||||
IDENTIFIER_POINTER (DECL_NAME (lhs)));
|
||||
break;
|
||||
case PREDECREMENT_EXPR:
|
||||
case PREINCREMENT_EXPR:
|
||||
case POSTDECREMENT_EXPR:
|
||||
case POSTINCREMENT_EXPR:
|
||||
{
|
||||
tree var_decl = TREE_OPERAND (lhs, 0);
|
||||
if (TREE_CODE (var_decl) == VAR_DECL
|
||||
|| TREE_CODE (var_decl) == PARM_DECL)
|
||||
if (identifier_name == DECL_NAME (var_decl))
|
||||
warning ("operation on `%s' may be undefined",
|
||||
IDENTIFIER_POINTER (DECL_NAME (var_decl)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* To save duplicating tree traversal code swap args, and recurse. */
|
||||
check_modify_expr (rhs, lhs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Build an assignment expression of lvalue LHS from value RHS.
|
||||
MODIFYCODE is the code for a binary operator that we use
|
||||
|
@ -3969,6 +4096,11 @@ build_modify_expr (lhs, modifycode, rhs)
|
|||
if (TREE_CODE (newrhs) == ERROR_MARK)
|
||||
return error_mark_node;
|
||||
|
||||
if (warn_sequence_point)
|
||||
check_modify_expr (lhs, rhs);
|
||||
|
||||
/* Scan operands */
|
||||
|
||||
result = build (MODIFY_EXPR, lhstype, lhs, newrhs);
|
||||
TREE_SIDE_EFFECTS (result) = 1;
|
||||
|
||||
|
|
|
@ -1559,6 +1559,52 @@ the enclosing @code{if}. The resulting code would look like this:
|
|||
@}
|
||||
@end smallexample
|
||||
|
||||
@item -Wsequence-point
|
||||
Warn about code that may have undefined semantics because of violations
|
||||
of sequence point rules in the C standard.
|
||||
|
||||
The C standard defines the order in which expressions in a C program are
|
||||
evaluated in terms of @dfn{sequence points}, which represent a partial
|
||||
ordering between the execution of parts of the program: those executed
|
||||
before the sequence point, and those executed after it. These occur
|
||||
after the evaluation of a full expression (one which is not part of a
|
||||
larger expression), after the evaluation of the first operand of a
|
||||
@code{&&}, @code{||}, @code{? :} or @code{,} (comma) operator, before a
|
||||
function is called (but after the evaluation of its arguments and the
|
||||
expression denoting the called function), and in certain other places.
|
||||
Other than as expressed by the sequence point rules, the order of
|
||||
evaluation of subexpressions of an expression is not specified. All
|
||||
these rules describe only a partial order rather than a total order,
|
||||
since, for example, if two functions are called within one expression
|
||||
with no sequence point between them, the order in which the functions
|
||||
are called is not specified. However, the standards committee have
|
||||
ruled that function calls do not overlap.
|
||||
|
||||
It is not specified when between sequence points modifications to the
|
||||
values of objects take effect. Programs whose behavior depends on this
|
||||
have undefined behavior; the C standard specifies that ``Between the
|
||||
previous and next sequence point an object shall have its stored value
|
||||
modified at most once by the evaluation of an expression. Furthermore,
|
||||
the prior value shall be read only to determine the value to be
|
||||
stored.''. If a program breaks these rules, the results on any
|
||||
particular implementation are entirely unpredictable.
|
||||
|
||||
Examples of code with undefined behavior are @code{a = a++;}, @code{a[n]
|
||||
= b[n++]} and @code{a[i++] = i;}. Some more complicated cases are not
|
||||
diagnosed by this option, and it may give an occasional false positive
|
||||
result, but in general it has been found fairly effective at detecting
|
||||
this sort of problem in programs.
|
||||
|
||||
The present implementation of this option only works for C programs. A
|
||||
future implementation may also work for C++ programs.
|
||||
|
||||
There is some controversy over the precise meaning of the sequence point
|
||||
rules in subtle cases. Alternative formal definitions may be found in
|
||||
Clive Feather's ``Annex S''
|
||||
@uref{http://wwwold.dkuug.dk/JTC1/SC22/WG14/www/docs/n908.htm} and in
|
||||
Michael Norrish's thesis
|
||||
@uref{http://www.cl.cam.ac.uk/users/mn200/PhD/thesis-report.ps.gz}.
|
||||
|
||||
@item -Wreturn-type
|
||||
Warn whenever a function is defined with a return-type that defaults to
|
||||
@code{int}. Also warn about any @code{return} statement with no
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2000-10-11 Joseph S. Myers <jsm28@cam.ac.uk>
|
||||
|
||||
* gcc.dg/sequence-pt-1.c: New test.
|
||||
|
||||
2000-10-11 Geoff Keating <geoffk@cygnus.com>
|
||||
|
||||
* gcc.c-torture/execute/20001011-1.c: New testcase.
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* Test for sequence point warnings. */
|
||||
/* Origin: Michael Meeks in
|
||||
<URL:http://gcc.gnu.org/ml/gcc-patches/1998-06/msg00316.html>,
|
||||
adapted to a testcase by Joseph Myers <jsm28@cam.ac.uk>. */
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wsequence-point" } */
|
||||
|
||||
struct s
|
||||
{
|
||||
int a;
|
||||
};
|
||||
|
||||
extern int fn (int);
|
||||
extern int sprintf (char *, const char *, ...);
|
||||
|
||||
void
|
||||
foo (int a, int b, int n, int p, int *ptr, struct s *sptr,
|
||||
int *ap, int *bp, int **cp, char *ans)
|
||||
{
|
||||
int len;
|
||||
a = a++; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = --a; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = ++a + b; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = a-- + b; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = (a++ && 4); /* { dg-bogus "undefined" "bogus sequence point warning" { xfail *-*-* } } */
|
||||
ap[n] = bp[n++]; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
ap[--n] = bp[n]; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
ap[++n] = bp[--n]; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
cp[n][n] = cp[n][n]++; /* { dg-warning "undefined" "sequence point warning" { xfail *-*-* } } */
|
||||
cp[n][p] = cp[n][n++]; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
*ptr++ = (int)ptr++; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
sptr->a = sptr->a++; /* { dg-warning "undefined" "sequence point warning" { xfail *-*-* } } */
|
||||
sptr->a = (int)(sptr++); /* { dg-warning "undefined" "sequence point warning" } */
|
||||
len = sprintf (ans, "%d", len++); /* { dg-bogus "undefined" "bogus sequence point warning" { xfail *-*-* } } */
|
||||
*ptr++ = fn (*ptr); /* { dg-warning "undefined" "sequence point warning" { xfail *-*-* } } */
|
||||
a = b = a++; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
b = a = --b; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = 1 + (a = 1); /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = (a = b); /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = (a = b) + 1; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
a = (bp[a++] = b) + 1; /* { dg-warning "undefined" "sequence point warning" } */
|
||||
}
|
|
@ -1247,6 +1247,9 @@ documented_lang_options[] =
|
|||
{ "-Wno-nested-externs", "" },
|
||||
{ "-Wparentheses", "Warn about possible missing parentheses" },
|
||||
{ "-Wno-parentheses", "" },
|
||||
{ "-Wsequence-point",
|
||||
"Warn about possible violations of sequence point rules" },
|
||||
{ "-Wno-sequence-point", "" },
|
||||
{ "-Wpointer-arith", "Warn about function pointer arithmetic" },
|
||||
{ "-Wno-pointer-arith", "" },
|
||||
{ "-Wredundant-decls",
|
||||
|
|
Loading…
Reference in New Issue