From 5567baa5982183aed0cc2c3ae63783031eb68c1e Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Sun, 4 Dec 2022 12:43:32 -0600 Subject: [PATCH] added intrinsic interfaces --- compiler/include/astnodes.h | 4 ++++ compiler/src/astnodes.c | 33 ++++++++++++++++++++++++++- compiler/src/checker.c | 21 ++++++++++++++++- compiler/src/parser.c | 5 ++++ core/container/iter.onyx | 34 ++++++++++++++++++++++++++-- core/intrinsics/type_interfaces.onyx | 26 +++++++++++++++++++++ core/misc/arg_parse.onyx | 2 +- core/std.onyx | 1 + tests/aoc-2021/day07.onyx | 2 +- tests/aoc-2021/day12.onyx | 4 ++-- tests/aoc-2021/day13.onyx | 5 ++-- 11 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 core/intrinsics/type_interfaces.onyx diff --git a/compiler/include/astnodes.h b/compiler/include/astnodes.h index ae5b9d29..fbfa4acc 100644 --- a/compiler/include/astnodes.h +++ b/compiler/include/astnodes.h @@ -1113,6 +1113,8 @@ struct AstInterface { bh_arr(InterfaceParam) params; bh_arr(InterfaceConstraint) exprs; + + b32 is_intrinsic: 1; }; typedef enum ConstraintPhase { @@ -1792,6 +1794,8 @@ AstFunction* macro_resolve_header(AstMacro* macro, Arguments* args, OnyxToken* c Type* polymorphic_struct_lookup(AstPolyStructType* ps_type, bh_arr(AstPolySolution) slns, OnyxFilePos pos, b32 error_if_failed); +b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint); + // NOTE: Useful inlined functions static inline b32 is_lval(AstNode* node) { node = strip_aliases(node); diff --git a/compiler/src/astnodes.c b/compiler/src/astnodes.c index 0c2a72b8..755ae7fa 100644 --- a/compiler/src/astnodes.c +++ b/compiler/src/astnodes.c @@ -614,7 +614,11 @@ TypeMatch unify_node_and_type_(AstTyped** pnode, Type* type, b32 permanent) { break; } - default: assert(0); + // This is not the right thing to return here. In theory it should + // try to extract the type of the first element of the array and then + // use that as the `array_type`. Then only if there are no elements in the + // array should it return TYPE_MATCH_FAILED; + default: return TYPE_MATCH_FAILED; } node->type = array_type; @@ -1587,3 +1591,30 @@ AstPolyCallType* convert_call_to_polycall(AstCall* call) { return pct; } + + +b32 resolve_intrinsic_interface_constraint(AstConstraint *constraint) { + AstInterface *interface = constraint->interface; + Type* type = type_build_from_ast(context.ast_alloc, constraint->type_args[0]); + if (!type) return 0; + + if (!strcmp(interface->name, "type_is_bool")) return type_is_bool(type); + if (!strcmp(interface->name, "type_is_int")) return type_is_integer(type); + if (!strcmp(interface->name, "type_is_float")) return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_Float); + if (!strcmp(interface->name, "type_is_number")) return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_Numeric); + if (!strcmp(interface->name, "type_is_simd")) return type->kind == Type_Kind_Basic && (type->Basic.flags & Basic_Flag_SIMD); + if (!strcmp(interface->name, "type_is_pointer")) return type_is_pointer(type) || type_is_rawptr(type); + if (!strcmp(interface->name, "type_is_enum")) return type->kind == Type_Kind_Enum; + if (!strcmp(interface->name, "type_is_simple")) return type->kind == Type_Kind_Basic + || type->kind == Type_Kind_Enum + || type->kind == Type_Kind_Pointer; + if (!strcmp(interface->name, "type_is_array")) return type->kind == Type_Kind_Array; + if (!strcmp(interface->name, "type_is_slice")) return type->kind == Type_Kind_Slice; + if (!strcmp(interface->name, "type_is_struct")) return type->kind == Type_Kind_Struct; + if (!strcmp(interface->name, "type_is_compound")) return type->kind == Type_Kind_Array + || type->kind == Type_Kind_Slice + || type->kind == Type_Kind_DynArray + || type->kind == Type_Kind_Struct; + + return 0; +} diff --git a/compiler/src/checker.c b/compiler/src/checker.c index edd04274..113682f4 100644 --- a/compiler/src/checker.c +++ b/compiler/src/checker.c @@ -3011,6 +3011,18 @@ CheckStatus check_constraint(AstConstraint *constraint) { onyx_ast_node_kind_string(constraint->interface->kind)); } + // #intrinsic interfaces + if (constraint->interface->is_intrinsic) { + b32 success = resolve_intrinsic_interface_constraint(constraint); + if (success) { + *constraint->report_status = Constraint_Check_Status_Success; + return Check_Complete; + } else { + *constraint->report_status = Constraint_Check_Status_Failed; + return Check_Failed; + } + } + bh_arr_new(global_heap_allocator, constraint->exprs, bh_arr_length(constraint->interface->exprs)); bh_arr_each(InterfaceConstraint, ic, constraint->interface->exprs) { InterfaceConstraint new_ic = {0}; @@ -3154,7 +3166,14 @@ CheckStatus check_constraint_context(ConstraintContext *cc, Scope *scope, OnyxFi strncat(constraint_map, "'", 511); } - onyx_report_error(constraint->exprs[constraint->expr_idx].expr->token->pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map); + OnyxFilePos error_pos; + if (constraint->exprs) { + error_pos = constraint->exprs[constraint->expr_idx].expr->token->pos; + } else { + error_pos = constraint->interface->token->pos; + } + + onyx_report_error(error_pos, Error_Critical, "Failed to satisfy constraint where %s.", constraint_map); onyx_report_error(constraint->token->pos, Error_Critical, "Here is where the interface was used."); onyx_report_error(pos, Error_Critical, "Here is the code that caused this constraint to be checked."); diff --git a/compiler/src/parser.c b/compiler/src/parser.c index e79b6acd..77dbcddb 100644 --- a/compiler/src/parser.c +++ b/compiler/src/parser.c @@ -2192,6 +2192,11 @@ static AstInterface* parse_interface(OnyxParser* parser) { expect_token(parser, ','); } + if (parse_possible_directive(parser, "intrinsic")) { + interface->is_intrinsic = 1; + return interface; + } + bh_arr_new(global_heap_allocator, interface->exprs, 2); expect_token(parser, '{'); diff --git a/core/container/iter.onyx b/core/container/iter.onyx index 4141f1ff..77f7bc38 100644 --- a/core/container/iter.onyx +++ b/core/container/iter.onyx @@ -453,9 +453,21 @@ enumerate :: (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Va }; } +use core.intrinsics.types {type_is_struct} + +// +// `from_array` has two almost identical implementations, +// but the details are important here. Normally, `from_array` +// returns an iterator by value, unless the array is of +// structures, then it returns an iterator by pointer. +// This seems weird, but in practice it is closer to what +// you want, as you don't want to have to copy every structure +// out of the array. While for primitives, you don't want to +// dereference it everywhere. +from_array :: #match #local {} + #overload -as_iterator :: from_array -from_array :: (arr: [] $T) => generator( +from_array :: (arr: [] $T/type_is_struct) => generator( ^.{ data = arr.data, count = arr.count, current = 0 }, (use ctx: ^$T) -> (typeof ctx.data, bool) { @@ -468,6 +480,24 @@ from_array :: (arr: [] $T) => generator( } ); +#overload +from_array :: (arr: [] $T) => generator( + ^.{ data = arr.data, count = arr.count, current = 0 }, + + (use ctx: ^$T) -> (typeof *ctx.data, bool) { + if current < count { + defer current += 1; + return data[current], true; + } + + return .{}, false; + } +); + + +#overload +as_iterator :: from_array + #overload as_iterator :: (x: ^[..] $T) -> Iterator(T) { Context :: struct (T: type_expr) { diff --git a/core/intrinsics/type_interfaces.onyx b/core/intrinsics/type_interfaces.onyx new file mode 100644 index 00000000..c60d59af --- /dev/null +++ b/core/intrinsics/type_interfaces.onyx @@ -0,0 +1,26 @@ +package core.intrinsics.types + +// +// Simple: bool, number, simd, pointer, enum +// Number: int, float +// Int: u8, .., u64 i8, .., i64 +// Float: f32 f64 +// Simd: v128 i8x16 i16x8 i32x4 i64x2 f32x4 f64x2 +// Pointer: ^T rawptr +// Compound: array, slice, struct +// Array: [$N] $T +// Slice: [] $T + +type_is_bool :: interface (t: $T) #intrinsic +type_is_int :: interface (t: $T) #intrinsic +type_is_float :: interface (t: $T) #intrinsic +type_is_number :: interface (t: $T) #intrinsic +type_is_simd :: interface (t: $T) #intrinsic +type_is_pointer :: interface (t: $T) #intrinsic +type_is_enum :: interface (t: $T) #intrinsic +type_is_simple :: interface (t: $T) #intrinsic +type_is_array :: interface (t: $T) #intrinsic +type_is_slice :: interface (t: $T) #intrinsic +type_is_struct :: interface (t: $T) #intrinsic +type_is_compound :: interface (t: $T) #intrinsic + diff --git a/core/misc/arg_parse.onyx b/core/misc/arg_parse.onyx index cde4b24e..43dc396f 100644 --- a/core/misc/arg_parse.onyx +++ b/core/misc/arg_parse.onyx @@ -4,7 +4,7 @@ use core arg_parse :: (c_args: [] cstr, output: any) -> bool { arg_iter := iter.as_iterator(c_args) - |> iter.map((x) => string.from_cstr(*x)); + |> iter.map(string.from_cstr); defer arg_iter.close(arg_iter.data); use runtime.info; diff --git a/core/std.onyx b/core/std.onyx index 1537974f..1b03d811 100644 --- a/core/std.onyx +++ b/core/std.onyx @@ -29,6 +29,7 @@ package core #load "./intrinsics/onyx" #load "./intrinsics/wasm" +#load "./intrinsics/type_interfaces" #load "./io/io" #load "./io/stream" diff --git a/tests/aoc-2021/day07.onyx b/tests/aoc-2021/day07.onyx index 0e96fdaa..f4fb8c08 100644 --- a/tests/aoc-2021/day07.onyx +++ b/tests/aoc-2021/day07.onyx @@ -8,7 +8,7 @@ main :: (args) => { nums := io.read_all(^reader) |> string.split(#char ",") |> iter.as_iterator() - |> iter.map((x) => cast(i32) conv.str_to_i64(*x)) + |> iter.map((x) => cast(i32) conv.parse_int(x)) |> iter.to_array(); min := array.fold(nums, nums[0], math.min); diff --git a/tests/aoc-2021/day12.onyx b/tests/aoc-2021/day12.onyx index 2837716a..51d30757 100644 --- a/tests/aoc-2021/day12.onyx +++ b/tests/aoc-2021/day12.onyx @@ -47,11 +47,11 @@ main :: (args) => { return iter.concat( iter.as_iterator(edges) |> iter.filter(name_copy, (x, n) => x.a == *n) - |> iter.map((x) => x.b), + |> iter.map(x => x.b), iter.as_iterator(edges) |> iter.filter(name_copy, (x, n) => x.b == *n) - |> iter.map((x) => x.a) + |> iter.map(x => x.a) ); } diff --git a/tests/aoc-2021/day13.onyx b/tests/aoc-2021/day13.onyx index 163339ea..ed19deb5 100644 --- a/tests/aoc-2021/day13.onyx +++ b/tests/aoc-2021/day13.onyx @@ -32,9 +32,8 @@ main :: (args) => { string.strip_whitespace(^line); if line.count == 0 do break; - parts := string.split(line, #char ",", allocator=context.temp_allocator) - |> iter.as_iterator() - |> iter.map((x) => cast(i32) conv.str_to_i64(*x)); + parts := string.split_iter(line, #char ",") + |> iter.map(x => cast(i32) conv.parse_int(x)); x, _ := iter.take_one(parts); y, _ := iter.take_one(parts); -- 2.25.1