added #locked; #bottom_test; changed interface syntax
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 8 Jun 2022 02:39:38 +0000 (21:39 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 8 Jun 2022 02:39:38 +0000 (21:39 -0500)
15 files changed:
core/container/array.onyx
core/container/iter.onyx
core/container/map.onyx
core/container/set.onyx
core/hash.onyx
core/runtime/info/helper.onyx
core/stdio.onyx
examples/22_interfaces.onyx
include/astnodes.h
src/checker.c
src/parser.c
src/symres.c
src/wasm_emit.c
tests/complicated_polymorph.onyx
tests/interfaces.onyx

index c22d75661022c5a122c3449ae54549f30b1e1bce..16e8f984232444fa6c1ada0f5ab7af5e7f87a587 100644 (file)
@@ -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;
 }
index 2ca0b67e11c336d6222228afcba43a2e9770c6b5..3dc54efa5b6212af8af92a017821bc8d63a664f3 100644 (file)
@@ -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();
 }
 
index 5f62d2ef99f0b8f88e93c237955441aebfba5a73..72537f43640567b9ff058c645765bfe0449e9b26 100644 (file)
@@ -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 ==.
index c758f0adab19460c933026c171d76574d4dbb717..dd429e14c3ec7a8e5b82fbb75b378201017fe032 100644 (file)
@@ -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;
 }
index c915f2cdf3288691916cbb87021a6731a42e4b2b..793b4bc9a595577fb30bd31cb05f13ca077c36b9 100644 (file)
@@ -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;
 }
index a5e70d01239a197b11583f3849625f9594ea6cfb..e5621f2ff35b00f5a95fc00bba79a45954f2e1f0 100644 (file)
@@ -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;
 }
 
index e9358c4d3cde146e458be3a2d5530dfd01b55ba3..4d98e95c77bf7e5802e192678f069b4c547a2f91 100644 (file)
@@ -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();
index 1bf958afecce30f727eeca6e1fabe167690d7296..7d5709d14c619c9c9dc5124f069ae88b8443f3b6 100644 (file)
@@ -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;
     }
 
index 0d4296fbe4f52fa95823d6a3727d8b06ae92f7b9..cdcf01ed9921b9f3176119d4beab9786549d1aaf 100644 (file)
@@ -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 {
index ee31a9a86ecefc95c960433be8c0a19e2b04aec5..99e7747febdcea49db26eef1d8139435c977c668 100644 (file)
@@ -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);
index 1b658e75fad5c2bb5728bc556b933d71365f21a8..2b22d7aabc8e025807334f9c55b7bb38f18ab864 100644 (file)
@@ -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);
 
index ad71c40eaae90d28a646e3b440cc6e148c0bbfc8..83e6da511ffcab751b8ea67ceadf72aac211f0ce 100644 (file)
@@ -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;
         }
 
index 012f24f6305da8c0a7746ee79b9bdf7875ccb7a0..bfe22faf7807ddc9aa2468923eadaa3a1b71f8f5 100644 (file)
@@ -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);
index 754edbc4de17ef3905dcfadeeb8db417aa0e29d9..aaa9c5583e9aadffde6a1e870afc41bce26dd98e 100644 (file)
@@ -14,7 +14,7 @@ main :: (args: [] cstr) {
         return g(f(a));
     }
 
-    Number :: interface (t: $T) {
+    Number :: interface (T as t) {
         { t * t } -> T;
     }
 
index f65fe987bb65b705fa8ec80f8d779106d8ce32d0..6ac739029d89ed945374801bc93fdef981ff031d 100644 (file)
@@ -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;