diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 77416ee06..585c1a673 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -208,6 +208,7 @@ LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_ Decl *decl = const_init->type->decl; Decl **members = decl->strukt.members; uint32_t count = vec_size(members); + if (decl->decl_kind == DECL_UNION && count) count = 1; LLVMValueRef *entries = NULL; bool was_modified = false; for (MemberIndex i = 0; i < count; i++) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 579ebeac8..cee8543a7 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3950,7 +3950,10 @@ static int decl_count_elements(Decl *structlike) { int elements = 0; Decl **members = structlike->strukt.members; - VECEACH(members, i) + unsigned member_size = vec_size(members); + if (member_size == 0) return 0; + if (structlike->decl_kind == DECL_UNION) member_size = 1; + for (unsigned i = 0; i < member_size; i++) { Decl *member = members[i]; if (member->decl_kind != DECL_VAR && !member->name) @@ -3972,6 +3975,12 @@ static inline void not_enough_elements(Expr *initializer, int element) } SEMA_ERROR(initializer->initializer_list[element - 1], "Too few elements in initializer, there should be elements after this one."); } + +static inline bool sema_is_anon_member(Decl *decl) +{ + return decl->decl_kind != DECL_VAR && !decl->name; +} + /** * Perform analysis for a plain initializer, that is one initializing all fields. * @return true if analysis succeeds. @@ -3981,22 +3990,21 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte Expr **elements = initializer->initializer_list; Decl **members = assigned->strukt.members; MemberIndex size = (MemberIndex)vec_size(elements); - MemberIndex expected_members = (MemberIndex)vec_size(members); + unsigned elements_needed = decl_count_elements(assigned); // 1. For struct number of members must be the same as the size of the struct. // Since we already handled the case with an empty initializer before going here // zero entries must be an error. assert(size > 0 && "We should already have handled the size == 0 case."); - if (expected_members == 0) + + // 2. We don't support this actually, but we used to. Maybe we will in the future. + if (elements_needed == 0) { // Generate a nice error message for zero. SEMA_ERROR(elements[0], "Too many elements in initializer, it must be empty."); return false; } - // 2. In case of a union, only expect a single entry. - if (assigned->decl_kind == DECL_UNION) expected_members = 1; - bool failable = false; bool is_bitstruct = assigned->decl_kind == DECL_BITSTRUCT; @@ -4008,17 +4016,18 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte return false; } } + // 3. Loop through all elements. - MemberIndex max_loop = MAX(size, expected_members); + MemberIndex max_loop = size > elements_needed ? size : elements_needed; for (MemberIndex i = 0; i < max_loop; i++) { // 4. Check if we exceeded the list of elements in the struct/union. // This way we can check the other elements which might help the // user pinpoint where they put the double elements. - if (i >= expected_members) + if (i >= elements_needed) { assert(i < size); - SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", expected_members); + SEMA_ERROR(elements[i], "Too many elements in initializer, expected only %d.", elements_needed); return false; } // 5. We might have anonymous members @@ -4048,14 +4057,15 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte return false; } Expr *new_initializer = expr_new(EXPR_INITIALIZER_LIST, elements[i]->span); - int max_index_to_copy = MIN(i + sub_element_count, size); + int max_index_to_copy = i + sub_element_count < size ? i + sub_element_count : size; for (int j = i; j < max_index_to_copy; j++) { vec_add(new_initializer->initializer_list, elements[j]); } int reduce_by = max_index_to_copy - i - 1; size -= reduce_by; - max_loop = MAX(size, expected_members); + elements_needed -= reduce_by; + max_loop = MAX(size, elements_needed); assert(size <= vec_size(initializer->initializer_list)); vec_resize(initializer->initializer_list, (unsigned)size); elements = initializer->initializer_list; @@ -4064,6 +4074,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte if (i >= size) { not_enough_elements(initializer, i); + return false; } Expr *element = elements[i]; // 6. We know the required type, so resolve the expression. @@ -4078,7 +4089,7 @@ static inline bool sema_expr_analyse_struct_plain_initializer(SemaContext *conte if (failable) initializer->type = type_get_failable(initializer->type); // 6. There's the case of too few values as well. Mark the last field as wrong. - assert(expected_members <= size); + assert(elements_needed <= size); if (expr_is_constant_eval(initializer, CONSTANT_EVAL_ANY)) { @@ -4225,6 +4236,7 @@ static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *ext // EXPR_DESIGNATED_INITIALIZER_LIST // or EXPR_INITIALIZER_LIST + // 1. Designated initializer is separately evaluated. if (expr->expr_kind == EXPR_DESIGNATED_INITIALIZER_LIST) { expr->type = external_type; @@ -4233,13 +4245,17 @@ static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *ext assert(expr->expr_kind == EXPR_INITIALIZER_LIST); + // 2. Grab the expressions inside. Expr **init_expressions = expr->initializer_list; - unsigned init_expression_count = vec_size(init_expressions); - // 1. Zero size init will initialize to empty. + // 3. Zero size init will initialize to empty. if (init_expression_count == 0) { + if (external_type->type_kind == TYPE_INFERRED_ARRAY) + { + REMINDER("Handle zero size inferred array."); + } external_type = sema_type_lower_by_size(external_type, 0); expr->type = external_type; ConstInitializer *initializer = CALLOCS(ConstInitializer); @@ -4249,15 +4265,19 @@ static inline bool sema_expr_analyse_initializer(SemaContext *context, Type *ext return true; } + // 4. If we have an inferred array, we need to set the size. external_type = sema_type_lower_by_size(external_type, init_expression_count); assigned = sema_type_lower_by_size(assigned, init_expression_count); + + // 5. Set the type. expr->type = external_type; + // 6. We might have a complist, because were analyzing $foo = { ... } or similar. if (external_type == type_complist) { return sema_expr_analyse_untyped_initializer(context, expr); } - // 3. Otherwise use the plain initializer. + // 7. If not, then we see if we have an array. if (assigned->type_kind == TYPE_UNTYPED_LIST || assigned->type_kind == TYPE_ARRAY || assigned->type_kind == TYPE_INFERRED_ARRAY || diff --git a/src/version.h b/src/version.h index b1d0afb4b..5d8065056 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.2.10" \ No newline at end of file +#define COMPILER_VERSION "0.2.11" \ No newline at end of file diff --git a/test/test_suite/struct/nested_struct_init.c3t b/test/test_suite/struct/nested_struct_init.c3t new file mode 100644 index 000000000..49165fb91 --- /dev/null +++ b/test/test_suite/struct/nested_struct_init.c3t @@ -0,0 +1,86 @@ +// #target: macos-x64 +module foo; +import libc; + +struct Matrix2x2 +{ + union + { + struct { + float m00, m01, m10, m11; + } + float[4] m; + } +} + +struct Matrix2x2_b +{ + union + { + float[4] m; + struct { + float m00, m01, m10, m11; + } + } +} + +fn void main() +{ + Matrix2x2 m = { 1, 2, 3, 4 }; + Matrix2x2_b m2 = { { 1, 2, 3, 4 } }; + libc::printf("%f %f %f %f\n", m.m00, m.m[1], m.m10, m.m[3]); +} +/* #expect: foo.ll + +%Matrix2x2 = type { %anon } +%anon = type { %anon.0 } +%anon.0 = type { float, float, float, float } +%Matrix2x2_b = type { %anon.1 } +%anon.1 = type { [4 x float] } +@.typeid.foo.anon = linkonce constant { i8, i64 } { i8 10, i64 4 }, align 8 +@.typeid.foo.anon.1 = linkonce constant { i8, i64 } { i8 11, i64 2 }, align 8 +@.typeid.foo.Matrix2x2 = linkonce constant { i8, i64 } { i8 10, i64 1 }, align 8 +@.typeid.foo.anon.2 = linkonce constant { i8, i64 } { i8 10, i64 4 }, align 8 +@.typeid.foo.anon.3 = linkonce constant { i8, i64 } { i8 11, i64 2 }, align 8 +@.typeid.foo.Matrix2x2_b = linkonce constant { i8, i64 } { i8 10, i64 1 }, align 8 +@.__const = private unnamed_addr constant %Matrix2x2 { %anon { %anon.0 { float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00 } } }, align 4 +@.__const.4 = private unnamed_addr constant %Matrix2x2_b { %anon.1 { [4 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00] } }, align 4 +@.str = private unnamed_addr constant [13 x i8] c"%f %f %f %f\0A\00", align 1 +; Function Attrs: nounwind +define void @foo.main() #0 { +entry: + %m = alloca %Matrix2x2, align 4 + %m2 = alloca %Matrix2x2_b, align 4 + %0 = bitcast %Matrix2x2* %m to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%Matrix2x2* @.__const to i8*), i32 16, i1 false) + %1 = bitcast %Matrix2x2_b* %m2 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %1, i8* align 4 bitcast (%Matrix2x2_b* @.__const.4 to i8*), i32 16, i1 false) + %2 = getelementptr inbounds %Matrix2x2, %Matrix2x2* %m, i32 0, i32 0 + %3 = bitcast %anon* %2 to %anon.0* + %4 = getelementptr inbounds %anon.0, %anon.0* %3, i32 0, i32 0 + %5 = load float, float* %4, align 4 + %fpfpext = fpext float %5 to double + %6 = getelementptr inbounds %Matrix2x2, %Matrix2x2* %m, i32 0, i32 0 + %7 = bitcast %anon* %6 to [4 x float]* + %8 = getelementptr inbounds [4 x float], [4 x float]* %7, i64 0, i64 1 + %9 = load float, float* %8, align 4 + %fpfpext1 = fpext float %9 to double + %10 = getelementptr inbounds %Matrix2x2, %Matrix2x2* %m, i32 0, i32 0 + %11 = bitcast %anon* %10 to %anon.0* + %12 = getelementptr inbounds %anon.0, %anon.0* %11, i32 0, i32 2 + %13 = load float, float* %12, align 4 + %fpfpext2 = fpext float %13 to double + %14 = getelementptr inbounds %Matrix2x2, %Matrix2x2* %m, i32 0, i32 0 + %15 = bitcast %anon* %14 to [4 x float]* + %16 = getelementptr inbounds [4 x float], [4 x float]* %15, i64 0, i64 3 + %17 = load float, float* %16, align 4 + %fpfpext3 = fpext float %17 to double + %18 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0), double %fpfpext, double %fpfpext1, double %fpfpext2, double %fpfpext3) + ret void +} +; Function Attrs: nounwind +define i32 @main(i32 %0, i8** %1) #0 { +entry: + call void @foo.main() + ret i32 0 +} \ No newline at end of file diff --git a/test/test_suite2/struct/nested_struct_init.c3t b/test/test_suite2/struct/nested_struct_init.c3t new file mode 100644 index 000000000..e846f04fb --- /dev/null +++ b/test/test_suite2/struct/nested_struct_init.c3t @@ -0,0 +1,74 @@ +// #target: macos-x64 +module foo; +import libc; + +struct Matrix2x2 +{ + union + { + struct { + float m00, m01, m10, m11; + } + float[4] m; + } +} + +struct Matrix2x2_b +{ + union + { + float[4] m; + struct { + float m00, m01, m10, m11; + } + } +} + +fn void main() +{ + Matrix2x2 m = { 1, 2, 3, 4 }; + Matrix2x2_b m2 = { { 1, 2, 3, 4 } }; + libc::printf("%f %f %f %f\n", m.m00, m.m[1], m.m10, m.m[3]); +} +/* #expect: foo.ll + +%Matrix2x2 = type { %anon } +%anon = type { %anon.0 } +%anon.0 = type { float, float, float, float } +%Matrix2x2_b = type { %anon.1 } +%anon.1 = type { [4 x float] } +@.typeid.foo.anon = linkonce constant { i8, i64 } { i8 10, i64 4 }, align 8 +@.typeid.foo.anon.1 = linkonce constant { i8, i64 } { i8 11, i64 2 }, align 8 +@.typeid.foo.Matrix2x2 = linkonce constant { i8, i64 } { i8 10, i64 1 }, align 8 +@.typeid.foo.anon.2 = linkonce constant { i8, i64 } { i8 10, i64 4 }, align 8 +@.typeid.foo.anon.3 = linkonce constant { i8, i64 } { i8 11, i64 2 }, align 8 +@.typeid.foo.Matrix2x2_b = linkonce constant { i8, i64 } { i8 10, i64 1 }, align 8 +@.__const = private unnamed_addr constant %Matrix2x2 { %anon { %anon.0 { float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00 } } }, align 4 +@.__const.4 = private unnamed_addr constant %Matrix2x2_b { %anon.1 { [4 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00] } }, align 4 +@.str = private unnamed_addr constant [13 x i8] c"%f %f %f %f\0A\00", align 1 +; Function Attrs: nounwind +define void @foo.main() #0 { +entry: + %m = alloca %Matrix2x2, align 4 + %m2 = alloca %Matrix2x2_b, align 4 + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %m, ptr align 4 @.__const, i32 16, i1 false) + call void @llvm.memcpy.p0.p0.i32(ptr align 4 %m2, ptr align 4 @.__const.4, i32 16, i1 false) + %0 = getelementptr inbounds %Matrix2x2, ptr %m, i32 0, i32 0 + %1 = getelementptr inbounds %anon.0, ptr %0, i32 0, i32 0 + %2 = load float, ptr %1, align 4 + %fpfpext = fpext float %2 to double + %3 = getelementptr inbounds %Matrix2x2, ptr %m, i32 0, i32 0 + %4 = getelementptr inbounds [4 x float], ptr %3, i64 0, i64 1 + %5 = load float, ptr %4, align 4 + %fpfpext1 = fpext float %5 to double + %6 = getelementptr inbounds %Matrix2x2, ptr %m, i32 0, i32 0 + %7 = getelementptr inbounds %anon.0, ptr %6, i32 0, i32 2 + %8 = load float, ptr %7, align 4 + %fpfpext2 = fpext float %8 to double + %9 = getelementptr inbounds %Matrix2x2, ptr %m, i32 0, i32 0 + %10 = getelementptr inbounds [4 x float], ptr %9, i64 0, i64 3 + %11 = load float, ptr %10, align 4 + %fpfpext3 = fpext float %11 to double + %12 = call i32 (ptr, ...) @printf(ptr @.str, double %fpfpext, double %fpfpext1, double %fpfpext2, double %fpfpext3) + ret void +}