diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 602e09a0a..d3f82b1d3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -34,7 +34,12 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex Type *t = operand->type; if (is_type_untyped(t)) { if (t == t_invalid || is_type_untyped_nil(t)) { - error(e->token, "Use of untyped nil in %.*s", LIT(context_name)); + error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name)); + e->type = t_invalid; + return NULL; + } + if (t == t_invalid || is_type_untyped_undef(t)) { + error(e->token, "Invalid use of --- in %.*s", LIT(context_name)); e->type = t_invalid; return NULL; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2f39a6b77..1fa0117ac 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -166,6 +166,13 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { Type *src = base_type(s); Type *dst = base_type(type); + if (is_type_untyped_undef(src)) { + if (type_has_undef(dst)) { + return 1; + } + return -1; + } + if (is_type_untyped_nil(src)) { if (type_has_nil(dst)) { return 1; @@ -328,6 +335,11 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n operand->mode = Addressing_Invalid; return; } + if (type == NULL && is_type_untyped_undef(operand->type)) { + error(operand->expr, "Use of --- in %.*s", LIT(context_name)); + operand->mode = Addressing_Invalid; + return; + } target_type = default_type(operand->type); if (type != NULL && !is_type_any(type)) { GB_ASSERT_MSG(is_type_typed(target_type), "%s", type_to_string(type)); @@ -3366,7 +3378,9 @@ void convert_to_typed(Checker *c, Operand *operand, Type *target_type, i32 level default: - if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { + if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) { + target_type = t_untyped_undef; + } else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) { operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); return; @@ -5043,6 +5057,9 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } else { // NOTE(bill): Generate the procedure type for this generic instance // TODO(bill): Clean this shit up! + + ProcedureInfo proc_info = {}; + if (pt->is_generic) { GB_ASSERT(entity != NULL); DeclInfo *old_decl = decl_info_of_entity(&c->info, entity); @@ -5091,8 +5108,13 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { add_entity_and_decl_info(c, ident, gen_entity, d); gen_entity->scope = entity->scope; add_entity_use(c, ident, gen_entity); - check_procedure_later(c, c->curr_ast_file, token, d, final_proc_type, pd->body, tags); + proc_info.file = c->curr_ast_file; + proc_info.token = token; + proc_info.decl = d; + proc_info.type = final_proc_type; + proc_info.body = pd->body; + proc_info.tags = tags; if (found) { array_add(found, gen_entity); @@ -5107,7 +5129,6 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { GB_ASSERT(gen_entity != NULL); } - TypeProc *pt = &final_proc_type->Proc; GB_ASSERT(pt->params != NULL); @@ -5124,6 +5145,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { continue; } else if (o.mode != Addressing_Type) { error(o.expr, "Expected a type for the argument"); + err = CallArgumentError_WrongTypes; } if (are_types_identical(e->type, o.type)) { @@ -5180,6 +5202,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { score += s; } } + + if (gen_entity != NULL && err == CallArgumentError_None) { + check_procedure_later(c, proc_info); + } } } @@ -5748,6 +5774,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t return kind; } + init_preload(c); o->mode = Addressing_Value; o->type = t_context; break; @@ -5761,6 +5788,11 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t check_ident(c, o, node, NULL, type_hint, false); case_end; + case_ast_node(u, Undef, node); + o->mode = Addressing_Value; + o->type = t_untyped_undef; + case_end; + case_ast_node(bl, BasicLit, node); Type *t = t_invalid; diff --git a/src/checker.cpp b/src/checker.cpp index 5f5b8a281..5576180a4 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1168,6 +1168,9 @@ void add_type_info_type(Checker *c, Type *t) { } } +void check_procedure_later(Checker *c, ProcedureInfo info) { + map_set(&c->procs, hash_decl_info(info.decl), info); +} void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, AstNode *body, u64 tags) { ProcedureInfo info = {}; @@ -1177,7 +1180,7 @@ void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *dec info.type = type; info.body = body; info.tags = tags; - map_set(&c->procs, hash_decl_info(decl), info); + check_procedure_later(c, info); } void push_procedure(Checker *c, Type *type) { @@ -2252,6 +2255,9 @@ void check_parsed_files(Checker *c) { // NOTE(bill): Nested procedures bodies will be added to this "queue" for_array(i, c->procs.entries) { ProcedureInfo *pi = &c->procs.entries[i].value; + if (pi->type == NULL) { + continue; + } CheckerContext prev_context = c->context; defer (c->context = prev_context); diff --git a/src/ir.cpp b/src/ir.cpp index 5e05bd804..bd3dd8b36 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -312,6 +312,7 @@ enum irValueKind { irValue_Constant, irValue_ConstantSlice, irValue_Nil, + irValue_Undef, irValue_TypeName, irValue_Global, irValue_Param, @@ -338,6 +339,10 @@ struct irValueNil { Type *type; }; +struct irValueUndef { + Type *type; +}; + struct irValueTypeName { Type * type; String name; @@ -380,6 +385,7 @@ struct irValue { irValueConstant Constant; irValueConstantSlice ConstantSlice; irValueNil Nil; + irValueUndef Undef; irValueTypeName TypeName; irValueGlobal Global; irValueParam Param; @@ -630,6 +636,8 @@ Type *ir_type(irValue *value) { return value->ConstantSlice.type; case irValue_Nil: return value->Nil.type; + case irValue_Undef: + return value->Undef.type; case irValue_TypeName: return value->TypeName.type; case irValue_Global: @@ -806,6 +814,13 @@ irValue *ir_value_nil(gbAllocator a, Type *type) { return v; } +irValue *ir_value_undef(gbAllocator a, Type *type) { + irValue *v = ir_alloc_value(a, irValue_Undef); + v->Undef.type = type; + return v; +} + + String ir_get_global_name(irModule *m, irValue *v) { if (v->kind != irValue_Global) { return str_lit(""); @@ -1261,7 +1276,7 @@ irValue *ir_add_global_string_array(irModule *m, String string) { -irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr) { +irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr, bool zero_initialized) { irBlock *b = proc->decl_block; // all variables must be in the first block irValue *instr = ir_instr_local(proc, e, true); instr->Instr.parent = b; @@ -1269,9 +1284,9 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, AstNode *expr) { array_add(&b->locals, instr); proc->local_count++; - // if (zero_initialized) { + if (zero_initialized) { ir_emit_zero_init(proc, instr); - // } + } if (expr != NULL && proc->entity != NULL) { irDebugInfo *di = *map_get(&proc->module->debug_info, hash_entity(proc->entity)); @@ -1302,7 +1317,7 @@ irValue *ir_add_local_for_identifier(irProcedure *proc, AstNode *name, bool zero return *prev_value; } } - return ir_add_local(proc, e, name); + return ir_add_local(proc, e, name, zero_initialized); } return NULL; } @@ -1318,7 +1333,7 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) { scope, empty_token, type, false); - return ir_add_local(proc, e, NULL); + return ir_add_local(proc, e, NULL, true); } @@ -1351,7 +1366,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_typ switch (p->kind) { case irParamPass_Value: { - irValue *l = ir_add_local(proc, e, expr); + irValue *l = ir_add_local(proc, e, expr, false); ir_emit_store(proc, l, v); return v; } @@ -1360,7 +1375,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_typ return ir_emit_load(proc, v); } case irParamPass_Integer: { - irValue *l = ir_add_local(proc, e, expr); + irValue *l = ir_add_local(proc, e, expr, false); irValue *iptr = ir_emit_conv(proc, l, make_type_pointer(proc->module->allocator, p->type)); ir_emit_store(proc, iptr, v); return ir_emit_load(proc, l); @@ -2771,6 +2786,9 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { if (is_type_untyped_nil(src)) { return ir_value_nil(proc->module->allocator, t); } + if (is_type_untyped_undef(src)) { + return ir_value_undef(proc->module->allocator, t); + } if (value->kind == irValue_Constant) { if (is_type_any(dst)) { @@ -4403,6 +4421,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { return ir_addr_load(proc, ir_build_addr(proc, expr)); case_end; + case_ast_node(u, Undef, expr); + return ir_value_undef(proc->module->allocator, tv.type); + case_end; + case_ast_node(i, Ident, expr); Entity *e = entity_of_ident(proc->module->info, expr); if (e->kind == Entity_Builtin) { @@ -5836,7 +5858,7 @@ void ir_build_range_interval(irProcedure *proc, AstNodeBinaryExpr *node, Type *v void ir_store_type_case_implicit(irProcedure *proc, AstNode *clause, irValue *value) { Entity *e = implicit_entity_of_node(proc->module->info, clause); GB_ASSERT(e != NULL); - irValue *x = ir_add_local(proc, e, NULL); + irValue *x = ir_add_local(proc, e, NULL, false); ir_emit_store(proc, x, value); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 195e61e84..4d2ab8082 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -730,6 +730,10 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin ir_fprintf(f, "zeroinitializer"); break; + case irValue_Undef: + ir_fprintf(f, "undef"); + break; + case irValue_TypeName: ir_print_encoded_local(f, value->TypeName.name); break; diff --git a/src/parser.cpp b/src/parser.cpp index f6345265b..4f5c0ef07 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -140,6 +140,7 @@ Array make_ast_node_array(AstFile *f, isize init_capacity = 8) { #define AST_NODE_KINDS \ AST_NODE_KIND(Ident, "identifier", Token) \ AST_NODE_KIND(Implicit, "implicit", Token) \ + AST_NODE_KIND(Undef, "undef", Token) \ AST_NODE_KIND(BasicLit, "basic literal", Token) \ AST_NODE_KIND(BasicDirective, "basic directive", struct { \ Token token; \ @@ -516,6 +517,7 @@ Token ast_node_token(AstNode *node) { switch (node->kind) { case AstNode_Ident: return node->Ident; case AstNode_Implicit: return node->Implicit; + case AstNode_Undef: return node->Undef; case AstNode_BasicLit: return node->BasicLit; case AstNode_BasicDirective: return node->BasicDirective.token; case AstNode_ProcLit: return ast_node_token(node->ProcLit.type); @@ -635,6 +637,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { case AstNode_Invalid: break; case AstNode_Ident: break; case AstNode_Implicit: break; + case AstNode_Undef: break; case AstNode_BasicLit: break; case AstNode_BasicDirective: break; case AstNode_Ellipsis: @@ -1073,6 +1076,11 @@ AstNode *ast_implicit(AstFile *f, Token token) { result->Implicit = token; return result; } +AstNode *ast_undef(AstFile *f, Token token) { + AstNode *result = make_ast_node(f, AstNode_Undef); + result->Undef = token; + return result; +} AstNode *ast_basic_lit(AstFile *f, Token basic_lit) { @@ -2200,6 +2208,9 @@ AstNode *parse_operand(AstFile *f, bool lhs) { case Token_Ident: return parse_ident(f); + case Token_Undef: + return ast_undef(f, expect_token(f, Token_Undef)); + case Token_context: return ast_implicit(f, expect_token(f, Token_context)); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 09a5d3dd9..c7c53a47e 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -56,6 +56,7 @@ TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \ TOKEN_KIND(Token_ArrowLeft, "<-"), \ TOKEN_KIND(Token_Inc, "++"), \ TOKEN_KIND(Token_Dec, "--"), \ + TOKEN_KIND(Token_Undef, "---"), \ \ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \ TOKEN_KIND(Token_CmpEq, "=="), \ @@ -916,7 +917,24 @@ Token tokenizer_get_token(Tokenizer *t) { case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break; case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break; case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc); break; - case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Dec, '>', Token_ArrowRight); break; + case '-': + token.kind = Token_Sub; + if (t->curr_rune == '=') { + advance_to_next_rune(t); + token.kind = Token_SubEq; + } else if (t->curr_rune == '-') { + advance_to_next_rune(t); + token.kind = Token_Dec; + if (t->curr_rune == '-') { + advance_to_next_rune(t); + token.kind = Token_Undef; + } + } else if (t->curr_rune == '>') { + advance_to_next_rune(t); + token.kind = Token_ArrowRight; + } + break; + case '/': { if (t->curr_rune == '/') { while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) { diff --git a/src/types.cpp b/src/types.cpp index 8f645b431..41ce0a19d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -38,6 +38,7 @@ enum BasicKind { Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, + Basic_UntypedUndef, Basic_COUNT, @@ -267,6 +268,7 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, + {Type_Basic, {Basic_UntypedUndef, BasicFlag_Untyped, 0, STR_LIT("untyped undefined")}}, }; // gb_global Type basic_type_aliases[] = { @@ -311,6 +313,7 @@ gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex]; gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; +gb_global Type *t_untyped_undef = &basic_types[Basic_UntypedUndef]; gb_global Type *t_u8_ptr = NULL; @@ -905,6 +908,10 @@ bool is_type_untyped_nil(Type *t) { t = base_type(t); return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil); } +bool is_type_untyped_undef(Type *t) { + t = base_type(t); + return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUndef); +} @@ -1001,6 +1008,11 @@ bool is_type_generic(Type *t) { } +bool type_has_undef(Type *t) { + t = base_type(t); + return true; +} + bool type_has_nil(Type *t) { t = base_type(t); switch (t->kind) {