using immutable thread_local on variable declarations

This commit is contained in:
Ginger Bill
2017-01-19 20:03:10 +00:00
parent 563b1e2b28
commit 3f023509a7
10 changed files with 166 additions and 195 deletions
+6 -6
View File
@@ -9,13 +9,13 @@
#import "sync.odin";
#import "utf8.odin";
T :: struct { x, y: int };
thread_local t: T;
main :: proc() {
T :: struct { x, y: int }
foo :: proc(using immutable t: T) {
a0 := t.x;
a1 := x;
x = 123; // Error: Cannot assign to an immutable: `x`
}
immutable using t := T{123, 321};
fmt.println(t);
// foo :: proc(x: ^i32) -> (int, int) {
// fmt.println("^int");
+1 -1
View File
@@ -150,7 +150,7 @@ Context :: struct #ordered {
user_index: int,
}
#thread_local __context: Context;
thread_local __context: Context;
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
+2 -72
View File
@@ -107,78 +107,6 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
gb_temp_arena_memory_end(tmp);
}
void check_var_decl_node(Checker *c, AstNodeValueDecl *vd) {
GB_ASSERT(vd->is_var == true);
isize entity_count = vd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
for_array(i, vd->names) {
AstNode *name = vd->names.e[i];
Entity *entity = NULL;
if (name->kind == AstNode_Ident) {
Token token = name->Ident;
String str = token.string;
Entity *found = NULL;
// NOTE(bill): Ignore assignments to `_`
if (str_ne(str, str_lit("_"))) {
found = current_scope_lookup_entity(c->context.scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
error(token,
"Redeclaration of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
} else {
error_node(name, "A variable declaration must be an identifier");
}
if (entity == NULL) {
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
}
entities[entity_index++] = entity;
}
Type *init_type = NULL;
if (vd->type) {
init_type = check_type_extra(c, vd->type, NULL);
if (init_type == NULL) {
init_type = t_invalid;
}
}
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
GB_ASSERT(e != NULL);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
continue;
}
e->flags |= EntityFlag_Visited;
if (e->type == NULL) {
e->type = init_type;
}
}
check_arity_match(c, vd);
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
for_array(i, vd->names) {
if (entities[i] != NULL) {
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
}
}
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
@@ -443,6 +371,8 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Variable);
u32 flags = c->context.decl->var_decl_flags;
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
+8 -7
View File
@@ -778,7 +778,8 @@ Type *check_get_params(Checker *c, Scope *scope, AstNodeArray params, bool *is_v
for_array(j, p->names) {
AstNode *name = p->names.e[j];
if (ast_node_expect(name, AstNode_Ident)) {
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, p->flags&FieldFlag_using);
Entity *param = make_entity_param(c->allocator, scope, name->Ident, type,
p->flags&FieldFlag_using, p->flags&FieldFlag_immutable);
if (p->flags&FieldFlag_no_alias) {
param->flags |= EntityFlag_NoAlias;
}
@@ -826,7 +827,7 @@ Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) {
Token token = ast_node_token(item);
token.string = str_lit(""); // NOTE(bill): results are not named
// TODO(bill): Should I have named results?
Entity *param = make_entity_param(c->allocator, scope, token, type, false);
Entity *param = make_entity_param(c->allocator, scope, token, type, false, false);
// NOTE(bill): No need to record
variables[variable_index++] = param;
}
@@ -2939,8 +2940,8 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
}
Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
variables[0] = make_entity_param(c->allocator, NULL, empty_token, type, false);
variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false);
variables[0] = make_entity_param(c->allocator, NULL, empty_token, type, false, true);
variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false, true);
Type *tuple = make_type_tuple(c->allocator);
tuple->Tuple.variables = variables;
@@ -4128,7 +4129,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
variables[variable_index++] = make_entity_constant(c->allocator, NULL, empty_token, type, operand.value);
break;
default:
variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false);
variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false, true);
break;
}
}
@@ -4745,8 +4746,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
Type *elem = t->Maybe.elem;
Token tok = make_token_ident(str_lit(""));
variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false);
variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false);
variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false, true);
variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false, true);
Type *tuple = make_type_tuple(c->allocator);
tuple->Tuple.variables = variables;
+64 -60
View File
@@ -720,8 +720,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
found = current_scope_lookup_entity(c->context.scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c->allocator, c->context.scope, token, type);
entity->Variable.is_immutable = true;
entity = make_entity_variable(c->allocator, c->context.scope, token, type, true);
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
@@ -1020,9 +1019,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
tt = make_type_pointer(c->allocator, case_type);
add_type_info_type(c, tt);
}
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt);
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt, true);
tag_var->flags |= EntityFlag_Used;
tag_var->Variable.is_immutable = true;
add_entity(c, c->context.scope, ms->var, tag_var);
add_entity_use(c, ms->var, tag_var);
}
@@ -1072,6 +1070,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(us, UsingStmt, node);
switch (us->node->kind) {
default:
// TODO(bill): Better error message for invalid using statement
error(us->token, "Invalid `using` statement");
break;
case_ast_node(es, ExprStmt, us->node);
// TODO(bill): Allow for just a LHS expression list rather than this silly code
Entity *e = NULL;
@@ -1199,52 +1201,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
case_end;
case_ast_node(vd, ValueDecl, us->node);
if (!vd->is_var) {
error_node(us->node, "`using` can only be applied to a variable declaration");
return;
}
if (vd->names.count > 1 && vd->type != NULL) {
error(us->token, "`using` can only be applied to one variable of the same type");
}
check_var_decl_node(c, vd);
for_array(name_index, vd->names) {
AstNode *item = vd->names.e[name_index];
if (item->kind != AstNode_Ident) {
// TODO(bill): Handle error here???
continue;
}
ast_node(i, Ident, item);
String name = i->string;
Entity *e = scope_lookup_entity(c->context.scope, name);
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
return;
}
}
}
} else {
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
case_end;
default:
error(us->token, "Invalid AST: Using Statement");
break;
}
case_end;
@@ -1268,14 +1224,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(vd, ValueDecl, node);
if (vd->is_var) {
isize entity_count = vd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
isize entity_count = 0;
if (vd->flags&VarDeclFlag_thread_local &&
!c->context.scope->is_file) {
error_node(node, "`thread_local` may only be applied to a variable declaration");
}
for_array(i, vd->names) {
AstNode *name = vd->names.e[i];
Entity *entity = NULL;
if (name->kind == AstNode_Ident) {
if (name->kind != AstNode_Ident) {
error_node(name, "A variable declaration must be an identifier");
} else {
Token token = name->Ident;
String str = token.string;
Entity *found = NULL;
@@ -1284,7 +1246,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
found = current_scope_lookup_entity(c->context.scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL, vd->flags&VarDeclFlag_immutable);
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
@@ -1294,13 +1256,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
} else {
error_node(name, "A variable declaration must be an identifier");
}
if (entity == NULL) {
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
}
entities[entity_index++] = entity;
entities[entity_count++] = entity;
}
Type *init_type = NULL;
@@ -1320,11 +1280,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
e->flags |= EntityFlag_Visited;
if (e->type == NULL)
if (e->type == NULL) {
e->type = init_type;
}
}
check_arity_match(c, vd);
check_arity_match(c, vd);
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
for_array(i, vd->names) {
@@ -1332,6 +1293,49 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
}
}
if ((vd->flags & VarDeclFlag_using) != 0) {
Token token = ast_node_token(node);
if (vd->type != NULL && entity_count > 1) {
error(token, "`using` can only be applied to one variable of the same type");
// TODO(bill): Should a `continue` happen here?
}
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
Entity *e = entities[entity_index];
if (e == NULL) {
continue;
}
bool is_immutable = false;
if (e->kind == Entity_Variable) {
is_immutable = e->Variable.is_immutable;
}
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
return;
}
}
}
} else {
// NOTE(bill): skip the rest to remove extra errors
error(token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
}
} else {
// NOTE(bill): Handled elsewhere
}
+7 -3
View File
@@ -61,7 +61,7 @@ typedef struct DeclInfo {
AstNode *type_expr;
AstNode *init_expr;
AstNode *proc_lit; // AstNode_ProcLit
u32 var_decl_tags;
u32 var_decl_flags;
MapBool deps; // Key: Entity *
} DeclInfo;
@@ -1395,8 +1395,12 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
continue;
}
Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL);
Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL, vd->flags&VarDeclFlag_immutable);
e->identifier = name;
if (vd->flags&VarDeclFlag_using) {
vd->flags &= ~VarDeclFlag_using; // NOTE(bill): This error will be only caught once
error_node(name, "`using` is not allowed at the file scope");
}
entities[entity_index++] = e;
DeclInfo *d = di;
@@ -1405,7 +1409,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
d = make_declaration_info(heap_allocator(), e->scope);
d->type_expr = vd->type;
d->init_expr = init_expr;
d->var_decl_tags = vd->tags;
d->var_decl_flags = vd->flags;
}
add_entity_and_decl_info(c, name, e, d);
+7 -6
View File
@@ -103,8 +103,9 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
return entity;
}
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) {
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type, bool is_immutable) {
Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
entity->Variable.is_immutable = is_immutable;
return entity;
}
@@ -128,8 +129,8 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
entity->flags |= EntityFlag_Used;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
entity->flags |= EntityFlag_Param;
@@ -137,7 +138,7 @@ Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type,
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
@@ -146,7 +147,7 @@ Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type,
}
Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
@@ -191,6 +192,6 @@ Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, Impli
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL);
return make_entity_variable(a, scope, token, NULL, false);
}
+10 -10
View File
@@ -1230,7 +1230,7 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) {
Entity *e = make_entity_variable(proc->module->allocator,
scope,
empty_token,
type);
type, false);
return ir_add_local(proc, e);
}
@@ -5064,7 +5064,7 @@ void ir_init_module(irModule *m, Checker *c, BuildContext *build_context) {
{
String name = str_lit(IR_TYPE_INFO_DATA_NAME);
isize count = c->info.type_info_map.entries.count;
Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count));
Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name), make_type_array(m->allocator, t_type_info, count), false);
irValue *g = ir_make_value_global(m->allocator, e, NULL);
g->Global.is_private = true;
ir_module_add_value(m, e, g);
@@ -5096,7 +5096,7 @@ void ir_init_module(irModule *m, Checker *c, BuildContext *build_context) {
String name = str_lit(IR_TYPE_INFO_DATA_MEMBER_NAME);
Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(name),
make_type_array(m->allocator, t_type_info_member, count));
make_type_array(m->allocator, t_type_info_member, count), false);
irValue *g = ir_make_value_global(m->allocator, e, NULL);
ir_module_add_value(m, e, g);
map_ir_value_set(&m->members, hash_string(name), g);
@@ -5303,7 +5303,7 @@ void ir_gen_tree(irGen *s) {
case Entity_Variable: {
irValue *g = ir_make_value_global(a, e, NULL);
if (decl->var_decl_tags & VarDeclTag_thread_local) {
if (decl->var_decl_flags & VarDeclFlag_thread_local) {
g->Global.is_thread_local = true;
}
irGlobalVariable var = {0};
@@ -5406,11 +5406,11 @@ void ir_gen_tree(irGen *s) {
proc_results->Tuple.variables = gb_alloc_array(a, Entity *, 1);
proc_results->Tuple.variable_count = 1;
proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false);
proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, make_token_ident(str_lit("reason")), t_i32, false);
proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false);
proc_params->Tuple.variables[0] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false, false);
proc_params->Tuple.variables[1] = make_entity_param(a, proc_scope, make_token_ident(str_lit("reason")), t_i32, false, false);
proc_params->Tuple.variables[2] = make_entity_param(a, proc_scope, blank_token, t_rawptr, false, false);
proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false);
proc_results->Tuple.variables[0] = make_entity_param(a, proc_scope, empty_token, t_i32, false, false);
Type *proc_type = make_type_proc(a, proc_scope,
@@ -5793,7 +5793,7 @@ void ir_gen_tree(irGen *s) {
token.string.text = gb_alloc_array(a, u8, name_len);
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
"%s-%d", name_base, id)-1;
Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count));
Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_string, count), false);
name_array = ir_make_value_global(a, e, NULL);
name_array->Global.is_private = true;
ir_module_add_value(m, e, name_array);
@@ -5808,7 +5808,7 @@ void ir_gen_tree(irGen *s) {
token.string.text = gb_alloc_array(a, u8, name_len);
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
"%s-%d", name_base, id)-1;
Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_type_info_enum_value, count));
Entity *e = make_entity_variable(a, NULL, token, make_type_array(a, t_type_info_enum_value, count), false);
value_array = ir_make_value_global(a, e, NULL);
value_array->Global.is_private = true;
ir_module_add_value(m, e, value_array);
+60 -30
View File
@@ -83,9 +83,11 @@ typedef enum ProcCallingConvention {
ProcCC_Invalid,
} ProcCallingConvention;
typedef enum VarDeclTag {
VarDeclTag_thread_local = 1<<0,
} VarDeclTag;
typedef enum VarDeclFlag {
VarDeclFlag_thread_local = 1<<0,
VarDeclFlag_using = 1<<1,
VarDeclFlag_immutable = 1<<2,
} VarDeclFlag;
typedef enum StmtStateFlag {
StmtStateFlag_bounds_check = 1<<0,
@@ -274,7 +276,7 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
AstNodeArray names; \
AstNode * type; \
AstNodeArray values; \
u64 tags; \
u32 flags; \
}) \
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
Token token; \
@@ -1209,6 +1211,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
case Token_defer:
case Token_asm:
case Token_using:
case Token_immutable:
case Token_break:
case Token_continue:
@@ -3087,31 +3090,73 @@ AstNode *parse_stmt(AstFile *f) {
// TODO(bill): Make using statements better
Token token = expect_token(f, Token_using);
AstNode *node = parse_stmt(f);
bool valid = false;
switch (node->kind) {
case AstNode_ValueDecl:
if (!node->ValueDecl.is_var) {
syntax_error(token, "`using` may not be applied to constant declarations");
return make_bad_stmt(f, token, f->curr_token);
} else {
if (f->curr_proc == NULL) {
syntax_error(token, "`using` is not allowed at the file scope");
} else {
node->ValueDecl.flags |= VarDeclFlag_using;
}
return node;
}
break;
case AstNode_ExprStmt: {
AstNode *e = unparen_expr(node->ExprStmt.expr);
while (e->kind == AstNode_SelectorExpr) {
e = unparen_expr(e->SelectorExpr.selector);
}
if (e->kind == AstNode_Ident) {
valid = true;
return make_using_stmt(f, token, node);
}
} break;
case AstNode_ValueDecl:
valid = node->ValueDecl.is_var;
break;
}
if (!valid) {
syntax_error(token, "Illegal use of `using` statement.");
return make_bad_stmt(f, token, f->curr_token);
}
return make_using_stmt(f, token, node);
syntax_error(token, "Illegal use of `using` statement");
return make_bad_stmt(f, token, f->curr_token);
} break;
case Token_immutable: {
Token token = expect_token(f, Token_immutable);
AstNode *node = parse_stmt(f);
if (node->kind == AstNode_ValueDecl) {
if (!node->ValueDecl.is_var) {
syntax_error(token, "`immutable` may not be applied to constant declarations");
return make_bad_stmt(f, token, f->curr_token);
} else {
node->ValueDecl.flags |= VarDeclFlag_immutable;
return node;
}
}
syntax_error(token, "`immutable` may only be applied to a variable declaration");
return make_bad_stmt(f, token, f->curr_token);
} break;
case Token_thread_local: {
Token token = expect_token(f, Token_thread_local);
AstNode *node = parse_stmt(f);
if (node->kind == AstNode_ValueDecl) {
if (!node->ValueDecl.is_var) {
syntax_error(token, "`thread_local` may not be applied to constant declarations");
return make_bad_stmt(f, token, f->curr_token);
}
if (f->curr_proc != NULL) {
syntax_error(token, "`thread_local` is only allowed at the file scope");
} else {
node->ValueDecl.flags |= VarDeclFlag_thread_local;
}
return node;
}
syntax_error(token, "`thread_local` may only be applied to a variable declaration");
return make_bad_stmt(f, token, f->curr_token);
}
case Token_push_allocator: {
next_token(f);
isize prev_level = f->expr_level;
@@ -3237,21 +3282,6 @@ AstNode *parse_stmt(AstFile *f) {
}
expect_semicolon(f, s);
return s;
} else if (str_eq(tag, str_lit("thread_local"))) {
AstNode *decl = parse_simple_stmt(f);
if (decl->kind != AstNode_ValueDecl &&
!decl->ValueDecl.is_var) {
syntax_error(token, "#thread_local may only be applied to variable declarations");
return make_bad_decl(f, token, ast_node_token(decl));
}
if (f->curr_proc != NULL) {
syntax_error(token, "#thread_local is only allowed at the file scope");
return make_bad_decl(f, token, ast_node_token(decl));
}
GB_ASSERT(decl->kind == AstNode_ValueDecl);
decl->ValueDecl.tags |= VarDeclTag_thread_local;
return decl;
} else if (str_eq(tag, str_lit("bounds_check"))) {
s = parse_stmt(f);
s->stmt_state_flags |= StmtStateFlag_bounds_check;
+1
View File
@@ -107,6 +107,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_no_alias, "no_alias"), \
TOKEN_KIND(Token_immutable, "immutable"), \
TOKEN_KIND(Token_thread_local, "thread_local"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_push_context, "push_context"), \