bugfixes and `Result` is now a `union`
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 May 2023 03:22:09 +0000 (22:22 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 23 May 2023 03:22:09 +0000 (22:22 -0500)
compiler/src/astnodes.c
compiler/src/doc.c
compiler/src/polymorph.h
core/container/result.onyx
core/conv/format.onyx
core/os/file.onyx
tests/tagged_unions.onyx

index 01558e5c49fd4ba3ddcaa75271f56cc9b741575f..b832ea85cd6c9e53023ff5a69383da1d971bcd22 100644 (file)
@@ -1603,6 +1603,8 @@ AstStructLiteral* make_optional_literal_some(bh_allocator a, AstTyped *expr, Typ
 
     opt_lit->type = opt_type;
     opt_lit->args.values[0]->type = opt_type->Union.tag_type;
+
+    opt_lit->flags |= Ast_Flag_Has_Been_Checked;
     return opt_lit;
 }
 
index 23115947df3931f0c3234208455f62213d3442d2..742c1c59367656c1915316e203a13f94144631a5 100644 (file)
@@ -278,10 +278,18 @@ static void write_type_node(bh_buffer *buffer, void *vnode) {
             bh_buffer_write_string(buffer, ((AstStructType *) node)->name);
             return;
 
+        case Ast_Kind_Union_Type:
+            bh_buffer_write_string(buffer, ((AstUnionType *) node)->name);
+            return;
+
         case Ast_Kind_Poly_Struct_Type:
             bh_buffer_write_string(buffer, ((AstPolyStructType *) node)->name);
             return;
 
+        case Ast_Kind_Poly_Union_Type:
+            bh_buffer_write_string(buffer, ((AstPolyUnionType *) node)->name);
+            return;
+
         case Ast_Kind_Poly_Call_Type:
             if (((AstPolyCallType *) node)->callee == (AstType *) builtin_optional_type) {
                 bh_buffer_write_string(buffer, "? ");
index ca1f84e93c9cbd03b1c880b184cc28fa4c6858fc..0baea789ec92d13ce618a25efb80645f075f0ea3 100644 (file)
@@ -987,6 +987,18 @@ b32 potentially_convert_function_to_polyproc(AstFunction *func) {
                     break;
                 }
 
+                case Ast_Kind_Poly_Union_Type: {
+                    AutoPolymorphVariable apv;
+                    apv.idx = param_idx;
+                    apv.base_type = param->local->type_node;
+                    apv.variable_count = bh_arr_length(((AstPolyUnionType *) param_type)->poly_params);
+                    apv.replace = to_replace;
+
+                    bh_arr_push(auto_vars, apv);
+                    done = 1;
+                    break;
+                }
+
                 default: done = 1; break;
             }
         }
@@ -1013,7 +1025,8 @@ b32 potentially_convert_function_to_polyproc(AstFunction *func) {
 
         AstType *dealiased_base_type = (AstType *) strip_aliases((AstNode *) apv->base_type);
 
-        if (dealiased_base_type->kind == Ast_Kind_Poly_Struct_Type) {
+        if (dealiased_base_type->kind == Ast_Kind_Poly_Struct_Type
+            || dealiased_base_type->kind == Ast_Kind_Poly_Union_Type) {
             pp.type_expr = (AstType *) pcall;
         } else {
             pp.type_expr = apv->base_type;
index d3836b290fdee0545adea47d4af5f453c754e195..2ef516d498cb68453c60593372caa471619f09e6 100644 (file)
@@ -18,50 +18,49 @@ use core {Optional}
     of type T, or an Err value of type E. `status` contains either
     .Ok, or .Err depending on which is currently held.
 """
-@conv.Custom_Format.{ #solidify format {T=Ok_Type, E=Err_Type} }
-Result :: struct (Ok_Type: type_expr, Err_Type: type_expr) {
-    status: Result_Status;
-    __data: Result_Data(Ok_Type, Err_Type);
-}
-
-#doc """
-    The type of Result(T, E).status
-"""
-Result_Status :: enum {
-    Ok  :: 1;
-    Err :: 2;
-}
-
-#local
-Result_Data :: struct (T: type_expr, E: type_expr) {
-    value: T;
-    error: E;
+Result :: union (Ok_Type: type_expr, Err_Type: type_expr) {
+    Err: Err_Type;
+    Ok: Ok_Type;
 }
 
 
 #inject Result {
     #doc "Quick way to return an Ok from a procedure."
-    return_ok  :: macro (x: $T) do return .{ .Ok, .{ value = x } };
+    return_ok  :: macro (x: $T) do return .{ Ok = x };
 
     #doc "Quick way to return an Err from a procedure."
-    return_err :: macro (x: $T) do return .{ .Err, .{ error = x } };
+    return_err :: macro (x: $T) do return .{ Err = x };
 
     #doc "Returns true if the result contains an Ok value."
-    is_ok :: (r: #Self) => r.status == .Ok;
+    is_ok :: (r: #Self) {
+        switch r {
+            case .Ok do return true;
+            case #default do return false;
+        }
+    }
 
     #doc "Returns true if the result contains an Err value."
-    is_err :: (r: #Self) => !r.status == .Err;
+    is_err :: (r: #Self) {
+        switch r {
+            case .Err do return true;
+            case #default do return false;
+        }
+    }
 
     #doc "Returns an Optional of the Ok type."
     ok :: (r: #Self) -> Optional(r.Ok_Type) {
-        if r.status == .Ok do return Optional.make(r.__data.value);
-        return .{};
+        switch r {
+            case .Ok => v do return v;
+            case #default do return .{};
+        }
     }
 
     #doc "Returns an Optional of the Err type."
     err :: (r: #Self) -> Optional(r.Err_Type) {
-        if r.status == .Err do return Optional.make(r.__data.error);
-        return .{};
+        switch r {
+            case .Err => v do return v;
+            case #default do return .{};
+        }
     }
 
     #doc """
@@ -69,13 +68,14 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
         result contains an Err, an assertion is thrown.
     """
     unwrap :: (r: #Self) -> r.Ok_Type {
-        if r.status == .Err {
-            msg := tprintf("Unwrapping Result with error '{}'.", r.__data.error);
-            assert(false, msg);
-            return .{};
+        switch r {
+            case .Ok => v do return v;
+            case #default {
+                msg := tprintf("Unwrapping Result with error '{}'.", r.__data.error);
+                assert(false, msg);
+                return .{};
+            }
         }
-
-        return r.__data.value;
     }
 
     #doc """
@@ -83,9 +83,10 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
         result contains an Err, the empty .{} value is returned.
     """
     unwrap_or_default :: (r: #Self) -> r.Ok_Type {
-        if r.status == .Err do return .{};
-
-        return r.__data.value;
+        switch r {
+            case .Ok => v do return v;
+            case #default do return .{};
+        }
     }
 
     #doc """
@@ -93,12 +94,12 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
         result contains an Err, a custom assertion message is thrown.
     """
     expect :: (r: #Self, msg: str) -> r.Ok_Type {
-        if r.status == .Err {
-            assert(false, msg);
-            return .{};
+        switch r {
+            case .Ok => v do return v;
+            case #default {
+                assert(false, msg);
+            }
         }
-
-        return r.__data.value;
     }
 
     #doc """
@@ -107,20 +108,26 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
             Err(e) => Err(e)
     """
     transform :: (r: Result($T, $E), f: (T) -> $R) -> Result(R, E) {
-        if r.status == .Err do return .{ .Err, .{ error = r.error } };
-        return .{ .Ok, .{ value = f(r.__data.value) } };
+        switch r {
+            case .Ok => v  do return .{ Ok = f(v) };
+            case .Err => v do return .{ Err = v };
+        }
     }
 
     #doc "Monadic chaining operation."
     and_then :: (r: #Self, f: (r.Ok_Type) -> Result($R, r.Err_Type)) -> Result(R, r.Err_Type) {
-        if r.status == .Err do return r;
-        return .{ .Ok, .{ value = f(r.__data.value) } };
+        switch r {
+            case .Ok => v  do return f(v);
+            case .Err => v do return .{ Err = v };
+        }
     }
 
     #doc "If the Result contains Err, generate is called to make a value"
     or_else :: (r: #Self, generate: () -> typeof r) => {
-        if r.status == .Ok do return r;
-        return .{};
+        switch r {
+            case .Ok => v do return v;
+            case #default do return generate();
+        }
     }
 
     #doc """
@@ -140,10 +147,10 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
             }
     """
     forward_err :: macro (r: Result($T, $E)) -> T {
-        res := r;
-        if res.status == .Ok do return res.__data.value;
-
-        return return .{ .Err, .{ error = res.__data.error } };
+        switch res := r; res {
+            case .Ok => v  do return v;
+            case .Err => v do return return .{ Err = v };
+        }
     }
 
     #doc """
@@ -151,10 +158,10 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
         enclosing procedure. Otherwise, the Ok value is returned.
     """
     or_return :: macro (r: Result($T, $E), v: $V) -> T {
-        res := r;
-        if res.status == .Ok do return res.__data.value;
-        
-        return return v;
+        switch res := r; res {
+            case .Ok => v  do return v;
+            case .Err do return return v;
+        }
     }
 
     #doc """
@@ -166,37 +173,31 @@ Result_Data :: struct (T: type_expr, E: type_expr) {
         This procedure is subject to change.
     """
     catch :: macro (r: Result($T, $E), on_err: Code) -> T {
-        res := r;
-        if res.status == .Ok do return res.__data.value;
-
-        #unquote on_err;
-    }
-
-    format :: (o: &conv.Format_Output, format: &conv.Format, res: &Result($T, $E)) {
-        if res.status == .Ok {
-            conv.format(o, "{}({\"})", res.status, res.__data.value);
-        } else {
-            conv.format(o, "{}({\"})", res.status, res.__data.error);
+        switch res := r; res {
+            case .Ok => v  do return v;
+            case .Err {
+                #unquote on_err;
+            }
         }
     }
 }
 
 #overload
-__implicit_bool_cast :: macro (r: Result($O, $E)) => r.status == .Ok;
+__implicit_bool_cast :: macro (r: Result($O, $E)) => cast(Result(O, E).tag_enum, r) == .Ok;
 
 
 #operator ? macro (r: Result($T, $E)) -> T {
-    res := r;
-    if r.status == .Ok do return r.__data.value;
-
-    return return .{ .Err, .{ error = res.__data.error } };
+    switch res := r; res {
+        case .Ok => v  do return v;
+        case .Err => v do return return .{ Err = v };
+    }
 }
 
 #operator ?? macro (r: Result($T, $E), v: T) -> T {
-    r_ = r;
-    if r_.status == .Ok do return r_.__data.value;
-
-    return v;
+    switch res := r; res {
+        case .Ok => v do return v;
+        case .Err do return v;
+    }
 }
 
 
index f16b97c961249ded89a4edf9d7bda05b095ebbd9..eb15333b0cba98071e0c21c92473259c117585ef 100644 (file)
@@ -739,7 +739,9 @@ format_any :: (output: &Format_Output, formatting: &Format, v: any) {
                 output->write(variant.name);
                 if variant.type != void {
                     output->write("(");
-                    format_any(output, formatting, any.{ cast([&] u8) v.data + u.alignment, variant.type });
+                    format := *formatting;
+                    format.quote_strings = true;
+                    format_any(output, &format, any.{ cast([&] u8) v.data + u.alignment, variant.type });
                     output->write(")");
                 }
             }
index db2077ea01adb69da7049c55e1d967081da609e7..c0e694e43b7870e0fd6be0fc8182371258fa7e87 100644 (file)
@@ -79,11 +79,11 @@ open :: (path: str, mode := OpenMode.Read) -> Result(File, os.FileError) {
     };
 
     file_data, error := fs.__file_open(path, mode);
-    if error != .None do return .{ .Err, .{error=error} };
+    if error != .None do return .{ Err = error };
 
     file.data = file_data;
     file.vtable = &fs.__file_stream_vtable;
-    return .{ .Ok, .{value=file} };
+    return .{ Ok = file };
 }
 
 close :: (file: &File) {
@@ -158,10 +158,10 @@ file_logger_open :: (filename: str, allocator := context.allocator) -> Result(&F
         raw_free(allocator, file_logger.file);
         raw_free(allocator, file_logger);
 
-        return return .{ .Err, .{error = "Unable to open file for logging."} };
+        return return .{ Err = "Unable to open file for logging." };
     });
 
-    return .{ .Ok, .{ value=file_logger } };
+    return .{ Ok = file_logger };
 }
 
 file_logger_use :: (logger: &File_Logger) {
index 50917f1edc577fa46162fc71650568d5ea0963b3..92b03cc90eb97fa271d6bb3cebfd481de11ee88f 100644 (file)
@@ -5,13 +5,21 @@ create_optional :: () -> ? i32 {
     return .{ Some = 123 };
 }
 
-new_optional_test :: () {
+create_result :: () -> Result(i32, str) {
+    return .{ Ok = 987 };
+    // return .{ Err = "oh no there was error :(" };
+}
+
+new_optional_test :: () -> Result(void, str) {
     v := create_optional();
-    v2 := Optional(str).{ None = .{} };
+    v2: ?str = "asdf";
     println(v);
     println(v2);
 
     println(v->unwrap());
+
+    r := create_result()?;
+    println(r);
 }
 
 union_is :: macro (u: $U, $variant: U.tag_enum) -> bool {
@@ -87,7 +95,7 @@ simple_test :: () {
     println(u);
 }
 
-main :: () {simple_test(); link_test(); new_optional_test();}
+main :: () {simple_test(); link_test(); new_optional_test()->err()? |> println();}
 
 Link :: union {
     End: void;
@@ -97,15 +105,15 @@ Link :: union {
     }
 }
 
-print_links :: (l: Link) {
+print_links :: (l: &Link) {
     walker := l;
     while true {
-        switch walker {
+        switch *walker {
             case .End do break break;
 
             case .Next => &next {
                 printf("{}\n", next.data);
-                walker = *next.next;
+                walker = next.next;
             }
         }
     }
@@ -124,7 +132,7 @@ link_test :: () {
         }
     };
     
-    print_links(l);
+    print_links(&l);
 }
 
 // main :: () { link_test(); }