diff --git a/lib/std/core/private/macho_runtime.c3 b/lib/std/core/private/macho_runtime.c3 index 93ebb93c2..f69e8b380 100644 --- a/lib/std/core/private/macho_runtime.c3 +++ b/lib/std/core/private/macho_runtime.c3 @@ -175,7 +175,7 @@ fn void runtime_startup() @public @export("__c3_runtime_startup") } assert(runtime_state == RUN_CTORS); runtime_state = READ_DYLIB; - ctor = null; + ctor_first = null; } fn void runtime_finalize() @public @export("__c3_runtime_finalize") diff --git a/releasenotes.md b/releasenotes.md index bfc4cc029..03c98b94c 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,8 @@ ## 0.6.1 Change list ### Changes / improvements +- Addition of $append and $concat functions. +- Added $$str_hash builtin. ### Fixes - Error with unsigned compare in `@ensure` when early returning 0 #1207. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index b84bb2af8..d6234e086 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -999,6 +999,7 @@ typedef struct Expr** args; } ExprCtAndOr; + typedef struct { CastKind kind : 8; @@ -1172,6 +1173,7 @@ struct Expr_ ExprConst const_expr; // 32 ExprCtArg ct_arg_expr; ExprCtAndOr ct_and_or_expr; + Expr** ct_concat; ExprOtherContext expr_other_context; ExprCastable castable_expr; ExprCtCall ct_call_expr; // 24 @@ -3344,6 +3346,8 @@ INLINE void expr_set_span(Expr *expr, SourceSpan loc) case EXPR_COMPOUND_LITERAL: case EXPR_COND: case EXPR_CT_AND_OR: + case EXPR_CT_APPEND: + case EXPR_CT_CONCAT: case EXPR_CT_ARG: case EXPR_CT_CALL: case EXPR_CT_CASTABLE: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 256dc8dd6..14264021c 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -429,6 +429,10 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_EXPRID(expr->castable_expr.expr); MACRO_COPY_TYPEID(expr->castable_expr.type); return expr; + case EXPR_CT_APPEND: + case EXPR_CT_CONCAT: + MACRO_COPY_EXPR_LIST(expr->ct_concat); + return expr; case EXPR_CT_AND_OR: MACRO_COPY_EXPR_LIST(expr->ct_and_or_expr.args); return expr; diff --git a/src/compiler/enums.h b/src/compiler/enums.h index d4effb9c6..6ab4d30d3 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -174,7 +174,8 @@ typedef enum case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: \ case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: \ case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \ - case EXPR_ANYSWITCH: case EXPR_STRINGIFY: case EXPR_CT_EVAL + case EXPR_ANYSWITCH: case EXPR_STRINGIFY: \ + case EXPR_CT_EVAL: case EXPR_CT_CONCAT: case EXPR_CT_APPEND typedef enum { @@ -231,8 +232,10 @@ typedef enum EXPR_CONST, EXPR_CT_AND_OR, EXPR_CT_ARG, + EXPR_CT_APPEND, EXPR_CT_CALL, EXPR_CT_CASTABLE, + EXPR_CT_CONCAT, EXPR_CT_DEFINED, EXPR_CT_EVAL, EXPR_CT_IDENT, @@ -575,9 +578,11 @@ typedef enum TOKEN_CT_ALIGNOF, // $alignof TOKEN_CT_AND, // $and + TOKEN_CT_APPEND, // $append TOKEN_CT_ASSERT, // $assert TOKEN_CT_ASSIGNABLE, // $assignable TOKEN_CT_CASE, // $case + TOKEN_CT_CONCAT, // $concat TOKEN_CT_DEFAULT, // $default TOKEN_CT_DEFINED, // $defined TOKEN_CT_ECHO, // $echo @@ -941,6 +946,7 @@ typedef enum BUILTIN_SCATTER, BUILTIN_SELECT, BUILTIN_SET_ROUNDING_MODE, + BUILTIN_STR_HASH, BUILTIN_SWIZZLE, BUILTIN_SWIZZLE2, BUILTIN_SIN, diff --git a/src/compiler/expr.c b/src/compiler/expr.c index 6d6401f6b..9f2e2283f 100644 --- a/src/compiler/expr.c +++ b/src/compiler/expr.c @@ -199,6 +199,8 @@ bool expr_is_constant_eval(Expr *expr, ConstantEvalKind eval_kind) case EXPR_OPERATOR_CHARS: case EXPR_STRINGIFY: case EXPR_CT_AND_OR: + case EXPR_CT_CONCAT: + case EXPR_CT_APPEND: case EXPR_CT_CASTABLE: case EXPR_CT_DEFINED: case EXPR_CT_IS_CONST: @@ -680,6 +682,8 @@ bool expr_is_pure(Expr *expr) case EXPR_COMPILER_CONST: case EXPR_CONST: case EXPR_CT_AND_OR: + case EXPR_CT_CONCAT: + case EXPR_CT_APPEND: case EXPR_CT_ARG: case EXPR_CT_CALL: case EXPR_CT_CASTABLE: diff --git a/src/compiler/llvm_codegen_builtins.c b/src/compiler/llvm_codegen_builtins.c index 38b1fd3f9..deaedd39e 100644 --- a/src/compiler/llvm_codegen_builtins.c +++ b/src/compiler/llvm_codegen_builtins.c @@ -1075,6 +1075,8 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) TODO case BUILTIN_LLROUND: TODO + case BUILTIN_STR_HASH: + UNREACHABLE case BUILTIN_NONE: UNREACHABLE } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 1b29b0cc6..f29c4b90f 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1104,6 +1104,21 @@ static Expr *parse_ct_embed(ParseContext *c, Expr *left) return embed; } + +static Expr *parse_ct_concat_append(ParseContext *c, Expr *left) +{ + assert(!left && "Unexpected left hand side"); + Expr *expr = EXPR_NEW_TOKEN(tok_is(c, TOKEN_CT_CONCAT) ? EXPR_CT_CONCAT : EXPR_CT_APPEND); + advance(c); + + CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); + if (!parse_arg_list(c, &expr->ct_concat, TOKEN_RPAREN, NULL, true)) return poisoned_expr; + CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); + RANGE_EXTEND_PREV(expr); + return expr; +} + + /** * ct_call ::= (CT_ALIGNOF | CT_FEATURE | CT_EXTNAMEOF | CT_OFFSETOF | CT_NAMEOF | CT_QNAMEOF) '(' flat_path ')' * flat_path ::= expr ('.' primary) | '[' expr ']')* @@ -1943,6 +1958,8 @@ ParseRule rules[TOKEN_EOF + 1] = { //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE } [TOKEN_FN] = { parse_lambda, NULL, PREC_NONE }, + [TOKEN_CT_CONCAT] = { parse_ct_concat_append, NULL, PREC_NONE }, + [TOKEN_CT_APPEND] = { parse_ct_concat_append, NULL, PREC_NONE }, [TOKEN_CT_SIZEOF] = { parse_ct_sizeof, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_AND] = {parse_ct_and_or, NULL, PREC_NONE }, diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 4c7223929..32e247346 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -1284,7 +1284,9 @@ Ast *parse_stmt(ParseContext *c) case TOKEN_CHAR_LITERAL: case TOKEN_CT_ALIGNOF: case TOKEN_CT_AND: + case TOKEN_CT_APPEND: case TOKEN_CT_ASSIGNABLE: + case TOKEN_CT_CONCAT: case TOKEN_CT_CONST_IDENT: case TOKEN_CT_IS_CONST: case TOKEN_CT_DEFINED: diff --git a/src/compiler/sema_builtins.c b/src/compiler/sema_builtins.c index 09cf80cb6..5fa7ee587 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -292,6 +292,19 @@ static bool sema_expr_analyse_syscall(SemaContext *context, Expr *expr) return true; } +bool sema_expr_analyse_str_hash(SemaContext *context, Expr *expr) +{ + Expr *inner = expr->call_expr.arguments[0]; + if (!sema_analyse_expr(context, inner)) return true; + if (!expr_is_const_string(inner)) + { + RETURN_SEMA_ERROR(inner, "You need a compile time constant string to take the hash of it."); + } + uint32_t hash = fnv1a(inner->const_expr.bytes.ptr, inner->const_expr.bytes.len); + expr_rewrite_const_int(expr, type_uint, hash); + return true; +} + bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) { expr->call_expr.is_builtin = true; @@ -321,6 +334,8 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) switch (func) { + case BUILTIN_STR_HASH: + return sema_expr_analyse_str_hash(context, expr); case BUILTIN_SWIZZLE2: case BUILTIN_SWIZZLE: return sema_expr_analyse_swizzle(context, expr, func == BUILTIN_SWIZZLE2); @@ -362,6 +377,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) rtype = type_void; break; case BUILTIN_SYSCALL: + case BUILTIN_STR_HASH: UNREACHABLE case BUILTIN_VECCOMPGE: case BUILTIN_VECCOMPEQ: @@ -915,39 +931,40 @@ static inline int builtin_expected_args(BuiltinFunction func) case BUILTIN_CEIL: case BUILTIN_COS: case BUILTIN_CTLZ: - case BUILTIN_POPCOUNT: case BUILTIN_CTTZ: case BUILTIN_EXACT_NEG: - case BUILTIN_EXP: case BUILTIN_EXP2: + case BUILTIN_EXP: case BUILTIN_FLOOR: + case BUILTIN_FRAMEADDRESS: case BUILTIN_LLRINT: case BUILTIN_LLROUND: - case BUILTIN_LOG: - case BUILTIN_LOG2: case BUILTIN_LOG10: + case BUILTIN_LOG2: + case BUILTIN_LOG: case BUILTIN_LRINT: case BUILTIN_LROUND: case BUILTIN_NEARBYINT: + case BUILTIN_POPCOUNT: + case BUILTIN_REDUCE_ADD: + case BUILTIN_REDUCE_AND: + case BUILTIN_REDUCE_MAX: + case BUILTIN_REDUCE_MIN: + case BUILTIN_REDUCE_MUL: + case BUILTIN_REDUCE_OR: + case BUILTIN_REDUCE_XOR: + case BUILTIN_RETURNADDRESS: case BUILTIN_REVERSE: case BUILTIN_RINT: case BUILTIN_ROUND: case BUILTIN_ROUNDEVEN: + case BUILTIN_SET_ROUNDING_MODE: case BUILTIN_SIN: case BUILTIN_SQRT: + case BUILTIN_STR_HASH: case BUILTIN_TRUNC: case BUILTIN_VOLATILE_LOAD: - case BUILTIN_REDUCE_MUL: - case BUILTIN_REDUCE_AND: - case BUILTIN_REDUCE_ADD: - case BUILTIN_REDUCE_OR: - case BUILTIN_REDUCE_XOR: - case BUILTIN_REDUCE_MAX: - case BUILTIN_REDUCE_MIN: - case BUILTIN_SET_ROUNDING_MODE: case BUILTIN_WASM_MEMORY_SIZE: - case BUILTIN_FRAMEADDRESS: - case BUILTIN_RETURNADDRESS: return 1; case BUILTIN_COPYSIGN: case BUILTIN_EXACT_ADD: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 02e85eff5..467ed0eb0 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -128,21 +128,19 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp static void sema_binary_unify_voidptr(SemaContext *context, Expr *left, Expr *right, Type **left_type_ref, Type **right_type_ref); // -- function helper functions -static inline bool -sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, bool optional, bool *no_match_ref); -static inline bool -sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional, - bool *no_match_ref); -static inline bool -sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, bool *optional, bool *no_match_ref); -static inline bool -sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var, bool optional, - const char *name, bool *no_match_ref); +static inline bool sema_expr_analyse_var_call(SemaContext *context, Expr *expr, Type *func_ptr_type, + bool optional, bool *no_match_ref); +static inline bool sema_expr_analyse_func_call(SemaContext *context, Expr *expr, Decl *decl, + Expr *struct_var, bool optional, bool *no_match_ref); +static inline bool sema_call_analyse_invocation(SemaContext *context, Expr *call, CalledDecl callee, + bool *optional, bool *no_match_ref); +static inline bool sema_call_analyse_func_invocation(SemaContext *context, Type *type, Expr *expr, Expr *struct_var, + bool optional, const char *name, bool *no_match_ref); static inline bool sema_call_check_invalid_body_arguments(SemaContext *context, Expr *call, CalledDecl *callee); -INLINE bool -sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, Expr **args, unsigned func_param_count, - Variadic variadic, unsigned vararg_index, bool *optional, Expr ***varargs_ref, - Expr **vararg_splat_ref, bool *no_match_ref); +INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, Expr *call, + Expr **args, unsigned func_param_count, + Variadic variadic, unsigned vararg_index, bool *optional, + Expr ***varargs_ref, Expr **vararg_splat_ref, bool *no_match_ref); static inline int sema_call_find_index_of_named_parameter(SemaContext *context, Decl **func_params, Expr *expr); static inline bool sema_call_check_contract_param_match(SemaContext *context, Decl *param, Expr *expr); static bool sema_call_analyse_body_expansion(SemaContext *macro_context, Expr *call); @@ -263,6 +261,39 @@ Expr *sema_enter_inline_member(Expr *parent, CanonicalType *type) return NULL; } } + +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; + MemberIndex 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); @@ -534,6 +565,8 @@ static bool sema_binary_is_expr_lvalue(SemaContext *context, Expr *top_expr, Exp case EXPR_CT_ARG: case EXPR_CT_CALL: case EXPR_CT_AND_OR: + case EXPR_CT_APPEND: + case EXPR_CT_CONCAT: case EXPR_CT_DEFINED: case EXPR_CT_IS_CONST: case EXPR_CT_EVAL: @@ -653,8 +686,10 @@ static bool expr_may_ref(Expr *expr) case EXPR_CT_CASTABLE: case EXPR_CT_AND_OR: case EXPR_CT_CALL: + case EXPR_CT_CONCAT: case EXPR_CT_DEFINED: case EXPR_CT_IS_CONST: + case EXPR_CT_APPEND: case EXPR_CT_EVAL: case EXPR_DECL: case EXPR_DESIGNATED_INITIALIZER_LIST: @@ -1255,14 +1290,19 @@ INLINE bool sema_call_expand_arguments(SemaContext *context, CalledDecl *callee, unsigned num_args = vec_size(args); Decl **params = callee->params; + assert(func_param_count < MAX_PARAMS); Expr **actual_args = VECNEW(Expr*, func_param_count); - for (unsigned i = 0; i < func_param_count; i++) vec_add(actual_args, NULL); + for (unsigned i = 0; i < func_param_count; i++) + { + vec_add(actual_args, NULL); + } // 2. Loop through the parameters. bool has_named = false; for (unsigned i = 0; i < num_args; i++) { Expr *arg = args[i]; + assert(expr_ok(arg)); // 3. Handle named parameters if (arg->expr_kind == EXPR_DESIGNATOR) @@ -7979,8 +8019,10 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_LAMBDA: case EXPR_EXPR_BLOCK: case EXPR_CT_IS_CONST: + case EXPR_CT_APPEND: case EXPR_CT_DEFINED: case EXPR_CT_AND_OR: + case EXPR_CT_CONCAT: case EXPR_STRINGIFY: case EXPR_TERNARY: case EXPR_CT_CASTABLE: @@ -8129,6 +8171,7 @@ static inline bool sema_expr_analyse_castable(SemaContext *context, Expr *expr) return true; } + static inline bool sema_expr_analyse_ct_and_or(SemaContext *context, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); @@ -8147,6 +8190,333 @@ static inline bool sema_expr_analyse_ct_and_or(SemaContext *context, Expr *expr) return true; } +typedef enum ConcatType_ +{ + CONCAT_UNKNOWN, + CONCAT_JOIN_BYTES, + CONCAT_JOIN_ARRAYS, + CONCAT_JOIN_LISTS, +} ConcatType; + + +bool sema_concat_join_arrays(SemaContext *context, Expr *expr, Expr **exprs, Type *type, ArraySize len) +{ + ConstInitializer **inits = VECNEW(ConstInitializer*, len); + FOREACH_BEGIN(Expr *element, exprs) + 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."); + } + ConstInitializer **element_inits = element->const_expr.initializer->init_array_full; + VECEACH(element_inits, i) + { + vec_add(inits, element_inits[i]); + } + FOREACH_END(); + expr->expr_kind = EXPR_CONST; + expr->resolve_status = RESOLVE_DONE; + expr->type = type; + ConstInitializer *new_init = CALLOCS(ConstInitializer); + new_init->init_array_full = inits; + new_init->type = type; + new_init->kind = CONST_INIT_ARRAY_FULL; + expr->const_expr = (ExprConst) { + .const_kind = CONST_INITIALIZER, + .initializer = new_init + }; + return true; +} + +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_BEGIN(ConstInitializer *i, init->init_array_full) + vec_add(inits, i); + FOREACH_END(); + } + 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)) 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_join_single(Expr *expr, Expr **exprs, Type *type, ArraySize len) +{ + ConstInitializer **inits = VECNEW(ConstInitializer*, len); + assert(len == vec_size(exprs)); + for (unsigned i = 0; i < len; i++) + { + ConstInitializer *in = CALLOCS(ConstInitializer); + in->kind = CONST_INIT_VALUE; + in->init_value = exprs[i]; + vec_add(inits, in); + } + expr->expr_kind = EXPR_CONST; + expr->resolve_status = RESOLVE_DONE; + ConstInitializer *new_init = CALLOCS(ConstInitializer); + new_init->init_array_full = inits; + new_init->type = expr->type = type_get_array(type, len); + 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) +{ + bool is_bytes = exprs[0]->const_expr.const_kind == CONST_BYTES; + char *data = malloc_arena(len + 1); + char *current = data; + FOREACH_BEGIN(Expr *element, exprs) + size_t str_len = element->const_expr.bytes.len; + if (!str_len) continue; + memcpy(current, element->const_expr.bytes.ptr, str_len); + current += str_len; + FOREACH_END(); + *current = '\0'; + expr->expr_kind = EXPR_CONST; + expr->const_expr = (ExprConst) { + .const_kind = exprs[0]->const_expr.const_kind, + .bytes.ptr = data, + .bytes.len = len + }; + expr->resolve_status = RESOLVE_DONE; + expr->type = exprs[0]->type; + return true; +} + +static inline bool sema_expr_analyse_ct_concat(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 (!expr_is_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)) 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_BEGIN(Expr *expr_untyped, single_expr->const_expr.untyped_list) + vec_add(untyped_exprs, expr_untyped); + FOREACH_END(); + 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_BEGIN(ConstInitializer *val, init->init_array_full) + vec_add(untyped_exprs, val->init_value); + FOREACH_END(); + } + concat_expr->expr_kind = EXPR_CONST; + concat_expr->type = type_untypedlist; + 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) +{ + assert(append_expr->resolve_status == RESOLVE_RUNNING); + if (!sema_expand_vasplat_exprs(context, append_expr->ct_concat)) return false; + Expr **exprs = append_expr->ct_concat; + unsigned expr_count = vec_size(exprs); + if (!expr_count) RETURN_SEMA_ERROR(append_expr, "No list given."); + Expr *list = exprs[0]; + if (expr_count < 2) + { + expr_replace(append_expr, list); + return true; + } + + if (!sema_analyse_expr(context, list)) return false; + if (!expr_is_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: + if (list->type != type_untypedlist) return sema_append_const_array(context, append_expr, list, exprs); + break; + 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 (!expr_is_const(element)) RETURN_SEMA_ERROR(element, "Expected the element to evaluate to a constant value."); + 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 inline bool sema_expr_analyse_ct_stringify(SemaContext *context, Expr *expr) { Expr *inner = expr->inner_expr; @@ -8365,6 +8735,10 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr) return sema_expr_analyse_ct_is_const(context, 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_concat(context, expr); case EXPR_CT_AND_OR: return sema_expr_analyse_ct_and_or(context, expr); case EXPR_CT_ARG: @@ -8820,7 +9194,7 @@ RETRY: type = type->decl->distinct->type; goto RETRY; case TYPE_UNTYPED_LIST: - UNREACHABLE + UNREACHABLE; case TYPE_ARRAY: case TYPE_VECTOR: return type->array.len; diff --git a/src/compiler/sema_liveness.c b/src/compiler/sema_liveness.c index 0c5cae3e9..6498490c5 100644 --- a/src/compiler/sema_liveness.c +++ b/src/compiler/sema_liveness.c @@ -278,6 +278,8 @@ RETRY: case EXPR_EMBED: case EXPR_CT_CASTABLE: case EXPR_CT_AND_OR: + case EXPR_CT_CONCAT: + case EXPR_CT_APPEND: case EXPR_MACRO_BODY: case EXPR_OTHER_CONTEXT: UNREACHABLE diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 017dc8f79..62d416a9e 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -618,7 +618,9 @@ static inline bool sema_expr_valid_try_expression(Expr *expr) case EXPR_COND: case EXPR_POISONED: case EXPR_CT_AND_OR: + case EXPR_CT_CONCAT: case EXPR_CT_ARG: + case EXPR_CT_APPEND: case EXPR_CT_CALL: case EXPR_CT_CASTABLE: case EXPR_CT_IS_CONST: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 5f6d81417..fe5e9172d 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -277,6 +277,7 @@ void symtab_init(uint32_t capacity) builtin_list[BUILTIN_SELECT] = KW_DEF("select"); builtin_list[BUILTIN_SET_ROUNDING_MODE] = KW_DEF("set_rounding_mode"); builtin_list[BUILTIN_SIN] = KW_DEF("sin"); + builtin_list[BUILTIN_STR_HASH] = KW_DEF("str_hash"); builtin_list[BUILTIN_SWIZZLE] = KW_DEF("swizzle"); builtin_list[BUILTIN_SWIZZLE2] = KW_DEF("swizzle2"); builtin_list[BUILTIN_SQRT] = KW_DEF("sqrt"); diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index d822e6407..eb3916f2c 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -328,12 +328,16 @@ const char *token_type_to_string(TokenType type) return "$alignof"; case TOKEN_CT_AND: return "$and"; + case TOKEN_CT_APPEND: + return "$append"; case TOKEN_CT_ASSERT: return "$assert"; case TOKEN_CT_ASSIGNABLE: return "$assignable"; case TOKEN_CT_CASE: return "$case"; + case TOKEN_CT_CONCAT: + return "$concat"; case TOKEN_CT_DEFAULT: return "$default"; case TOKEN_CT_DEFINED: diff --git a/src/compiler/types.c b/src/compiler/types.c index 375fee71d..38236755b 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -709,6 +709,7 @@ AlignSize type_abi_alignment(Type *type) static inline void create_type_cache(Type *type) { + assert(type->type_cache == NULL); for (int i = 0; i < ARRAY_OFFSET; i++) { vec_add(type->type_cache, NULL); diff --git a/test/test_suite/compile_time/concat_append.c3t b/test/test_suite/compile_time/concat_append.c3t new file mode 100644 index 000000000..f28ff4999 --- /dev/null +++ b/test/test_suite/compile_time/concat_append.c3t @@ -0,0 +1,57 @@ +// #target: macos-x64 +module test; +macro foo() +{ + var c = $concat("hello", " world"); + String[*] a = $concat(String[1] { "hello" }, String[1] { " world" }); + int[2] $a = { 1, 2 }; + $a = $append($a, 100); + int z = $typeof($a).len; + var b = $a; + var d = $append(int[]{}, 1, 2, 3); + var e = $append(String[1] { "hello... " }, " there!"); + var f = $concat("", ""); + var g = $concat("bye"); + var h = $$str_hash("helloworld"); +} +fn int main() +{ + foo(); + return 0; +} + +/* #expect: test.ll + +@.str = private unnamed_addr constant [12 x i8] c"hello world\00", align 1 +@.str.1 = private unnamed_addr constant [6 x i8] c"hello\00", align 1 +@.str.2 = private unnamed_addr constant [7 x i8] c" world\00", align 1 +@.__const = private unnamed_addr constant [2 x %"char[]"] [%"char[]" { ptr @.str.1, i64 5 }, %"char[]" { ptr @.str.2, i64 6 }], align 16 +@.__const.3 = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 100], align 4 +@.__const.4 = private unnamed_addr constant [3 x i32] [i32 1, i32 2, i32 3], align 4 +@.str.5 = private unnamed_addr constant [10 x i8] c"hello... \00", align 1 +@.str.6 = private unnamed_addr constant [8 x i8] c" there!\00", align 1 +@.__const.7 = private unnamed_addr constant [2 x %"char[]"] [%"char[]" { ptr @.str.5, i64 9 }, %"char[]" { ptr @.str.6, i64 7 }], align 16 +@.str.8 = private unnamed_addr constant [4 x i8] c"bye\00", align 1 + +define i32 @main() #0 { +entry: + %c = alloca %"char[]", align 8 + %a = alloca [2 x %"char[]"], align 16 + %z = alloca i32, align 4 + %b = alloca [3 x i32], align 4 + %d = alloca [3 x i32], align 4 + %e = alloca [2 x %"char[]"], align 16 + %f = alloca %"char[]", align 8 + %g = alloca %"char[]", align 8 + %h = alloca i32, align 4 + store %"char[]" { ptr @.str, i64 11 }, ptr %c, align 8 + call void @llvm.memcpy.p0.p0.i32(ptr align 16 %a, ptr align 16 @.__const, i32 32, i1 false) + store i32 3, ptr %z, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %b, ptr align 4 @.__const.3, i32 12, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %d, ptr align 4 @.__const.4, i32 12, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 16 %e, ptr align 16 @.__const.7, i32 32, i1 false) + store %"char[]" zeroinitializer, ptr %f, align 8 + store %"char[]" { ptr @.str.8, i64 3 }, ptr %g, align 8 + store i32 1000299617, ptr %h, align 4 + ret i32 0 +}