From 3f023509a7184a9146853e23b189c1a079323143 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Thu, 19 Jan 2017 20:03:10 +0000 Subject: [PATCH] `using` `immutable` `thread_local` on variable declarations --- code/demo.odin | 12 ++--- core/_preload.odin | 2 +- src/check_decl.c | 74 +-------------------------- src/check_expr.c | 15 +++--- src/check_stmt.c | 124 +++++++++++++++++++++++---------------------- src/checker.c | 10 ++-- src/entity.c | 13 ++--- src/ir.c | 20 ++++---- src/parser.c | 90 +++++++++++++++++++++----------- src/tokenizer.c | 1 + 10 files changed, 166 insertions(+), 195 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 272ef0ec2..a4081e139 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -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"); diff --git a/core/_preload.odin b/core/_preload.odin index 8c852d94a..54a3d178f 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -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); diff --git a/src/check_decl.c b/src/check_decl.c index 5b6d9d230..dd112d76c 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -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; diff --git a/src/check_expr.c b/src/check_expr.c index 8f0a26dfb..d708b40a4 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -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; diff --git a/src/check_stmt.c b/src/check_stmt.c index c2a09070f..407205df2 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -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 } diff --git a/src/checker.c b/src/checker.c index a294787d7..b1917e03d 100644 --- a/src/checker.c +++ b/src/checker.c @@ -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); diff --git a/src/entity.c b/src/entity.c index 70c117d21..aaccad476 100644 --- a/src/entity.c +++ b/src/entity.c @@ -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); } diff --git a/src/ir.c b/src/ir.c index b7d41817c..23f84abbe 100644 --- a/src/ir.c +++ b/src/ir.c @@ -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); diff --git a/src/parser.c b/src/parser.c index 4d799b375..6afc807af 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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; diff --git a/src/tokenizer.c b/src/tokenizer.c index 74a0b4529..6f2213ca0 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -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"), \