added: implicit conversion to optional where appropriate
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 11 Mar 2023 03:25:36 +0000 (21:25 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 11 Mar 2023 03:25:36 +0000 (21:25 -0600)
compiler/include/astnodes.h
compiler/src/astnodes.c
tests/first_class_optional [new file with mode: 0644]
tests/first_class_optional.onyx [new file with mode: 0644]

index 778dd0a28a6640579f9962fe9851751656426e49..1972298850ffa13511f9698fd5646d5b72be70cd 100644 (file)
@@ -1783,19 +1783,20 @@ TypeMatch implicit_cast_to_bool(AstTyped **pnode);
 
 AstNode* strip_aliases(AstNode* node);
 
-AstNumLit*       make_bool_literal(bh_allocator, b32 b);
-AstNumLit*       make_int_literal(bh_allocator a, i64 value);
-AstNumLit*       make_float_literal(bh_allocator a, f64 value);
-AstRangeLiteral* make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
-AstBinaryOp*     make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
-AstArgument*     make_argument(bh_allocator a, AstTyped* value);
-AstFieldAccess*  make_field_access(bh_allocator a, AstTyped* node, char* field);
-AstAddressOf*    make_address_of(bh_allocator a, AstTyped* node);
-AstLocal*        make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
-AstLocal*        make_local_with_type(bh_allocator a, OnyxToken* token, Type* type);
-AstNode*         make_symbol(bh_allocator a, OnyxToken* sym);
-AstUnaryOp*      make_cast(bh_allocator a, AstTyped* expr, Type* to);
-AstZeroValue*    make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
+AstNumLit*        make_bool_literal(bh_allocator, b32 b);
+AstNumLit*        make_int_literal(bh_allocator a, i64 value);
+AstNumLit*        make_float_literal(bh_allocator a, f64 value);
+AstRangeLiteral*  make_range_literal(bh_allocator a, AstTyped* low, AstTyped* high);
+AstBinaryOp*      make_binary_op(bh_allocator a, BinaryOp operation, AstTyped* left, AstTyped* right);
+AstArgument*      make_argument(bh_allocator a, AstTyped* value);
+AstFieldAccess*   make_field_access(bh_allocator a, AstTyped* node, char* field);
+AstAddressOf*     make_address_of(bh_allocator a, AstTyped* node);
+AstLocal*         make_local(bh_allocator a, OnyxToken* token, AstType* type_node);
+AstLocal*         make_local_with_type(bh_allocator a, OnyxToken* token, Type* type);
+AstNode*          make_symbol(bh_allocator a, OnyxToken* sym);
+AstUnaryOp*       make_cast(bh_allocator a, AstTyped* expr, Type* to);
+AstZeroValue*     make_zero_value(bh_allocator a, OnyxToken *token, Type* type);
+AstStructLiteral* make_optional_literal_some(bh_allocator a, AstTyped *expr, Type* opt_type);
 
 void arguments_initialize(Arguments* args);
 b32 fill_in_arguments(Arguments* args, AstNode* provider, char** err_msg, b32 insert_zero_values);
index 8cc2e2ccc78da561c1b102a6dca7a6d4680f91c4..38f01ce5c8ca33a4db9b97ea8eb39e6fdf02670a 100644 (file)
@@ -751,6 +751,20 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         return legal ? TYPE_MATCH_SUCCESS : TYPE_MATCH_FAILED;
     }
 
+    // If the destination type is an optional, and the node's type is a value of
+    // the same underlying type, then we can construct an optional with a value
+    // implicitly. This makes working with optionals barable.
+    else if (type_struct_constructed_from_poly_struct(type, builtin_optional_type)) {
+        TypeMatch match = unify_node_and_type_(pnode, type->Struct.poly_sln[0].type, permanent);
+        if (match != TYPE_MATCH_SUCCESS) return match;
+
+        AstStructLiteral *opt_lit = make_optional_literal_some(context.ast_alloc, node, type);
+
+        *(AstStructLiteral **) pnode = opt_lit;
+        return TYPE_MATCH_SUCCESS;
+    }
+
+
     // If the node is a numeric literal, try to convert it to the destination type.
     else if (node->kind == Ast_Kind_NumLit) {
         if (convert_numlit_to_type((AstNumLit *) node, type)) return TYPE_MATCH_SUCCESS;
@@ -1430,6 +1444,7 @@ AstNumLit* make_bool_literal(bh_allocator a, b32 b) {
     AstNumLit* bl = onyx_ast_node_new(a, sizeof(AstNumLit), Ast_Kind_NumLit);
     bl->flags |= Ast_Flag_Comptime;
     bl->type_node = (AstType *) &basic_type_bool;
+    bl->type = &basic_types[Basic_Kind_Bool];
 
     bl->value.i = b ? 1 : 0;
     return bl;
@@ -1539,6 +1554,21 @@ AstZeroValue* make_zero_value(bh_allocator a, OnyxToken* token, Type* type) {
     return zero_value;
 }
 
+AstStructLiteral* make_optional_literal_some(bh_allocator a, AstTyped *expr, Type *opt_type) {
+    AstStructLiteral *opt_lit = onyx_ast_node_new(a, sizeof(AstStructLiteral), Ast_Kind_Struct_Literal);
+    opt_lit->token = expr->token;
+
+    arguments_initialize(&opt_lit->args);
+    arguments_ensure_length(&opt_lit->args, 2);
+    opt_lit->args.values[0] = make_bool_literal(a, 1);
+    opt_lit->args.values[1] = expr;
+
+    opt_lit->type = opt_type;
+    return opt_lit;
+}
+
+
+
 void arguments_initialize(Arguments* args) {
     if (args->values == NULL)       bh_arr_new(global_heap_allocator, args->values, 2);
     if (args->named_values == NULL) bh_arr_new(global_heap_allocator, args->named_values, 2);
diff --git a/tests/first_class_optional b/tests/first_class_optional
new file mode 100644 (file)
index 0000000..ec6955c
--- /dev/null
@@ -0,0 +1,6 @@
+0
+30
+None
+Some(40)
+Some(0)
+Some(60)
diff --git a/tests/first_class_optional.onyx b/tests/first_class_optional.onyx
new file mode 100644 (file)
index 0000000..1fc21d4
--- /dev/null
@@ -0,0 +1,20 @@
+use core
+
+foo :: (x: ?i32) -> i32 {
+    return x? + 10;
+}
+
+bar :: (x: ?i32) -> ?i32 {
+    return x? * 2;
+}
+
+main :: () {
+    println(foo(.{}));
+    println(foo(20));
+
+    println(bar(.{}));
+    println(bar(20));
+
+    println(bar(foo(.{})));
+    println(bar(foo(20)));
+}
\ No newline at end of file