From: Brendan Hansen Date: Fri, 23 Sep 2022 02:53:21 +0000 (-0500) Subject: gave a face-lift to the interface semantics X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b2b34a1ee257313ba438030b0bbafd39aa6ab933;p=onyx.git gave a face-lift to the interface semantics --- diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index c0cfc53f..2d383918 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -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 { diff --git a/compiler/include/types.h b/compiler/include/types.h index 5c2b5e69..fadbc915 100644 --- a/compiler/include/types.h +++ b/compiler/include/types.h @@ -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); diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index c7744d77..dedddf47 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -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; } diff --git a/compiler/src/checker.c b/compiler/src/checker.c index df26f20e..c6ea3ae9 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -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) { diff --git a/compiler/src/parser.c b/compiler/src/parser.c index 6ff1aaa4..30da360e 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -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, diff --git a/compiler/src/polymorph.h b/compiler/src/polymorph.h index b62f45af..ae79d30b 100644 --- a/compiler/src/polymorph.h +++ b/compiler/src/polymorph.h @@ -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; diff --git a/compiler/src/types.c b/compiler/src/types.c index 0d9568a0..23ff6d4d 100644 --- a/compiler/src/types.c +++ b/compiler/src/types.c @@ -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 diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 1ed4b112..3c325a82 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -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;