From 268d3b8ef45e1cce442b7ffe534715a5510cce67 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 13 Apr 2020 14:52:26 -0700 Subject: [PATCH 3/3] Label scoping is special, and expression statements are their own scope. A label may be used before being defined, and you don't have to pre-declare it. And it may be used in an inner scope from the declaration, but not the other way around. We work around this by simply making the rule be that as we close the scope of a label symbol, instead of killing the symbol, we will transfer that symbol to the outer label scope if it hasn't been defined yet. But a label that has been defined can never transfer out. And when defining a label, test that there wasn't some earlier use of it in another (outer) scope. --- expression.c | 4 ++-- parse.c | 11 +++++++++++ scope.c | 36 +++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/expression.c b/expression.c index 40141a5e..08650724 100644 --- a/expression.c +++ b/expression.c @@ -71,9 +71,9 @@ struct token *parens_expression(struct token *token, struct expression **expr, c struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND); *expr = e; e->statement = stmt; - start_block_scope(); + start_label_scope(); token = compound_statement(token->next, stmt); - end_block_scope(); + end_label_scope(); token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); diff --git a/parse.c b/parse.c index 64aaf07b..8e238f59 100644 --- a/parse.c +++ b/parse.c @@ -2539,6 +2539,17 @@ static struct token *statement(struct token *token, struct statement **tree) // skip the label to avoid multiple definitions return statement(token, tree); } + /* + * If the scope of the label symbol is different + * from the current label scope, that means that + * it must have been used at an outer scope. + * + * That's not ok. + */ + if (s->scope != label_scope) { + sparse_error(stmt->pos, "label '%s' used outside label expression", show_ident(s->ident)); + sparse_error(s->pos, "invalid use here"); + } stmt->type = STMT_LABEL; stmt->label_identifier = s; s->stmt = stmt; diff --git a/scope.c b/scope.c index 8df9a5e0..4b0f7947 100644 --- a/scope.c +++ b/scope.c @@ -136,10 +136,44 @@ void end_block_scope(void) end_scope(&block_scope); } +/* + * Label scope is special. + * + * The scope of a label is limited to its definition, but + * a label can be used in an inner scope before it's defined. + * + * So when we end the label scope, we move all the non-defined + * labels out by one level. + */ void end_label_scope(void) { + struct symbol *sym; + struct symbol_list *labels = label_scope->symbols; + + label_scope->symbols = NULL; + label_scope = label_scope->next; + FOR_EACH_PTR(labels, sym) { + + /* + * Do we have a definition for it? + * Ok, we're done with this label + */ + if (sym->stmt) { + remove_symbol_scope(sym); + continue; + } + + if (label_scope == &builtin_scope) { + warning(sym->pos, "Unused label '%s'", sym->ident->name); + remove_symbol_scope(sym); + continue; + } + + /* Re-bind the symbol to the parent scope, we'll try again */ + bind_scope(sym, label_scope); + } END_FOR_EACH_PTR(sym); + end_scope(&block_scope); - end_scope(&label_scope); } int is_outer_scope(struct scope *scope) -- 2.24.0.158.g3fed155289