"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/onyx",
- "args": ["-verbose", "progs/simd_test.onyx"],
+ "args": ["-verbose", "progs/poly_struct.onyx"],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
--- /dev/null
+Release v0.0.3
+--------------
+Additions:
+* Added polymorphic structs
+* Added polymorphic structs to polymorphic pattern matching
+
+Removals:
+
+Changes:
+
+Bug fixes:
+* Fixed error message index for struct literal member type mismatch.
+* Fixed freeing non-allocated memory when generating documentation.
--- /dev/null
+package core
+
+I32Map :: struct ($T) {
+ hashes : [..] i32;
+ entries : [..] I32MapEntry(T);
+}
+
+I32MapEntry :: struct ($T) {
+ key : i32;
+ next : i32;
+ value : T;
+}
+
+i32map_init :: proc (imap: ^I32Map($T), hash_count: i32 = 16) {
+ array_init(^imap.hashes, hash_count);
+ array_init(^imap.entries, 4);
+
+ for i: 0 .. hash_count do array_push(^imap.hashes, -1);
+}
+
+i32map_free :: proc (imap: ^I32Map($T)) {
+ array_free(^imap.hashes);
+ array_free(^imap.entries);
+}
+
+i32map_put :: proc (imap: ^I32Map($T), key: i32, value: T) {
+ lr := i32map_lookup(imap, key);
+
+ if lr.entry_index >= 0 {
+ imap.entries[lr.entry_index].value = value;
+ return;
+ }
+
+ entry : I32MapEntry(T);
+ entry.key = key;
+ entry.value = value;
+ entry.next = imap.hashes[lr.hash_index];
+
+ array_push(^imap.entries, entry);
+
+ imap.hashes[lr.hash_index] = imap.entries.count - 1;
+}
+
+i32map_has :: proc (imap: ^I32Map($T), key: i32) -> bool {
+ lr := i32map_lookup(imap, key);
+ return lr.entry_index >= 0;
+}
+
+i32map_get :: proc (imap: ^I32Map($T), key: i32) -> i32 {
+ lr := i32map_lookup(imap, key);
+ if lr.entry_index >= 0 do return imap.entries[lr.entry_index].value;
+
+ return null;
+}
+
+i32map_delete :: proc (imap: ^I32Map($T), key: i32) {
+ lr := i32map_lookup(imap, key);
+ if lr.entry_index < 0 do return;
+
+ if lr.entry_prev < 0 do imap.hashes[lr.hash_index] = imap.entries[lr.entry_index].next;
+ else do imap.hashes[lr.entry_prev] = imap.entries[lr.entry_index].next;
+
+ if lr.entry_index == imap.entries.count - 1 {
+ array_pop(^imap.entries);
+ return;
+ }
+
+ array_fast_delete(^imap.entries, lr.entry_index);
+ last := i32map_lookup(imap, imap.entries[lr.entry_index].key);
+ if last.entry_prev >= 0 do imap.entries[last.entry_prev].next = lr.entry_index;
+ else do imap.hashes[last.hash_index] = lr.entry_index;
+}
+
+i32map_clear :: proc (imap: ^I32Map($T)) {
+ for i: 0 .. imap.hashes.count do imap.hashes.data[i] = -1;
+ imap.entries.count = 0;
+}
+
+
+
+//
+// Private symbols
+//
+
+#private
+I32MapLookupResult :: struct {
+ hash_index : i32 = -1;
+ entry_index : i32 = -1;
+ entry_prev : i32 = -1;
+}
+
+#private
+i32map_lookup :: proc (imap: ^I32Map($T), key: i32) -> I32MapLookupResult {
+ lr := I32MapLookupResult.{};
+
+ hash := cast(u32) 0xcbf29ce7 ^ cast(u32) key;
+
+ lr.hash_index = hash % imap.hashes.count;
+ lr.entry_index = imap.hashes[lr.hash_index];
+
+ while lr.entry_index >= 0 {
+ if imap.entries[lr.entry_index].key == key do return lr;
+
+ lr.entry_prev = lr.entry_index;
+ lr.entry_index = imap.entries[lr.entry_index].next;
+ }
+
+ return lr;
+}
array_free(^entries);
}
-
ptrmap_put :: proc (use pmap: ^PtrMap, key: rawptr, value: rawptr) {
lr := ptrmap_lookup(pmap, key);
#include_file "core/alloc"
#include_file "core/array"
+#include_file "core/i32map"
#include_file "core/intrinsics"
#include_file "core/math"
#include_file "core/memory"
#include_file "core/alloc"
#include_file "core/array"
#include_file "core/file"
+#include_file "core/i32map"
#include_file "core/intrinsics"
#include_file "core/math"
#include_file "core/memory"
typedef struct AstVarArgType AstVarArgType;
typedef struct AstStructType AstStructType;
typedef struct AstStructMember AstStructMember;
+typedef struct AstPolyStructType AstPolyStructType;
+typedef struct AstPolyCallType AstPolyCallType;
typedef struct AstEnumType AstEnumType;
typedef struct AstEnumValue AstEnumValue;
typedef struct AstTypeAlias AstTypeAlias;
Ast_Kind_DynArr_Type,
Ast_Kind_VarArg_Type,
Ast_Kind_Struct_Type,
+ Ast_Kind_Poly_Struct_Type,
+ Ast_Kind_Poly_Call_Type,
Ast_Kind_Enum_Type,
Ast_Kind_Type_Alias,
Ast_Kind_Type_Raw_Alias,
AstTyped_base;
AstTyped* initial_value;
};
+struct AstPolyStructType {
+ AstType_base;
+
+ Scope *scope;
+ bh_arr(OnyxToken *) poly_params;
+ bh_table(AstStructType *) concrete_structs;
+
+ AstStructType* base_struct;
+};
+struct AstPolyCallType {
+ AstType_base;
+
+ AstType* callee;
+ bh_arr(AstType *) params;
+};
struct AstEnumType {
AstType_base;
Scope *scope;
} PolyProcLookupMethod;
AstFunction* polymorphic_proc_lookup(AstPolyProc* pp, PolyProcLookupMethod pp_lookup, ptr actual, OnyxFilePos pos);
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params);
+
// NOTE: Useful inlined functions
static inline b32 is_lval(AstNode* node) {
return (node->kind == Ast_Kind_Local)
// NOTE: Resolving all symbols in the tree
void onyx_resolve_symbols();
+AstType* symres_type(AstType* type);
void symres_function(AstFunction* func);
// NOTE: Inferring and checking types in the tree
u16 alignment, mem_count; \
bh_table(StructMember) members; \
bh_arr(StructMember *) memarr; \
+ bh_arr(Type *) poly_args; \
}) \
TYPE_KIND(Array, struct { u32 size; u32 count; Type *elem; }) \
TYPE_KIND(Slice, struct { Type *ptr_to_data; }) \
--- /dev/null
+package main
+
+#include_file "core/std/wasi"
+
+use package core
+
+
+main :: proc (args: [] cstring) {
+
+ imap : I32Map(string);
+ i32map_init(^imap);
+
+ i32map_put(^imap, 50, "Hello ");
+ i32map_put(^imap, 1234, "World!");
+
+ print(i32map_get(^imap, 50));
+ print(i32map_get(^imap, 1234));
+ print("\n");
+}
}
case Ast_Kind_Type_Alias:
- case Ast_Kind_Struct_Type: {
+ case Ast_Kind_Struct_Type:
+ case Ast_Kind_Poly_Struct_Type: {
ent.type = Entity_Type_Type_Alias;
ent.type_alias = (AstType *) node;
bh_arr_push(compiler_state->prog_info.entities, ent);
type_lookup_member_by_idx(sl->type, i, &smem);
Type* formal = smem.type;
+ // NOTE: We may want to report a different error if the struct member was named,
+ // since those names may not be in the correct order. - brendanfh 2020/09/12
if (!types_are_compatible(formal, (*actual)->type)) {
onyx_report_error(sl->token->pos,
"Mismatched types for %d%s member, expected '%s', got '%s'.",
b32 check_field_access(AstFieldAccess** pfield) {
AstFieldAccess* field = *pfield;
if (check_expression(&field->expr)) return 1;
+ if (field->expr->type == NULL) return 1;
if (!type_is_structlike(field->expr->type)) {
onyx_report_error(field->token->pos,
case Ast_Kind_NumLit:
case Ast_Kind_StrLit:
case Ast_Kind_Package:
- case Ast_Kind_Struct_Type:
case Ast_Kind_Enum_Type:
case Ast_Kind_Enum_Value:
case Ast_Kind_Overloaded_Function:
case Ast_Kind_DynArr_Type: return sizeof(AstDynArrType);
case Ast_Kind_VarArg_Type: return sizeof(AstVarArgType);
case Ast_Kind_Struct_Type: return sizeof(AstStructType);
+ case Ast_Kind_Poly_Struct_Type: return sizeof(AstPolyStructType);
+ case Ast_Kind_Poly_Call_Type: return sizeof(AstPolyCallType);
case Ast_Kind_Enum_Type: return sizeof(AstEnumType);
case Ast_Kind_Type_Alias: return sizeof(AstTypeAlias);
case Ast_Kind_Type_Raw_Alias: return sizeof(AstTypeRawAlias);
((AstTypeAlias *) nn)->to = (AstType *) ast_clone(a, ((AstTypeAlias *) node)->to);
break;
+ case Ast_Kind_Struct_Type: {
+ AstStructType* ds = (AstStructType *) nn;
+ AstStructType* ss = (AstStructType *) node;
+
+ ds->members = NULL;
+ bh_arr_new(global_heap_allocator, ds->members, bh_arr_length(ss->members));
+
+ bh_arr_each(AstStructMember *, smem, ss->members) {
+ bh_arr_push(ds->members, (AstStructMember *) ast_clone(a, *smem));
+ }
+
+ ds->stcache = NULL;
+ break;
+ }
+
+ case Ast_Kind_Struct_Member:
+ ((AstStructMember *) nn)->initial_value = (AstTyped *) ast_clone(a, ((AstStructMember *) node)->initial_value);
+ break;
+
case Ast_Kind_Function_Type:
((AstFunctionType *) nn)->return_type = (AstType *) ast_clone(a, ((AstFunctionType *) node)->return_type);
((AstFunctionType *) nn)->param_count = ((AstFunctionType *) node)->param_count;
*next_insertion = (AstType *) symbol_node;
}
+ if (parser->curr->type == '(') {
+ consume_token(parser);
+
+ bh_arr(AstType *) 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);
+ bh_arr_push(params, t);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+ expect_token(parser, ')');
+
+ AstPolyCallType* pc_type = make_node(AstPolyCallType, Ast_Kind_Poly_Call_Type);
+ pc_type->callee = *next_insertion;
+ pc_type->params = params;
+
+ *next_insertion = (AstType *) pc_type;
+ }
+
next_insertion = NULL;
}
}
static AstStructType* parse_struct(OnyxParser* parser) {
- AstStructType* s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
- s_node->token = expect_token(parser, Token_Type_Keyword_Struct);
+ OnyxToken *s_token = expect_token(parser, Token_Type_Keyword_Struct);
+
+ AstStructType* s_node;
+ AstPolyStructType* poly_struct = NULL;
+
+ s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
+ s_node->token = s_token;
+
+ if (parser->curr->type == '(') {
+ consume_token(parser);
+
+ bh_arr(OnyxToken *) poly_params = NULL;
+ bh_arr_new(global_heap_allocator, poly_params, 1);
+
+ while (parser->curr->type == '$') {
+ consume_token(parser);
+ if (parser->hit_unexpected_token) return NULL;
+
+ OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
+ bh_arr_push(poly_params, sym_token);
+
+ if (parser->curr->type != ')')
+ expect_token(parser, ',');
+ }
+ expect_token(parser, ')');
+
+ poly_struct = make_node(AstPolyStructType, Ast_Kind_Poly_Struct_Type);
+ poly_struct->token = s_token;
+ poly_struct->poly_params = poly_params;
+ poly_struct->base_struct = s_node;
+ }
bh_arr_new(global_heap_allocator, s_node->members, 4);
expect_token(parser, '}');
- return s_node;
+ if (poly_struct != NULL) {
+ // NOTE: Not a StructType
+ return (AstStructType *) poly_struct;
+
+ } else {
+ return s_node;
+ }
}
// e
} else if (node->kind != Ast_Kind_Overloaded_Function
&& node->kind != Ast_Kind_StrLit) {
- if (node->kind == Ast_Kind_Struct_Type || node->kind == Ast_Kind_Enum_Type) {
+ if (node->kind == Ast_Kind_Struct_Type
+ || node->kind == Ast_Kind_Enum_Type
+ || node->kind == Ast_Kind_Poly_Struct_Type) {
((AstStructType *)node)->name = bh_aprintf(global_heap_allocator,
"%b", symbol->text, symbol->length);
}
static void scope_enter(Scope* new_scope);
static void scope_leave();
-static AstType* symres_type(AstType* type);
+AstType* symres_type(AstType* type);
static void symres_local(AstLocal** local);
static void symres_call(AstCall* call);
static void symres_size_of(AstSizeOf* so);
semstate.curr_scope = semstate.curr_scope->parent;
}
-static AstType* symres_type(AstType* type) {
+AstType* symres_type(AstType* type) {
if (type == NULL) return NULL;
if (type->kind == Ast_Kind_Type_Alias) {
return type;
}
+ if (type->kind == Ast_Kind_Poly_Struct_Type) {
+ AstPolyStructType* pst_node = (AstPolyStructType *) type;
+ pst_node->scope = scope_create(semstate.node_allocator, semstate.curr_scope);
+
+ return type;
+ }
+
+ if (type->kind == Ast_Kind_Poly_Call_Type) {
+ AstPolyCallType* pc_node = (AstPolyCallType *) type;
+
+ pc_node->callee = symres_type(pc_node->callee);
+
+ bh_arr_each(AstType *, param, pc_node->params) {
+ *param = symres_type(*param);
+ }
+
+ return type;
+ }
+
return type;
}
case Ast_Kind_Type_Raw_Alias:
return ((AstTypeRawAlias *) type_node)->to;
+ case Ast_Kind_Poly_Struct_Type:
+ // @Cleanup: Replace this with a proper onyx_report_error. - brendanfh 2020/09/14
+ assert(("Polymorphic struct used without instantiation.", 0));
+ break;
+
+ case Ast_Kind_Poly_Call_Type: {
+ AstPolyCallType* pc_type = (AstPolyCallType *) type_node;
+
+ // @Cleanup: make this a proper error message. -brendanfh 2020/09/14
+ assert(pc_type->callee && pc_type->callee->kind == Ast_Kind_Poly_Struct_Type);
+
+ 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));
+ }
+
+ AstStructType* concrete = polymorphic_struct_lookup(ps_type, param_types);
+
+ bh_arr_free(param_types);
+
+ return type_build_from_ast(alloc, (AstType *) concrete);
+ }
+
case Ast_Kind_Symbol:
assert(("symbol node in type expression", 0));
return NULL;
"SLICE TYPE",
"DYNARR TYPE",
"STRUCT TYPE",
+ "POLYMORPHIC STRUCT TYPE",
+ "POLYMORPHIC STRUCT CALL TYPE",
"ENUM TYPE",
"TYPE_ALIAS",
"TYPE RAW ALIAS"
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;
+
+ 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],
+ .actual = elem.actual->Struct.poly_args[i],
+ }));
+ }
+
+ break;
+ }
+
default: break;
}
}
return func;
}
+
+
+AstStructType* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(Type *) params) {
+ // @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);
+
+ 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;
+ }
+
+ AstTypeRawAlias* raw = onyx_ast_node_new(semstate.node_allocator, sizeof(AstTypeRawAlias), Ast_Kind_Type_Raw_Alias);
+ raw->to = params[i];
+
+ symbol_introduce(ps_type->scope, ps_type->poly_params[i], (AstNode *) raw);
+ }
+
+ static 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_name(((AstTypeRawAlias *) value)->to), 1023);
+ strncat(key_buf, ";", 1023);
+ bh_table_each_end;
+
+ if (bh_table_has(AstStructType *, ps_type->concrete_structs, key_buf)) {
+ return bh_table_get(AstStructType *, ps_type->concrete_structs, key_buf);
+ }
+
+ AstStructType* concrete_struct = (AstStructType *) ast_clone(semstate.node_allocator, ps_type->base_struct);
+
+ semstate.curr_scope = ps_type->scope;
+ concrete_struct = (AstStructType *) symres_type((AstType *) concrete_struct);
+ if (onyx_has_errors()) goto has_error;
+ goto no_errors;
+
+has_error:
+ // onyx_report_error((OnyxFilePos) { 0 }, "Error in polymorphic struct generated from this call site.");
+ return NULL;
+
+no_errors:
+ bh_table_put(AstStructType *, ps_type->concrete_structs, key_buf, concrete_struct);
+
+ Type* cs_type = type_build_from_ast(semstate.node_allocator, (AstType *) concrete_struct);
+
+ cs_type->Struct.poly_args = NULL;
+ bh_arr_new(global_heap_allocator, cs_type->Struct.poly_args, bh_arr_length(params));
+
+ fori (i, 0, bh_arr_length(params)) bh_arr_push(cs_type->Struct.poly_args, params[i]);
+
+ return concrete_struct;
+}
+
i32 sort_entities(const void* e1, const void* e2) {
return ((Entity *)e1)->type - ((Entity *)e2)->type;
}