diff --git a/src/check_decl.cpp b/src/check_decl.cpp index e2f0c45a3..2673a4d17 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -312,6 +312,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { ast_node(pd, ProcDecl, d->proc_decl); check_open_scope(c, pd->type); + defer (check_close_scope(c)); + check_procedure_type(c, proc_type, pd->type); 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; + TypeProc *pt = &proc_type->Proc; + if (d->scope->is_file && e->token.string == "main") { - if (proc_type != NULL) { - TypeProc *pt = &proc_type->Proc; - if (pt->param_count != 0 || - pt->result_count != 0) { - gbString str = type_to_string(proc_type); - error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str); - gb_string_free(str); - } + if (pt->param_count != 0 || + pt->result_count != 0) { + gbString str = type_to_string(proc_type); + 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 (is_foreign) { 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); } - - if (proc_type != NULL && is_type_proc(proc_type)) { - TypeProc *tp = &proc_type->Proc; - if (tp->result_count == 0 && 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 (pt->result_count == 0 && is_require_results) { + error_node(pd->type, "`#require_results` is not needed on a procedure with no results"); + } else { + pt->require_results = is_require_results; } 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) { @@ -506,10 +507,8 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { } if (d == NULL) { - DeclInfo **found = map_get(&c->info.entities, hash_pointer(e)); - if (found) { - d = *found; - } else { + d = decl_info_of_entity(&c->info, e); + if (d == NULL) { // TODO(bill): Err here? e->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; Type *t = base_type(type_deref(e->type)); if (is_type_struct(t) || is_type_raw_union(t)) { - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); - GB_ASSERT(found != NULL); - for_array(i, (*found)->elements.entries) { - Entity *f = (*found)->elements.entries[i].value; + Scope *scope = scope_of_node(&c->info, t->Record.node); + GB_ASSERT(scope != NULL); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[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; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8fdec8e53..6a391f00c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -57,9 +57,8 @@ void check_scope_decls(Checker *c, Array nodes, isize reserve_size) { default: continue; } - DeclInfo **found = map_get(&c->info.entities, hash_pointer(e)); - if (found != NULL) { - DeclInfo *d = *found; + DeclInfo *d = decl_info_of_entity(&c->info, e); + if (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) { Operand o = {}; - check_expr(c, &o, default_value); + check_expr_or_type(c, &o, default_value); if (is_operand_nil(o)) { default_is_nil = true; } 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"); } } - - type = check_type(c, type_expr); + if (type_expr->kind == AstNode_HelperType) { + type = make_type_generic(c->allocator, 0); + } else { + type = check_type(c, type_expr); + } if (default_value != NULL) { Operand o = {}; @@ -1135,13 +1137,18 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari for_array(j, p->names) { AstNode *name = p->names[j]; if (ast_node_expect(name, AstNode_Ident)) { - Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, - (p->flags&FieldFlag_using) != 0, false); + Entity *param = NULL; + 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) { param->flags |= EntityFlag_NoAlias; } - param->Variable.default_value = value; - param->Variable.default_is_nil = default_is_nil; add_entity(c, scope, name, 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; } } + + 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; 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; case Entity_Label: @@ -1844,11 +1860,6 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) } */ case_end; - case_ast_node(ht, HelperType, e); - *type = check_type(c, ht->type); - return true; - case_end; - case_ast_node(pt, PointerType, e); Type *elem = check_type(c, pt->type); 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; 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) { 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) { - HashKey key = hash_pointer(e); - ExprInfo *found = map_get(&c->info.untyped, key); + ExprInfo *found = check_get_expr_info(&c->info, e); if (found == NULL) { return; } @@ -3054,12 +3064,12 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, bool final) { if (!final && is_type_untyped(type)) { old.type = base_type(type); - map_set(&c->info.untyped, key, old); + check_set_expr_info(&c->info, e, old); return; } // 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)) { 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) { - ExprInfo *found = map_get(&c->info.untyped, hash_pointer(e)); + ExprInfo *found = check_get_expr_info(&c->info, e); if (found) { found->value = value; } @@ -4741,7 +4751,13 @@ bool check_unpack_arguments(Checker *c, isize lhs_count, Array *operand bool optional_ok = false; for_array(i, rhs) { 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 (allow_ok && lhs_count == 2 && rhs.count == 1 && @@ -4801,6 +4817,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (param_tuple != NULL) { for (isize i = param_count-1; i >= 0; i--) { Entity *e = param_tuple->variables[i]; + if (e->kind == Entity_TypeName) { + break; + } + GB_ASSERT(e->kind == Entity_Variable); if (e->Variable.default_value.kind != ExactValue_Invalid) { param_count_excluding_defaults--; @@ -4863,13 +4883,25 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { CallArgumentError err = CallArgumentError_None; + GB_ASSERT(proc_type->Proc.params != NULL); Entity **sig_params = param_tuple->variables; isize operand_index = 0; isize max_operand_count = gb_min(param_count, operands.count); 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]; + 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) { o = operands[operand_index]; } @@ -4984,17 +5016,27 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { params_visited[index] = true; Operand *o = &operands[i]; + Entity *e = pt->params->Tuple.variables[index]; - Type *param_type = pt->params->Tuple.variables[index]->type; - - i64 s = 0; - if (!check_is_assignable_to_with_score(c, o, param_type, &s)) { - if (show_error) { - check_assignment(c, o, param_type, str_lit("procedure argument")); + if (e->kind == Entity_TypeName) { + GB_ASSERT(pt->is_generic); + GB_ASSERT(!pt->variadic); + if (o->mode == Addressing_Invalid) { + continue; + } 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++) { Entity *e = procs[i]; - DeclInfo **found = map_get(&c->info.entities, hash_pointer(e)); - GB_ASSERT(found != NULL); - DeclInfo *d = *found; + DeclInfo *d = decl_info_of_entity(&c->info, e); + GB_ASSERT(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; 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; - break; - case 1: - operand->mode = Addressing_Value; - operand->type = pt->Proc.results->Tuple.variables[0]->type; - break; - default: - operand->mode = Addressing_Value; - operand->type = pt->Proc.results; - break; + } else { + switch (pt->Proc.result_count) { + case 0: + operand->mode = Addressing_NoValue; + break; + case 1: + operand->mode = Addressing_Value; + operand->type = pt->Proc.results->Tuple.variables[0]->type; + break; + default: + operand->mode = Addressing_Value; + operand->type = pt->Proc.results; + break; + } } operand->expr = call; @@ -6628,11 +6678,6 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, "}"); 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); str = gb_string_appendc(str, "atomic "); str = write_expr_to_string(str, at->type); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 8b1590e59..fbf301878 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -530,9 +530,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo Type *t = base_type(type_deref(e->type)); if (is_type_struct(t) || is_type_raw_union(t) || is_type_union(t)) { // TODO(bill): Make it work for unions too - Scope **found_ = map_get(&c->info.scopes, hash_pointer(t->Record.node)); - GB_ASSERT(found_ != NULL); - Scope *found = *found_; + Scope *found = scope_of_node(&c->info, t->Record.node); for_array(i, found->elements.entries) { Entity *f = found->elements.entries[i].value; 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)); if (is_type_struct(t) || is_type_raw_union(t)) { - Scope **found = map_get(&c->info.scopes, hash_pointer(t->Record.node)); - GB_ASSERT(found != NULL); - for_array(i, (*found)->elements.entries) { - Entity *f = (*found)->elements.entries[i].value; + Scope *scope = scope_of_node(&c->info, t->Record.node); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[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; diff --git a/src/checker.cpp b/src/checker.cpp index 3dabbb694..7fc849237 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -284,7 +284,8 @@ struct Checker { AstFile * curr_ast_file; Scope * global_scope; - Array procs; // NOTE(bill): Procedures to check + // NOTE(bill): Procedures to check + Map procs; // Key: DeclInfo * Array delayed_imports; Array delayed_foreign_libraries; Array file_nodes; @@ -307,6 +308,22 @@ struct DelayedEntity { 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) { add_global_constant(a, name, t_untyped_string, exact_value_string(value)); - } + + void init_universal_scope(void) { BuildContext *bc = &build_context; // 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); 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_foreign_libraries, a); array_init(&c->file_nodes, a); @@ -738,7 +756,7 @@ void destroy_checker(Checker *c) { destroy_checker_info(&c->info); destroy_scope(c->global_scope); array_free(&c->proc_stack); - array_free(&c->procs); + map_destroy(&c->procs); array_free(&c->delayed_imports); array_free(&c->delayed_foreign_libraries); array_free(&c->file_nodes); @@ -761,9 +779,9 @@ Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) { 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 *found = map_get(&i->types, hash_pointer(expression)); + TypeAndValue *found = map_get(&i->types, hash_pointer(expr)); if (found) result = *found; return result; } @@ -812,8 +830,22 @@ AstFile *ast_file_of_filename(CheckerInfo *i, String filename) { } 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.body = body; info.tags = tags; - array_add(&c->procs, info); + map_set(&c->procs, hash_pointer(decl), info); } 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 *map, CheckerInfo *info, Entity *node) { - if (node == NULL) { +void add_dependency_to_map(Map *map, CheckerInfo *info, Entity *entity) { + if (entity == NULL) { return; } - if (map_get(map, hash_pointer(node)) != NULL) { + if (map_get(map, hash_pointer(entity)) != NULL) { return; } - map_set(map, hash_pointer(node), node); + map_set(map, hash_pointer(entity), entity); - DeclInfo **found = map_get(&info->entities, hash_pointer(node)); - if (found == NULL) { + DeclInfo *decl = decl_info_of_entity(info, entity); + if (decl == NULL) { return; } - DeclInfo *decl = *found; for_array(i, decl->deps.entries) { Entity *e = cast(Entity *)decl->deps.entries[i].key.ptr; add_dependency_to_map(map, info, e); @@ -1333,6 +1362,15 @@ void check_procedure_overloading(Checker *c, Entity *e) { 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); switch (kind) { case ProcOverload_Identical: @@ -2132,9 +2170,17 @@ void check_parsed_files(Checker *c) { // Check procedure bodies // NOTE(bill): Nested procedures bodies will be added to this "queue" - for_array(i, c->procs) { - ProcedureInfo *pi = &c->procs[i]; + for_array(i, c->procs.entries) { + ProcedureInfo *pi = &c->procs.entries[i].value; 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); 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); - - c->context = prev_context; } // Add untyped expression values diff --git a/src/ir.cpp b/src/ir.cpp index b9f07c166..75f9b8153 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -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) { Scope *scope = NULL; if (node != NULL) { - Scope **found = map_get(&proc->module->info->scopes, hash_pointer(node)); - if (found) { - scope = *found; - } + scope = scope_of_node(proc->module->info, node); GB_ASSERT_MSG(scope != NULL, "Block scope not found for %.*s", LIT(ast_node_strings[node->kind])); } diff --git a/src/parser.cpp b/src/parser.cpp index 4f0595d78..8aadba765 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -363,7 +363,6 @@ AST_NODE_KIND(_DeclEnd, "", i32) \ AST_NODE_KIND(_TypeBegin, "", i32) \ AST_NODE_KIND(HelperType, "type", struct { \ Token token; \ - AstNode *type; \ }) \ AST_NODE_KIND(ProcType, "procedure type", struct { \ Token token; \ @@ -790,7 +789,6 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { break; case AstNode_HelperType: - n->HelperType.type = clone_ast_node(a, n->HelperType.type); break; case AstNode_ProcType: 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); result->HelperType.token = token; - result->HelperType.type = type; return result; } @@ -1684,9 +1681,6 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) { case AstNode_TypeMatchStmt: return true; - case AstNode_HelperType: - return is_semicolon_optional_for_node(f, s->HelperType.type); - case AstNode_PointerType: 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); } - case Token_type: { - Token token = expect_token(f, Token_type); - return ast_helper_type(f, token, parse_type(f)); - } - case Token_Hash: { Token token = expect_token(f, Token_Hash); 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); } -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) { Token tok = f->curr_token; next_token(f); @@ -3068,7 +3057,13 @@ AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) { } 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) { Token tok = f->curr_token; 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_EOF) { 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}; array_add(&list, naf); 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) { 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)) { // 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; if (f->curr_token.kind != Token_Eq) { 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)) { // 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); total_decl_name_count += names.count; 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)); } else { Array 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); total_decl_name_count += names.count; 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)); } else { AstNode *name = names[0]; diff --git a/src/types.cpp b/src/types.cpp index 4b1f276c0..94d4cf7c8 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -115,6 +115,7 @@ struct TypeRecord { #define TYPE_KINDS \ TYPE_KIND(Basic, BasicType) \ + TYPE_KIND(Generic, struct{ i64 id; }) \ TYPE_KIND(Pointer, struct { Type *elem; }) \ TYPE_KIND(Atomic, struct { Type *elem; }) \ TYPE_KIND(Array, struct { Type *elem; i64 count; }) \ @@ -145,6 +146,7 @@ struct TypeRecord { bool variadic; \ bool require_results; \ bool c_vararg; \ + bool is_generic; \ ProcCallingConvention calling_convention; \ }) \ TYPE_KIND(Map, struct { \ @@ -467,6 +469,12 @@ Type *make_type_basic(gbAllocator a, BasicType basic) { 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 *t = alloc_type(a, Type_Pointer); 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); } +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) { t = base_type(t);