diff --git a/tcc.h b/tcc.h index 82238ce0..a4506f41 100644 --- a/tcc.h +++ b/tcc.h @@ -575,6 +575,8 @@ typedef struct DLLReference { #define LABEL_DEFINED 0 /* label is defined */ #define LABEL_FORWARD 1 /* label is forward defined */ #define LABEL_DECLARED 2 /* label is declared but never used */ +#define LABEL_GONE 3 /* label isn't in scope, but not yet popped + from local_label_stack (stmt exprs) */ /* type_decl() types */ #define TYPE_ABSTRACT 1 /* type without variable */ diff --git a/tccpp.c b/tccpp.c index e70c2bb2..2802d8eb 100644 --- a/tccpp.c +++ b/tccpp.c @@ -1402,9 +1402,12 @@ ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep) } } /* remove label */ - table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; + if (s->r != LABEL_GONE) + table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; if (!keep) sym_free(s); + else + s->r = LABEL_GONE; } if (!keep) *ptop = slast; diff --git a/tests/tcctest.c b/tests/tcctest.c index 0f445f14..40bf3fde 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -3139,6 +3139,22 @@ void statement_expr_test(void) /* Test that we can give out addresses of local labels. */ consume_ulong(({ __label__ __here; __here: (unsigned long)&&__here; })); + + /* Test interaction between local and global label stacks and the + need to defer popping symbol from them when within statement + expressions. Note how the labels are both named LBL. */ + i = 0; + ({ + { + __label__ LBL; + LBL: if (i++ == 0) goto LBL; + } + /* jump to a classical label out of an expr-stmt that had previously + overshadowed that classical label */ + goto LBL; + }); + LBL: + printf("stmtexpr: %d should be 2\n", i); } void local_label_test(void)