mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Add splat defaults for designated initialization #2441.
Add ??? and +++= to list-precedence.
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 | = *= /= %= -= += |= &= ^= <<= >>= +++=");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
66
test/test_suite/functions/splat_initializer.c3t
Normal file
66
test/test_suite/functions/splat_initializer.c3t
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user