diff --git a/releasenotes.md b/releasenotes.md index f675f8d78..4ae8d685b 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -3,6 +3,7 @@ ## 0.6.5 Change list ### Changes / improvements +- Allow splat in initializers. ### Fixes - Fix bug where `a > 0 ? f() : g()` could cause a compiler crash if both returned `void!`. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index dbaec60be..0312ac665 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2290,7 +2290,7 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement); bool sema_expr_analyse_assign_right_side(SemaContext *context, Expr *expr, Type *left_type, Expr *right, bool is_unwrapped_var, bool is_declaration); bool sema_expr_analyse_initializer_list(SemaContext *context, Type *to, Expr *expr); -Expr **sema_expand_vasplat_exprs(SemaContext *c, Expr **exprs); +Expr **sema_expand_vasplat_exprs(SemaContext *context, Expr **exprs); bool sema_expr_analyse_general_call(SemaContext *context, Expr *expr, Decl *decl, Expr *struct_var, bool optional, bool *no_match_ref); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index dfe4d102b..33f3e1b0d 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -5213,10 +5213,10 @@ static Expr **sema_vasplat_insert(SemaContext *context, Expr **init_expressions, return init_expressions; } -Expr **sema_expand_vasplat_exprs(SemaContext *c, Expr **exprs) +Expr **sema_expand_vasplat_exprs(SemaContext *context, Expr **exprs) { - if (!c || !c->current_macro) return exprs; - + if (!context) return exprs; + bool in_macro = context->current_macro; unsigned count = vec_size(exprs); bool expand; do @@ -5224,15 +5224,54 @@ Expr **sema_expand_vasplat_exprs(SemaContext *c, Expr **exprs) expand = false; for (unsigned i = 0; i < count; i++) { - if (exprs[i]->expr_kind == EXPR_VASPLAT) + Expr *arg = exprs[i]; + ExprKind kind = arg->expr_kind; + if (in_macro && kind == EXPR_VASPLAT) { - exprs = sema_vasplat_insert(c, exprs, exprs[i], i); + exprs = sema_vasplat_insert(context, exprs, arg, i); // If we have null back it failed. if (!exprs) return NULL; count = vec_size(exprs); expand = true; break; } + if (kind == EXPR_SPLAT) + { + Expr *inner = arg->inner_expr; + if (!sema_analyse_expr(context, inner)) return false; + Type *flat = type_flatten(inner->type); + switch (flat->type_kind) + { + case TYPE_VECTOR: + case TYPE_ARRAY: + case TYPE_SLICE: + case TYPE_UNTYPED_LIST: + // These may be splatted + break; + default: + SEMA_ERROR(arg, "An argument of type %s cannot be splatted.", + type_quoted_error_string(inner->type)); + return NULL; + } + ArrayIndex len = sema_len_from_expr(inner); + if (len == -1) + { + SEMA_ERROR(arg, + "Splat may not be used with if the length is not known, but if you slice it to a constant length it will work (e.g '...val[:2]')"); + return NULL; + } + if (len == 0 && !expr_is_const(arg)) + { + SEMA_ERROR(arg, "A non-constant zero size splat is not allowed."); + return NULL; + } + Expr **new_args = sema_splat_arraylike_insert(context, exprs, inner, len, i); + if (!new_args) return false; + if (!exprs) return NULL; + count = vec_size(exprs); + expand = true; + break; + } } } while (expand); return exprs; diff --git a/test/test_suite/functions/splat_init.c3t b/test/test_suite/functions/splat_init.c3t new file mode 100644 index 000000000..13d82a87f --- /dev/null +++ b/test/test_suite/functions/splat_init.c3t @@ -0,0 +1,40 @@ +// #target: macos-x64 +module splat; +import std; + +int a = 0; + +fn int[2] test() +{ + a++; + return { 2, 3 }; +} +fn void main() +{ + int[4] z = { a, ...test(), a }; +} + +/* #expect: splat.ll + +define void @splat.main() #0 { +entry: + %z = alloca [4 x i32], align 16 + %.anon = alloca [2 x i32], align 4 + %result = alloca [2 x i32], align 4 + %0 = load i32, ptr @splat.a, align 4 + store i32 %0, ptr %z, align 4 + %ptradd = getelementptr inbounds i8, ptr %z, i64 4 + %1 = call i64 @splat.test() + store i64 %1, ptr %result, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %.anon, ptr align 4 %result, i32 8, i1 false) + %2 = load i32, ptr %.anon, align 4 + store i32 %2, ptr %ptradd, align 4 + %ptradd1 = getelementptr inbounds i8, ptr %z, i64 8 + %ptradd2 = getelementptr inbounds i8, ptr %.anon, i64 4 + %3 = load i32, ptr %ptradd2, align 4 + store i32 %3, ptr %ptradd1, align 4 + %ptradd3 = getelementptr inbounds i8, ptr %z, i64 12 + %4 = load i32, ptr @splat.a, align 4 + store i32 %4, ptr %ptradd3, align 4 + ret void +}