From 63fc77a86175727f205e1914e4c48386ca39b3ba Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 4 Sep 2024 09:34:51 +0200 Subject: [PATCH] Move of const to separate file and removal of old concat code. --- CMakeLists.txt | 4 +- src/compiler/compiler_internal.h | 4 +- src/compiler/diagnostics.c | 2 +- src/compiler/sema_const.c | 518 +++++++++++++++++++ src/compiler/sema_expr.c | 830 +------------------------------ src/compiler/sema_internal.h | 16 + src/compiler/sema_stmts.c | 21 +- src/compiler/semantic_analyser.c | 2 +- 8 files changed, 567 insertions(+), 830 deletions(-) create mode 100644 src/compiler/sema_const.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 461c0f7ca..4d4a33455 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,7 +337,9 @@ add_executable(c3c src/utils/http.c src/compiler/sema_liveness.c src/build/common_build.c - ${CMAKE_BINARY_DIR}/git_hash.h) + src/compiler/sema_const.c + ${CMAKE_BINARY_DIR}/git_hash.h + ) if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") # We are inside of a git repository so rebuilding the hash every time something changes. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 4b9d01754..284072755 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -64,7 +64,7 @@ typedef uint16_t FileId; #define RETURN_PRINT_ERROR_HERE(...) do { print_error_at(c->span, __VA_ARGS__); return false; } while (0) #define PRINT_ERROR_LAST(...) print_error_at(c->prev_span, __VA_ARGS__) #define RETURN_PRINT_ERROR_LAST(...) do { print_error_at(c->prev_span, __VA_ARGS__); return false; } while (0) -#define SEMA_NOTE(_node, ...) sema_error_prev_at((_node)->span, __VA_ARGS__) +#define SEMA_NOTE(_node, ...) sema_note_prev_at((_node)->span, __VA_ARGS__) #define EXPAND_EXPR_STRING(str_) (str_)->const_expr.bytes.len, (str_)->const_expr.bytes.ptr #define TABLE_MAX_LOAD 0.5 @@ -2245,7 +2245,7 @@ bool sema_resolve_type_info(SemaContext *context, TypeInfo *type_info, ResolveTy void print_error_at(SourceSpan loc, const char *message, ...); void print_error_after(SourceSpan loc, const char *message, ...); -void sema_error_prev_at(SourceSpan loc, const char *message, ...); +void sema_note_prev_at(SourceSpan loc, const char *message, ...); void sema_verror_range(SourceSpan location, const char *message, va_list args); void print_error(ParseContext *context, const char *message, ...); diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 89e14bb6a..9e14de1cc 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -185,7 +185,7 @@ void print_error_after(SourceSpan loc, const char *message, ...) va_end(list); } -void sema_error_prev_at(SourceSpan loc, const char *message, ...) +void sema_note_prev_at(SourceSpan loc, const char *message, ...) { va_list args; va_start(args, message); diff --git a/src/compiler/sema_const.c b/src/compiler/sema_const.c new file mode 100644 index 000000000..bfbf3248a --- /dev/null +++ b/src/compiler/sema_const.c @@ -0,0 +1,518 @@ +// Copyright (c) 2024 Christoffer Lerno and contributors. All rights reserved. +// Use of this source code is governed by the GNU LGPLv3.0 license +// a copy of which can be found in the LICENSE file. + +#include "sema_internal.h" +static inline ArraySize sema_get_const_len(SemaContext *context, Expr *expr); +static bool sema_append_const_array_one(SemaContext *context, Expr *expr, Expr *list, Expr *element); +static ArrayIndex len_from_const_initializer(ConstInitializer *init); +static bool sema_append_concat_const_bytes(SemaContext *context, Expr *expr, Expr *list, Expr *element, bool is_append); + + +typedef enum +{ + CONCAT_UNKNOWN, + CONCAT_JOIN_BYTES, + CONCAT_JOIN_ARRAYS, + CONCAT_JOIN_LISTS, +} ConcatType; + + +static ArrayIndex len_from_const_initializer(ConstInitializer *init) +{ + switch (init->kind) + { + case CONST_INIT_ZERO: + return 0; + case CONST_INIT_STRUCT: + case CONST_INIT_UNION: + case CONST_INIT_VALUE: + case CONST_INIT_ARRAY_VALUE: + return -1; + case CONST_INIT_ARRAY: + { + ArrayIndex max = 0; + FOREACH(ConstInitializer *, element, init->init_array.elements) + { + assert(element->kind == CONST_INIT_ARRAY_VALUE); + if (element->init_array_value.index > max) max = element->init_array_value.index; + } + return max; + } + case CONST_INIT_ARRAY_FULL: + return vec_size(init->init_array_full); + } + UNREACHABLE +} + +ArrayIndex sema_len_from_const(Expr *expr) +{ + // We also handle the case where we have a cast from a const array. + if (!sema_cast_const(expr)) + { + if (type_flatten(expr->type)->type_kind != TYPE_SLICE) return -1; + if (expr->expr_kind == EXPR_SLICE) + { + return range_const_len(&expr->subscript_expr.range); + } + if (expr->expr_kind != EXPR_CAST) return -1; + if (expr->cast_expr.kind != CAST_APTSA) return -1; + Expr *inner = exprptr(expr->cast_expr.expr); + if (inner->expr_kind != EXPR_UNARY || inner->unary_expr.operator != UNARYOP_ADDR) return -1; + inner = inner->unary_expr.expr; + if (!sema_cast_const(inner)) return -1; + expr = inner; + } + switch (expr->const_expr.const_kind) + { + case CONST_FLOAT: + case CONST_INTEGER: + case CONST_BOOL: + case CONST_ENUM: + case CONST_ERR: + case CONST_POINTER: + case CONST_TYPEID: + case CONST_MEMBER: + return -1; + case CONST_BYTES: + case CONST_STRING: + return expr->const_expr.bytes.len; + case CONST_INITIALIZER: + return len_from_const_initializer(expr->const_expr.initializer); + case CONST_UNTYPED_LIST: + return vec_size(expr->const_expr.untyped_list); + } + UNREACHABLE +} + +static inline bool sema_expr_const_append(SemaContext *context, Expr *append_expr, Expr *list, Expr *element) +{ + Expr **untyped_list = NULL; + switch (list->const_expr.const_kind) + { + case CONST_INITIALIZER: + assert(list->type != type_untypedlist); + return sema_append_const_array_one(context, append_expr, list, element); + case CONST_UNTYPED_LIST: + untyped_list = list->const_expr.untyped_list; + break; + case CONST_POINTER: + if (list->type->canonical->type_kind == TYPE_SLICE) + { + return sema_append_const_array_one(context, append_expr, list, element); + } + FALLTHROUGH; + case CONST_BYTES: + case CONST_STRING: + return sema_append_concat_const_bytes(context, append_expr, list, element, true); + default: + RETURN_SEMA_ERROR(list, "Expected some kind of list or vector here."); + } + vec_add(untyped_list, element); + append_expr->expr_kind = EXPR_CONST; + append_expr->const_expr = (ExprConst) { .untyped_list = untyped_list, .const_kind = CONST_UNTYPED_LIST }; + append_expr->type = type_untypedlist; + append_expr->resolve_status = RESOLVE_DONE; + return true; +} + +static bool sema_concat_const_bytes(SemaContext *context, Expr *expr, Type *type, bool is_bytes, const char *a, const char *b, ArraySize alen, ArraySize blen) +{ + Type *indexed = type_get_indexed_type(type); + char *data = malloc_arena(alen + blen + 1); + char *current = data; + if (alen) memcpy(current, a, alen); + current += alen; + if (blen) memcpy(current, b, blen); + current += blen; + current[0] = '\0'; + expr->expr_kind = EXPR_CONST; + expr->const_expr = (ExprConst) { + .const_kind = is_bytes ? CONST_BYTES : CONST_STRING, + .bytes.ptr = data, + .bytes.len = alen + blen + }; + expr->resolve_status = RESOLVE_DONE; + expr->type = is_bytes ? type_get_array(indexed, alen + blen) : type; + return true; +} + +static bool sema_concat_character(SemaContext *context, Expr *expr, Type *type, const char *a, ArraySize alen, uint64_t c) +{ + char append_array[4]; + int len; + if (c <= 0x7f) + { + append_array[0] = (char) c; + len = 1; + } + else if (c <= 0x7ff) + { + append_array[0] = (char) (0xC0 | (c >> 6)); + append_array[1] = (char) (0x80 | (c & 0x3F)); + len = 2; + } + else if (c <= 0xffff) + { + append_array[0] = (char) (0xE0 | (c >> 12)); + append_array[1] = (char) (0x80 | ((c >> 6) & 0x3F)); + append_array[2] = (char) (0x80 | (c & 0x3F)); + len = 3; + } + else + { + append_array[0] = (char) (0xF0 | (c >> 18)); + append_array[1] = (char) (0x80 | ((c >> 12) & 0x3F)); + append_array[2] = (char) (0x80 | ((c >> 6) & 0x3F)); + append_array[3] = (char) (0x80 | (c & 0x3F)); + len = 4; + } + return sema_concat_const_bytes(context, expr, type, false, a, append_array, alen, len); +} + +/** + * 1. String + Bytes|String + * 2. Bytes + Bytes|String + * 3. Bytes + (i)char => Bytes + * 4. String + character => String + * 5. String + (i)char array/vector => String // Disallowed for now + * 6. Bytes + (i)char array/vector => Bytes // Disallowed for now + */ +static bool sema_concat_bytes_and_other(SemaContext *context, Expr *expr, Expr *left, Expr *right) +{ + ArraySize len = left->const_expr.bytes.len; + bool is_bytes = left->const_expr.const_kind == CONST_BYTES; + Type *indexed = type_get_indexed_type(left->type); + const char *left_bytes = left->const_expr.bytes.ptr; + RETRY:; + switch (right->const_expr.const_kind) + { + case CONST_INTEGER: + if (is_bytes) + { + // 2. Bytes + (i)char => Bytes + if (!cast_implicit(context, right, type_char, false)) return false; + char c = (char) int_to_i64(right->const_expr.ixx); + return sema_concat_const_bytes(context, expr, left->type, true, left_bytes, &c, len, 1); + } + // 1. String + character => String + if (int_is_neg(right->const_expr.ixx) || int_icomp(right->const_expr.ixx, 0x10FFFF, BINARYOP_GT)) + { + RETURN_SEMA_ERROR(right, "Cannot concatenate a string with an non-unicode value."); + } + return sema_concat_character(context, expr, left->type, left_bytes, len, right->const_expr.ixx.i.low); + case CONST_FLOAT: + case CONST_BOOL: + case CONST_ENUM: + case CONST_ERR: + case CONST_POINTER: + case CONST_TYPEID: + case CONST_MEMBER: + RETURN_SEMA_ERROR(expr, "Concatenating %s with %s is not possible at compile time.", + type_quoted_error_string(left->type), type_to_error_string(right->type)); + case CONST_BYTES: + case CONST_STRING: + return sema_concat_const_bytes(context, expr, left->type, is_bytes, left_bytes, + right->const_expr.bytes.ptr, + len, right->const_expr.bytes.len); + case CONST_UNTYPED_LIST: + if (!cast_implicit(context, right, type_get_inferred_array(indexed), false)) return false; + goto RETRY; + case CONST_INITIALIZER: + if (!cast_implicit(context, right, type_get_inferred_array(indexed), false)) return false; + expr_contract_array(&right->const_expr, left->const_expr.const_kind); + goto RETRY; + } + UNREACHABLE +} +static bool sema_append_concat_const_bytes(SemaContext *context, Expr *expr, Expr *list, Expr *element, bool is_append) +{ + Type *indexed = type_get_indexed_type(list->type); + assert(indexed && "This should always work"); + if (is_append && !cast_implicit(context, element, indexed, false)) return false; + size_t str_len = list->const_expr.bytes.len; + size_t element_len = is_append ? 1 : element->const_expr.bytes.len; + bool is_bytes = list->const_expr.const_kind == CONST_BYTES; + char *data = malloc_arena(str_len + element_len + 1); + char *current = data; + if (str_len) memcpy(current, list->const_expr.bytes.ptr, str_len); + current += str_len; + if (is_append) + { + current[0] = (unsigned char)element->const_expr.ixx.i.low; + } + else + { + if (element_len) memcpy(current, element->const_expr.bytes.ptr, element_len); + } + current += element_len; + current[0] = '\0'; + expr->expr_kind = EXPR_CONST; + expr->const_expr = (ExprConst) { + .const_kind = list->const_expr.const_kind, + .bytes.ptr = data, + .bytes.len = str_len + element_len + }; + expr->resolve_status = RESOLVE_DONE; + expr->type = is_bytes ? type_get_array(indexed, str_len + element_len) : list->type; + return true; +} + +static inline ArraySize sema_get_const_len(SemaContext *context, Expr *expr) +{ + switch (expr->const_expr.const_kind) + { + case CONST_FLOAT: + case CONST_INTEGER: + case CONST_BOOL: + case CONST_ENUM: + case CONST_ERR: + case CONST_POINTER: + case CONST_TYPEID: + return 1; + case CONST_BYTES: + case CONST_STRING: + return expr->const_expr.bytes.len; + case CONST_INITIALIZER: + { + bool may_be_array; + bool is_const_size; + ArrayIndex len = (ArraySize) sema_get_initializer_const_array_size(context, expr, &may_be_array, + &is_const_size); + assert(is_const_size); + return (ArraySize) len; + } + case CONST_UNTYPED_LIST: + return vec_size(expr->const_expr.untyped_list); + case CONST_MEMBER: + return 1; + } + UNREACHABLE +} + +static bool sema_append_const_array_one(SemaContext *context, Expr *expr, Expr *list, Expr *element) +{ + bool is_empty_slice = list->const_expr.const_kind == CONST_POINTER; + if (!is_empty_slice && list->const_expr.initializer->kind != CONST_INIT_ARRAY_FULL) + { + RETURN_SEMA_ERROR(list, "Only fully initialized arrays may be appended to."); + } + Type *array_type = type_flatten(list->type); + bool is_vector = array_type->type_kind == TYPE_VECTOR || array_type->type_kind == TYPE_INFERRED_VECTOR; + unsigned len = (is_empty_slice ? 0 : sema_get_const_len(context, list)) + 1; + Type *indexed = type_get_indexed_type(list->type); + Type *new_type = is_vector ? type_get_vector(indexed, len) : type_get_array(indexed, len); + ConstInitializer *init = list->const_expr.initializer; + ConstInitializer **inits = VECNEW(ConstInitializer*, len); + if (!is_empty_slice) + { + FOREACH(ConstInitializer *, i, init->init_array_full) vec_add(inits, i); + } + + assert(element->resolve_status == RESOLVE_DONE); + if (!cast_implicit(context, element, indexed, false)) return false; + ConstInitializer *in = CALLOCS(ConstInitializer); + in->kind = CONST_INIT_VALUE; + in->init_value = element; + vec_add(inits, in); + expr->expr_kind = EXPR_CONST; + expr->resolve_status = RESOLVE_DONE; + expr->type = new_type; + ConstInitializer *new_init = CALLOCS(ConstInitializer); + new_init->init_array_full = inits; + new_init->type = new_type; + new_init->kind = CONST_INIT_ARRAY_FULL; + expr->const_expr = (ExprConst) { + .const_kind = CONST_INITIALIZER, + .initializer = new_init + }; + return true; +} + +/** + * The following valid cases exist: + * + * 1. String/Bytes + ... => String/Bytes + * 2. Vector/slice/array + Untyped list => Merged untyped list + * 3. Vector/slice/array + arraylike => vector/array iff canoncial match, otherwise Untyped list + * 4. Untyped list + Vector/array/slice => Merged untyped list + * 5. Vector/array/slice + element => Vector/array/slice + 1 len iff canonical match, Untyped list otherwise + * 6. Untyped list + element => Untyped list + */ +bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *left, Expr *right) +{ + assert(concat_expr->resolve_status == RESOLVE_RUNNING); + bool join_single = false; + ArraySize len = 0; + bool use_array = true; + Type *indexed_type = NULL; + if (!sema_analyse_expr(context, left)) return false; + if (!sema_cast_const(left)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); + if (!sema_analyse_expr(context, right)) return false; + if (!sema_cast_const(right)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); + Type *element_type = left->type->canonical; + Type *right_type = right->type->canonical; + ConstKind right_kind = right->const_expr.const_kind; + switch (left->const_expr.const_kind) + { + case CONST_POINTER: + if (element_type->type_kind == TYPE_SLICE) + { + len = 0; + indexed_type = type_get_indexed_type(element_type); + break; + } + FALLTHROUGH; + case CONST_FLOAT: + case CONST_INTEGER: + case CONST_BOOL: + case CONST_ENUM: + case CONST_ERR: + case CONST_TYPEID: + RETURN_SEMA_ERROR(left, "Only bytes, strings and list-like constants can be concatenated."); + case CONST_BYTES: + case CONST_STRING: + return sema_concat_bytes_and_other(context, concat_expr, left, right); + case CONST_INITIALIZER: + switch (type_flatten(element_type)->type_kind) + { + case TYPE_VECTOR: + case TYPE_INFERRED_VECTOR: + use_array = false; + FALLTHROUGH; + case TYPE_SLICE: + case TYPE_ARRAY: + case TYPE_INFERRED_ARRAY: + { + switch (left->const_expr.initializer->kind) + { + case CONST_INIT_ARRAY_FULL: + break; + case CONST_INIT_ZERO: + if (type_flatten(element_type)->type_kind == TYPE_SLICE) break; + FALLTHROUGH; + default: + RETURN_SEMA_ERROR(left, "Only fully initialized arrays may be concatenated."); + } + indexed_type = type_get_indexed_type(element_type); + assert(indexed_type); + len = sema_get_const_len(context, left); + break; + } + case TYPE_UNTYPED_LIST: + UNREACHABLE + default: + RETURN_SEMA_ERROR(left, "Only bytes, strings and array-like constants can be concatenated."); + } + break; + case CONST_UNTYPED_LIST: + len = vec_size(left->const_expr.untyped_list); + break; + case CONST_MEMBER: + RETURN_SEMA_ERROR(left, "This can't be concatenated."); + } + switch (right->const_expr.const_kind) + { + case CONST_FLOAT: + case CONST_INTEGER: + case CONST_BOOL: + case CONST_ENUM: + case CONST_ERR: + case CONST_POINTER: + case CONST_TYPEID: + case CONST_MEMBER: + return sema_expr_const_append(context, concat_expr, left, right); + case CONST_BYTES: + case CONST_STRING: + if (left->type == type_untypedlist || indexed_type == right_type) return sema_expr_const_append(context, concat_expr, left, right); + if (!type_is_integer(indexed_type) || type_size(indexed_type) != 1) + { + RETURN_SEMA_ERROR(right, "You can't concatenate %s and %s.", type_quoted_error_string(left->type), + type_to_error_string(right_type)); + } + if (!cast_implicit(context, right, type_get_inferred_array(indexed_type), false)) return false; + expr_contract_array(&left->const_expr, CONST_BYTES); + return sema_concat_bytes_and_other(context, concat_expr, left, right); + case CONST_UNTYPED_LIST: + break; + case CONST_INITIALIZER: + if (indexed_type && cast_implicit_silent(context, right, indexed_type, false)) + { + return sema_expr_const_append(context, concat_expr, left, right); + } + break; + } + if (indexed_type && !cast_implicit_silent(context, right, type_get_inferred_array(indexed_type), false)) + { + indexed_type = NULL; + } + len += sema_get_const_len(context, right); + if (!indexed_type) + { + Expr **untyped_exprs = VECNEW(Expr*, len + 1); + Expr *exprs[2] = { left, right }; + for (unsigned i = 0; i < 2; i++) + { + Expr *single_expr = exprs[i]; + if (expr_is_const_untyped_list(single_expr)) + { + FOREACH(Expr *, expr_untyped, single_expr->const_expr.untyped_list) + { + vec_add(untyped_exprs, expr_untyped); + } + continue; + } + ConstInitializer *init = single_expr->const_expr.initializer; + if (init->kind != CONST_INIT_ARRAY_FULL) + { + assert(init->type != type_untypedlist); + RETURN_SEMA_ERROR(single_expr, "Expected a full array here."); + } + FOREACH(ConstInitializer *, val, init->init_array_full) + { + vec_add(untyped_exprs, val->init_value); + } + } + concat_expr->expr_kind = EXPR_CONST; + concat_expr->type = type_untypedlist; + concat_expr->resolve_status = RESOLVE_DONE; + concat_expr->const_expr = (ExprConst) { + .const_kind = CONST_UNTYPED_LIST, + .untyped_list = untyped_exprs + }; + return true; + } + ConstInitializer **inits = VECNEW(ConstInitializer*, len); + Expr *exprs[2] = { left, right }; + for (int i = 0; i < 2; i++) + { + Expr *element = exprs[i]; + assert(element->const_expr.const_kind == CONST_INITIALIZER); + ConstInitType init_type = element->const_expr.initializer->kind; + switch (init_type) + { + case CONST_INIT_ARRAY_FULL: + break; + case CONST_INIT_ZERO: + if (type_flatten(element->type)->type_kind == TYPE_SLICE) continue; + default: + RETURN_SEMA_ERROR(element, "Only fully initialized arrays may be concatenated."); + } + FOREACH(ConstInitializer *, init, element->const_expr.initializer->init_array_full) + { + vec_add(inits, init); + } + } + concat_expr->expr_kind = EXPR_CONST; + concat_expr->resolve_status = RESOLVE_DONE; + concat_expr->type = use_array ? type_get_array(indexed_type, len) : type_get_vector(indexed_type, len); + ConstInitializer *new_init = CALLOCS(ConstInitializer); + new_init->init_array_full = inits; + new_init->type = concat_expr->type; + new_init->kind = CONST_INIT_ARRAY_FULL; + concat_expr->const_expr = (ExprConst) { + .const_kind = CONST_INITIALIZER, + .initializer = new_init + }; + return true; +} diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index f65e51705..c05d315ad 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -144,7 +144,6 @@ static bool sema_slice_len_is_in_range(SemaContext *context, Type *type, Expr *l static bool sema_slice_index_is_in_range(SemaContext *context, Type *type, Expr *index_expr, bool end_index, bool from_end, bool *remove_from_end); static Expr **sema_vasplat_append(SemaContext *context, Expr **init_expressions, Expr *expr); INLINE Expr **sema_expand_vasplat(SemaContext *c, Expr **list, unsigned index); -static inline IndexDiff range_const_len(Range *range); static inline bool sema_expr_begin_analyse(SemaContext *context, Expr *expr); static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr); @@ -193,6 +192,7 @@ static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool // -- implementations +// Limit folding to integers and floats, exclude vectors. static inline bool sema_constant_fold_ops(Expr *expr) { if (!sema_cast_const(expr)) return false; @@ -256,38 +256,6 @@ Expr *sema_enter_inline_member(Expr *parent, CanonicalType *type) } } -static inline ArraySize sema_get_const_len(SemaContext *context, Expr *expr) -{ - switch (expr->const_expr.const_kind) - { - case CONST_FLOAT: - case CONST_INTEGER: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_POINTER: - case CONST_TYPEID: - return 1; - case CONST_BYTES: - case CONST_STRING: - return expr->const_expr.bytes.len; - case CONST_INITIALIZER: - { - bool may_be_array; - bool is_const_size; - ArrayIndex len = (ArraySize) sema_get_initializer_const_array_size(context, expr, &may_be_array, - &is_const_size); - assert(is_const_size); - return (ArraySize) len; - } - case CONST_UNTYPED_LIST: - return vec_size(expr->const_expr.untyped_list); - case CONST_MEMBER: - return 1; - } - UNREACHABLE -} - Expr *sema_expr_analyse_ct_arg_index(SemaContext *context, Expr *index_expr, unsigned *index_ref, bool report_error) { unsigned args = vec_size(context->macro_varargs); @@ -5057,21 +5025,6 @@ bool range_is_const(Range *range) if (start && !expr_is_const(start)) return false; return true; } -static inline IndexDiff range_const_len(Range *range) -{ - Expr *start = exprptr(range->start); - Expr *end = exprptrzero(range->end); - if (!end || !expr_is_const_int(end)) return -1; - if (!int_fits(end->const_expr.ixx, TYPE_I32)) return -1; - IndexDiff end_val = (IndexDiff)int_to_i64(end->const_expr.ixx); - if (range->is_len) return end_val; - if (!expr_is_const_int(start)) return -1; - if (!int_fits(start->const_expr.ixx, TYPE_I32)) return -1; - IndexDiff start_val = (IndexDiff)int_to_i64(start->const_expr.ixx); - if (range->start_from_end && range->end_from_end) return start_val - end_val + 1; - if (range->start_from_end != range->end_from_end) return -1; - return end_val - start_val + 1; -} static bool sema_expr_analyse_slice_assign(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped) { @@ -7081,496 +7034,6 @@ static inline bool sema_expr_analyse_ct_and_or(SemaContext *context, Expr *expr, return true; } -typedef enum -{ - CONCAT_UNKNOWN, - CONCAT_JOIN_BYTES, - CONCAT_JOIN_ARRAYS, - CONCAT_JOIN_LISTS, -} ConcatType; - - -static bool sema_append_const_array_element(SemaContext *context, Expr *expr, Expr *list, Expr **exprs) -{ - bool is_empty_slice = list->const_expr.const_kind == CONST_POINTER; - if (!is_empty_slice && list->const_expr.initializer->kind != CONST_INIT_ARRAY_FULL) - { - RETURN_SEMA_ERROR(list, "Only fully initialized arrays may be appended to."); - } - Type *array_type = type_flatten(list->type); - bool is_vector = array_type->type_kind == TYPE_VECTOR || array_type->type_kind == TYPE_INFERRED_VECTOR; - unsigned len = (is_empty_slice ? 0 : sema_get_const_len(context, list)) + vec_size(exprs) - 1; - Type *indexed = type_get_indexed_type(list->type); - Type *new_type = is_vector ? type_get_vector(indexed, len) : type_get_array(indexed, len); - ConstInitializer *init = list->const_expr.initializer; - ConstInitializer **inits = VECNEW(ConstInitializer*, len); - if (!is_empty_slice) - { - FOREACH(ConstInitializer *, i, init->init_array_full) vec_add(inits, i); - } - unsigned elements = vec_size(exprs); - for (unsigned i = 1; i < elements; i++) - { - Expr *element = exprs[i]; - if (!sema_analyse_inferred_expr(context, indexed, element)) return false; - if (!cast_implicit(context, element, indexed, false)) return false; - ConstInitializer *in = CALLOCS(ConstInitializer); - in->kind = CONST_INIT_VALUE; - in->init_value = element; - vec_add(inits, in); - } - expr->expr_kind = EXPR_CONST; - expr->resolve_status = RESOLVE_DONE; - expr->type = new_type; - ConstInitializer *new_init = CALLOCS(ConstInitializer); - new_init->init_array_full = inits; - new_init->type = new_type; - new_init->kind = CONST_INIT_ARRAY_FULL; - expr->const_expr = (ExprConst) { - .const_kind = CONST_INITIALIZER, - .initializer = new_init - }; - return true; -} - -static bool sema_append_const_array_one(SemaContext *context, Expr *expr, Expr *list, Expr *element) -{ - bool is_empty_slice = list->const_expr.const_kind == CONST_POINTER; - if (!is_empty_slice && list->const_expr.initializer->kind != CONST_INIT_ARRAY_FULL) - { - RETURN_SEMA_ERROR(list, "Only fully initialized arrays may be appended to."); - } - Type *array_type = type_flatten(list->type); - bool is_vector = array_type->type_kind == TYPE_VECTOR || array_type->type_kind == TYPE_INFERRED_VECTOR; - unsigned len = (is_empty_slice ? 0 : sema_get_const_len(context, list)) + 1; - Type *indexed = type_get_indexed_type(list->type); - Type *new_type = is_vector ? type_get_vector(indexed, len) : type_get_array(indexed, len); - ConstInitializer *init = list->const_expr.initializer; - ConstInitializer **inits = VECNEW(ConstInitializer*, len); - if (!is_empty_slice) - { - FOREACH(ConstInitializer *, i, init->init_array_full) vec_add(inits, i); - } - - assert(element->resolve_status == RESOLVE_DONE); - if (!cast_implicit(context, element, indexed, false)) return false; - ConstInitializer *in = CALLOCS(ConstInitializer); - in->kind = CONST_INIT_VALUE; - in->init_value = element; - vec_add(inits, in); - expr->expr_kind = EXPR_CONST; - expr->resolve_status = RESOLVE_DONE; - expr->type = new_type; - ConstInitializer *new_init = CALLOCS(ConstInitializer); - new_init->init_array_full = inits; - new_init->type = new_type; - new_init->kind = CONST_INIT_ARRAY_FULL; - expr->const_expr = (ExprConst) { - .const_kind = CONST_INITIALIZER, - .initializer = new_init - }; - return true; -} - -static bool sema_concat_const_bytes(SemaContext *context, Expr *expr, Type *type, bool is_bytes, const char *a, const char *b, ArraySize alen, ArraySize blen) -{ - Type *indexed = type_get_indexed_type(type); - char *data = malloc_arena(alen + blen + 1); - char *current = data; - if (alen) memcpy(current, a, alen); - current += alen; - if (blen) memcpy(current, b, blen); - current += blen; - current[0] = '\0'; - expr->expr_kind = EXPR_CONST; - expr->const_expr = (ExprConst) { - .const_kind = is_bytes ? CONST_BYTES : CONST_STRING, - .bytes.ptr = data, - .bytes.len = alen + blen - }; - expr->resolve_status = RESOLVE_DONE; - expr->type = is_bytes ? type_get_array(indexed, alen + blen) : type; - return true; -} - -static bool sema_concat_character(SemaContext *context, Expr *expr, Type *type, const char *a, ArraySize alen, uint64_t c) -{ - char append_array[4]; - int len; - if (c <= 0x7f) - { - append_array[0] = (char) c; - len = 1; - } - else if (c <= 0x7ff) - { - append_array[0] = (char) (0xC0 | (c >> 6)); - append_array[1] = (char) (0x80 | (c & 0x3F)); - len = 2; - } - else if (c <= 0xffff) - { - append_array[0] = (char) (0xE0 | (c >> 12)); - append_array[1] = (char) (0x80 | ((c >> 6) & 0x3F)); - append_array[2] = (char) (0x80 | (c & 0x3F)); - len = 3; - } - else - { - append_array[0] = (char) (0xF0 | (c >> 18)); - append_array[1] = (char) (0x80 | ((c >> 12) & 0x3F)); - append_array[2] = (char) (0x80 | ((c >> 6) & 0x3F)); - append_array[3] = (char) (0x80 | (c & 0x3F)); - len = 4; - } - return sema_concat_const_bytes(context, expr, type, false, a, append_array, alen, len); -} - -/** - * 1. String + Bytes|String - * 2. Bytes + Bytes|String - * 3. Bytes + (i)char => Bytes - * 4. String + character => String - * 5. String + (i)char array/vector => String // Disallowed for now - * 6. Bytes + (i)char array/vector => Bytes // Disallowed for now - */ -static bool sema_concat_bytes_and_other(SemaContext *context, Expr *expr, Expr *left, Expr *right) -{ - ArraySize len = left->const_expr.bytes.len; - bool is_bytes = left->const_expr.const_kind == CONST_BYTES; - Type *indexed = type_get_indexed_type(left->type); - const char *left_bytes = left->const_expr.bytes.ptr; -RETRY:; - switch (right->const_expr.const_kind) - { - case CONST_INTEGER: - if (is_bytes) - { - // 2. Bytes + (i)char => Bytes - if (!cast_implicit(context, right, type_char, false)) return false; - char c = (char) int_to_i64(right->const_expr.ixx); - return sema_concat_const_bytes(context, expr, left->type, true, left_bytes, &c, len, 1); - } - // 1. String + character => String - if (int_is_neg(right->const_expr.ixx) || int_icomp(right->const_expr.ixx, 0x10FFFF, BINARYOP_GT)) - { - RETURN_SEMA_ERROR(right, "Cannot concatenate a string with an non-unicode value."); - } - return sema_concat_character(context, expr, left->type, left_bytes, len, right->const_expr.ixx.i.low); - case CONST_FLOAT: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_POINTER: - case CONST_TYPEID: - case CONST_MEMBER: - RETURN_SEMA_ERROR(expr, "Concatenating %s with %s is not possible at compile time.", - type_quoted_error_string(left->type), type_to_error_string(right->type)); - case CONST_BYTES: - case CONST_STRING: - return sema_concat_const_bytes(context, expr, left->type, is_bytes, left_bytes, - right->const_expr.bytes.ptr, - len, right->const_expr.bytes.len); - case CONST_UNTYPED_LIST: - if (!cast_implicit(context, right, type_get_inferred_array(indexed), false)) return false; - goto RETRY; - case CONST_INITIALIZER: - if (!cast_implicit(context, right, type_get_inferred_array(indexed), false)) return false; - expr_contract_array(&right->const_expr, left->const_expr.const_kind); - goto RETRY; - } - UNREACHABLE -} -static bool sema_append_concat_const_bytes(SemaContext *context, Expr *expr, Expr *list, Expr *element, bool is_append) -{ - Type *indexed = type_get_indexed_type(list->type); - assert(indexed && "This should always work"); - if (is_append && !cast_implicit(context, element, indexed, false)) return false; - size_t str_len = list->const_expr.bytes.len; - size_t element_len = is_append ? 1 : element->const_expr.bytes.len; - bool is_bytes = list->const_expr.const_kind == CONST_BYTES; - char *data = malloc_arena(str_len + element_len + 1); - char *current = data; - if (str_len) memcpy(current, list->const_expr.bytes.ptr, str_len); - current += str_len; - if (is_append) - { - current[0] = (unsigned char)element->const_expr.ixx.i.low; - } - else - { - if (element_len) memcpy(current, element->const_expr.bytes.ptr, element_len); - } - current += element_len; - current[0] = '\0'; - expr->expr_kind = EXPR_CONST; - expr->const_expr = (ExprConst) { - .const_kind = list->const_expr.const_kind, - .bytes.ptr = data, - .bytes.len = str_len + element_len - }; - expr->resolve_status = RESOLVE_DONE; - expr->type = is_bytes ? type_get_array(indexed, str_len + element_len) : list->type; - return true; -/* - - bool is_empty_slice = list->const_expr.const_kind == CONST_POINTER; - if (!is_empty_slice && list->const_expr.initializer->kind != CONST_INIT_ARRAY_FULL) - { - RETURN_SEMA_ERROR(list, "Only fully initialized arrays may be appended to."); - } - Type *array_type = type_flatten(list->type); - bool is_vector = array_type->type_kind == TYPE_VECTOR || array_type->type_kind == TYPE_INFERRED_VECTOR; - unsigned len = (is_empty_slice ? 0 : sema_get_const_len(context, list)) + 1; - Type *indexed = type_get_indexed_type(list->type); - Type *new_type = is_vector ? type_get_vector(indexed, len) : type_get_array(indexed, len); - ConstInitializer *init = list->const_expr.initializer; - ConstInitializer **inits = VECNEW(ConstInitializer*, len); - if (!is_empty_slice) - { - FOREACH(ConstInitializer *, i, init->init_array_full) vec_add(inits, i); - } - - assert(element->resolve_status == RESOLVE_DONE); - if (!cast_implicit(context, element, indexed, false)) return false; - ConstInitializer *in = CALLOCS(ConstInitializer); - in->kind = CONST_INIT_VALUE; - in->init_value = element; - vec_add(inits, in); - expr->expr_kind = EXPR_CONST; - expr->resolve_status = RESOLVE_DONE; - expr->type = new_type; - ConstInitializer *new_init = CALLOCS(ConstInitializer); - new_init->init_array_full = inits; - new_init->type = new_type; - new_init->kind = CONST_INIT_ARRAY_FULL; - expr->const_expr = (ExprConst) { - .const_kind = CONST_INITIALIZER, - .initializer = new_init - }; - return true;*/ -} - -static inline bool sema_expr_const_append(SemaContext *context, Expr *append_expr, Expr *list, Expr *element) -{ - Expr **untyped_list = NULL; - switch (list->const_expr.const_kind) - { - case CONST_INITIALIZER: - assert(list->type != type_untypedlist); - return sema_append_const_array_one(context, append_expr, list, element); - case CONST_UNTYPED_LIST: - untyped_list = list->const_expr.untyped_list; - break; - case CONST_POINTER: - if (list->type->canonical->type_kind == TYPE_SLICE) - { - return sema_append_const_array_one(context, append_expr, list, element); - } - FALLTHROUGH; - case CONST_BYTES: - case CONST_STRING: - return sema_append_concat_const_bytes(context, append_expr, list, element, true); - default: - RETURN_SEMA_ERROR(list, "Expected some kind of list or vector here."); - } - vec_add(untyped_list, element); - append_expr->expr_kind = EXPR_CONST; - append_expr->const_expr = (ExprConst) { .untyped_list = untyped_list, .const_kind = CONST_UNTYPED_LIST }; - append_expr->type = type_untypedlist; - append_expr->resolve_status = RESOLVE_DONE; - return true; -} - -/** - * The following valid cases exist: - * - * 1. String/Bytes + ... => String/Bytes - * 2. Vector/slice/array + Untyped list => Merged untyped list - * 3. Vector/slice/array + arraylike => vector/array iff canoncial match, otherwise Untyped list - * 4. Untyped list + Vector/array/slice => Merged untyped list - * 5. Vector/array/slice + element => Vector/array/slice + 1 len iff canonical match, Untyped list otherwise - * 6. Untyped list + element => Untyped list - */ -static inline bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *left, Expr *right) -{ - assert(concat_expr->resolve_status == RESOLVE_RUNNING); - bool join_single = false; - ArraySize len = 0; - bool use_array = true; - Type *indexed_type = NULL; - if (!sema_analyse_expr(context, left)) return false; - if (!sema_cast_const(left)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); - if (!sema_analyse_expr(context, right)) return false; - if (!sema_cast_const(right)) RETURN_SEMA_ERROR(left, "Expected this to evaluate to a constant value."); - Type *element_type = left->type->canonical; - Type *right_type = right->type->canonical; - ConstKind right_kind = right->const_expr.const_kind; - switch (left->const_expr.const_kind) - { - case CONST_POINTER: - if (element_type->type_kind == TYPE_SLICE) - { - len = 0; - indexed_type = type_get_indexed_type(element_type); - break; - } - FALLTHROUGH; - case CONST_FLOAT: - case CONST_INTEGER: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_TYPEID: - RETURN_SEMA_ERROR(left, "Only bytes, strings and list-like constants can be concatenated."); - case CONST_BYTES: - case CONST_STRING: - return sema_concat_bytes_and_other(context, concat_expr, left, right); - case CONST_INITIALIZER: - switch (type_flatten(element_type)->type_kind) - { - case TYPE_VECTOR: - case TYPE_INFERRED_VECTOR: - use_array = false; - FALLTHROUGH; - case TYPE_SLICE: - case TYPE_ARRAY: - case TYPE_INFERRED_ARRAY: - { - switch (left->const_expr.initializer->kind) - { - case CONST_INIT_ARRAY_FULL: - break; - case CONST_INIT_ZERO: - if (type_flatten(element_type)->type_kind == TYPE_SLICE) break; - FALLTHROUGH; - default: - RETURN_SEMA_ERROR(left, "Only fully initialized arrays may be concatenated."); - } - indexed_type = type_get_indexed_type(element_type); - assert(indexed_type); - len = sema_get_const_len(context, left); - break; - } - case TYPE_UNTYPED_LIST: - UNREACHABLE - default: - RETURN_SEMA_ERROR(left, "Only bytes, strings and array-like constants can be concatenated."); - } - break; - case CONST_UNTYPED_LIST: - len = vec_size(left->const_expr.untyped_list); - break; - case CONST_MEMBER: - RETURN_SEMA_ERROR(left, "This can't be concatenated."); - } - switch (right->const_expr.const_kind) - { - case CONST_FLOAT: - case CONST_INTEGER: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_POINTER: - case CONST_TYPEID: - case CONST_MEMBER: - return sema_expr_const_append(context, concat_expr, left, right); - case CONST_BYTES: - case CONST_STRING: - if (left->type == type_untypedlist || indexed_type == right_type) return sema_expr_const_append(context, concat_expr, left, right); - if (!type_is_integer(indexed_type) || type_size(indexed_type) != 1) - { - RETURN_SEMA_ERROR(right, "You can't concatenate %s and %s.", type_quoted_error_string(left->type), - type_to_error_string(right_type)); - } - if (!cast_implicit(context, right, type_get_inferred_array(indexed_type), false)) return false; - expr_contract_array(&left->const_expr, CONST_BYTES); - return sema_concat_bytes_and_other(context, concat_expr, left, right); - case CONST_UNTYPED_LIST: - break; - case CONST_INITIALIZER: - if (indexed_type && cast_implicit_silent(context, right, indexed_type, false)) - { - return sema_expr_const_append(context, concat_expr, left, right); - } - break; - } - if (indexed_type && !cast_implicit_silent(context, right, type_get_inferred_array(indexed_type), false)) - { - indexed_type = NULL; - } - len += sema_get_const_len(context, right); - if (!indexed_type) - { - Expr **untyped_exprs = VECNEW(Expr*, len + 1); - Expr *exprs[2] = { left, right }; - for (unsigned i = 0; i < 2; i++) - { - Expr *single_expr = exprs[i]; - if (expr_is_const_untyped_list(single_expr)) - { - FOREACH(Expr *, expr_untyped, single_expr->const_expr.untyped_list) - { - vec_add(untyped_exprs, expr_untyped); - } - continue; - } - ConstInitializer *init = single_expr->const_expr.initializer; - if (init->kind != CONST_INIT_ARRAY_FULL) - { - assert(init->type != type_untypedlist); - RETURN_SEMA_ERROR(single_expr, "Expected a full array here."); - } - FOREACH(ConstInitializer *, val, init->init_array_full) - { - vec_add(untyped_exprs, val->init_value); - } - } - concat_expr->expr_kind = EXPR_CONST; - concat_expr->type = type_untypedlist; - concat_expr->resolve_status = RESOLVE_DONE; - concat_expr->const_expr = (ExprConst) { - .const_kind = CONST_UNTYPED_LIST, - .untyped_list = untyped_exprs - }; - return true; - } - ConstInitializer **inits = VECNEW(ConstInitializer*, len); - Expr *exprs[2] = { left, right }; - for (int i = 0; i < 2; i++) - { - Expr *element = exprs[i]; - assert(element->const_expr.const_kind == CONST_INITIALIZER); - ConstInitType init_type = element->const_expr.initializer->kind; - switch (init_type) - { - case CONST_INIT_ARRAY_FULL: - break; - case CONST_INIT_ZERO: - if (type_flatten(element->type)->type_kind == TYPE_SLICE) continue; - default: - RETURN_SEMA_ERROR(element, "Only fully initialized arrays may be concatenated."); - } - FOREACH(ConstInitializer *, init, element->const_expr.initializer->init_array_full) - { - vec_add(inits, init); - } - } - concat_expr->expr_kind = EXPR_CONST; - concat_expr->resolve_status = RESOLVE_DONE; - concat_expr->type = use_array ? type_get_array(indexed_type, len) : type_get_vector(indexed_type, len); - ConstInitializer *new_init = CALLOCS(ConstInitializer); - new_init->init_array_full = inits; - new_init->type = concat_expr->type; - new_init->kind = CONST_INIT_ARRAY_FULL; - concat_expr->const_expr = (ExprConst) { - .const_kind = CONST_INITIALIZER, - .initializer = new_init - }; - return true; -} - - static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr) { if (expr->binary_expr.operator == BINARYOP_ELSE) return sema_expr_analyse_or_error(context, expr); @@ -9209,48 +8672,6 @@ bool sema_concat_join_arrays(SemaContext *context, Expr *expr, Expr **exprs, Typ return true; } -static bool sema_append_const_array(SemaContext *context, Expr *expr, Expr *list, Expr **exprs) -{ - bool is_empty_slice = list->const_expr.const_kind == CONST_POINTER; - if (!is_empty_slice && list->const_expr.initializer->kind != CONST_INIT_ARRAY_FULL) - { - RETURN_SEMA_ERROR(list, "Only fully initialized arrays may be concatenated."); - } - Type *array_type = type_flatten(list->type); - bool is_vector = array_type->type_kind == TYPE_VECTOR || array_type->type_kind == TYPE_INFERRED_VECTOR; - unsigned len = (is_empty_slice ? 0 : sema_get_const_len(context, list)) + vec_size(exprs) - 1; - Type *indexed = type_get_indexed_type(list->type); - Type *new_type = is_vector ? type_get_vector(indexed, len) : type_get_array(indexed, len); - ConstInitializer *init = list->const_expr.initializer; - ConstInitializer **inits = VECNEW(ConstInitializer*, len); - if (!is_empty_slice) - { - FOREACH(ConstInitializer *, i, init->init_array_full) vec_add(inits, i); - } - unsigned elements = vec_size(exprs); - for (unsigned i = 1; i < elements; i++) - { - Expr *element = exprs[i]; - if (!sema_analyse_inferred_expr(context, indexed, element)) return false; - if (!cast_implicit(context, element, indexed, false)) return false; - ConstInitializer *in = CALLOCS(ConstInitializer); - in->kind = CONST_INIT_VALUE; - in->init_value = element; - vec_add(inits, in); - } - expr->expr_kind = EXPR_CONST; - expr->resolve_status = RESOLVE_DONE; - expr->type = new_type; - ConstInitializer *new_init = CALLOCS(ConstInitializer); - new_init->init_array_full = inits; - new_init->type = new_type; - new_init->kind = CONST_INIT_ARRAY_FULL; - expr->const_expr = (ExprConst) { - .const_kind = CONST_INITIALIZER, - .initializer = new_init - }; - return true; -} bool sema_concat_join_bytes(Expr *expr, Expr **exprs, ArraySize len) { @@ -9276,151 +8697,6 @@ bool sema_concat_join_bytes(Expr *expr, Expr **exprs, ArraySize len) return true; } -static inline bool sema_expr_analyse_ct_concatfn(SemaContext *context, Expr *concat_expr) -{ - assert(concat_expr->resolve_status == RESOLVE_RUNNING); - if (!sema_expand_vasplat_exprs(context, concat_expr->ct_concat)) return false; - Expr **exprs = concat_expr->ct_concat; - unsigned expr_count = vec_size(exprs); - Type *element_type = NULL; - ConstInitializer *initializer = NULL; - ConcatType concat = CONCAT_UNKNOWN; - bool join_single = false; - ArraySize len = 0; - bool use_array = true; - Type *indexed_type = NULL; - for (unsigned i = 0; i < expr_count; i++) - { - Expr *single_expr = exprs[i]; - if (!sema_analyse_expr(context, single_expr)) return false; - if (!sema_cast_const(single_expr)) RETURN_SEMA_ERROR(single_expr, "Expected this to evaluate to a constant value."); - if (concat == CONCAT_UNKNOWN) - { - element_type = single_expr->type; - switch (single_expr->const_expr.const_kind) - { - case CONST_FLOAT: - case CONST_INTEGER: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_POINTER: - case CONST_TYPEID: - RETURN_SEMA_ERROR(single_expr, "Only bytes, strings and array-like constants can be concatenated."); - case CONST_BYTES: - case CONST_STRING: - concat = CONCAT_JOIN_BYTES; - len = single_expr->const_expr.bytes.len; - break; - case CONST_INITIALIZER: - switch (type_flatten(element_type)->type_kind) - { - case TYPE_VECTOR: - case TYPE_INFERRED_VECTOR: - use_array = false; - FALLTHROUGH; - case TYPE_SLICE: - case TYPE_ARRAY: - case TYPE_INFERRED_ARRAY: - { - switch (single_expr->const_expr.initializer->kind) - { - case CONST_INIT_ARRAY_FULL: - break; - case CONST_INIT_ZERO: - if (type_flatten(element_type)->type_kind == TYPE_SLICE) break; - FALLTHROUGH; - default: - RETURN_SEMA_ERROR(single_expr, "Only fully initialized arrays may be concatenated."); - } - concat = CONCAT_JOIN_ARRAYS; - indexed_type = type_get_indexed_type(element_type); - assert(indexed_type); - len = sema_get_const_len(context, single_expr); - break; - } - case TYPE_UNTYPED_LIST: - concat = CONCAT_JOIN_LISTS; - len = sema_get_const_len(context, single_expr); - break; - default: - RETURN_SEMA_ERROR(single_expr, "Only bytes, strings and array-like constants can be concatenated."); - } - break; - case CONST_UNTYPED_LIST: - concat = CONCAT_JOIN_LISTS; - len = vec_size(single_expr->const_expr.untyped_list); - continue; - case CONST_MEMBER: - RETURN_SEMA_ERROR(single_expr, "This can't be concatenated."); - } - if (expr_count == 1) - { - expr_replace(concat_expr, single_expr); - return true; - } - continue; - } - Type *type = single_expr->type; - if (type == type_untypedlist) concat = CONCAT_JOIN_LISTS; - switch (concat) - { - case CONCAT_JOIN_ARRAYS: - if (type_get_indexed_type(element_type)->canonical != indexed_type) - { - concat = CONCAT_JOIN_LISTS; - } - FALLTHROUGH; - case CONCAT_JOIN_LISTS: - if (type != type_untypedlist && !type_is_arraylike(type_flatten(single_expr->type))) RETURN_SEMA_ERROR(single_expr, "Expected an array or list."); - break; - case CONCAT_JOIN_BYTES: - if (!cast_implicit(context, single_expr, element_type, false)) return false; - break; - default: - UNREACHABLE - } - len += sema_get_const_len(context, single_expr); - } - - if (concat == CONCAT_JOIN_BYTES) return sema_concat_join_bytes(concat_expr, exprs, len); - if (concat == CONCAT_JOIN_LISTS) - { - Expr **untyped_exprs = VECNEW(Expr*, len + 1); - for (unsigned i = 0; i < expr_count; i++) - { - Expr *single_expr = exprs[i]; - if (expr_is_const_untyped_list(single_expr)) - { - FOREACH(Expr *, expr_untyped, single_expr->const_expr.untyped_list) - { - vec_add(untyped_exprs, expr_untyped); - } - continue; - } - ConstInitializer *init = single_expr->const_expr.initializer; - if (init->kind != CONST_INIT_ARRAY_FULL) - { - if (init->kind == CONST_INIT_ZERO && init->type == type_untypedlist) continue; - RETURN_SEMA_ERROR(single_expr, "Expected a full array here."); - } - FOREACH(ConstInitializer *, val, init->init_array_full) - { - vec_add(untyped_exprs, val->init_value); - } - } - concat_expr->expr_kind = EXPR_CONST; - concat_expr->type = type_untypedlist; - concat_expr->resolve_status = RESOLVE_DONE; - concat_expr->const_expr = (ExprConst) { - .const_kind = CONST_UNTYPED_LIST, - .untyped_list = untyped_exprs - }; - return true; - } - assert(indexed_type); - return sema_concat_join_arrays(context, concat_expr, exprs, use_array ? type_get_array(indexed_type, len) : type_get_vector(indexed_type, len), len); -} static inline bool sema_expr_analyse_ct_append(SemaContext *context, Expr *append_expr) { @@ -9435,38 +8711,16 @@ static inline bool sema_expr_analyse_ct_append(SemaContext *context, Expr *appen expr_replace(append_expr, list); return true; } - - if (!sema_analyse_expr(context, list)) return false; - if (!sema_cast_const(list)) RETURN_SEMA_ERROR(list, "Expected the list to evaluate to a constant value."); - Expr **untyped_list = NULL; - switch (list->const_expr.const_kind) - { - case CONST_INITIALIZER: - assert(list->type != type_untypedlist); - return sema_append_const_array(context, append_expr, list, exprs); - case CONST_UNTYPED_LIST: - untyped_list = list->const_expr.untyped_list; - break; - case CONST_POINTER: - if (list->type->canonical->type_kind == TYPE_SLICE) - { - return sema_append_const_array(context, append_expr, list, exprs); - } - FALLTHROUGH; - default: - RETURN_SEMA_ERROR(list, "Expected some kind of list or vector here."); - } for (unsigned i = 1; i < expr_count; i++) { - Expr *element = exprs[i]; - if (!sema_analyse_expr(context, element)) return false; - if (!sema_cast_const(element)) RETURN_SEMA_ERROR(element, "Expected the element to evaluate to a constant value."); - vec_add(untyped_list, element); + Expr binary = { .expr_kind = EXPR_BINARY, .span = append_expr->span }; + binary.binary_expr.left = exprid(list); + binary.binary_expr.right = exprid(exprs[i]); + binary.binary_expr.operator = BINARYOP_CT_CONCAT; + if (!sema_expr_analyse_ct_concat(context, &binary, list, exprs[i])) return false; + *list = binary; } - append_expr->expr_kind = EXPR_CONST; - append_expr->const_expr = (ExprConst) { .untyped_list = untyped_list, .const_kind = CONST_UNTYPED_LIST }; - append_expr->type = type_untypedlist; - append_expr->resolve_status = RESOLVE_DONE; + expr_replace(append_expr, list); return true; } @@ -9694,9 +8948,8 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) case EXPR_CT_DEFINED: return sema_expr_analyse_ct_defined(context, expr); case EXPR_CT_APPEND: - return sema_expr_analyse_ct_append(context, expr); case EXPR_CT_CONCAT: - return sema_expr_analyse_ct_concatfn(context, expr); + return sema_expr_analyse_ct_append(context, expr); case EXPR_CT_AND_OR: return sema_expr_analyse_ct_and_or_fn(context, expr); case EXPR_CT_ARG: @@ -9885,71 +9138,6 @@ NO_MATCH_REF: return false; } -static ArrayIndex len_from_const_initializer(ConstInitializer *init) -{ - switch (init->kind) - { - case CONST_INIT_ZERO: - return 0; - case CONST_INIT_STRUCT: - case CONST_INIT_UNION: - case CONST_INIT_VALUE: - case CONST_INIT_ARRAY_VALUE: - return -1; - case CONST_INIT_ARRAY: - { - ArrayIndex max = 0; - FOREACH(ConstInitializer *, element, init->init_array.elements) - { - assert(element->kind == CONST_INIT_ARRAY_VALUE); - if (element->init_array_value.index > max) max = element->init_array_value.index; - } - return max; - } - case CONST_INIT_ARRAY_FULL: - return vec_size(init->init_array_full); - } - UNREACHABLE -} -ArrayIndex sema_len_from_const(Expr *expr) -{ - // We also handle the case where we have a cast from a const array. - if (!sema_cast_const(expr)) - { - if (type_flatten(expr->type)->type_kind != TYPE_SLICE) return -1; - if (expr->expr_kind == EXPR_SLICE) - { - return range_const_len(&expr->subscript_expr.range); - } - if (expr->expr_kind != EXPR_CAST) return -1; - if (expr->cast_expr.kind != CAST_APTSA) return -1; - Expr *inner = exprptr(expr->cast_expr.expr); - if (inner->expr_kind != EXPR_UNARY || inner->unary_expr.operator != UNARYOP_ADDR) return -1; - inner = inner->unary_expr.expr; - if (!sema_cast_const(inner)) return -1; - expr = inner; - } - switch (expr->const_expr.const_kind) - { - case CONST_FLOAT: - case CONST_INTEGER: - case CONST_BOOL: - case CONST_ENUM: - case CONST_ERR: - case CONST_POINTER: - case CONST_TYPEID: - case CONST_MEMBER: - return -1; - case CONST_BYTES: - case CONST_STRING: - return expr->const_expr.bytes.len; - case CONST_INITIALIZER: - return len_from_const_initializer(expr->const_expr.initializer); - case CONST_UNTYPED_LIST: - return vec_size(expr->const_expr.untyped_list); - } - UNREACHABLE -} static inline bool sema_cast_ct_ident_rvalue(SemaContext *context, Expr *expr) { Decl *decl = expr->ct_ident_expr.decl; diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index af73a9f35..93e82109a 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -111,6 +111,7 @@ Type *sema_resolve_type_get_func(Signature *signature, CallABI abi); INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result); INLINE bool sema_set_alloca_alignment(SemaContext *context, Type *type, AlignSize *result); INLINE void sema_display_deprecated_warning_on_use(SemaContext *context, Decl *decl, SourceSpan use); +bool sema_expr_analyse_ct_concat(SemaContext *context, Expr *concat_expr, Expr *left, Expr *right); INLINE bool sema_set_abi_alignment(SemaContext *context, Type *type, AlignSize *result) { @@ -158,3 +159,18 @@ INLINE void sema_display_deprecated_warning_on_use(SemaContext *context, Decl *d sema_warning_at(span, "'%s' is deprecated.", decl->name); } +static inline IndexDiff range_const_len(Range *range) +{ + Expr *start = exprptr(range->start); + Expr *end = exprptrzero(range->end); + if (!end || !expr_is_const_int(end)) return -1; + if (!int_fits(end->const_expr.ixx, TYPE_I32)) return -1; + IndexDiff end_val = (IndexDiff)int_to_i64(end->const_expr.ixx); + if (range->is_len) return end_val; + if (!expr_is_const_int(start)) return -1; + if (!int_fits(start->const_expr.ixx, TYPE_I32)) return -1; + IndexDiff start_val = (IndexDiff)int_to_i64(start->const_expr.ixx); + if (range->start_from_end && range->end_from_end) return start_val - end_val + 1; + if (range->start_from_end != range->end_from_end) return -1; + return end_val - start_val + 1; +} diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index c7544fb5f..fd69825a3 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -23,7 +23,7 @@ static inline bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *stateme static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement); static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement); -static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *context, Expr *ret_expr); +static inline bool sema_check_return_matches_opt_returns(SemaContext *context, Expr *ret_expr); static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom); static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *statement); static inline bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement); @@ -366,14 +366,26 @@ static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, As stmt->return_stmt.cleanup_fail = stmt->return_stmt.cleanup ? astid(copy_ast_defer(astptr(stmt->return_stmt.cleanup))) : 0; } -static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *context, Expr *ret_expr) +/** + * Check that an optional returned actually matches the "returns!" declared + * by the contract. + */ +static inline bool sema_check_return_matches_opt_returns(SemaContext *context, Expr *ret_expr) { if (!IS_OPTIONAL(ret_expr) || !context->call_env.opt_returns) return true; + + // TODO if this is a call, then we should check against + // the "return!" in that call. + // But for now we if (ret_expr->expr_kind != EXPR_OPTIONAL) return true; Expr *inner = ret_expr->inner_expr; if (!sema_cast_const(inner)) return true; + + // Here we have a const optional return. assert(ret_expr->inner_expr->const_expr.const_kind == CONST_ERR); Decl *fault = ret_expr->inner_expr->const_expr.enum_err_val; + + // Check that we find it. FOREACH(Decl *, opt, context->call_env.opt_returns) { if (opt->decl_kind == DECL_FAULT) @@ -383,6 +395,7 @@ static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *con } if (opt == fault) return true; } + // No match RETURN_SEMA_ERROR(ret_expr, "This value does not match declared optional returns, it needs to be declared with the other optional returns."); } @@ -465,7 +478,7 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state { if (!sema_analyse_expr(context, ret_expr)) return false; } - if (is_macro && !sema_return_optional_check_is_valid_in_scope(context, ret_expr)) return false; + if (is_macro && !sema_check_return_matches_opt_returns(context, ret_expr)) return false; } else @@ -571,7 +584,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement { if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_optional(expected_rtype), NULL, false)) return false; if (!sema_check_not_stack_variable_escape(context, return_expr)) return false; - if (!sema_return_optional_check_is_valid_in_scope(context, return_expr)) return false; + if (!sema_check_return_matches_opt_returns(context, return_expr)) return false; } else { diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index ef74b6808..894e2098b 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -513,7 +513,7 @@ void sema_print_inline(SemaContext *context) InliningSpan *inlined_at = context->inlined_at; while (inlined_at) { - sema_error_prev_at(inlined_at->span, "Inlined from here."); + sema_note_prev_at(inlined_at->span, "Inlined from here."); inlined_at = inlined_at->prev; } }