On Fri, Mar 28, 2014 at 02:44:21PM +, Joseph S. Myers wrote:
On Fri, 28 Mar 2014, Marek Polacek wrote:
PR51088 contains some Really Bizzare code. We should tell users
not to do any shenanigans like that.
Ok for trunk?
I don't think a doc patch resolves this bug. The compiler should never
generate code with an undefined reference to a local label like that;
either the code should get a compile-time error (that's what I suggest),
or it should generate output that links but has undefined behavior at
runtime.
Ok, with this patch the compiler should issue an error if someone's
trying to take an address of a label defined in a statement expression
outside of that statement expression.
I admit this was very tricky; I had to completely revamp the patch
several times, this one is the least disrupting and simplest one
I could come up with. It works by marking labels that are declared
outside of stmt expr while we're entering a stmt expr (but we mustn't
do this for nested stmt exprs). If we're then defining the label in
stmt expr and it was referenced outside of this stmt expr, raise an error.
This patch doesn't catch cases like ({ A:0; }); A;, in that case the
behavior is just undefined.
Does this approach make sense?
Regtested/bootstrapped on x86_64-linux. I don't think it's stage4 material,
so ok for next stage1?
2014-04-08 Marek Polacek pola...@redhat.com
PR c/51088
* c-decl.c (stmt_expr_depth): New variable.
(struct c_label_vars): Add seen_outside_stmt_expr variable.
(c_bindings_start_stmt_expr): Bump stmt_expr_depth. Mark labels
declared outside of statement expressions.
(c_bindings_end_stmt_expr): Decrement stmt_expr_depth.
(make_label): Set seen_outside_stmt_expr.
(check_earlier_gotos): Return true if error was issued.
(define_label): Give error if taking an address of a label defined
in statement expression outside of the statement expression.
* doc/extend.texi (Statement Exprs): Add note about taking
addresses of labels inside of statement expressions.
* gcc.c-torture/compile/pr17913.c (f): Add dg-error.
* gcc.dg/pr51088.c: New test.
diff --git gcc/c/c-decl.c gcc/c/c-decl.c
index df84980..15663ae 100644
--- gcc/c/c-decl.c
+++ gcc/c/c-decl.c
@@ -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. */
vecc_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,