use package core.array as array
use package core.string as string
-Map :: struct ($K, $V) {
+Map :: struct (K: type_expr, V: type_expr) {
hashes : [..] i32;
entries : [..] MapEntry(K, V);
default_value : V;
}
-MapEntry :: struct ($K, $T) {
+MapEntry :: struct (K: type_expr, V: type_expr) {
next : i32;
key : K;
- value : T;
+ value : V;
}
init :: proc (use map: ^Map($K, $V), dv: V = ~~0, hash_count: i32 = 16) {
typedef struct AstStructType AstStructType;
typedef struct AstStructMember AstStructMember;
typedef struct AstPolyStructType AstPolyStructType;
+typedef struct AstPolyStructParam AstPolyStructParam;
typedef struct AstPolyCallType AstPolyCallType;
typedef struct AstEnumType AstEnumType;
typedef struct AstEnumValue AstEnumValue;
AstTyped_base;
AstTyped* initial_value;
};
+struct AstPolyStructParam {
+ AstTyped_base;
+};
struct AstPolyStructType {
AstType_base;
Scope *scope;
- bh_arr(OnyxToken *) poly_params;
+ bh_arr(AstPolyStructParam) poly_params;
bh_table(AstStructType *) concrete_structs;
AstStructType* base_struct;
AstType_base;
AstType* callee;
- bh_arr(AstType *) params;
+
+ // NOTE: These nodes can be either AstTypes, or AstTyped expressions.
+ bh_arr(AstNode *) params;
};
struct AstEnumType {
AstType_base;
};
typedef enum PolySolutionKind {
+ PSK_Undefined,
PSK_Type,
PSK_Value,
} PolySolutionKind;
extern AstBasicType basic_type_int_unsized;
extern AstBasicType basic_type_float_unsized;
+// :TypeExprHack
+extern AstNode type_expr_symbol;
+
extern AstNode builtin_package_node;
extern AstNumLit builtin_heap_start;
extern AstGlobal builtin_stack_top;
AstNode* polymorphic_proc_try_solidify(AstPolyProc* pp, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos);
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos);
// NOTE: Useful inlined functions
static inline b32 is_lval(AstNode* node) {
u16 alignment, mem_count; \
bh_table(StructMember) members; \
bh_arr(StructMember *) memarr; \
- bh_arr(Type *) poly_args; \
+ bh_arr(struct AstPolySolution) poly_sln; \
}) \
TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
TYPE_KIND(Slice, struct { Type *ptr_to_data; }) \
AstNumLit builtin_heap_start = { Ast_Kind_NumLit, Ast_Flag_Const, &builtin_heap_start_token, NULL, (AstType *) &basic_type_rawptr, NULL, 0 };
AstGlobal builtin_stack_top = { Ast_Kind_Global, Ast_Flag_Const | Ast_Flag_Global_Stack_Top, &builtin_stack_top_token, NULL, (AstType *) &basic_type_rawptr, NULL };
+// :TypeExprHack
+static OnyxToken type_expr_token = { Token_Type_Symbol, 9, "type_expr", { 0 } };
+AstNode type_expr_symbol = { Ast_Kind_Basic_Type, 0, &type_expr_token, NULL };
+
AstType *builtin_string_type;
AstType *builtin_range_type;
Type *builtin_range_type_type;
{ NULL, "f32", (AstNode *) &basic_type_f32 },
{ NULL, "f64", (AstNode *) &basic_type_f64 },
{ NULL, "rawptr", (AstNode *) &basic_type_rawptr },
+ { NULL, "type_expr", (AstNode *) &type_expr_symbol },
{ "simd", "i8x16", (AstNode *) &basic_type_i8x16 },
{ "simd", "i16x8", (AstNode *) &basic_type_i16x8 },
CheckStatus check_memres_type(AstMemRes* memres);
CheckStatus check_memres(AstMemRes* memres);
+static inline void fill_in_type(AstTyped* node);
+
static inline void fill_in_array_count(AstType* type_node) {
if (type_node == NULL) return;
}
}
+static inline void fill_in_poly_call_args(AstType* type_node) {
+ if (type_node == NULL) return;
+ if (type_node->kind != Ast_Kind_Poly_Call_Type) return;
+
+ AstPolyCallType* pctype = (AstPolyCallType *) type_node;
+
+ bh_arr_each(AstNode *, param, pctype->params) {
+ if (!node_is_type(*param)) {
+ check_expression((AstTyped **) param);
+ resolve_expression_type((AstTyped *) *param);
+ fill_in_type((AstTyped *) *param);
+ }
+ }
+}
+
static inline void fill_in_type(AstTyped* node) {
fill_in_array_count(node->type_node);
+ fill_in_poly_call_args(node->type_node);
if (node->type == NULL)
node->type = type_build_from_ast(semstate.allocator, node->type_node);
token_toggle_end((*smem)->token);
if (s.included_through_use) {
- onyx_report_error((*smem)->token->pos, "Cannot specify value for member '%s', whic was included through a 'use' statement.", s.name);
+ onyx_report_error((*smem)->token->pos, "Cannot specify value for member '%s', which was included through a 'use' statement.", s.name);
return Check_Error;
}
u32 idx = (*smem)->idx;
if (sl->values[idx] == NULL) {
- if ((*smem)->initial_value == NULL) {
+ if (*(*smem)->initial_value == NULL) {
onyx_report_error(sl->token->pos, "No value was given for the field '%s'.", (*smem)->name);
return Check_Error;
}
pcd->params = NULL;
bh_arr_new(global_heap_allocator, pcd->params, bh_arr_length(pcs->params));
- bh_arr_each(AstType *, param, pcs->params) {
- bh_arr_push(pcd->params, (AstType *) ast_clone(a, *param));
+ bh_arr_each(AstNode *, param, pcs->params) {
+ bh_arr_push(pcd->params, ast_clone(a, *param));
}
break;
if (parser->curr->type == '(') {
OnyxToken* paren_token = expect_token(parser, '(');
- bh_arr(AstType *) params = NULL;
+ bh_arr(AstNode *) params = NULL;
bh_arr_new(global_heap_allocator, params, 2);
while (parser->curr->type != ')') {
if (parser->hit_unexpected_token) break;
- AstType* t = parse_type(parser);
+ AstNode* t = (AstNode *) parse_type(parser);
bh_arr_push(params, t);
if (parser->curr->type != ')')
if (parser->curr->type == '(') {
consume_token(parser);
- bh_arr(OnyxToken *) poly_params = NULL;
+ bh_arr(AstPolyStructParam) poly_params = NULL;
bh_arr_new(global_heap_allocator, poly_params, 1);
- while (parser->curr->type == '$') {
- consume_token(parser);
+ while (parser->curr->type != ')') {
if (parser->hit_unexpected_token) return NULL;
OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
- bh_arr_push(poly_params, sym_token);
+ expect_token(parser, ':');
+
+ AstType* param_type = parse_type(parser);
+
+ bh_arr_push(poly_params, ((AstPolyStructParam) {
+ .token = sym_token,
+ .type_node = param_type,
+ .type = NULL,
+ }));
if (parser->curr->type != ')')
expect_token(parser, ',');
AstPolyStructType* pst_node = (AstPolyStructType *) type;
pst_node->scope = scope_create(semstate.node_allocator, semstate.curr_scope, pst_node->token->pos);
+ bh_arr_each(AstPolyStructParam, param, pst_node->poly_params) {
+ param->type_node = symres_type(param->type_node);
+ param->type = type_build_from_ast(semstate.node_allocator, param->type_node);
+ }
+
return type;
}
pc_node->callee = symres_type(pc_node->callee);
- bh_arr_each(AstType *, param, pc_node->params) {
- *param = symres_type(*param);
+ bh_arr_each(AstNode *, param, pc_node->params) {
+ if (node_is_type(*param)) {
+ *param = (AstNode *) symres_type((AstType *) *param);
+ } else {
+ symres_expression((AstTyped **) param);
+ }
}
return type;
}
static void symres_struct_defaults(AstType* t) {
- switch (t->kind) {
- case Ast_Kind_Struct_Type: {
- AstStructType* st = (AstStructType *) t;
- bh_arr_each(AstStructMember *, smem, st->members) {
- if ((*smem)->initial_value != NULL) {
- symres_expression(&(*smem)->initial_value);
- }
- }
- break;
- }
-
- case Ast_Kind_Poly_Struct_Type: {
- AstPolyStructType* st = (AstPolyStructType *) t;
- bh_arr_each(AstStructMember *, smem, st->base_struct->members) {
- if ((*smem)->initial_value != NULL) {
- symres_expression(&(*smem)->initial_value);
- }
- }
- break;
+ if (t->kind != Ast_Kind_Struct_Type) return;
+
+ AstStructType* st = (AstStructType *) t;
+ bh_arr_each(AstStructMember *, smem, st->members) {
+ if ((*smem)->initial_value != NULL) {
+ symres_expression(&(*smem)->initial_value);
}
-
- default: break;
}
}
AstPolyStructType* ps_type = (AstPolyStructType *) pc_type->callee;
- bh_arr(Type *) param_types = NULL;
- bh_arr_new(global_heap_allocator, param_types, bh_arr_length(pc_type->params));
- bh_arr_each(AstType *, ptype, pc_type->params) {
- bh_arr_push(param_types, type_build_from_ast(alloc, *ptype));
+ bh_arr(AstPolySolution) slns = NULL;
+ bh_arr_new(global_heap_allocator, slns, bh_arr_length(pc_type->params));
+ bh_arr_each(AstNode *, given, pc_type->params) {
+ if (node_is_type(*given)) {
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Type,
+ .type = type_build_from_ast(alloc, (AstType *) *given),
+ }));
+ } else {
+ bh_arr_push(slns, ((AstPolySolution) {
+ .kind = PSK_Value,
+ .value = (AstTyped *) *given,
+ }));
+ }
}
- AstStructType* concrete = polymorphic_struct_lookup(ps_type, param_types, pc_type->token->pos);
+ AstStructType* concrete = polymorphic_struct_lookup(ps_type, slns, pc_type->token->pos);
- bh_arr_free(param_types);
+ // This should be copied in the previous function.
+ // CLEANUP: Maybe don't copy it and just use this one since it is allocated on the heap?
+ bh_arr_free(slns);
return type_build_from_ast(alloc, (AstType *) concrete);
}
bh_arr(PolySolveElem) elem_queue = NULL;
bh_arr_new(global_heap_allocator, elem_queue, 4);
- PolySolveResult result = { -1, { NULL } };
+ PolySolveResult result = { PSK_Undefined, { NULL } };
bh_arr_push(elem_queue, ((PolySolveElem) {
.type_expr = type_expr,
+ .kind = PSK_Type,
.actual = actual
}));
if (elem.type_expr == (AstType *) target) {
result.kind = elem.kind;
- if (result.kind == PSK_Type) {
- result.actual = elem.actual;
- } else {
- result.value = elem.value;
- }
+
+ assert(elem.kind != PSK_Undefined);
+ if (result.kind == PSK_Type) result.actual = elem.actual;
+ if (result.kind == PSK_Value) result.value = elem.value;
break;
}
case Ast_Kind_Poly_Call_Type: {
if (elem.actual->kind != Type_Kind_Struct) break;
- if (bh_arr_length(elem.actual->Struct.poly_args) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
+ if (bh_arr_length(elem.actual->Struct.poly_sln) != bh_arr_length(((AstPolyCallType *) elem.type_expr)->params)) break;
AstPolyCallType* pt = (AstPolyCallType *) elem.type_expr;
fori (i, 0, bh_arr_length(pt->params)) {
- bh_arr_push(elem_queue, ((PolySolveElem) {
- .type_expr = pt->params[i],
- .kind = PSK_Type,
- .actual = elem.actual->Struct.poly_args[i],
- }));
+ PolySolutionKind kind = elem.actual->Struct.poly_sln[i].kind;
+ if (kind == PSK_Type) {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .kind = kind,
+ .type_expr = (AstType *) pt->params[i],
+ .actual = elem.actual->Struct.poly_sln[i].type,
+ }));
+ } else {
+ bh_arr_push(elem_queue, ((PolySolveElem) {
+ .kind = kind,
+ .type_expr = (AstType *) pt->params[i],
+ .value = elem.actual->Struct.poly_sln[i].value,
+ }));
+ }
}
break;
PolySolveResult resolved = solve_poly_type(param->poly_sym, param->type_expr, actual_type);
switch (resolved.kind) {
- case -1:
+ case PSK_Undefined:
onyx_report_error(pos,
"Unable to solve for polymoprhic variable '%b', using the type '%s'.",
param->poly_sym->token->text,
}
// NOTE: This returns a volatile string. Do not store it without copying it.
-static char* build_polyproc_unique_key(bh_arr(AstPolySolution) slns) {
+static char* build_poly_slns_unique_key(bh_arr(AstPolySolution) slns) {
static char key_buf[1024];
fori (i, 0, 1024) key_buf[i] = 0;
}
// NOTE: Check if a version of this polyproc has already been created.
- char* unique_key = build_polyproc_unique_key(slns);
+ char* unique_key = build_poly_slns_unique_key(slns);
if (bh_table_has(AstFunction *, pp->concrete_funcs, unique_key)) {
return bh_table_get(AstFunction *, pp->concrete_funcs, unique_key);
}
}
}
+char* build_poly_struct_name(AstPolyStructType* ps_type, Type* cs_type) {
+ char name_buf[256];
+ fori (i, 0, 256) name_buf[i] = 0;
+
+ strncat(name_buf, ps_type->name, 255);
+ strncat(name_buf, "(", 255);
+ bh_arr_each(AstPolySolution, ptype, cs_type->Struct.poly_sln) {
+ if (ptype != cs_type->Struct.poly_sln)
+ strncat(name_buf, ", ", 255);
+
+ // This logic will have to be other places as well.
+
+ switch (ptype->kind) {
+ case PSK_Undefined: assert(0); break;
+ case PSK_Type: strncat(name_buf, type_get_name(ptype->type), 255); break;
+ case PSK_Value: {
+ // FIX
+ if (ptype->value->kind == Ast_Kind_NumLit) {
+ AstNumLit* nl = (AstNumLit *) ptype->value;
+ if (type_is_integer(nl->type)) {
+ strncat(name_buf, bh_bprintf("%l", nl->value.l), 127);
+ } else {
+ strncat(name_buf, "numlit (FIX ME)", 127);
+ }
+ } else {
+ strncat(name_buf, "<expr>", 127);
+ }
+
+ break;
+ }
+ }
+ }
+ strncat(name_buf, ")", 255);
+
+ return bh_aprintf(global_heap_allocator, "%s", name_buf);
+}
-AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params, OnyxFilePos pos) {
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos) {
// @Cleanup
- assert(bh_arr_length(ps_type->poly_params) == bh_arr_length(params));
assert(ps_type->scope != NULL);
if (ps_type->concrete_structs == NULL) {
bh_table_init(global_heap_allocator, ps_type->concrete_structs, 16);
}
- scope_clear(ps_type->scope);
+ if (bh_arr_length(slns) < bh_arr_length(ps_type->poly_params)) {
+ onyx_report_error(pos, "Not enough arguments for polymorphic struct creation. Expected %d, got %d",
+ bh_arr_length(ps_type->poly_params),
+ bh_arr_length(slns));
- fori (i, 0, bh_arr_length(ps_type->poly_params)) {
- if (params[i] == NULL) {
- onyx_report_error((OnyxFilePos) { 0 }, "Type parameter is not a type.");
- return NULL;
+ return NULL;
+ }
+
+ i32 i = 0;
+ bh_arr_each(AstPolySolution, sln, slns) {
+ PolySolutionKind expected_kind = PSK_Undefined;
+ if ((AstNode *) ps_type->poly_params[i].type_node == &type_expr_symbol) {
+ expected_kind = PSK_Type;
+ } else {
+ expected_kind = PSK_Value;
}
- AstTypeRawAlias* raw = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
- raw->to = params[i];
+ if (sln->kind != expected_kind) {
+ if (expected_kind == PSK_Type)
+ onyx_report_error(pos, "Expected type expression for %d%s argument.", i + 1, bh_num_suffix(i + 1));
- symbol_introduce(ps_type->scope, ps_type->poly_params[i], (AstNode *) raw);
- }
+ if (expected_kind == PSK_Value)
+ onyx_report_error(pos, "Expected value expression of type '%s' for %d%s argument.",
+ type_get_name(ps_type->poly_params[i].type),
+ i + 1, bh_num_suffix(i + 1));
- char key_buf[1024];
- fori (i, 0, 1024) key_buf[i] = 0;
- bh_table_each_start(AstNode *, ps_type->scope->symbols);
- strncat(key_buf, key, 1023);
- strncat(key_buf, "=", 1023);
- strncat(key_buf, type_get_unique_name(((AstTypeRawAlias *) value)->to), 1023);
- strncat(key_buf, ";", 1023);
- bh_table_each_end;
+ return NULL;
+ }
+
+ if (sln->kind == PSK_Value) {
+ if ((sln->value->flags & Ast_Flag_Comptime) == 0) {
+ onyx_report_error(pos,
+ "Expected compile-time known argument for '%b'.",
+ sln->poly_sym->token->text,
+ sln->poly_sym->token->length);
+ return NULL;
+ }
- if (bh_table_has(AstStructType *, ps_type->concrete_structs, key_buf)) {
- return bh_table_get(AstStructType *, ps_type->concrete_structs, key_buf);
+ if (!types_are_compatible(sln->value->type, ps_type->poly_params[i].type)) {
+ onyx_report_error(pos, "Expected compile-time argument of type '%s', got '%s'.",
+ type_get_name(ps_type->poly_params[i].type),
+ type_get_name(sln->value->type));
+ return NULL;
+ }
+ }
+
+ sln->poly_sym = (AstNode *) &ps_type->poly_params[i];
+ i++;
}
- AstStructType* concrete_struct = (AstStructType *) ast_clone(semstate.node_allocator, ps_type->base_struct);
+ char* unique_key = build_poly_slns_unique_key(slns);
+ if (bh_table_has(AstStructType *, ps_type->concrete_structs, unique_key)) {
+ return bh_table_get(AstStructType *, ps_type->concrete_structs, unique_key);
+ }
- Scope* old_scope = semstate.curr_scope;
- semstate.curr_scope = ps_type->scope;
- concrete_struct = (AstStructType *) symres_type((AstType *) concrete_struct);
- semstate.curr_scope = old_scope;
+ scope_clear(ps_type->scope);
- if (onyx_has_errors()) goto has_error;
- goto no_errors;
+ bh_arr_each(AstPolySolution, sln, slns) {
+ AstNode *node = NULL;
+
+ switch (sln->kind) {
+ case PSK_Type:
+ node = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+ ((AstTypeRawAlias *) node)->to = sln->type;
+ break;
-has_error:
- // onyx_report_error(pos, "Error in polymorphic struct generated from this call site.");
- return NULL;
+ case PSK_Value:
+ // CLEANUP: Maybe clone this?
+ node = (AstNode *) sln->value;
+ break;
+ }
-no_errors:
- bh_table_put(AstStructType *, ps_type->concrete_structs, key_buf, concrete_struct);
+ symbol_introduce(ps_type->scope, sln->poly_sym->token, node);
+ }
- Type* cs_type = type_build_from_ast(semstate.node_allocator, (AstType *) concrete_struct);
+ AstStructType* concrete_struct = (AstStructType *) ast_clone(semstate.node_allocator, ps_type->base_struct);
- cs_type->Struct.poly_args = NULL;
- bh_arr_new(global_heap_allocator, cs_type->Struct.poly_args, bh_arr_length(params));
+ Entity struct_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Type_Alias,
+ .type_alias = (AstType *) concrete_struct,
+ .package = NULL,
+ .scope = ps_type->scope,
+ };
+ Entity struct_default_entity = {
+ .state = Entity_State_Resolve_Symbols,
+ .type = Entity_Type_Struct_Member_Default,
+ .type_alias = (AstType *) concrete_struct,
+ .package = NULL,
+ .scope = ps_type->scope,
+ };
- fori (i, 0, bh_arr_length(params)) bh_arr_push(cs_type->Struct.poly_args, params[i]);
+ entity_bring_to_state(&struct_entity, Entity_State_Code_Gen);
+ entity_bring_to_state(&struct_default_entity, Entity_State_Code_Gen);
+
+ if (onyx_has_errors()) {
+ onyx_report_error(pos, "Error in creating polymoprhic struct instantiation here.");
+ return NULL;
+ }
- char name_buf[256];
- fori (i, 0, 256) name_buf[i] = 0;
+ bh_table_put(AstStructType *, ps_type->concrete_structs, unique_key, concrete_struct);
- strncat(name_buf, ps_type->name, 255);
- strncat(name_buf, "(", 255);
- bh_arr_each(Type *, ptype, cs_type->Struct.poly_args) {
- if (ptype != cs_type->Struct.poly_args)
- strncat(name_buf, ", ", 255);
+ Type* cs_type = type_build_from_ast(semstate.node_allocator, (AstType *) concrete_struct);
+ cs_type->Struct.poly_sln = NULL;
+ bh_arr_new(global_heap_allocator, cs_type->Struct.poly_sln, bh_arr_length(slns));
- strncat(name_buf, type_get_name(*ptype), 255);
- }
- strncat(name_buf, ")", 255);
- cs_type->Struct.name = bh_aprintf(semstate.node_allocator, "%s", name_buf);
+ fori (i, 0, bh_arr_length(slns)) bh_arr_push(cs_type->Struct.poly_sln, slns[i]);
+ cs_type->Struct.name = build_poly_struct_name(ps_type, cs_type);
return concrete_struct;
}
--- /dev/null
+12345
+12345
+12345
+12345
+1234
+Hello World!
--- /dev/null
+#load "core/std/js"
+
+use package core
+
+main :: proc (args: [] cstr) {
+
+ NewPolyStruct :: struct (T: type_expr, N: i32) {
+ x : [N] T;
+ y : [N] f32;
+ }
+
+ nps : NewPolyStruct(i32, #value 4);
+
+ for ^x: nps.x do *x = 12345;
+ for ^y: nps.y do *y = 67890;
+
+ for x: nps.x do println(x);
+
+ SimpleWithDefault :: struct (T: type_expr, default: str) {
+ x : T;
+ str_member : str = default;
+ }
+
+ swd := <SimpleWithDefault(i32, #value "Hello World!")>.{ x = 1234 };
+ println(swd.x);
+ println(swd.str_member);
+}
\ No newline at end of file
test_polymorphic :: proc () {
println("\n\nTesting a polymorphic struct.");
- PolyStruct :: struct ($T, $R) {
+ PolyStruct :: struct (T: type_expr, R: type_expr) {
t_data : T;
r_data : R;
}
test_polymorphic_with_defaults :: proc () {
println("\n\nTesting a polymorphic struct with default values.");
- PolyStruct :: struct ($T, $R) {
+ PolyStruct :: struct (T: type_expr, R: type_expr) {
t_data : T = 1234;
r_data : R = 5678;
}
test_polymorphic_union_with_use :: proc () {
println("\n\nTesting a polymorphic union with use.");
- PolyStruct :: struct ($T, $R) {
+ PolyStruct :: struct (T: type_expr, R: type_expr) {
use container : struct #union {
t_data : T;
r_data : R;
test_polymorphic_union :: proc () {
println("\n\nTesting a polymorphic union.");
- PolyUnion :: struct ($T, $R) #union {
+ PolyUnion :: struct (T: type_expr, R: type_expr) #union {
t_data : T;
r_data : R;
}