mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-24 00:17:54 +00:00
using immutable thread_local on variable declarations
This commit is contained in:
+6
-6
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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"), \
|
||||
|
||||
Reference in New Issue
Block a user