Add splat defaults for designated initialization #2441.

Add ??? and +++= to list-precedence.
This commit is contained in:
Christoffer Lerno
2025-10-09 12:45:55 +02:00
parent e34a26422f
commit 0d85caf21c
10 changed files with 204 additions and 22 deletions

View File

@@ -4,6 +4,7 @@
### Changes / improvements
- Error when using $vaarg/$vacount/$vasplat and similar in a macro without vaargs #2510.
- Add splat defaults for designated initialization #2441.
### Fixes
- Bug in `io::write_using_write_byte`.
@@ -13,6 +14,7 @@
- Compiler segfault when accessing member of number cast to bitstruct #2516.
- Compiler assert when getting a member of a `bitstruct : char @bigendian` #2517.
- Incorrect visibility on local globals with public aliases. #2519
- Add ??? and +++= to list-precedence.
### Stdlib changes
- Sorting functions correctly took slices by value, but also other types by value. Now, only slices are accepted by value, other containers are always by ref.

View File

@@ -1171,8 +1171,8 @@ void print_syntax(BuildOptions *options)
puts(" 8. Relational | < > <= >= == !=");
puts(" 9. And | && &&&");
puts("10. Or | || |||");
puts("11. Ternary | ?: ??");
puts("12. Assign | = *= /= %= -= += |= &= ^= <<= >>=");
puts("11. Ternary | ?: ?? ???");
puts("12. Assign | = *= /= %= -= += |= &= ^= <<= >>= +++=");
}
}

View File

@@ -1179,6 +1179,12 @@ typedef struct
bool is_ref;
} ExprOtherContext;
typedef struct
{
Expr **list;
Expr *splat;
} ExprDesignatedInit;
typedef struct
{
Expr *inner;
@@ -1231,7 +1237,7 @@ struct Expr_
ExprIdentifierRaw ct_ident_expr; // 24
Decl *decl_expr; // 8
Decl *iota_decl_expr; // 8
Expr **designated_init_list; // 8
ExprDesignatedInit designated_init; // 16
ExprDesignator designator_expr; // 16
ExprNamedArgument named_argument_expr;
ExprEmbedExpr embed_expr; // 16
@@ -3719,6 +3725,7 @@ static inline void exprid_set_span(ExprId expr_id, SourceSpan loc);
static inline void expr_set_span(Expr *expr, SourceSpan loc)
{
if (!expr) return;
expr->span = loc;
switch (expr->expr_kind)
{
@@ -3755,7 +3762,8 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc)
expr_list_set_span(expr->initializer_list, loc);
return;
case EXPR_DESIGNATED_INITIALIZER_LIST:
expr_list_set_span(expr->designated_init_list, loc);
expr_set_span(expr->designated_init.splat, loc);
expr_list_set_span(expr->designated_init.list, loc);
return;
case EXPR_MAKE_ANY:
expr_set_span(expr->make_any_expr.inner, loc);

View File

@@ -592,7 +592,8 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_EXPR_LIST(expr->initializer_list);
return expr;
case EXPR_DESIGNATED_INITIALIZER_LIST:
MACRO_COPY_EXPR_LIST(expr->designated_init_list);
MACRO_COPY_EXPR(expr->designated_init.splat);
MACRO_COPY_EXPR_LIST(expr->designated_init.list);
return expr;
case EXPR_EXPRESSION_LIST:
MACRO_COPY_EXPR_LIST(expr->expression_list);

View File

@@ -420,7 +420,7 @@ bool expr_is_runtime_const(Expr *expr)
case EXPR_INITIALIZER_LIST:
return expr_list_is_constant_eval(expr->initializer_list);
case EXPR_DESIGNATED_INITIALIZER_LIST:
return expr_list_is_constant_eval(expr->designated_init_list);
return (!expr->designated_init.splat || expr_is_const(expr->designated_init.splat)) && expr_list_is_constant_eval(expr->designated_init.list);
case EXPR_SLICE:
if (!exprid_is_runtime_const(expr->slice_expr.expr)) return false;
return expr->slice_expr.range.range_type == RANGE_CONST_RANGE;

View File

@@ -49,7 +49,7 @@ static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr);
static inline void llvm_emit_memcmp(GenContext *c, BEValue *be_value, LLVMValueRef ptr, LLVMValueRef other_ptr, LLVMValueRef size);
static LLVMTypeRef llvm_find_inner_struct_type_for_coerce(GenContext *c, LLVMTypeRef struct_type, ByteSize dest_size);
static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVMValueRef expand_ptr, LLVMValueRef *args, unsigned *arg_count_ref, AlignSize alignment);
static inline void llvm_emit_initialize_reference_designated_bitstruct(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements);
static inline void llvm_emit_initialize_reference_designated_bitstruct(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements, Expr *splat);
INLINE LLVMValueRef llvm_emit_bitstruct_value_update(GenContext *c, LLVMValueRef current_val, TypeSize bits, LLVMTypeRef bitstruct_type, Decl *member, LLVMValueRef val);
INLINE void llvm_emit_initialize_reference_bitstruct_array(GenContext *c, BEValue *ref, Decl *bitstruct, Expr** elements);
#define MAX_AGG 16
@@ -1824,12 +1824,21 @@ static void llvm_emit_initialize_designated_element(GenContext *c, BEValue *ref,
}
}
static inline void llvm_emit_initialize_reference_designated_bitstruct_array(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements)
static inline void llvm_emit_initialize_reference_designated_bitstruct_array(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements, Expr *splat)
{
LLVMTypeRef type = llvm_get_type(c, ref->type);
bool is_bitswap = bitstruct_requires_bitswap(bitstruct);
llvm_value_addr(c, ref);
llvm_store_zero(c, ref);
if (splat)
{
BEValue splat_val;
llvm_emit_expr(c, &splat_val, splat);
llvm_store(c, ref, &splat_val);
}
else
{
llvm_store_zero(c, ref);
}
AlignSize alignment = ref->alignment;
LLVMValueRef array_ptr = ref->value;
// Now walk through the elements.
@@ -1845,16 +1854,27 @@ static inline void llvm_emit_initialize_reference_designated_bitstruct_array(Gen
}
}
static inline void llvm_emit_initialize_reference_designated_bitstruct(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements)
static inline void llvm_emit_initialize_reference_designated_bitstruct(GenContext *c, BEValue *ref, Decl *bitstruct, Expr **elements, Expr *splat)
{
Type *underlying_type = type_lowering(ref->type);
if (underlying_type->type_kind == TYPE_ARRAY)
{
llvm_emit_initialize_reference_designated_bitstruct_array(c, ref, bitstruct, elements);
llvm_emit_initialize_reference_designated_bitstruct_array(c, ref, bitstruct, elements, splat);
return;
}
LLVMTypeRef type = llvm_get_type(c, underlying_type);
LLVMValueRef data = LLVMConstNull(type);
LLVMValueRef data;
if (!splat)
{
data = LLVMConstNull(type);
}
else
{
BEValue splat_val;
llvm_emit_expr(c, &splat_val, splat);
llvm_value_rvalue(c, &splat_val);
data = splat_val.value;
}
TypeSize bits = type_bit_size(underlying_type);
// Now walk through the elements.
@@ -1877,13 +1897,15 @@ static inline void llvm_emit_initialize_reference_designated_bitstruct(GenContex
static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEValue *ref, Expr *expr)
{
Expr **elements = expr->designated_init_list;
Expr **elements = expr->designated_init.list;
Expr *splat = expr->designated_init.splat;
ASSERT(vec_size(elements));
Type *type = type_flatten(expr->type);
ASSERT(type->type_kind != TYPE_SLICE);
if (type->type_kind == TYPE_BITSTRUCT)
{
llvm_emit_initialize_reference_designated_bitstruct(c, ref, type->decl, elements);
llvm_emit_initialize_reference_designated_bitstruct(c, ref, type->decl, elements, splat);
return;
}
@@ -1891,7 +1913,16 @@ static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEVa
llvm_value_addr(c, ref);
// Clear the memory
llvm_store_zero(c, ref);
if (splat)
{
BEValue splat_value;
llvm_emit_expr(c, &splat_value, splat);
llvm_store(c, ref, &splat_value);
}
else
{
llvm_store_zero(c, ref);
}
// Now walk through the elements.
FOREACH(Expr *, designator, elements)
@@ -6369,8 +6400,19 @@ static inline void llvm_emit_vector_initializer_list(GenContext *c, BEValue *val
}
else
{
vec_value = llvm_get_zero_raw(llvm_type);
Expr **elements = expr->designated_init_list;
Expr **elements = expr->designated_init.list;
Expr *splat = expr->designated_init.splat;
if (splat)
{
BEValue splat_val;
llvm_emit_expr(c, &splat_val, splat);
llvm_value_rvalue(c, &splat_val);
vec_value = splat_val.value;
}
else
{
vec_value = llvm_get_zero_raw(llvm_type);
}
FOREACH(Expr *, designator, elements)
{

View File

@@ -862,13 +862,18 @@ static Expr *parse_initializer_list(ParseContext *c, Expr *left, SourceSpan lhs_
ASSERT(!left && "Unexpected left hand side");
Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST);
advance_and_verify(c, TOKEN_LBRACE);
Expr *splat = NULL;
if (!try_consume(c, TOKEN_RBRACE))
{
Expr **exprs = NULL;
if (!parse_init_list(c, &exprs, TOKEN_RBRACE, NULL, true)) return poisoned_expr;
int designated = -1;
FOREACH(Expr *, expr, exprs)
FOREACH_IDX(i, Expr *, expr, exprs)
{
if (i == 0 && expr->expr_kind == EXPR_SPLAT)
{
splat = expr;
}
if (expr->expr_kind == EXPR_DESIGNATOR)
{
if (designated == 0)
@@ -876,10 +881,12 @@ static Expr *parse_initializer_list(ParseContext *c, Expr *left, SourceSpan lhs_
designated = expr->designator_expr.path[0]->kind == DESIGNATOR_FIELD ? 1 : 2;
goto ERROR;
}
designated = expr->designator_expr.path[0]->kind == DESIGNATOR_FIELD ? 1 : 2;
continue;
}
if (designated > 0) goto ERROR;
if (designated == -1 && splat) continue;
designated = 0;
continue;
ERROR:;
@@ -896,7 +903,12 @@ ERROR:;
RANGE_EXTEND_PREV(initializer_list);
if (designated > 0)
{
initializer_list->designated_init_list = exprs;
if (splat)
{
vec_erase_front(exprs, 1);
splat = splat->inner_expr;
}
initializer_list->designated_init = (ExprDesignatedInit) { .splat = splat, .list = exprs };
initializer_list->expr_kind = EXPR_DESIGNATED_INITIALIZER_LIST;
}
else

View File

@@ -503,7 +503,17 @@ static inline bool sema_expr_analyse_untyped_initializer(SemaContext *context, E
static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type *assigned, Type *flattened,
Expr *initializer, bool *no_match_ref)
{
Expr **init_expressions = initializer->designated_init_list;
Expr **init_expressions = initializer->designated_init.list;
Expr *splat = initializer->designated_init.splat;
if (splat)
{
if (!sema_analyse_expr_rvalue(context, splat)) return false;
sema_cast_const(splat);
if (IS_OPTIONAL(splat))
{
RETURN_SEMA_ERROR(splat, "An optional splat is not permitted.");
}
}
Type *original = flattened->canonical;
bool is_bitstruct = original->type_kind == TYPE_BITSTRUCT;
bool is_structlike = type_is_union_or_strukt(original) || is_bitstruct;
@@ -558,6 +568,36 @@ static bool sema_expr_analyse_designated_initializer(SemaContext *context, Type
{
type = assigned;
}
if (splat && type->canonical != splat->type->canonical)
{
if (type_is_subtype(splat->type->canonical, type->canonical))
{
Decl *decl = original->decl;
Expr *designator = expr_new(EXPR_DESIGNATOR, initializer->span);
DesignatorElement **elements = NULL;
while (true)
{
DesignatorElement *designator_element = MALLOCS(DesignatorElement);
designator_element->kind = DESIGNATOR_FIELD;
designator_element->index = 0;
vec_add(elements, designator_element);
assert(decl->is_substruct);
Decl *member = decl->strukt.members[0];
if (member->type->canonical == splat->type) break;
decl = member;
}
designator->resolve_status = RESOLVE_DONE;
designator->designator_expr.path = elements;
designator->designator_expr.value = splat;
designator->type = splat->type;
vec_insert_first(initializer->designated_init.list, designator);
initializer->designated_init.splat = NULL;
}
else
{
RETURN_SEMA_ERROR(splat, "Splat type does not match initializer type.");
}
}
initializer->type = type_add_optional(type, optional);
initializer->resolve_status = RESOLVE_DONE;
if (expr_is_runtime_const(initializer))
@@ -647,7 +687,17 @@ NO_MATCH:;
static void sema_create_const_initializer_from_designated_init(ConstInitializer *const_init, Expr *initializer)
{
// Flatten the type since the external type might be typedef or a distinct type.
const_init_rewrite_to_zero(const_init, type_flatten(initializer->type));
Type *flattened = type_flatten(initializer->type);
if (initializer->designated_init.splat)
{
Expr *splat = initializer->designated_init.splat;
ASSERT_SPAN(splat, expr_is_const_initializer(splat));
*const_init = *splat->const_expr.initializer;
}
else
{
const_init_rewrite_to_zero(const_init, flattened);
}
ASSERT(type_flatten(initializer->type)->type_kind != TYPE_SLICE);
// Loop through the initializers.
FOREACH(Expr *, expr, initializer->initializer_list)

View File

@@ -401,7 +401,8 @@ RETRY:
sema_trace_expr_list_liveness(expr->expression_list);
return;
case EXPR_DESIGNATED_INITIALIZER_LIST:
sema_trace_expr_list_liveness(expr->designated_init_list);
sema_trace_expr_liveness(expr->designated_init.splat);
sema_trace_expr_list_liveness(expr->designated_init.list);
return;
case EXPR_IDENTIFIER:
sema_trace_decl_liveness(expr->ident_expr);

View File

@@ -0,0 +1,66 @@
// #target: mingw-x64
module test;
import std;
struct ParentType
{
int a;
int b;
int x;
int y;
}
struct ChildType
{
inline ParentType p;
int c;
int ef;
char[10] ff;
}
const ParentType ABC = { .a = 1, .b = 2, .x = 12312, .y = -1 };
const ChildType ABC2 = { .a = 1, .b = 1, .x = 12, .y = 0 };
ChildType gc = { ...ABC2, .b = 7, .c = 8 };
ChildType gc2 = { ...ABC, .b = 7, .c = 8 };
fn void main()
{
ParentType some_p = { .a = 1, .b = 2, .x = 12312, .y = -1 };
ChildType c = { ...some_p, .b = 7, .c = 8 };
ParentType t = { ...some_p, .b = 343};
ChildType d = gc;
ChildType e = gc2;
}
/* #expect: test.ll
@"$ct.test.ParentType" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 16, i64 0, i64 4, [0 x i64] zeroinitializer }, comdat, align 8
@"$ct.test.ChildType" = linkonce global %.introspect { i8 10, i64 ptrtoint (ptr @"$ct.test.ParentType" to i64), ptr null, i64 36, i64 0, i64 4, [0 x i64] zeroinitializer }, comdat, align 8
@test.ABC = local_unnamed_addr constant %ParentType { i32 1, i32 7, i32 12312, i32 -1 }, align 4
@test.ABC2 = local_unnamed_addr constant %ChildType { %ParentType { i32 1, i32 7, i32 12, i32 0 }, i32 8, i32 0, [10 x i8] zeroinitializer }, align 4
@test.gc = local_unnamed_addr global %ChildType { %ParentType { i32 1, i32 7, i32 12, i32 0 }, i32 8, i32 0, [10 x i8] zeroinitializer }, align 4
@test.gc2 = local_unnamed_addr global %ChildType { %ParentType { i32 1, i32 7, i32 12312, i32 -1 }, i32 8, i32 0, [10 x i8] zeroinitializer }, align 4
@.__const = private unnamed_addr constant %ParentType { i32 1, i32 2, i32 12312, i32 -1 }, align 4
; Function Attrs: nounwind uwtable
define void @test.main() #0 {
entry:
%some_p = alloca %ParentType, align 4
%c = alloca %ChildType, align 4
%t = alloca %ParentType, align 4
%d = alloca %ChildType, align 4
%e = alloca %ChildType, align 4
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %some_p, ptr align 4 @.__const, i32 16, i1 false)
call void @llvm.memset.p0.i64(ptr align 4 %c, i8 0, i64 36, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %c, ptr align 4 %some_p, i32 16, i1 false)
%ptradd = getelementptr inbounds i8, ptr %c, i64 4
store i32 7, ptr %ptradd, align 4
%ptradd1 = getelementptr inbounds i8, ptr %c, i64 16
store i32 8, ptr %ptradd1, align 4
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %t, ptr align 4 %some_p, i32 16, i1 false)
%ptradd2 = getelementptr inbounds i8, ptr %t, i64 4
store i32 343, ptr %ptradd2, align 4
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %d, ptr align 4 @test.gc, i32 36, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %e, ptr align 4 @test.gc2, i32 36, i1 false)
ret void
}