diff --git a/code/demo.odin b/code/demo.odin index 8eeaeb357..6e0ae84fa 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,430 +1,29 @@ -import ( - "fmt.odin"; - "atomics.odin"; - "bits.odin"; - "decimal.odin"; - "hash.odin"; - "math.odin"; - "mem.odin"; - "opengl.odin"; - "os.odin"; - "raw.odin"; - "strconv.odin"; - "strings.odin"; - "sync.odin"; - "sort.odin"; - "types.odin"; - "utf8.odin"; - "utf16.odin"; -/* -*/ -) +import "fmt.odin"; - -general_stuff :: proc() { - // Complex numbers - a := 3 + 4i; - b: complex64 = 3 + 4i; - c: complex128 = 3 + 4i; - d := complex(2, 3); - - e := a / conj(a); - fmt.println("(3+4i)/(3-4i) =", e); - fmt.println(real(e), "+", imag(e), "i"); - - - // C-style variadic procedures - foreign __llvm_core { - // The variadic part allows for extra type checking too which C does not provide - c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---; - } - str := "%d\n\x00"; - // c_printf(&str[0], i32(789456123)); - - - Foo :: struct { - x: int; - y: f32; - z: string; - } - foo := Foo{123, 0.513, "A string"}; - x, y, z := expand_to_tuple(foo); - fmt.println(x, y, z); - compile_assert(type_of(x) == int); - compile_assert(type_of(y) == f32); - compile_assert(type_of(z) == string); - - - // By default, all variables are zeroed - // This can be overridden with the "uninitialized value" - // This is similar to `nil` but applied to everything - undef_int: int = ---; - - - // Context system is now implemented using Implicit Parameter Passing (IPP) - // The previous implementation was Thread Local Storage (TLS) - // IPP has the advantage that it works on systems without TLS and that you can - // link the context to the stack frame and thus look at previous contexts - // - // It does mean that a pointer is implicitly passed procedures with the default - // Odin calling convention (#cc_odin) - // This can be overridden with something like #cc_contextless or #cc_c if performance - // is worried about - -} - -foreign_blocks :: proc() { - // See sys/windows.odin -} - -default_arguments :: proc() { - hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b); - fmt.println("\nTesting default arguments:"); - hello(1, 2); - hello(1); - hello(); -} - -named_arguments :: proc() { - Colour :: enum { - Red, - Orange, - Yellow, - Green, - Blue, - Octarine, +Vector :: struct(N: int, T: type) { + using _: raw_union { + using e: [N]T; + when 0 < N && N <= 4 { + using v: struct { + when N >= 1 do x: T; + when N >= 2 do y: T; + when N >= 3 do z: T; + when N >= 4 do w: T; + }; + } }; - using Colour; - - make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) { - fmt.println(); - fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase); - } - - make_character("Frank", "¡Ay, caramba!", Blue, Green); - - - // As the procedures have more and more parameters, it is very easy - // to get many of the arguments in the wrong order especialy if the - // types are the same - make_character("¡Ay, caramba!", "Frank", Green, Blue); - - // Named arguments help to disambiguate this problem - make_character(catch_phrase = "¡Ay, caramba!", name = "Frank", - least_favourite_colour = Green, favourite_colour = Blue); - - - // The named arguments can be specifed in any order. - make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!", - least_favourite_colour = Green, name = "Dennis"); - - - // NOTE: You cannot mix named arguments with normal values - /* - make_character("Dennis", - favourite_colour = Octarine, catch_phrase = "U wot m8!", - least_favourite_colour = Green); - */ - - - // Named arguments can also aid with default arguments - numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14, - d := "The Best String!", e := false, f := 10.3/3.1, g := false) { - g_str := g ? "true" : "false"; - fmt.printf("How many?! %s: %v\n", s, g_str); - } - - numerous_things("First"); - numerous_things(s = "Second", g = true); - - - // Default values can be placed anywhere, not just at the end like in other languages - weird :: proc(pre: string, mid: int = 0, post: string) { - fmt.println(pre, mid, post); - } - - weird("How many things", 42, "huh?"); - weird(pre = "Prefix", post = "Pat"); - } - -default_return_values :: proc() { - foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") { - match x { - case 0: return; - case 1: return "Goodbye"; - case 2: return "Goodbye", "cruel world..."; - case 3: return second = "cruel world...", first = "Goodbye"; - } - - return second = "my old friend."; - } - - fmt.printf("%s %s\n", foo(0)); - fmt.printf("%s %s\n", foo(1)); - fmt.printf("%s %s\n", foo(2)); - fmt.printf("%s %s\n", foo(3)); - fmt.printf("%s %s\n", foo(4)); - fmt.println(); - - - // A more "real" example - Error :: enum { - None, - WhyTheNumberThree, - TenIsTooBig, - }; - - Entity :: struct { - name: string; - id: u32; - } - - some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) { - match { - case input == 3: return err = Error.WhyTheNumberThree; - case input >= 10: return err = Error.TenIsTooBig; - } - - e := new(Entity); - e.id = u32(input); - - return result = e; - } -} - -call_location :: proc() { - amazing :: proc(n: int, using loc := #caller_location) { - fmt.printf("%s(%d:%d) just asked to do something amazing.\n", - fully_pathed_filename, line, column); - fmt.printf("Normal -> %d\n", n); - fmt.printf("Amazing -> %d\n", n+1); - fmt.println(); - } - - loc := #location(main); - fmt.println("`main` is located at", loc); - - fmt.println("This line is located at", #location()); - fmt.println(); - - amazing(3); - amazing(4, #location(call_location)); - - // See _preload.odin for the implementations of `assert` and `panic` - -} - - -explicit_parametric_polymorphic_procedures :: proc() { - // This is how `new` is actually implemented, see _preload.odin - alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T)); - - int_ptr := alloc_type(int); - defer free(int_ptr); - int_ptr^ = 137; - fmt.println(int_ptr, int_ptr^); - - // Named arguments work too! - another_ptr := alloc_type(T = f32); - defer free(another_ptr); - - - add :: proc(T: type, args: ...T) -> T { - res: T; - for arg in args do res += arg; - return res; - } - - fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6)); - - swap :: proc(T: type, a, b: ^T) { - tmp := a^; - a^ = b^; - b^ = tmp; - } - - a, b: int = 3, 4; - fmt.println("Pre-swap:", a, b); - swap(int, &a, &b); - fmt.println("Post-swap:", a, b); - a, b = b, a; // Or use this syntax for this silly example case - - - Vector2 :: struct {x, y: f32;}; - { - // A more complicated example using subtyping - // Something like this could be used in a game - - Entity :: struct { - using position: Vector2; - flags: u64; - id: u64; - derived: any; - } - - Rock :: struct { - using entity: Entity; - heavy: bool; - } - Door :: struct { - using entity: Entity; - open: bool; - } - Monster :: struct { - using entity: Entity; - is_robot: bool; - is_zombie: bool; - } - - new_entity :: proc(T: type, x, y: f32) -> ^T { - result := new(T); - result.derived = result^; - result.x = x; - result.y = y; - - return result; - } - - entities: [dynamic]^Entity; - - rock := new_entity(Rock, 3, 5); - - // Named arguments work too! - door := new_entity(T = Door, x = 3, y = 6); - - // And named arguments can be any order - monster := new_entity( - y = 1, - x = 2, - T = Monster, - ); - - append(&entities, rock, door, monster); - - fmt.println("Subtyping"); - for entity in entities { - match e in entity.derived { - case Rock: fmt.println("Rock", e.x, e.y); - case Door: fmt.println("Door", e.x, e.y); - case Monster: fmt.println("Monster", e.x, e.y); - } - } - } - { - Entity :: struct { - using position: Vector2; - flags: u64; - id: u64; - variant: union { Rock, Door, Monster }; - } - - Rock :: struct { - using entity: ^Entity; - heavy: bool; - } - Door :: struct { - using entity: ^Entity; - open: bool; - } - Monster :: struct { - using entity: ^Entity; - is_robot: bool; - is_zombie: bool; - } - - new_entity :: proc(T: type, x, y: f32) -> ^T { - result := new(Entity); - result.variant = T{entity = result}; - result.x = x; - result.y = y; - - return cast(^T)&result.variant; - } - - entities: [dynamic]^Entity; - - rock := new_entity(Rock, 3, 5); - - // Named arguments work too! - door := new_entity(T = Door, x = 3, y = 6); - - // And named arguments can be any order - monster := new_entity( - y = 1, - x = 2, - T = Monster, - ); - - append(&entities, rock, door, monster); - - fmt.println("Union"); - for entity in entities { - match e in entity.variant { - case Rock: fmt.println("Rock", e.x, e.y); - case Door: fmt.println("Door", e.x, e.y); - case Monster: fmt.println("Monster", e.x, e.y); - } - } - } -} - - -implicit_polymorphic_assignment :: proc() { - yep :: proc(p: proc(x: int)) { - p(123); - } - - frank :: proc(x: $T) do fmt.println("frank ->", x); - tim :: proc(x, y: $T) do fmt.println("tim ->", x, y); - yep(frank); - // yep(tim); -} - - - +Vector3 :: Vector(3, f32); main :: proc() { -/* - foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y); - foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y); - foo :: proc(x: type) do fmt.println("#3", type_info(x)); - - f :: foo; - - f(y = 3785.1546, x = 123); - f(x = int, y = 897.513); - f(x = f32); - - general_stuff(); - foreign_blocks(); - default_arguments(); - named_arguments(); - default_return_values(); - call_location(); - explicit_parametric_polymorphic_procedures(); - implicit_polymorphic_assignment(); - - - // Command line argument(s)! - // -opt=0,1,2,3 -*/ -/* - program := "+ + * - /"; - accumulator := 0; - - for token in program { - match token { - case '+': accumulator += 1; - case '-': accumulator -= 1; - case '*': accumulator *= 2; - case '/': accumulator /= 2; - case: // Ignore everything else - } - } - - fmt.printf("The program \"%s\" calculates the value %d\n", - program, accumulator); -*/ + v: Vector3; + v[0] = 1; + v[1] = 4; + v[2] = 9; + fmt.println(v.e); + v.x = 4; + v.y = 9; + v.z = 16; + fmt.println(v.v); } diff --git a/code/demo_backup.odin b/code/demo_backup.odin new file mode 100644 index 000000000..8eeaeb357 --- /dev/null +++ b/code/demo_backup.odin @@ -0,0 +1,430 @@ +import ( + "fmt.odin"; + "atomics.odin"; + "bits.odin"; + "decimal.odin"; + "hash.odin"; + "math.odin"; + "mem.odin"; + "opengl.odin"; + "os.odin"; + "raw.odin"; + "strconv.odin"; + "strings.odin"; + "sync.odin"; + "sort.odin"; + "types.odin"; + "utf8.odin"; + "utf16.odin"; +/* +*/ +) + + +general_stuff :: proc() { + // Complex numbers + a := 3 + 4i; + b: complex64 = 3 + 4i; + c: complex128 = 3 + 4i; + d := complex(2, 3); + + e := a / conj(a); + fmt.println("(3+4i)/(3-4i) =", e); + fmt.println(real(e), "+", imag(e), "i"); + + + // C-style variadic procedures + foreign __llvm_core { + // The variadic part allows for extra type checking too which C does not provide + c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---; + } + str := "%d\n\x00"; + // c_printf(&str[0], i32(789456123)); + + + Foo :: struct { + x: int; + y: f32; + z: string; + } + foo := Foo{123, 0.513, "A string"}; + x, y, z := expand_to_tuple(foo); + fmt.println(x, y, z); + compile_assert(type_of(x) == int); + compile_assert(type_of(y) == f32); + compile_assert(type_of(z) == string); + + + // By default, all variables are zeroed + // This can be overridden with the "uninitialized value" + // This is similar to `nil` but applied to everything + undef_int: int = ---; + + + // Context system is now implemented using Implicit Parameter Passing (IPP) + // The previous implementation was Thread Local Storage (TLS) + // IPP has the advantage that it works on systems without TLS and that you can + // link the context to the stack frame and thus look at previous contexts + // + // It does mean that a pointer is implicitly passed procedures with the default + // Odin calling convention (#cc_odin) + // This can be overridden with something like #cc_contextless or #cc_c if performance + // is worried about + +} + +foreign_blocks :: proc() { + // See sys/windows.odin +} + +default_arguments :: proc() { + hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b); + fmt.println("\nTesting default arguments:"); + hello(1, 2); + hello(1); + hello(); +} + +named_arguments :: proc() { + Colour :: enum { + Red, + Orange, + Yellow, + Green, + Blue, + Octarine, + }; + using Colour; + + make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) { + fmt.println(); + fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase); + } + + make_character("Frank", "¡Ay, caramba!", Blue, Green); + + + // As the procedures have more and more parameters, it is very easy + // to get many of the arguments in the wrong order especialy if the + // types are the same + make_character("¡Ay, caramba!", "Frank", Green, Blue); + + // Named arguments help to disambiguate this problem + make_character(catch_phrase = "¡Ay, caramba!", name = "Frank", + least_favourite_colour = Green, favourite_colour = Blue); + + + // The named arguments can be specifed in any order. + make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!", + least_favourite_colour = Green, name = "Dennis"); + + + // NOTE: You cannot mix named arguments with normal values + /* + make_character("Dennis", + favourite_colour = Octarine, catch_phrase = "U wot m8!", + least_favourite_colour = Green); + */ + + + // Named arguments can also aid with default arguments + numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14, + d := "The Best String!", e := false, f := 10.3/3.1, g := false) { + g_str := g ? "true" : "false"; + fmt.printf("How many?! %s: %v\n", s, g_str); + } + + numerous_things("First"); + numerous_things(s = "Second", g = true); + + + // Default values can be placed anywhere, not just at the end like in other languages + weird :: proc(pre: string, mid: int = 0, post: string) { + fmt.println(pre, mid, post); + } + + weird("How many things", 42, "huh?"); + weird(pre = "Prefix", post = "Pat"); + +} + + +default_return_values :: proc() { + foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") { + match x { + case 0: return; + case 1: return "Goodbye"; + case 2: return "Goodbye", "cruel world..."; + case 3: return second = "cruel world...", first = "Goodbye"; + } + + return second = "my old friend."; + } + + fmt.printf("%s %s\n", foo(0)); + fmt.printf("%s %s\n", foo(1)); + fmt.printf("%s %s\n", foo(2)); + fmt.printf("%s %s\n", foo(3)); + fmt.printf("%s %s\n", foo(4)); + fmt.println(); + + + // A more "real" example + Error :: enum { + None, + WhyTheNumberThree, + TenIsTooBig, + }; + + Entity :: struct { + name: string; + id: u32; + } + + some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) { + match { + case input == 3: return err = Error.WhyTheNumberThree; + case input >= 10: return err = Error.TenIsTooBig; + } + + e := new(Entity); + e.id = u32(input); + + return result = e; + } +} + +call_location :: proc() { + amazing :: proc(n: int, using loc := #caller_location) { + fmt.printf("%s(%d:%d) just asked to do something amazing.\n", + fully_pathed_filename, line, column); + fmt.printf("Normal -> %d\n", n); + fmt.printf("Amazing -> %d\n", n+1); + fmt.println(); + } + + loc := #location(main); + fmt.println("`main` is located at", loc); + + fmt.println("This line is located at", #location()); + fmt.println(); + + amazing(3); + amazing(4, #location(call_location)); + + // See _preload.odin for the implementations of `assert` and `panic` + +} + + +explicit_parametric_polymorphic_procedures :: proc() { + // This is how `new` is actually implemented, see _preload.odin + alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T)); + + int_ptr := alloc_type(int); + defer free(int_ptr); + int_ptr^ = 137; + fmt.println(int_ptr, int_ptr^); + + // Named arguments work too! + another_ptr := alloc_type(T = f32); + defer free(another_ptr); + + + add :: proc(T: type, args: ...T) -> T { + res: T; + for arg in args do res += arg; + return res; + } + + fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6)); + + swap :: proc(T: type, a, b: ^T) { + tmp := a^; + a^ = b^; + b^ = tmp; + } + + a, b: int = 3, 4; + fmt.println("Pre-swap:", a, b); + swap(int, &a, &b); + fmt.println("Post-swap:", a, b); + a, b = b, a; // Or use this syntax for this silly example case + + + Vector2 :: struct {x, y: f32;}; + { + // A more complicated example using subtyping + // Something like this could be used in a game + + Entity :: struct { + using position: Vector2; + flags: u64; + id: u64; + derived: any; + } + + Rock :: struct { + using entity: Entity; + heavy: bool; + } + Door :: struct { + using entity: Entity; + open: bool; + } + Monster :: struct { + using entity: Entity; + is_robot: bool; + is_zombie: bool; + } + + new_entity :: proc(T: type, x, y: f32) -> ^T { + result := new(T); + result.derived = result^; + result.x = x; + result.y = y; + + return result; + } + + entities: [dynamic]^Entity; + + rock := new_entity(Rock, 3, 5); + + // Named arguments work too! + door := new_entity(T = Door, x = 3, y = 6); + + // And named arguments can be any order + monster := new_entity( + y = 1, + x = 2, + T = Monster, + ); + + append(&entities, rock, door, monster); + + fmt.println("Subtyping"); + for entity in entities { + match e in entity.derived { + case Rock: fmt.println("Rock", e.x, e.y); + case Door: fmt.println("Door", e.x, e.y); + case Monster: fmt.println("Monster", e.x, e.y); + } + } + } + { + Entity :: struct { + using position: Vector2; + flags: u64; + id: u64; + variant: union { Rock, Door, Monster }; + } + + Rock :: struct { + using entity: ^Entity; + heavy: bool; + } + Door :: struct { + using entity: ^Entity; + open: bool; + } + Monster :: struct { + using entity: ^Entity; + is_robot: bool; + is_zombie: bool; + } + + new_entity :: proc(T: type, x, y: f32) -> ^T { + result := new(Entity); + result.variant = T{entity = result}; + result.x = x; + result.y = y; + + return cast(^T)&result.variant; + } + + entities: [dynamic]^Entity; + + rock := new_entity(Rock, 3, 5); + + // Named arguments work too! + door := new_entity(T = Door, x = 3, y = 6); + + // And named arguments can be any order + monster := new_entity( + y = 1, + x = 2, + T = Monster, + ); + + append(&entities, rock, door, monster); + + fmt.println("Union"); + for entity in entities { + match e in entity.variant { + case Rock: fmt.println("Rock", e.x, e.y); + case Door: fmt.println("Door", e.x, e.y); + case Monster: fmt.println("Monster", e.x, e.y); + } + } + } +} + + +implicit_polymorphic_assignment :: proc() { + yep :: proc(p: proc(x: int)) { + p(123); + } + + frank :: proc(x: $T) do fmt.println("frank ->", x); + tim :: proc(x, y: $T) do fmt.println("tim ->", x, y); + yep(frank); + // yep(tim); +} + + + + +main :: proc() { +/* + foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y); + foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y); + foo :: proc(x: type) do fmt.println("#3", type_info(x)); + + f :: foo; + + f(y = 3785.1546, x = 123); + f(x = int, y = 897.513); + f(x = f32); + + general_stuff(); + foreign_blocks(); + default_arguments(); + named_arguments(); + default_return_values(); + call_location(); + explicit_parametric_polymorphic_procedures(); + implicit_polymorphic_assignment(); + + + // Command line argument(s)! + // -opt=0,1,2,3 +*/ +/* + program := "+ + * - /"; + accumulator := 0; + + for token in program { + match token { + case '+': accumulator += 1; + case '-': accumulator -= 1; + case '*': accumulator *= 2; + case '/': accumulator /= 2; + case: // Ignore everything else + } + } + + fmt.printf("The program \"%s\" calculates the value %d\n", + program, accumulator); +*/ +} diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 96827590d..7c33cf9e5 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -558,7 +558,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count if (type_expr != nullptr) { e->type = check_type(c, type_expr); } - if (e->type != nullptr && is_type_polymorphic(e->type)) { + if (e->type != nullptr && is_type_polymorphic(base_type(e->type))) { error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name)); e->type = t_invalid; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a5557ee86..47151cc74 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -12,6 +12,7 @@ enum CallArgumentError { CallArgumentError_ParameterNotFound, CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, + CallArgumentError_NoneConstantParameter, }; enum CallArgumentErrorMode { @@ -972,7 +973,7 @@ Entity *make_names_field_for_record(Checker *c, Scope *scope) { return e; } -void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { +void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Array *poly_operands) { GB_ASSERT(is_type_struct(struct_type)); ast_node(st, StructType, node); @@ -984,11 +985,132 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { min_field_count += f->names.count; case_end; } - } struct_type->Record.names = make_names_field_for_record(c, c->context.scope); - auto fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct")); + Type *polymorphic_params = nullptr; + bool is_polymorphic = false; + if (st->polymorphic_params != nullptr) { + ast_node(field_list, FieldList, st->polymorphic_params); + Array params = field_list->list; + if (params.count != 0) { + isize variable_count = 0; + for_array(i, params) { + AstNode *field = params[i]; + if (ast_node_expect(field, AstNode_Field)) { + ast_node(f, Field, field); + variable_count += gb_max(f->names.count, 1); + } + } + + Array entities = {}; + array_init(&entities, c->allocator, variable_count); + + for_array(i, params) { + AstNode *param = params[i]; + if (param->kind != AstNode_Field) { + continue; + } + ast_node(p, Field, param); + AstNode *type_expr = p->type; + Type *type = nullptr; + bool is_type_param = false; + bool is_type_polymorphic_type = false; + if (type_expr == nullptr) { + error(param, "Expected a type for this parameter"); + continue; + } + if (type_expr->kind == AstNode_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); + } + if (type_expr->kind == AstNode_TypeType) { + is_type_param = true; + type = make_type_generic(c->allocator, 0, str_lit("")); + } else { + type = check_type(c, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } + } + + if (type == nullptr) { + error(params[i], "Invalid parameter type"); + type = t_invalid; + } + if (is_type_untyped(type)) { + if (is_type_untyped_undef(type)) { + error(params[i], "Cannot determine parameter type from ---"); + } else { + error(params[i], "Cannot determine parameter type from a nil"); + } + type = t_invalid; + } + + if (is_type_polymorphic_type) { + gbString str = type_to_string(type); + error(params[i], "Parameter types cannot be polymorphic, got %s", str); + gb_string_free(str); + type = t_invalid; + } + + if (!is_type_param && !is_type_constant_type(type)) { + gbString str = type_to_string(type); + error(params[i], "A parameter must be a valid constant type, got %s", str); + gb_string_free(str); + } + + Scope *scope = c->context.scope; + for_array(j, p->names) { + AstNode *name = p->names[j]; + if (!ast_node_expect(name, AstNode_Ident)) { + continue; + } + Entity *e = nullptr; + + Token token = name->Ident.token; + + if (poly_operands != nullptr) { + Operand operand = (*poly_operands)[entities.count]; + if (is_type_param) { + GB_ASSERT(operand.mode == Addressing_Type); + e = make_entity_type_name(c->allocator, scope, token, operand.type); + e->TypeName.is_type_alias = true; + } else { + GB_ASSERT(operand.mode == Addressing_Constant); + e = make_entity_constant(c->allocator, scope, token, operand.type, operand.value); + } + } else { + if (is_type_param) { + e = make_entity_type_name(c->allocator, scope, token, type); + e->TypeName.is_type_alias = true; + } else { + e = make_entity_constant(c->allocator, scope, token, type, empty_exact_value); + } + } + + add_entity(c, scope, name, e); + array_add(&entities, e); + } + } + + if (entities.count > 0) { + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = entities.data; + tuple->Tuple.variable_count = entities.count; + + polymorphic_params = tuple; + } + } + } + + is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr; + + Array fields = {}; + + if (!is_polymorphic) { + fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct")); + } struct_type->Record.scope = c->context.scope; struct_type->Record.is_packed = st->is_packed; @@ -996,6 +1118,9 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { struct_type->Record.fields = fields.data; struct_type->Record.fields_in_src_order = fields.data; struct_type->Record.field_count = fields.count; + struct_type->Record.polymorphic_params = polymorphic_params; + struct_type->Record.is_polymorphic = is_polymorphic; + type_set_offsets(c->allocator, struct_type); @@ -1703,53 +1828,55 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari for_array(j, p->names) { AstNode *name = p->names[j]; - if (ast_node_expect(name, AstNode_Ident)) { - Entity *param = nullptr; - if (is_type_param) { - if (operands != nullptr) { - Operand o = (*operands)[variable_index]; - if (o.mode == Addressing_Type) { - type = o.type; - } else { - if (!c->context.no_polymorphic_errors) { - error(o.expr, "Expected a type to assign to the type parameter"); - } - success = false; - type = t_invalid; - } - } - param = make_entity_type_name(c->allocator, scope, name->Ident.token, type); - param->TypeName.is_type_alias = true; - } else { - if (operands != nullptr && is_type_polymorphic_type) { - Operand op = (*operands)[variable_index]; - type = determine_type_from_polymorphic(c, type, op); - if (type == t_invalid) { - success = false; - } - } - - if (p->flags&FieldFlag_no_alias) { - if (!is_type_pointer(type)) { - error(params[i], "`#no_alias` can only be applied to fields of pointer type"); - p->flags &= ~FieldFlag_no_alias; // Remove the flag - } - } - - param = make_entity_param(c->allocator, scope, name->Ident.token, type, - (p->flags&FieldFlag_using) != 0, false); - param->Variable.default_value = value; - param->Variable.default_is_nil = default_is_nil; - param->Variable.default_is_location = default_is_location; - - } - if (p->flags&FieldFlag_no_alias) { - param->flags |= EntityFlag_NoAlias; - } - - add_entity(c, scope, name, param); - variables[variable_index++] = param; + if (!ast_node_expect(name, AstNode_Ident)) { + continue; } + + Entity *param = nullptr; + if (is_type_param) { + if (operands != nullptr) { + Operand o = (*operands)[variable_index]; + if (o.mode == Addressing_Type) { + type = o.type; + } else { + if (!c->context.no_polymorphic_errors) { + error(o.expr, "Expected a type to assign to the type parameter"); + } + success = false; + type = t_invalid; + } + } + param = make_entity_type_name(c->allocator, scope, name->Ident.token, type); + param->TypeName.is_type_alias = true; + } else { + if (operands != nullptr && is_type_polymorphic_type) { + Operand op = (*operands)[variable_index]; + type = determine_type_from_polymorphic(c, type, op); + if (type == t_invalid) { + success = false; + } + } + + if (p->flags&FieldFlag_no_alias) { + if (!is_type_pointer(type)) { + error(params[i], "`#no_alias` can only be applied to fields of pointer type"); + p->flags &= ~FieldFlag_no_alias; // Remove the flag + } + } + + param = make_entity_param(c->allocator, scope, name->Ident.token, type, + (p->flags&FieldFlag_using) != 0, false); + param->Variable.default_value = value; + param->Variable.default_is_nil = default_is_nil; + param->Variable.default_is_location = default_is_location; + + } + if (p->flags&FieldFlag_no_alias) { + param->flags |= EntityFlag_NoAlias; + } + + add_entity(c, scope, name, param); + variables[variable_index++] = param; } } @@ -2358,7 +2485,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { if (count >= 0) { return count; } - error(e, "Invalid array count"); + error(e, "Invalid negative array count %lld", cast(long long)count); } return 0; } @@ -2663,7 +2790,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) *type = make_type_struct(c->allocator); set_base_type(named_type, *type); check_open_scope(c, e); - check_struct_type(c, *type, e); + check_struct_type(c, *type, e, nullptr); check_close_scope(c); (*type)->Record.node = e; return true; @@ -6114,8 +6241,263 @@ Entity *find_using_index_expr(Type *t) { return nullptr; } +isize lookup_polymorphic_struct_parameter(TypeRecord *st, String parameter_name) { + if (!st->is_polymorphic) return -1; + + TypeTuple *params = &st->polymorphic_params->Tuple; + isize param_count = params->variable_count; + for (isize i = 0; i < param_count; i++) { + Entity *e = params->variables[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} + +CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, AstNode *call) { + ast_node(ce, CallExpr, call); + + Type *original_type = operand->type; + Type *struct_type = base_type(operand->type); + GB_ASSERT(is_type_struct(struct_type)); + TypeRecord *st = &struct_type->Record; + GB_ASSERT(st->is_polymorphic); + + bool show_error = true; + + Array operands = {}; + defer (array_free(&operands)); + + bool named_fields = false; + + if (is_call_expr_field_value(ce)) { + named_fields = true; + array_init_count(&operands, heap_allocator(), ce->args.count); + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + check_expr_or_type(c, &operands[i], fv->value); + } + + bool vari_expand = (ce->ellipsis.pos.line != 0); + if (vari_expand) { + error(ce->ellipsis, "Invalid use of `..` in a polymorphic type call`"); + } + + } else { + array_init(&operands, heap_allocator(), 2*ce->args.count); + check_unpack_arguments(c, -1, &operands, ce->args, false); + } + + CallArgumentError err = CallArgumentError_None; + + TypeTuple *tuple = &st->polymorphic_params->Tuple; + isize param_count = tuple->variable_count; + + Array ordered_operands = operands; + if (named_fields) { + bool *visited = gb_alloc_array(c->allocator, bool, param_count); + + array_init_count(&ordered_operands, c->tmp_allocator, param_count); + + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + if (fv->field->kind != AstNode_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name `%s` in polymorphic type call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_struct_parameter(st, name); + if (index < 0) { + if (show_error) { + error(arg, "No parameter named `%.*s` for this polymorphic type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[index]) { + if (show_error) { + error(arg, "Duplicate parameter `%.*s` in polymorphic type", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + visited[index] = true; + ordered_operands[index] = operands[i]; + } + + for (isize i = 0; i < param_count; i++) { + if (!visited[i]) { + Entity *e = tuple->variables[i]; + if (is_blank_ident(e->token)) { + continue; + } + + if (show_error) { + if (e->kind == Entity_TypeName) { + error(call, "Type parameter `%.*s` is missing in polymorphic type call", + LIT(e->token.string)); + } else { + gbString str = type_to_string(e->type); + error(call, "Parameter `%.*s` of type `%s` is missing in polymorphic type call", + LIT(e->token.string), str); + gb_string_free(str); + } + } + err = CallArgumentError_ParameterMissing; + } + } + } + + if (err != 0) { + operand->mode = Addressing_Invalid; + return err; + } + + i64 score = 0; + for (isize i = 0; i < param_count; i++) { + Operand *o = &ordered_operands[i]; + if (o->mode == Addressing_Invalid) { + continue; + } + Entity *e = tuple->variables[i]; + + if (e->kind == Entity_TypeName) { + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument `%.*s`", LIT(e->token.string)); + } + err = CallArgumentError_WrongTypes; + } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(10); + } + } else { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, e->type, &s)) { + if (show_error) { + check_assignment(c, o, e->type, str_lit("polymorphic type argument")); + } + err = CallArgumentError_WrongTypes; + } + o->type = e->type; + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for this polymorphic type argument"); + } + err = CallArgumentError_NoneConstantParameter; + } + score += s; + } + } + + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } + + if (err == 0) { + // TODO(bill): Check for previous types + gbAllocator a = c->allocator; + + auto *found_gen_types = map_get(&c->info.gen_types, hash_pointer(original_type)); + + if (found_gen_types != nullptr) { + for_array(i, *found_gen_types) { + Entity *e = (*found_gen_types)[i]; + Type *t = base_type(e->type); + TypeTuple *tuple = &t->Record.polymorphic_params->Tuple; + bool ok = true; + GB_ASSERT(param_count == tuple->variable_count); + for (isize j = 0; j < param_count; j++) { + Entity *p = tuple->variables[j]; + Operand o = ordered_operands[j]; + if (p->kind == Entity_TypeName) { + if (!are_types_identical(o.type, p->type)) { + ok = false; + } + } else if (p->kind == Entity_Constant) { + if (!are_types_identical(o.type, p->type)) { + ok = false; + } + if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { + ok = false; + } + } else { + GB_PANIC("Unknown entity kind"); + } + } + if (ok) { + operand->mode = Addressing_Type; + operand->type = e->type; + return err; + } + } + } + + String generated_name = make_string_c(expr_to_string(call)); + + Type *named_type = make_type_named(a, generated_name, nullptr, nullptr); + Type *struct_type = make_type_struct(a); + AstNode *node = clone_ast_node(a, st->node); + set_base_type(named_type, struct_type); + check_open_scope(c, node); + check_struct_type(c, struct_type, node, &ordered_operands); + check_close_scope(c); + struct_type->Record.node = node; + + Entity *e = nullptr; + + { + Token token = ast_node_token(node); + token.kind = Token_String; + token.string = generated_name; + + AstNode *node = gb_alloc_item(a, AstNode); + node->kind = AstNode_Ident; + node->Ident.token = token; + + e = make_entity_type_name(a, st->scope->parent, token, named_type); + add_entity(c, st->scope->parent, node, e); + add_entity_use(c, node, e); + } + + named_type->Named.type_name = e; + + if (found_gen_types) { + array_add(found_gen_types, e); + } else { + Array array = {}; + array_init(&array, heap_allocator()); + array_add(&array, e); + map_set(&c->info.gen_types, hash_pointer(original_type), array); + } + + operand->mode = Addressing_Type; + operand->type = named_type; + } + return err; +} + + ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { - GB_ASSERT(call->kind == AstNode_CallExpr); ast_node(ce, CallExpr, call); if (ce->proc != nullptr && ce->proc->kind == AstNode_BasicDirective) { @@ -6170,28 +6552,43 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { if (operand->mode == Addressing_Type) { Type *t = operand->type; - gbString str = type_to_string(t); - defer (gb_string_free(str)); + if (is_type_polymorphic_struct(t)) { + auto err = check_polymorphic_struct_type(c, operand, call); + if (err == 0) { + AstNode *ident = operand->expr; + while (ident->kind == AstNode_SelectorExpr) { + AstNode *s = ident->SelectorExpr.selector; + ident = s; + } + add_entity_use(c, ident, entity_of_ident(&c->info, ident)); + add_type_and_value(&c->info, call, Addressing_Type, operand->type, empty_exact_value); + } else { + operand->mode = Addressing_Invalid; + operand->type = t_invalid; + } + } else { + gbString str = type_to_string(t); + defer (gb_string_free(str)); - operand->mode = Addressing_Invalid; - isize arg_count = ce->args.count; - switch (arg_count) { - case 0: error(call, "Missing argument in conversion to `%s`", str); break; - default: error(call, "Too many arguments in conversion to `%s`", str); break; - case 1: { - AstNode *arg = ce->args[0]; - if (arg->kind == AstNode_FieldValue) { - error(call, "`field = value` cannot be used in a type conversion"); - arg = arg->FieldValue.value; - // NOTE(bill): Carry on the cast regardless + operand->mode = Addressing_Invalid; + isize arg_count = ce->args.count; + switch (arg_count) { + case 0: error(call, "Missing argument in conversion to `%s`", str); break; + default: error(call, "Too many arguments in conversion to `%s`", str); break; + case 1: { + AstNode *arg = ce->args[0]; + if (arg->kind == AstNode_FieldValue) { + error(call, "`field = value` cannot be used in a type conversion"); + arg = arg->FieldValue.value; + // NOTE(bill): Carry on the cast regardless + } + check_expr(c, operand, arg); + if (operand->mode != Addressing_Invalid) { + check_cast(c, operand, t); + } + } break; } - check_expr(c, operand, arg); - if (operand->mode != Addressing_Invalid) { - check_cast(c, operand, t); - } - } break; } - return Expr_Expr; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6bf873bd3..51e6adf79 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1666,150 +1666,151 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case_end; case_ast_node(vd, ValueDecl, node); - if (vd->is_mutable) { - Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); - isize entity_count = 0; + if (!vd->is_mutable) { + break; + } + Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); + isize entity_count = 0; - if (vd->flags & VarDeclFlag_thread_local) { - vd->flags &= ~VarDeclFlag_thread_local; - error(node, "`thread_local` may only be applied to a variable declaration"); - } + if (vd->flags & VarDeclFlag_thread_local) { + vd->flags &= ~VarDeclFlag_thread_local; + error(node, "`thread_local` may only be applied to a variable declaration"); + } - for_array(i, vd->names) { - AstNode *name = vd->names[i]; - Entity *entity = nullptr; - if (name->kind != AstNode_Ident) { - error(name, "A variable declaration must be an identifier"); + for_array(i, vd->names) { + AstNode *name = vd->names[i]; + Entity *entity = nullptr; + if (name->kind != AstNode_Ident) { + error(name, "A variable declaration must be an identifier"); + } else { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + // NOTE(bill): Ignore assignments to `_` + if (!is_blank_ident(str)) { + found = current_scope_lookup_entity(c->context.scope, str); + } + if (found == nullptr) { + entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false); + entity->identifier = name; + + AstNode *fl = c->context.curr_foreign_library; + if (fl != nullptr) { + GB_ASSERT(fl->kind == AstNode_Ident); + entity->Variable.is_foreign = true; + entity->Variable.foreign_library_ident = fl; + } } else { - Token token = name->Ident.token; - String str = token.string; - Entity *found = nullptr; - // NOTE(bill): Ignore assignments to `_` - if (!is_blank_ident(str)) { - found = current_scope_lookup_entity(c->context.scope, str); - } - if (found == nullptr) { - entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false); - entity->identifier = name; + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } + if (entity == nullptr) { + entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + } + entity->parent_proc_decl = c->context.curr_proc_decl; + entities[entity_count++] = entity; + } - AstNode *fl = c->context.curr_foreign_library; - if (fl != nullptr) { - GB_ASSERT(fl->kind == AstNode_Ident); - entity->Variable.is_foreign = true; - entity->Variable.foreign_library_ident = fl; - } - } else { - TokenPos pos = found->token.pos; - error(token, - "Redeclaration of `%.*s` in this scope\n" + Type *init_type = nullptr; + if (vd->type != nullptr) { + init_type = check_type(c, vd->type, nullptr); + if (init_type == nullptr) { + init_type = t_invalid; + } else if (is_type_polymorphic(base_type(init_type))) { + error(vd->type, "Invalid use of a polymorphic type in variable declaration"); + init_type = t_invalid; + } + } + + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != nullptr); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; + } + e->flags |= EntityFlag_Visited; + + if (e->type == nullptr) { + e->type = init_type; + } + } + + check_arity_match(c, vd); + check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); + + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + if (e->Variable.is_foreign) { + if (vd->values.count > 0) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } + init_entity_foreign_library(c, e); + + String name = e->token.string; + auto *fp = &c->info.foreigns; + HashKey key = hash_string(name); + Entity **found = map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(e->token, + "Foreign entity `%.*s` previously declared elsewhere with a different type\n" "\tat %.*s(%td:%td)", - LIT(str), LIT(pos.file), pos.line, pos.column); - entity = found; + LIT(name), LIT(pos.file), pos.line, pos.column); } - } - if (entity == nullptr) { - entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); - } - entity->parent_proc_decl = c->context.curr_proc_decl; - entities[entity_count++] = entity; - } - - Type *init_type = nullptr; - if (vd->type) { - init_type = check_type(c, vd->type, nullptr); - if (init_type == nullptr) { - init_type = t_invalid; - } else if (is_type_polymorphic(init_type)) { - error(vd->type, "Invalid use of a polymorphic type in variable declaration"); - init_type = t_invalid; + } else { + map_set(fp, key, e); } } + add_entity(c, c->context.scope, e->identifier, e); + } - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - GB_ASSERT(e != nullptr); - if (e->flags & EntityFlag_Visited) { - e->type = t_invalid; + if ((vd->flags & VarDeclFlag_using) != 0) { + Token token = ast_node_token(node); + if (vd->type != nullptr && entity_count > 1) { + error(token, "`using` can only be applied to one variable of the same type"); + // TODO(bill): Should a `continue` happen here? + } + + for (isize entity_index = 0; entity_index < entity_count; entity_index++) { + Entity *e = entities[entity_index]; + if (e == nullptr) { continue; } - e->flags |= EntityFlag_Visited; - - if (e->type == nullptr) { - e->type = init_type; + if (e->kind != Entity_Variable) { + continue; } - } + bool is_immutable = e->Variable.is_immutable; + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); - check_arity_match(c, vd); - check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); - - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - if (e->Variable.is_foreign) { - if (vd->values.count > 0) { - error(e->token, "A foreign variable declaration cannot have a default value"); - } - init_entity_foreign_library(c, e); - - String name = e->token.string; - auto *fp = &c->info.foreigns; - HashKey key = hash_string(name); - Entity **found = map_get(fp, key); - if (found) { - Entity *f = *found; - TokenPos pos = f->token.pos; - Type *this_type = base_type(e->type); - Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { - error(e->token, - "Foreign entity `%.*s` previously declared elsewhere with a different type\n" - "\tat %.*s(%td:%td)", - LIT(name), LIT(pos.file), pos.line, pos.column); - } - } else { - map_set(fp, key, e); - } - } - add_entity(c, c->context.scope, e->identifier, e); - } - - if ((vd->flags & VarDeclFlag_using) != 0) { - Token token = ast_node_token(node); - if (vd->type != nullptr && entity_count > 1) { - error(token, "`using` can only be applied to one variable of the same type"); - // TODO(bill): Should a `continue` happen here? - } - - for (isize entity_index = 0; entity_index < entity_count; entity_index++) { - Entity *e = entities[entity_index]; - if (e == nullptr) { - continue; - } - if (e->kind != Entity_Variable) { - continue; - } - bool is_immutable = e->Variable.is_immutable; - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - - if (is_type_struct(t) || is_type_raw_union(t)) { - Scope *scope = scope_of_node(&c->info, t->Record.node); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); - uvar->Variable.is_immutable = is_immutable; - Entity *prev = scope_insert_entity(c->context.scope, uvar); - if (prev != nullptr) { - error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); - return; - } + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope *scope = scope_of_node(&c->info, t->Record.node); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + uvar->Variable.is_immutable = is_immutable; + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != nullptr) { + error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + return; } } - } else { - // NOTE(bill): skip the rest to remove extra errors - error(token, "`using` can only be applied to variables of type struct or raw_union"); - return; } + } else { + // NOTE(bill): skip the rest to remove extra errors + error(token, "`using` can only be applied to variables of type struct or raw_union"); + return; } } } diff --git a/src/checker.cpp b/src/checker.cpp index d7882414a..1cbf5db40 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -301,6 +301,7 @@ struct CheckerInfo { Map untyped; // Key: AstNode * | Expression -> ExprInfo Map implicits; // Key: AstNode * Map > gen_procs; // Key: AstNode * | Identifier -> Entity + Map > gen_types; // Key: Type * Map entities; // Key: Entity * Map foreigns; // Key: String Map files; // Key: String (full path) @@ -742,6 +743,7 @@ void init_checker_info(CheckerInfo *i) { map_init(&i->foreigns, a); map_init(&i->implicits, a); map_init(&i->gen_procs, a); + map_init(&i->gen_types, a); map_init(&i->type_info_map, a); map_init(&i->files, a); i->type_info_count = 0; @@ -758,6 +760,7 @@ void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->foreigns); map_destroy(&i->implicits); map_destroy(&i->gen_procs); + map_destroy(&i->gen_types); map_destroy(&i->type_info_map); map_destroy(&i->files); } diff --git a/src/ir.cpp b/src/ir.cpp index 8746b676b..3d9ef820a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3670,6 +3670,18 @@ void ir_pop_target_list(irProcedure *proc) { void ir_gen_global_type_name(irModule *m, Entity *e, String name) { if (e->type == nullptr) return; + if (is_type_polymorphic(base_type(e->type))) { + auto found = map_get(&m->info->gen_types, hash_pointer(e->type)); + if (found == nullptr) { + return; + } + for_array(i, *found) { + Entity *e = (*found)[i]; + ir_mangle_add_sub_type_name(m, e, name); + } + return; + } + irValue *t = ir_value_type_name(m->allocator, name, e->type); ir_module_add_value(m, e, t); map_set(&m->members, hash_string(name), t); @@ -5731,8 +5743,10 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { } bool polymorphic = is_type_polymorphic(e->type); - - if (!polymorphic && map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) { + if (polymorphic && !is_type_struct(e->type)) { + continue; + } + if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) { // NOTE(bill): Nothing depends upon it so doesn't need to be built continue; } @@ -5741,6 +5755,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { // NOTE(bill): Generate a new name // parent_proc.name-guid String ts_name = e->token.string; + isize name_len = proc->name.len + 1 + ts_name.len + 1 + 10 + 1; u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); i32 guid = cast(i32)proc->module->members.entries.count; @@ -5751,6 +5766,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { name, e->type); map_set(&proc->module->entity_names, hash_entity(e), name); ir_gen_global_type_name(proc->module, e, name); + } else if (e->kind == Entity_Procedure) { CheckerInfo *info = proc->module->info; DeclInfo *decl = decl_info_of_entity(info, e); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7bd79fe2e..c683f4dc6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -340,7 +340,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Type_Named: if (is_type_struct(t) || is_type_union(t)) { String *name = map_get(&m->entity_names, hash_pointer(t->Named.type_name)); - GB_ASSERT_MSG(name != nullptr, "%.*s", LIT(t->Named.name)); + GB_ASSERT_MSG(name != nullptr, "%.*s %p", LIT(t->Named.name), t->Named.type_name); ir_print_encoded_local(f, *name); } else { ir_print_type(f, m, base_type(t)); diff --git a/src/parser.cpp b/src/parser.cpp index a2d8ab1f9..ca5e59983 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -111,6 +111,7 @@ enum StmtStateFlag { }; enum FieldFlag { + FieldFlag_NONE = 0, FieldFlag_ellipsis = 1<<0, FieldFlag_using = 1<<1, FieldFlag_no_alias = 1<<2, @@ -418,12 +419,13 @@ AST_NODE_KIND(_TypeBegin, "", i32) \ AstNode *elem; \ }) \ AST_NODE_KIND(StructType, "struct type", struct { \ - Token token; \ - Array fields; \ - isize field_count; \ - bool is_packed; \ - bool is_ordered; \ - AstNode *align; \ + Token token; \ + Array fields; \ + isize field_count; \ + AstNode * polymorphic_params; \ + bool is_packed; \ + bool is_ordered; \ + AstNode * align; \ }) \ AST_NODE_KIND(UnionType, "union type", struct { \ Token token; \ @@ -866,6 +868,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { break; case AstNode_StructType: n->StructType.fields = clone_ast_node_array(a, n->StructType.fields); + n->StructType.polymorphic_params = clone_ast_node(a, n->StructType.polymorphic_params); n->StructType.align = clone_ast_node(a, n->StructType.align); break; case AstNode_UnionType: @@ -1459,14 +1462,15 @@ AstNode *ast_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) } AstNode *ast_struct_type(AstFile *f, Token token, Array fields, isize field_count, - bool is_packed, bool is_ordered, AstNode *align) { + AstNode *polymorphic_params, bool is_packed, bool is_ordered, AstNode *align) { AstNode *result = make_ast_node(f, AstNode_StructType); - result->StructType.token = token; - result->StructType.fields = fields; - result->StructType.field_count = field_count; - result->StructType.is_packed = is_packed; - result->StructType.is_ordered = is_ordered; - result->StructType.align = align; + result->StructType.token = token; + result->StructType.fields = fields; + result->StructType.field_count = field_count; + result->StructType.polymorphic_params = polymorphic_params; + result->StructType.is_packed = is_packed; + result->StructType.is_ordered = is_ordered; + result->StructType.align = align; return result; } @@ -2182,6 +2186,8 @@ AstNode * parse_simple_stmt (AstFile *f, StmtAllowFlag flags); AstNode * parse_type (AstFile *f); AstNode * parse_call_expr (AstFile *f, AstNode *operand); AstNode * parse_record_field_list(AstFile *f, isize *name_count_); +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token); + AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { if (statement == nullptr) { @@ -2205,7 +2211,7 @@ AstNode *convert_stmt_to_body(AstFile *f, AstNode *stmt) { syntax_error(stmt, "Expected a normal statement rather than a block statement"); return stmt; } - GB_ASSERT(is_ast_node_stmt(stmt)); + GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_decl(stmt)); Token open = ast_node_token(stmt); Token close = ast_node_token(stmt); Array stmts = make_ast_node_array(f, 1); @@ -2430,10 +2436,21 @@ AstNode *parse_operand(AstFile *f, bool lhs) { case Token_struct: { Token token = expect_token(f, Token_struct); + AstNode *polymorphic_params = nullptr; bool is_packed = false; bool is_ordered = false; AstNode *align = nullptr; + if (allow_token(f, Token_OpenParen)) { + isize param_count = 0; + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + if (param_count == 0) { + syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric"); + polymorphic_params = nullptr; + } + expect_token_after(f, Token_CloseParen, "parameter list"); + } + isize prev_level = f->expr_level; f->expr_level = -1; @@ -2477,7 +2494,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, is_packed, is_ordered, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_ordered, align); } break; case Token_union: { @@ -3394,7 +3411,6 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) { return parse_body(f); } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters); AstNode *parse_results(AstFile *f) { @@ -3414,7 +3430,7 @@ AstNode *parse_results(AstFile *f) { AstNode *list = nullptr; expect_token(f, Token_OpenParen); - list = parse_field_list(f, nullptr, 0, Token_CloseParen, true); + list = parse_field_list(f, nullptr, 0, Token_CloseParen, true, false); expect_token_after(f, Token_CloseParen, "parameter list"); return list; } @@ -3424,7 +3440,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) { AstNode *results = nullptr; expect_token(f, Token_OpenParen); - params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true); + params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true); expect_token_after(f, Token_CloseParen, "parameter list"); results = parse_results(f); @@ -3669,7 +3685,7 @@ AstNode *parse_record_field_list(AstFile *f, isize *name_count_) { return ast_field_list(f, start_token, decls); } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters) { +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token) { TokenKind separator = Token_Comma; Token start_token = f->curr_token; @@ -3682,7 +3698,6 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok isize total_name_count = 0; bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis; - bool allow_type_token = allow_default_parameters; while (f->curr_token.kind != follow && f->curr_token.kind != Token_Colon && @@ -3691,10 +3706,9 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token); AstNodeAndFlags naf = {param, flags}; array_add(&list, naf); - if (f->curr_token.kind != Token_Comma) { + if (!allow_token(f, Token_Comma)) { break; } - advance_token(f); } if (f->curr_token.kind == Token_Colon) { @@ -3750,7 +3764,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *default_value = nullptr; expect_token_after(f, Token_Colon, "field list"); if (f->curr_token.kind != Token_Eq) { - type = parse_var_type(f, allow_ellipsis, allow_default_parameters); + type = parse_var_type(f, allow_ellipsis, allow_type_token); } if (allow_token(f, Token_Eq)) { // TODO(bill): Should this be true==lhs or false==rhs? diff --git a/src/types.cpp b/src/types.cpp index d25f3fba9..5f3bee127 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -94,6 +94,8 @@ struct TypeRecord { bool are_offsets_being_processed; bool is_packed; bool is_ordered; + bool is_polymorphic; + Type * polymorphic_params; // Type_Tuple i64 custom_align; // NOTE(bill): Only used in structs at the moment Entity * names; @@ -937,6 +939,15 @@ bool is_type_indexable(Type *t) { return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t); } +bool is_type_polymorphic_struct(Type *t) { + t = base_type(t); + if (t->kind == Type_Record && + t->Record.kind == TypeRecord_Struct) { + return t->Record.is_polymorphic; + } + return false; +} + bool is_type_polymorphic(Type *t) { switch (t->kind) { case Type_Generic: @@ -967,7 +978,7 @@ bool is_type_polymorphic(Type *t) { if (t->Proc.is_polymorphic) { return true; } - #if 0 + #if 1 if (t->Proc.param_count > 0 && is_type_polymorphic(t->Proc.params)) { return true; @@ -995,6 +1006,9 @@ bool is_type_polymorphic(Type *t) { } break; case Type_Record: + if (t->Record.is_polymorphic) { + return true; + } for (isize i = 0; i < t->Record.field_count; i++) { if (is_type_polymorphic(t->Record.fields[i]->type)) { return true;