diff --git a/analyser/module_analyser_expr.c2 b/analyser/module_analyser_expr.c2 index da0bdd0d8..1486b20fa 100644 --- a/analyser/module_analyser_expr.c2 +++ b/analyser/module_analyser_expr.c2 @@ -237,7 +237,7 @@ fn IdentifierKind Analyser.setExprFlags(Analyser* ma, Expr** e_ptr, Decl* d) { break; case Variable: VarDecl* vd = (VarDecl*)d; - if (vd.isGlobal() || vd.hasLocalQualifier()) e.setCtc(); + if (vd.isGlobal() || vd.isStatic()) e.setCtc(); e.setLValue(); const Expr* init = vd.getInit(); QualType t = d.getType(); diff --git a/analyser/module_analyser_stmt.c2 b/analyser/module_analyser_stmt.c2 index fdcf6faae..f9f76f1d3 100644 --- a/analyser/module_analyser_stmt.c2 +++ b/analyser/module_analyser_stmt.c2 @@ -375,7 +375,7 @@ fn QualType Analyser.analyseDecl(Analyser* ma, VarDecl* vd) { bool has_init_call = vd.hasInitCall(); if (!has_init_call && initExpr) { ma.analyseInitExpr(initExpr, res, vd.getAssignLoc(), false, false); - if (vd.hasLocalQualifier()) { + if (vd.isStatic()) { Expr* e = vd.getInit(); if (!e.isCtc()) { ma.errorRange(e.getLoc(), e.getRange(), "initializer element is not a compile-time constant"); @@ -550,7 +550,7 @@ fn void Analyser.checkReturnAddrOfLocal(Analyser* ma, Expr* arg) { if (!d.isVarDecl()) return; VarDecl* vd = (VarDecl*)d; - if ((vd.isLocal() && !vd.hasLocalQualifier()) || vd.isParameter()) { + if ((vd.isLocal() && !vd.isStatic()) || vd.isParameter()) { ma.error(arg.getLoc(), "function returns address of local variable"); } } diff --git a/ast/ast_evaluator.c2 b/ast/ast_evaluator.c2 index 10a190066..4f722c343 100644 --- a/ast/ast_evaluator.c2 +++ b/ast/ast_evaluator.c2 @@ -326,7 +326,7 @@ fn Value Evaluator.get_decl_value(Evaluator* eval, const Decl* d) { return eval.get_value(initval); case LocalVar: // accept const static local variables if (vd.getType().isConst()) goto eval_init; - if (vd.hasLocalQualifier()) break; + if (vd.isStatic()) break; fallthrough; case FunctionParam: u32 off = vd.getOffset(); @@ -371,7 +371,7 @@ fn Value Evaluator.set_decl_value(Evaluator* eval, const Decl* d, Value v) { case GlobalVar: break; case LocalVar: - if (vd.hasLocalQualifier()) break; + if (vd.isStatic()) break; fallthrough; case FunctionParam: u32 off = vd.getOffset(); diff --git a/ast/var_decl.c2 b/ast/var_decl.c2 index 0639dc3b4..b898116c8 100644 --- a/ast/var_decl.c2 +++ b/ast/var_decl.c2 @@ -55,12 +55,13 @@ type VarDeclBits struct { VarDeclKind kind : 3; u32 is_bitfield : 1; // bitfield is struct-member u32 has_init : 1; // global and local variables - u32 has_local : 1; // local variables only + u32 is_static : 1; // local variables only u32 has_init_call : 1; // local variables only u32 attr_weak : 1; // globals only u32 addr_used : 1; AutoAttr auto_attr : 2; // for parameters only FormatAttr format_attr : 2; // for parameters only + u32 is_tlocal : 1; // local or global variables with thread local storage } public type BitFieldLayout struct { @@ -312,12 +313,21 @@ public fn BitFieldLayout* VarDecl.getBitfieldLayout(const VarDecl* d) { return nil; } -public fn bool VarDecl.hasLocalQualifier(const VarDecl* d) { - return d.base.varDeclBits.has_local; +public fn bool VarDecl.isStatic(const VarDecl* d) { + return d.base.varDeclBits.is_static; } -public fn void VarDecl.setLocal(VarDecl* d, bool has_local) { - d.base.varDeclBits.has_local = has_local; +public fn void VarDecl.setStatic(VarDecl* d, bool is_static) { + d.base.varDeclBits.is_static = is_static; +} + +public fn bool VarDecl.isTLocal(const VarDecl* d) { + return d.base.varDeclBits.is_tlocal; +} + +public fn void VarDecl.setTLocal(VarDecl* d) { + d.base.varDeclBits.is_static = true; + d.base.varDeclBits.is_tlocal = true; } public fn void VarDecl.setInitCall(VarDecl* d, bool has_init_call) { @@ -390,7 +400,7 @@ fn void VarDecl.print(const VarDecl* d, string_buffer.Buf* out, u32 indent) { BitFieldLayout* layout = d.getBitfieldLayout(); out.print(" bitfield(%d, %d)", layout.bit_offset, layout.bit_width); } - if (d.hasLocalQualifier()) out.add(" (local)"); + if (d.isStatic()) out.add(" (static)"); if (d.base.varDeclBits.attr_weak) out.add(" weak"); if (d.base.varDeclBits.addr_used) out.add(" addr_used"); switch (d.base.varDeclBits.auto_attr) { diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index 6b1cf2499..34d94ba06 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -472,7 +472,7 @@ public fn VarDecl* Builder.actOnVarDecl(Builder* b, const TypeRefHolder* ref, SrcLoc assignLoc, Expr* initValue, - bool has_local, + bool is_static, bool has_init_call) { VarDecl* d = VarDecl.create(b.context, @@ -485,7 +485,7 @@ public fn VarDecl* Builder.actOnVarDecl(Builder* b, assignLoc, false, initValue); - d.setLocal(has_local); + d.setStatic(is_static); d.setInitCall(has_init_call); return d; } diff --git a/generator/c/c_generator.c2 b/generator/c/c_generator.c2 index b9edd6701..8b6ebdd86 100644 --- a/generator/c/c_generator.c2 +++ b/generator/c/c_generator.c2 @@ -1462,6 +1462,7 @@ const char[] C_defines = #define NULL ((void*)0) #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + #define tlocal _Thread_local ```; fn void Generator.emit_external_header(Generator* gen, const char* target) { diff --git a/generator/c/c_generator_stmt.c2 b/generator/c/c_generator_stmt.c2 index dc7b9c0f6..b8f04e973 100644 --- a/generator/c/c_generator_stmt.c2 +++ b/generator/c/c_generator_stmt.c2 @@ -21,7 +21,7 @@ import string_buffer; fn void Generator.emitVarDecl(Generator* gen, VarDecl* vd, string_buffer.Buf* out, bool emit_init, bool first) { Decl* d = (Decl*)vd; if (first) { - if (vd.hasLocalQualifier()) out.add("static "); + if (vd.isStatic()) out.add(vd.isTLocal() ? "tlocal " : "static "); gen.emitTypePre(out, d.getType()); } else { out.add1(','); diff --git a/generator/c2i/c2i_generator_decl.c2 b/generator/c2i/c2i_generator_decl.c2 index 27e09cc10..5afb3799a 100644 --- a/generator/c2i/c2i_generator_decl.c2 +++ b/generator/c2i/c2i_generator_decl.c2 @@ -42,7 +42,7 @@ fn void Generator.emitVarDecl(Generator* gen, string_buffer.Buf* out, const VarD Decl* d = (Decl*)vd; if (first) { - if (vd.hasLocalQualifier()) out.add("local "); + if (vd.isStatic()) out.add(vd.isTLocal() ? "tlocal" : "local "); gen.emitType(out, d.getType()); } else { out.add1(','); diff --git a/generator/ir/ir_generator.c2 b/generator/ir/ir_generator.c2 index e6247e264..ea5ecd0a4 100644 --- a/generator/ir/ir_generator.c2 +++ b/generator/ir/ir_generator.c2 @@ -702,7 +702,7 @@ fn void Generator.collectLocalVars(Generator* gen, Stmt* s) { u32 count = ds.getDeclCount(); for (u32 i = 0; i < count; i++) { VarDecl* vd = ds.getDecl(i); - if (vd.hasLocalQualifier()) { + if (vd.isStatic()) { // TODO move to collectLocals? Decl* d = (Decl*)vd; ir.Ref ref; diff --git a/generator/ir/ir_generator_expr.c2 b/generator/ir/ir_generator_expr.c2 index 9cabdb5f3..c43d6fa25 100644 --- a/generator/ir/ir_generator_expr.c2 +++ b/generator/ir/ir_generator_expr.c2 @@ -174,7 +174,7 @@ fn void Generator.emitVarDecl(Generator* gen, ir.Ref* result, Decl* d) { const VarDecl* vd = (VarDecl*)d; if (vd.isGlobal()) { gen.emitSymbol(result, d); - } else if (vd.hasLocalQualifier()) { + } else if (vd.isStatic()) { u32 gen_idx = d.getGenIdx(); if (!gen_idx) { QualType qt = d.getType(); diff --git a/generator/ir/ir_generator_stmt.c2 b/generator/ir/ir_generator_stmt.c2 index d401c6650..6593bc91b 100644 --- a/generator/ir/ir_generator_stmt.c2 +++ b/generator/ir/ir_generator_stmt.c2 @@ -96,7 +96,7 @@ fn bool Generator.emitStmt(Generator* gen, const Stmt* s) { for (u32 i = 0; i < count; i++) { VarDecl* vd = ds.getDecl(i); - if (vd.hasLocalQualifier()) return true; // already done + if (vd.isStatic()) return true; // already done const Expr* ie = vd.getInit(); if (ie) { diff --git a/parser/c2_parser.c2 b/parser/c2_parser.c2 index 8dd1671a3..5299a9d85 100644 --- a/parser/c2_parser.c2 +++ b/parser/c2_parser.c2 @@ -647,7 +647,8 @@ fn bool Parser.parseFunctionParams(Parser* p, DeclList* params, bool is_public, param_default ::= EQUALS constant_expression. */ fn VarDecl* Parser.parseParamDecl(Parser* p, bool is_public, bool accept_default) { - if (p.tok.kind == KW_local) p.error("keyword 'local' is not allowed here"); + if (p.tok.kind == KW_local || p.tok.kind == KW_static || p.tok.kind == KW_tlocal) + p.error("keyword '%s' is not allowed here", p.tok.kind.str()); TypeRefHolder ref.init(); // Note: dont check arrays in this phase, but in Analyser @@ -754,7 +755,11 @@ fn void Parser.parseArrayEntry(Parser* p) { } fn void Parser.parseVarDecl(Parser* p, bool is_public) { - if (p.tok.kind == KW_local) p.error("keyword 'local' cannot be used at file scope"); + if (p.tok.kind == KW_local || p.tok.kind == KW_static) + p.error("keyword '%s' cannot be used at file scope", p.tok.kind.str()); + + bool has_tlocal = (p.tok.kind == KW_tlocal); + if (has_tlocal) p.consumeToken(); bool need_semi = true; TypeRefHolder ref.init(); @@ -814,8 +819,9 @@ fn void Parser.parseVarDecl(Parser* p, bool is_public) { } bool has_embed = p.builder.hasEmbedAttr(); - Decl* d = (Decl*)p.builder.actOnGlobalVarDecl(name, loc, is_public, &ref, assignLoc, has_embed, initValue); - p.applyAttributes(d, num_attr); + VarDecl* vd = p.builder.actOnGlobalVarDecl(name, loc, is_public, &ref, assignLoc, has_embed, initValue); + if (has_tlocal) vd.setTLocal(); + p.applyAttributes((Decl*)vd, num_attr); if (p.tok.kind != Comma) break; diff --git a/parser/c2_parser_stmt.c2 b/parser/c2_parser_stmt.c2 index 18c999bd4..f20e23a30 100644 --- a/parser/c2_parser_stmt.c2 +++ b/parser/c2_parser_stmt.c2 @@ -57,6 +57,8 @@ fn Stmt* Parser.parseStmt(Parser* p) { case KW_int ... KW_unsigned: // C type keywords case KW_const: case KW_local: + case KW_static: + case KW_tlocal: case KW_volatile: case KW_void: // checkSemi, allowLocal, !isCondition @@ -506,10 +508,10 @@ fn Stmt* Parser.parseWhileStmt(Parser* p) { fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool isCondition) { VarDecl*[MaxMultiDecl] decls; u32 num_decls = 0; - bool has_local = false; - if (p.tok.kind == KW_local) { - has_local = true; - if (!allowLocal) p.error("keyword 'local' is not allowed here"); + bool is_static = (p.tok.kind == KW_local || p.tok.kind == KW_static); + bool has_tlocal = (p.tok.kind == KW_tlocal); + if (is_static | has_tlocal) { + if (!allowLocal) p.error("keyword '%s' is not allowed here", p.tok.kind.str()); p.consumeToken(); } @@ -548,8 +550,8 @@ fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool i case Dot: if (p.peekToken(1) == Identifier && p.peekToken(2) == LParen) { - if (has_local) - p.error("local qualified variables cannot have an init call"); + if (is_static | has_tlocal) + p.error("static local variables cannot have an init call"); if (isCondition) p.error("cannot use an init call inside a condition"); u32 name_len = p.tok.len; @@ -571,7 +573,9 @@ fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool i default: break; } - decls[num_decls++] = p.builder.actOnVarDecl(name, loc, &ref, assignLoc, initValue, has_local, has_init_call); + VarDecl* vd = p.builder.actOnVarDecl(name, loc, &ref, assignLoc, initValue, is_static, has_init_call); + if (has_tlocal) vd.setTLocal(); + decls[num_decls++] = vd; if (p.tok.kind != Comma) break; p.consumeToken(); @@ -628,7 +632,7 @@ fn bool Parser.isDeclaration(Parser* p) { const Kind kind = p.tok.kind; if (kind == Identifier) return p.isTypeSpec(); if (kind.isTypeKeyword() && (p.peekToken(1) != Dot)) return true; - if (kind == KW_local) return true; + if (kind == KW_local || kind == KW_static || kind == KW_tlocal) return true; if (kind.isQualifier()) return true; return false; } diff --git a/parser/c2_parser_type.c2 b/parser/c2_parser_type.c2 index 5c3ded010..e16999e84 100644 --- a/parser/c2_parser_type.c2 +++ b/parser/c2_parser_type.c2 @@ -361,7 +361,8 @@ fn void Parser.parseEnumAssocAttributes(Parser* p, DeclList* params) { } fn VarDecl* Parser.parseEnumAttribute(Parser* p) { - if (p.tok.kind == KW_local) p.error("keyword 'local' is not allowed here"); + if (p.tok.kind == KW_local || p.tok.kind == KW_static) + p.error("keyword '%s' is not allowed here", p.tok.kind.str()); bool is_public = false; if (p.tok.kind == KW_public) { diff --git a/parser/keywords.c2 b/parser/keywords.c2 index d3fcfea90..284ccc152 100644 --- a/parser/keywords.c2 +++ b/parser/keywords.c2 @@ -21,8 +21,8 @@ import token local; import string; public type Info struct { - // Note: should be big enough to hold all keywords (4+436+216 with alignment) - Kind[656] indexes; + // Note: should be big enough to hold all keywords (4+436+220 with alignment) + Kind[660] indexes; u32 max_index; } diff --git a/parser/token.c2 b/parser/token.c2 index 381a153f1..783602332 100644 --- a/parser/token.c2 +++ b/parser/token.c2 @@ -135,10 +135,12 @@ public type Kind enum u8 { KW_public, KW_return, KW_sizeof, + KW_static, KW_static_assert, KW_struct, KW_switch, KW_template, + KW_tlocal, KW_to_container, KW_true, KW_type, @@ -294,10 +296,12 @@ const char*[Kind] token_names = { [KW_public] = "public", [KW_return] = "return", [KW_sizeof] = "sizeof", + [KW_static] = "static", [KW_static_assert] = "static_assert", [KW_struct] = "struct", [KW_switch] = "switch", [KW_template] = "template", + [KW_tlocal] = "tlocal", [KW_to_container] = "to_container", [KW_true] = "true", [KW_type] = "type", diff --git a/test/c_generator/variables/local_variable.c2t b/test/c_generator/variables/local_variable.c2t index 3b0113147..c16e11305 100644 --- a/test/c_generator/variables/local_variable.c2t +++ b/test/c_generator/variables/local_variable.c2t @@ -6,7 +6,7 @@ module test; fn void test1() { - local i32 a = 10; + static i32 a = 10; } // @expect{atleast, cgen/build.c} diff --git a/test/c_generator/variables/tlocal_variable.c2t b/test/c_generator/variables/tlocal_variable.c2t new file mode 100644 index 000000000..3d8a701df --- /dev/null +++ b/test/c_generator/variables/tlocal_variable.c2t @@ -0,0 +1,18 @@ +// @recipe bin + $warnings no-unused + $backend c + +// @file{file1} +module test; + +fn void test1() { + tlocal i32 a = 10; +} + +// @expect{atleast, cgen/build.c} + +static void test_test1(void) +{ + tlocal int a = 10; +} + diff --git a/test/functions/return_local.c2 b/test/functions/return_local.c2 index 0da8b6225..b650346cd 100644 --- a/test/functions/return_local.c2 +++ b/test/functions/return_local.c2 @@ -2,7 +2,7 @@ module test; fn i32* test1() { - local i32 a = 10; + static i32 a = 10; return &a; } diff --git a/test/init/init_call_bad_local.c2 b/test/init/init_call_bad_local.c2 index bedf8d879..be2264e47 100644 --- a/test/init/init_call_bad_local.c2 +++ b/test/init/init_call_bad_local.c2 @@ -8,5 +8,5 @@ fn void Foo.init(Foo* f, i32 v) { f.v = v; } fn void test1() { Foo foo.init(1); // This one is OK - local Foo foo1.init(1); // @error{local qualified variables cannot have an init call} + static Foo foo1.init(1); // @error{static local variables cannot have an init call} } diff --git a/test/parser/reserved_words.c2 b/test/parser/reserved_words.c2 index da551d463..3b4840e96 100644 --- a/test/parser/reserved_words.c2 +++ b/test/parser/reserved_words.c2 @@ -17,7 +17,6 @@ type Foo struct { u32 restrict; // @error{reserved word 'restrict' cannot be used as struct/union member name} u32 short; // @error{reserved word 'short' cannot be used as struct/union member name} u32 signed; // @error{reserved word 'signed' cannot be used as struct/union member name} - u32 static; // @error{reserved word 'static' cannot be used as struct/union member name} u32 thread_local; // @error{reserved word 'thread_local' cannot be used as struct/union member name} u32 typedef; // @error{reserved word 'typedef' cannot be used as struct/union member name} u32 typeof; // @error{reserved word 'typeof' cannot be used as struct/union member name} @@ -43,7 +42,6 @@ fn void foo( u32 restrict, // @error{reserved word 'restrict' cannot be used as parameter name} u32 short, // @error{reserved word 'short' cannot be used as parameter name} u32 signed, // @error{reserved word 'signed' cannot be used as parameter name} - u32 static, // @error{reserved word 'static' cannot be used as parameter name} u32 thread_local, // @error{reserved word 'thread_local' cannot be used as parameter name} u32 typedef, // @error{reserved word 'typedef' cannot be used as parameter name} u32 typeof, // @error{reserved word 'typeof' cannot be used as parameter name} @@ -69,7 +67,6 @@ fn void bar() { u32 restrict; // @error{reserved word 'restrict' cannot be used as variable name} u32 short; // @error{reserved word 'short' cannot be used as variable name} u32 signed; // @error{reserved word 'signed' cannot be used as variable name} - u32 static; // @error{reserved word 'static' cannot be used as variable name} u32 thread_local; // @error{reserved word 'thread_local' cannot be used as variable name} u32 typedef; // @error{reserved word 'typedef' cannot be used as variable name} u32 typeof; // @error{reserved word 'typeof' cannot be used as variable name} @@ -94,7 +91,6 @@ u32 register; // @error{reserved word 'register' cannot be used as variable na u32 restrict; // @error{reserved word 'restrict' cannot be used as variable name} u32 short; // @error{reserved word 'short' cannot be used as variable name} u32 signed; // @error{reserved word 'signed' cannot be used as variable name} -u32 static; // @error{reserved word 'static' cannot be used as variable name} u32 thread_local; // @error{reserved word 'thread_local' cannot be used as variable name} u32 typedef; // @error{reserved word 'typedef' cannot be used as variable name} u32 typeof; // @error{reserved word 'typeof' cannot be used as variable name} diff --git a/test/template/function/non_public_rtype.c2 b/test/template/function/non_public_rtype.c2 index 720b5cbcc..2209ab72d 100644 --- a/test/template/function/non_public_rtype.c2 +++ b/test/template/function/non_public_rtype.c2 @@ -8,7 +8,7 @@ type A struct { // TODO: why not public `fn X* increment() {` public fn X* increment() template X { - local X x2; + static X x2; return &x2; } diff --git a/test/template/function/pointer_rtype.c2 b/test/template/function/pointer_rtype.c2 index ae2181c3a..3c1410b5b 100644 --- a/test/template/function/pointer_rtype.c2 +++ b/test/template/function/pointer_rtype.c2 @@ -6,7 +6,7 @@ type A struct { } fn X* increment() template X { - local X x2; + static X x2; return x2; // @error{invalid type conversion from 'test.A' to 'test.A*'} } diff --git a/test/template/function/pointer_rtype_ok.c2 b/test/template/function/pointer_rtype_ok.c2 index 25c4576f9..bf8c07dc3 100644 --- a/test/template/function/pointer_rtype_ok.c2 +++ b/test/template/function/pointer_rtype_ok.c2 @@ -6,7 +6,7 @@ type A struct { } fn X* increment() template X { - local X x2; + static X x2; return &x2; } diff --git a/test/types/struct/bitfield_validator.c2 b/test/types/struct/bitfield_validator.c2 index 623cb38b3..b0f19ef65 100644 --- a/test/types/struct/bitfield_validator.c2 +++ b/test/types/struct/bitfield_validator.c2 @@ -584,7 +584,7 @@ fn bool match_name(const char *name, const char *end, const char *pattern) { } fn void check(const char* s, usize a, u8* p1, u8* p2) { - local bool header; + static bool header; usize b = (usize)(p2 - p1); if (!filter_name(s)) return; diff --git a/test/types/struct/struct_member_local_keyword.c2 b/test/types/struct/struct_member_local_keyword.c2 index 5abac7ca1..bd8df04b6 100644 --- a/test/types/struct/struct_member_local_keyword.c2 +++ b/test/types/struct/struct_member_local_keyword.c2 @@ -3,6 +3,6 @@ module test; type Point struct { i32 x; - local i32 y; // @error{expected type specifier} + static i32 y; // @error{expected type specifier} } diff --git a/test/vars/local_duplicate_name.c2 b/test/vars/local_duplicate_name.c2 index 5addbab39..321dc05a2 100644 --- a/test/vars/local_duplicate_name.c2 +++ b/test/vars/local_duplicate_name.c2 @@ -4,10 +4,10 @@ module test; i32 a; // @note{previous definition is here} public fn void test1(i32 arg1) { // @note{previous definition is here} - local i32 arg1; // @error{redefinition of 'arg1'} + static i32 arg1; // @error{redefinition of 'arg1'} } public fn void test2() { - local i32 a; // @error{redefinition of 'a'} + static i32 a; // @error{redefinition of 'a'} } diff --git a/test/vars/local_init.c2 b/test/vars/local_init.c2 index 168b75219..f6f9e3600 100644 --- a/test/vars/local_init.c2 +++ b/test/vars/local_init.c2 @@ -4,11 +4,11 @@ module test; i32 gg = 10; public fn void test1(i32 arg1) { - local const char* str = "abc"; - local i32 a; - local i32* aa = ≫ - local i32 b = 10; - local i32* c = &b; - local i32 d = arg1; // @error{initializer element is not a compile-time constant} + static const char* str = "abc"; + static i32 a; + static i32* aa = ≫ + static i32 b = 10; + static i32* c = &b; + static i32 d = arg1; // @error{initializer element is not a compile-time constant} }