From 5c7d859fdb72307bd063304d9b280d4583d12312 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 26 Jan 2023 14:50:41 +0100 Subject: [PATCH] Some updates to builtin checking. --- lib/std/core/conv.c3 | 4 + lib/std/core/str.c3 | 5 +- src/compiler/sema_builtins.c | 263 ++++++++++++++++++++--------------- src/compiler/types.c | 3 + src/version.h | 2 +- 5 files changed, 165 insertions(+), 112 deletions(-) diff --git a/lib/std/core/conv.c3 b/lib/std/core/conv.c3 index 1612a553b..f346dbc16 100644 --- a/lib/std/core/conv.c3 +++ b/lib/std/core/conv.c3 @@ -307,6 +307,8 @@ fn usz! utf32to8(Char32[] utf32, String utf8_buffer) len -= used; ptr += used; } + // Zero terminate if there is space. + if (len > 0) ptr[0] = 0; return utf8_buffer.len - len; } @@ -331,6 +333,8 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer) i += width; ptr[len32++] = uc; } + // Zero terminate if possible + if (len32 + 1 < buf_len) ptr[len32] = 0; return len32; } diff --git a/lib/std/core/str.c3 b/lib/std/core/str.c3 index 7e99ec541..bbc0048d5 100644 --- a/lib/std/core/str.c3 +++ b/lib/std/core/str.c3 @@ -288,7 +288,7 @@ fn usz utf8_codepoints(String utf8) return len; } -fn Char32[]! utf8to32(String utf8, Allocator* allocator = mem::current_allocator) +fn Char32[]! utf8to32(String utf8, Allocator* allocator = mem::current_allocator()) { usz codepoints = conv::utf8_codepoints(utf8); Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?; @@ -297,7 +297,7 @@ fn Char32[]! utf8to32(String utf8, Allocator* allocator = mem::current_allocator return data[:codepoints]; } -fn String utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator) +fn String utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator()) { usz len = conv::utf8len_for_utf32(utf32); char* data = allocator.alloc(len + 1)!!; @@ -321,6 +321,7 @@ fn String! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocato usz len = conv::utf8len_for_utf16(utf16); char* data = allocator.alloc(len + 1)?; conv::utf16to8_unsafe(utf16, data)?; + data[len] = 0; return data[:len]; } diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 98dee7fc5..032d317d3 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -24,7 +24,7 @@ static bool sema_check_builtin_args_match(Expr **args, size_t arg_len); static bool sema_check_builtin_args_const(Expr **args, size_t arg_len); static bool sema_check_builtin_args(Expr **args, BuiltinArg *arg_type, size_t arg_len); static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, bool swizzle_two); -static inline unsigned builtin_expected_args(BuiltinFunction func); +static inline int builtin_expected_args(BuiltinFunction func); static bool sema_check_builtin_args_match(Expr **args, size_t arg_len) { @@ -163,24 +163,31 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, b { Expr **args = expr->call_expr.arguments; unsigned arg_count = vec_size(args); - if (swizzle_two && arg_count < 3) - { - SEMA_ERROR(expr, "Expected at least 3 arguments."); - return false; - } - else if (arg_count < 2) - { - SEMA_ERROR(expr, "Expected at least 2 arguments."); - return false; - } bool optional = false; int first_mask_value = swizzle_two ? 2 : 1; + Type *first = NULL; for (unsigned i = 0; i < first_mask_value; i++) { - if (!sema_analyse_expr(context, args[i])) return false; + Expr *arg = args[i]; + if (!sema_analyse_expr(context, arg)) return false; + if (!type_flat_is_vector(arg->type)) + { + SEMA_ERROR(arg, "A vector was expected here."); + return false; + } optional = optional || type_is_optional(args[i]->type); + if (i == 0) + { + first = type_no_optional(arg->type)->canonical; + continue; + } + else if (type_no_optional(arg->type->canonical) != first) + { + SEMA_ERROR(arg, "Vector type does not match the first vector."); + return false; + } } - unsigned components = type_flatten(args[0]->type)->array.len; + unsigned components = type_flatten(first)->array.len; if (swizzle_two) components *= 2; for (unsigned i = first_mask_value; i < arg_count; i++) { @@ -193,7 +200,12 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, b } if (mask_val->const_expr.ixx.i.low >= components) { - SEMA_ERROR(mask_val, "The swizzle position must be between 0 and %d.", components - 1); + if (components == 1) + { + SEMA_ERROR(mask_val, "The only possible swizzle position is 0."); + return false; + } + SEMA_ERROR(mask_val, "The swizzle position must be in the range 0-%d.", components - 1); return false; } } @@ -211,26 +223,104 @@ bool is_valid_atomicity(Expr* expr) return true; } + +bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) +{ + Expr **args = expr->call_expr.arguments; + Expr *pointer = args[0]; + + if (!sema_analyse_expr(context, pointer)) return false; + bool optional = IS_OPTIONAL(args[0]); + Type *comp_type = type_flatten(args[0]->type); + if (!type_is_pointer(comp_type)) + { + SEMA_ERROR(args[0], "Expected a pointer here."); + return false; + } + Type *pointee = comp_type->pointer; + for (int i = 1; i < 3; i++) + { + if (!sema_analyse_expr_rhs(context, pointee, args[i], true)) return false; + optional = optional || IS_OPTIONAL(args[i]); + } + for (int i = 3; i < 5; i++) + { + if (!sema_analyse_expr_rhs(context, type_bool, args[i], false)) return false; + if (!expr_is_const(args[i])) + { + SEMA_ERROR(args[i], "Expected a constant boolean value."); + return false; + } + } + for (int i = 5; i < 7; i++) + { + if (!sema_analyse_expr_rhs(context, type_char, args[i], false)) return false; + if (!is_valid_atomicity(args[i])) return false; + } + Expr *align = args[7]; + if (!sema_analyse_expr_rhs(context, type_usz, align, false)) return false; + if (!expr_is_const_int(align) + || !int_fits(align->const_expr.ixx, TYPE_U64) + || (!is_power_of_two(align->const_expr.ixx.i.low) && align->const_expr.ixx.i.low)) + { + SEMA_ERROR(args[7], "Expected a constant power-of-two alignment or zero."); + return false; + } + expr->type = type_add_optional(args[1]->type, optional); + return true; +} +bool sema_expr_analyse_syscall(SemaContext *context, Expr *expr) +{ + Expr **args = expr->call_expr.arguments; + unsigned arg_count = vec_size(args); + if (arg_count > 7) + { + SEMA_ERROR(args[7], "Only 7 arguments supported for $$syscall."); + } + bool optional = false; + for (unsigned i = 0; i < arg_count; i++) + { + Expr *arg = args[i]; + if (!sema_analyse_expr_rhs(context, type_uptr, arg, true)) return false; + optional = optional || type_is_optional(arg->type); + } + switch (platform_target.arch) + { + case ARCH_TYPE_AARCH64: + case ARCH_TYPE_AARCH64_BE: + case ARCH_TYPE_X86: + case ARCH_TYPE_X86_64: + break; + default: + SEMA_ERROR(expr, "Target does not support $$syscall."); + return false; + } + expr->type = type_add_optional(type_uptr, optional); + return true; +} + bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) { expr->call_expr.is_builtin = true; expr->call_expr.arguments = sema_expand_vasplat_exprs(context, expr->call_expr.arguments); + BuiltinFunction func = exprptr(expr->call_expr.function)->builtin_expr.builtin; - if (func == BUILTIN_SWIZZLE || func == BUILTIN_SWIZZLE2) - { - return sema_expr_analyse_swizzle(context, expr, func == BUILTIN_SWIZZLE2); - } - unsigned expected_args = builtin_expected_args(func); Expr **args = expr->call_expr.arguments; unsigned arg_count = vec_size(args); + int expected_args = builtin_expected_args(func); + bool expect_vararg = false; + if (expected_args < 0) + { + expected_args = -expected_args; + expect_vararg = true; + } - bool is_vararg = func == BUILTIN_SYSCALL; // 1. Handle arg count, so at least we know that is ok. - if (expected_args != arg_count && !is_vararg) + if (arg_count < expected_args || (arg_count > expected_args && !expect_vararg)) { if (arg_count == 0) { - SEMA_ERROR(expr, "Expected %d arguments to builtin.", expected_args); + SEMA_ERROR(expr, "Expected %s%d arguments to builtin.", expect_vararg ? "at least " : "", expected_args); return false; } if (arg_count < expected_args) @@ -241,11 +331,34 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) SEMA_ERROR(args[expected_args], "Too many arguments."); return false; } - if (is_vararg && expected_args > arg_count) + + switch (func) { - SEMA_ERROR(expr, "Expected at least %d arguments to builtin.\n", expected_args); - return false; + case BUILTIN_SWIZZLE2: + case BUILTIN_SWIZZLE: + return sema_expr_analyse_swizzle(context, expr, func == BUILTIN_SWIZZLE2); + case BUILTIN_SYSCALL: + return sema_expr_analyse_syscall(context, expr); + case BUILTIN_TRAP: + case BUILTIN_UNREACHABLE: + expr->type = type_void; + return true; + case BUILTIN_SYSCLOCK: + expr->type = type_ulong; + return true; + case BUILTIN_GET_ROUNDING_MODE: + expr->type = type_int; + return true; + case BUILTIN_FRAMEADDRESS: + case BUILTIN_STACKTRACE: + expr->type = type_voidptr; + return true; + case BUILTIN_COMPARE_EXCHANGE: + return sema_expr_analyse_compare_exchange(context, expr); + default: + break; } + bool optional = false; // 2. We can now check all the arguments, since they in general work on the @@ -259,62 +372,6 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) Type *rtype = NULL; switch (func) { - case BUILTIN_SWIZZLE2: - case BUILTIN_SWIZZLE: - UNREACHABLE; - case BUILTIN_TRAP: - case BUILTIN_UNREACHABLE: - rtype = type_void; - break; - case BUILTIN_COMPARE_EXCHANGE: - { - Type *comp_type = type_no_optional(args[0]->type->canonical); - if (!type_is_pointer(comp_type)) - { - SEMA_ERROR(args[0], "Expected a pointer here."); - return false; - } - Type *pointee = comp_type->pointer; - for (int i = 1; i < 3; i++) - { - if (pointee != type_no_optional(args[i]->type->canonical)) - { - SEMA_ERROR(args[i], "Expected an argument of type %s.", type_quoted_error_string(pointee)); - return false; - } - } - for (int i = 3; i < 5; i++) - { - if (args[i]->type->canonical != type_bool || !expr_is_const(args[i])) - { - SEMA_ERROR(args[i], "Expected a constant boolean value."); - return false; - } - } - for (int i = 5; i < 7; i++) - { - if (!is_valid_atomicity(args[i])) return false; - } - Expr *align = args[7]; - if (!expr_is_const_int(align) - || !int_fits(align->const_expr.ixx, TYPE_U64) - || (!is_power_of_two(align->const_expr.ixx.i.low) && align->const_expr.ixx.i.low)) - { - SEMA_ERROR(args[7], "Expected a constant power-of-two alignment or zero."); - return false; - } - rtype = args[1]->type; - break; - } - case BUILTIN_SYSCLOCK: - rtype = type_ulong; - break; - case BUILTIN_GET_ROUNDING_MODE: - rtype = type_int; - break; - case BUILTIN_FRAMEADDRESS: - rtype = type_voidptr; - break; case BUILTIN_SET_ROUNDING_MODE: if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_INTEGER }, @@ -323,27 +380,6 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) rtype = type_void; break; case BUILTIN_SYSCALL: - if (arg_count > 7) - { - SEMA_ERROR(args[7], "Only 7 arguments supported for $$syscall."); - } - rtype = type_uptr; - for (unsigned i = 0; i < arg_count; i++) - { - if (!cast_implicit(context, args[i], type_uptr)) return false; - } - switch (platform_target.arch) - { - case ARCH_TYPE_AARCH64: - case ARCH_TYPE_AARCH64_BE: - case ARCH_TYPE_X86: - case ARCH_TYPE_X86_64: - break; - default: - SEMA_ERROR(expr, "Target does not support $$syscall."); - return false; - } - break; case BUILTIN_VECCOMPGE: case BUILTIN_VECCOMPEQ: case BUILTIN_VECCOMPLE: @@ -405,9 +441,6 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_check_builtin_args_const(&args[3], 2)) return false; rtype = type_void; break; - case BUILTIN_STACKTRACE: - rtype = type_voidptr; - break; case BUILTIN_BITREVERSE: case BUILTIN_BSWAP: case BUILTIN_CTLZ: @@ -590,16 +623,31 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) break; } case BUILTIN_NONE: + case BUILTIN_COMPARE_EXCHANGE: + case BUILTIN_FRAMEADDRESS: + case BUILTIN_GET_ROUNDING_MODE: + case BUILTIN_SWIZZLE: + case BUILTIN_SWIZZLE2: + case BUILTIN_STACKTRACE: + case BUILTIN_SYSCLOCK: + case BUILTIN_TRAP: + case BUILTIN_UNREACHABLE: UNREACHABLE } expr->type = type_add_optional(rtype, optional); return true; } -static inline unsigned builtin_expected_args(BuiltinFunction func) +static inline int builtin_expected_args(BuiltinFunction func) { switch (func) { + case BUILTIN_SYSCALL: + return -1; + case BUILTIN_SWIZZLE: + return -2; + case BUILTIN_SWIZZLE2: + return -3; case BUILTIN_GET_ROUNDING_MODE: case BUILTIN_STACKTRACE: case BUILTIN_SYSCLOCK: @@ -633,7 +681,6 @@ static inline unsigned builtin_expected_args(BuiltinFunction func) case BUILTIN_ROUNDEVEN: case BUILTIN_SIN: case BUILTIN_SQRT: - case BUILTIN_SYSCALL: case BUILTIN_TRUNC: case BUILTIN_VOLATILE_LOAD: case BUILTIN_REDUCE_MUL: @@ -688,8 +735,6 @@ static inline unsigned builtin_expected_args(BuiltinFunction func) return 5; case BUILTIN_COMPARE_EXCHANGE: return 8; - case BUILTIN_SWIZZLE2: - case BUILTIN_SWIZZLE: case BUILTIN_NONE: UNREACHABLE } diff --git a/src/compiler/types.c b/src/compiler/types.c index 55a3a565e..fa52d9027 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -1041,6 +1041,9 @@ Type *type_get_indexed_type(Type *type) case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; goto RETRY; + case TYPE_OPTIONAL: + type = type->optional; + goto RETRY; case TYPE_TYPEDEF: type = type->canonical; goto RETRY; diff --git a/src/version.h b/src/version.h index dce0782ff..195f2d729 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.23" \ No newline at end of file +#define COMPILER_VERSION "0.4.24" \ No newline at end of file