From: Brendan Hansen Date: Wed, 8 Jun 2022 02:39:38 +0000 (-0500) Subject: added #locked; #bottom_test; changed interface syntax X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=16aaf93c1628e10702fe5c73a6676c1ad1ca8624;p=onyx.git added #locked; #bottom_test; changed interface syntax --- diff --git a/core/container/array.onyx b/core/container/array.onyx index c22d7566..16e8f984 100644 --- a/core/container/array.onyx +++ b/core/container/array.onyx @@ -12,7 +12,7 @@ package core.array // --------------------------------- // Dynamic Arrays // --------------------------------- -make :: #match { +make :: #match #locked { ($T: type_expr, capacity := 4, allocator := context.allocator) -> [..] T { arr : [..] T; init(^arr, capacity, allocator); @@ -54,7 +54,7 @@ free :: (arr: ^[..] $T) { (package core.array).free(x); } -copy :: #match { +copy :: #match #locked { (arr: ^[..] $T, allocator := context.allocator) -> [..] T { new_arr : [..] T; init(^new_arr, arr.count, allocator); @@ -285,7 +285,7 @@ set :: (arr: [] $T, idx: i32, value: T) { arr.data[idx] = value; } -contains :: #match { +contains :: #match #locked { macro (arr: [] $T, $cmp: Code) -> bool { for it: arr do if #unquote cmp do return true; return false; @@ -328,7 +328,7 @@ reverse :: (arr: [] $T) { // Simple insertion sort // cmp should return >0 if left > right // -sort :: #match { +sort :: #match #locked { (arr: [] $T, cmp: (T, T) -> i32) { for i: 1 .. arr.count { x := arr.data[i]; @@ -364,7 +364,7 @@ sort :: #match { } } -quicksort :: #match { +quicksort :: #match #locked { (arr: [] $T, cmp: ( T, T) -> i32) do quicksort_impl(arr, cmp, 0, arr.count - 1); , (arr: [] $T, cmp: (^T, ^T) -> i32) do quicksort_impl(arr, cmp, 0, arr.count - 1); , } @@ -379,7 +379,7 @@ quicksort :: #match { quicksort_impl(arr, cmp, pivot + 1, hi); } - quicksort_partition :: #match { + quicksort_partition :: #match #locked { (arr: [] $T, cmp: (T, T) -> i32, lo, hi: i32) -> i32 { pivot := arr[hi]; i := lo - 1; @@ -437,7 +437,7 @@ fold :: (arr: [] $T, init: $R, f: (T, R) -> R) -> R { return val; } -map :: #match { +map :: #match #locked { macro (arr: [] $T, f: (^T) -> void) do for ^it: arr do f(it);, macro (arr: [] $T, f: (T) -> T) do for ^it: arr do *it = f(*it);, macro (arr: [] $T, body: Code) do for ^it: arr do #unquote body;, @@ -445,30 +445,32 @@ map :: #match { macro (arr: [] $T, data: $R, f: (T, R) -> T) do for ^it: arr do *it = f(*it, data);, } -every :: #match {} -#match every macro (arr: [] $T, predicate: (T) -> bool) -> bool { - every :: every - return every(arr, #(predicate(it))); -} +every :: #match #locked { + macro (arr: [] $T, predicate: (T) -> bool) -> bool { + every :: every + return every(arr, #(predicate(it))); + }, -#match every macro (arr: [] $T, predicate_body: Code) -> bool { - for arr { - if !(#unquote predicate_body) do return false; + macro (arr: [] $T, predicate_body: Code) -> bool { + for arr { + if !(#unquote predicate_body) do return false; + } + return true; } - return true; } -some :: #match {} -#match some macro (arr: [] $T, predicate: (T) -> bool) -> bool { - some :: some - return every(arr, #(predicate(it))); -} +some :: #match #locked { + macro (arr: [] $T, predicate: (T) -> bool) -> bool { + some :: some + return every(arr, #(predicate(it))); + }, -#match some macro (arr: [] $T, predicate_body: Code) -> bool { - for arr { - if #unquote predicate_body do return false; + macro (arr: [] $T, predicate_body: Code) -> bool { + for arr { + if #unquote predicate_body do return false; + } + return true; } - return true; } fill :: (arr: [] $T, value: T) { @@ -510,33 +512,35 @@ find_ptr :: (arr: [] $T, value: T) -> ^T { return null; } -first :: #match {} -#match first macro (arr: [] $T, predicate: (T) -> bool) -> ^T { - first :: first - return first(arr, #(predicate(it))); -} +first :: #match #locked { + macro (arr: [] $T, predicate: (T) -> bool) -> ^T { + first :: first + return first(arr, #(predicate(it))); + }, -#match first macro (arr: [] $T, predicate_body: Code) -> ^T { - // This is to preserve the semantics that "it" is - // not a pointer (same as contains) - for ^it_ptr: arr { - it := *it_ptr; - if #unquote predicate_body do return it_ptr; - } + macro (arr: [] $T, predicate_body: Code) -> ^T { + // This is to preserve the semantics that "it" is + // not a pointer (same as contains) + for ^it_ptr: arr { + it := *it_ptr; + if #unquote predicate_body do return it_ptr; + } - return null; + return null; + } } -count_where :: #match {} -#match count_where macro (arr: [] $T, predicate: (T) -> bool) -> u32 { - count_where :: count_where - return count_where(arr, #(predicate(it))); -} +count_where :: #match #locked { + macro (arr: [] $T, predicate: (T) -> bool) -> u32 { + count_where :: count_where + return count_where(arr, #(predicate(it))); + }, -#match count_where macro (arr: [] $T, predicate_body: Code) -> u32 { - count: u32 = 0; - for arr { - if #unquote predicate_body do count += 1; + macro (arr: [] $T, predicate_body: Code) -> u32 { + count: u32 = 0; + for arr { + if #unquote predicate_body do count += 1; + } + return count; } - return count; } diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 2ca0b67e..3dc54efa 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -29,7 +29,7 @@ package core.iter as_iterator :: #match {} #local __iterable_test :: macro (i: Iterator($V)) {} -Iterable :: interface (t: $T) { +Iterable :: interface (T as t) { as_iterator(t) |> __iterable_test(); } diff --git a/core/container/map.onyx b/core/container/map.onyx index 5f62d2ef..72537f43 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -11,7 +11,7 @@ package core.map } #local { - ValidKey :: interface (t: $T) { + ValidKey :: interface (T as t) { // In order to use a certain type as a key in a Map, you must // provide an implementation of core.hash.to_u32() for that type, // and you must provide an operator overload for ==. diff --git a/core/container/set.onyx b/core/container/set.onyx index c758f0ad..dd429e14 100644 --- a/core/container/set.onyx +++ b/core/container/set.onyx @@ -7,7 +7,7 @@ package core.set math :: package core.math } -#local SetValue :: interface (t: $T) { +#local SetValue :: interface (T as t) { { hash.to_u32(t) } -> u32; { t == t } -> bool; } diff --git a/core/hash.onyx b/core/hash.onyx index c915f2cd..793b4bc9 100644 --- a/core/hash.onyx +++ b/core/hash.onyx @@ -14,6 +14,6 @@ to_u32 :: #match { (key: type_expr) -> u32 do return to_u32(cast(u32) key); } -Hashable :: interface (t: $T) { +Hashable :: interface (T as t) { { to_u32(t) } -> u32; } diff --git a/core/runtime/info/helper.onyx b/core/runtime/info/helper.onyx index a5e70d01..e5621f2f 100644 --- a/core/runtime/info/helper.onyx +++ b/core/runtime/info/helper.onyx @@ -302,12 +302,8 @@ get_struct_method :: (type: type_expr, method_name: str) -> ^any { info := cast(^Type_Info_Struct) get_type_info(type); if info.kind != .Struct do return null; - for ^method: info.methods { - if method.name == method_name { - return ^method.func; - } - } - + method := core.array.first(info.methods, #(it.name == method_name)); + if method != null do return ^method.func; return null; } diff --git a/core/stdio.onyx b/core/stdio.onyx index e9358c4d..4d98e95c 100644 --- a/core/stdio.onyx +++ b/core/stdio.onyx @@ -14,7 +14,7 @@ package core auto_flush_stdio := true -print :: #match { +print :: #match #locked { (x: str) { io.write(^stdio.print_writer, x); if x[x.count - 1] == #char "\n" && auto_flush_stdio do __flush_stdio(); diff --git a/examples/22_interfaces.onyx b/examples/22_interfaces.onyx index 1bf958af..7d5709d1 100644 --- a/examples/22_interfaces.onyx +++ b/examples/22_interfaces.onyx @@ -32,7 +32,7 @@ add_example :: () { // expressions do not type check correctly, then the set of parameters does not // satisfy the interface. - CanAdd :: interface (t: $T) { + CanAdd :: interface (T as t) { t + t; } @@ -59,7 +59,7 @@ add_example :: () { // These have to be defined out here because #operator and #match are only allowed // as top level expressions currently. -NumberLike :: interface (t: $T) { +NumberLike :: interface (T as t) { // The constraints here are a little stricter than before. This syntax // allows you to specify the expected type of the expression. If the type // of the expression and the type after the arrow do not match, then the @@ -101,7 +101,7 @@ overloaded_procedure_example :: () { // when the are combined with overloaded procedures. // Take this example. Here is the same CanAdd interface from before. - CanAdd :: interface (t: $T) { + CanAdd :: interface (T as t) { t + t; } diff --git a/include/astnodes.h b/include/astnodes.h index 0d4296fb..cdcf01ed 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -794,10 +794,17 @@ struct AstIfWhile { AstBlock *true_stmt; AstBlock *false_stmt; - // Used by Static_If - Scope *defined_in_scope; - bh_arr(struct Entity *) true_entities; - bh_arr(struct Entity *) false_entities; + union { + // Used by Static_If + struct { + Scope *defined_in_scope; + bh_arr(struct Entity *) true_entities; + bh_arr(struct Entity *) false_entities; + }; + + // Used by While + b32 bottom_test; + }; }; typedef struct AstIfWhile AstIf; typedef struct AstIfWhile AstWhile; @@ -1041,12 +1048,14 @@ struct AstOverloadedFunction { // the complete set of overloads that can be used by an overloaded // function. bh_imap all_overloads; + + b32 locked : 1; }; // @CLEANUP: Is this really necessary? typedef struct InterfaceParam { OnyxToken *value_token; - AstType *type_node; + OnyxToken *type_token; } InterfaceParam; typedef struct InterfaceConstraint { diff --git a/src/checker.c b/src/checker.c index ee31a9a8..99e7747f 100644 --- a/src/checker.c +++ b/src/checker.c @@ -220,7 +220,13 @@ CheckStatus check_while(AstIfWhile* whilenode) { } if (whilenode->true_stmt) CHECK(statement, (AstNode **) &whilenode->true_stmt); - if (whilenode->false_stmt) CHECK(statement, (AstNode **) &whilenode->false_stmt); + if (whilenode->false_stmt) { + if (whilenode->bottom_test) { + ERROR(whilenode->token->pos, "while-loops with an 'else' clause cannot be bottom tested."); + } + + CHECK(statement, (AstNode **) &whilenode->false_stmt); + } return Check_Success; } @@ -2750,13 +2756,12 @@ CheckStatus check_constraint(AstConstraint *constraint) { sentinel->token = ip->value_token; sentinel->type_node = constraint->type_args[i]; - assert(ip->type_node); AstAlias *type_alias = onyx_ast_node_new(context.ast_alloc, sizeof(AstAlias), Ast_Kind_Alias); - type_alias->token = ip->type_node->token; + type_alias->token = ip->type_token; type_alias->alias = (AstTyped *) constraint->type_args[i]; symbol_introduce(constraint->scope, ip->value_token, (AstNode *) sentinel); - symbol_introduce(constraint->scope, ip->type_node->token, (AstNode *) type_alias); + symbol_introduce(constraint->scope, ip->type_token, (AstNode *) type_alias); } assert(constraint->entity); diff --git a/src/parser.c b/src/parser.c index 1b658e75..2b22d7aa 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1111,6 +1111,10 @@ static AstIfWhile* parse_while_stmt(OnyxParser* parser) { AstIfWhile* while_node = make_node(AstIfWhile, Ast_Kind_While); while_node->token = while_token; + if (parse_possible_directive(parser, "bottom_test")) { + while_node->bottom_test = 1; + } + AstTyped* cond; AstNode* initialization_or_cond=NULL; b32 had_initialization = 0; @@ -2139,12 +2143,9 @@ static AstInterface* parse_interface(OnyxParser* parser) { if (parser->hit_unexpected_token) return interface; InterfaceParam ip; + ip.type_token = expect_token(parser, Token_Type_Symbol); + expect_token(parser, Token_Type_Keyword_As); ip.value_token = expect_token(parser, Token_Type_Symbol); - expect_token(parser, ':'); - expect_token(parser, '$'); - - OnyxToken* type_sym = expect_token(parser, Token_Type_Symbol); - ip.type_node = (AstType *) make_symbol(parser->allocator, type_sym); bh_arr_push(interface->params, ip); @@ -2343,11 +2344,17 @@ static void parse_function_params(OnyxParser* parser, AstFunction* func) { } static AstOverloadedFunction* parse_overloaded_function(OnyxParser* parser, OnyxToken* token) { + b32 locked = 0; + if (parse_possible_directive(parser, "locked")) { + locked = 1; + } + expect_token(parser, '{'); AstOverloadedFunction* ofunc = make_node(AstOverloadedFunction, Ast_Kind_Overloaded_Function); ofunc->token = token; ofunc->flags |= Ast_Flag_Comptime; + ofunc->locked = locked; bh_arr_new(global_heap_allocator, ofunc->overloads, 4); diff --git a/src/symres.c b/src/symres.c index ad71c40e..83e6da51 100644 --- a/src/symres.c +++ b/src/symres.c @@ -1355,14 +1355,19 @@ static SymresStatus symres_process_directive(AstNode* directive) { if (add_overload->overloaded_function == NULL) return Symres_Error; // NOTE: Error message will already be generated if (add_overload->overloaded_function->kind != Ast_Kind_Overloaded_Function) { - onyx_report_error(add_overload->token->pos, Error_Critical, "#add_overload directive did not resolve to an overloaded function."); + onyx_report_error(add_overload->token->pos, Error_Critical, "#match directive expects a matched procedure."); + return Symres_Error; + } - } else { - AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function; - SYMRES(expression, (AstTyped **) &add_overload->overload); - add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload); + AstOverloadedFunction* ofunc = (AstOverloadedFunction *) add_overload->overloaded_function; + if (ofunc->locked) { + onyx_report_error(add_overload->token->pos, Error_Critical, "Cannot add match option here as the original #match was declared as #locked."); + onyx_report_error(ofunc->token->pos, Error_Critical, "Here is the original #match."); + return Symres_Error; } + SYMRES(expression, (AstTyped **) &add_overload->overload); + add_overload_option(&ofunc->overloads, add_overload->precedence, add_overload->overload); break; } diff --git a/src/wasm_emit.c b/src/wasm_emit.c index 012f24f6..bfe22faf 100644 --- a/src/wasm_emit.c +++ b/src/wasm_emit.c @@ -824,14 +824,22 @@ EMIT_FUNC(while, AstIfWhile* while_node) { emit_enter_structured_block(mod, &code, SBT_Breakable_Block); emit_enter_structured_block(mod, &code, SBT_Continue_Loop); - emit_expression(mod, &code, while_node->cond); - WI(WI_I32_EQZ); - WID(WI_COND_JUMP, 0x01); + if (!while_node->bottom_test) { + emit_expression(mod, &code, while_node->cond); + WI(WI_I32_EQZ); + WID(WI_COND_JUMP, 0x01); + } emit_block(mod, &code, while_node->true_stmt, 0); - if (bh_arr_last(code).type != WI_JUMP) - WID(WI_JUMP, 0x00); + if (while_node->bottom_test) { + emit_expression(mod, &code, while_node->cond); + WID(WI_COND_JUMP, 0x00); + + } else { + if (bh_arr_last(code).type != WI_JUMP) + WID(WI_JUMP, 0x00); + } emit_leave_structured_block(mod, &code); emit_leave_structured_block(mod, &code); diff --git a/tests/complicated_polymorph.onyx b/tests/complicated_polymorph.onyx index 754edbc4..aaa9c558 100644 --- a/tests/complicated_polymorph.onyx +++ b/tests/complicated_polymorph.onyx @@ -14,7 +14,7 @@ main :: (args: [] cstr) { return g(f(a)); } - Number :: interface (t: $T) { + Number :: interface (T as t) { { t * t } -> T; } diff --git a/tests/interfaces.onyx b/tests/interfaces.onyx index f65fe987..6ac73902 100644 --- a/tests/interfaces.onyx +++ b/tests/interfaces.onyx @@ -2,7 +2,7 @@ use package core -Hashable :: interface (t: $T) { +Hashable :: interface (T as t) { { hash.to_u32(t) } -> u32; } @@ -16,7 +16,7 @@ try_hash :: #match { }, } -CanCastTo :: interface (t: $T, d: $D) { +CanCastTo :: interface (T as t, D as d) { { cast(D) t } -> D; } @@ -37,7 +37,7 @@ do_math :: macro (x, y: $T) -> T where SemiRing(T) { return x + y + x * y; } -SemiRing :: interface (t: $T) { +SemiRing :: interface (T as t) { {t + t} -> T; {t * t} -> T; } @@ -46,7 +46,7 @@ bit_math :: (x, y: $T) -> T where BitField(T) { return (x & y) | (x ^ y); } -BitField :: interface (t: $T) { +BitField :: interface (T as t) { { ~t } -> T; { t & t } -> T; { t | t } -> T;