diff --git a/code/demo.odin b/code/demo.odin index 9b9566faa..786392b77 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,8 +1,9 @@ #import "fmt.odin" + main :: proc() { maybe_print :: proc(x: ?int) { - if v, ok := maybe_value(x); ok { + if v, ok := x?; ok { fmt.println(v) } else { fmt.println("nowt") diff --git a/core/_preload.odin b/core/_preload.odin index 62b42b8ce..73b8ad358 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -327,6 +327,3 @@ __enum_to_string :: proc(info: ^Type_Info, value: i64) -> string { } return "" } - - - diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 19a4807d8..9e1295b9a 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -168,8 +168,6 @@ enum BuiltinProcId { BuiltinProc_enum_to_string, - BuiltinProc_maybe_value, - BuiltinProc_Count, }; struct BuiltinProc { @@ -213,9 +211,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("abs"), 1, false, Expr_Expr}, {STR_LIT("enum_to_string"), 1, false, Expr_Expr}, - - {STR_LIT("maybe_value"), 1, false, Expr_Expr}, - }; struct CheckerContext { @@ -874,8 +869,6 @@ Map generate_minimum_dependency_map(CheckerInfo *info, Entity *start) Map map = {}; // Key: Entity * map_init(&map, gb_heap_allocator()); - add_dependency_to_map(&map, info, start); - gb_for_array(i, info->entities.entries) { auto *entry = &info->entities.entries[i]; Entity *e = cast(Entity *)cast(uintptr)entry->key.key; @@ -885,6 +878,8 @@ Map generate_minimum_dependency_map(CheckerInfo *info, Entity *start) } } + add_dependency_to_map(&map, info, start); + return map; } @@ -1200,6 +1195,16 @@ void check_parsed_files(Checker *c) { add_curr_ast_file(c, d->scope->file); if (d->scope == e->scope) { + if (kind != Entity_Procedure && e->token.string == "main") { + if (e->scope->is_init) { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + } else if (e->scope->is_global && e->token.string == "main") { + error(e->token, "`main` is reserved as the entry point procedure in the initial scope"); + continue; + } + Scope *prev_scope = c->context.scope; c->context.scope = d->scope; check_entity_decl(c, e, d, NULL); diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 527b2d92d..26b8399d5 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -3028,31 +3028,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->mode = Addressing_Value; operand->type = t_string; } break; - - case BuiltinProc_maybe_value: { - Type *type = operand->type; - if (!is_type_maybe(type)) { - gbString type_str = type_to_string(operand->type); - defer (gb_string_free(type_str)); - error(ast_node_token(call), - "Expected a maybe to `maybe_value`, got `%s`", - type_str); - return false; - } - - operand->mode = Addressing_Value; - - Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); - Type *elem = base_type(type)->Maybe.elem; - Token t = make_token_ident(make_string("")); - variables[0] = make_entity_param(c->allocator, NULL, t, elem, false); - variables[1] = make_entity_param(c->allocator, NULL, t, t_bool, false); - - Type *tuple = make_type_tuple(c->allocator); - tuple->Tuple.variables = variables; - tuple->Tuple.variable_count = 2; - operand->type = tuple; - } break; } return true; @@ -3270,6 +3245,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint o->type = t_invalid; switch (node->kind) { + default: + goto error; + break; + case_ast_node(be, BadExpr, node) goto error; case_end; @@ -3731,6 +3710,34 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } case_end; + case_ast_node(de, DemaybeExpr, node); + check_expr_or_type(c, o, de->expr); + if (o->mode == Addressing_Invalid) { + goto error; + } else { + Type *t = base_type(o->type); + if (t->kind == Type_Maybe) { + Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); + Type *elem = t->Maybe.elem; + Token tok = make_token_ident(make_string("")); + variables[0] = make_entity_param(c->allocator, NULL, tok, elem, false); + variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false); + + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = variables; + tuple->Tuple.variable_count = 2; + + o->type = tuple; + o->mode = Addressing_Variable; + } else { + gbString str = expr_to_string(o->expr); + error(ast_node_token(o->expr), "Cannot demaybe `%s`", str); + gb_string_free(str); + goto error; + } + } + case_end; + case AstNode_ProcType: case AstNode_PointerType: case AstNode_MaybeType: @@ -3910,6 +3917,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, "^"); case_end; + case_ast_node(de, DemaybeExpr, node); + str = write_expr_to_string(str, de->expr); + str = gb_string_appendc(str, "?"); + case_end; + case_ast_node(be, BinaryExpr, node); str = write_expr_to_string(str, be->left); str = gb_string_appendc(str, " "); diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index a843da551..cf9e561b1 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -121,12 +121,9 @@ void ssa_gen_tree(ssaGen *s) { continue; } - if (entry_point != NULL) { - auto found = map_get(&min_dep_map, hash_pointer(e)); - if (found == NULL) { - // NOTE(bill): Nothing depends upon it so doesn't need to be built - continue; - } + if (map_get(&min_dep_map, hash_pointer(e)) == NULL) { + // NOTE(bill): Nothing depends upon it so doesn't need to be built + continue; } if (!scope->is_global && !scope->is_init) { diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 03879509b..c144d0011 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -2689,24 +2689,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue args[1] = ssa_emit_conv(proc, x, t_i64); return ssa_emit_global_call(proc, "__enum_to_string", args, 2); } break; - - case BuiltinProc_maybe_value: { - ssa_emit_comment(proc, make_string("maybe_value")); - ssaValue *maybe = ssa_build_expr(proc, ce->args[0]); - Type *t = default_type(type_of_expr(proc->module->info, expr)); - GB_ASSERT(is_type_tuple(t)); - - Type *elem = ssa_type(maybe); - GB_ASSERT(is_type_maybe(elem)); - elem = base_type(elem)->Maybe.elem; - - ssaValue *result = ssa_add_local_generated(proc, t); - ssaValue *gep0 = ssa_emit_struct_gep(proc, result, v_zero32, make_type_pointer(proc->module->allocator, elem)); - ssaValue *gep1 = ssa_emit_struct_gep(proc, result, v_one32, make_type_pointer(proc->module->allocator, t_bool)); - ssa_emit_store(proc, gep0, ssa_emit_struct_ev(proc, maybe, 0, elem)); - ssa_emit_store(proc, gep1, ssa_emit_struct_ev(proc, maybe, 1, t_bool)); - return ssa_emit_load(proc, result); - } break; } } } @@ -2803,6 +2785,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return ssa_emit_call(proc, value, args, arg_count); case_end; + case_ast_node(de, DemaybeExpr, expr); + return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); + case_end; + case_ast_node(se, SliceExpr, expr); return ssa_emit_load(proc, ssa_build_addr(proc, expr).addr); case_end; @@ -3191,6 +3177,25 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { return ssa_make_addr(gep, expr); case_end; + case_ast_node(de, DemaybeExpr, expr); + ssa_emit_comment(proc, make_string("DemaybeExpr")); + ssaValue *maybe = ssa_build_expr(proc, de->expr); + Type *t = default_type(type_of_expr(proc->module->info, expr)); + GB_ASSERT(is_type_tuple(t)); + + Type *elem = ssa_type(maybe); + GB_ASSERT(is_type_maybe(elem)); + elem = base_type(elem)->Maybe.elem; + + ssaValue *result = ssa_add_local_generated(proc, t); + ssaValue *gep0 = ssa_emit_struct_gep(proc, result, v_zero32, make_type_pointer(proc->module->allocator, elem)); + ssaValue *gep1 = ssa_emit_struct_gep(proc, result, v_one32, make_type_pointer(proc->module->allocator, t_bool)); + ssa_emit_store(proc, gep0, ssa_emit_struct_ev(proc, maybe, 0, elem)); + ssa_emit_store(proc, gep1, ssa_emit_struct_ev(proc, maybe, 1, t_bool)); + + return ssa_make_addr(result, expr); + case_end; + case_ast_node(ce, CallExpr, expr); ssaValue *e = ssa_build_expr(proc, expr); ssaValue *v = ssa_add_local_generated(proc, ssa_type(e)); diff --git a/src/parser.cpp b/src/parser.cpp index 44b1c62b7..c8559739e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -129,6 +129,7 @@ AST_NODE_KIND(_ExprBegin, "", struct{}) \ AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ + AST_NODE_KIND(DemaybeExpr, "demaybe expression", struct { Token op; AstNode *expr; }) \ AST_NODE_KIND(CallExpr, "call expression", struct { \ AstNode *proc; \ AstNodeArray args; \ @@ -398,6 +399,8 @@ Token ast_node_token(AstNode *node) { return node->FieldValue.eq; case AstNode_DerefExpr: return node->DerefExpr.op; + case AstNode_DemaybeExpr: + return node->DemaybeExpr.op; case AstNode_BadStmt: return node->BadStmt.begin; case AstNode_EmptyStmt: @@ -597,6 +600,13 @@ AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) { return result; } +AstNode *make_demaybe_expr(AstFile *f, AstNode *expr, Token op) { + AstNode *result = make_node(f, AstNode_DemaybeExpr); + result->DemaybeExpr.expr = expr; + result->DemaybeExpr.op = op; + return result; +} + AstNode *make_basic_lit(AstFile *f, Token basic_lit) { AstNode *result = make_node(f, AstNode_BasicLit); @@ -1453,7 +1463,8 @@ AstNode *parse_call_expr(AstFile *f, AstNode *operand) { next_token(f); } - gb_array_append(args, parse_expr(f, false)); + AstNode *arg = parse_expr(f, false); + gb_array_append(args, arg); if (f->curr_token.kind != Token_Comma) { if (f->curr_token.kind == Token_CloseParen) @@ -1564,6 +1575,10 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) { operand = make_deref_expr(f, operand, expect_token(f, Token_Pointer)); break; + case Token_Maybe: // Demaybe + operand = make_demaybe_expr(f, operand, expect_token(f, Token_Maybe)); + break; + case Token_OpenBrace: { if (!lhs && is_literal_type(operand) && f->expr_level >= 0) { if (f->curr_token.pos.line == f->prev_token.pos.line) { @@ -2715,12 +2730,12 @@ AstNode *parse_stmt(AstFile *f) { f->is_global_scope = true; return make_empty_stmt(f, f->curr_token); } - syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope."); + syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope"); return make_bad_decl(f, token, f->curr_token); } else if (tag == "import") { // TODO(bill): better error messages Token import_name = {}; - Token file_path = expect_token(f, Token_String); + Token file_path = expect_token_after(f, Token_String, "#import"); if (allow_token(f, Token_as)) { // NOTE(bill): Custom import name if (f->curr_token.kind == Token_Period) { @@ -2728,14 +2743,19 @@ AstNode *parse_stmt(AstFile *f) { import_name.kind = Token_Identifier; next_token(f); } else { - import_name = expect_token(f, Token_Identifier); + import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration"); + } + + if (import_name.string == "_") { + syntax_error(token, "Illegal import name: `_`"); + return make_bad_decl(f, token, f->curr_token); } } if (f->curr_proc == NULL) { return make_import_decl(f, s->TagStmt.token, file_path, import_name, false); } - syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope."); + syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope"); return make_bad_decl(f, token, file_path); } else if (tag == "load") { // TODO(bill): better error messages @@ -2746,14 +2766,14 @@ AstNode *parse_stmt(AstFile *f) { if (f->curr_proc == NULL) { return make_import_decl(f, s->TagStmt.token, file_path, import_name, true); } - syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope."); + syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope"); return make_bad_decl(f, token, file_path); } else if (tag == "foreign_system_library") { Token file_path = expect_token(f, Token_String); if (f->curr_proc == NULL) { return make_foreign_system_library(f, s->TagStmt.token, file_path); } - syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope."); + syntax_error(token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope"); return make_bad_decl(f, token, file_path); } else if (tag == "thread_local") { AstNode *var_decl = parse_simple_stmt(f); @@ -2762,7 +2782,7 @@ AstNode *parse_stmt(AstFile *f) { return make_bad_decl(f, token, ast_node_token(var_decl)); } if (f->curr_proc != NULL) { - syntax_error(token, "#thread_local is only allowed at the file scope."); + syntax_error(token, "#thread_local is only allowed at the file scope"); return make_bad_decl(f, token, ast_node_token(var_decl)); } var_decl->VarDecl.tags |= VarDeclTag_thread_local; @@ -2783,12 +2803,12 @@ AstNode *parse_stmt(AstFile *f) { return s; } - s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument return s; } break; - case Token_OpenBrace: return parse_block_stmt(f); + case Token_OpenBrace: + return parse_block_stmt(f); case Token_Semicolon: s = make_empty_stmt(f, token);