diff --git a/examples/demo.odin b/examples/demo.odin index 81362301e..a01e96aa7 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -9,20 +9,32 @@ main :: proc() { name: string } - Frog :: type struct { + Amp :: type struct { using entity: Entity jump_height: f32 } - - f := Frog{} - f.name = "ribbit" - - print_name :: proc(using e: Entity) { - print_string(name); nl() + Frog :: type struct { + using amp: Amp + volume: f64 } - print_name(f.entity) - print_name(f) + f := Frog{}; + f.name = "ribbit"; + f.jump_height = 1337; + + e := ^f.entity; + parent := e down_cast ^Frog; + + print_name :: proc(using e: Entity, v: Vec3) { + print_string(name); nl() + print_int(v.x as int); nl() + } + + print_f32(f.jump_height); nl() + print_f32(parent.jump_height); nl() + + print_name(f.entity, Vec3{1, 2, 3}) + print_name(parent.entity, Vec3{3, 2, 1}) } diff --git a/examples/opengl.odin b/examples/opengl.odin index 79668dd00..ce540af8e 100644 --- a/examples/opengl.odin +++ b/examples/opengl.odin @@ -27,23 +27,23 @@ GL_TEXTURE_MIN_FILTER :: 0x2801 GL_TEXTURE_WRAP_S :: 0x2802 GL_TEXTURE_WRAP_T :: 0x2803 -glClear :: proc(mask: u32) #foreign -glClearColor :: proc(r, g, b, a: f32) #foreign -glBegin :: proc(mode: i32) #foreign -glEnd :: proc() #foreign -glColor3f :: proc(r, g, b: f32) #foreign -glColor4f :: proc(r, g, b, a: f32) #foreign -glVertex2f :: proc(x, y: f32) #foreign -glVertex3f :: proc(x, y, z: f32) #foreign -glTexCoord2f :: proc(u, v: f32) #foreign -glLoadIdentity :: proc() #foreign +glClear :: proc(mask: u32) #foreign +glClearColor :: proc(r, g, b, a: f32) #foreign +glBegin :: proc(mode: i32) #foreign +glEnd :: proc() #foreign +glColor3f :: proc(r, g, b: f32) #foreign +glColor4f :: proc(r, g, b, a: f32) #foreign +glVertex2f :: proc(x, y: f32) #foreign +glVertex3f :: proc(x, y, z: f32) #foreign +glTexCoord2f :: proc(u, v: f32) #foreign +glLoadIdentity :: proc() #foreign glOrtho :: proc(left, right, bottom, top, near, far: f64) #foreign -glBlendFunc :: proc(sfactor, dfactor: i32) #foreign -glEnable :: proc(cap: i32) #foreign -glDisable :: proc(cap: i32) #foreign -glGenTextures :: proc(count: i32, result: ^u32) #foreign -glTexParameteri :: proc(target, pname, param: i32) #foreign -glTexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign -glBindTexture :: proc(target: i32, texture: u32) #foreign +glBlendFunc :: proc(sfactor, dfactor: i32) #foreign +glEnable :: proc(cap: i32) #foreign +glDisable :: proc(cap: i32) #foreign +glGenTextures :: proc(count: i32, result: ^u32) #foreign +glTexParameteri :: proc(target, pname, param: i32) #foreign +glTexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign +glBindTexture :: proc(target: i32, texture: u32) #foreign glTexImage2D :: proc(target, level, internal_format, width, height, border, format, _type: i32, pixels: rawptr) #foreign diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index 5c00de8dd..0b9c056ef 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -1240,6 +1240,35 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) { return false; } +String check_down_cast_name(Type *dst_, Type *src_) { + String result = {}; + Type *dst = type_deref(dst_); + Type *src = type_deref(src_); + Type *dst_s = get_base_type(dst); + GB_ASSERT(dst_s->kind == Type_Struct || dst_s->kind == Type_Union); + // HACK(bill): struct/union variable overlay from unsafe tagged union + for (isize i = 0; i < dst_s->Struct.field_count; i++) { + Entity *f = dst_s->Struct.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); + if (f->Variable.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; +} + void check_binary_expr(Checker *c, Operand *x, AstNode *node) { GB_ASSERT(node->kind == AstNode_BinaryExpr); Operand y_ = {}, *y = &y_; @@ -1328,6 +1357,69 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) { x->type = type; + return; + } else if (be->op.kind == Token_down_cast) { + check_expr(c, x, be->left); + Type *type = check_type(c, be->right); + if (x->mode == Addressing_Invalid) + return; + + if (x->mode == Addressing_Constant) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Cannot `down_cast` a constant expression: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (is_type_untyped(x->type)) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Cannot `down_cast` an untyped expression: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(is_type_pointer(x->type) && is_type_pointer(type))) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointers: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + Type *src = type_deref(x->type); + Type *dst = type_deref(type); + Type *bsrc = get_base_type(src); + Type *bdst = get_base_type(dst); + + if (!(bsrc->kind == Type_Struct || bsrc->kind == Type_Union)) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer from structs or unions: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + if (!(bdst->kind == Type_Struct || bdst->kind == Type_Union)) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Can only `down_cast` pointer to structs or unions: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + String param_name = check_down_cast_name(dst, src); + if (param_name.len == 0) { + gbString expr_str = expr_to_string(node); + defer (gb_string_free(expr_str)); + error(&c->error_collector, ast_node_token(node), "Illegal `down_cast`: `%s`", expr_str); + x->mode = Addressing_Invalid; + return; + } + + x->mode = Addressing_Value; + x->type = type; return; } @@ -1637,125 +1729,6 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu return true; } -struct Selection { - Entity *entity; - gbArray(isize) index; - b32 indirect; // Set if there was a pointer deref anywhere down the line -}; -Selection empty_selection = {}; - -Selection make_selection(Entity *entity, gbArray(isize) index, b32 indirect) { - Selection s = {entity, index, indirect}; - return s; -} - -void selection_add_index(Selection *s, isize index) { - if (s->index == NULL) { - gb_array_init(s->index, gb_heap_allocator()); - } - gb_array_append(s->index, index); -} - -Selection lookup_field(Type *type_, String field_name, AddressingMode mode, Selection sel = empty_selection) { - GB_ASSERT(type_ != NULL); - - if (are_strings_equal(field_name, make_string("_"))) { - return empty_selection; - } - - Type *type = type_deref(type_); - b32 is_ptr = type != type_; - type = get_base_type(type); - - switch (type->kind) { - case Type_Struct: - if (mode == Addressing_Type) { - for (isize i = 0; i < type->Struct.other_field_count; i++) { - Entity *f = type->Struct.other_fields[i]; - GB_ASSERT(f->kind != Entity_Variable); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } - } - } else { - for (isize i = 0; i < type->Struct.field_count; i++) { - Entity *f = type->Struct.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } - - if (f->Variable.anonymous) { - isize prev_count = 0; - if (sel.index != NULL) { - prev_count = gb_array_count(sel.index); - } - selection_add_index(&sel, i); // HACK(bill): Leaky memory - - sel = lookup_field(f->type, field_name, mode, sel); - - if (sel.entity != NULL) { - // gb_printf("%.*s, %.*s, %.*s\n", LIT(field_name), LIT(str), LIT(sel.entity->token.string)); - if (is_type_pointer(f->type)) - sel.indirect = true; - return sel; - } - gb_array_count(sel.index) = prev_count; - } - } - } - break; - - case Type_Union: - for (isize i = 0; i < type->Union.field_count; i++) { - Entity *f = type->Union.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - selection_add_index(&sel, i); - sel.entity = f; - return sel; - } - } - for (isize i = 0; i < type->Union.field_count; i++) { - Entity *f = type->Union.fields[i]; - GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); - String str = f->token.string; - if (f->Variable.anonymous) { - selection_add_index(&sel, i); // HACK(bill): Leaky memory - sel = lookup_field(f->type, field_name, mode, sel); - if (sel.entity != NULL && is_type_pointer(f->type)) { - sel.indirect = true; - } - return sel; - } - } - break; - - case Type_Enum: - if (mode == Addressing_Type) { - for (isize i = 0; i < type->Enum.field_count; i++) { - Entity *f = type->Enum.fields[i]; - GB_ASSERT(f->kind == Entity_Constant); - String str = f->token.string; - if (are_strings_equal(field_name, str)) { - // Enums are constant expression - return make_selection(f, NULL, i); - } - } - } - break; - } - - return sel; -} - Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { GB_ASSERT(node->kind == AstNode_SelectorExpr); @@ -1763,7 +1736,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { AstNode *op_expr = se->expr; AstNode *selector = se->selector; if (selector) { - Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode).entity; + Entity *entity = lookup_field(operand->type, selector->Ident.token.string, operand->mode == Addressing_Type).entity; if (entity == NULL) { gbString op_str = expr_to_string(op_expr); gbString type_str = type_to_string(operand->type); @@ -1967,7 +1940,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) ast_node(arg, Ident, field_arg); - Selection sel = lookup_field(type, arg->token.string, operand->mode); + Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(ce->arg_list), @@ -2004,7 +1977,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) ast_node(i, Ident, s->selector); - Selection sel = lookup_field(type, i->token.string, operand->mode); + Selection sel = lookup_field(type, i->token.string, operand->mode == Addressing_Type); if (sel.entity == NULL) { gbString type_str = type_to_string(type); error(&c->error_collector, ast_node_token(arg), @@ -2630,7 +2603,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } String name = kv->field->Ident.token.string; - Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode); + Selection sel = lookup_field(type, kv->field->Ident.token.string, o->mode == Addressing_Type); if (sel.entity == NULL) { error(&c->error_collector, ast_node_token(elem), "Unknown field `%.*s` in structure literal", LIT(name)); diff --git a/src/checker/type.cpp b/src/checker/type.cpp index 2bec64615..cd11f3365 100644 --- a/src/checker/type.cpp +++ b/src/checker/type.cpp @@ -247,6 +247,8 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun Type *type_deref(Type *t) { if (t != NULL) { Type *bt = get_base_type(t); + if (bt == NULL) + return NULL; if (bt != NULL && bt->kind == Type_Pointer) return bt->Pointer.elem; } @@ -595,6 +597,102 @@ gb_global i64 basic_type_sizes[] = { +struct Selection { + Entity *entity; + gbArray(isize) index; + b32 indirect; // Set if there was a pointer deref anywhere down the line +}; +Selection empty_selection = {}; + +Selection make_selection(Entity *entity, gbArray(isize) index, b32 indirect) { + Selection s = {entity, index, indirect}; + return s; +} + +void selection_add_index(Selection *s, isize index) { + if (s->index == NULL) { + gb_array_init(s->index, gb_heap_allocator()); + } + gb_array_append(s->index, index); +} + +Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) { + GB_ASSERT(type_ != NULL); + + if (are_strings_equal(field_name, make_string("_"))) { + return empty_selection; + } + + Type *type = type_deref(type_); + b32 is_ptr = type != type_; + type = get_base_type(type); + + switch (type->kind) { + // HACK(bill): struct/union variable overlay from unsafe tagged union + case Type_Struct: + case Type_Union: + if (is_type) { + for (isize i = 0; i < type->Struct.other_field_count; i++) { + Entity *f = type->Struct.other_fields[i]; + GB_ASSERT(f->kind != Entity_Variable); + String str = f->token.string; + if (are_strings_equal(field_name, str)) { + selection_add_index(&sel, i); + sel.entity = f; + return sel; + } + } + } else { + for (isize i = 0; i < type->Struct.field_count; i++) { + Entity *f = type->Struct.fields[i]; + GB_ASSERT(f->kind == Entity_Variable && f->Variable.is_field); + String str = f->token.string; + if (are_strings_equal(field_name, str)) { + selection_add_index(&sel, i); + sel.entity = f; + return sel; + } + + if (f->Variable.anonymous) { + isize prev_count = 0; + if (sel.index != NULL) { + prev_count = gb_array_count(sel.index); + } + selection_add_index(&sel, i); // HACK(bill): Leaky memory + + sel = lookup_field(f->type, field_name, is_type, sel); + + if (sel.entity != NULL) { + if (is_type_pointer(f->type)) + sel.indirect = true; + return sel; + } + gb_array_count(sel.index) = prev_count; + } + } + } + break; + + case Type_Enum: + if (is_type) { + for (isize i = 0; i < type->Enum.field_count; i++) { + Entity *f = type->Enum.fields[i]; + GB_ASSERT(f->kind == Entity_Constant); + String str = f->token.string; + if (are_strings_equal(field_name, str)) { + // Enums are constant expression + return make_selection(f, NULL, i); + } + } + } + break; + } + + return sel; +} + + + i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t); i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t); i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index); @@ -763,6 +861,24 @@ i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) return 0; } +gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator()); + +i64 type_offset_of_from_selection(BaseTypeSizes s, gbAllocator allocator, Type *t, Selection sel) { + i64 offset = 0; + for (isize i = 0; i < gb_array_count(sel.index); i++) { + isize index = sel.index[i]; + t = get_base_type(t); + if (t->kind == Type_Struct) { + type_set_offsets(s, allocator, t); + GB_ASSERT(gb_is_between(index, 0, t->Struct.field_count-1)); + offset += t->Struct.offsets[index]; + t = t->Struct.fields[index]->type; + } + } + return offset; +} + + gbString write_type_to_string(gbString str, Type *type) { if (type == NULL) { @@ -866,7 +982,7 @@ gbString write_type_to_string(gbString str, Type *type) { } -gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator()) { +gbString type_to_string(Type *type, gbAllocator a) { gbString str = gb_string_make(a, ""); return write_type_to_string(str, type); } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 38a4647f1..3f02b16aa 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -1408,7 +1408,7 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t, b32 is_arg if (field_name.len > 0) { // NOTE(bill): It can be casted - Selection sel = lookup_field(sb, field_name, Addressing_Variable); + Selection sel = lookup_field(sb, field_name, false); if (sel.entity != NULL) { if (src_is_ptr) { value = ssa_emit_load(proc, value); @@ -1517,6 +1517,24 @@ ssaValue *ssa_emit_transmute(ssaProcedure *proc, ssaValue *value, Type *t) { return NULL; } +ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) { + GB_ASSERT(is_type_pointer(ssa_type(value))); + gbAllocator allocator = proc->module->allocator; + + // String field_name = check_down_cast_name(t, ssa_type(value)); + String field_name = check_down_cast_name(t, type_deref(ssa_type(value))); + GB_ASSERT(field_name.len > 0); + Selection sel = lookup_field(t, field_name, false); + Type *t_u8_ptr = make_type_pointer(allocator, t_u8); + ssaValue *bytes = ssa_emit_conv(proc, value, t_u8_ptr); + + // IMPORTANT TODO(bill): THIS ONLY DOES ONE LAY DEEP!!! FUCKING HELL THIS IS NOT WHAT I SIGNED UP FOR! + + i64 offset_ = type_offset_of_from_selection(proc->module->sizes, allocator, type_deref(t), sel); + ssaValue *offset = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(-offset_)); + ssaValue *head = ssa_emit_ptr_offset(proc, bytes, offset); + return ssa_emit_conv(proc, head, t); +} @@ -1619,6 +1637,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue case Token_transmute: return ssa_emit_transmute(proc, ssa_build_expr(proc, be->left), tv->type); + case Token_down_cast: + return ssa_emit_down_cast(proc, ssa_build_expr(proc, be->left), tv->type); + default: GB_PANIC("Invalid binary expression"); break; @@ -1705,7 +1726,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue if (elem->kind == AstNode_FieldValue) { ast_node(kv, FieldValue, elem); - Selection sel = lookup_field(base_type, kv->field->Ident.token.string, Addressing_Value); + Selection sel = lookup_field(base_type, kv->field->Ident.token.string, false); field_index = sel.index[0]; field_expr = ssa_build_expr(proc, kv->value); } else { @@ -2115,7 +2136,7 @@ ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) { p = ssa_add_using_variable(proc, parent); } - Selection sel = lookup_field(parent->type, name, Addressing_Variable); + Selection sel = lookup_field(parent->type, name, false); GB_ASSERT(sel.entity != NULL); ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent)); ssaValue *v = NULL; @@ -2160,7 +2181,7 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) { case_ast_node(se, SelectorExpr, expr); Type *type = get_base_type(type_of_expr(proc->module->info, se->expr)); - Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string, Addressing_Value); + Selection sel = lookup_field(type, unparen_expr(se->selector)->Ident.token.string, false); GB_ASSERT(sel.entity != NULL); ssaValue *e = ssa_build_addr(proc, se->expr).addr; diff --git a/src/parser.cpp b/src/parser.cpp index f8ae53dad..2deac0b2d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1452,24 +1452,28 @@ AstNode *parse_binary_expr(AstFile *f, b32 lhs, i32 prec_in) { lhs = false; } - - if (op.kind == Token_DoublePrime) { + switch (op.kind) { + case Token_DoublePrime: { AstNode *proc = parse_identifier(f); AstNode *right = parse_binary_expr(f, false, prec+1); expression->next = right; expression = make_call_expr(f, proc, expression, 2, op, ast_node_token(right)); continue; - } + } break; - if (op.kind == Token_as || op.kind == Token_transmute) { + case Token_as: + case Token_transmute: + case Token_down_cast: right = parse_type(f); - } else { + break; + + default: right = parse_binary_expr(f, false, prec+1); if (!right) { ast_file_err(f, op, "Expected expression on the right hand side of the binary operator"); } + break; } - expression = make_binary_expr(f, op, expression, right); } } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 5cf787075..7581771e4 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -30,6 +30,7 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \ \ TOKEN_KIND(Token_as, "as"), \ TOKEN_KIND(Token_transmute, "transmute"), \ + TOKEN_KIND(Token_down_cast, "down_cast"), \ \ TOKEN_KIND(Token_Prime, "'"), \ TOKEN_KIND(Token_DoublePrime, "''"), \ @@ -207,6 +208,7 @@ i32 token_precedence(Token t) { return 6; case Token_as: case Token_transmute: + case Token_down_cast: return 7; } @@ -629,6 +631,8 @@ Token tokenizer_get_token(Tokenizer *t) { token.kind = Token_as; } else if (are_strings_equal(token.string, token_strings[Token_transmute])) { token.kind = Token_transmute; + } else if (are_strings_equal(token.string, token_strings[Token_down_cast])) { + token.kind = Token_down_cast; } else { for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) { if (are_strings_equal(token.string, token_strings[k])) {