From 763cd2649da4c87d8a9be2c2e44f8535e757a95a Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 12:21:02 +0000 Subject: [PATCH 1/8] Fix index assignment rules for indirection --- code/demo.odin | 15 ++++++--- src/check_expr.c | 28 +++++++++++----- src/check_stmt.c | 85 +++++++++++++++++++++++++++++------------------- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 238d638e4..c9cf55616 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,13 +1,19 @@ #import "fmt.odin"; -main :: proc() { +x := [...]int{1, 2, 3, 4}; +main :: proc() { { - Vec2 :: [vector 2]f32; - i: f32 = 1; - b := Vec2{i, i}; + + + foo :: proc() -> [...]int { + return x; + } + + // foo()[0] = 2; } +/* /* Version 0.1.0 @@ -130,5 +136,6 @@ main :: proc() { compile_assert(size_of([vector 7]i32) == size_of([7]i32)); // align_of([vector 7]i32) != align_of([7]i32) // this may be the case } +*/ } diff --git a/src/check_expr.c b/src/check_expr.c index 3b58dc904..5b91ffd39 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -3839,9 +3839,9 @@ void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, ok.type = t_bool; array_add(operands, val); array_add(operands, ok); - continue; + } else { + array_add(operands, o); } - array_add(operands, o); } else { TypeTuple *tuple = &o.type->Tuple; for (isize j = 0; j < tuple->variable_count; j++) { @@ -4097,8 +4097,8 @@ void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) { } } -bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { - t = base_type(type_deref(t)); +bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_count) { + Type *t = base_type(type_deref(type)); switch (t->kind) { case Type_Basic: @@ -4116,15 +4116,20 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { case Type_Array: *max_count = t->Array.count; - if (o->mode != Addressing_Variable) { + if (indirection) { + o->mode = Addressing_Variable; + } else if (o->mode != Addressing_Variable) { o->mode = Addressing_Value; } + o->type = t->Array.elem; return true; case Type_Vector: *max_count = t->Vector.count; - if (o->mode != Addressing_Variable) { + if (indirection) { + o->mode = Addressing_Variable; + } else if (o->mode != Addressing_Variable) { o->mode = Addressing_Value; } o->type = t->Vector.elem; @@ -4138,7 +4143,11 @@ bool check_set_index_data(Operand *o, Type *t, i64 *max_count) { case Type_DynamicArray: o->type = t->DynamicArray.elem; - o->mode = Addressing_Variable; + if (indirection) { + o->mode = Addressing_Variable; + } else if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } return true; } @@ -5023,6 +5032,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } Type *t = base_type(type_deref(o->type)); + bool is_ptr = is_type_pointer(o->type); bool is_const = o->mode == Addressing_Constant; if (is_type_map(t)) { @@ -5039,7 +5049,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } i64 max_count = -1; - bool valid = check_set_index_data(o, t, &max_count); + bool valid = check_set_index_data(o, t, is_ptr, &max_count); if (is_const) { valid = false; @@ -5048,7 +5058,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint if (!valid && (is_type_struct(t) || is_type_raw_union(t))) { Entity *found = find_using_index_expr(t); if (found != NULL) { - valid = check_set_index_data(o, found->type, &max_count); + valid = check_set_index_data(o, found->type, is_type_pointer(found->type), &max_count); } } diff --git a/src/check_stmt.c b/src/check_stmt.c index bbe254615..983c9f25f 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -182,40 +182,40 @@ bool check_is_terminating(AstNode *node) { return false; } -Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { - if (op_a->mode == Addressing_Invalid || - (op_a->type == t_invalid && op_a->mode != Addressing_Overload)) { +Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { + if (rhs->mode == Addressing_Invalid || + (rhs->type == t_invalid && rhs->mode != Addressing_Overload)) { return NULL; } - AstNode *node = unparen_expr(lhs); + AstNode *node = unparen_expr(lhs_node); // NOTE(bill): Ignore assignments to `_` if (node->kind == AstNode_Ident && str_eq(node->Ident.string, str_lit("_"))) { add_entity_definition(&c->info, node, NULL); - check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier")); - if (op_a->mode == Addressing_Invalid) { + check_assignment(c, rhs, NULL, str_lit("assignment to `_` identifier")); + if (rhs->mode == Addressing_Invalid) { return NULL; } - return op_a->type; + return rhs->type; } Entity *e = NULL; bool used = false; - Operand op_b = {Addressing_Invalid}; + Operand lhs = {Addressing_Invalid}; - check_expr(c, &op_b, lhs); - if (op_b.mode == Addressing_Invalid || - op_b.type == t_invalid) { + check_expr(c, &lhs, lhs_node); + if (lhs.mode == Addressing_Invalid || + lhs.type == t_invalid) { return NULL; } - if (op_a->mode == Addressing_Overload) { - isize overload_count = op_a->overload_count; - Entity **procs = op_a->overload_entities; + if (rhs->mode == Addressing_Overload) { + isize overload_count = rhs->overload_count; + Entity **procs = rhs->overload_entities; GB_ASSERT(procs != NULL && overload_count > 0); // NOTE(bill): These should be done @@ -227,19 +227,19 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { Operand x = {0}; x.mode = Addressing_Value; x.type = t; - if (check_is_assignable_to(c, &x, op_b.type)) { + if (check_is_assignable_to(c, &x, lhs.type)) { e = procs[i]; - add_entity_use(c, op_a->expr, e); + add_entity_use(c, rhs->expr, e); break; } } if (e != NULL) { // HACK TODO(bill): Should the entities be freed as it's technically a leak - op_a->mode = Addressing_Value; - op_a->type = e->type; - op_a->overload_count = 0; - op_a->overload_entities = NULL; + rhs->mode = Addressing_Value; + rhs->type = e->type; + rhs->overload_count = 0; + rhs->overload_entities = NULL; } } else { if (node->kind == AstNode_Ident) { @@ -256,43 +256,62 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { e->flags |= EntityFlag_Used; } - Type *assignment_type = op_b.type; - switch (op_b.mode) { + Type *assignment_type = lhs.type; + switch (lhs.mode) { case Addressing_Invalid: return NULL; case Addressing_Variable: - case Addressing_MapIndex: break; + case Addressing_MapIndex: { + AstNode *ln = unparen_expr(lhs_node); + if (ln->kind == AstNode_IndexExpr) { + AstNode *x = ln->IndexExpr.expr; + TypeAndValue *tav = type_and_value_of_expression(&c->info, x); + GB_ASSERT(tav != NULL); + switch (tav->mode) { + case Addressing_Variable: + break; + case Addressing_Value: + if (!is_type_pointer(tav->type)) { + gbString str = expr_to_string(lhs.expr); + error_node(lhs.expr, "Cannot assign to the value of a map `%s`", str); + gb_string_free(str); + return NULL; + } + break; + } + } + } break; default: { - if (op_b.expr->kind == AstNode_SelectorExpr) { + if (lhs.expr->kind == AstNode_SelectorExpr) { // NOTE(bill): Extra error checks Operand op_c = {Addressing_Invalid}; - ast_node(se, SelectorExpr, op_b.expr); + ast_node(se, SelectorExpr, lhs.expr); check_expr(c, &op_c, se->expr); if (op_c.mode == Addressing_MapIndex) { - gbString str = expr_to_string(op_b.expr); - error_node(op_b.expr, "Cannot assign to record field `%s` in map", str); + gbString str = expr_to_string(lhs.expr); + error_node(lhs.expr, "Cannot assign to record field `%s` in map", str); gb_string_free(str); return NULL; } } - gbString str = expr_to_string(op_b.expr); + gbString str = expr_to_string(lhs.expr); if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) { - error_node(op_b.expr, "Cannot assign to an immutable: `%s`", str); + error_node(lhs.expr, "Cannot assign to an immutable: `%s`", str); } else { - error_node(op_b.expr, "Cannot assign to `%s`", str); + error_node(lhs.expr, "Cannot assign to `%s`", str); } gb_string_free(str); } break; } - check_assignment(c, op_a, assignment_type, str_lit("assignment")); - if (op_a->mode == Addressing_Invalid) { + check_assignment(c, rhs, assignment_type, str_lit("assignment")); + if (rhs->mode == Addressing_Invalid) { return NULL; } - return op_a->type; + return rhs->type; } bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { From 74d15ab84b39a83285953b963f1637956f091f45 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 12:35:50 +0000 Subject: [PATCH 2/8] Reimplement `immutable` with different rules. --- code/demo.odin | 10 +++------- src/check_expr.c | 43 +++++++++++++++++++++---------------------- src/check_stmt.c | 8 ++------ src/checker.c | 34 ++++++++++++++++++---------------- src/parser.c | 6 +++--- src/tokenizer.c | 1 + 6 files changed, 48 insertions(+), 54 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index c9cf55616..5d2e53636 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,16 +1,12 @@ #import "fmt.odin"; -x := [...]int{1, 2, 3, 4}; main :: proc() { { - - foo :: proc() -> [...]int { - return x; - } - - // foo()[0] = 2; + x := [...]int{1, 2, 3, 4}; + immutable y := ^x; + fmt.println(y^[0]); } /* diff --git a/src/check_expr.c b/src/check_expr.c index 5b91ffd39..25576ae85 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1030,7 +1030,7 @@ void check_ident(Checker *c, Operand *o, AstNode *n, Type *named_type, Type *typ } o->mode = Addressing_Variable; if (e->Variable.is_immutable) { - o->mode = Addressing_Value; + o->mode = Addressing_Immutable; } break; @@ -2714,7 +2714,9 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h break; case Entity_Variable: // TODO(bill): This is the rule I need? - if (sel.indirect || operand->mode != Addressing_Value) { + if (operand->mode == Addressing_Immutable) { + // Okay + } else if (sel.indirect || operand->mode != Addressing_Value) { operand->mode = Addressing_Variable; } else { operand->mode = Addressing_Value; @@ -4097,6 +4099,16 @@ void check_expr_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) { } } +void check_set_mode_with_indirection(Operand *o, bool indirection) { + if (o->mode != Addressing_Immutable) { + if (indirection) { + o->mode = Addressing_Variable; + } else if (o->mode != Addressing_Variable) { + o->mode = Addressing_Value; + } + } +} + bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_count) { Type *t = base_type(type_deref(type)); @@ -4106,9 +4118,7 @@ bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_cou if (o->mode == Addressing_Constant) { *max_count = o->value.value_string.len; } - if (o->mode != Addressing_Variable) { - o->mode = Addressing_Value; - } + check_set_mode_with_indirection(o, indirection); o->type = t_u8; return true; } @@ -4116,22 +4126,13 @@ bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_cou case Type_Array: *max_count = t->Array.count; - if (indirection) { - o->mode = Addressing_Variable; - } else if (o->mode != Addressing_Variable) { - o->mode = Addressing_Value; - } - + check_set_mode_with_indirection(o, indirection); o->type = t->Array.elem; return true; case Type_Vector: *max_count = t->Vector.count; - if (indirection) { - o->mode = Addressing_Variable; - } else if (o->mode != Addressing_Variable) { - o->mode = Addressing_Value; - } + check_set_mode_with_indirection(o, indirection); o->type = t->Vector.elem; return true; @@ -4143,11 +4144,7 @@ bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_cou case Type_DynamicArray: o->type = t->DynamicArray.elem; - if (indirection) { - o->mode = Addressing_Variable; - } else if (o->mode != Addressing_Variable) { - o->mode = Addressing_Value; - } + check_set_mode_with_indirection(o, indirection); return true; } @@ -5183,7 +5180,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } else { Type *t = base_type(o->type); if (t->kind == Type_Pointer) { - o->mode = Addressing_Variable; + if (o->mode != Addressing_Immutable) { + o->mode = Addressing_Variable; + } o->type = t->Pointer.elem; } else { gbString str = expr_to_string(o->expr); diff --git a/src/check_stmt.c b/src/check_stmt.c index 983c9f25f..6769ddb40 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -268,17 +268,13 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { AstNode *x = ln->IndexExpr.expr; TypeAndValue *tav = type_and_value_of_expression(&c->info, x); GB_ASSERT(tav != NULL); - switch (tav->mode) { - case Addressing_Variable: - break; - case Addressing_Value: + if (tav->mode != Addressing_Variable) { if (!is_type_pointer(tav->type)) { gbString str = expr_to_string(lhs.expr); error_node(lhs.expr, "Cannot assign to the value of a map `%s`", str); gb_string_free(str); return NULL; } - break; } } } break; @@ -297,7 +293,7 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { } gbString str = expr_to_string(lhs.expr); - if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) { + if (lhs.mode == Addressing_Immutable) { error_node(lhs.expr, "Cannot assign to an immutable: `%s`", str); } else { error_node(lhs.expr, "Cannot assign to `%s`", str); diff --git a/src/checker.c b/src/checker.c index 17af9a44a..5ae1460ad 100644 --- a/src/checker.c +++ b/src/checker.c @@ -6,6 +6,22 @@ typedef enum ExprKind { Expr_Stmt, } ExprKind; +typedef enum AddressingMode { + Addressing_Invalid, + + Addressing_NoValue, + Addressing_Value, + Addressing_Variable, + Addressing_Immutable, + Addressing_Constant, + Addressing_Type, + Addressing_Builtin, + Addressing_Overload, + Addressing_MapIndex, + + Addressing_Count, +} AddressingMode; + // Statements and Declarations typedef enum StmtFlag { Stmt_BreakAllowed = 1<<0, @@ -106,22 +122,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { }; - -typedef enum AddressingMode { - Addressing_Invalid, - - Addressing_NoValue, - Addressing_Value, - Addressing_Variable, - Addressing_Constant, - Addressing_Type, - Addressing_Builtin, - Addressing_Overload, - Addressing_MapIndex, - - Addressing_Count, -} AddressingMode; - #include "types.c" #define MAP_TYPE Entity * @@ -149,7 +149,9 @@ bool is_operand_value(Operand o) { switch (o.mode) { case Addressing_Value: case Addressing_Variable: + case Addressing_Immutable: case Addressing_Constant: + case Addressing_MapIndex: return true; } return false; diff --git a/src/parser.c b/src/parser.c index b5d8f442b..69ea32c48 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2358,7 +2358,7 @@ bool is_token_field_prefix(TokenKind kind) { switch (kind) { case Token_using: case Token_no_alias: - // case Token_immutable: + case Token_immutable: return true; } return false; @@ -2374,7 +2374,7 @@ u32 parse_field_prefixes(AstFile *f) { switch (f->curr_token.kind) { case Token_using: using_count += 1; next_token(f); break; case Token_no_alias: no_alias_count += 1; next_token(f); break; - // case Token_immutable: immutable_count += 1; next_token(f); break; + case Token_immutable: immutable_count += 1; next_token(f); break; } } if (using_count > 1) syntax_error(f->curr_token, "Multiple `using` in this field list"); @@ -3272,7 +3272,7 @@ AstNode *parse_stmt(AstFile *f) { return ast_bad_stmt(f, token, f->curr_token); } break; -#if 0 +#if 1 case Token_immutable: { Token token = expect_token(f, Token_immutable); AstNode *node = parse_stmt(f); diff --git a/src/tokenizer.c b/src/tokenizer.c index f2cabfb02..fc85fd927 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -102,6 +102,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \ + TOKEN_KIND(Token_immutable, "immutable"), \ TOKEN_KIND(Token_cast, "cast"), \ TOKEN_KIND(Token_transmute, "transmute"), \ TOKEN_KIND(Token_down_cast, "down_cast"), \ From d1f65097c48afe6d869949cc5ede76a8b14401a9 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 15:19:29 +0000 Subject: [PATCH 3/8] Fix immutable rules; add some general documentation immutable is still a little weird and not completely what you'd expect. Maybe just not having it is better. --- code/demo.odin | 12 +-- core/hash.odin | 8 +- core/opengl.odin | 174 +++++++++++++++++++++--------------------- core/sys/windows.odin | 13 ++-- core/utf8.odin | 4 +- src/build_settings.c | 8 +- src/check_expr.c | 11 ++- src/checker.c | 68 +++++++++-------- src/common.c | 3 + src/entity.c | 9 ++- src/map.c | 3 + src/parser.c | 4 +- src/string.c | 1 + src/types.c | 17 +++-- 14 files changed, 181 insertions(+), 154 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 5d2e53636..caee21a05 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -2,16 +2,9 @@ main :: proc() { - { - - x := [...]int{1, 2, 3, 4}; - immutable y := ^x; - fmt.println(y^[0]); - } - /* /* - Version 0.1.0 + Version 0.1.1 Added: * Dynamic Arrays `[...]Type` @@ -24,10 +17,11 @@ main :: proc() { * Entities prefixes with an underscore do not get exported on imports * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps * enum types have an implict `names` field, a []string of all the names in that enum + * immutable variables are "completely immutable" + * `slice_to_bytes` - convert any slice to a slice of bytes Removed: * Maybe/option types - * immutable variables * Remove `type` keyword and other "reserved" keywords * `compile_assert` and `assert`return the value of the condition for semantic reasons diff --git a/core/hash.odin b/core/hash.odin index ac421cda9..de1ffd0b6 100644 --- a/core/hash.odin +++ b/core/hash.odin @@ -1,14 +1,14 @@ crc32 :: proc(data: []byte) -> u32 { result := ~cast(u32)0; for b in data { - result = result>>8 ~ __CRC32_TABLE[(result ~ cast(u32)b) & 0xff]; + result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff]; } return ~result; } crc64 :: proc(data: []byte) -> u64 { result := ~cast(u64)0; for b in data { - result = result>>8 ~ __CRC64_TABLE[(result ~ cast(u64)b) & 0xff]; + result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff]; } return ~result; } @@ -202,7 +202,7 @@ murmur64 :: proc(data: []byte) -> u64 { } -__CRC32_TABLE := [256]u32{ +immutable _crc32_table := [256]u32{ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, @@ -268,7 +268,7 @@ __CRC32_TABLE := [256]u32{ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, }; -__CRC64_TABLE := [256]u64{ +immutable _crc64_table := [256]u64{ 0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5, 0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a, 0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b, diff --git a/core/opengl.odin b/core/opengl.odin index e76e63f06..2821740b4 100644 --- a/core/opengl.odin +++ b/core/opengl.odin @@ -20,13 +20,13 @@ Viewport :: proc(x, y, width, height: i32) #foreign lib "gl Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho"; Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f"; Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f"; +GetError :: proc() -> i32 #foreign lib "glGetError"; +GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString"; +GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv"; +TexCoord2f :: proc(x, y: f32) #foreign lib "glTexCoord2f"; TexImage2D :: proc(target, level, internal_format, width, height, border, - format, _type: i32, pixels: rawptr) #foreign lib "glTexImage2D"; - -GetError :: proc() -> i32 #foreign lib "glGetError"; -GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString"; -GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv"; + format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D"; string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; } @@ -42,114 +42,114 @@ GetProcAddress :: proc(name: string) -> proc() #cc_c { return res; } -GenBuffers: proc(count: i32, buffers: ^u32) #cc_c; -GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c; -GenSamplers: proc(count: i32, buffers: ^u32) #cc_c; -BindBuffer: proc(target: i32, buffer: u32) #cc_c; -BindVertexArray: proc(buffer: u32) #cc_c; -BindSampler: proc(position: i32, sampler: u32) #cc_c; -BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c; -BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c; +GenBuffers: proc(count: i32, buffers: ^u32) #cc_c; +GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c; +GenSamplers: proc(count: i32, buffers: ^u32) #cc_c; +BindBuffer: proc(target: i32, buffer: u32) #cc_c; +BindVertexArray: proc(buffer: u32) #cc_c; +BindSampler: proc(position: i32, sampler: u32) #cc_c; +BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c; +BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c; -DrawArrays: proc(mode, first: i32, count: u32) #cc_c; -DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c; +DrawArrays: proc(mode, first: i32, count: u32) #cc_c; +DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c; -MapBuffer: proc(target, access: i32) -> rawptr #cc_c; -UnmapBuffer: proc(target: i32) #cc_c; +MapBuffer: proc(target, access: i32) -> rawptr #cc_c; +UnmapBuffer: proc(target: i32) #cc_c; -VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c; +VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c; EnableVertexAttribArray: proc(index: u32) #cc_c; -CreateShader: proc(shader_type: i32) -> u32 #cc_c; -ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c; -CompileShader: proc(shader: u32) #cc_c; -CreateProgram: proc() -> u32 #cc_c; -AttachShader: proc(program, shader: u32) #cc_c; -DetachShader: proc(program, shader: u32) #cc_c; -DeleteShader: proc(shader: u32) #cc_c; -LinkProgram: proc(program: u32) #cc_c; -UseProgram: proc(program: u32) #cc_c; -DeleteProgram: proc(program: u32) #cc_c; +CreateShader: proc(shader_type: i32) -> u32 #cc_c; +ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c; +CompileShader: proc(shader: u32) #cc_c; +CreateProgram: proc() -> u32 #cc_c; +AttachShader: proc(program, shader: u32) #cc_c; +DetachShader: proc(program, shader: u32) #cc_c; +DeleteShader: proc(shader: u32) #cc_c; +LinkProgram: proc(program: u32) #cc_c; +UseProgram: proc(program: u32) #cc_c; +DeleteProgram: proc(program: u32) #cc_c; -GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c; -GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c; -GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c; -GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c; +GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c; +GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c; +GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c; +GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c; -ActiveTexture: proc(texture: i32) #cc_c; -GenerateMipmap: proc(target: i32) #cc_c; +ActiveTexture: proc(texture: i32) #cc_c; +GenerateMipmap: proc(target: i32) #cc_c; -SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c; -SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c; -SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c; -SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c; -SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c; -SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c; +SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c; +SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c; +SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c; +SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c; +SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c; +SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c; -Uniform1i: proc(loc: i32, v0: i32) #cc_c; -Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c; -Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c; -Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c; -Uniform1f: proc(loc: i32, v0: f32) #cc_c; -Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c; -Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c; -Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c; -UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c; +Uniform1i: proc(loc: i32, v0: i32) #cc_c; +Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c; +Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c; +Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c; +Uniform1f: proc(loc: i32, v0: f32) #cc_c; +Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c; +Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c; +Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c; +UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c; -GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c; +GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c; init :: proc() { set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); } - set_proc_address(^GenBuffers, "glGenBuffers\x00"); - set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00"); - set_proc_address(^GenSamplers, "glGenSamplers\x00"); - set_proc_address(^BindBuffer, "glBindBuffer\x00"); - set_proc_address(^BindSampler, "glBindSampler\x00"); - set_proc_address(^BindVertexArray, "glBindVertexArray\x00"); - set_proc_address(^BufferData, "glBufferData\x00"); - set_proc_address(^BufferSubData, "glBufferSubData\x00"); + set_proc_address(^GenBuffers, "glGenBuffers\x00"); + set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00"); + set_proc_address(^GenSamplers, "glGenSamplers\x00"); + set_proc_address(^BindBuffer, "glBindBuffer\x00"); + set_proc_address(^BindSampler, "glBindSampler\x00"); + set_proc_address(^BindVertexArray, "glBindVertexArray\x00"); + set_proc_address(^BufferData, "glBufferData\x00"); + set_proc_address(^BufferSubData, "glBufferSubData\x00"); - set_proc_address(^DrawArrays, "glDrawArrays\x00"); - set_proc_address(^DrawElements, "glDrawElements\x00"); + set_proc_address(^DrawArrays, "glDrawArrays\x00"); + set_proc_address(^DrawElements, "glDrawElements\x00"); - set_proc_address(^MapBuffer, "glMapBuffer\x00"); - set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00"); + set_proc_address(^MapBuffer, "glMapBuffer\x00"); + set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00"); set_proc_address(^VertexAttribPointer, "glVertexAttribPointer\x00"); set_proc_address(^EnableVertexAttribArray, "glEnableVertexAttribArray\x00"); - set_proc_address(^CreateShader, "glCreateShader\x00"); - set_proc_address(^ShaderSource, "glShaderSource\x00"); - set_proc_address(^CompileShader, "glCompileShader\x00"); - set_proc_address(^CreateProgram, "glCreateProgram\x00"); - set_proc_address(^AttachShader, "glAttachShader\x00"); - set_proc_address(^DetachShader, "glDetachShader\x00"); - set_proc_address(^DeleteShader, "glDeleteShader\x00"); - set_proc_address(^LinkProgram, "glLinkProgram\x00"); - set_proc_address(^UseProgram, "glUseProgram\x00"); - set_proc_address(^DeleteProgram, "glDeleteProgram\x00"); + set_proc_address(^CreateShader, "glCreateShader\x00"); + set_proc_address(^ShaderSource, "glShaderSource\x00"); + set_proc_address(^CompileShader, "glCompileShader\x00"); + set_proc_address(^CreateProgram, "glCreateProgram\x00"); + set_proc_address(^AttachShader, "glAttachShader\x00"); + set_proc_address(^DetachShader, "glDetachShader\x00"); + set_proc_address(^DeleteShader, "glDeleteShader\x00"); + set_proc_address(^LinkProgram, "glLinkProgram\x00"); + set_proc_address(^UseProgram, "glUseProgram\x00"); + set_proc_address(^DeleteProgram, "glDeleteProgram\x00"); - set_proc_address(^GetShaderiv, "glGetShaderiv\x00"); - set_proc_address(^GetProgramiv, "glGetProgramiv\x00"); - set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00"); - set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00"); + set_proc_address(^GetShaderiv, "glGetShaderiv\x00"); + set_proc_address(^GetProgramiv, "glGetProgramiv\x00"); + set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00"); + set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00"); - set_proc_address(^ActiveTexture, "glActiveTexture\x00"); - set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00"); + set_proc_address(^ActiveTexture, "glActiveTexture\x00"); + set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00"); - set_proc_address(^Uniform1i, "glUniform1i\x00"); - set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00"); + set_proc_address(^Uniform1i, "glUniform1i\x00"); + set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00"); - set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00"); + set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00"); - set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00"); - set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00"); - set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00"); - set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00"); - set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00"); - set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00"); + set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00"); + set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00"); + set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00"); + set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00"); + set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00"); + set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00"); } diff --git a/core/sys/windows.odin b/core/sys/windows.odin index 4c32cbd3e..165a0f761 100644 --- a/core/sys/windows.odin +++ b/core/sys/windows.odin @@ -41,11 +41,14 @@ WS_CAPTION :: 0x00C00000; WS_VISIBLE :: 0x10000000; WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX; -WM_DESTROY :: 0x0002; -WM_CLOSE :: 0x0010; -WM_QUIT :: 0x0012; -WM_KEYDOWN :: 0x0100; -WM_KEYUP :: 0x0101; +WM_DESTROY :: 0x0002; +WM_SIZE :: 0x0005; +WM_CLOSE :: 0x0010; +WM_ACTIVATEAPP :: 0x001C; +WM_QUIT :: 0x0012; +WM_KEYDOWN :: 0x0100; +WM_KEYUP :: 0x0101; +WM_SIZING :: 0x0214; PM_REMOVE :: 1; diff --git a/core/utf8.odin b/core/utf8.odin index d1798547d..8720e1f20 100644 --- a/core/utf8.odin +++ b/core/utf8.odin @@ -30,7 +30,7 @@ HICB :: 0b1011_1111; Accept_Range :: struct { lo, hi: u8 } -accept_ranges := [5]Accept_Range{ +immutable accept_ranges := [5]Accept_Range{ {0x80, 0xbf}, {0xa0, 0xbf}, {0x80, 0x9f}, @@ -38,7 +38,7 @@ accept_ranges := [5]Accept_Range{ {0x80, 0x8f}, }; -accept_sizes := [256]byte{ +immutable accept_sizes := [256]byte{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f diff --git a/src/build_settings.c b/src/build_settings.c index fd4af92c2..d1dcecf6e 100644 --- a/src/build_settings.c +++ b/src/build_settings.c @@ -1,4 +1,6 @@ +// This stores the information for the specify architecture of this build typedef struct BuildContext { + // Constants String ODIN_OS; // target operating system String ODIN_ARCH; // target architecture String ODIN_ENDIAN; // target endian @@ -6,8 +8,10 @@ typedef struct BuildContext { String ODIN_VERSION; // compiler version String ODIN_ROOT; // Odin ROOT - i64 word_size; - i64 max_align; + // In bytes + i64 word_size; // Size of a pointer, must be >= 4 + i64 max_align; // max alignment, must be >= 1 (and typically >= word_size) + String llc_flags; String link_flags; bool is_dll; diff --git a/src/check_expr.c b/src/check_expr.c index 25576ae85..355f4016e 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -4103,7 +4103,8 @@ void check_set_mode_with_indirection(Operand *o, bool indirection) { if (o->mode != Addressing_Immutable) { if (indirection) { o->mode = Addressing_Variable; - } else if (o->mode != Addressing_Variable) { + } else if (o->mode != Addressing_Variable && + o->mode != Addressing_Constant) { o->mode = Addressing_Value; } } @@ -4139,7 +4140,9 @@ bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_cou case Type_Slice: o->type = t->Slice.elem; - o->mode = Addressing_Variable; + if (o->mode != Addressing_Immutable) { + o->mode = Addressing_Variable; + } return true; case Type_DynamicArray: @@ -5132,7 +5135,9 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } - o->mode = Addressing_Value; + if (o->mode != Addressing_Immutable) { + o->mode = Addressing_Value; + } i64 indices[2] = {0}; AstNode *nodes[2] = {se->low, se->high}; diff --git a/src/checker.c b/src/checker.c index 5ae1460ad..c2fd9e672 100644 --- a/src/checker.c +++ b/src/checker.c @@ -6,22 +6,6 @@ typedef enum ExprKind { Expr_Stmt, } ExprKind; -typedef enum AddressingMode { - Addressing_Invalid, - - Addressing_NoValue, - Addressing_Value, - Addressing_Variable, - Addressing_Immutable, - Addressing_Constant, - Addressing_Type, - Addressing_Builtin, - Addressing_Overload, - Addressing_MapIndex, - - Addressing_Count, -} AddressingMode; - // Statements and Declarations typedef enum StmtFlag { Stmt_BreakAllowed = 1<<0, @@ -124,11 +108,26 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { #include "types.c" -#define MAP_TYPE Entity * -#define MAP_PROC map_entity_ -#define MAP_NAME MapEntity -#include "map.c" +typedef enum AddressingMode { + Addressing_Invalid, // invalid addressing mode + Addressing_NoValue, // no value (void in C) + Addressing_Value, // computed value (rvalue) + Addressing_Immutable, // immutable computed value (const rvalue) + Addressing_Variable, // addressable variable (lvalue) + Addressing_Constant, // constant & type will be a of Type_Basic (stripping Type_Named) + Addressing_Type, // type + Addressing_Builtin, // built in procedure + Addressing_Overload, // overloaded procedure + Addressing_MapIndex, // map index expression + // lhs: acts like a Variable + // ths: acts like a value with an optional boolean part (for existence check) +} AddressingMode; +// Operand is used as an intermediate value whilst checking +// Operands store an addressing mode, the expression being evaluated, +// its type and node, and other specific information for certain +// addressing modes +// Its zero-value is a valid "invalid operand" typedef struct Operand { AddressingMode mode; Type * type; @@ -161,7 +160,7 @@ bool is_operand_nil(Operand o) { } - +// DeclInfo is used to store information of certain declarations to allow for "any order" usage typedef struct DeclInfo { Scope *scope; @@ -175,6 +174,17 @@ typedef struct DeclInfo { MapBool deps; // Key: Entity * } DeclInfo; +// ProcedureInfo stores the information needed for checking a procedure +typedef struct ProcedureInfo { + AstFile * file; + Token token; + DeclInfo *decl; + Type * type; // Type_Procedure + AstNode * body; // AstNode_BlockStmt + u32 tags; +} ProcedureInfo; + +// ExprInfo stores information used for "untyped" expressions typedef struct ExprInfo { bool is_lhs; // Debug info AddressingMode mode; @@ -187,14 +197,12 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue return ei; } -typedef struct ProcedureInfo { - AstFile * file; - Token token; - DeclInfo *decl; - Type * type; // Type_Procedure - AstNode * body; // AstNode_BlockStmt - u32 tags; -} ProcedureInfo; + + +#define MAP_TYPE Entity * +#define MAP_PROC map_entity_ +#define MAP_NAME MapEntity +#include "map.c" typedef struct Scope { Scope * parent; @@ -258,7 +266,7 @@ typedef struct CheckerContext { Type * type_hint; } CheckerContext; -// NOTE(bill): Symbol tables +// CheckerInfo stores all the symbol information for a type-checked program typedef struct CheckerInfo { MapTypeAndValue types; // Key: AstNode * | Expression -> Type (and value) MapEntity definitions; // Key: AstNode * | Identifier -> Entity diff --git a/src/common.c b/src/common.c index f09282b5a..2c40e22ed 100644 --- a/src/common.c +++ b/src/common.c @@ -131,6 +131,9 @@ i16 f32_to_f16(f32 value) { // //////////////////////////////////////////////////////////////// +typedef Array(i32) Array_i32; +typedef Array(isize) Array_isize; + #define MAP_TYPE String #define MAP_PROC map_string_ diff --git a/src/entity.c b/src/entity.c index 04ef323a1..99acef870 100644 --- a/src/entity.c +++ b/src/entity.c @@ -40,12 +40,15 @@ typedef enum EntityFlag { EntityFlag_TypeField = 1<<8, } EntityFlag; +// Zero value means the overloading process is not yet done typedef enum OverloadKind { - Overload_No = -1, - Overload_Unknown = 0, - Overload_Yes = +1, + Overload_Unknown, + Overload_No, + Overload_Yes, } OverloadKind; + +// An Entity is a named "thing" in the language typedef struct Entity Entity; struct Entity { EntityKind kind; diff --git a/src/map.c b/src/map.c index 60af7f668..83c9ce95a 100644 --- a/src/map.c +++ b/src/map.c @@ -6,6 +6,9 @@ #define MAP_NAME MapString #include "map.c" */ +// A `Map` is an unordered hash table which can allow for a key to point to multiple values +// with the use of the `multi_*` procedures. +// TODO(bill): I should probably allow the `multi_*` stuff to be #ifdefed out #ifndef MAP_UTIL_STUFF #define MAP_UTIL_STUFF diff --git a/src/parser.c b/src/parser.c index 69ea32c48..fedde531e 100644 --- a/src/parser.c +++ b/src/parser.c @@ -111,7 +111,9 @@ AstNodeArray make_ast_node_array(AstFile *f) { } - +// NOTE(bill): This massive define is so it is possible to create a discriminated union (and extra debug info) +// for the AstNode. I personally prefer discriminated unions over subtype polymorphism as I can preallocate +// all the nodes and even memcpy in a different kind of node #define AST_NODE_KINDS \ AST_NODE_KIND(Ident, "identifier", Token) \ AST_NODE_KIND(Implicit, "implicit", Token) \ diff --git a/src/string.c b/src/string.c index 38874a7d5..19161de93 100644 --- a/src/string.c +++ b/src/string.c @@ -19,6 +19,7 @@ typedef struct String { #define str_lit(c_str) (String){cast(u8 *)c_str, gb_size_of(c_str)-1} +// NOTE(bill): String16 is only used for Windows due to its file directories typedef struct String16 { wchar_t *text; isize len; diff --git a/src/types.c b/src/types.c index 6c0524955..06a407576 100644 --- a/src/types.c +++ b/src/types.c @@ -178,8 +178,9 @@ typedef struct Type { bool failure; } Type; -typedef Array(i32) Array_i32; +// TODO(bill): Should I add extra information here specifying the kind of selection? +// e.g. field, constant, vector field, type field, etc. typedef struct Selection { Entity * entity; Array_i32 index; @@ -195,6 +196,7 @@ Selection make_selection(Entity *entity, Array_i32 index, bool indirect) { void selection_add_index(Selection *s, isize index) { // IMPORTANT NOTE(bill): this requires a stretchy buffer/dynamic array so it requires some form // of heap allocation + // TODO(bill): Find a way to use a backing buffer for initial use as the general case is probably .count<3 if (s->index.e == NULL) { array_init(&s->index, heap_allocator()); } @@ -277,6 +279,7 @@ gb_global Type *t_byte_slice = NULL; gb_global Type *t_string_slice = NULL; +// Type generated for the "preload" file gb_global Type *t_type_info = NULL; gb_global Type *t_type_info_record = NULL; gb_global Type *t_type_info_enum_value = NULL; @@ -303,7 +306,6 @@ gb_global Type *t_type_info_raw_union = NULL; gb_global Type *t_type_info_enum = NULL; gb_global Type *t_type_info_map = NULL; - gb_global Type *t_type_info_named_ptr = NULL; gb_global Type *t_type_info_integer_ptr = NULL; gb_global Type *t_type_info_float_ptr = NULL; @@ -323,8 +325,6 @@ gb_global Type *t_type_info_raw_union_ptr = NULL; gb_global Type *t_type_info_enum_ptr = NULL; gb_global Type *t_type_info_map_ptr = NULL; - - gb_global Type *t_allocator = NULL; gb_global Type *t_allocator_ptr = NULL; gb_global Type *t_context = NULL; @@ -337,14 +337,15 @@ gb_global Type *t_map_header = NULL; -i64 type_size_of (gbAllocator allocator, Type *t); -i64 type_align_of (gbAllocator allocator, Type *t); -i64 type_offset_of(gbAllocator allocator, Type *t, i32 index); - +i64 type_size_of (gbAllocator allocator, Type *t); +i64 type_align_of (gbAllocator allocator, Type *t); +i64 type_offset_of (gbAllocator allocator, Type *t, i32 index); gbString type_to_string(Type *type); + + Type *base_type(Type *t) { for (;;) { if (t == NULL) { From 8b5e3428a1e569abf763e63e859754e767e107e7 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 16:37:24 +0000 Subject: [PATCH 4/8] Optional ok for `union_cast` (similar to map indices) --- code/demo.odin | 8 +++++++- core/_preload.odin | 25 ++++++++++++++----------- core/fmt.odin | 6 +++--- src/check_decl.c | 11 ----------- src/check_expr.c | 33 ++++++++++++--------------------- src/check_stmt.c | 40 ++++++++++++++++++---------------------- src/checker.c | 25 +++++++++++++------------ src/ir.c | 41 +++++++++++++++++++++++++++++++++-------- 8 files changed, 100 insertions(+), 89 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index caee21a05..a2049654c 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -2,6 +2,12 @@ main :: proc() { + x := type_info(int); + t1, ok := union_cast(^Type_Info.Integer)x; + _, ok = union_cast(^Type_Info.Integer)x; + t2 := union_cast(^Type_Info.Integer)x; + + /* /* Version 0.1.1 @@ -17,7 +23,7 @@ main :: proc() { * Entities prefixes with an underscore do not get exported on imports * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps * enum types have an implict `names` field, a []string of all the names in that enum - * immutable variables are "completely immutable" + * immutable variables are "completely immutable" - rules need a full explanation * `slice_to_bytes` - convert any slice to a slice of bytes Removed: diff --git a/core/_preload.odin b/core/_preload.odin index 7ff3bc44f..5114e8ef2 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -315,7 +315,11 @@ __assert :: proc(file: string, line, column: int, msg: string) #inline { file, line, column, msg); __debug_trap(); } - +__panic :: proc(file: string, line, column: int, msg: string) #inline { + fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n", + file, line, column, msg); + __debug_trap(); +} __bounds_check_error :: proc(file: string, line, column: int, index, count: int) { if 0 <= index && index < count { return; @@ -341,6 +345,13 @@ __substring_expr_error :: proc(file: string, line, column: int, low, high: int) file, line, column, low, high); __debug_trap(); } +__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) { + if !ok { + fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n", + file, line, column, from, to); + __debug_trap(); + } +} __string_decode_rune :: proc(s: string) -> (rune, int) #inline { return utf8.decode_rune(s); @@ -449,6 +460,8 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in } +// Map stuff + __default_hash :: proc(data: []byte) -> u64 { return hash.fnv64a(data); } @@ -650,13 +663,3 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) { m.hashes[last.hash_index] = fr.entry_index; } } - - -__print_ti_ptr :: proc(ti: ^Type_Info) { - fmt.println(ti); - match e in ti { - case Type_Info.Enum: - fmt.println(e.names); - } -} - diff --git a/core/fmt.odin b/core/fmt.odin index 542f951e9..937d15c8a 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -790,10 +790,10 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { buffer_write_string(fi.buf, "map["); defer buffer_write_byte(fi.buf, ']'); entries := ^(cast(^Raw_Dynamic_Map)v.data).entries; - gs, gs_ok := union_cast(^Struct)type_info_base(info.generated_struct); assert(gs_ok); - ed, ed_ok := union_cast(^Dynamic_Array)type_info_base(gs.types[1]); assert(ed_ok); + gs := union_cast(^Struct)type_info_base(info.generated_struct); + ed := union_cast(^Dynamic_Array)type_info_base(gs.types[1]); - entry_type, et_ok := union_cast(^Struct)ed.elem; assert(et_ok); + entry_type := union_cast(^Struct)ed.elem; entry_size := ed.elem_size; for i in 0.. 0 { diff --git a/src/check_decl.c b/src/check_decl.c index ebee92b5e..bc49e36c1 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -72,25 +72,14 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra } } - isize max = gb_min(lhs_count, rhs_count); for (isize i = 0; i < max; i++) { check_init_variable(c, lhs[i], &operands.e[i], context_name); } - if (rhs_count > 0 && lhs_count != rhs_count) { error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); } -#if 0 - if (lhs[0]->kind == Entity_Variable && - lhs[0]->Variable.is_let) { - if (lhs_count != rhs_count) { - error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count); - } - } -#endif - gb_temp_arena_memory_end(tmp); } diff --git a/src/check_expr.c b/src/check_expr.c index 355f4016e..a93165574 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -265,8 +265,6 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n } - - if (type == NULL) { return; } @@ -1115,7 +1113,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { return 0; } -Type *make_map_tuple_type(gbAllocator a, Type *value) { +Type *make_optional_ok_type(gbAllocator a, Type *value) { Type *t = make_type_tuple(a); t->Tuple.variables = gb_alloc_array(a, Entity *, 2); t->Tuple.variable_count = 2; @@ -1221,7 +1219,7 @@ void check_map_type(Checker *c, Type *type, AstNode *node) { type->Map.generated_struct_type = generated_struct_type; } - type->Map.lookup_result_type = make_map_tuple_type(a, value); + type->Map.lookup_result_type = make_optional_ok_type(a, value); // error_node(node, "`map` types are not yet implemented"); } @@ -3821,17 +3819,15 @@ int valid_proc_and_score_cmp(void const *a, void const *b) { typedef Array(Operand) ArrayOperand; -void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray args, bool allow_map_ok) { - for_array(i, args) { +void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray rhs, bool allow_ok) { + for_array(i, rhs) { Operand o = {0}; - check_multi_expr(c, &o, args.e[i]); + check_multi_expr(c, &o, rhs.e[i]); if (o.type == NULL || o.type->kind != Type_Tuple) { - if (o.mode == Addressing_MapIndex && - allow_map_ok && - lhs_count == 2 && - args.count == 1) { - Type *tuple = make_map_tuple_type(c->allocator, o.type); + if (allow_ok && lhs_count == 2 && rhs.count == 1 && + (o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk)) { + Type *tuple = make_optional_ok_type(c->allocator, o.type); add_type_and_value(&c->info, o.expr, o.mode, tuple, o.value); Operand val = o; @@ -4922,16 +4918,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } - Entity **variables = gb_alloc_array(c->allocator, Entity *, 2); - variables[0] = make_entity_param(c->allocator, NULL, empty_token, t, false, true); - variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false, true); + add_type_info_type(c, o->type); + add_type_info_type(c, t); - Type *tuple = make_type_tuple(c->allocator); - tuple->Tuple.variables = variables; - tuple->Tuple.variable_count = 2; - - o->type = tuple; - o->mode = Addressing_Value; + o->type = t; + o->mode = Addressing_OptionalOk; } break; case Token_down_cast: { if (o->mode == Addressing_Constant) { diff --git a/src/check_stmt.c b/src/check_stmt.c index 6769ddb40..138e1277e 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -260,6 +260,7 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { switch (lhs.mode) { case Addressing_Invalid: return NULL; + case Addressing_Variable: break; case Addressing_MapIndex: { @@ -278,6 +279,7 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) { } } } break; + default: { if (lhs.expr->kind == AstNode_SelectorExpr) { // NOTE(bill): Extra error checks @@ -434,40 +436,34 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { switch (as->op.kind) { case Token_Eq: { // a, b, c = 1, 2, 3; // Multisided - if (as->lhs.count == 0) { + + isize lhs_count = as->lhs.count; + if (lhs_count == 0) { error(as->op, "Missing lhs in assignment statement"); return; } + // TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just + // leave it? + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // an extra allocation - Array(Operand) operands; - array_init_reserve(&operands, c->tmp_allocator, 2 * as->lhs.count); + ArrayOperand operands = {0}; + array_init_reserve(&operands, c->tmp_allocator, 2 * lhs_count); + check_unpack_arguments(c, lhs_count, &operands, as->rhs, true); - for_array(i, as->rhs) { - AstNode *rhs = as->rhs.e[i]; - Operand o = {0}; - check_multi_expr(c, &o, rhs); - if (o.type->kind != Type_Tuple) { - array_add(&operands, o); - } else { - TypeTuple *tuple = &o.type->Tuple; - for (isize j = 0; j < tuple->variable_count; j++) { - o.type = tuple->variables[j]->type; - array_add(&operands, o); - } + isize rhs_count = operands.count; + for_array(i, operands) { + if (operands.e[i].mode == Addressing_Invalid) { + rhs_count--; } } - isize lhs_count = as->lhs.count; - isize rhs_count = operands.count; - - isize operand_count = gb_min(as->lhs.count, operands.count); - for (isize i = 0; i < operand_count; i++) { - AstNode *lhs = as->lhs.e[i]; - check_assignment_variable(c, &operands.e[i], lhs); + isize max = gb_min(lhs_count, rhs_count); + for (isize i = 0; i < max; i++) { + check_assignment_variable(c, &operands.e[i], as->lhs.e[i]); } if (lhs_count != rhs_count) { error_node(as->lhs.e[0], "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); diff --git a/src/checker.c b/src/checker.c index c2fd9e672..9fbf3d36d 100644 --- a/src/checker.c +++ b/src/checker.c @@ -109,18 +109,19 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { #include "types.c" typedef enum AddressingMode { - Addressing_Invalid, // invalid addressing mode - Addressing_NoValue, // no value (void in C) - Addressing_Value, // computed value (rvalue) - Addressing_Immutable, // immutable computed value (const rvalue) - Addressing_Variable, // addressable variable (lvalue) - Addressing_Constant, // constant & type will be a of Type_Basic (stripping Type_Named) - Addressing_Type, // type - Addressing_Builtin, // built in procedure - Addressing_Overload, // overloaded procedure - Addressing_MapIndex, // map index expression - // lhs: acts like a Variable - // ths: acts like a value with an optional boolean part (for existence check) + Addressing_Invalid, // invalid addressing mode + Addressing_NoValue, // no value (void in C) + Addressing_Value, // computed value (rvalue) + Addressing_Immutable, // immutable computed value (const rvalue) + Addressing_Variable, // addressable variable (lvalue) + Addressing_Constant, // constant & type will be a of Type_Basic (stripping Type_Named) + Addressing_Type, // type + Addressing_Builtin, // built in procedure + Addressing_Overload, // overloaded procedure + Addressing_MapIndex, // map index expression - + // lhs: acts like a Variable + // rhs: acts like OptionalOk + Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) } AddressingMode; // Operand is used as an intermediate value whilst checking diff --git a/src/ir.c b/src/ir.c index f49f9200e..e6bf588de 100644 --- a/src/ir.c +++ b/src/ir.c @@ -2462,13 +2462,19 @@ irValue *ir_emit_down_cast(irProcedure *proc, irValue *value, Type *t) { return ir_emit_conv(proc, head, t); } -irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *tuple) { - GB_ASSERT(tuple->kind == Type_Tuple); +irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *type, TokenPos pos) { gbAllocator a = proc->module->allocator; Type *src_type = ir_type(value); bool is_ptr = is_type_pointer(src_type); + bool is_tuple = true; + Type *tuple = type; + if (type->kind != Type_Tuple) { + is_tuple = false; + tuple = make_optional_ok_type(a, type); + } + irValue *v = ir_add_local_generated(proc, tuple); if (is_ptr) { @@ -2541,6 +2547,25 @@ irValue *ir_emit_union_cast(irProcedure *proc, irValue *value, Type *tuple) { ir_start_block(proc, end_block); } + + if (!is_tuple) { + // NOTE(bill): Panic on invalid conversion + Type *dst_type = tuple->Tuple.variables[0]->type; + + irValue *ok = ir_emit_load(proc, ir_emit_struct_ep(proc, v, 1)); + irValue **args = gb_alloc_array(a, irValue *, 6); + args[0] = ok; + + args[1] = ir_make_const_string(a, pos.file); + args[2] = ir_make_const_int(a, pos.line); + args[3] = ir_make_const_int(a, pos.column); + + args[4] = ir_type_info(proc, src_type); + args[5] = ir_type_info(proc, dst_type); + ir_emit_global_call(proc, "__union_cast_check", args, 6); + + return ir_emit_load(proc, ir_emit_struct_ep(proc, v, 0)); + } return ir_emit_load(proc, v); } @@ -2930,23 +2955,23 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv case_ast_node(ce, CastExpr, expr); Type *type = tv->type; - irValue *expr = ir_build_expr(proc, ce->expr); + irValue *e = ir_build_expr(proc, ce->expr); switch (ce->token.kind) { case Token_cast: ir_emit_comment(proc, str_lit("cast - cast")); - return ir_emit_conv(proc, expr, type); + return ir_emit_conv(proc, e, type); case Token_transmute: ir_emit_comment(proc, str_lit("cast - transmute")); - return ir_emit_transmute(proc, expr, type); + return ir_emit_transmute(proc, e, type); case Token_down_cast: ir_emit_comment(proc, str_lit("cast - down_cast")); - return ir_emit_down_cast(proc, expr, type); + return ir_emit_down_cast(proc, e, type); case Token_union_cast: ir_emit_comment(proc, str_lit("cast - union_cast")); - return ir_emit_union_cast(proc, expr, type); + return ir_emit_union_cast(proc, e, type, ast_node_token(expr).pos); default: GB_PANIC("Unknown cast expression"); @@ -3384,7 +3409,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv args[1] = ir_make_const_int(proc->module->allocator, pos.line); args[2] = ir_make_const_int(proc->module->allocator, pos.column); args[3] = msg; - ir_emit_global_call(proc, "__assert", args, 4); + ir_emit_global_call(proc, "__panic", args, 4); return NULL; } break; From 2722de65b7e2397c0b968abc4c652711095ec7ca Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 17:24:56 +0000 Subject: [PATCH 5/8] Prevent `cast` on pointer to union types --- code/demo.odin | 7 +++++++ core/_preload.odin | 2 +- core/fmt.odin | 2 +- core/types.odin | 2 +- src/check_expr.c | 17 ++++++++++++++--- src/parser.c | 6 +++--- src/tokenizer.c | 2 ++ 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index a2049654c..438488045 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,4 +1,11 @@ #import "fmt.odin"; +#import "atomic.odin"; +#import "hash.odin"; +#import "math.odin"; +#import "mem.odin"; +#import "opengl.odin"; +#import "os.odin"; +#import "utf8.odin"; main :: proc() { diff --git a/core/_preload.odin b/core/_preload.odin index 5114e8ef2..28cd761f4 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -381,7 +381,7 @@ Raw_Dynamic_Array :: struct #ordered { }; Raw_Dynamic_Map :: struct #ordered { - hashes: [...]int, + hashes: [dynamic]int, entries: Raw_Dynamic_Array, }; diff --git a/core/fmt.odin b/core/fmt.odin index 937d15c8a..86af31a59 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -140,7 +140,7 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { if info.params == nil { buffer_write_string(buf, "()"); } else { - t := cast(^Tuple)info.params; + t := union_cast(^Tuple)info.params; buffer_write_string(buf, "("); for type, i in t.types { if i > 0 { buffer_write_string(buf, ", "); } diff --git a/core/types.odin b/core/types.odin index 5a46770d5..c541aaac5 100644 --- a/core/types.odin +++ b/core/types.odin @@ -1,6 +1,6 @@ is_signed :: proc(info: ^Type_Info) -> bool { if is_integer(info) { - i := cast(^Type_Info.Integer)info; + i := union_cast(^Type_Info.Integer)info; return i.signed; } if is_float(info) { diff --git a/src/check_expr.c b/src/check_expr.c index a93165574..9ea57bd21 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1071,7 +1071,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { } Operand o = {0}; if (e->kind == AstNode_UnaryExpr && - e->UnaryExpr.op.kind == Token_Question) { + e->UnaryExpr.op.kind == Token_Ellipsis) { return -1; } @@ -1966,10 +1966,21 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { // Cast between pointers if (is_type_pointer(src) && is_type_pointer(dst)) { + Type *s = base_type(type_deref(src)); + if (is_type_union(s)) { + // NOTE(bill): Should the error be here?! + // NOTE(bill): This error should suppress the next casting error as it's at the same position + gbString xs = type_to_string(x); + gbString ys = type_to_string(y); + error_node(operand->expr, "Cannot cast from a union pointer `%s` to `%s`, try using `union_cast` or cast to a `rawptr`", xs, ys); + gb_string_free(ys); + gb_string_free(xs); + return false; + } return true; } - // (u)int <-> pointer + // (u)int <-> rawptr if (is_type_int_or_uint(src) && is_type_rawptr(dst)) { return true; } @@ -4494,7 +4505,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != NULL) { AstNode *count = cl->type->ArrayType.count; if (count->kind == AstNode_UnaryExpr && - count->UnaryExpr.op.kind == Token_Question) { + count->UnaryExpr.op.kind == Token_Ellipsis) { type = make_type_array(c->allocator, check_type(c, cl->type->ArrayType.elem), -1); is_to_be_determined_array_count = true; } diff --git a/src/parser.c b/src/parser.c index fedde531e..987022068 100644 --- a/src/parser.c +++ b/src/parser.c @@ -2574,8 +2574,8 @@ AstNode *parse_type_or_ident(AstFile *f) { AstNode *count_expr = NULL; bool is_vector = false; - if (f->curr_token.kind == Token_Question) { - count_expr = ast_unary_expr(f, expect_token(f, Token_Question), NULL); + if (f->curr_token.kind == Token_Ellipsis) { + count_expr = ast_unary_expr(f, expect_token(f, Token_Ellipsis), NULL); } else if (f->curr_token.kind == Token_vector) { next_token(f); if (f->curr_token.kind != Token_CloseBracket) { @@ -2586,7 +2586,7 @@ AstNode *parse_type_or_ident(AstFile *f) { syntax_error(f->curr_token, "Vector type missing count"); } is_vector = true; - } else if (f->curr_token.kind == Token_Ellipsis) { + } else if (f->curr_token.kind == Token_dynamic) { next_token(f); expect_token(f, Token_CloseBracket); return ast_dynamic_array_type(f, token, parse_type(f)); diff --git a/src/tokenizer.c b/src/tokenizer.c index fc85fd927..68ab270be 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -99,6 +99,8 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_raw_union, "raw_union"), \ TOKEN_KIND(Token_enum, "enum"), \ TOKEN_KIND(Token_vector, "vector"), \ + TOKEN_KIND(Token_static, "static"), \ + TOKEN_KIND(Token_dynamic, "dynamic"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \ From daa1cd55a1ba79a7f5e59a1615d7cb750547a006 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 17:33:11 +0000 Subject: [PATCH 6/8] Move error handling for casting --- core/fmt.odin | 3 +- src/check_expr.c | 162 ++++++++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 81 deletions(-) diff --git a/core/fmt.odin b/core/fmt.odin index 86af31a59..32c369dbe 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -155,10 +155,9 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { case Tuple: count := info.names.count; if count != 1 { buffer_write_string(buf, "("); } - for i in 0.. 0 { buffer_write_string(buf, ", "); } - name := info.names[i]; type := info.types[i]; if name.count > 0 { diff --git a/src/check_expr.c b/src/check_expr.c index 9ea57bd21..25f7dd942 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1927,6 +1927,76 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) { x->mode = Addressing_Value; } + +String check_down_cast_name(Type *dst_, Type *src_) { + String result = {0}; + Type *dst = type_deref(dst_); + Type *src = type_deref(src_); + Type *dst_s = base_type(dst); + GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s)); + for (isize i = 0; i < dst_s->Record.field_count; i++) { + Entity *f = dst_s->Record.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); + if (f->flags & EntityFlag_Anonymous) { + if (are_types_identical(f->type, src_)) { + return f->token.string; + } + if (are_types_identical(type_deref(f->type), src_)) { + return f->token.string; + } + + if (!is_type_pointer(f->type)) { + result = check_down_cast_name(f->type, src_); + if (result.len > 0) { + return result; + } + } + } + } + + return result; +} + +Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offset, AstNode *node) { + GB_ASSERT(node->kind == AstNode_BinaryExpr); + ast_node(be, BinaryExpr, node); + GB_ASSERT(is_type_pointer(ptr->type)); + GB_ASSERT(is_type_integer(offset->type)); + GB_ASSERT(op == Token_Add || op == Token_Sub); + + Operand operand = {0}; + operand.mode = Addressing_Value; + operand.type = ptr->type; + operand.expr = node; + + if (base_type(ptr->type) == t_rawptr) { + gbString str = type_to_string(ptr->type); + error_node(node, "Invalid pointer type for pointer arithmetic: `%s`", str); + gb_string_free(str); + operand.mode = Addressing_Invalid; + return operand; + } + + + if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { + i64 elem_size = type_size_of(c->allocator, ptr->type); + i64 ptr_val = ptr->value.value_pointer; + i64 offset_val = exact_value_to_integer(offset->value).value_integer; + i64 new_ptr_val = ptr_val; + if (op == Token_Add) { + new_ptr_val += elem_size*offset_val; + } else { + new_ptr_val -= elem_size*offset_val; + } + operand.mode = Addressing_Constant; + operand.value = make_exact_value_pointer(new_ptr_val); + } + + return operand; +} + + + bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { if (check_is_assignable_to(c, operand, y)) { return true; @@ -2012,78 +2082,19 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { return true; } + { + gbString expr_str = expr_to_string(operand->expr); + gbString to_type = type_to_string(y); + gbString from_type = type_to_string(x); + error_node(operand->expr, "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); + gb_string_free(from_type); + gb_string_free(to_type); + gb_string_free(expr_str); + } return false; } -String check_down_cast_name(Type *dst_, Type *src_) { - String result = {0}; - Type *dst = type_deref(dst_); - Type *src = type_deref(src_); - Type *dst_s = base_type(dst); - GB_ASSERT(is_type_struct(dst_s) || is_type_raw_union(dst_s)); - for (isize i = 0; i < dst_s->Record.field_count; i++) { - Entity *f = dst_s->Record.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field); - if (f->flags & EntityFlag_Anonymous) { - if (are_types_identical(f->type, src_)) { - return f->token.string; - } - if (are_types_identical(type_deref(f->type), src_)) { - return f->token.string; - } - - if (!is_type_pointer(f->type)) { - result = check_down_cast_name(f->type, src_); - if (result.len > 0) { - return result; - } - } - } - } - - return result; -} - -Operand check_ptr_addition(Checker *c, TokenKind op, Operand *ptr, Operand *offset, AstNode *node) { - GB_ASSERT(node->kind == AstNode_BinaryExpr); - ast_node(be, BinaryExpr, node); - GB_ASSERT(is_type_pointer(ptr->type)); - GB_ASSERT(is_type_integer(offset->type)); - GB_ASSERT(op == Token_Add || op == Token_Sub); - - Operand operand = {0}; - operand.mode = Addressing_Value; - operand.type = ptr->type; - operand.expr = node; - - if (base_type(ptr->type) == t_rawptr) { - gbString str = type_to_string(ptr->type); - error_node(node, "Invalid pointer type for pointer arithmetic: `%s`", str); - gb_string_free(str); - operand.mode = Addressing_Invalid; - return operand; - } - - - if (ptr->mode == Addressing_Constant && offset->mode == Addressing_Constant) { - i64 elem_size = type_size_of(c->allocator, ptr->type); - i64 ptr_val = ptr->value.value_pointer; - i64 offset_val = exact_value_to_integer(offset->value).value_integer; - i64 new_ptr_val = ptr_val; - if (op == Token_Add) { - new_ptr_val += elem_size*offset_val; - } else { - new_ptr_val -= elem_size*offset_val; - } - operand.mode = Addressing_Constant; - operand.value = make_exact_value_pointer(new_ptr_val); - } - - return operand; -} - - -void check_conversion(Checker *c, Operand *x, Type *type) { +void check_cast(Checker *c, Operand *x, Type *type) { bool is_const_expr = x->mode == Addressing_Constant; bool can_convert = false; @@ -2104,14 +2115,7 @@ void check_conversion(Checker *c, Operand *x, Type *type) { } if (!can_convert) { - gbString expr_str = expr_to_string(x->expr); - gbString to_type = type_to_string(type); - gbString from_type = type_to_string(x->type); - error_node(x->expr, "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); - gb_string_free(from_type); - gb_string_free(to_type); - gb_string_free(expr_str); - + // NOTE(bill): Error handled in `cast_is_castable_to` x->mode = Addressing_Invalid; return; } @@ -4007,7 +4011,7 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { case 1: check_expr(c, operand, ce->args.e[0]); if (operand->mode != Addressing_Invalid) { - check_conversion(c, operand, t); + check_cast(c, operand, t); } break; } @@ -4836,7 +4840,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } switch (ce->token.kind) { case Token_cast: - check_conversion(c, o, t); + check_cast(c, o, t); break; case Token_transmute: { if (o->mode == Addressing_Constant) { From 3ecf3505fd62c6aa10190a0be97e8572476e8c8b Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 17:34:02 +0000 Subject: [PATCH 7/8] Ignore previous silly commit :P I shouldn't have move it --- src/check_expr.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/check_expr.c b/src/check_expr.c index 25f7dd942..4461a4def 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -2082,15 +2082,6 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { return true; } - { - gbString expr_str = expr_to_string(operand->expr); - gbString to_type = type_to_string(y); - gbString from_type = type_to_string(x); - error_node(operand->expr, "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); - gb_string_free(from_type); - gb_string_free(to_type); - gb_string_free(expr_str); - } return false; } @@ -2115,7 +2106,13 @@ void check_cast(Checker *c, Operand *x, Type *type) { } if (!can_convert) { - // NOTE(bill): Error handled in `cast_is_castable_to` + gbString expr_str = expr_to_string(x->expr); + gbString to_type = type_to_string(type); + gbString from_type = type_to_string(x->type); + error_node(x->expr, "Cannot cast `%s` as `%s` from `%s`", expr_str, to_type, from_type); + gb_string_free(from_type); + gb_string_free(to_type); + gb_string_free(expr_str); x->mode = Addressing_Invalid; return; } From 71100ed427ee2eec8d8a9d4d9616102738097e80 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 14 Feb 2017 19:26:32 +0000 Subject: [PATCH 8/8] Ternary expression (removed if and block expression) --- code/demo.odin | 15 ++- core/fmt.odin | 12 ++- src/check_expr.c | 228 +++++------------------------------------ src/check_stmt.c | 11 -- src/checker.c | 4 +- src/ir.c | 88 ++++++---------- src/parser.c | 256 ++++++++++++++++++++++------------------------- 7 files changed, 188 insertions(+), 426 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 438488045..a3fee1555 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -9,18 +9,14 @@ main :: proc() { - x := type_info(int); - t1, ok := union_cast(^Type_Info.Integer)x; - _, ok = union_cast(^Type_Info.Integer)x; - t2 := union_cast(^Type_Info.Integer)x; - - + x: f32 = false ? 123 : 55; + fmt.println("Ternary:", x); /* /* Version 0.1.1 Added: - * Dynamic Arrays `[...]Type` + * Dynamic Arrays [dynamic]Type` * Dynamic Maps `map[Key]Value` * Dynamic array and map literals * Custom struct alignemnt `struct #align 8 { bar: i8 }` @@ -32,6 +28,7 @@ main :: proc() { * enum types have an implict `names` field, a []string of all the names in that enum * immutable variables are "completely immutable" - rules need a full explanation * `slice_to_bytes` - convert any slice to a slice of bytes + * `union_cast` allows for optional ok check Removed: * Maybe/option types @@ -101,7 +98,7 @@ main :: proc() { } { - x: [...]f64; + x: [dynamic]f64; reserve(x, 16); defer free(x); append(x, 2_000_000.500_000, 3, 5, 7); @@ -114,7 +111,7 @@ main :: proc() { } { - x := [...]f64{2_000_000.500_000, 3, 5, 7}; + x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; defer free(x); fmt.println(x); } diff --git a/core/fmt.odin b/core/fmt.odin index 32c369dbe..22e7eddd4 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -116,7 +116,11 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { case ti == type_info(int): buffer_write_string(buf, "int"); case ti == type_info(uint): buffer_write_string(buf, "uint"); default: - buffer_write_string(buf, if info.signed { give "i" } else { give "u"}); + if info.signed { + buffer_write_string(buf, "i"); + } else { + buffer_write_string(buf, "u"); + } fi := Fmt_Info{buf = buf}; fmt_int(^fi, cast(u64)(8*info.size), false, 'd'); } @@ -392,7 +396,11 @@ fmt_bad_verb :: proc(using fi: ^Fmt_Info, verb: rune) { fmt_bool :: proc(using fi: ^Fmt_Info, b: bool, verb: rune) { match verb { case 't', 'v': - buffer_write_string(buf, if b { give "true" } else { give "false" }); + if b { + buffer_write_string(buf, "true"); + } else { + buffer_write_string(buf, "false"); + } default: fmt_bad_verb(fi, verb); } diff --git a/src/check_expr.c b/src/check_expr.c index 4461a4def..687f4ebea 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1114,11 +1114,12 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { } Type *make_optional_ok_type(gbAllocator a, Type *value) { + bool typed = true; Type *t = make_type_tuple(a); t->Tuple.variables = gb_alloc_array(a, Entity *, 2); t->Tuple.variable_count = 2; t->Tuple.variables[0] = make_entity_field(a, NULL, blank_token, value, false, 0); - t->Tuple.variables[1] = make_entity_field(a, NULL, blank_token, t_bool, false, 1); + t->Tuple.variables[1] = make_entity_field(a, NULL, blank_token, typed ? t_bool : t_untyped_bool, false, 1); return t; } @@ -2113,6 +2114,7 @@ void check_cast(Checker *c, Operand *x, Type *type) { gb_string_free(from_type); gb_string_free(to_type); gb_string_free(expr_str); + x->mode = Addressing_Invalid; return; } @@ -3831,7 +3833,8 @@ int valid_proc_and_score_cmp(void const *a, void const *b) { typedef Array(Operand) ArrayOperand; -void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray rhs, bool allow_ok) { +bool check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, AstNodeArray rhs, bool allow_ok) { + bool optional_ok = false; for_array(i, rhs) { Operand o = {0}; check_multi_expr(c, &o, rhs.e[i]); @@ -3849,6 +3852,8 @@ void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, ok.type = t_bool; array_add(operands, val); array_add(operands, ok); + + optional_ok = true; } else { array_add(operands, o); } @@ -3860,6 +3865,8 @@ void check_unpack_arguments(Checker *c, isize lhs_count, ArrayOperand *operands, } } } + + return optional_ok; } Type *check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) { @@ -4162,40 +4169,6 @@ bool check_set_index_data(Operand *o, Type *type, bool indirection, i64 *max_cou return false; } - -bool check_is_giving(AstNode *node, AstNode **give_expr) { - switch (node->kind) { - case_ast_node(es, ExprStmt, node); - if (es->expr->kind == AstNode_GiveExpr) { - if (give_expr) *give_expr = es->expr; - return true; - } - case_end; - - case_ast_node(ge, GiveExpr, node); - if (give_expr) *give_expr = node; - return true; - case_end; - - case_ast_node(be, BlockExpr, node); - // Iterate backwards - for (isize n = be->stmts.count-1; n >= 0; n--) { - AstNode *stmt = be->stmts.e[n]; - if (stmt->kind == AstNode_EmptyStmt) { - continue; - } - if (stmt->kind == AstNode_ExprStmt && stmt->ExprStmt.expr->kind == AstNode_GiveExpr) { - if (give_expr) *give_expr = stmt->ExprStmt.expr; - return true; - } - } - case_end; - } - - if (give_expr) *give_expr = NULL; - return false; -} - ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -4297,177 +4270,30 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint o->type = type; case_end; - case_ast_node(ge, GiveExpr, node); + case_ast_node(te, TernaryExpr, node); if (c->proc_stack.count == 0) { - error_node(node, "A give expression is only allowed within a procedure"); + error_node(node, "A ternary expression is only allowed within a procedure"); goto error; } - - if (ge->results.count == 0) { - error_node(node, "`give` has no results"); - goto error; - } - - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); - - Array(Operand) operands; - array_init_reserve(&operands, c->tmp_allocator, 2*ge->results.count); - - for_array(i, ge->results) { - AstNode *rhs = ge->results.e[i]; - Operand o = {0}; - check_multi_expr(c, &o, rhs); - if (!is_operand_value(o)) { - error_node(rhs, "Expected a value for a `give`"); - continue; - } - if (o.type->kind != Type_Tuple) { - array_add(&operands, o); - } else { - TypeTuple *tuple = &o.type->Tuple; - for (isize j = 0; j < tuple->variable_count; j++) { - o.type = tuple->variables[j]->type; - array_add(&operands, o); - } - } - } - - if (operands.count == 0) { - error_node(node, "`give` has no value"); - gb_temp_arena_memory_end(tmp); - goto error; - } else if (operands.count == 1) { - Operand operand = operands.e[0]; - Type *th = type_hint != NULL ? type_hint : c->context.type_hint; - if (th != NULL) { - convert_to_typed(c, &operand, th, 0); - } - // IMPORTANT NOTE(bill): This type could be untyped!!! - o->type = default_type(operand.type); - o->mode = Addressing_Value; - } else { - Type *tuple = make_type_tuple(c->allocator); - - Entity **variables = gb_alloc_array(c->allocator, Entity *, operands.count); - isize variable_index = 0; - for_array(i, operands) { - Operand operand = operands.e[i]; - // Type *type = default_type(operand.type); - Type *type = operand.type; - switch (operand.mode) { - case Addressing_Constant: - variables[variable_index++] = make_entity_constant(c->allocator, NULL, empty_token, type, operand.value); - break; - default: - variables[variable_index++] = make_entity_param(c->allocator, NULL, empty_token, type, false, true); - break; - } - } - tuple->Tuple.variables = variables; - tuple->Tuple.variable_count = operands.count; - - o->type = tuple; - o->mode = Addressing_Value; - } - gb_temp_arena_memory_end(tmp); - case_end; - - case_ast_node(be, BlockExpr, node); - if (c->proc_stack.count == 0) { - error_node(node, "A block expression is only allowed within a procedure"); - goto error; - } - - for (isize i = be->stmts.count-1; i >= 0; i--) { - if (be->stmts.e[i]->kind != AstNode_EmptyStmt) { - break; - } - be->stmts.count--; - } - - if (be->stmts.count == 0) { - error_node(node, "Empty block expression"); - goto error; - } - - CheckerContext prev_context = c->context; - c->context.type_hint = type_hint; - check_open_scope(c, node); - check_stmt_list(c, be->stmts, Stmt_GiveAllowed); - check_close_scope(c); - - c->context = prev_context; - - AstNode *give_node = NULL; - if (!check_is_giving(node, &give_node) || give_node == NULL) { - error_node(node, "Missing give statement at end of block expression"); - goto error; - } - - GB_ASSERT(give_node != NULL && give_node->kind == AstNode_GiveExpr); - be->give_node = give_node; - - Type *type = type_of_expr(&c->info, give_node); - if (type == NULL) { - goto error; - } else if (type == t_invalid) { - o->type = t_invalid; - o->mode = Addressing_Invalid; - } else { - o->type = type; - o->mode = Addressing_Value; - } - case_end; - - case_ast_node(ie, IfExpr, node); - if (c->proc_stack.count == 0) { - error_node(node, "An if expression is only allowed within a procedure"); - goto error; - } - - check_open_scope(c, node); - - if (ie->init != NULL) { - check_stmt(c, ie->init, 0); - } - Operand operand = {Addressing_Invalid}; - check_expr(c, &operand, ie->cond); + check_expr(c, &operand, te->cond); if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - error_node(ie->cond, "Non-boolean condition in if expression"); + error_node(te->cond, "Non-boolean condition in if expression"); } Operand x = {Addressing_Invalid}; Operand y = {Addressing_Invalid}; - Type *if_type = NULL; - Type *else_type = NULL; - if (type_hint) { - gb_printf_err("here\n"); - } - check_expr_with_type_hint(c, &x, ie->body, type_hint); - if_type = x.type; + check_expr_with_type_hint(c, &x, te->x, type_hint); - if (ie->else_expr != NULL) { - switch (ie->else_expr->kind) { - case AstNode_IfExpr: - case AstNode_BlockExpr: - check_expr_with_type_hint(c, &y, ie->else_expr, if_type); - else_type = y.type; - break; - default: - error_node(ie->else_expr, "Invalid else expression in if expression"); - break; - } + if (te->y != NULL) { + check_expr_with_type_hint(c, &y, te->y, type_hint); } else { - error_node(ie->else_expr, "An if expression must have an else expression"); - check_close_scope(c); + error_node(node, "A ternary expression must have an else clause"); goto error; } - check_close_scope(c); - - if (if_type == NULL || if_type == t_invalid || - else_type == NULL || else_type == t_invalid) { + if (x.type == NULL || x.type == t_invalid || + y.type == NULL || y.type == t_invalid) { goto error; } @@ -4482,16 +4308,16 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } - if (!are_types_identical(if_type, else_type)) { - gbString its = type_to_string(if_type); - gbString ets = type_to_string(else_type); - error_node(node, "Mismatched types in if expression, %s vs %s", its, ets); + if (!are_types_identical(x.type, y.type)) { + gbString its = type_to_string(x.type); + gbString ets = type_to_string(y.type); + error_node(node, "Mismatched types in ternary expression, %s vs %s", its, ets); gb_string_free(ets); gb_string_free(its); goto error; } - o->type = if_type; + o->type = x.type; o->mode = Addressing_Value; case_end; @@ -5371,12 +5197,6 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = gb_string_appendc(str, "}"); case_end; - case_ast_node(be, BlockExpr, node); - str = gb_string_appendc(str, "block expression"); - case_end; - case_ast_node(ie, IfExpr, node); - str = gb_string_appendc(str, "if expression"); - case_end; case_ast_node(te, TagExpr, node); str = gb_string_appendc(str, "#"); diff --git a/src/check_stmt.c b/src/check_stmt.c index 138e1277e..7315f80ee 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -30,11 +30,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) { case AstNode_ReturnStmt: error_node(n, "Statements after this `return` are never executed"); break; - case AstNode_ExprStmt: - if (n->ExprStmt.expr->kind == AstNode_GiveExpr) { - error_node(n, "A `give` must be the last statement in a block"); - } - break; } } @@ -413,12 +408,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { if (operand.expr->kind == AstNode_CallExpr) { return; } - if (operand.expr->kind == AstNode_GiveExpr) { - if ((flags&Stmt_GiveAllowed) != 0) { - return; - } - error_node(node, "Illegal use of `give`"); - } gbString expr_str = expr_to_string(operand.expr); error_node(node, "Expression is not used: `%s`", expr_str); gb_string_free(expr_str); diff --git a/src/checker.c b/src/checker.c index 9fbf3d36d..0b3c66166 100644 --- a/src/checker.c +++ b/src/checker.c @@ -400,9 +400,7 @@ void check_open_scope(Checker *c, AstNode *node) { node = unparen_expr(node); GB_ASSERT(node->kind == AstNode_Invalid || is_ast_node_stmt(node) || - is_ast_node_type(node) || - node->kind == AstNode_BlockExpr || - node->kind == AstNode_IfExpr ); + is_ast_node_type(node)); Scope *scope = make_scope(c->context.scope, c->allocator); add_scope(c, node, scope); if (node->kind == AstNode_ProcType) { diff --git a/src/ir.c b/src/ir.c index e6bf588de..ad6f65fea 100644 --- a/src/ir.c +++ b/src/ir.c @@ -2851,25 +2851,40 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv return ir_addr_load(proc, ir_build_addr(proc, expr)); case_end; - case_ast_node(be, BlockExpr, expr); - ir_emit_comment(proc, str_lit("BlockExpr")); + case_ast_node(te, TernaryExpr, expr); + ir_emit_comment(proc, str_lit("TernaryExpr")); + + irValueArray edges = {0}; + array_init_reserve(&edges, proc->module->allocator, 2); + + GB_ASSERT(te->y != NULL); + irBlock *then = ir_new_block(proc, NULL, "if.then"); + irBlock *done = ir_new_block(proc, NULL, "if.done"); // NOTE(bill): Append later + irBlock *else_ = ir_new_block(proc, NULL, "if.else"); + + irValue *cond = ir_build_cond(proc, te->cond, then, else_); + ir_start_block(proc, then); + ir_open_scope(proc); - - AstNodeArray stmts = be->stmts; - stmts.count--; - ir_build_stmt_list(proc, stmts); - - AstNode *give_stmt = be->stmts.e[be->stmts.count-1]; - GB_ASSERT(give_stmt->kind == AstNode_ExprStmt); - AstNode *give_expr = give_stmt->ExprStmt.expr; - GB_ASSERT(give_expr->kind == AstNode_GiveExpr); - irValue *value = ir_build_expr(proc, give_expr); - + array_add(&edges, ir_build_expr(proc, te->x)); ir_close_scope(proc, irDeferExit_Default, NULL); - return value; + ir_emit_jump(proc, done); + ir_start_block(proc, else_); + + ir_open_scope(proc); + array_add(&edges, ir_build_expr(proc, te->y)); + ir_close_scope(proc, irDeferExit_Default, NULL); + + ir_emit_jump(proc, done); + ir_start_block(proc, done); + + Type *type = type_of_expr(proc->module->info, expr); + + return ir_emit(proc, ir_make_instr_phi(proc, edges, type)); case_end; +#if 0 case_ast_node(ie, IfExpr, expr); ir_emit_comment(proc, str_lit("IfExpr")); if (ie->init != NULL) { @@ -2908,50 +2923,7 @@ irValue *ir_build_single_expr(irProcedure *proc, AstNode *expr, TypeAndValue *tv return ir_emit(proc, ir_make_instr_phi(proc, edges, type)); case_end; - - case_ast_node(ge, GiveExpr, expr); - ir_emit_comment(proc, str_lit("GiveExpr")); - - irValue *v = NULL; - Type *give_type = type_of_expr(proc->module->info, expr); - GB_ASSERT(give_type != NULL); - if (give_type->kind != Type_Tuple) { - v = ir_emit_conv(proc, ir_build_expr(proc, ge->results.e[0]), give_type); - } else { - TypeTuple *tuple = &give_type->Tuple; - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena); - - irValueArray results; - array_init_reserve(&results, proc->module->tmp_allocator, tuple->variable_count); - - for_array(res_index, ge->results) { - irValue *res = ir_build_expr(proc, ge->results.e[res_index]); - Type *t = ir_type(res); - if (t->kind == Type_Tuple) { - for (isize i = 0; i < t->Tuple.variable_count; i++) { - Entity *e = t->Tuple.variables[i]; - irValue *v = ir_emit_struct_ev(proc, res, i); - array_add(&results, v); - } - } else { - array_add(&results, res); - } - } - - v = ir_add_local_generated(proc, give_type); - for_array(i, results) { - Entity *e = tuple->variables[i]; - irValue *res = ir_emit_conv(proc, results.e[i], e->type); - irValue *field = ir_emit_struct_ep(proc, v, i); - ir_emit_store(proc, field, res); - } - v = ir_emit_load(proc, v); - - gb_temp_arena_memory_end(tmp); - } - - return v; - case_end; +#endif case_ast_node(ce, CastExpr, expr); Type *type = tv->type; diff --git a/src/parser.c b/src/parser.c index 987022068..52343b649 100644 --- a/src/parser.c +++ b/src/parser.c @@ -168,24 +168,9 @@ AST_NODE_KIND(_ExprBegin, "", i32) \ Token open; \ Token close; \ }) \ - AST_NODE_KIND(CastExpr, "cast expression", struct { Token token; AstNode *type, *expr; Token open, close; }) \ - AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ - AST_NODE_KIND(BlockExpr, "block expr", struct { \ - AstNodeArray stmts; \ - Token open, close; \ - AstNode *give_node; \ - }) \ - AST_NODE_KIND(GiveExpr, "give expression", struct { \ - Token token; \ - AstNodeArray results; \ - }) \ - AST_NODE_KIND(IfExpr, "if expression", struct { \ - Token token; \ - AstNode *init; \ - AstNode *cond; \ - AstNode *body; \ - AstNode *else_expr; \ - }) \ + AST_NODE_KIND(CastExpr, "cast expression", struct { Token token; AstNode *type, *expr; Token open, close; }) \ + AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ + AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \ AST_NODE_KIND(IntervalExpr, "interval expression", struct { Token op; AstNode *left, *right; }) \ AST_NODE_KIND(_ExprEnd, "", i32) \ AST_NODE_KIND(_StmtBegin, "", i32) \ @@ -465,9 +450,7 @@ Token ast_node_token(AstNode *node) { case AstNode_CastExpr: return node->CastExpr.token; case AstNode_FieldValue: return node->FieldValue.eq; case AstNode_DerefExpr: return node->DerefExpr.op; - case AstNode_BlockExpr: return node->BlockExpr.open; - case AstNode_GiveExpr: return node->GiveExpr.token; - case AstNode_IfExpr: return node->IfExpr.token; + case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond); case AstNode_IntervalExpr: return ast_node_token(node->IntervalExpr.left); case AstNode_BadStmt: return node->BadStmt.begin; @@ -768,29 +751,11 @@ AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token o return result; } - -AstNode *ast_block_expr(AstFile *f, AstNodeArray stmts, Token open, Token close) { - AstNode *result = make_ast_node(f, AstNode_BlockExpr); - result->BlockExpr.stmts = stmts; - result->BlockExpr.open = open; - result->BlockExpr.close = close; - return result; -} - -AstNode *ast_give_expr(AstFile *f, Token token, AstNodeArray results) { - AstNode *result = make_ast_node(f, AstNode_GiveExpr); - result->GiveExpr.token = token; - result->GiveExpr.results = results; - return result; -} - -AstNode *ast_if_expr(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_expr) { - AstNode *result = make_ast_node(f, AstNode_IfExpr); - result->IfExpr.token = token; - result->IfExpr.init = init; - result->IfExpr.cond = cond; - result->IfExpr.body = body; - result->IfExpr.else_expr = else_expr; +AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) { + AstNode *result = make_ast_node(f, AstNode_TernaryExpr); + result->TernaryExpr.cond = cond; + result->TernaryExpr.x = x; + result->TernaryExpr.y = y; return result; } @@ -1321,13 +1286,13 @@ void expect_semicolon(AstFile *f, AstNode *s) { return; } } else { - switch (s->kind) { - case AstNode_GiveExpr: - if (f->curr_token.kind == Token_CloseBrace) { - return; - } - break; - } + // switch (s->kind) { + // case AstNode_GiveExpr: + // if (f->curr_token.kind == Token_CloseBrace) { + // return; + // } + // break; + // } } syntax_error(prev_token, "Expected `;` after %.*s, got %.*s", LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind])); @@ -1612,72 +1577,72 @@ AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { -AstNode *parse_block_expr(AstFile *f) { - AstNodeArray stmts = {0}; - Token open, close; - open = expect_token(f, Token_OpenBrace); - f->expr_level++; - stmts = parse_stmt_list(f); - f->expr_level--; - close = expect_token(f, Token_CloseBrace); - return ast_block_expr(f, stmts, open, close); -} +// AstNode *parse_block_expr(AstFile *f) { +// AstNodeArray stmts = {0}; +// Token open, close; +// open = expect_token(f, Token_OpenBrace); +// f->expr_level++; +// stmts = parse_stmt_list(f); +// f->expr_level--; +// close = expect_token(f, Token_CloseBrace); +// return ast_block_expr(f, stmts, open, close); +// } -AstNode *parse_if_expr(AstFile *f) { - if (f->curr_proc == NULL) { - syntax_error(f->curr_token, "You cannot use an if expression in the file scope"); - return ast_bad_stmt(f, f->curr_token, f->curr_token); - } +// AstNode *parse_if_expr(AstFile *f) { +// if (f->curr_proc == NULL) { +// syntax_error(f->curr_token, "You cannot use an if expression in the file scope"); +// return ast_bad_stmt(f, f->curr_token, f->curr_token); +// } - Token token = expect_token(f, Token_if); - AstNode *init = NULL; - AstNode *cond = NULL; - AstNode *body = NULL; - AstNode *else_expr = NULL; +// Token token = expect_token(f, Token_if); +// AstNode *init = NULL; +// AstNode *cond = NULL; +// AstNode *body = NULL; +// AstNode *else_expr = NULL; - isize prev_level = f->expr_level; - f->expr_level = -1; +// isize prev_level = f->expr_level; +// f->expr_level = -1; - if (allow_token(f, Token_Semicolon)) { - cond = parse_expr(f, false); - } else { - init = parse_simple_stmt(f, false); - if (allow_token(f, Token_Semicolon)) { - cond = parse_expr(f, false); - } else { - cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); - init = NULL; - } - } +// if (allow_token(f, Token_Semicolon)) { +// cond = parse_expr(f, false); +// } else { +// init = parse_simple_stmt(f, false); +// if (allow_token(f, Token_Semicolon)) { +// cond = parse_expr(f, false); +// } else { +// cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); +// init = NULL; +// } +// } - f->expr_level = prev_level; +// f->expr_level = prev_level; - if (cond == NULL) { - syntax_error(f->curr_token, "Expected condition for if statement"); - } +// if (cond == NULL) { +// syntax_error(f->curr_token, "Expected condition for if statement"); +// } - body = parse_block_expr(f); +// body = parse_block_expr(f); - if (allow_token(f, Token_else)) { - switch (f->curr_token.kind) { - case Token_if: - else_expr = parse_if_expr(f); - break; - case Token_OpenBrace: - else_expr = parse_block_expr(f); - break; - default: - syntax_error(f->curr_token, "Expected if expression block statement"); - else_expr = ast_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); - break; - } - } else { - syntax_error(f->curr_token, "An if expression must have an else clause"); - return ast_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); - } +// if (allow_token(f, Token_else)) { +// switch (f->curr_token.kind) { +// case Token_if: +// else_expr = parse_if_expr(f); +// break; +// case Token_OpenBrace: +// else_expr = parse_block_expr(f); +// break; +// default: +// syntax_error(f->curr_token, "Expected if expression block statement"); +// else_expr = ast_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); +// break; +// } +// } else { +// syntax_error(f->curr_token, "An if expression must have an else clause"); +// return ast_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); +// } - return ast_if_expr(f, token, init, cond, body, else_expr); -} +// return ast_if_expr(f, token, init, cond, body, else_expr); +// } AstNode *parse_operand(AstFile *f, bool lhs) { AstNode *operand = NULL; // Operand @@ -1791,16 +1756,16 @@ AstNode *parse_operand(AstFile *f, bool lhs) { return type; } - case Token_if: - if (!lhs && f->expr_level >= 0) { - return parse_if_expr(f); - } - break; - case Token_OpenBrace: - if (!lhs && f->expr_level >= 0) { - return parse_block_expr(f); - } - break; + // case Token_if: + // if (!lhs && f->expr_level >= 0) { + // return parse_if_expr(f); + // } + // break; + // case Token_OpenBrace: + // if (!lhs && f->expr_level >= 0) { + // return parse_block_expr(f); + // } + // break; default: { AstNode *type = parse_type_or_ident(f); @@ -1984,6 +1949,19 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) { } break; + case Token_Question: + if (!lhs && operand != NULL && f->expr_level >= 0) { + AstNode *cond = operand; + Token token_q = expect_token(f, Token_Question); + AstNode *x = parse_expr(f, false); + Token token_c = expect_token(f, Token_Colon); + AstNode *y = parse_expr(f, false); + operand = ast_ternary_expr(f, cond, x, y); + } else { + loop = false; + } + break; + default: loop = false; break; @@ -2925,27 +2903,27 @@ AstNode *parse_return_stmt(AstFile *f) { } -AstNode *parse_give_stmt(AstFile *f) { - if (f->curr_proc == NULL) { - syntax_error(f->curr_token, "You cannot use a give statement in the file scope"); - return ast_bad_stmt(f, f->curr_token, f->curr_token); - } - if (f->expr_level == 0) { - syntax_error(f->curr_token, "A give statement must be used within an expression"); - return ast_bad_stmt(f, f->curr_token, f->curr_token); - } +// AstNode *parse_give_stmt(AstFile *f) { +// if (f->curr_proc == NULL) { +// syntax_error(f->curr_token, "You cannot use a give statement in the file scope"); +// return ast_bad_stmt(f, f->curr_token, f->curr_token); +// } +// if (f->expr_level == 0) { +// syntax_error(f->curr_token, "A give statement must be used within an expression"); +// return ast_bad_stmt(f, f->curr_token, f->curr_token); +// } - Token token = expect_token(f, Token_give); - AstNodeArray results; - if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) { - results = parse_rhs_expr_list(f); - } else { - results = make_ast_node_array(f); - } - AstNode *ge = ast_give_expr(f, token, results); - expect_semicolon(f, ge); - return ast_expr_stmt(f, ge); -} +// Token token = expect_token(f, Token_give); +// AstNodeArray results; +// if (f->curr_token.kind != Token_Semicolon && f->curr_token.kind != Token_CloseBrace) { +// results = parse_rhs_expr_list(f); +// } else { +// results = make_ast_node_array(f); +// } +// AstNode *ge = ast_give_expr(f, token, results); +// expect_semicolon(f, ge); +// return ast_expr_stmt(f, ge); +// } AstNode *parse_for_stmt(AstFile *f) { if (f->curr_proc == NULL) { @@ -3227,7 +3205,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_defer: return parse_defer_stmt(f); case Token_asm: return parse_asm_stmt(f); case Token_return: return parse_return_stmt(f); - case Token_give: return parse_give_stmt(f); + // case Token_give: return parse_give_stmt(f); case Token_break: case Token_continue: