diff --git a/missing.txt b/missing.txt index 65f37dd17..e22a5a02c 100644 --- a/missing.txt +++ b/missing.txt @@ -1,14 +1,15 @@ Things missing: * Attributes -- All types: @noreflect +- All types: @noreflect, @deprecated - Struct: @packed, @aligned, @opaque - Enums: @distinct, @noreflect - Unions: @packed, @aligned, @opaque - Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention() -- Calls: @noinline +- Calls: @noinline, @inline - Variables, parameters: @unused - Constants, globals: @unused, @used, @section +- Labels: @unused * Designated initializer - Array initializer @@ -17,6 +18,7 @@ Things missing: * Initializers - Array initializers - Union initializers +- Initializers with anonymous members * Asserts - assert, $assert @@ -36,7 +38,10 @@ Things missing: - Bitstruct - Enumset - Typeid -- Union usage + +* Struct / union +- Cast to union? +- Structural typed anonymous structs and casts to them. * Expressions - Disallow x >= 0 and x < 0 on unsigned types unless in a macro. diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 811c1a86f..d397b426a 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -106,24 +106,67 @@ struct TestStruct2 } union TestUnion +{ + int a; + double f; + TestStruct2 e; +} +union SimpleUnion { int a; double f; } - -func TestStruct structTest(int i) +struct AnonStruct { + int a; + struct sune + { + int b; + int c; + } + struct + { + int b1; + int c1; + } + union + { + int b2; + int c2; + } + int x; +} - TestUnion tu; -/* TestStruct foo = { i }; - TestStruct foo2 = { a = i };*/ +func void testAnonStruct() +{ + AnonStruct s = { b2 = 3, b1 = 7, sune.b = 1 }; + s.sune.b = 1; + s.b1 = 2; + s.b2 = 3; + s.c2 = 4; + +} + +func void testUnion() +{ + SimpleUnion s; + s.a = 1; + s.f = 1.0; + //s = { f = 1.0 }; + TestUnion tu = { e = TestStruct2 { c = 1 } }; + tu.e = TestStruct2 { c = 1 }; +} + +func TestStruct2 structTest(int i) +{ + TestStruct foo = { i }; + TestStruct foo2 = { a = i }; TestStruct foo3 = TestStruct { i }; - return foo3; -/* TestStruct2 bar = { c = 2 }; + TestStruct2 bar = { c = 2 }; int x = 3 * i; TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; - return bar2;*/ + return bar2; } func void enumInferenceTest() { diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 1a0437c3e..657a2cb18 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -8,7 +8,6 @@ static void fprint_asts_recursive(FILE *file, Ast **asts, int indent); Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility) { - assert(name.string); Decl *decl = CALLOCS(Decl); decl->decl_kind = decl_kind; decl->name_span = name.span; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 7ee85e311..ddce8d6a3 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -227,6 +227,7 @@ typedef struct typedef struct { uint32_t abi_alignment; + uint32_t id; uint64_t size; Decl **members; } StructDecl; @@ -237,7 +238,11 @@ typedef struct _VarDecl unsigned id : 16; VarDeclKind kind : 3; TypeInfo *type_info; - Expr *init_expr; + union + { + Expr *init_expr; + Decl *parent; + }; void *backend_ref; void *backend_debug_ref; } VarDecl; @@ -364,7 +369,11 @@ typedef struct _Decl { struct { - Decl** method_functions; + union + { + Decl* parent_struct; + Decl** method_functions; + }; union { ErrorDecl error; @@ -460,8 +469,6 @@ typedef struct Token sub_element; Decl *ref; }; - // TODO cleanup - int index; } ExprAccess; typedef struct @@ -997,7 +1004,6 @@ bool cast_to_runtime(Expr *expr); void cast_to_smallest_runtime(Expr *expr); void llvm_codegen(Context *context); -void llvm_set_struct_size_alignment(Decl *decl); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr); @@ -1182,6 +1188,20 @@ static inline Type *type_reduced(Type *type) return canonical; } +static inline bool type_is_structlike(Type *type) +{ + assert(type->canonical = type); + switch (type->type_kind) + { + case TYPE_UNION: + case TYPE_STRUCT: + return true; + default: + return false; + + } +} + static inline Type *type_reduced_from_expr(Expr *expr) { return type_reduced(expr->type); diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index de51838fb..d9e5239d4 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -54,10 +54,38 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E } } +static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member) +{ + unsigned index; + Decl *current_parent; + if (decl_is_struct_type(member)) + { + index = member->strukt.id; + current_parent = member->parent_struct; + } + else + { + index = member->var.id; + current_parent = member->var.parent; + } + assert(current_parent); + if (parent != current_parent) + { + value = gencontext_emit_member_addr(context, value, parent, current_parent); + } + + if (current_parent->decl_kind == DECL_UNION) + { + return LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_type(member->type), 0), ""); + } + return LLVMBuildStructGEP2(context->builder, llvm_type(current_parent->type), value, index, ""); +} static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr) { - LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - return LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); + Expr *parent = expr->access_expr.parent; + LLVMValueRef value = gencontext_emit_address(context, parent); + Decl *member = expr->access_expr.ref; + return gencontext_emit_member_addr(context, value, parent->type->canonical->decl, member); } LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr) @@ -177,6 +205,70 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); } +static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) +{ + LLVMTypeRef type = llvm_type(expr->type); + LLVMValueRef ref = gencontext_emit_alloca(context, type, "literal"); + + if (expr->expr_initializer.init_type == INITIALIZER_ZERO) + { + LLVMBuildMemSet(context->builder, + ref, + LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), + expr->type->decl->strukt.abi_alignment); + return ref; + } + + Expr **elements = expr->expr_initializer.initializer_expr; + + bool is_union = expr->type->canonical->type_kind == TYPE_UNION; + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) + { + assert(!is_union); + VECEACH(elements, i) + { + Expr *element = elements[i]; + LLVMValueRef init_value = gencontext_emit_expr(context, element); + LLVMValueRef subref = LLVMBuildStructGEP2(context->builder, type, ref, i, ""); + LLVMBuildStore(context->builder, init_value, subref); + } + return ref; + } + + + // Clear the temp. + LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); + + + VECEACH(elements, i) + { + Expr *element = elements[i]; + LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); + Decl *parent = expr->type->decl; + DesignatedInitPath *path = &element->designated_init_expr.path; + LLVMValueRef subref = ref; + assert(element->expr_kind == EXPR_DESIGNATED_INIT); + // If this is a union, we down a step to begin with. + if (is_union) + { + subref = LLVMBuildBitCast(context->builder, ref, LLVMPointerType(llvm_type(path->decl->type), 0), "unioncast"); + parent = path->decl; + path = path->sub_path; + } + while (path) + { + subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); + parent = path->decl; + path = path->sub_path; + } + LLVMBuildStore(context->builder, sub_value, subref); + } + + return ref; +} + static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff) { Type *type = type_reduced_from_expr(expr); @@ -580,10 +672,6 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) if (binary_op == BINARYOP_ASSIGN) { LLVMValueRef addr = gencontext_emit_address(context, expr->binary_expr.left); - if (expr->binary_expr.right->expr_kind == EXPR_INITIALIZER_LIST) - { - return gencontext_emit_initialization_from_expr(context, addr, expr->binary_expr.right); - } LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right); LLVMBuildStore(context->builder, value, addr); return value; @@ -725,7 +813,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr { // Improve, add string description to the access? LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent); - LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, ""); + LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, expr->access_expr.ref->var.id, ""); return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, ""); } @@ -741,54 +829,6 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont -static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) -{ - LLVMTypeRef type = llvm_type(expr->type); - LLVMValueRef value = LLVMGetUndef(type); - - if (expr->expr_initializer.init_type == INITIALIZER_ZERO) - { - LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); - value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); - return value; - } - - Expr **elements = expr->expr_initializer.initializer_expr; - - if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) - { - VECEACH(elements, i) - { - LLVMValueRef init_value = gencontext_emit_expr(context, elements[i]); - value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); - } - return value; - } - - LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp"); - value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false), - LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); - - VECEACH(elements, i) - { - Expr *element = elements[i]; - LLVMValueRef sub_value = gencontext_emit_expr(context, element->designated_init_expr.value); - Decl *parent = expr->type->decl; - DesignatedInitPath *path = &element->designated_init_expr.path; - LLVMValueRef subref = ref; - assert(element->expr_kind == EXPR_DESIGNATED_INIT); - while (path) - { - subref = LLVMBuildStructGEP2(context->builder, llvm_type(parent->type), subref, path->decl->var.id, "access"); - parent = path->decl; - path = path->sub_path; - } - LLVMBuildStore(context->builder, sub_value, subref); - } - - return ref; -} static inline LLVMValueRef gencontext_emit_expr_block(GenContext *context, Expr *expr) { diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 6df4a31b6..e8e66bc21 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -52,20 +52,28 @@ static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl } case DECL_UNION: { - LLVMTypeRef max_type = NULL; + Decl *max_type = NULL; unsigned long long max_size = 0; VECEACH(decl->strukt.members, i) { - LLVMTypeRef type = llvm_get_type(context, decl->strukt.members[i]->type); - unsigned long long size = LLVMStoreSizeOfType(target_data_layout(), type); + Decl *member = decl->strukt.members[i]; + unsigned size = type_size(member->type); if (size > max_size || !max_type) { max_size = size; - max_type = type; + max_type = member; } } LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name); - LLVMStructSetBody(type, &max_type, 1, false); + if (max_type) + { + LLVMTypeRef type_ref = llvm_get_type(context, max_type->type); + LLVMStructSetBody(type, &type_ref, 1, false); + } + else + { + LLVMStructSetBody(type, NULL, 0, true); + } return type; } case DECL_ENUM: @@ -222,9 +230,3 @@ LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type) return llvm_get_type(context->context, type); } -void llvm_set_struct_size_alignment(Decl *decl) -{ - LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type); - decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type); - decl->strukt.abi_alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); -} diff --git a/src/compiler/parser.c b/src/compiler/parser.c index b4528ac34..d04af80a0 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -989,11 +989,11 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) Decl *member; if (context->next_tok.type != TOKEN_IDENT) { - Token name_replacement = context->tok; - name_replacement.string = NULL; - member = decl_new_with_type(name_replacement, decl_kind, parent->visibility); - advance(context); - } + Token name_replacement = context->tok; + name_replacement.string = NULL; + member = decl_new_with_type(name_replacement, decl_kind, parent->visibility); + advance(context); + } else { advance(context); @@ -1010,6 +1010,8 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) advance_and_verify(context, TOKEN_IDENT); } if (!parse_attributes(context, member)) return false; + member->parent_struct = parent; + member->strukt.id = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); if (!parse_struct_body(context, member, context->tok.type == TOKEN_IDENT ? member : visible_parent)) { @@ -1036,6 +1038,7 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) unsigned index = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); member->var.id = index; + member->var.parent = parent; advance(context); if (context->tok.type != TOKEN_COMMA) break; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 359599e07..12ead3405 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -38,6 +38,53 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl) } +static inline void sema_set_struct_size(Decl *decl) +{ + // TODO packed + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + assert(member_size > 0); + // Add padding. + if (member_alignment && (size % member_alignment)) + { + size += member_alignment - size % member_alignment; + } + // Add size. + size += member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + if (alignment && size % alignment) + { + size += alignment - size % alignment; + } + decl->strukt.size = size; +} + +static inline void sema_set_union_size(Decl *decl) +{ + uint64_t size = 0; + uint64_t alignment = 0; + VECEACH(decl->strukt.members, i) + { + Decl *member = decl->strukt.members[i]; + Type *canonical = member->type->canonical; + uint64_t member_size = type_size(canonical); + uint64_t member_alignment = type_abi_alignment(canonical); + if (member_size > size) size = member_size; + if (member_alignment > alignment) alignment = member_alignment; + } + decl->strukt.abi_alignment = alignment; + decl->strukt.size = size; +} + + static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { assert(decl->resolve_status == RESOLVE_NOT_DONE); @@ -63,12 +110,19 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) decl_poison(decl); } } + if (decl->decl_kind == DECL_UNION) + { + sema_set_union_size(decl); + } + else + { + sema_set_struct_size(decl); + } DEBUG_LOG("Analysis complete."); return decl_ok(decl); } assert(decl->decl_kind == DECL_VAR); assert(decl->var.kind == VARDECL_MEMBER); - assert(!decl->var.init_expr); if (!sema_resolve_type_info(context, decl->var.type_info)) { decl_poison(decl); @@ -92,7 +146,7 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl) decl_poison(decl); continue; } - if (!sema_analyse_struct_member(context, decl->strukt.members[i])) + if (!sema_analyse_struct_member(context, member)) { if (decl_ok(decl)) { @@ -433,51 +487,6 @@ static inline bool sema_analyse_generic(Context *context, Decl *decl) return true; } -static inline void sema_set_struct_size(Decl *decl) -{ - // TODO packed - uint64_t size = 0; - uint64_t alignment = 0; - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - Type *canonical = member->type->canonical; - uint64_t member_size = type_size(canonical); - uint64_t member_alignment = type_abi_alignment(canonical); - assert(member_size > 0); - // Add padding. - if (member_alignment && (size % member_alignment)) - { - size += member_alignment - size % member_alignment; - } - // Add size. - size += member_size; - if (member_alignment > alignment) alignment = member_alignment; - } - decl->strukt.abi_alignment = alignment; - if (alignment && size % alignment) - { - size += alignment - size % alignment; - } - decl->strukt.size = size; -} - -static inline void sema_set_union_size(Decl *decl) -{ - uint64_t size = 0; - uint64_t alignment = 0; - VECEACH(decl->strukt.members, i) - { - Decl *member = decl->strukt.members[i]; - Type *canonical = member->type->canonical; - uint64_t member_size = type_size(canonical); - uint64_t member_alignment = type_abi_alignment(canonical); - if (member_size > size) size = member_size; - if (member_alignment > alignment) alignment = member_alignment; - } - decl->strukt.abi_alignment = alignment; - decl->strukt.size = size; -} bool sema_analyse_decl(Context *context, Decl *decl) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 61f7015fb..efae32883 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -335,17 +335,19 @@ static inline bool sema_expr_analyse_method_function(Context *context, Expr *exp } -static Decl *strukt_recursive_search_member(Decl *strukt, const char *name, int *index) +static Decl *strukt_recursive_search_member(Decl *strukt, const char *name) { VECEACH(strukt->strukt.members, i) { - (*index)++; Decl *member = strukt->strukt.members[i]; if (member->name == name) return member; - if (!member->name && decl_is_struct_type(member)) + if (!member->name && type_is_structlike(member->type->canonical)) { - Decl *result = strukt_recursive_search_member(member, name, index); - if (result) return result; + Decl *result = strukt_recursive_search_member(member->type->canonical->decl, name); + if (result) + { + return result; + } } } return NULL; @@ -380,8 +382,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex default: UNREACHABLE } - int index = -1; - Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string, &index); + Decl *member = strukt_recursive_search_member(decl, expr->access_expr.sub_element.string); if (!member) { SEMA_TOKEN_ERROR(expr->access_expr.sub_element, "There is no element '%s.%s'.", decl->name, expr->access_expr.sub_element.string); @@ -397,7 +398,7 @@ static inline bool sema_expr_analyse_access(Context *context, Type *to, Expr *ex expr->access_expr.parent = deref; } expr->type = member->type; - expr->access_expr.index = index; + expr->access_expr.ref = member; return true; }