adding 'final'

This commit is contained in:
Wolfgang Bumiller 2013-12-06 22:41:15 +01:00
parent 072bff44e6
commit 50f905b821
4 changed files with 31 additions and 0 deletions

2
ast.h
View file

@ -73,6 +73,8 @@ enum {
*/ */
AST_FLAG_ARRAY_INIT = 1 << 10, AST_FLAG_ARRAY_INIT = 1 << 10,
AST_FLAG_FINAL_DECL = 1 << 11,
AST_FLAG_LAST, AST_FLAG_LAST,
AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN) AST_FLAG_TYPE_MASK = (AST_FLAG_VARIADIC | AST_FLAG_NORETURN)
}; };

View file

@ -2862,6 +2862,14 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
return false; 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)) { else if (!strcmp(parser_tokval(parser), "alias") && !(flags & AST_FLAG_ALIAS)) {
flags |= AST_FLAG_ALIAS; flags |= AST_FLAG_ALIAS;
*message = NULL; *message = NULL;
@ -2966,6 +2974,8 @@ static bool parse_qualifiers(parser_t *parser, bool with_local, int *cvq, bool *
had_var = true; had_var = true;
else if (!strcmp(parser_tokval(parser), "noref")) else if (!strcmp(parser_tokval(parser), "noref"))
had_noref = true; 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) { else if (!had_const && !had_var && !had_noref && !had_attrib && !had_static && !flags) {
return false; return false;
} }
@ -5289,6 +5299,12 @@ static bool parse_variable(parser_t *parser, ast_block *localblock, bool nofield
retval = false; retval = false;
goto cleanup; 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; proto = (ast_value*)old;
if (!ast_istype(old, ast_value)) { if (!ast_istype(old, ast_value)) {
parseerror(parser, "internal error: not an 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; goto cleanup;
} }
proto->expression.flags |= var->expression.flags; 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); ast_delete(var);
var = proto; var = proto;
} }

3
tests/final.qc Normal file
View file

@ -0,0 +1,3 @@
float here;
final float here;
float here;

5
tests/final.tmpl Normal file
View file

@ -0,0 +1,5 @@
I: final.qc
D: final keyword
T: -fail
C: -std=gmqcc
F: -no-defs