mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
Code use API rather than raw CheckerInfo; begin work on generic procedures
This commit is contained in:
+25
-26
@@ -312,6 +312,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|||||||
ast_node(pd, ProcDecl, d->proc_decl);
|
ast_node(pd, ProcDecl, d->proc_decl);
|
||||||
|
|
||||||
check_open_scope(c, pd->type);
|
check_open_scope(c, pd->type);
|
||||||
|
defer (check_close_scope(c));
|
||||||
|
|
||||||
check_procedure_type(c, proc_type, pd->type);
|
check_procedure_type(c, proc_type, pd->type);
|
||||||
|
|
||||||
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
||||||
@@ -322,15 +324,14 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|||||||
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
|
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
|
||||||
|
|
||||||
|
|
||||||
|
TypeProc *pt = &proc_type->Proc;
|
||||||
|
|
||||||
if (d->scope->is_file && e->token.string == "main") {
|
if (d->scope->is_file && e->token.string == "main") {
|
||||||
if (proc_type != NULL) {
|
if (pt->param_count != 0 ||
|
||||||
TypeProc *pt = &proc_type->Proc;
|
pt->result_count != 0) {
|
||||||
if (pt->param_count != 0 ||
|
gbString str = type_to_string(proc_type);
|
||||||
pt->result_count != 0) {
|
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||||
gbString str = type_to_string(proc_type);
|
gb_string_free(str);
|
||||||
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
|
||||||
gb_string_free(str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,6 +344,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (pt->is_generic) {
|
||||||
|
if (pd->body == NULL) {
|
||||||
|
error(e->token, "Generic procedures must have a body");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pd->body != NULL) {
|
if (pd->body != NULL) {
|
||||||
if (is_foreign) {
|
if (is_foreign) {
|
||||||
error_node(pd->body, "A foreign procedure cannot have a body");
|
error_node(pd->body, "A foreign procedure cannot have a body");
|
||||||
@@ -357,14 +364,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|||||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
|
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pt->result_count == 0 && is_require_results) {
|
||||||
if (proc_type != NULL && is_type_proc(proc_type)) {
|
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
|
||||||
TypeProc *tp = &proc_type->Proc;
|
} else {
|
||||||
if (tp->result_count == 0 && is_require_results) {
|
pt->require_results = is_require_results;
|
||||||
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
|
|
||||||
} else {
|
|
||||||
tp->require_results = is_require_results;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_foreign) {
|
if (is_foreign) {
|
||||||
@@ -428,8 +431,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
check_close_scope(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||||
@@ -506,10 +507,8 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (d == NULL) {
|
if (d == NULL) {
|
||||||
DeclInfo **found = map_get(&c->info.entities, hash_pointer(e));
|
d = decl_info_of_entity(&c->info, e);
|
||||||
if (found) {
|
if (d == NULL) {
|
||||||
d = *found;
|
|
||||||
} else {
|
|
||||||
// TODO(bill): Err here?
|
// TODO(bill): Err here?
|
||||||
e->type = t_invalid;
|
e->type = t_invalid;
|
||||||
set_base_type(named_type, t_invalid);
|
set_base_type(named_type, t_invalid);
|
||||||
@@ -577,10 +576,10 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
|||||||
String name = e->token.string;
|
String name = e->token.string;
|
||||||
Type *t = base_type(type_deref(e->type));
|
Type *t = base_type(type_deref(e->type));
|
||||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||||
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
|
Scope *scope = scope_of_node(&c->info, t->Record.node);
|
||||||
GB_ASSERT(found != NULL);
|
GB_ASSERT(scope != NULL);
|
||||||
for_array(i, (*found)->elements.entries) {
|
for_array(i, scope->elements.entries) {
|
||||||
Entity *f = (*found)->elements.entries[i].value;
|
Entity *f = scope->elements.entries[i].value;
|
||||||
if (f->kind == Entity_Variable) {
|
if (f->kind == Entity_Variable) {
|
||||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||||
uvar->Variable.is_immutable = is_immutable;
|
uvar->Variable.is_immutable = is_immutable;
|
||||||
|
|||||||
+96
-51
@@ -57,9 +57,8 @@ void check_scope_decls(Checker *c, Array<AstNode *> nodes, isize reserve_size) {
|
|||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
DeclInfo **found = map_get(&c->info.entities, hash_pointer(e));
|
DeclInfo *d = decl_info_of_entity(&c->info, e);
|
||||||
if (found != NULL) {
|
if (d != NULL) {
|
||||||
DeclInfo *d = *found;
|
|
||||||
check_entity_decl(c, e, d, NULL);
|
check_entity_decl(c, e, d, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1069,7 +1068,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
|
|
||||||
if (type_expr == NULL) {
|
if (type_expr == NULL) {
|
||||||
Operand o = {};
|
Operand o = {};
|
||||||
check_expr(c, &o, default_value);
|
check_expr_or_type(c, &o, default_value);
|
||||||
if (is_operand_nil(o)) {
|
if (is_operand_nil(o)) {
|
||||||
default_is_nil = true;
|
default_is_nil = true;
|
||||||
} else if (o.mode != Addressing_Constant) {
|
} else if (o.mode != Addressing_Constant) {
|
||||||
@@ -1088,8 +1087,11 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
error_node(param, "Invalid AST: Invalid variadic parameter");
|
error_node(param, "Invalid AST: Invalid variadic parameter");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (type_expr->kind == AstNode_HelperType) {
|
||||||
type = check_type(c, type_expr);
|
type = make_type_generic(c->allocator, 0);
|
||||||
|
} else {
|
||||||
|
type = check_type(c, type_expr);
|
||||||
|
}
|
||||||
|
|
||||||
if (default_value != NULL) {
|
if (default_value != NULL) {
|
||||||
Operand o = {};
|
Operand o = {};
|
||||||
@@ -1135,13 +1137,18 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
|
|||||||
for_array(j, p->names) {
|
for_array(j, p->names) {
|
||||||
AstNode *name = p->names[j];
|
AstNode *name = p->names[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,
|
Entity *param = NULL;
|
||||||
(p->flags&FieldFlag_using) != 0, false);
|
if (type->kind == Type_Generic) {
|
||||||
|
param = make_entity_type_name(c->allocator, scope, name->Ident, type);
|
||||||
|
} else {
|
||||||
|
param = make_entity_param(c->allocator, scope, name->Ident, type,
|
||||||
|
(p->flags&FieldFlag_using) != 0, false);
|
||||||
|
param->Variable.default_value = value;
|
||||||
|
param->Variable.default_is_nil = default_is_nil;
|
||||||
|
}
|
||||||
if (p->flags&FieldFlag_no_alias) {
|
if (p->flags&FieldFlag_no_alias) {
|
||||||
param->flags |= EntityFlag_NoAlias;
|
param->flags |= EntityFlag_NoAlias;
|
||||||
}
|
}
|
||||||
param->Variable.default_value = value;
|
|
||||||
param->Variable.default_is_nil = default_is_nil;
|
|
||||||
|
|
||||||
add_entity(c, scope, name, param);
|
add_entity(c, scope, name, param);
|
||||||
variables[variable_index++] = param;
|
variables[variable_index++] = param;
|
||||||
@@ -1436,6 +1443,15 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
|||||||
type->Proc.c_vararg = true;
|
type->Proc.c_vararg = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_generic = false;
|
||||||
|
for (isize i = 0; i < param_count; i++) {
|
||||||
|
Entity *e = params->Tuple.variables[i];
|
||||||
|
if (e->type->kind == Type_Generic) {
|
||||||
|
is_generic = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type->Proc.is_generic = is_generic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1593,7 +1609,7 @@ Entity *check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *
|
|||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
case Entity_LibraryName:
|
case Entity_LibraryName:
|
||||||
error_node(n, "Use of library `%.*s` not in #foreign tag", LIT(name));
|
error_node(n, "Use of library `%.*s` not in foreign block", LIT(name));
|
||||||
return e;
|
return e;
|
||||||
|
|
||||||
case Entity_Label:
|
case Entity_Label:
|
||||||
@@ -1844,11 +1860,6 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
|
|||||||
} */
|
} */
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ht, HelperType, e);
|
|
||||||
*type = check_type(c, ht->type);
|
|
||||||
return true;
|
|
||||||
case_end;
|
|
||||||
|
|
||||||
case_ast_node(pt, PointerType, e);
|
case_ast_node(pt, PointerType, e);
|
||||||
Type *elem = check_type(c, pt->type);
|
Type *elem = check_type(c, pt->type);
|
||||||
i64 esz = type_size_of(c->allocator, elem);
|
i64 esz = type_size_of(c->allocator, elem);
|
||||||
@@ -2559,7 +2570,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
|
|||||||
|
|
||||||
TokenPos pos = ast_node_token(x->expr).pos;
|
TokenPos pos = ast_node_token(x->expr).pos;
|
||||||
if (x_is_untyped) {
|
if (x_is_untyped) {
|
||||||
ExprInfo *info = map_get(&c->info.untyped, hash_pointer(x->expr));
|
ExprInfo *info = check_get_expr_info(&c->info, x->expr);
|
||||||
if (info != NULL) {
|
if (info != NULL) {
|
||||||
info->is_lhs = true;
|
info->is_lhs = true;
|
||||||
}
|
}
|
||||||
@@ -3014,8 +3025,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
|
|||||||
|
|
||||||
|
|
||||||
void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
||||||
HashKey key = hash_pointer(e);
|
ExprInfo *found = check_get_expr_info(&c->info, e);
|
||||||
ExprInfo *found = map_get(&c->info.untyped, key);
|
|
||||||
if (found == NULL) {
|
if (found == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3054,12 +3064,12 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
|||||||
|
|
||||||
if (!final && is_type_untyped(type)) {
|
if (!final && is_type_untyped(type)) {
|
||||||
old.type = base_type(type);
|
old.type = base_type(type);
|
||||||
map_set(&c->info.untyped, key, old);
|
check_set_expr_info(&c->info, e, old);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to remove it and then give it a new one
|
// We need to remove it and then give it a new one
|
||||||
map_remove(&c->info.untyped, key);
|
check_remove_expr_info(&c->info, e);
|
||||||
|
|
||||||
if (old.is_lhs && !is_type_integer(type)) {
|
if (old.is_lhs && !is_type_integer(type)) {
|
||||||
gbString expr_str = expr_to_string(e);
|
gbString expr_str = expr_to_string(e);
|
||||||
@@ -3074,7 +3084,7 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
|
void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
|
||||||
ExprInfo *found = map_get(&c->info.untyped, hash_pointer(e));
|
ExprInfo *found = check_get_expr_info(&c->info, e);
|
||||||
if (found) {
|
if (found) {
|
||||||
found->value = value;
|
found->value = value;
|
||||||
}
|
}
|
||||||
@@ -4741,7 +4751,13 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array<Operand> *operand
|
|||||||
bool optional_ok = false;
|
bool optional_ok = false;
|
||||||
for_array(i, rhs) {
|
for_array(i, rhs) {
|
||||||
Operand o = {};
|
Operand o = {};
|
||||||
check_multi_expr(c, &o, rhs[i]);
|
check_expr_base(c, &o, rhs[i], NULL);
|
||||||
|
if (o.mode == Addressing_NoValue) {
|
||||||
|
error_operand_no_value(&o);
|
||||||
|
o.mode = Addressing_Invalid;
|
||||||
|
}
|
||||||
|
// check_multi_expr(c, &o, rhs[i]);
|
||||||
|
|
||||||
|
|
||||||
if (o.type == NULL || o.type->kind != Type_Tuple) {
|
if (o.type == NULL || o.type->kind != Type_Tuple) {
|
||||||
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
|
if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
|
||||||
@@ -4801,6 +4817,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
if (param_tuple != NULL) {
|
if (param_tuple != NULL) {
|
||||||
for (isize i = param_count-1; i >= 0; i--) {
|
for (isize i = param_count-1; i >= 0; i--) {
|
||||||
Entity *e = param_tuple->variables[i];
|
Entity *e = param_tuple->variables[i];
|
||||||
|
if (e->kind == Entity_TypeName) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
GB_ASSERT(e->kind == Entity_Variable);
|
GB_ASSERT(e->kind == Entity_Variable);
|
||||||
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
if (e->Variable.default_value.kind != ExactValue_Invalid) {
|
||||||
param_count_excluding_defaults--;
|
param_count_excluding_defaults--;
|
||||||
@@ -4863,13 +4883,25 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
|||||||
|
|
||||||
CallArgumentError err = CallArgumentError_None;
|
CallArgumentError err = CallArgumentError_None;
|
||||||
|
|
||||||
|
|
||||||
GB_ASSERT(proc_type->Proc.params != NULL);
|
GB_ASSERT(proc_type->Proc.params != NULL);
|
||||||
Entity **sig_params = param_tuple->variables;
|
Entity **sig_params = param_tuple->variables;
|
||||||
isize operand_index = 0;
|
isize operand_index = 0;
|
||||||
isize max_operand_count = gb_min(param_count, operands.count);
|
isize max_operand_count = gb_min(param_count, operands.count);
|
||||||
for (; operand_index < max_operand_count; operand_index++) {
|
for (; operand_index < max_operand_count; operand_index++) {
|
||||||
Type *t = sig_params[operand_index]->type;
|
Entity *e = sig_params[operand_index];
|
||||||
|
Type *t = e->type;
|
||||||
Operand o = operands[operand_index];
|
Operand o = operands[operand_index];
|
||||||
|
if (e->kind == Entity_TypeName) {
|
||||||
|
GB_ASSERT(proc_type->Proc.is_generic);
|
||||||
|
GB_ASSERT(!variadic);
|
||||||
|
if (o.mode == Addressing_Invalid) {
|
||||||
|
continue;
|
||||||
|
} else if (o.mode != Addressing_Type) {
|
||||||
|
error_node(o.expr, "Expected a type for the argument");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (variadic) {
|
if (variadic) {
|
||||||
o = operands[operand_index];
|
o = operands[operand_index];
|
||||||
}
|
}
|
||||||
@@ -4984,17 +5016,27 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
|||||||
|
|
||||||
params_visited[index] = true;
|
params_visited[index] = true;
|
||||||
Operand *o = &operands[i];
|
Operand *o = &operands[i];
|
||||||
|
Entity *e = pt->params->Tuple.variables[index];
|
||||||
|
|
||||||
Type *param_type = pt->params->Tuple.variables[index]->type;
|
if (e->kind == Entity_TypeName) {
|
||||||
|
GB_ASSERT(pt->is_generic);
|
||||||
i64 s = 0;
|
GB_ASSERT(!pt->variadic);
|
||||||
if (!check_is_assignable_to_with_score(c, o, param_type, &s)) {
|
if (o->mode == Addressing_Invalid) {
|
||||||
if (show_error) {
|
continue;
|
||||||
check_assignment(c, o, param_type, str_lit("procedure argument"));
|
} else if (o->mode != Addressing_Type) {
|
||||||
|
error_node(o->expr, "Expected a type for the argument");
|
||||||
}
|
}
|
||||||
err = CallArgumentError_WrongTypes;
|
score += 1;
|
||||||
|
} else {
|
||||||
|
i64 s = 0;
|
||||||
|
if (!check_is_assignable_to_with_score(c, o, e->type, &s)) {
|
||||||
|
if (show_error) {
|
||||||
|
check_assignment(c, o, e->type, str_lit("procedure argument"));
|
||||||
|
}
|
||||||
|
err = CallArgumentError_WrongTypes;
|
||||||
|
}
|
||||||
|
score += s;
|
||||||
}
|
}
|
||||||
score += s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -5079,9 +5121,8 @@ Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNod
|
|||||||
|
|
||||||
for (isize i = 0; i < overload_count; i++) {
|
for (isize i = 0; i < overload_count; i++) {
|
||||||
Entity *e = procs[i];
|
Entity *e = procs[i];
|
||||||
DeclInfo **found = map_get(&c->info.entities, hash_pointer(e));
|
DeclInfo *d = decl_info_of_entity(&c->info, e);
|
||||||
GB_ASSERT(found != NULL);
|
GB_ASSERT(d != NULL);
|
||||||
DeclInfo *d = *found;
|
|
||||||
check_entity_decl(c, e, d, NULL);
|
check_entity_decl(c, e, d, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5277,18 +5318,27 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
|
|||||||
operand->expr = call;
|
operand->expr = call;
|
||||||
return Expr_Stmt;
|
return Expr_Stmt;
|
||||||
}
|
}
|
||||||
switch (pt->Proc.result_count) {
|
|
||||||
case 0:
|
bool results_are_generic = false;
|
||||||
|
if (pt->Proc.results != NULL) {
|
||||||
|
results_are_generic = is_type_generic(pt->Proc.results);
|
||||||
|
}
|
||||||
|
if (results_are_generic) {
|
||||||
operand->mode = Addressing_NoValue;
|
operand->mode = Addressing_NoValue;
|
||||||
break;
|
} else {
|
||||||
case 1:
|
switch (pt->Proc.result_count) {
|
||||||
operand->mode = Addressing_Value;
|
case 0:
|
||||||
operand->type = pt->Proc.results->Tuple.variables[0]->type;
|
operand->mode = Addressing_NoValue;
|
||||||
break;
|
break;
|
||||||
default:
|
case 1:
|
||||||
operand->mode = Addressing_Value;
|
operand->mode = Addressing_Value;
|
||||||
operand->type = pt->Proc.results;
|
operand->type = pt->Proc.results->Tuple.variables[0]->type;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
operand->mode = Addressing_Value;
|
||||||
|
operand->type = pt->Proc.results;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operand->expr = call;
|
operand->expr = call;
|
||||||
@@ -6628,11 +6678,6 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
|||||||
str = gb_string_appendc(str, "}");
|
str = gb_string_appendc(str, "}");
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ht, HelperType, node);
|
|
||||||
str = gb_string_appendc(str, "#type ");
|
|
||||||
str = write_expr_to_string(str, ht->type);
|
|
||||||
case_end;
|
|
||||||
|
|
||||||
case_ast_node(at, AtomicType, node);
|
case_ast_node(at, AtomicType, node);
|
||||||
str = gb_string_appendc(str, "atomic ");
|
str = gb_string_appendc(str, "atomic ");
|
||||||
str = write_expr_to_string(str, at->type);
|
str = write_expr_to_string(str, at->type);
|
||||||
|
|||||||
+4
-7
@@ -530,9 +530,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo
|
|||||||
Type *t = base_type(type_deref(e->type));
|
Type *t = base_type(type_deref(e->type));
|
||||||
if (is_type_struct(t) || is_type_raw_union(t) || is_type_union(t)) {
|
if (is_type_struct(t) || is_type_raw_union(t) || is_type_union(t)) {
|
||||||
// TODO(bill): Make it work for unions too
|
// TODO(bill): Make it work for unions too
|
||||||
Scope **found_ = map_get(&c->info.scopes, hash_pointer(t->Record.node));
|
Scope *found = scope_of_node(&c->info, t->Record.node);
|
||||||
GB_ASSERT(found_ != NULL);
|
|
||||||
Scope *found = *found_;
|
|
||||||
for_array(i, found->elements.entries) {
|
for_array(i, found->elements.entries) {
|
||||||
Entity *f = found->elements.entries[i].value;
|
Entity *f = found->elements.entries[i].value;
|
||||||
if (f->kind == Entity_Variable) {
|
if (f->kind == Entity_Variable) {
|
||||||
@@ -1695,10 +1693,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
Type *t = base_type(type_deref(e->type));
|
Type *t = base_type(type_deref(e->type));
|
||||||
|
|
||||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||||
Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node));
|
Scope *scope = scope_of_node(&c->info, t->Record.node);
|
||||||
GB_ASSERT(found != NULL);
|
for_array(i, scope->elements.entries) {
|
||||||
for_array(i, (*found)->elements.entries) {
|
Entity *f = scope->elements.entries[i].value;
|
||||||
Entity *f = (*found)->elements.entries[i].value;
|
|
||||||
if (f->kind == Entity_Variable) {
|
if (f->kind == Entity_Variable) {
|
||||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||||
uvar->Variable.is_immutable = is_immutable;
|
uvar->Variable.is_immutable = is_immutable;
|
||||||
|
|||||||
+66
-22
@@ -284,7 +284,8 @@ struct Checker {
|
|||||||
|
|
||||||
AstFile * curr_ast_file;
|
AstFile * curr_ast_file;
|
||||||
Scope * global_scope;
|
Scope * global_scope;
|
||||||
Array<ProcedureInfo> procs; // NOTE(bill): Procedures to check
|
// NOTE(bill): Procedures to check
|
||||||
|
Map<ProcedureInfo> procs; // Key: DeclInfo *
|
||||||
Array<DelayedDecl> delayed_imports;
|
Array<DelayedDecl> delayed_imports;
|
||||||
Array<DelayedDecl> delayed_foreign_libraries;
|
Array<DelayedDecl> delayed_foreign_libraries;
|
||||||
Array<CheckerFileNode> file_nodes;
|
Array<CheckerFileNode> file_nodes;
|
||||||
@@ -307,6 +308,22 @@ struct DelayedEntity {
|
|||||||
DeclInfo * decl;
|
DeclInfo * decl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Entity * entity_of_ident (CheckerInfo *i, AstNode *identifier);
|
||||||
|
TypeAndValue type_and_value_of_expr (CheckerInfo *i, AstNode *expr);
|
||||||
|
Type * type_of_expr (CheckerInfo *i, AstNode *expr);
|
||||||
|
Entity * implicit_entity_of_node(CheckerInfo *i, AstNode *clause);
|
||||||
|
DeclInfo * decl_info_of_entity (CheckerInfo *i, Entity * e);
|
||||||
|
DeclInfo * decl_info_of_ident (CheckerInfo *i, AstNode *ident);
|
||||||
|
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
|
||||||
|
Scope * scope_of_node (CheckerInfo *i, AstNode *node);
|
||||||
|
ExprInfo * check_get_expr_info (CheckerInfo *i, AstNode *expr);
|
||||||
|
void check_set_expr_info (CheckerInfo *i, AstNode *expr, ExprInfo info);
|
||||||
|
void check_remove_expr_info (CheckerInfo *i, AstNode *expr);
|
||||||
|
|
||||||
|
Entity *current_scope_lookup_entity(Scope *s, String name);
|
||||||
|
void scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entity **entity_);
|
||||||
|
Entity *scope_lookup_entity (Scope *s, String name);
|
||||||
|
Entity *scope_insert_entity (Scope *s, Entity *entity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -596,11 +613,12 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
|
|||||||
|
|
||||||
void add_global_string_constant(gbAllocator a, String name, String value) {
|
void add_global_string_constant(gbAllocator a, String name, String value) {
|
||||||
add_global_constant(a, name, t_untyped_string, exact_value_string(value));
|
add_global_constant(a, name, t_untyped_string, exact_value_string(value));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void init_universal_scope(void) {
|
void init_universal_scope(void) {
|
||||||
BuildContext *bc = &build_context;
|
BuildContext *bc = &build_context;
|
||||||
// NOTE(bill): No need to free these
|
// NOTE(bill): No need to free these
|
||||||
@@ -701,7 +719,7 @@ void init_checker(Checker *c, Parser *parser, BuildContext *bc) {
|
|||||||
init_checker_info(&c->info);
|
init_checker_info(&c->info);
|
||||||
|
|
||||||
array_init(&c->proc_stack, a);
|
array_init(&c->proc_stack, a);
|
||||||
array_init(&c->procs, a);
|
map_init(&c->procs, a);
|
||||||
array_init(&c->delayed_imports, a);
|
array_init(&c->delayed_imports, a);
|
||||||
array_init(&c->delayed_foreign_libraries, a);
|
array_init(&c->delayed_foreign_libraries, a);
|
||||||
array_init(&c->file_nodes, a);
|
array_init(&c->file_nodes, a);
|
||||||
@@ -738,7 +756,7 @@ void destroy_checker(Checker *c) {
|
|||||||
destroy_checker_info(&c->info);
|
destroy_checker_info(&c->info);
|
||||||
destroy_scope(c->global_scope);
|
destroy_scope(c->global_scope);
|
||||||
array_free(&c->proc_stack);
|
array_free(&c->proc_stack);
|
||||||
array_free(&c->procs);
|
map_destroy(&c->procs);
|
||||||
array_free(&c->delayed_imports);
|
array_free(&c->delayed_imports);
|
||||||
array_free(&c->delayed_foreign_libraries);
|
array_free(&c->delayed_foreign_libraries);
|
||||||
array_free(&c->file_nodes);
|
array_free(&c->file_nodes);
|
||||||
@@ -761,9 +779,9 @@ Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeAndValue type_and_value_of_expr(CheckerInfo *i, AstNode *expression) {
|
TypeAndValue type_and_value_of_expr(CheckerInfo *i, AstNode *expr) {
|
||||||
TypeAndValue result = {};
|
TypeAndValue result = {};
|
||||||
TypeAndValue *found = map_get(&i->types, hash_pointer(expression));
|
TypeAndValue *found = map_get(&i->types, hash_pointer(expr));
|
||||||
if (found) result = *found;
|
if (found) result = *found;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -812,8 +830,22 @@ AstFile *ast_file_of_filename(CheckerInfo *i, String filename) {
|
|||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
Scope *scope_of_node(CheckerInfo *i, AstNode *node) {
|
||||||
|
Scope **found = map_get(&i->scopes, hash_pointer(node));
|
||||||
|
if (found) {
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ExprInfo *check_get_expr_info(CheckerInfo *i, AstNode *expr) {
|
||||||
|
return map_get(&i->untyped, hash_pointer(expr));
|
||||||
|
}
|
||||||
|
void check_set_expr_info(CheckerInfo *i, AstNode *expr, ExprInfo info) {
|
||||||
|
map_set(&i->untyped, hash_pointer(expr), info);
|
||||||
|
}
|
||||||
|
void check_remove_expr_info(CheckerInfo *i, AstNode *expr) {
|
||||||
|
map_remove(&i->untyped, hash_pointer(expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1082,7 +1114,7 @@ void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *dec
|
|||||||
info.type = type;
|
info.type = type;
|
||||||
info.body = body;
|
info.body = body;
|
||||||
info.tags = tags;
|
info.tags = tags;
|
||||||
array_add(&c->procs, info);
|
map_set(&c->procs, hash_pointer(decl), info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_procedure(Checker *c, Type *type) {
|
void push_procedure(Checker *c, Type *type) {
|
||||||
@@ -1113,24 +1145,21 @@ void add_curr_ast_file(Checker *c, AstFile *file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *entity) {
|
||||||
|
if (entity == NULL) {
|
||||||
void add_dependency_to_map(Map<Entity *> *map, CheckerInfo *info, Entity *node) {
|
|
||||||
if (node == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (map_get(map, hash_pointer(node)) != NULL) {
|
if (map_get(map, hash_pointer(entity)) != NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
map_set(map, hash_pointer(node), node);
|
map_set(map, hash_pointer(entity), entity);
|
||||||
|
|
||||||
|
|
||||||
DeclInfo **found = map_get(&info->entities, hash_pointer(node));
|
DeclInfo *decl = decl_info_of_entity(info, entity);
|
||||||
if (found == NULL) {
|
if (decl == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclInfo *decl = *found;
|
|
||||||
for_array(i, decl->deps.entries) {
|
for_array(i, decl->deps.entries) {
|
||||||
Entity *e = cast(Entity *)decl->deps.entries[i].key.ptr;
|
Entity *e = cast(Entity *)decl->deps.entries[i].key.ptr;
|
||||||
add_dependency_to_map(map, info, e);
|
add_dependency_to_map(map, info, e);
|
||||||
@@ -1333,6 +1362,15 @@ void check_procedure_overloading(Checker *c, Entity *e) {
|
|||||||
|
|
||||||
TokenPos pos = q->token.pos;
|
TokenPos pos = q->token.pos;
|
||||||
|
|
||||||
|
if (is_type_proc(q->type)) {
|
||||||
|
TypeProc *ptq = &base_type(q->type)->Proc;
|
||||||
|
if (ptq->is_generic) {
|
||||||
|
q->type = t_invalid;
|
||||||
|
error(q->token, "Generic procedure `%.*s` cannot be overloaded", LIT(name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
|
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ProcOverload_Identical:
|
case ProcOverload_Identical:
|
||||||
@@ -2132,9 +2170,17 @@ void check_parsed_files(Checker *c) {
|
|||||||
|
|
||||||
// Check procedure bodies
|
// Check procedure bodies
|
||||||
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
// NOTE(bill): Nested procedures bodies will be added to this "queue"
|
||||||
for_array(i, c->procs) {
|
for_array(i, c->procs.entries) {
|
||||||
ProcedureInfo *pi = &c->procs[i];
|
ProcedureInfo *pi = &c->procs.entries[i].value;
|
||||||
CheckerContext prev_context = c->context;
|
CheckerContext prev_context = c->context;
|
||||||
|
defer (c->context = prev_context);
|
||||||
|
|
||||||
|
TypeProc *pt = &pi->type->Proc;
|
||||||
|
if (pt->is_generic) {
|
||||||
|
error(pi->token, "Generic procedures are not yet supported");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
add_curr_ast_file(c, pi->file);
|
add_curr_ast_file(c, pi->file);
|
||||||
|
|
||||||
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
||||||
@@ -2150,8 +2196,6 @@ void check_parsed_files(Checker *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_proc_body(c, pi->token, pi->decl, pi->type, pi->body);
|
check_proc_body(c, pi->token, pi->decl, pi->type, pi->body);
|
||||||
|
|
||||||
c->context = prev_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add untyped expression values
|
// Add untyped expression values
|
||||||
|
|||||||
+1
-4
@@ -1134,10 +1134,7 @@ irValue *ir_generate_array(irModule *m, Type *elem_type, i64 count, String prefi
|
|||||||
irBlock *ir_new_block(irProcedure *proc, AstNode *node, char *label) {
|
irBlock *ir_new_block(irProcedure *proc, AstNode *node, char *label) {
|
||||||
Scope *scope = NULL;
|
Scope *scope = NULL;
|
||||||
if (node != NULL) {
|
if (node != NULL) {
|
||||||
Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node));
|
scope = scope_of_node(proc->module->info, node);
|
||||||
if (found) {
|
|
||||||
scope = *found;
|
|
||||||
}
|
|
||||||
GB_ASSERT_MSG(scope != NULL, "Block scope not found for %.*s", LIT(ast_node_strings[node->kind]));
|
GB_ASSERT_MSG(scope != NULL, "Block scope not found for %.*s", LIT(ast_node_strings[node->kind]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-19
@@ -363,7 +363,6 @@ AST_NODE_KIND(_DeclEnd, "", i32) \
|
|||||||
AST_NODE_KIND(_TypeBegin, "", i32) \
|
AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||||
AST_NODE_KIND(HelperType, "type", struct { \
|
AST_NODE_KIND(HelperType, "type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
AstNode *type; \
|
|
||||||
}) \
|
}) \
|
||||||
AST_NODE_KIND(ProcType, "procedure type", struct { \
|
AST_NODE_KIND(ProcType, "procedure type", struct { \
|
||||||
Token token; \
|
Token token; \
|
||||||
@@ -790,7 +789,6 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case AstNode_HelperType:
|
case AstNode_HelperType:
|
||||||
n->HelperType.type = clone_ast_node(a, n->HelperType.type);
|
|
||||||
break;
|
break;
|
||||||
case AstNode_ProcType:
|
case AstNode_ProcType:
|
||||||
break;
|
break;
|
||||||
@@ -1313,10 +1311,9 @@ AstNode *ast_union_field(AstFile *f, AstNode *name, AstNode *list) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AstNode *ast_helper_type(AstFile *f, Token token, AstNode *type) {
|
AstNode *ast_helper_type(AstFile *f, Token token) {
|
||||||
AstNode *result = make_ast_node(f, AstNode_HelperType);
|
AstNode *result = make_ast_node(f, AstNode_HelperType);
|
||||||
result->HelperType.token = token;
|
result->HelperType.token = token;
|
||||||
result->HelperType.type = type;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1684,9 +1681,6 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
|
|||||||
case AstNode_TypeMatchStmt:
|
case AstNode_TypeMatchStmt:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case AstNode_HelperType:
|
|
||||||
return is_semicolon_optional_for_node(f, s->HelperType.type);
|
|
||||||
|
|
||||||
case AstNode_PointerType:
|
case AstNode_PointerType:
|
||||||
return is_semicolon_optional_for_node(f, s->PointerType.type);
|
return is_semicolon_optional_for_node(f, s->PointerType.type);
|
||||||
|
|
||||||
@@ -2108,11 +2102,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
|||||||
return ast_paren_expr(f, operand, open, close);
|
return ast_paren_expr(f, operand, open, close);
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token_type: {
|
|
||||||
Token token = expect_token(f, Token_type);
|
|
||||||
return ast_helper_type(f, token, parse_type(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
case Token_Hash: {
|
case Token_Hash: {
|
||||||
Token token = expect_token(f, Token_Hash);
|
Token token = expect_token(f, Token_Hash);
|
||||||
Token name = expect_token(f, Token_Ident);
|
Token name = expect_token(f, Token_Ident);
|
||||||
@@ -3057,7 +3046,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) {
|
|||||||
return ast_proc_type(f, proc_token, params, results, tags, cc);
|
return ast_proc_type(f, proc_token, params, results, tags, cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
|
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis, bool allow_type_token) {
|
||||||
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
|
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
|
||||||
Token tok = f->curr_token;
|
Token tok = f->curr_token;
|
||||||
next_token(f);
|
next_token(f);
|
||||||
@@ -3068,7 +3057,13 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
|
|||||||
}
|
}
|
||||||
return ast_ellipsis(f, tok, type);
|
return ast_ellipsis(f, tok, type);
|
||||||
}
|
}
|
||||||
AstNode *type = parse_type_attempt(f);
|
AstNode *type = NULL;
|
||||||
|
if (allow_type_token &&
|
||||||
|
f->curr_token.kind == Token_type) {
|
||||||
|
type = ast_helper_type(f, expect_token(f, Token_type));
|
||||||
|
} else {
|
||||||
|
type = parse_type_attempt(f);
|
||||||
|
}
|
||||||
if (type == NULL) {
|
if (type == NULL) {
|
||||||
Token tok = f->curr_token;
|
Token tok = f->curr_token;
|
||||||
error(tok, "Expected a type");
|
error(tok, "Expected a type");
|
||||||
@@ -3221,7 +3216,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
f->curr_token.kind != Token_Colon &&
|
f->curr_token.kind != Token_Colon &&
|
||||||
f->curr_token.kind != Token_EOF) {
|
f->curr_token.kind != Token_EOF) {
|
||||||
u32 flags = parse_field_prefixes(f);
|
u32 flags = parse_field_prefixes(f);
|
||||||
AstNode *param = parse_var_type(f, allow_ellipsis);
|
AstNode *param = parse_var_type(f, allow_ellipsis, is_procedure);
|
||||||
AstNodeAndFlags naf = {param, flags};
|
AstNodeAndFlags naf = {param, flags};
|
||||||
array_add(&list, naf);
|
array_add(&list, naf);
|
||||||
if (f->curr_token.kind != Token_Comma) {
|
if (f->curr_token.kind != Token_Comma) {
|
||||||
@@ -3248,7 +3243,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
|
|
||||||
if (f->curr_token.kind != Token_Eq) {
|
if (f->curr_token.kind != Token_Eq) {
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
type = parse_var_type(f, allow_ellipsis);
|
type = parse_var_type(f, allow_ellipsis, is_procedure);
|
||||||
}
|
}
|
||||||
if (allow_token(f, Token_Eq)) {
|
if (allow_token(f, Token_Eq)) {
|
||||||
// TODO(bill): Should this be true==lhs or false==rhs?
|
// TODO(bill): Should this be true==lhs or false==rhs?
|
||||||
@@ -3282,7 +3277,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
|||||||
AstNode *default_value = NULL;
|
AstNode *default_value = NULL;
|
||||||
if (f->curr_token.kind != Token_Eq) {
|
if (f->curr_token.kind != Token_Eq) {
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
type = parse_var_type(f, allow_ellipsis);
|
type = parse_var_type(f, allow_ellipsis, is_procedure);
|
||||||
}
|
}
|
||||||
if (allow_token(f, Token_Eq)) {
|
if (allow_token(f, Token_Eq)) {
|
||||||
// TODO(bill): Should this be true==lhs or false==rhs?
|
// TODO(bill): Should this be true==lhs or false==rhs?
|
||||||
@@ -3482,7 +3477,7 @@ AstNode *parse_type_or_ident(AstFile *f) {
|
|||||||
u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags);
|
u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags);
|
||||||
total_decl_name_count += names.count;
|
total_decl_name_count += names.count;
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
AstNode *type = parse_var_type(f, false);
|
AstNode *type = parse_var_type(f, false, false);
|
||||||
array_add(&decls, ast_field(f, names, type, NULL, set_flags));
|
array_add(&decls, ast_field(f, names, type, NULL, set_flags));
|
||||||
} else {
|
} else {
|
||||||
Array<AstNode *> names = parse_ident_list(f);
|
Array<AstNode *> names = parse_ident_list(f);
|
||||||
@@ -3493,7 +3488,7 @@ AstNode *parse_type_or_ident(AstFile *f) {
|
|||||||
u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags);
|
u32 set_flags = check_field_prefixes(f, names.count, FieldFlag_using, decl_flags);
|
||||||
total_decl_name_count += names.count;
|
total_decl_name_count += names.count;
|
||||||
expect_token_after(f, Token_Colon, "field list");
|
expect_token_after(f, Token_Colon, "field list");
|
||||||
AstNode *type = parse_var_type(f, false);
|
AstNode *type = parse_var_type(f, false, false);
|
||||||
array_add(&decls, ast_field(f, names, type, NULL, set_flags));
|
array_add(&decls, ast_field(f, names, type, NULL, set_flags));
|
||||||
} else {
|
} else {
|
||||||
AstNode *name = names[0];
|
AstNode *name = names[0];
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ struct TypeRecord {
|
|||||||
|
|
||||||
#define TYPE_KINDS \
|
#define TYPE_KINDS \
|
||||||
TYPE_KIND(Basic, BasicType) \
|
TYPE_KIND(Basic, BasicType) \
|
||||||
|
TYPE_KIND(Generic, struct{ i64 id; }) \
|
||||||
TYPE_KIND(Pointer, struct { Type *elem; }) \
|
TYPE_KIND(Pointer, struct { Type *elem; }) \
|
||||||
TYPE_KIND(Atomic, struct { Type *elem; }) \
|
TYPE_KIND(Atomic, struct { Type *elem; }) \
|
||||||
TYPE_KIND(Array, struct { Type *elem; i64 count; }) \
|
TYPE_KIND(Array, struct { Type *elem; i64 count; }) \
|
||||||
@@ -145,6 +146,7 @@ struct TypeRecord {
|
|||||||
bool variadic; \
|
bool variadic; \
|
||||||
bool require_results; \
|
bool require_results; \
|
||||||
bool c_vararg; \
|
bool c_vararg; \
|
||||||
|
bool is_generic; \
|
||||||
ProcCallingConvention calling_convention; \
|
ProcCallingConvention calling_convention; \
|
||||||
}) \
|
}) \
|
||||||
TYPE_KIND(Map, struct { \
|
TYPE_KIND(Map, struct { \
|
||||||
@@ -467,6 +469,12 @@ Type *make_type_basic(gbAllocator a, BasicType basic) {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type *make_type_generic(gbAllocator a, i64 id) {
|
||||||
|
Type *t = alloc_type(a, Type_Generic);
|
||||||
|
t->Generic.id = id;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
Type *make_type_pointer(gbAllocator a, Type *elem) {
|
Type *make_type_pointer(gbAllocator a, Type *elem) {
|
||||||
Type *t = alloc_type(a, Type_Pointer);
|
Type *t = alloc_type(a, Type_Pointer);
|
||||||
t->Pointer.elem = elem;
|
t->Pointer.elem = elem;
|
||||||
@@ -917,6 +925,72 @@ bool is_type_indexable(Type *t) {
|
|||||||
return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
|
return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_type_generic(Type *t) {
|
||||||
|
t = core_type(t);
|
||||||
|
switch (t->kind) {
|
||||||
|
case Type_Generic:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case Type_Pointer:
|
||||||
|
return is_type_generic(t->Pointer.elem);
|
||||||
|
case Type_Atomic:
|
||||||
|
return is_type_generic(t->Atomic.elem);
|
||||||
|
case Type_Array:
|
||||||
|
return is_type_generic(t->Array.elem);
|
||||||
|
case Type_DynamicArray:
|
||||||
|
return is_type_generic(t->DynamicArray.elem);
|
||||||
|
case Type_Vector:
|
||||||
|
return is_type_generic(t->Vector.elem);
|
||||||
|
case Type_Slice:
|
||||||
|
return is_type_generic(t->Slice.elem);
|
||||||
|
|
||||||
|
case Type_Tuple:
|
||||||
|
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||||
|
if (is_type_generic(t->Tuple.variables[i]->type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Type_Proc:
|
||||||
|
if (t->Proc.param_count > 0 &&
|
||||||
|
is_type_generic(t->Proc.params)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (t->Proc.result_count > 0 &&
|
||||||
|
is_type_generic(t->Proc.results)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// case Type_Record:
|
||||||
|
// GB_ASSERT(t->Record.kind != TypeRecord_Enum);
|
||||||
|
// for (isize i = 0; i < t->Record.field_count; i++) {
|
||||||
|
// if (is_type_generic(t->Record.fields[i]->type)) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// for (isize i = 1; i < t->Record.variant_count; i++) {
|
||||||
|
// if (is_type_generic(t->Record.variants[i]->type)) {
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case Type_Map:
|
||||||
|
if (is_type_generic(t->Map.key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_type_generic(t->Map.value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool type_has_nil(Type *t) {
|
bool type_has_nil(Type *t) {
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
|
|||||||
Reference in New Issue
Block a user