diff --git a/code/demo.odin b/code/demo.odin index da37c37f7..6a4f4b261 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,5 +1,411 @@ import "fmt.odin"; +proc general_stuff() { + // Complex numbers + var a = 3 + 4i; + var b: complex64 = 3 + 4i; + var c: complex128 = 3 + 4i; + var d = complex(2, 3); + + var 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 + proc c_printf(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf"; + } + + + type Foo struct { + x: int, + y: f32, + z: string, + } + var foo = Foo{123, 0.513, "A string"}; + var x, y, z = expand_to_tuple(foo); + fmt.println(x, y, z); + + + // By default, all variables are zeroed + // This can be overridden with the "uninitialized value" + var 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 + +} + +proc foreign_blocks() { + // See sys/windows.odin +} + + +proc default_arguments() { + proc hello(a: int = 9, b: int = 9) { + fmt.printf("a is %d; b is %d\n", a, b); + } + fmt.println("\nTesting default arguments:"); + hello(1, 2); + hello(1); + hello(); +} + +proc named_arguments() { + type Colour enum { + Red, + Orange, + Yellow, + Green, + Blue, + Octarine, + }; + using Colour; + + proc make_character(name, catch_phrase: string, favorite_color, least_favorite_color: Colour) { + fmt.println(); + fmt.printf("My name is %v and I like %v. %v\n", name, favorite_color, 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 + make_character("¡Ay, caramba!", "Frank", Green, Blue); + + // Named arguments help to disambiguate this problem + make_character(catch_phrase = "¡Ay, caramba!", name = "Frank", + least_favorite_color = Green, favorite_color = Blue); + + + // The named arguments can be specifed in any order. + make_character(favorite_color = Octarine, catch_phrase = "U wot m8!", + least_favorite_color = Green, name = "Dennis"); + + + // NOTE: You cannot mix named arguments with normal values + /* + make_character("Dennis", + favorite_color = Octarine, catch_phrase = "U wot m8!", + least_favorite_color = Green); + */ + + + // Named arguments can also aid with default arguments + proc numerous_things(s : string, a = 1, b = 2, c = 3.14, d = "The Best String!", e = false, f = 10.3/3.1, g = false) { + var 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 + proc weird(pre: string, mid: int = 0, post: string) { + fmt.println(pre, mid, post); + } + + weird("How many things", 42, "huh?"); + weird(pre = "Prefix", post = "Pat"); + +} + + +proc default_return_values() { + proc foo(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 + type Error enum { + None, + WhyTheNumberThree, + TenIsTooBig, + }; + + type Entity struct { + name: string, + id: u32, + } + + proc some_thing(input: int) -> (result: ^Entity = nil, err = Error.None) { + match { + case input == 3: return err = Error.WhyTheNumberThree; + case input >= 10: return err = Error.TenIsTooBig; + } + + var e = new(Entity); + e.id = u32(input); + + return result = e; + } +} + +proc call_location() { + proc amazing(n: int, using loc = #caller_location) { + fmt.printf("%s(%d:%d) just asked to do something amazing to %d.\n", + fully_pathed_filename, line, column); + fmt.printf("Amazing -> %d\n", n+1); + } + + var 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` + +} + + +proc explicit_parametric_polymorphic_procedures() { + // This is how `new` is actually implemented, see _preload.odin + proc alloc_type(T: type) -> ^T { + return ^T(alloc(size_of(T), align_of(T))); + } + + var int_ptr = alloc_type(int); + defer free(int_ptr); + int_ptr^ = 137; + fmt.println(int_ptr, int_ptr^); + + // Named arguments work too! + var another_ptr = alloc_type(T = f32); + defer free(another_ptr); + + + proc add(T: type, args: ..T) -> T { + var res: T; + for arg in args { + res += arg; + } + return res; + } + + fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6)); + + proc swap(T: type, a, b: ^T) { + var tmp = a^; + a^ = b^; + b^ = tmp; + } + + var 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 + + + + + + + // A more complicated example using subtyping + // Something like this could be used a game + type Vector2 struct {x, y: f32}; + + type Entity struct { + using position: Vector2, + flags: u64, + id: u64, + batch_index: u32, + slot_index: u32, + portable_id: u32, + derived: any, + } + + type Rock struct { + using entity: ^Entity, + heavy: bool, + } + type Door struct { + using entity: ^Entity, + open: bool, + } + type Monster struct { + using entity: ^Entity, + is_robot: bool, + is_zombie: bool, + } + + type EntityManager struct { + batches: [dynamic]^EntityBatch, + next_portable_id: u32, + } + + const ENTITIES_PER_BATCH = 16; + type EntityBatch struct { + data: [ENTITIES_PER_BATCH]Entity, + occupied: [ENTITIES_PER_BATCH]bool, + batch_index: u32, + } + + proc use_empty_slot(manager: ^EntityManager, batch: ^EntityBatch) -> ^Entity { + for ok, i in batch.occupied { + if ok -> continue; + batch.occupied[i] = true; + + var e = &batch.data[i]; + e.batch_index = u32(batch.batch_index); + e.slot_index = u32(i); + e.portable_id = manager.next_portable_id; + manager.next_portable_id++; + return e; + } + return nil; + } + + proc gen_new_entity(manager: ^EntityManager) -> ^Entity { + for b in manager.batches { + var e = use_empty_slot(manager, b); + if e != nil -> return e; + } + + var new_batch = new(EntityBatch); + append(manager.batches, new_batch); + new_batch.batch_index = u32(len(manager.batches)-1); + + return use_empty_slot(manager, new_batch); + } + + + + proc new_entity(manager: ^EntityManager, Type: type, x, y: int) -> ^Type { + var result = new(Type); + result.entity = gen_new_entity(manager); + result.derived.data = result; + result.derived.type_info = type_info(Type); + + result.position.x = f32(x); + result.position.y = f32(y); + + return result; + } + + var manager: EntityManager; + var entities: [dynamic]^Entity; + + var rock = new_entity(&manager, Rock, 3, 5); + + // Named arguments work too! + var door = new_entity(manager = &manager, Type = Door, x = 3, y = 6); + + // And named arguments can be any order + var monster = new_entity( + y = 1, + x = 2, + manager = &manager, + Type = Monster, + ); + + append(entities, rock, door, monster); + + // An alternative to `union`s + for entity in entities { + match e in entity.derived { + case Rock: fmt.println("Rock", e.portable_id); + case Door: fmt.println("Door", e.portable_id); + case Monster: fmt.println("Monster", e.portable_id); + } + } +} + +proc main() { + general_stuff(); + foreign_blocks(); + default_arguments(); + named_arguments(); + default_return_values(); + call_location(); + explicit_parametric_polymorphic_procedures(); + + // Command line argument(s)! + // -opt=0,1,2,3 + + + /*************/ + /* Questions */ + /*************/ + + /* + I'm questioning if I should change the declaration syntax back to Jai-like + as I've found solutions to the problems I had with it before. + + Should I change back to Jai-like declarations or keep with the Pascal-like? + + Jai-like + + x: int; + x: int = 123; + x := 123; + + foo : int : 123; + foo :: 123; + + MyInt :: int; + BarType :: proc(); + + bar :: proc() { + } + + foreign lib { + foreign_bar :: proc() ---; + } + + Pascal-like + + var x: int; + var x: int = 123; + var x = 123; + + const foo: int = 123; + const foo = 123; + + type MyInt int; + type BarType proc(); + + proc bar() { + } + + foreign lib { + proc foreign_bar(); + } + */ + +} + +/* proc main() { var program = "+ + * - /"; var accumulator = 0; @@ -17,3 +423,5 @@ proc main() { 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 d3f82b1d3..8d375d548 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -45,6 +45,11 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex } t = default_type(t); } + if (is_type_gen_proc(t)) { + error(e->token, "Invalid use of a generic procedure in %.*s", LIT(context_name)); + e->type = t_invalid; + return NULL; + } if (is_type_bit_field_value(t)) { t = default_bit_field_value_type(t); } @@ -361,7 +366,12 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { if (pt->is_generic) { if (pd->body == NULL) { - error(e->token, "Generic procedures must have a body"); + error(e->token, "Polymorphic procedures must have a body"); + } + + if (is_foreign) { + error(e->token, "A foreign procedures cannot be a polymorphic"); + return; } } @@ -387,6 +397,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { pt->require_results = is_require_results; } + + if (is_foreign) { String name = e->token.string; if (pd->link_name.len > 0) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index f6b2cf5c1..2f3b1630a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -12,7 +12,6 @@ enum CallArgumentError { CallArgumentError_ParameterNotFound, CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, - CallArgumentError_GenericProcedureNotSupported, }; enum CallArgumentErrorMode { @@ -285,9 +284,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { if (is_type_any(dst)) { - // NOTE(bill): Anything can cast to `Any` - add_type_info_type(c, s); - return 10; + if (!is_type_gen_proc(src)) { + // NOTE(bill): Anything can cast to `Any` + add_type_info_type(c, s); + return 10; + } } @@ -4241,13 +4242,17 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id case BuiltinProc_type_of: - // proc type_of_val(val: Type) -> type(Type) - check_assignment(c, operand, NULL, str_lit("argument of `type_of_val`")); + // proc type_of(val: Type) -> type(Type) + check_assignment(c, operand, NULL, str_lit("argument of `type_of`")); if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) { return false; } - if (operand->type == NULL || operand->type == t_invalid || is_type_gen_proc(operand->type)) { - error(operand->expr, "Invalid argument to `type_of_val`"); + if (operand->type == NULL || operand->type == t_invalid) { + error(operand->expr, "Invalid argument to `type_of`"); + return false; + } + if (is_type_gen_proc(operand->type)) { + error(operand->expr, "`type_of` of generic procedure cannot be determined"); return false; } operand->mode = Addressing_Type; @@ -4999,10 +5004,10 @@ Entity *find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Type *final_proc_type = make_type_proc(c->allocator, c->context.scope, NULL, 0, NULL, 0, false, pt->calling_convention); check_procedure_type(c, final_proc_type, pt->node, operands); - auto *found = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier)); - if (found) { - for_array(i, *found) { - Entity *other = (*found)[i]; + auto *found_gen_procs = map_get(&c->info.gen_procs, hash_pointer(base_entity->identifier)); + if (found_gen_procs) { + for_array(i, *found_gen_procs) { + Entity *other = (*found_gen_procs)[i]; if (are_types_identical(other->type, final_proc_type)) { // NOTE(bill): This scope is not needed any more, destroy it destroy_scope(scope); @@ -5041,13 +5046,13 @@ Entity *find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, proc_info.body = pd->body; proc_info.tags = tags; - if (found) { - array_add(found, entity); + if (found_gen_procs) { + array_add(found_gen_procs, entity); } else { Array array = {}; array_init(&array, heap_allocator()); array_add(&array, entity); - map_set(&c->info.gen_procs, hash_pointer(entity->identifier), array); + map_set(&c->info.gen_procs, hash_pointer(base_entity->identifier), array); } GB_ASSERT(entity != NULL); @@ -5373,7 +5378,6 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { Entity *gen_entity = NULL; if (pt->is_generic && err == CallArgumentError_None) { - // err = CallArgumentError_GenericProcedureNotSupported; ProcedureInfo proc_info = {}; gen_entity = find_or_generate_polymorphic_procedure(c, entity, &ordered_operands, &proc_info); if (gen_entity != NULL) { diff --git a/src/checker.cpp b/src/checker.cpp index 7d717e7cb..051020ae6 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -87,7 +87,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("size_of"), 1, false, Expr_Expr}, {STR_LIT("align_of"), 1, false, Expr_Expr}, {STR_LIT("offset_of"), 2, false, Expr_Expr}, - {STR_LIT("type_of_val"), 1, false, Expr_Expr}, + {STR_LIT("type_of"), 1, false, Expr_Expr}, {STR_LIT("type_info"), 1, false, Expr_Expr}, {STR_LIT("compile_assert"), 1, false, Expr_Expr}, @@ -1456,7 +1456,7 @@ void check_procedure_overloading(Checker *c, Entity *e) { TypeProc *ptq = &base_type(q->type)->Proc; if (ptq->is_generic) { q->type = t_invalid; - error(q->token, "Generic procedure `%.*s` cannot be overloaded", LIT(name)); + error(q->token, "Polymorphic procedure `%.*s` cannot be overloaded", LIT(name)); continue; } } @@ -2269,7 +2269,6 @@ void check_parsed_files(Checker *c) { if (pi->decl->gen_proc_type == NULL) { continue; } - // gb_printf_err("Generic procedure `%.*s` -> %s\n", LIT(pi->token.string), type_to_string(pi->decl->gen_proc_type)); } add_curr_ast_file(c, pi->file);