diff --git a/ast.h b/ast.h index 2643413..936a587 100644 --- a/ast.h +++ b/ast.h @@ -73,6 +73,8 @@ enum { */ AST_FLAG_ARRAY_INIT = 1 << 10, + AST_FLAG_FINAL_DECL = 1 << 11, + AST_FLAG_LAST, AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN) }; diff --git a/parser.c b/parser.c index 0e43f50..f531161 100644 --- a/parser.c +++ b/parser.c @@ -2862,6 +2862,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * return false; } } + else if (!strcmp(parser_tokval(parser), "final")) { + flags |= AST_FLAG_FINAL_DECL; + if (!parser_next(parser) || parser->tok != TOKEN_ATTRIBUTE_CLOSE) { + parseerror(parser, "`final` attribute has no parameters, expected `]]`"); + *cvq = CV_WRONG; + return false; + } + } else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) { flags |= AST_FLAG_ALIAS; *message = NULL; @@ -2966,6 +2974,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool * had_var = true; else if (!strcmp(parser_tokval(parser), "noref")) had_noref = true; + else if (!strcmp(parser_tokval(parser), "final")) + flags |= AST_FLAG_FINAL_DECL; else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) { return false; } @@ -5289,6 +5299,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield retval = false; goto cleanup; } + if (old->flags & AST_FLAG_FINAL_DECL) { + parseerror(parser, "cannot redeclare variable `%s`, declared final here: %s:%i", + var->name, ast_ctx(old).file, ast_ctx(old).line); + retval = false; + goto cleanup; + } proto = (ast_value*)old; if (!ast_istype(old, ast_value)) { parseerror(parser, "internal error: not an ast_value"); @@ -5302,6 +5318,11 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield goto cleanup; } proto->expression.flags |= var->expression.flags; + /* copy the context for finals, + * so the error can show where it was actually made 'final' + */ + if (proto->expression.flags & AST_FLAG_FINAL_DECL) + ast_ctx(old) = ast_ctx(var); ast_delete(var); var = proto; } diff --git a/tests/final.qc b/tests/final.qc new file mode 100644 index 0000000..e55599a --- /dev/null +++ b/tests/final.qc @@ -0,0 +1,3 @@ +float here; +final float here; +float here; diff --git a/tests/final.tmpl b/tests/final.tmpl new file mode 100644 index 0000000..b754123 --- /dev/null +++ b/tests/final.tmpl @@ -0,0 +1,5 @@ +I: final.qc +D: final keyword +T: -fail +C: -std=gmqcc +F: -no-defs