gave a face-lift to the interface semantics
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 23 Sep 2022 02:53:21 +0000 (21:53 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 23 Sep 2022 02:53:21 +0000 (21:53 -0500)
compiler/include/astnodes.h
compiler/include/types.h
compiler/src/astnodes.c
compiler/src/checker.c
compiler/src/parser.c
compiler/src/polymorph.h
compiler/src/types.c
core/container/iter.onyx

index c0cfc53f90cf51b59e50815f6fc4db3fa674b114..2d383918659b0c3f52e64691f77a4d5725a680ac 100644 (file)
@@ -1179,6 +1179,9 @@ struct AstPolyParam {
 
     // Used for baked values. The expected type of the parameter.
     Type* type;
+
+    // Used to store interface specified with $T/Interface.
+    AstNode *implicit_interface;
 };
 
 struct AstPolySolution {
index 5c2b5e69784d0e461da849fde23471e1c74a2c64..fadbc91588919eed7a05a221ab43883a188e2efe 100644 (file)
@@ -192,6 +192,7 @@ struct AstStructLiteral;
 
 void types_init();
 void types_dump_type_info();
+Type* type_lookup_by_id(u32 id);
 
 b32 types_are_compatible(Type* t1, Type* t2);
 u32 type_size_of(Type* type);
index c7744d779e4b9d77aa138c132d935f019a501215..dedddf47c568d42ca1267b5634be47cd1e664ed5 100644 (file)
@@ -802,6 +802,31 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) {
         }
     }
 
+    //
+    // This is a SUPER RARE case. It is used when checking to
+    // see if the return value from a interface constraint is
+    // an instance of a type generic polymorphic structure.
+    // For example, converting to an iterator can be tested with:
+    //
+    //       IsIterable :: interface (t: $T) {
+    //           { t->AsIterator() } -> Iterator;
+    //       }
+    //
+    // Here, Iterator is a polymorphic type, and would normally
+    // not be allowed in any other expression. However, check_constraint
+    // has a special check to see if the above happens, and when
+    // it does, this case allows the types to pass correctly.
+    // Note, this should be the ONLY time this happens. All
+    // other uses of checking polymorphic structures against
+    // actual nodes strictly forbidden.
+    else if (type->kind == Type_Kind_PolyStruct) {
+        if (node_type->kind == Type_Kind_Struct) {
+            if (node_type->Struct.constructed_from->type_id == type->id) {
+                return TYPE_MATCH_SUCCESS;
+            }
+        }
+    }
+
     return TYPE_MATCH_FAILED;
 }
 
index df26f20eeda8e4094c755d24780163ef2df8dc37..c6ea3ae90d0b1014b22ca9100f6f6c62df0a49f2 100644 (file)
@@ -2811,6 +2811,12 @@ CheckStatus check_constraint(AstConstraint *constraint) {
 
             constraint->scope = scope_create(context.ast_alloc, constraint->interface->entity->scope, constraint->token->pos);
 
+            if (bh_arr_length(constraint->type_args) != bh_arr_length(constraint->interface->params)) {
+                ERROR_(constraint->token->pos, "Wrong number of arguments given to interface. Expected %d, got %d.",
+                    bh_arr_length(constraint->interface->params),
+                    bh_arr_length(constraint->type_args));
+            }
+
             fori (i, 0, bh_arr_length(constraint->interface->params)) {
                 InterfaceParam *ip = &constraint->interface->params[i];
 
@@ -2858,7 +2864,28 @@ CheckStatus check_constraint(AstConstraint *constraint) {
 
                     ic->expected_type = type_build_from_ast(context.ast_alloc, ic->expected_type_expr);
                     if (ic->expected_type == NULL) {
-                        YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
+                        //
+                        // To make interfaces easier to use, I wanted to have
+                        // the ability to easily specify the return type as a
+                        // polymorphic structure, without needing to support
+                        // some crazy syntax like "-> Iterator($V)". For this
+                        // reason, I allow you to say "-> Iterator" in the
+                        // expected return type. Normally type_build_from_ast
+                        // does not return an instance of a polymorphic structure.
+                        // This prevents needing to handle that case across
+                        // the entire compiler. HOWEVER, in this case I actually
+                        // do want to get the polymorphic structure type if
+                        // it was one. So, I check here to see if the expected_type_expr
+                        // node has a type_id assigned. If it does, then there
+                        // is a chance it is a polymorphic struct. This bypasses
+                        // the usual code and looks up the type directly, from
+                        // then it will be used in the TYPE_CHECK below.
+                        if (ic->expected_type_expr->type_id) {
+                            ic->expected_type = type_lookup_by_id(ic->expected_type_expr->type_id);
+
+                        } else {
+                            YIELD(ic->expected_type_expr->token->pos, "Waiting on expected type expression to be resolved.");
+                        }
                     }
 
                     TYPE_CHECK(&ic->expr, ic->expected_type) {
index 6ff1aaa45fefd08015a7ed935669e01757c297cb..30da360e244110f0bab72f493c47bad45374338a 100644 (file)
@@ -1685,6 +1685,11 @@ static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_inser
     AstNode* symbol_node = make_node(AstNode, Ast_Kind_Symbol);
     symbol_node->token = expect_token(parser, Token_Type_Symbol);
 
+    AstNode *implicit_interface = NULL;
+    if (consume_token_if_next(parser, '/')) {
+        implicit_interface = (AstNode *) parse_factor(parser);
+    }
+
     **next_insertion = (AstType *) symbol_node;
     *next_insertion = NULL;
 
@@ -1692,6 +1697,7 @@ static void parse_polymorphic_variable(OnyxParser* parser, AstType*** next_inser
         bh_arr_push(pv, ((AstPolyParam) {
             .kind     = PPK_Poly_Type,
             .poly_sym = symbol_node,
+            .implicit_interface = implicit_interface,
 
             // These will be filled out by function_params()
             .type_expr = NULL,
index b62f45afd60568c3e4954e73641ef59d3b780312..ae79d30bd23eeb44254b5a1291ccbc748b6e7621 100644 (file)
@@ -175,6 +175,17 @@ static AstSolidifiedFunction generate_solidified_function(
     //                                             - brendanfh 2021/01/18
     u32 removed_params = 0;
     bh_arr_each(AstPolyParam, param, pp->poly_params) {
+        if (param->implicit_interface) {
+            AstConstraint *constraint = onyx_ast_node_new(context.ast_alloc, sizeof(AstConstraint), Ast_Kind_Constraint);
+            constraint->interface = (AstInterface *) param->implicit_interface;
+            constraint->token = constraint->interface->token;
+
+            bh_arr_new(global_heap_allocator, constraint->type_args, 1);
+            bh_arr_push(constraint->type_args, (AstType *) ast_clone(context.ast_alloc, param->poly_sym));
+
+            bh_arr_push(solidified_func.func->constraints.constraints, constraint);
+        }
+
         if (param->kind != PPK_Baked_Value) continue;
 
         bh_arr_deleten(solidified_func.func->params, param->idx - removed_params, 1);
@@ -956,6 +967,7 @@ b32 potentially_convert_function_to_polyproc(AstFunction *func) {
         AstPolyParam pp;
         pp.idx = apv->idx;
         pp.kind = PPK_Poly_Type;
+        pp.implicit_interface = NULL;
 
         AstPolyCallType* pcall = onyx_ast_node_new(context.ast_alloc, sizeof(AstPolyCallType), Ast_Kind_Poly_Call_Type);
         pcall->callee = *apv->replace;
index 0d9568a02f703d1e3fe790107fc4d599f52e0af8..23ff6d4df8e2599067d80f0e4ff8c1e8453d1368 100644 (file)
@@ -80,6 +80,14 @@ void types_dump_type_info() {
     }
 }
 
+Type* type_lookup_by_id(u32 id) {
+    if (bh_imap_has(&type_map, id)) {
+        return (Type *) bh_imap_get(&type_map, id);
+    }
+
+    return NULL;
+}
+
 b32 types_are_compatible_(Type* t1, Type* t2, b32 recurse_pointers) {
     // NOTE: If they are pointing to the same thing,
     // it is safe to assume they are the same type
index 1ed4b11207eeca5b821cc61a01627ab38225fce8..3c325a8281e2b15a94a80305729b8f48cdffcd7c 100644 (file)
@@ -28,9 +28,8 @@ package core.iter
 
 as_iterator :: #match {}
 
-#local __iterable_test :: macro (i: Iterator($V)) {}
 Iterable :: interface (t: $T) {
-    as_iterator(t) |> __iterable_test();
+    { as_iterator(t) } -> Iterator;
 }
 
 close :: (it: Iterator($T)) {
@@ -413,7 +412,7 @@ const :: (value: $T) -> Iterator(T) {
 enumerate :: #match #local {}
 
 #overload
-enumerate :: macro (it: $T, start_index: i32 = 0) -> #auto where Iterable(T) {
+enumerate :: macro (it: $T/Iterable, start_index: i32 = 0) => {
     as_iterator :: as_iterator
     enumerate :: enumerate
     return enumerate(as_iterator(it), start_index);
@@ -595,7 +594,7 @@ fold :: #match #local {}
 
 // @Cleanup // some way to shorten this would be nice
 #overload
-fold :: macro (it: $T, init: $R, combine: $S) -> #auto where Iterable(T) {
+fold :: macro (it: $T/Iterable, init: $R, combine: $S) => {
     fold :: fold
     return fold(as_iterator(it), init, combine);
 }
@@ -612,7 +611,7 @@ fold :: (it: Iterator($T), initial_value: $R, combine: (T, R) -> R) -> R {
 count :: #match #local {}
 
 #overload
-count :: macro (it: $T, cond: $F) -> #auto where Iterable(T) {
+count :: macro (it: $T/Iterable, cond: $F) => {
     count :: count
     return count(as_iterator(it), cond);
 }
@@ -627,7 +626,7 @@ count :: (it: Iterator($T), cond: (T) -> bool) -> i32 {
 some :: #match #local {}
 
 #overload
-some :: macro (it: $T, cond: $F) -> #auto where Iterable(T) {
+some :: macro (it: $T/Iterable, cond: $F) => {
     some :: some
     as_iterator :: as_iterator
     return some(as_iterator(it), cond);
@@ -642,7 +641,7 @@ some :: (it: Iterator($T), cond: (T) -> bool) -> bool {
 every :: #match #local {}
 
 #overload
-every :: macro (it: $T, cond: $F) -> #auto where Iterable(T) {
+every :: macro (it: $T/Iterable, cond: $F) => {
     every :: every
     as_iterator :: as_iterator
     return every(as_iterator(it), cond);
@@ -670,7 +669,7 @@ to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T {
     distributor :: #match #local {}
 
     #overload
-    distributor :: macro (it: $T) -> #auto where Iterable(T) {
+    distributor :: macro (it: $T/Iterable) => {
         distributor :: distributor;
         as_iterator :: as_iterator;
         return distributor(as_iterator(it));
@@ -716,7 +715,7 @@ to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T {
     parallel_for :: #match #local {}
 
     #overload
-    parallel_for :: macro (iterable: $I, thread_count: u32, thread_data: ^$Ctx, body: Code) where Iterable(I) {
+    parallel_for :: macro (iterable: $I/Iterable, thread_count: u32, thread_data: ^$Ctx, body: Code) {
         parallel_for :: parallel_for;
         as_iterator  :: as_iterator;