Commit 16ef3acc by Joseph Myers Committed by Joseph Myers

re PR c++/772 (Statement expressions issues)

	PR c/772
	PR c/17913
	* c-tree.h (C_DECL_UNJUMPABLE_STMT_EXPR,
	C_DECL_UNDEFINABLE_STMT_EXPR, struct c_label_list, struct
	c_label_context, label_context_stack): New.
	* c-decl.c (define_label): Check for jumps into statement
	expressions.  Add label to list of defined labels.
	(start_function): Push context on label_context_stack.
	(finish_function): Pop context from label_context_stack.
	* c-typeck.c (label_context_stack): New.
	(c_finish_goto_label): Check for jumps into statement
	expressions.  Add label to list of jumped to labels.
	(struct c_switch): Add blocked_stmt_expr.
	(c_start_case): Initialize it.
	(do_case): Check it.
	(c_finish_case): Verify !blocked_stmt_expr.
	(c_begin_stmt_expr):  Push context on label_context_stack.
	Increment blocked_stmt_expr.  Mark labels jumped to from outside
	as undefinable.
	(c_finish_stmt_expr): December blocked_stmt_expr.  Mark labels
	defined in the statement expression and no longer jumpable to.
	Mark labels jumped to from just outside the statement expression
	as again definable.  Pop context from label_context_stack.
	* doc/extend.texi (Statement Exprs): Update.

objc:
	* objc-act.c (objc_start_function): Push context on
	label_context_stack.

testsuite:
	* gcc.dg/stmt-expr-label-1.c, gcc.dg/stmt-expr-label-2.c,
	gcc.dg/stmt-expr-label-3.c : New tests.
	* gcc.c-torture/execute/medce-2.c: Remove.

From-SVN: r97273
parent 1e55c0e2
2005-03-30 Joseph S. Myers <joseph@codesourcery.com> 2005-03-30 Joseph S. Myers <joseph@codesourcery.com>
PR c/772
PR c/17913
* c-tree.h (C_DECL_UNJUMPABLE_STMT_EXPR,
C_DECL_UNDEFINABLE_STMT_EXPR, struct c_label_list, struct
c_label_context, label_context_stack): New.
* c-decl.c (define_label): Check for jumps into statement
expressions. Add label to list of defined labels.
(start_function): Push context on label_context_stack.
(finish_function): Pop context from label_context_stack.
* c-typeck.c (label_context_stack): New.
(c_finish_goto_label): Check for jumps into statement
expressions. Add label to list of jumped to labels.
(struct c_switch): Add blocked_stmt_expr.
(c_start_case): Initialize it.
(do_case): Check it.
(c_finish_case): Verify !blocked_stmt_expr.
(c_begin_stmt_expr): Push context on label_context_stack.
Increment blocked_stmt_expr. Mark labels jumped to from outside
as undefinable.
(c_finish_stmt_expr): December blocked_stmt_expr. Mark labels
defined in the statement expression and no longer jumpable to.
Mark labels jumped to from just outside the statement expression
as again definable. Pop context from label_context_stack.
* doc/extend.texi (Statement Exprs): Update.
2005-03-30 Joseph S. Myers <joseph@codesourcery.com>
PR c/20368 PR c/20368
* c-decl.c (start_function): Check for old_decl being * c-decl.c (start_function): Check for old_decl being
error_mark_node. error_mark_node.
......
...@@ -2440,6 +2440,7 @@ define_label (location_t location, tree name) ...@@ -2440,6 +2440,7 @@ define_label (location_t location, tree name)
if there is a containing function with a declared label with if there is a containing function with a declared label with
the same name. */ the same name. */
tree label = I_LABEL_DECL (name); tree label = I_LABEL_DECL (name);
struct c_label_list *nlist;
if (label if (label
&& ((DECL_CONTEXT (label) == current_function_decl && ((DECL_CONTEXT (label) == current_function_decl
...@@ -2456,6 +2457,8 @@ define_label (location_t location, tree name) ...@@ -2456,6 +2457,8 @@ define_label (location_t location, tree name)
/* The label has been used or declared already in this function, /* The label has been used or declared already in this function,
but not defined. Update its location to point to this but not defined. Update its location to point to this
definition. */ definition. */
if (C_DECL_UNDEFINABLE_STMT_EXPR (label))
error ("%Jjump into statement expression", label);
DECL_SOURCE_LOCATION (label) = location; DECL_SOURCE_LOCATION (label) = location;
} }
else else
...@@ -2472,6 +2475,11 @@ define_label (location_t location, tree name) ...@@ -2472,6 +2475,11 @@ define_label (location_t location, tree name)
warning ("%Htraditional C lacks a separate namespace for labels, " warning ("%Htraditional C lacks a separate namespace for labels, "
"identifier %qE conflicts", &location, name); "identifier %qE conflicts", &location, name);
nlist = XOBNEW (&parser_obstack, struct c_label_list);
nlist->next = label_context_stack->labels_def;
nlist->label = label;
label_context_stack->labels_def = nlist;
/* Mark label as having been defined. */ /* Mark label as having been defined. */
DECL_INITIAL (label) = error_mark_node; DECL_INITIAL (label) = error_mark_node;
return label; return label;
...@@ -5631,6 +5639,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, ...@@ -5631,6 +5639,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
{ {
tree decl1, old_decl; tree decl1, old_decl;
tree restype, resdecl; tree restype, resdecl;
struct c_label_context *nstack;
current_function_returns_value = 0; /* Assume, until we see it does. */ current_function_returns_value = 0; /* Assume, until we see it does. */
current_function_returns_null = 0; current_function_returns_null = 0;
...@@ -5639,6 +5648,12 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, ...@@ -5639,6 +5648,12 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
current_extern_inline = 0; current_extern_inline = 0;
c_switch_stack = NULL; c_switch_stack = NULL;
nstack = XOBNEW (&parser_obstack, struct c_label_context);
nstack->labels_def = NULL;
nstack->labels_used = NULL;
nstack->next = label_context_stack;
label_context_stack = nstack;
/* Indicate no valid break/continue context by setting these variables /* Indicate no valid break/continue context by setting these variables
to some non-null, non-label value. We'll notice and emit the proper to some non-null, non-label value. We'll notice and emit the proper
error message in c_finish_bc_stmt. */ error message in c_finish_bc_stmt. */
...@@ -6220,6 +6235,8 @@ finish_function (void) ...@@ -6220,6 +6235,8 @@ finish_function (void)
{ {
tree fndecl = current_function_decl; tree fndecl = current_function_decl;
label_context_stack = label_context_stack->next;
if (TREE_CODE (fndecl) == FUNCTION_DECL if (TREE_CODE (fndecl) == FUNCTION_DECL
&& targetm.calls.promote_prototypes (TREE_TYPE (fndecl))) && targetm.calls.promote_prototypes (TREE_TYPE (fndecl)))
{ {
......
...@@ -106,6 +106,17 @@ struct lang_type GTY(()) ...@@ -106,6 +106,17 @@ struct lang_type GTY(())
sizeof and typeof it is set for other function decls as well. */ sizeof and typeof it is set for other function decls as well. */
#define C_DECL_USED(EXP) DECL_LANG_FLAG_5 (EXP) #define C_DECL_USED(EXP) DECL_LANG_FLAG_5 (EXP)
/* Record whether a label was defined in a statement expression which
has finished and so can no longer be jumped to. */
#define C_DECL_UNJUMPABLE_STMT_EXPR(EXP) \
DECL_LANG_FLAG_6 (LABEL_DECL_CHECK (EXP))
/* Record whether a label was the subject of a goto from outside the
current level of statement expression nesting and so cannot be
defined right now. */
#define C_DECL_UNDEFINABLE_STMT_EXPR(EXP) \
DECL_LANG_FLAG_7 (LABEL_DECL_CHECK (EXP))
/* Nonzero for a decl which either doesn't exist or isn't a prototype. /* Nonzero for a decl which either doesn't exist or isn't a prototype.
N.B. Could be simplified if all built-in decls had complete prototypes N.B. Could be simplified if all built-in decls had complete prototypes
(but this is presently difficult because some of them need FILE*). */ (but this is presently difficult because some of them need FILE*). */
...@@ -350,6 +361,27 @@ struct language_function GTY(()) ...@@ -350,6 +361,27 @@ struct language_function GTY(())
int extern_inline; int extern_inline;
}; };
/* Save lists of labels used or defined in particular statement
expression contexts. Allocated on the parser obstack. */
struct c_label_list
{
/* The label at the head of the list. */
tree label;
/* The rest of the list. */
struct c_label_list *next;
};
struct c_label_context
{
/* The labels defined at this level of nesting. */
struct c_label_list *labels_def;
/* The labels used at this level of nesting. */
struct c_label_list *labels_used;
/* The next outermost context. */
struct c_label_context *next;
};
/* in c-parser.c */ /* in c-parser.c */
extern void c_parse_init (void); extern void c_parse_init (void);
...@@ -452,6 +484,7 @@ extern int in_sizeof; ...@@ -452,6 +484,7 @@ extern int in_sizeof;
extern int in_typeof; extern int in_typeof;
extern struct c_switch *c_switch_stack; extern struct c_switch *c_switch_stack;
extern struct c_label_context *label_context_stack;
extern tree require_complete_type (tree); extern tree require_complete_type (tree);
extern int same_translation_unit_p (tree, tree); extern int same_translation_unit_p (tree, tree);
......
...@@ -64,6 +64,8 @@ int in_sizeof; ...@@ -64,6 +64,8 @@ int in_sizeof;
/* The level of nesting inside "typeof". */ /* The level of nesting inside "typeof". */
int in_typeof; int in_typeof;
struct c_label_context *label_context_stack;
/* Nonzero if we've already printed a "missing braces around initializer" /* Nonzero if we've already printed a "missing braces around initializer"
message within this initializer. */ message within this initializer. */
static int missing_braces_mentioned; static int missing_braces_mentioned;
...@@ -6478,6 +6480,23 @@ c_finish_goto_label (tree label) ...@@ -6478,6 +6480,23 @@ c_finish_goto_label (tree label)
if (!decl) if (!decl)
return NULL_TREE; return NULL_TREE;
if (C_DECL_UNJUMPABLE_STMT_EXPR (decl))
{
error ("jump into statement expression");
return NULL_TREE;
}
if (!C_DECL_UNDEFINABLE_STMT_EXPR (decl))
{
/* No jump from outside this statement expression context, so
record that there is a jump from within this context. */
struct c_label_list *nlist;
nlist = XOBNEW (&parser_obstack, struct c_label_list);
nlist->next = label_context_stack->labels_used;
nlist->label = decl;
label_context_stack->labels_used = nlist;
}
TREE_USED (decl) = 1; TREE_USED (decl) = 1;
return add_stmt (build1 (GOTO_EXPR, void_type_node, decl)); return add_stmt (build1 (GOTO_EXPR, void_type_node, decl));
} }
...@@ -6604,6 +6623,11 @@ struct c_switch { ...@@ -6604,6 +6623,11 @@ struct c_switch {
of the GNU case range extension. */ of the GNU case range extension. */
splay_tree cases; splay_tree cases;
/* Number of nested statement expressions within this switch
statement; if nonzero, case and default labels may not
appear. */
unsigned int blocked_stmt_expr;
/* The next node on the stack. */ /* The next node on the stack. */
struct c_switch *next; struct c_switch *next;
}; };
...@@ -6658,6 +6682,7 @@ c_start_case (tree exp) ...@@ -6658,6 +6682,7 @@ c_start_case (tree exp)
cs->switch_stmt = build_stmt (SWITCH_STMT, exp, NULL_TREE, orig_type); cs->switch_stmt = build_stmt (SWITCH_STMT, exp, NULL_TREE, orig_type);
cs->orig_type = orig_type; cs->orig_type = orig_type;
cs->cases = splay_tree_new (case_compare, NULL, NULL); cs->cases = splay_tree_new (case_compare, NULL, NULL);
cs->blocked_stmt_expr = 0;
cs->next = c_switch_stack; cs->next = c_switch_stack;
c_switch_stack = cs; c_switch_stack = cs;
...@@ -6671,7 +6696,7 @@ do_case (tree low_value, tree high_value) ...@@ -6671,7 +6696,7 @@ do_case (tree low_value, tree high_value)
{ {
tree label = NULL_TREE; tree label = NULL_TREE;
if (c_switch_stack) if (c_switch_stack && !c_switch_stack->blocked_stmt_expr)
{ {
label = c_add_case_label (c_switch_stack->cases, label = c_add_case_label (c_switch_stack->cases,
SWITCH_STMT_COND (c_switch_stack->switch_stmt), SWITCH_STMT_COND (c_switch_stack->switch_stmt),
...@@ -6680,6 +6705,15 @@ do_case (tree low_value, tree high_value) ...@@ -6680,6 +6705,15 @@ do_case (tree low_value, tree high_value)
if (label == error_mark_node) if (label == error_mark_node)
label = NULL_TREE; label = NULL_TREE;
} }
else if (c_switch_stack && c_switch_stack->blocked_stmt_expr)
{
if (low_value)
error ("case label in statement expression not containing "
"enclosing switch statement");
else
error ("%<default%> label in statement expression not containing "
"enclosing switch statement");
}
else if (low_value) else if (low_value)
error ("case label not within a switch statement"); error ("case label not within a switch statement");
else else
...@@ -6697,6 +6731,8 @@ c_finish_case (tree body) ...@@ -6697,6 +6731,8 @@ c_finish_case (tree body)
SWITCH_STMT_BODY (cs->switch_stmt) = body; SWITCH_STMT_BODY (cs->switch_stmt) = body;
gcc_assert (!cs->blocked_stmt_expr);
/* Emit warnings as needed. */ /* Emit warnings as needed. */
c_do_switch_warnings (cs->cases, cs->switch_stmt); c_do_switch_warnings (cs->cases, cs->switch_stmt);
...@@ -6963,12 +6999,30 @@ tree ...@@ -6963,12 +6999,30 @@ tree
c_begin_stmt_expr (void) c_begin_stmt_expr (void)
{ {
tree ret; tree ret;
struct c_label_context *nstack;
struct c_label_list *glist;
/* We must force a BLOCK for this level so that, if it is not expanded /* We must force a BLOCK for this level so that, if it is not expanded
later, there is a way to turn off the entire subtree of blocks that later, there is a way to turn off the entire subtree of blocks that
are contained in it. */ are contained in it. */
keep_next_level (); keep_next_level ();
ret = c_begin_compound_stmt (true); ret = c_begin_compound_stmt (true);
if (c_switch_stack)
{
c_switch_stack->blocked_stmt_expr++;
gcc_assert (c_switch_stack->blocked_stmt_expr != 0);
}
for (glist = label_context_stack->labels_used;
glist != NULL;
glist = glist->next)
{
C_DECL_UNDEFINABLE_STMT_EXPR (glist->label) = 1;
}
nstack = XOBNEW (&parser_obstack, struct c_label_context);
nstack->labels_def = NULL;
nstack->labels_used = NULL;
nstack->next = label_context_stack;
label_context_stack = nstack;
/* Mark the current statement list as belonging to a statement list. */ /* Mark the current statement list as belonging to a statement list. */
STATEMENT_LIST_STMT_EXPR (ret) = 1; STATEMENT_LIST_STMT_EXPR (ret) = 1;
...@@ -6981,8 +7035,36 @@ c_finish_stmt_expr (tree body) ...@@ -6981,8 +7035,36 @@ c_finish_stmt_expr (tree body)
{ {
tree last, type, tmp, val; tree last, type, tmp, val;
tree *last_p; tree *last_p;
struct c_label_list *dlist, *glist, *glist_prev = NULL;
body = c_end_compound_stmt (body, true); body = c_end_compound_stmt (body, true);
if (c_switch_stack)
{
gcc_assert (c_switch_stack->blocked_stmt_expr != 0);
c_switch_stack->blocked_stmt_expr--;
}
/* It is no longer possible to jump to labels defined within this
statement expression. */
for (dlist = label_context_stack->labels_def;
dlist != NULL;
dlist = dlist->next)
{
C_DECL_UNJUMPABLE_STMT_EXPR (dlist->label) = 1;
}
/* It is again possible to define labels with a goto just outside
this statement expression. */
for (glist = label_context_stack->next->labels_used;
glist != NULL;
glist = glist->next)
{
C_DECL_UNDEFINABLE_STMT_EXPR (glist->label) = 0;
glist_prev = glist;
}
if (glist_prev != NULL)
glist_prev->next = label_context_stack->labels_used;
else
label_context_stack->next->labels_used = label_context_stack->labels_used;
label_context_stack = label_context_stack->next;
/* Locate the last statement in BODY. See c_end_compound_stmt /* Locate the last statement in BODY. See c_end_compound_stmt
about always returning a BIND_EXPR. */ about always returning a BIND_EXPR. */
......
...@@ -187,6 +187,29 @@ work with C++. (Note that some versions of the GNU C Library contained ...@@ -187,6 +187,29 @@ work with C++. (Note that some versions of the GNU C Library contained
header files using statement-expression that lead to precisely this header files using statement-expression that lead to precisely this
bug.) bug.)
Jumping into a statement expression with @code{goto} or using a
@code{switch} statement outside the statement expression with a
@code{case} or @code{default} label inside the statement expression is
not permitted. Jumping into a statement expression with a computed
@code{goto} (@pxref{Labels as Values}) yields undefined behavior.
Jumping out of a statement expression is permitted, but if the
statement expression is part of a larger expression then it is
unspecified which other subexpressions of that expression have been
evaluated except where the language definition requires certain
subexpressions to be evaluated before or after the statement
expression. In any case, as with a function call the evaluation of a
statement expression is not interleaved with the evaluation of other
parts of the containing expression. For example,
@smallexample
foo (), ((@{ bar1 (); goto a; 0; @}) + bar2 ()), baz();
@end smallexample
@noindent
will call @code{foo} and @code{bar1} and will not call @code{baz} but
may or may not call @code{bar2}. If @code{bar2} is called, it will be
called after @code{foo} and before @code{bar1}
@node Local Labels @node Local Labels
@section Locally Declared Labels @section Locally Declared Labels
@cindex local labels @cindex local labels
......
2005-03-30 Joseph S. Myers <joseph@codesourcery.com>
PR c/772
PR c/17913
* objc-act.c (objc_start_function): Push context on
label_context_stack.
2005-03-23 Joseph S. Myers <joseph@codesourcery.com> 2005-03-23 Joseph S. Myers <joseph@codesourcery.com>
* objc-act.c (next_sjlj_build_enter_and_setjmp, * objc-act.c (next_sjlj_build_enter_and_setjmp,
......
...@@ -7666,6 +7666,12 @@ objc_start_function (tree name, tree type, tree attrs, ...@@ -7666,6 +7666,12 @@ objc_start_function (tree name, tree type, tree attrs,
cplus_decl_attributes (&fndecl, attrs, 0); cplus_decl_attributes (&fndecl, attrs, 0);
start_preparsed_function (fndecl, attrs, /*flags=*/SF_DEFAULT); start_preparsed_function (fndecl, attrs, /*flags=*/SF_DEFAULT);
#else #else
struct c_label_context *nstack;
nstack = XOBNEW (&parser_obstack, struct c_label_context);
nstack->labels_def = NULL;
nstack->labels_used = NULL;
nstack->next = label_context_stack;
label_context_stack = nstack;
decl_attributes (&fndecl, attrs, 0); decl_attributes (&fndecl, attrs, 0);
announce_function (fndecl); announce_function (fndecl);
DECL_INITIAL (fndecl) = error_mark_node; DECL_INITIAL (fndecl) = error_mark_node;
......
2005-03-30 Joseph S. Myers <joseph@codesourcery.com> 2005-03-30 Joseph S. Myers <joseph@codesourcery.com>
PR c/772
PR c/17913
* gcc.dg/stmt-expr-label-1.c, gcc.dg/stmt-expr-label-2.c,
gcc.dg/stmt-expr-label-3.c : New tests.
* gcc.c-torture/execute/medce-2.c: Remove.
2005-03-30 Joseph S. Myers <joseph@codesourcery.com>
PR c/20368 PR c/20368
* gcc.dg/pr20368-1.c, gcc.dg/pr20368-2.c, gcc.dg/pr20368-3.c: New * gcc.dg/pr20368-1.c, gcc.dg/pr20368-2.c, gcc.dg/pr20368-3.c: New
tests. tests.
......
extern void abort ();
static int ok = 0;
int bar(void)
{
ok |= 1;
return 1;
}
void bat(void)
{
ok |= 2;
}
void baz(void)
{
ok |= 4;
}
void foo()
{
goto lab;
if (0)
{
if (({lab: bar();}))
bat ();
else
baz ();
}
}
int main()
{
foo();
if (ok != 3)
abort ();
return 0;
}
/* Test for labels in statement expressions: bugs 772 and 17913.
switch statements must not jump into statement expressions. */
/* Origin: Joseph Myers <joseph@codesourcery.com> */
/* { dg-do compile } */
/* { dg-options "" } */
void
f (int a)
{
switch (a)
{
case 0:
case 1:
({
case 2: /* { dg-error "error: case label in statement expression not containing enclosing switch statement" } */
default: /* { dg-error "error: 'default' label in statement expression not containing enclosing switch statement" } */
switch (a)
{
case 3:
default:
;
}
0;
});
}
}
/* Test for labels in statement expressions: bugs 772 and 17913.
Test the particular case of bug 17913. */
/* Origin: Joseph Myers <joseph@codesourcery.com> */
/* { dg-do compile } */
/* { dg-options "-O2" } */
void f(void) { 1 ? 1 : ({ a : 1; 1; }); goto a; } /* { dg-error "error: jump into statement expression" } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment