// 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 {
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);
}
}
+ //
+ // 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;
}
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];
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) {
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;
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,
// - 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);
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;
}
}
+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
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)) {
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);
// @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);
}
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);
}
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);
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);
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));
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;