@@ -157,6 +157,9 @@ enum machine_mode c_default_pointer_mode = VOIDmode;
/* If non-zero, implicit "omp declare target" attribute is added into the
attribute lists. */
int current_omp_declare_target_attribute;
+
+/* Remember how many statement expressions we've entered. */
+static int stmt_expr_depth;
/* Each c_binding structure describes one binding of an identifier to
a decl. All the decls in a scope - irrespective of namespace - are
@@ -313,6 +316,8 @@ struct GTY(()) c_label_vars {
goto statements seen before the label was defined, so that we can
issue appropriate warnings for them. */
vec<c_goto_bindings_p, va_gc> *gotos;
+ /* Whether this label is referenced outside of statement expression. */
+ bool seen_outside_stmt_expr;
};
/* Each c_scope structure describes the complete contents of one
@@ -1360,6 +1365,16 @@ c_bindings_start_stmt_expr (struct c_spot_bindings* switch_bindings)
{
struct c_scope *scope;
+ /* We entered a statement expression. */
+ stmt_expr_depth++;
+
+ if (stmt_expr_depth == 1)
+ /* Remember which labels are declared outside of statement exprs. */
+ for (struct c_binding *b = current_function_scope->bindings;
+ b != NULL; b = b->prev)
+ if (TREE_CODE (b->decl) == LABEL_DECL)
+ b->u.label->seen_outside_stmt_expr = true;
+
for (scope = current_scope; scope != NULL; scope = scope->outer)
{
struct c_binding *b;
@@ -1393,6 +1408,9 @@ c_bindings_end_stmt_expr (struct c_spot_bindings *switch_bindings)
{
struct c_scope *scope;
+ /* We're leaving a statement expression. */
+ stmt_expr_depth--;
+
for (scope = current_scope; scope != NULL; scope = scope->outer)
{
struct c_binding *b;
@@ -3074,6 +3092,7 @@ make_label (location_t location, tree name, bool defining,
set_spot_bindings (&label_vars->label_bindings, defining);
label_vars->decls_in_scope = make_tree_vector ();
label_vars->gotos = NULL;
+ label_vars->seen_outside_stmt_expr = false;
*p_label_vars = label_vars;
return label;
@@ -3228,11 +3247,12 @@ declare_label (tree name)
/* When we define a label, issue any appropriate warnings if there are
any gotos earlier in the function which jump to this label. */
-static void
+static bool
check_earlier_gotos (tree label, struct c_label_vars* label_vars)
{
unsigned int ix;
struct c_goto_bindings *g;
+ bool error_p = false;
FOR_EACH_VEC_SAFE_ELT (label_vars->gotos, ix, g)
{
@@ -3276,6 +3296,7 @@ check_earlier_gotos (tree label, struct c_label_vars* label_vars)
if (g->goto_bindings.stmt_exprs > 0)
{
+ error_p = true;
error_at (g->loc, "jump into statement expression");
inform (DECL_SOURCE_LOCATION (label), "label %qD defined here",
label);
@@ -3286,6 +3307,7 @@ check_earlier_gotos (tree label, struct c_label_vars* label_vars)
subsequent gotos to this label when we see them. */
vec_safe_truncate (label_vars->gotos, 0);
label_vars->gotos = NULL;
+ return error_p;
}
/* Define a label, specifying the location in the source file.
@@ -3323,7 +3345,20 @@ define_label (location_t location, tree name)
/* Issue warnings as required about any goto statements from
earlier in the function. */
- check_earlier_gotos (label, label_vars);
+ bool gave_error = check_earlier_gotos (label, label_vars);
+
+ /* If we're defining a label in a statement expression,
+ check that the declaration wasn't outside of this statement
+ expression. */
+ if (STATEMENT_LIST_STMT_EXPR (cur_stmt_list)
+ && label_vars->seen_outside_stmt_expr
+ && ! gave_error)
+ {
+ error_at (location, "taking address of label %qD defined in "
+ "statement expression", label);
+ DECL_INITIAL (label) = error_mark_node;
+ return 0;
+ }
}
else
{
@@ -206,6 +206,8 @@ Jumping into a statement expression with @code{goto} or using 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}) has undefined behavior.
+Taking the address of a label defined inside of a statement
+expression from outside of the statement expression is invalid.
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
@@ -2,6 +2,6 @@
void f (void)
{
void *p = &&a;
- 1 ? 1 : ({ a : 1; });
+ 1 ? 1 : ({ a : 1; }); /* { dg-error "taking address of label" } */
goto *p;
}
@@ -0,0 +1,104 @@
+/* PR c/51088 */
+/* { dg-do compile } */
+/* { dg-options "-std=c99" } */
+
+void
+fn1 (void)
+{
+ void *L = &&A;
+ ({ A:0; }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn2 (void)
+{
+ void *L = &&A;
+ ({ void *p = &&A; A:0; }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn3 (void)
+{
+ ({ void *p = &&A; A:0; });
+}
+
+void
+fn4 (void)
+{
+ {
+ {
+ {
+ void *L = &&A;
+ ({ A:0; }); /* { dg-error "taking address of label" } */
+ }
+ }
+ }
+}
+
+void
+fn5 (void)
+{
+ void *L = &&A;
+ ({ ({ ({ ({ A:0; }); }); }); }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn6 (void)
+{
+ void *L = &&A;
+ {
+ A:;
+ }
+}
+
+void
+fn7 (void)
+{
+ void *L = &&A;
+ {
+ ({ A:0; }); /* { dg-error "taking address of label" } */
+ }
+}
+
+void
+fn8 (void)
+{
+ ({ void *p = &&A; ({ A:0; }); });
+}
+
+void
+fn9 (void)
+{
+ &&A;
+ ({ ({ A:0; }); }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn10 (void)
+{
+ &&A;
+ ({ ({ 0; }); });
+ ({ ({ A:0; }); }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn11 (void)
+{
+ goto A;
+ ({ ({ &&A; 0; }); });
+ A:;
+}
+
+void
+fn12 (void)
+{
+ ({ &&A; });
+ ({ A:0; }); /* { dg-error "taking address of label" } */
+}
+
+void
+fn13 (void)
+{
+ ({ ({ ({ &&A; }); }); });
+ ({ ({ ({ A:0; }); }); }); /* { dg-error "taking address of label" } */
+}