From 150d4e343ddd99eef1d921cdd494c108ac2e9a12 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Aug 2019 10:24:18 +0100 Subject: Fix `~(1 << x)` type inference bug --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e2090688f..2787aa436 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1168,7 +1168,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, true); + check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } -- cgit v1.2.3 From 01c10aa9447d135bdda5dc25583a5e1f94cbda6d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Aug 2019 13:54:35 +0100 Subject: `inline for` loops (only for 'in' based for loops) --- examples/demo/demo.odin | 39 +++++++ src/check_stmt.cpp | 272 ++++++++++++++++++++++++++++++++++++++++++++---- src/checker.hpp | 3 + src/exact_value.cpp | 4 + src/ir.cpp | 144 +++++++++++++++++++++++++ src/parser.cpp | 83 ++++++++++++++- src/parser.hpp | 9 ++ 7 files changed, 530 insertions(+), 24 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index a96056b61..cef814a56 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1018,6 +1018,44 @@ quaternions :: proc() { fmt.println(c); fmt.println(q); } + { // Memory layout of Quaternions + q := 1 + 2i + 3j + 4k; + a := transmute([4]f64)q; + fmt.println("Quaternion memory layout: xyzw/(ijkr)"); + fmt.println(q); // 1.000+2.000i+3.000j+4.000k + fmt.println(a); // [2.000, 3.000, 4.000, 1.000] + } +} + +inline_for_statement :: proc() { + fmt.println("\n#inline for statements"); + + fmt.println("Ranges"); + inline for x, i in 1..<4 { + fmt.println(x, i); + } + + fmt.println("Strings"); + inline for r, i in "Hello, 世界" { + fmt.println(r, i); + } + + fmt.println("Arrays"); + inline for elem, idx in ([4]int{1, 4, 9, 16}) { + fmt.println(elem, idx); + } + + + Foo_Enum :: enum { + A = 1, + B, + C = 6, + D, + } + fmt.println("Enum types"); + inline for elem, idx in Foo_Enum { + fmt.println(elem, idx); + } } main :: proc() { @@ -1040,5 +1078,6 @@ main :: proc() { deferred_procedure_associations(); reflection(); quaternions(); + inline_for_statement(); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2787aa436..88661558d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -132,6 +132,10 @@ bool check_is_terminating(Ast *node) { } case_end; + case_ast_node(rs, InlineRangeStmt, node); + return false; + case_end; + case_ast_node(rs, RangeStmt, node); return false; case_end; @@ -587,6 +591,236 @@ void add_constant_switch_case(CheckerContext *ctx, Map *seen, Oper multi_map_insert(seen, key, tap); } +void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(irs, InlineRangeStmt, node); + check_open_scope(ctx, node); + + Type *val0 = nullptr; + Type *val1 = nullptr; + Entity *entities[2] = {}; + isize entity_count = 0; + + Ast *expr = unparen_expr(irs->expr); + + ExactValue inline_for_depth = exact_value_i64(0); + + if (is_ast_range(expr)) { + ast_node(ie, BinaryExpr, expr); + Operand x = {Addressing_Invalid}; + Operand y = {Addressing_Invalid}; + + check_expr(ctx, &x, ie->left); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + check_expr(ctx, &y, ie->right); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + convert_to_typed(ctx, &x, y.type); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + convert_to_typed(ctx, &y, x.type); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + convert_to_typed(ctx, &x, default_type(y.type)); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + convert_to_typed(ctx, &y, default_type(x.type)); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + if (!are_types_identical(x.type, y.type)) { + if (x.type != t_invalid && + y.type != t_invalid) { + gbString xt = type_to_string(x.type); + gbString yt = type_to_string(y.type); + gbString expr_str = expr_to_string(x.expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + goto skip_expr; + } + + Type *type = x.type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + goto skip_expr; + } + + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant) { + ExactValue a = x.value; + ExactValue b = y.value; + + GB_ASSERT(are_types_identical(x.type, y.type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + goto skip_expr; + } + + inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + } else { + error(ie->op, "Interval expressions must be constant"); + goto skip_expr; + } + + add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); + add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); + val0 = type; + val1 = t_int; + } else { + Operand operand = {Addressing_Invalid}; + check_expr_or_type(ctx, &operand, irs->expr); + + if (operand.mode == Addressing_Type) { + if (!is_type_enum(operand.type)) { + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over the type '%s'", t); + gb_string_free(t); + goto skip_expr; + } else { + val0 = operand.type; + val1 = t_int; + add_type_info_type(ctx, operand.type); + + Type *bt = base_type(operand.type); + inline_for_depth = exact_value_i64(bt->Enum.fields.count); + goto skip_expr; + } + } else if (operand.mode != Addressing_Invalid) { + Type *t = base_type(operand.type); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + val0 = t_rune; + val1 = t_int; + inline_for_depth = exact_value_i64(operand.value.value_string.len); + } + break; + case Type_Array: + val0 = t->Array.elem; + val1 = t_int; + inline_for_depth = exact_value_i64(t->Array.count); + break; + } + } + + if (val0 == nullptr) { + gbString s = expr_to_string(operand.expr); + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over '%s' of type '%s' in an 'inline for' statement", s, t); + gb_string_free(t); + gb_string_free(s); + } else if (operand.mode != Addressing_Constant) { + error(operand.expr, "An 'inline for' expression must be known at compile time"); + } + } + + skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + + Ast * lhs[2] = {irs->val0, irs->val1}; + Type *rhs[2] = {val0, val1}; + + for (isize i = 0; i < 2; i++) { + if (lhs[i] == nullptr) { + continue; + } + Ast * name = lhs[i]; + Type *type = rhs[i]; + + Entity *entity = nullptr; + if (name->kind == Ast_Ident) { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + + if (!is_blank_ident(str)) { + found = scope_lookup_current(ctx->scope, str); + } + if (found == nullptr) { + bool is_immutable = true; + entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved); + entity->flags |= EntityFlag_Value; + add_entity_definition(&ctx->checker->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } else { + error(name, "A variable declaration must be an identifier"); + } + + if (entity == nullptr) { + entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); + } + + entities[entity_count++] = entity; + + if (type == nullptr) { + entity->type = t_invalid; + entity->flags |= EntityFlag_Used; + } + } + + for (isize i = 0; i < entity_count; i++) { + add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + } + + + // NOTE(bill): Minimize the amount of nesting of an 'inline for' + i64 prev_inline_for_depth = ctx->inline_for_depth; + defer (ctx->inline_for_depth = prev_inline_for_depth); + { + i64 v = exact_value_to_i64(inline_for_depth); + if (v <= 0) { + // Do nothing + } else { + ctx->inline_for_depth = gb_max(ctx->inline_for_depth, 1) * v; + } + + if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + if (prev_inline_for_depth > 0) { + error(node, "Nested 'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } else { + error(node, "'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } + error_line("\tUse a normal 'for' loop instead by removing the 'inline' prefix\n"); + ctx->inline_for_depth = MAX_INLINE_FOR_DEPTH; + } + } + + check_stmt(ctx, irs->body, mod_flags); + + + check_close_scope(ctx); +} + void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ss, SwitchStmt, node); @@ -1298,6 +1532,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(rs, RangeStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; @@ -1320,29 +1555,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_expr(ctx, &x, ie->left); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } check_expr(ctx, &y, ie->right); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, y.type); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, x.type); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, default_type(y.type)); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, default_type(x.type)); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } if (!are_types_identical(x.type, y.type)) { @@ -1356,13 +1591,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gb_string_free(yt); gb_string_free(xt); } - goto skip_expr; + goto skip_expr_range_stmt; } Type *type = x.type; if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; + goto skip_expr_range_stmt; } if (x.mode == Addressing_Constant && @@ -1382,18 +1617,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (!ok) { // TODO(bill): Better error message error(ie->op, "Invalid interval range"); - goto skip_expr; + goto skip_expr_range_stmt; } } - if (x.mode != Addressing_Constant) { - x.value = empty_exact_value; - } - if (y.mode != Addressing_Constant) { - y.value = empty_exact_value; - } - - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); val0 = type; @@ -1407,12 +1634,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gbString t = type_to_string(operand.type); error(operand.expr, "Cannot iterate over the type '%s'", t); gb_string_free(t); - goto skip_expr; + goto skip_expr_range_stmt; } else { val0 = operand.type; val1 = t_int; add_type_info_type(ctx, operand.type); - goto skip_expr; + goto skip_expr_range_stmt; } } else if (operand.mode != Addressing_Invalid) { bool is_ptr = is_type_pointer(operand.type); @@ -1457,7 +1684,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } - skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + Ast * lhs[2] = {rs->val0, rs->val1}; Type *rhs[2] = {val0, val1}; @@ -1515,6 +1743,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(irs, InlineRangeStmt, node); + check_inline_range_stmt(ctx, node, mod_flags); + case_end; + case_ast_node(ss, SwitchStmt, node); check_switch_stmt(ctx, node, mod_flags); case_end; diff --git a/src/checker.hpp b/src/checker.hpp index 4e4a8cfe1..a37a87b87 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -276,6 +276,9 @@ struct CheckerContext { CheckerPolyPath *poly_path; isize poly_level; // TODO(bill): Actually handle correctly +#define MAX_INLINE_FOR_DEPTH 1024ll + i64 inline_for_depth; + bool in_enum_type; bool collect_delayed_decls; bool allow_polymorphic_types; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 54d4ef6f2..fcc6f1973 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -813,6 +813,10 @@ gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactV return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_increment_one(ExactValue const &x) { + return exact_binary_operator_value(Token_Add, x, exact_value_i64(1)); +} + i32 cmp_f64(f64 a, f64 b) { return (a > b) - (a < b); diff --git a/src/ir.cpp b/src/ir.cpp index 2a4bb028b..7dc5988e3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9061,6 +9061,150 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { ir_start_block(proc, done); case_end; + case_ast_node(rs, InlineRangeStmt, node); + ir_emit_comment(proc, str_lit("InlineRangeStmt")); + ir_open_scope(proc); // Open scope here + + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) { + val0_type = type_of_expr(rs->val0); + } + if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) { + val1_type = type_of_expr(rs->val1); + } + + if (val0_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val0, true); + } + if (val1_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val1, true); + } + + irValue *val = nullptr; + irValue *key = nullptr; + irBlock *loop = nullptr; + irBlock *done = nullptr; + Ast *expr = unparen_expr(rs->expr); + + TypeAndValue tav = type_and_value_of_expr(expr); + + if (is_ast_range(expr)) { + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + TokenKind op = expr->BinaryExpr.op.kind; + Ast *start_expr = expr->BinaryExpr.left; + Ast *end_expr = expr->BinaryExpr.right; + GB_ASSERT(start_expr->tav.mode == Addressing_Constant); + GB_ASSERT(end_expr->tav.mode == Addressing_Constant); + + ExactValue start = start_expr->tav.value; + ExactValue end = end_expr->tav.value; + if (op == Token_Ellipsis) { // .. [start, end] + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_LtEq, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } else if (op == Token_RangeHalf) { // ..< [start, end) + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_Lt, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } + + + } else if (tav.mode == Addressing_Type) { + GB_ASSERT(is_type_enum(type_deref(tav.type))); + Type *et = type_deref(tav.type); + Type *bet = base_type(et); + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + for_array(i, bet->Enum.fields) { + Entity *field = bet->Enum.fields[i]; + GB_ASSERT(field->kind == Entity_Constant); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, field->Constant.value)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + } else { + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + GB_ASSERT(expr->tav.mode == Addressing_Constant); + + Type *t = base_type(expr->tav.type); + + + switch (t->kind) { + case Type_Basic: + GB_ASSERT(is_type_string(t)); + { + ExactValue value = expr->tav.value; + GB_ASSERT(value.kind == ExactValue_String); + String str = value.value_string; + Rune codepoint = 0; + isize offset = 0; + do { + isize width = gb_utf8_decode(str.text+offset, str.len-offset, &codepoint); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, exact_value_i64(codepoint))); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(offset))); + ir_build_stmt(proc, rs->body); + + offset += width; + } while (offset < str.len); + } + break; + case Type_Array: + if (t->Array.count > 0) { + irValue *val = ir_build_expr(proc, expr); + irValue *val_addr = ir_address_from_load_or_generate_local(proc, val); + + for (i64 i = 0; i < t->Array.count; i++) { + if (val0_type) { + // NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32 + irValue *elem = ir_emit_array_epi(proc, val_addr, cast(i32)i); + ir_addr_store(proc, val0_addr, ir_emit_load(proc, elem)); + } + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + + } + break; + default: + GB_PANIC("Invalid inline for type"); + break; + } + } + + + ir_close_scope(proc, irDeferExit_Default, nullptr); + case_end; + case_ast_node(ss, SwitchStmt, node); ir_emit_comment(proc, str_lit("SwitchStmt")); if (ss->init != nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index 9aebc78d6..8c32f35ff 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -51,6 +51,7 @@ Token ast_token(Ast *node) { case Ast_ReturnStmt: return node->ReturnStmt.token; case Ast_ForStmt: return node->ForStmt.token; case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_InlineRangeStmt: return node->InlineRangeStmt.inline_token; case Ast_CaseClause: return node->CaseClause.token; case Ast_SwitchStmt: return node->SwitchStmt.token; case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; @@ -257,6 +258,12 @@ Ast *clone_ast(Ast *node) { n->RangeStmt.expr = clone_ast(n->RangeStmt.expr); n->RangeStmt.body = clone_ast(n->RangeStmt.body); break; + case Ast_InlineRangeStmt: + n->InlineRangeStmt.val0 = clone_ast(n->InlineRangeStmt.val0); + n->InlineRangeStmt.val1 = clone_ast(n->InlineRangeStmt.val1); + n->InlineRangeStmt.expr = clone_ast(n->InlineRangeStmt.expr); + n->InlineRangeStmt.body = clone_ast(n->InlineRangeStmt.body); + break; case Ast_CaseClause: n->CaseClause.list = clone_ast_array(n->CaseClause.list); n->CaseClause.stmts = clone_ast_array(n->CaseClause.stmts); @@ -748,6 +755,18 @@ Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_toke return result; } +Ast *ast_inline_range_stmt(AstFile *f, Token inline_token, Token for_token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) { + Ast *result = alloc_ast_node(f, Ast_InlineRangeStmt); + result->InlineRangeStmt.inline_token = inline_token; + result->InlineRangeStmt.for_token = for_token; + result->InlineRangeStmt.val0 = val0; + result->InlineRangeStmt.val1 = val1; + result->InlineRangeStmt.in_token = in_token; + result->InlineRangeStmt.expr = expr; + result->InlineRangeStmt.body = body; + return result; +} + Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) { Ast *result = alloc_ast_node(f, Ast_SwitchStmt); result->SwitchStmt.token = token; @@ -1119,6 +1138,17 @@ Token advance_token(AstFile *f) { return prev; } +bool peek_token_kind(AstFile *f, TokenKind kind) { + for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) { + Token tok = f->tokens[i]; + if (kind != Token_Comment && tok.kind == Token_Comment) { + continue; + } + return tok.kind == kind; + } + return false; +} + Token expect_token(AstFile *f, TokenKind kind) { Token prev = f->curr_token; if (prev.kind != kind) { @@ -2092,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { bool prev_allow_range = f->allow_range; f->allow_range = true; - elem = parse_expr(f, false); + elem = parse_expr(f, true); f->allow_range = prev_allow_range; if (allow_token(f, Token_Semicolon)) { underlying = parse_type(f); @@ -2650,7 +2680,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { allow_token(f, Token_in); bool prev_allow_range = f->allow_range; f->allow_range = true; - Ast *expr = parse_expr(f, false); + Ast *expr = parse_expr(f, true); f->allow_range = prev_allow_range; auto rhs = array_make(heap_allocator(), 0, 1); @@ -3779,10 +3809,55 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context =` - case Token_proc: case Token_inline: + if (peek_token_kind(f, Token_for)) { + Token inline_token = expect_token(f, Token_inline); + Token for_token = expect_token(f, Token_for); + Ast *val0 = nullptr; + Ast *val1 = nullptr; + Token in_token = {}; + Ast *expr = nullptr; + Ast *body = nullptr; + + bool bad_stmt = false; + + if (f->curr_token.kind != Token_in) { + Array idents = parse_ident_list(f, false); + switch (idents.count) { + case 1: + val0 = idents[0]; + break; + case 2: + val0 = idents[0]; + val1 = idents[1]; + break; + default: + syntax_error(for_token, "Expected either 1 or 2 identifiers"); + bad_stmt = true; + break; + } + } + in_token = expect_token(f, Token_in); + + bool prev_allow_range = f->allow_range; + f->allow_range = true; + expr = parse_expr(f, true); + f->allow_range = prev_allow_range; + + if (allow_token(f, Token_do)) { + body = convert_stmt_to_body(f, parse_stmt(f)); + } else { + body = parse_block_stmt(f, false); + } + if (bad_stmt) { + return ast_bad_stmt(f, inline_token, f->curr_token); + } + return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); + } + /* fallthrough */ case Token_no_inline: + case Token_context: // Also allows for `context =` + case Token_proc: case Token_Ident: case Token_Integer: case Token_Float: diff --git a/src/parser.hpp b/src/parser.hpp index 3489f1a9b..32398592e 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -326,6 +326,15 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ Ast *expr; \ Ast *body; \ }) \ + AST_KIND(InlineRangeStmt, "inline range statement", struct { \ + Token inline_token; \ + Token for_token; \ + Ast *val0; \ + Ast *val1; \ + Token in_token; \ + Ast *expr; \ + Ast *body; \ + }) \ AST_KIND(CaseClause, "case clause", struct { \ Token token; \ Array list; \ -- cgit v1.2.3 From c89fc35e941275085332ba16b453432b5b7a5086 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 29 Aug 2019 14:36:42 +0100 Subject: Fix global variable initialization ordering (related to #427) --- src/check_decl.cpp | 5 +++-- src/check_stmt.cpp | 7 ++++++- src/checker.cpp | 57 ++++++++++++++++++++++++++++++-------------------- src/ir.cpp | 13 ++---------- src/priority_queue.cpp | 4 ++-- 5 files changed, 47 insertions(+), 39 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index cf3cb8e97..d87e6def0 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -791,7 +791,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } -void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { +void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Variable); @@ -805,6 +805,7 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); + GB_ASSERT(decl == ctx->decl); if (decl != nullptr) { check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac); } @@ -1051,7 +1052,7 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ switch (e->kind) { case Entity_Variable: - check_var_decl(&c, e, d->type_expr, d->init_expr); + check_global_variable_decl(&c, e, d->type_expr, d->init_expr); break; case Entity_Constant: check_const_decl(&c, e, d->type_expr, d->init_expr, named_type); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 88661558d..6945fb00e 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1735,7 +1735,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } for (isize i = 0; i < entity_count; i++) { - add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + Entity *e = entities[i]; + DeclInfo *d = decl_info_of_entity(e); + GB_ASSERT(d == nullptr); + add_entity(ctx->checker, ctx->scope, e->identifier, e); + d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl); + add_entity_and_decl_info(ctx, e->identifier, e, d); } check_stmt(ctx, rs->body, new_flags); diff --git a/src/checker.cpp b/src/checker.cpp index d6f5ad402..fc8cb5c63 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -85,9 +85,13 @@ int entity_graph_node_cmp(EntityGraphNode **data, isize i, isize j) { EntityGraphNode *y = data[j]; isize a = x->entity->order_in_src; isize b = y->entity->order_in_src; - if (x->dep_count < y->dep_count) return -1; - if (x->dep_count > y->dep_count) return +1; - return a < b ? -1 : b > a; + if (x->dep_count < y->dep_count) { + return -1; + } + if (x->dep_count == y->dep_count) { + return a < b ? -1 : b > a; + } + return +1; } void entity_graph_node_swap(EntityGraphNode **data, isize i, isize j) { @@ -1164,6 +1168,7 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec add_entity_definition(&c->checker->info, identifier, e); GB_ASSERT(e->decl_info == nullptr); e->decl_info = d; + d->entity = e; array_add(&c->checker->info.entities, e); e->order_in_src = c->checker->info.entities.count; e->pkg = c->pkg; @@ -1602,6 +1607,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("args__"), str_lit("type_table"), + str_lit("__type_info_of"), str_lit("global_scratch_allocator"), str_lit("Type_Info"), @@ -1688,9 +1694,10 @@ bool is_entity_a_dependency(Entity *e) { if (e == nullptr) return false; switch (e->kind) { case Entity_Procedure: - case Entity_Variable: - case Entity_Constant: return true; + case Entity_Constant: + case Entity_Variable: + return e->pkg != nullptr; } return false; } @@ -1717,18 +1724,17 @@ Array generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = M.entries[i].value; DeclInfo *decl = decl_info_of_entity(e); - if (decl != nullptr) { - for_array(j, decl->deps.entries) { - auto entry = decl->deps.entries[j]; - Entity *dep = entry.ptr; - if (dep && is_entity_a_dependency(dep)) { - EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); - if (m_ != nullptr) { - EntityGraphNode *m = *m_; - entity_graph_node_set_add(&n->succ, m); - entity_graph_node_set_add(&m->pred, n); - } - } + GB_ASSERT(decl != nullptr); + + for_array(j, decl->deps.entries) { + Entity *dep = decl->deps.entries[j].ptr; + GB_ASSERT(dep != nullptr); + if (is_entity_a_dependency(dep)) { + EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); + GB_ASSERT(m_ != nullptr); + EntityGraphNode *m = *m_; + entity_graph_node_set_add(&n->succ, m); + entity_graph_node_set_add(&m->pred, n); } } } @@ -2562,6 +2568,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Ast *init_expr = value; DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl); + d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; d->attributes = vd->attributes; @@ -3576,7 +3583,6 @@ void calculate_global_init_order(Checker *c) { #define TIME_SECTION(str) #endif - CheckerInfo *info = &c->info; TIME_SECTION("generate entity dependency graph"); @@ -3618,21 +3624,26 @@ void calculate_global_init_order(Checker *c) { for_array(i, n->pred.entries) { EntityGraphNode *p = n->pred.entries[i].ptr; - p->dep_count -= gb_max(p->dep_count-1, 0); + p->dep_count -= 1; + p->dep_count = gb_max(p->dep_count, 0); priority_queue_fix(&pq, p->index); } - if (e == nullptr || e->kind != Entity_Variable) { + DeclInfo *d = decl_info_of_entity(e); + if (e->kind != Entity_Variable) { continue; } - DeclInfo *d = decl_info_of_entity(e); - + // IMPORTANT NOTE(bill, 2019-08-29): Just add it regardless of the ordering + // because it does not need any initialization other than zero + // if (!decl_info_has_init(d)) { + // continue; + // } if (ptr_set_exists(&emitted, d)) { continue; } ptr_set_add(&emitted, d); - d->entity = e; + array_add(&info->variable_init_order, d); } diff --git a/src/ir.cpp b/src/ir.cpp index 62519c259..26f787976 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2226,18 +2226,9 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { Type *field_type = base_complex_elem_type(type); - // Field "real" - irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, - 0, - nullptr, - di); + irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, 0*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, 1*cast(i32)type_size_of(field_type), nullptr, di); map_set(&module->debug_info, hash_pointer(real_di), real_di); - - // Field "imag" - irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, - real_di->DerivedType.size, - nullptr, - di); map_set(&module->debug_info, hash_pointer(imag_di), imag_di); irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2); diff --git a/src/priority_queue.cpp b/src/priority_queue.cpp index 7c36e6a22..aee2061b5 100644 --- a/src/priority_queue.cpp +++ b/src/priority_queue.cpp @@ -20,7 +20,7 @@ bool priority_queue_shift_down(PriorityQueue *pq, isize i0, isize n) { if (j2 < n && pq->cmp(&pq->queue[0], j2, j1) < 0) { j = j2; } - if (pq->cmp(&pq->queue[0], i, j) < 0) break; + if (pq->cmp(&pq->queue[0], j, i) >= 0) break; pq->swap(&pq->queue[0], i, j); i = j; @@ -32,7 +32,7 @@ template void priority_queue_shift_up(PriorityQueue *pq, isize j) { while (0 <= j && j < pq->queue.count) { isize i = (j-1)/2; - if (i == j || pq->cmp(&pq->queue[0], i, j) < 0) { + if (i == j || pq->cmp(&pq->queue[0], j, i) >= 0) { break; } pq->swap(&pq->queue[0], i, j); -- cgit v1.2.3 From 68582c5ad1b2bf562242b9d2f40c89efad343b66 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2019 19:47:04 +0100 Subject: Add suggestions to errors on casts and assignments. --- src/check_decl.cpp | 3 +- src/check_expr.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++------- src/check_stmt.cpp | 3 +- 3 files changed, 85 insertions(+), 14 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 173ba4a90..166b6e715 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -112,7 +112,8 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar isize rhs_count = operands.count; for_array(i, operands) { if (operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 8a5ed9c1c..5ef42a3fc 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -90,7 +90,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); void set_procedure_abi_types(CheckerContext *c, Type *type); - +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); @@ -786,6 +786,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co op_type_str, type_str, LIT(context_name)); + check_assignment_error_suggestion(c, operand, type); break; } operand->mode = Addressing_Invalid; @@ -1509,32 +1510,98 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ return false; } + +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (are_types_identical(src, dst)) { + error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + +void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (is_type_pointer(o->type) && is_type_integer(type)) { + if (is_type_uintptr(type)) { + error_line("\tSuggestion: a pointer may be directly casted to %s\n", b); + } else { + error_line("\tSuggestion: for a pointer to be casted to an integer, it must be converted to 'uintptr' first\n"); + i64 x = type_size_of(o->type); + i64 y = type_size_of(type); + if (x != y) { + error_line("\tNote: the type of expression and the type of the cast have a different size in bytes, %lld vs %lld\n", x, y); + } + } + } else if (is_type_integer(o->type) && is_type_pointer(type)) { + if (is_type_uintptr(o->type)) { + error_line("\tSuggestion: %a may be directly casted to %s\n", a, b); + } else { + error_line("\tSuggestion: for an integer to be casted to a pointer, it must be converted to 'uintptr' first\n"); + } + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + + void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { GB_ASSERT(is_type_constant_type(type)); GB_ASSERT(o->mode == Addressing_Constant); if (!check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + ); + if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - #if 0 - gbAllocator ha = heap_allocator(); - String str = big_int_to_string(ha, &o->value.value_integer); - defer (gb_free(ha, str.text)); - error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); - #else error(o->expr, "Cannot convert '%s' to '%s'", a, b); - #endif + check_assignment_error_suggestion(c, o, type); } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } - - gb_string_free(b); - gb_string_free(a); - o->mode = Addressing_Invalid; } } @@ -2165,6 +2232,8 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) { gb_string_free(to_type); gb_string_free(expr_str); + check_cast_error_suggestion(c, x, type); + x->mode = Addressing_Invalid; return; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6945fb00e..9163fadc2 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1366,7 +1366,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { isize rhs_count = rhs_operands.count; for_array(i, rhs_operands) { if (rhs_operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } -- cgit v1.2.3 From 7fa2d25eea28955c12d8fc6a597e8615562c0ee9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 5 Oct 2019 10:22:41 +0100 Subject: Fix #complete switch with pointer case doesn't compile #416 --- src/check_stmt.cpp | 4 ++-- src/types.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 9163fadc2..26e3f8c26 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1205,7 +1205,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_PANIC("Unknown type to type switch statement"); } - if (ptr_set_exists(&seen, y.type)) { + if (type_ptr_set_exists(&seen, y.type)) { TokenPos pos = cc->token.pos; gbString expr_str = expr_to_string(y.expr); error(y.expr, @@ -1257,7 +1257,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { for_array(i, variants) { Type *t = variants[i]; - if (!ptr_set_exists(&seen, t)) { + if (!type_ptr_set_exists(&seen, t)) { array_add(&unhandled, t); } } diff --git a/src/types.cpp b/src/types.cpp index 94b89d8f5..d8a579510 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -551,8 +551,27 @@ i64 type_offset_of (Type *t, i32 index); gbString type_to_string (Type *type); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); +bool are_types_identical(Type *x, Type *y); +bool type_ptr_set_exists(PtrSet *s, Type *t) { + if (ptr_set_exists(s, t)) { + return true; + } + + // TODO(bill, 2019-10-05): This is very slow and it's probably a lot + // faster to cache types correctly + for_array(i, s->entries) { + Type *f = s->entries[i].ptr; + if (are_types_identical(t, f)) { + ptr_set_add(s, t); + return true; + } + } + + return false; +} + Type *base_type(Type *t) { for (;;) { if (t == nullptr) { -- cgit v1.2.3 From 94879ed14997c4d2745ddb669d08b34437ceff75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 12:14:04 +0100 Subject: Fix Compiler assertion when applying `using` to `_` procedure parameter. #451 --- src/check_decl.cpp | 3 +-- src/check_stmt.cpp | 7 +++---- src/entity.cpp | 3 ++- src/ir.cpp | 4 ++-- 4 files changed, 8 insertions(+), 9 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5e8479a7f..ebfc29899 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1167,11 +1167,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty for_array(i, scope->elements.entries) { Entity *f = scope->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); uvar->Variable.is_immutable = is_immutable; if (is_value) uvar->flags |= EntityFlag_Value; - ProcUsingVar puv = {e, uvar}; array_add(&using_entities, puv); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 26e3f8c26..72913903d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -496,8 +496,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b for_array(i, found->elements.entries) { Entity *f = found->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->using_expr = expr; + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr); Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { gbString expr_str = expr_to_string(expr); @@ -2052,7 +2051,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { // TODO(bill): Should a 'continue' happen here? } - for (isize entity_index = 0; entity_index < entity_count; entity_index++) { + for (isize entity_index = 0; entity_index < 1; entity_index++) { Entity *e = entities[entity_index]; if (e == nullptr) { continue; @@ -2071,7 +2070,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { for_array(i, scope->elements.entries) { Entity *f = scope->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); uvar->Variable.is_immutable = is_immutable; Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { diff --git a/src/entity.cpp b/src/entity.cpp index 78726171d..bbea68e8b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -221,12 +221,13 @@ Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, bool is_imm return entity; } -Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type) { +Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast *using_expr) { GB_ASSERT(parent != nullptr); token.pos = parent->token.pos; Entity *entity = alloc_entity(Entity_Variable, parent->scope, token, type); entity->using_parent = parent; entity->parent_proc_decl = parent->parent_proc_decl; + entity->using_expr = using_expr; entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Used; entity->state = EntityState_Resolved; diff --git a/src/ir.cpp b/src/ir.cpp index bea9ee8b3..c654e2bf8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9743,7 +9743,7 @@ void ir_begin_procedure_body(irProcedure *proc) { } Type *abi_type = proc->type->Proc.abi_compat_params[i]; - if (e->token.string != "" && !is_blank_ident(e->token)) { + if (e->token.string != "") { ir_add_param(proc, e, name, abi_type, parameter_index); } @@ -9766,7 +9766,7 @@ void ir_begin_procedure_body(irProcedure *proc) { if (abi_types.count > 0) { abi_type = abi_types[i]; } - if (e->token.string != "" && !is_blank_ident(e->token)) { + if (e->token.string != "") { ir_add_param(proc, e, nullptr, abi_type, parameter_index); } if (is_type_tuple(abi_type)) { -- cgit v1.2.3 From 7fae890ef9eb72f3131914f9af2469f9bfb1e59e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:06:29 +0100 Subject: Allow ranges for array-like compound literals --- core/odin/parser/parser.odin | 3 + src/check_expr.cpp | 229 ++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 84 +--------------- src/common.cpp | 3 + src/exact_value.cpp | 2 +- src/ir.cpp | 124 ++++++++++++++++++----- src/ir_print.cpp | 48 +++++++-- src/parser.cpp | 11 ++- src/parser.hpp | 1 + 9 files changed, 340 insertions(+), 165 deletions(-) (limited to 'src/check_stmt.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index fee9b7bdc..fb3a87e8b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2391,6 +2391,9 @@ parse_value :: proc(p: ^Parser) -> ^ast.Expr { if p.curr_tok.kind == .Open_Brace { return parse_literal_value(p, nil); } + prev_allow_range = p.allow_range; + defer p.allow_range = prev_allow_range; + p.allow_range = true; return parse_expr(p, false); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ea9248089..f07ebe09c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6635,6 +6635,98 @@ bool ternary_compare_types(Type *x, Type *y) { } +bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_) { + if (!is_ast_range(node)) { + return false; + } + + ast_node(ie, BinaryExpr, node); + + check_expr(c, x, ie->left); + if (x->mode == Addressing_Invalid) { + return false; + } + check_expr(c, y, ie->right); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, y->type); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, x->type); + if (y->mode == Addressing_Invalid) { + return false; + } + + convert_to_typed(c, x, default_type(y->type)); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, default_type(x->type)); + if (y->mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + return false; + } + + Type *type = x->type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + GB_ASSERT(are_types_identical(x->type, y->type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + return false; + } + + ExactValue inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + if (inline_for_depth_) *inline_for_depth_ = inline_for_depth; + } else { + error(ie->op, "Interval expressions must be constant"); + return false; + } + + add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + + return true; +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -6697,35 +6789,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); - // NOTE(bill, 2018-06-17): Placing this in the parser is slower than - // placing it here for some reason. So don't move it to the parsing - // stage if you _think_ it will be faster, only do it if you _know_ it - // will be faster. Type *t = t_invalid; - switch (bl->token.kind) { - case Token_Integer: t = t_untyped_integer; break; - case Token_Float: t = t_untyped_float; break; - case Token_String: t = t_untyped_string; break; - case Token_Rune: t = t_untyped_rune; break; - case Token_Imag: { - String s = bl->token.string; - Rune r = s[s.len-1]; - // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers - switch (r) { - case 'i': t = t_untyped_complex; break; - case 'j': t = t_untyped_quaternion; break; - case 'k': t = t_untyped_quaternion; break; + switch (bl->value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - break; - } default: - GB_PANIC("Unknown literal"); + GB_PANIC("Unhandled value type for basic literal"); break; } + o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_from_basic_literal(bl->token); + o->value = bl->value; case_end; case_ast_node(bd, BasicDirective, node); @@ -7090,9 +7173,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { - Map seen = {}; - map_init(&seen, heap_allocator()); - defer (map_destroy(&seen)); + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -7102,36 +7184,89 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } ast_node(fv, FieldValue, elem); - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - i64 index = exact_value_to_i64(op_index.value); + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + i64 max_index = hi; - if (map_get(&seen, hash_integer(u64(index))) != nullptr) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } - map_set(&seen, hash_integer(u64(index)), true); + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - if (max < index) { - max = index; - } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < max_index) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } - is_constant = is_constant && operand.mode == Addressing_Constant; + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } } cl->max_index = max; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 72913903d..d4398664b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -605,89 +605,15 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (is_ast_range(expr)) { ast_node(ie, BinaryExpr, expr); - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; + Operand x = {}; + Operand y = {}; - check_expr(ctx, &x, ie->left); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - check_expr(ctx, &y, ie->right); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, y.type); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, x.type); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, default_type(y.type)); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, default_type(x.type)); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - if (!are_types_identical(x.type, y.type)) { - if (x.type != t_invalid && - y.type != t_invalid) { - gbString xt = type_to_string(x.type); - gbString yt = type_to_string(y.type); - gbString expr_str = expr_to_string(x.expr); - error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); - gb_string_free(expr_str); - gb_string_free(yt); - gb_string_free(xt); - } - goto skip_expr; - } - - Type *type = x.type; - if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { - error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; - } - - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant) { - ExactValue a = x.value; - ExactValue b = y.value; - - GB_ASSERT(are_types_identical(x.type, y.type)); - - TokenKind op = Token_Lt; - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: error(ie->op, "Invalid range operator"); break; - } - bool ok = compare_exact_values(op, a, b); - if (!ok) { - // TODO(bill): Better error message - error(ie->op, "Invalid interval range"); - goto skip_expr; - } - - inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { - inline_for_depth = exact_value_increment_one(inline_for_depth); - } - - } else { - error(ie->op, "Interval expressions must be constant"); + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { goto skip_expr; } - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); - add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); - val0 = type; + val0 = x.type; val1 = t_int; } else { Operand operand = {Addressing_Invalid}; diff --git a/src/common.cpp b/src/common.cpp index db6505a36..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -143,6 +143,9 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) +#include "range_cache.cpp" + + u64 fnv64a(void const *data, isize len) { u8 const *bytes = cast(u8 const *)data; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 95d04d93b..42b22c8ef 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -293,12 +293,12 @@ ExactValue exact_value_from_basic_literal(Token token) { case 'i': return exact_value_complex(0, imag); case 'j': return exact_value_quaternion(0, 0, imag, 0); case 'k': return exact_value_quaternion(0, 0, 0, imag); + default: GB_PANIC("Invalid imaginary basic literal"); } } case Token_Rune: { Rune r = GB_RUNE_INVALID; gb_utf8_decode(token.string.text, token.string.len, &r); - // gb_printf("%.*s rune: %d\n", LIT(token.string), r); return exact_value_i64(r); } default: diff --git a/src/ir.cpp b/src/ir.cpp index c654e2bf8..5eeba91a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7865,14 +7865,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (ir_is_elem_const(proc->module, fv->value, et)) { continue; } - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - irCompoundLitElemTempData data = {}; - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *value = ir_build_expr(proc, fv->value); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { @@ -7897,15 +7923,15 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_value = return_ptr_hint_value); defer (proc->return_ptr_hint_used = return_ptr_hint_used); + irValue *field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - if (expr == nullptr) { - continue; - } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); - irValue *field_expr = ir_build_expr(proc, expr); + if (field_expr == nullptr) { + field_expr = ir_build_expr(proc, expr); + } Type *t = ir_type(field_expr); GB_ASSERT(t->kind != Type_Tuple); irValue *ev = ir_emit_conv(proc, field_expr, et); @@ -7945,19 +7971,43 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { continue; } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *field_expr = ir_build_expr(proc, fv->value); - GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } - irValue *ev = ir_emit_conv(proc, field_expr, et); + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { continue; @@ -8015,14 +8065,36 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Ast *elem = cl->elems[i]; if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - i64 field_index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *ev = ir_build_expr(proc, fv->value); - irValue *value = ir_emit_conv(proc, ev, et); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for (i64 k = lo; k < hi; k++) { + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)k); + ir_emit_store(proc, ep, value); + } + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } } else { irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cece0c1db..1d0c8af35 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -887,17 +887,47 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * for (isize j = 0; j < elem_count; j++) { Ast *elem = cl->elems[j]; ast_node(fv, FieldValue, elem); - TypeAndValue index_tav = fv->field->tav; - GB_ASSERT(index_tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(index_tav.value); - if (index == i) { - TypeAndValue tav = fv->value->tav; - if (tav.mode != Addressing_Constant) { + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + for (i64 k = lo; k < hi; k++) { + if (k > lo) ir_write_str_lit(f, ", "); + + ir_print_compound_element(f, m, tav.value, elem_type); + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; break; } - ir_print_compound_element(f, m, tav.value, elem_type); - found = true; - break; } } diff --git a/src/parser.cpp b/src/parser.cpp index 7f866922a..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -588,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -1509,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1735,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); diff --git a/src/parser.hpp b/src/parser.hpp index 9c3f733e5..83df7a9d6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -222,6 +222,7 @@ enum StmtAllowFlag { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ + ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ -- cgit v1.2.3