mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 16:18:52 +00:00
using immutable thread_local on variable declarations
This commit is contained in:
+6
-6
@@ -9,13 +9,13 @@
|
|||||||
#import "sync.odin";
|
#import "sync.odin";
|
||||||
#import "utf8.odin";
|
#import "utf8.odin";
|
||||||
|
|
||||||
|
T :: struct { x, y: int };
|
||||||
|
thread_local t: T;
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
T :: struct { x, y: int }
|
immutable using t := T{123, 321};
|
||||||
foo :: proc(using immutable t: T) {
|
fmt.println(t);
|
||||||
a0 := t.x;
|
|
||||||
a1 := x;
|
|
||||||
x = 123; // Error: Cannot assign to an immutable: `x`
|
|
||||||
}
|
|
||||||
|
|
||||||
// foo :: proc(x: ^i32) -> (int, int) {
|
// foo :: proc(x: ^i32) -> (int, int) {
|
||||||
// fmt.println("^int");
|
// fmt.println("^int");
|
||||||
|
|||||||
+1
-1
@@ -150,7 +150,7 @@ Context :: struct #ordered {
|
|||||||
user_index: int,
|
user_index: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
#thread_local __context: Context;
|
thread_local __context: Context;
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
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);
|
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) {
|
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||||
if (operand->mode == Addressing_Invalid ||
|
if (operand->mode == Addressing_Invalid ||
|
||||||
operand->type == t_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->type == NULL);
|
||||||
GB_ASSERT(e->kind == Entity_Variable);
|
GB_ASSERT(e->kind == Entity_Variable);
|
||||||
|
|
||||||
|
u32 flags = c->context.decl->var_decl_flags;
|
||||||
|
|
||||||
if (e->flags & EntityFlag_Visited) {
|
if (e->flags & EntityFlag_Visited) {
|
||||||
e->type = t_invalid;
|
e->type = t_invalid;
|
||||||
return;
|
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) {
|
for_array(j, p->names) {
|
||||||
AstNode *name = p->names.e[j];
|
AstNode *name = p->names.e[j];
|
||||||
if (ast_node_expect(name, AstNode_Ident)) {
|
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) {
|
if (p->flags&FieldFlag_no_alias) {
|
||||||
param->flags |= EntityFlag_NoAlias;
|
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 token = ast_node_token(item);
|
||||||
token.string = str_lit(""); // NOTE(bill): results are not named
|
token.string = str_lit(""); // NOTE(bill): results are not named
|
||||||
// TODO(bill): Should I have named results?
|
// 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
|
// NOTE(bill): No need to record
|
||||||
variables[variable_index++] = param;
|
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);
|
Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
|
||||||
variables[0] = make_entity_param(c->allocator, NULL, empty_token, type, 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);
|
variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false, true);
|
||||||
|
|
||||||
Type *tuple = make_type_tuple(c->allocator);
|
Type *tuple = make_type_tuple(c->allocator);
|
||||||
tuple->Tuple.variables = variables;
|
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);
|
variables[variable_index++] = make_entity_constant(c->allocator, NULL, empty_token, type, operand.value);
|
||||||
break;
|
break;
|
||||||
default:
|
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;
|
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);
|
Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
|
||||||
Type *elem = t->Maybe.elem;
|
Type *elem = t->Maybe.elem;
|
||||||
Token tok = make_token_ident(str_lit(""));
|
Token tok = make_token_ident(str_lit(""));
|
||||||
variables[0] = make_entity_param(c->allocator, NULL, tok, elem, 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);
|
variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false, true);
|
||||||
|
|
||||||
Type *tuple = make_type_tuple(c->allocator);
|
Type *tuple = make_type_tuple(c->allocator);
|
||||||
tuple->Tuple.variables = variables;
|
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);
|
found = current_scope_lookup_entity(c->context.scope, str);
|
||||||
}
|
}
|
||||||
if (found == NULL) {
|
if (found == NULL) {
|
||||||
entity = make_entity_variable(c->allocator, c->context.scope, token, type);
|
entity = make_entity_variable(c->allocator, c->context.scope, token, type, true);
|
||||||
entity->Variable.is_immutable = true;
|
|
||||||
add_entity_definition(&c->info, name, entity);
|
add_entity_definition(&c->info, name, entity);
|
||||||
} else {
|
} else {
|
||||||
TokenPos pos = found->token.pos;
|
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);
|
tt = make_type_pointer(c->allocator, case_type);
|
||||||
add_type_info_type(c, tt);
|
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->flags |= EntityFlag_Used;
|
||||||
tag_var->Variable.is_immutable = true;
|
|
||||||
add_entity(c, c->context.scope, ms->var, tag_var);
|
add_entity(c, c->context.scope, ms->var, tag_var);
|
||||||
add_entity_use(c, 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);
|
case_ast_node(us, UsingStmt, node);
|
||||||
switch (us->node->kind) {
|
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);
|
case_ast_node(es, ExprStmt, us->node);
|
||||||
// TODO(bill): Allow for just a LHS expression list rather than this silly code
|
// TODO(bill): Allow for just a LHS expression list rather than this silly code
|
||||||
Entity *e = NULL;
|
Entity *e = NULL;
|
||||||
@@ -1199,52 +1201,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
case_end;
|
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;
|
case_end;
|
||||||
|
|
||||||
@@ -1268,14 +1224,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
|
|
||||||
case_ast_node(vd, ValueDecl, node);
|
case_ast_node(vd, ValueDecl, node);
|
||||||
if (vd->is_var) {
|
if (vd->is_var) {
|
||||||
isize entity_count = vd->names.count;
|
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
|
||||||
isize entity_index = 0;
|
isize entity_count = 0;
|
||||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
|
||||||
|
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) {
|
for_array(i, vd->names) {
|
||||||
AstNode *name = vd->names.e[i];
|
AstNode *name = vd->names.e[i];
|
||||||
Entity *entity = NULL;
|
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;
|
Token token = name->Ident;
|
||||||
String str = token.string;
|
String str = token.string;
|
||||||
Entity *found = NULL;
|
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);
|
found = current_scope_lookup_entity(c->context.scope, str);
|
||||||
}
|
}
|
||||||
if (found == NULL) {
|
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);
|
add_entity_definition(&c->info, name, entity);
|
||||||
} else {
|
} else {
|
||||||
TokenPos pos = found->token.pos;
|
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);
|
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||||
entity = found;
|
entity = found;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
error_node(name, "A variable declaration must be an identifier");
|
|
||||||
}
|
}
|
||||||
if (entity == NULL) {
|
if (entity == NULL) {
|
||||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
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;
|
Type *init_type = NULL;
|
||||||
@@ -1320,11 +1280,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
}
|
}
|
||||||
e->flags |= EntityFlag_Visited;
|
e->flags |= EntityFlag_Visited;
|
||||||
|
|
||||||
if (e->type == NULL)
|
if (e->type == NULL) {
|
||||||
e->type = init_type;
|
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"));
|
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||||
|
|
||||||
for_array(i, vd->names) {
|
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]);
|
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 {
|
} else {
|
||||||
// NOTE(bill): Handled elsewhere
|
// NOTE(bill): Handled elsewhere
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -61,7 +61,7 @@ typedef struct DeclInfo {
|
|||||||
AstNode *type_expr;
|
AstNode *type_expr;
|
||||||
AstNode *init_expr;
|
AstNode *init_expr;
|
||||||
AstNode *proc_lit; // AstNode_ProcLit
|
AstNode *proc_lit; // AstNode_ProcLit
|
||||||
u32 var_decl_tags;
|
u32 var_decl_flags;
|
||||||
|
|
||||||
MapBool deps; // Key: Entity *
|
MapBool deps; // Key: Entity *
|
||||||
} DeclInfo;
|
} 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]));
|
error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
|
||||||
continue;
|
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;
|
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;
|
entities[entity_index++] = e;
|
||||||
|
|
||||||
DeclInfo *d = di;
|
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 = make_declaration_info(heap_allocator(), e->scope);
|
||||||
d->type_expr = vd->type;
|
d->type_expr = vd->type;
|
||||||
d->init_expr = init_expr;
|
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);
|
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;
|
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 *entity = alloc_entity(a, Entity_Variable, scope, token, type);
|
||||||
|
entity->Variable.is_immutable = is_immutable;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,8 +129,8 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
|
|||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
|
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);
|
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
|
||||||
entity->flags |= EntityFlag_Used;
|
entity->flags |= EntityFlag_Used;
|
||||||
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
|
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
|
||||||
entity->flags |= EntityFlag_Param;
|
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 *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_src_index = field_src_index;
|
||||||
entity->Variable.field_index = field_src_index;
|
entity->Variable.field_index = field_src_index;
|
||||||
entity->flags |= EntityFlag_Field;
|
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 *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_src_index = field_src_index;
|
||||||
entity->Variable.field_index = field_src_index;
|
entity->Variable.field_index = field_src_index;
|
||||||
entity->flags |= EntityFlag_Field;
|
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) {
|
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||||
token.string = str_lit("_");
|
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,
|
Entity *e = make_entity_variable(proc->module->allocator,
|
||||||
scope,
|
scope,
|
||||||
empty_token,
|
empty_token,
|
||||||
type);
|
type, false);
|
||||||
return ir_add_local(proc, e);
|
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);
|
String name = str_lit(IR_TYPE_INFO_DATA_NAME);
|
||||||
isize count = c->info.type_info_map.entries.count;
|
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);
|
irValue *g = ir_make_value_global(m->allocator, e, NULL);
|
||||||
g->Global.is_private = true;
|
g->Global.is_private = true;
|
||||||
ir_module_add_value(m, e, g);
|
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);
|
String name = str_lit(IR_TYPE_INFO_DATA_MEMBER_NAME);
|
||||||
Entity *e = make_entity_variable(m->allocator, NULL, make_token_ident(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);
|
irValue *g = ir_make_value_global(m->allocator, e, NULL);
|
||||||
ir_module_add_value(m, e, g);
|
ir_module_add_value(m, e, g);
|
||||||
map_ir_value_set(&m->members, hash_string(name), g);
|
map_ir_value_set(&m->members, hash_string(name), g);
|
||||||
@@ -5303,7 +5303,7 @@ void ir_gen_tree(irGen *s) {
|
|||||||
|
|
||||||
case Entity_Variable: {
|
case Entity_Variable: {
|
||||||
irValue *g = ir_make_value_global(a, e, NULL);
|
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;
|
g->Global.is_thread_local = true;
|
||||||
}
|
}
|
||||||
irGlobalVariable var = {0};
|
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.variables = gb_alloc_array(a, Entity *, 1);
|
||||||
proc_results->Tuple.variable_count = 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[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);
|
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);
|
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,
|
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.text = gb_alloc_array(a, u8, name_len);
|
||||||
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
|
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
|
||||||
"%s-%d", name_base, id)-1;
|
"%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 = ir_make_value_global(a, e, NULL);
|
||||||
name_array->Global.is_private = true;
|
name_array->Global.is_private = true;
|
||||||
ir_module_add_value(m, e, name_array);
|
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.text = gb_alloc_array(a, u8, name_len);
|
||||||
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
|
token.string.len = gb_snprintf(cast(char *)token.string.text, name_len,
|
||||||
"%s-%d", name_base, id)-1;
|
"%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 = ir_make_value_global(a, e, NULL);
|
||||||
value_array->Global.is_private = true;
|
value_array->Global.is_private = true;
|
||||||
ir_module_add_value(m, e, value_array);
|
ir_module_add_value(m, e, value_array);
|
||||||
|
|||||||
+60
-30
@@ -83,9 +83,11 @@ typedef enum ProcCallingConvention {
|
|||||||
ProcCC_Invalid,
|
ProcCC_Invalid,
|
||||||
} ProcCallingConvention;
|
} ProcCallingConvention;
|
||||||
|
|
||||||
typedef enum VarDeclTag {
|
typedef enum VarDeclFlag {
|
||||||
VarDeclTag_thread_local = 1<<0,
|
VarDeclFlag_thread_local = 1<<0,
|
||||||
} VarDeclTag;
|
VarDeclFlag_using = 1<<1,
|
||||||
|
VarDeclFlag_immutable = 1<<2,
|
||||||
|
} VarDeclFlag;
|
||||||
|
|
||||||
typedef enum StmtStateFlag {
|
typedef enum StmtStateFlag {
|
||||||
StmtStateFlag_bounds_check = 1<<0,
|
StmtStateFlag_bounds_check = 1<<0,
|
||||||
@@ -274,7 +276,7 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
|||||||
AstNodeArray names; \
|
AstNodeArray names; \
|
||||||
AstNode * type; \
|
AstNode * type; \
|
||||||
AstNodeArray values; \
|
AstNodeArray values; \
|
||||||
u64 tags; \
|
u32 flags; \
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -1209,6 +1211,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
|
|||||||
case Token_defer:
|
case Token_defer:
|
||||||
case Token_asm:
|
case Token_asm:
|
||||||
case Token_using:
|
case Token_using:
|
||||||
|
case Token_immutable:
|
||||||
|
|
||||||
case Token_break:
|
case Token_break:
|
||||||
case Token_continue:
|
case Token_continue:
|
||||||
@@ -3087,31 +3090,73 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
// TODO(bill): Make using statements better
|
// TODO(bill): Make using statements better
|
||||||
Token token = expect_token(f, Token_using);
|
Token token = expect_token(f, Token_using);
|
||||||
AstNode *node = parse_stmt(f);
|
AstNode *node = parse_stmt(f);
|
||||||
bool valid = false;
|
|
||||||
|
|
||||||
switch (node->kind) {
|
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: {
|
case AstNode_ExprStmt: {
|
||||||
AstNode *e = unparen_expr(node->ExprStmt.expr);
|
AstNode *e = unparen_expr(node->ExprStmt.expr);
|
||||||
while (e->kind == AstNode_SelectorExpr) {
|
while (e->kind == AstNode_SelectorExpr) {
|
||||||
e = unparen_expr(e->SelectorExpr.selector);
|
e = unparen_expr(e->SelectorExpr.selector);
|
||||||
}
|
}
|
||||||
if (e->kind == AstNode_Ident) {
|
if (e->kind == AstNode_Ident) {
|
||||||
valid = true;
|
return make_using_stmt(f, token, node);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case AstNode_ValueDecl:
|
|
||||||
valid = node->ValueDecl.is_var;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
syntax_error(token, "Illegal use of `using` statement");
|
||||||
syntax_error(token, "Illegal use of `using` statement.");
|
return make_bad_stmt(f, token, f->curr_token);
|
||||||
return make_bad_stmt(f, token, f->curr_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_using_stmt(f, token, node);
|
|
||||||
} break;
|
} 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: {
|
case Token_push_allocator: {
|
||||||
next_token(f);
|
next_token(f);
|
||||||
isize prev_level = f->expr_level;
|
isize prev_level = f->expr_level;
|
||||||
@@ -3237,21 +3282,6 @@ AstNode *parse_stmt(AstFile *f) {
|
|||||||
}
|
}
|
||||||
expect_semicolon(f, s);
|
expect_semicolon(f, s);
|
||||||
return 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"))) {
|
} else if (str_eq(tag, str_lit("bounds_check"))) {
|
||||||
s = parse_stmt(f);
|
s = parse_stmt(f);
|
||||||
s->stmt_state_flags |= StmtStateFlag_bounds_check;
|
s->stmt_state_flags |= StmtStateFlag_bounds_check;
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
|||||||
TOKEN_KIND(Token_using, "using"), \
|
TOKEN_KIND(Token_using, "using"), \
|
||||||
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
||||||
TOKEN_KIND(Token_immutable, "immutable"), \
|
TOKEN_KIND(Token_immutable, "immutable"), \
|
||||||
|
TOKEN_KIND(Token_thread_local, "thread_local"), \
|
||||||
TOKEN_KIND(Token_asm, "asm"), \
|
TOKEN_KIND(Token_asm, "asm"), \
|
||||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||||
|
|||||||
Reference in New Issue
Block a user