diff --git a/missing.txt b/missing.txt new file mode 100644 index 000000000..65f37dd17 --- /dev/null +++ b/missing.txt @@ -0,0 +1,65 @@ +Things missing: + +* Attributes +- All types: @noreflect +- Struct: @packed, @aligned, @opaque +- Enums: @distinct, @noreflect +- Unions: @packed, @aligned, @opaque +- Functions: @inline, @reflect, @noreturn, @section, @unused, @used, @interrupt, @naked, @convention() +- Calls: @noinline +- Variables, parameters: @unused +- Constants, globals: @unused, @used, @section + +* Designated initializer +- Array initializer +- Array range initializer { [1..2] = 2 } + +* Initializers +- Array initializers +- Union initializers + +* Asserts +- assert, $assert +- @unreachable + +* Types +- Vararrays +- Strings +- Array +- Slice +- Values: size, alignment, name, qualifiedName +- Functions: offsetof +- Distinct types +- Simd types? +- Complex types? +- Subtype casts +- Bitstruct +- Enumset +- Typeid +- Union usage + +* Expressions +- Disallow x >= 0 and x < 0 on unsigned types unless in a macro. + +* Switch +- String switch +- Range case + +* Functions +- Varargs +- C ABI +- Safe varargs + +* Pre-post conditions +- Breakdown here + +* Error handling +- Error unions +- Catch/try +- Function return channel + +* Enum +- Values: min, max, array +- Functions: fomOrdinal, ordinal, fromName, name, fromFullName, fullName, fromQualifiedName, qualifiedName, (), fromValue() + + diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 9e937fdad..811c1a86f 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -2,16 +2,6 @@ module bar; typedef int as Bob; -/* hello *//* there */ -/+ why /+ you /* lucky +/ +/ -// Whut -// Here -// -//--- -/* - Hello - */ - struct Test { int a; @@ -38,32 +28,6 @@ struct Teob int oekfeo; } -typedef long as Frob; - -enum EnumTestAlias : Frob -{ - VALUE1 = 4, - VALUE2 -} - -enum EnumTestDefault -{ - VALUE, - VALUE2 -} - -enum EnumTestNoOverflowAfterLong : long -{ - VALUE = 0x7FFF_FFFF_FFFF_FFFE, - VALUE_NO_EXCEED -} - -enum EnumTestSmall : ushort -{ - VALUE = 0xFF, - VALUE2 = 0xFFFF -} - enum EnumWithData : ushort (int a, char[] x, long b = 4) { // Currently the args are ignored TODO! @@ -105,8 +69,8 @@ error Error { BLURB, NO_SUCH_FILE, - } + error OtherError { FOO_BAR @@ -128,6 +92,39 @@ enum Inf2 : byte typedef Inf as BooInf; +struct TestStruct +{ + int a; +} + +struct TestStruct2 +{ + TestStruct a; + char xx; + TestStruct b; + int c; +} + +union TestUnion +{ + int a; + double f; +} + + +func TestStruct structTest(int i) +{ + + TestUnion tu; +/* TestStruct foo = { i }; + TestStruct foo2 = { a = i };*/ + TestStruct foo3 = TestStruct { i }; + return foo3; +/* TestStruct2 bar = { c = 2 }; + int x = 3 * i; + TestStruct2 bar2 = { b.a = x, a.a = x + 1 }; + return bar2;*/ +} func void enumInferenceTest() { OtherError e = OtherError.FOO_BAR; diff --git a/resources/tests/comments_ok.c3 b/resources/tests/comments_ok.c3 new file mode 100644 index 000000000..4e22e310f --- /dev/null +++ b/resources/tests/comments_ok.c3 @@ -0,0 +1,13 @@ +module comments; +/* Span *//* style */ + +/+ Nested /+ Errors /* Inside +/ +/ +// Single line +/* + Multiline span style + */ + +func void test() +{ + return; +} \ No newline at end of file diff --git a/resources/tests/enum_ok.c3 b/resources/tests/enum_ok.c3 index 47bee6e57..4fc723771 100644 --- a/resources/tests/enum_ok.c3 +++ b/resources/tests/enum_ok.c3 @@ -4,3 +4,28 @@ enum EnumTest : long VALUE2 } +typedef long as Frob; + +enum EnumTestAlias : Frob +{ + VALUE1 = 4, + VALUE2 +} + +enum EnumTestDefault +{ + VALUE, + VALUE2 +} + +enum EnumTestNoOverflowAfterLong : long +{ + VALUE = 0x7FFF_FFFF_FFFF_FFFE, + VALUE_NO_EXCEED +} + +enum EnumTestSmall : ushort +{ + VALUE = 0xFF, + VALUE2 = 0xFFFF +} diff --git a/resources/tests/error_decl_ok.c3 b/resources/tests/error_decl_ok.c3 new file mode 100644 index 000000000..e9ced8885 --- /dev/null +++ b/resources/tests/error_decl_ok.c3 @@ -0,0 +1,12 @@ +module errors; + +error TheError +{ + FOO_MISSING, + NO_SUCH_FILE, +} + +error OtherError +{ + BAR_OVERFLOWED +} diff --git a/resources/tests/structs_ok.c3 b/resources/tests/structs_ok.c3 new file mode 100644 index 000000000..e69de29bb diff --git a/resources/tests/typedef_errors.c3 b/resources/tests/typedef_errors.c3 new file mode 100644 index 000000000..49180f6d2 --- /dev/null +++ b/resources/tests/typedef_errors.c3 @@ -0,0 +1,5 @@ +module typedefs; + +typedef Loop as Loop2; +typedef Loop2 as Loop3; +typedef Loop3 as Loop; diff --git a/resources/tests/typedefs_ok.c3 b/resources/tests/typedefs_ok.c3 new file mode 100644 index 000000000..c1a2c397e --- /dev/null +++ b/resources/tests/typedefs_ok.c3 @@ -0,0 +1,14 @@ +module typedefs; + +// Standard case +typedef int as Foo; + +// Nested resolution +typedef AType as BType; +typedef int as AType; + +enum Bar : BType +{ + A, + B +} diff --git a/src/compiler/ast.c b/src/compiler/ast.c index d2be7fee5..1a0437c3e 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -551,12 +551,6 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_expr_common(file, expr, indent + 1); fprint_type_info_recursive(file, expr->type_access.type, indent + 1); break; - case EXPR_STRUCT_VALUE: - fprintf_indented(file, indent, "(structvalue\n"); - fprint_expr_common(file, expr, indent + 1); - fprint_type_info_recursive(file, expr->struct_value_expr.type, indent + 1); - fprint_expr_recursive(file, expr->struct_value_expr.init_expr, indent + 1); - break; case EXPR_ACCESS: fprintf_indented(file, indent, "(access .%s\n", expr->access_expr.sub_element.string); fprint_expr_common(file, expr, indent + 1); @@ -595,12 +589,27 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_expr_recursive(file, expr->ternary_expr.else_expr, indent + 1); break; case EXPR_INITIALIZER_LIST: - fprintf_indented(file, indent, "(initializerlist\n"); + fprintf_indented(file, indent, "(initializerlist "); + switch (expr->expr_initializer.init_type) + { + case INITIALIZER_UNKNOWN: + fprintf(file, "not-analyzed\n"); + break; + case INITIALIZER_ZERO: + fprintf(file, "zero\n"); + break; + case INITIALIZER_NORMAL: + fprintf(file, "normal\n"); + break; + case INITIALIZER_DESIGNATED: + fprintf(file, "designated\n"); + break; + } fprint_expr_common(file, expr, indent + 1); { - VECEACH(expr->initializer_expr, i) + VECEACH(expr->expr_initializer.initializer_expr, i) { - fprint_expr_recursive(file, expr->initializer_expr[i], indent + 1); + fprint_expr_recursive(file, expr->expr_initializer.initializer_expr[i], indent + 1); } } break; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3360123cf..7ee85e311 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -137,7 +137,7 @@ typedef struct { unsigned char bitsize; unsigned char bytesize; - unsigned char min_alignment; + unsigned char abi_alignment; unsigned char pref_alignment; } TypeBuiltin; @@ -226,7 +226,7 @@ typedef struct typedef struct { - uint32_t alignment; + uint32_t abi_alignment; uint64_t size; Decl **members; } StructDecl; @@ -500,6 +500,32 @@ typedef struct Ast **stmts; } ExprFuncBlock; +typedef enum +{ + INITIALIZER_UNKNOWN, + INITIALIZER_ZERO, + INITIALIZER_DESIGNATED, + INITIALIZER_NORMAL +} InitializerType; + +typedef struct +{ + InitializerType init_type; + Expr** initializer_expr; +} ExprInitializer; + +typedef struct _DesignatedInitPath +{ + Decl *decl; + struct _DesignatedInitPath *sub_path; +} DesignatedInitPath; + +typedef struct +{ + DesignatedInitPath path; + Expr *value; +} ExprDesignatedInit; + struct _Expr { ExprKind expr_kind : 8; @@ -507,6 +533,7 @@ struct _Expr SourceRange span; Type *type; union { + ExprDesignatedInit designated_init_expr; ExprCast cast_expr; ExprConst const_expr; ExprStructValue struct_value_expr; @@ -522,7 +549,7 @@ struct _Expr ExprAccess access_expr; ExprIdentifier identifier_expr; ExprType type_expr; - Expr** initializer_expr; + ExprInitializer expr_initializer; Expr** expression_list; ExprScope expr_scope; ExprFuncBlock expr_block; @@ -1133,6 +1160,7 @@ bool type_is_subtype(Type *type, Type *possible_subtype); Type *type_find_common_ancestor(Type *left, Type *right); const char *type_to_error_string(Type *type); size_t type_size(Type *canonical); +size_t type_abi_alignment(Type *canonical); void type_append_signature_name(Type *type, char *dst, size_t *offset); Type *type_find_max_type(Type *type, Type *other); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 746625013..0940beec4 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -223,14 +223,13 @@ typedef enum EXPR_SIZEOF, EXPR_SUBSCRIPT, EXPR_ACCESS, - EXPR_STRUCT_VALUE, - EXPR_STRUCT_INIT_VALUES, EXPR_INITIALIZER_LIST, EXPR_EXPRESSION_LIST, EXPR_CAST, EXPR_SCOPED_EXPR, EXPR_MACRO_EXPR, EXPR_EXPR_BLOCK, + EXPR_DESIGNATED_INIT, } ExprKind; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a339bc32c..de51838fb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -101,12 +101,11 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_POST_UNARY: case EXPR_TYPE_ACCESS: case EXPR_CALL: - case EXPR_STRUCT_VALUE: - case EXPR_STRUCT_INIT_VALUES: case EXPR_INITIALIZER_LIST: case EXPR_EXPRESSION_LIST: case EXPR_CAST: case EXPR_MACRO_EXPR: + case EXPR_DESIGNATED_INIT: UNREACHABLE } UNREACHABLE @@ -740,30 +739,55 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont return value; } + + static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr) { LLVMTypeRef type = llvm_type(expr->type); LLVMValueRef value = LLVMGetUndef(type); - if (!vec_size(expr->initializer_expr)) + 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.alignment); - return ref; + LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.abi_alignment); + return value; } - VECEACH(expr->initializer_expr, i) + Expr **elements = expr->expr_initializer.initializer_expr; + + if (expr->expr_initializer.init_type == INITIALIZER_NORMAL) { - LLVMValueRef init_value = gencontext_emit_expr(context, expr->initializer_expr[i]); - value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); + VECEACH(elements, i) + { + LLVMValueRef init_value = gencontext_emit_expr(context, elements[i]); + value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal"); + } + return value; } - return value; -} -static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *context, Expr *expr) -{ - TODO + 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) @@ -803,6 +827,9 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { case EXPR_POISONED: UNREACHABLE + case EXPR_DESIGNATED_INIT: + // This is handled inside of initializer setup + UNREACHABLE case EXPR_EXPR_BLOCK: return gencontext_emit_expr_block(context, expr); case EXPR_SCOPED_EXPR: @@ -831,10 +858,6 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) return gencontext_emit_call_expr(context, expr); case EXPR_ACCESS: return gencontext_emit_access_expr(context, expr); - case EXPR_STRUCT_VALUE: - return gencontext_emit_struct_value_expr(context, expr); - case EXPR_STRUCT_INIT_VALUES: - return gencontext_emit_struct_init_values_expr(context, expr); case EXPR_INITIALIZER_LIST: return gencontext_emit_initializer_list_expr(context, expr); case EXPR_EXPRESSION_LIST: diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 428e1dde6..20da33bab 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -44,11 +44,11 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { Expr *expr = decl->var.init_expr; // Quick path for empty initializer list - if (expr->expr_kind == EXPR_INITIALIZER_LIST && vec_size(expr->initializer_expr) == 0) + if (expr->expr_kind == EXPR_INITIALIZER_LIST && expr->expr_initializer.init_type == INITIALIZER_ZERO) { LLVMBuildMemSet(context->builder, decl->var.backend_ref, LLVMConstInt(llvm_type(type_byte), 0, false), LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), - expr->type->decl->strukt.alignment); + expr->type->decl->strukt.abi_alignment); return decl->var.backend_ref; } diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 749801279..6df4a31b6 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -226,5 +226,5 @@ 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.alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); + decl->strukt.abi_alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type); } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 381118072..2632df310 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -249,8 +249,9 @@ Expr *parse_initializer(Context *context) Expr *parse_initializer_list(Context *context) { Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST, context->tok); + initializer_list->expr_initializer.init_type = INITIALIZER_UNKNOWN; CONSUME_OR(TOKEN_LBRACE, &poisoned_expr); - if (!parse_param_list(context, &initializer_list->initializer_expr, false)) return &poisoned_expr; + if (!parse_param_list(context, &initializer_list->expr_initializer.initializer_expr, false)) return &poisoned_expr; CONSUME_OR(TOKEN_RBRACE, &poisoned_expr); return initializer_list; } diff --git a/src/compiler/parser.c b/src/compiler/parser.c index eb4691057..b4528ac34 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -1033,7 +1033,9 @@ bool parse_struct_body(Context *context, Decl *parent, Decl *visible_parent) decl_poison(other); decl_poison(member); } + unsigned index = vec_size(parent->strukt.members); parent->strukt.members = VECADD(parent->strukt.members, member); + member->var.id = index; advance(context); if (context->tok.type != TOKEN_COMMA) break; } @@ -1994,11 +1996,7 @@ Expr *parse_type_identifier_with_path(Context *context, Path *path) RANGE_EXTEND_PREV(type); if (context->tok.type == TOKEN_LBRACE) { - Expr *expr = EXPR_NEW_TOKEN(EXPR_STRUCT_VALUE, context->tok); - expr->struct_value_expr.type = type; - expr->struct_value_expr.init_expr = TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); - - return expr; + return TRY_EXPR_OR(parse_initializer_list(context), &poisoned_expr); } EXPECT_OR(TOKEN_DOT, &poisoned_expr); return parse_type_access(context, type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 68bb9cde3..359599e07 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -40,6 +40,8 @@ static inline bool sema_analyse_error(Context *context __unused, Decl *decl) static inline bool sema_analyse_struct_member(Context *context, Decl *decl) { + assert(decl->resolve_status == RESOLVE_NOT_DONE); + decl->resolve_status = RESOLVE_RUNNING; if (decl->decl_kind == DECL_STRUCT || decl->decl_kind == DECL_UNION) { DEBUG_LOG("Beginning analysis of inner struct/union"); @@ -74,6 +76,7 @@ static inline bool sema_analyse_struct_member(Context *context, Decl *decl) } decl->type = decl->var.type_info->type; assert(decl->var.type_info->type); + decl->resolve_status = RESOLVE_DONE; return true; } @@ -430,6 +433,52 @@ 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) { if (decl->resolve_status == RESOLVE_DONE) return decl_ok(decl); @@ -450,9 +499,13 @@ bool sema_analyse_decl(Context *context, Decl *decl) if (!sema_analyse_throws(context, decl)) return decl_poison(decl); break; case DECL_STRUCT: + if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); + sema_set_struct_size(decl); + decl_set_external_name(decl); + break; case DECL_UNION: if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); - llvm_set_struct_size_alignment(decl); + sema_set_union_size(decl); decl_set_external_name(decl); break; case DECL_FUNC: diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index ab86eb7d7..61f7015fb 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -280,15 +280,6 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr } } -static inline bool sema_expr_analyse_struct_value(Context *context, Type *to, Expr *expr) -{ - TODO -} - -static inline bool sema_expr_analyse_struct_init_values(Context *context, Type *to, Expr *expr) -{ - TODO -} static inline bool sema_expr_analyse_subscript(Context *context, Type *to, Expr *expr) { @@ -492,6 +483,7 @@ static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr * expr->resolve_status = RESOLVE_RUNNING; expr->identifier_expr.decl = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier); expr->resolve_status = RESOLVE_DONE; + expr->type = expr->identifier_expr.decl->type; return expr->identifier_expr.decl; } @@ -507,6 +499,7 @@ static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *acce } decl = access_expr->access_expr.ref = sema_analyse_init_identifier_string(context, decl->type->decl, access_expr->access_expr.sub_element.string); access_expr->resolve_status = RESOLVE_DONE; + access_expr->type = decl->type; return decl; } @@ -534,74 +527,189 @@ static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr) } } +static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr); -typedef enum +static bool sema_expr_analyse_designated_init_ident(Context *context, DesignatedInitPath *path, Decl *top, const char *name) { - INIT_SEMA_ERROR, - INIT_SEMA_NOT_FOUND, - INIT_SEMA_OK -} InitSemaResult; - -static InitSemaResult sema_expr_analyse_struct_named_initializer_list(Context *context, Decl *assigned, Expr *expr_list) -{ - VECEACH(expr_list->initializer_expr, i) + // 3. Loop through the members. + Decl **members = top->strukt.members; + VECEACH(members, i) { - Expr *expr = expr_list->initializer_expr[i]; + Decl *member = members[i]; + if (member->name == name) + { + // 4. If found, set this to the current path. + (*path) = (DesignatedInitPath) { .decl = member, .sub_path = NULL }; + + // 5. And we're done! + return true; + } + // 6. We might encounter anonymous members. Treat this as a possible "match" + if (!member->name) + { + DesignatedInitPath anon_path; + bool found = sema_expr_analyse_designated_init_ident(context, &anon_path, member, name); + if (found) + { + // 7. If found, create a copy path. + DesignatedInitPath *path_copy = malloc_arena(sizeof(DesignatedInitPath)); + *path_copy = anon_path; + (*path) = (DesignatedInitPath) { .decl = member, .sub_path = path_copy }; + return true; + } + } + } + return false; + +} +static bool sema_expr_analyse_designated_init(Context *context, DesignatedInitPath *path, Decl *top, Expr *path_expr) +{ + // Our expression will look like this for a.b[3].c = ... + // access(subscript(access(identifier))), now we need to reverse that. + switch (path_expr->expr_kind) + { + case EXPR_IDENTIFIER: + + // Resolving for an identifier: + // 1. Can't be a path attached. + if (path_expr->identifier_expr.path) return false; + + // 2. Ensure it's a union or struct + if (!decl_is_struct_type(top)) return false; + + return sema_expr_analyse_designated_init_ident(context, path, top, path_expr->identifier_expr.identifier); + + case EXPR_ACCESS: + + // Resolve for access: + + // 1. Resolve the parent path: + if (!sema_expr_analyse_designated_init(context, path, top, path_expr->access_expr.parent)) return false; + + // 2. Our new top is now lowest init path. + while (path->sub_path) + { + path = path->sub_path; + } + top = path->decl; + + // 3. Do an analysis with the identifier: + path->sub_path = malloc_arena(sizeof(DesignatedInitPath)); + return sema_expr_analyse_designated_init_ident(context, + path->sub_path, + top->type->decl, + path_expr->access_expr.sub_element.string); + case EXPR_SUBSCRIPT: + + // Resolve for subscript: + + // 1. Resolve the parent path: + if (!sema_expr_analyse_designated_init(context, path, top, path_expr->subscript_expr.expr)) return false; + + // 2. Our new top is now lowest init path. + while (path->sub_path) + { + path = path->sub_path; + } + top = path->decl; + + TODO // Analyse index etc. + default: + return false; + } +} + +static bool sema_expr_analyse_struct_designated_initializer_list(Context *context, Decl *assigned, Expr *expr_list) +{ + assert(expr_list->expr_initializer.init_type == INITIALIZER_UNKNOWN); + VECEACH(expr_list->expr_initializer.initializer_expr, i) + { + Expr *expr = expr_list->expr_initializer.initializer_expr[i]; if (expr->expr_kind != EXPR_BINARY && expr->binary_expr.operator != BINARYOP_ASSIGN) { if (i != 0) { SEMA_ERROR(expr, "Named and non-named initializers are not allowed together, please choose one or the other."); - return INIT_SEMA_ERROR; + return false; } // If there is an unexpected expression and no previous element then this is a normal initializer list. - return INIT_SEMA_NOT_FOUND; + expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; + return true; } - Expr *path = expr->binary_expr.left; + Expr *path_expr = expr->binary_expr.left; Expr *value = expr->binary_expr.right; - Decl *result = sema_analyse_init_path(context, assigned, path); - if (!result) + DesignatedInitPath path; + if (!sema_expr_analyse_designated_init(context, &path, assigned, path_expr)) { if (i != 0) { - SEMA_ERROR(path, "Unexpected element when initializing '%s', did you get the name right?", assigned->name); - return INIT_SEMA_ERROR; + SEMA_ERROR(path_expr, "Unexpected element when initializing '%s', did you get the name right?", assigned->name); + return false; } - return INIT_SEMA_NOT_FOUND; + expr_list->expr_initializer.init_type = INITIALIZER_NORMAL; + return true; } - if (!sema_analyse_expr_of_required_type(context, result->type, value)) return INIT_SEMA_ERROR; + // Walk down to the last decl. + DesignatedInitPath *init_path = &path; + while (init_path->sub_path) init_path = init_path->sub_path; + + if (!sema_analyse_expr_of_required_type(context, init_path->decl->type, value)) return false; + + // Destruct the expression and replace. + expr->designated_init_expr.value = value; // Do this first! + expr->resolve_status = RESOLVE_DONE; + expr->designated_init_expr.path = path; + expr->expr_kind = EXPR_DESIGNATED_INIT; + expr->type = init_path->decl->type; } - return INIT_SEMA_OK; + expr_list->expr_initializer.init_type = INITIALIZER_DESIGNATED; + return true; } static inline bool sema_expr_analyse_struct_initializer_list(Context *context, Type *assigned, Expr *expr) { + expr->type = assigned; + Decl **members = assigned->decl->strukt.members; unsigned size = vec_size(members); - // Zero size init will initialize to empty. - if (size == 0) return true; - InitSemaResult result = sema_expr_analyse_struct_named_initializer_list(context, assigned->decl, expr); - if (result == INIT_SEMA_ERROR) return false; - if (result == INIT_SEMA_OK) + // Zero size init will initialize to empty. + if (size == 0) { - TODO + expr->expr_initializer.init_type = INITIALIZER_ZERO; + return true; } + + if (!sema_expr_analyse_struct_designated_initializer_list(context, assigned->decl, expr)) return false; + + // If we already parsed this. + if (expr->expr_initializer.init_type == INITIALIZER_DESIGNATED) return true; + + Expr **elements = expr->expr_initializer.initializer_expr; + + assert(expr->expr_initializer.init_type == INITIALIZER_NORMAL); + if (assigned->type_kind == TYPE_UNION) { - SEMA_ERROR(expr->initializer_expr[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }"); + SEMA_ERROR(elements[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }"); return false; } - if (size < vec_size(expr->initializer_expr)) + + VECEACH(elements, i) { - SEMA_ERROR(expr->initializer_expr[size], "Too many elements in initializer, expected only %d.", size); + if (i >= size) + { + SEMA_ERROR(elements[size], "Too many elements in initializer, expected only %d.", size); + return false; + } + if (!sema_analyse_expr_of_required_type(context, members[i]->type, elements[i])) return false; + } + + if (size > vec_size(elements)) + { + SEMA_ERROR(elements[vec_size(elements) - 1], "Too few elements in initializer, expected %d.", size); return false; } - VECEACH(expr->initializer_expr, i) - { - if (!sema_analyse_expr_of_required_type(context, members[i]->type, expr->initializer_expr[i])) return false; - } - expr->type = assigned; return true; } @@ -1910,6 +2018,7 @@ static Expr *expr_shallow_copy(Expr *source) return copy; } + static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list); static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr); static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source); @@ -1968,6 +2077,9 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex Expr *expr = expr_shallow_copy(source_expr); switch (source_expr->expr_kind) { + case EXPR_DESIGNATED_INIT: + // This type of expression is only created after analysis. + UNREACHABLE case EXPR_EXPR_BLOCK: ast_copy_list_from_macro(context, macro, &expr->expr_block.stmts); return expr; @@ -2017,15 +2129,8 @@ static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_ex case EXPR_ACCESS: EXPR_COPY(expr->access_expr.parent); return expr; - case EXPR_STRUCT_VALUE: - expr->struct_value_expr.type = type_info_copy_from_macro(context, macro, expr->struct_value_expr.type); - EXPR_COPY(expr->struct_value_expr.init_expr); - return expr; - case EXPR_STRUCT_INIT_VALUES: - TODO - return expr; case EXPR_INITIALIZER_LIST: - expr->initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->initializer_expr); + expr->expr_initializer.initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->expr_initializer.initializer_expr); return expr; case EXPR_EXPRESSION_LIST: expr->expression_list = expr_copy_expr_list_from_macro(context, macro, expr->expression_list); @@ -2426,6 +2531,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_POISONED: return false; case EXPR_SCOPED_EXPR: + case EXPR_DESIGNATED_INIT: UNREACHABLE case EXPR_EXPR_BLOCK: return sema_expr_analyse_expr_block(context, to, expr); @@ -2457,10 +2563,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return sema_expr_analyse_subscript(context, to, expr); case EXPR_ACCESS: return sema_expr_analyse_access(context, to, expr); - case EXPR_STRUCT_VALUE: - return sema_expr_analyse_struct_value(context, to, expr); - case EXPR_STRUCT_INIT_VALUES: - return sema_expr_analyse_struct_init_values(context, to, expr); case EXPR_INITIALIZER_LIST: return sema_expr_analyse_initializer_list(context, to, expr); case EXPR_CAST: diff --git a/src/compiler/target.c b/src/compiler/target.c index 3b9a8039b..db35a19b2 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -109,6 +109,7 @@ void target_setup() LLVMTypeRef float_type = LLVMFloatType(); LLVMTypeRef double_type = LLVMDoubleType(); LLVMTypeRef quad_type = LLVMFP128Type(); + LLVMTypeRef pointer_type = LLVMPointerType(int_type, 0); build_target.align_byte = LLVMABIAlignmentOfType(build_target.llvm_data_layout, byte_type); build_target.align_short = LLVMABIAlignmentOfType(build_target.llvm_data_layout, short_type); build_target.align_int = LLVMABIAlignmentOfType(build_target.llvm_data_layout, int_type); @@ -116,6 +117,7 @@ void target_setup() build_target.align_f128 = LLVMABIAlignmentOfType(build_target.llvm_data_layout, quad_type); build_target.align_double = LLVMABIAlignmentOfType(build_target.llvm_data_layout, double_type); build_target.align_float = LLVMABIAlignmentOfType(build_target.llvm_data_layout, float_type); + build_target.align_pointer = LLVMABIAlignmentOfType(build_target.llvm_data_layout, pointer_type); build_target.little_endian = LLVMByteOrder(build_target.llvm_data_layout) == LLVMLittleEndian; build_target.width_c_short = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_SHORT); build_target.width_c_int = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_INT); diff --git a/src/compiler/target.h b/src/compiler/target.h index d7c8058a7..89cf5b10a 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -151,15 +151,15 @@ typedef struct bool asm_supported; bool float_128; bool float_16; - unsigned align_min_pointer; - unsigned align_min_byte; - unsigned align_min_short; - unsigned align_min_int; - unsigned align_min_long; - unsigned align_min_half; - unsigned align_min_float; - unsigned align_min_double; - unsigned align_min_f128; + unsigned align_pref_pointer; + unsigned align_pref_byte; + unsigned align_pref_short; + unsigned align_pref_int; + unsigned align_pref_long; + unsigned align_pref_half; + unsigned align_pref_float; + unsigned align_pref_double; + unsigned align_pref_f128; unsigned align_pointer; unsigned align_byte; unsigned align_short; diff --git a/src/compiler/types.c b/src/compiler/types.c index 0b8e6cb1c..cdcc088cd 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -198,29 +198,18 @@ size_t type_size(Type *canonical) case TYPE_META_TYPE: return 0; case TYPE_ENUM: - return type_size(canonical->decl->enums.type_info->type->canonical); + return canonical->decl->enums.type_info->type->canonical->builtin.bytesize; + case TYPE_ERROR: + return type_error->canonical->builtin.bytesize; case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERROR: - TODO + return canonical->decl->strukt.size; case TYPE_VOID: return 1; case TYPE_BOOL: - case TYPE_I8: - case TYPE_I16: - case TYPE_I32: - case TYPE_I64: - case TYPE_U8: - case TYPE_U16: - case TYPE_U32: - case TYPE_U64: - case TYPE_F32: - case TYPE_F64: + case ALL_INTS: + case ALL_FLOATS: return canonical->builtin.bytesize; - case TYPE_IXX: - return 8; - case TYPE_FXX: - return 8; case TYPE_FUNC: case TYPE_POINTER: case TYPE_VARARRAY: @@ -233,7 +222,44 @@ size_t type_size(Type *canonical) case TYPE_ERROR_UNION: TODO } - TODO + UNREACHABLE +} + +size_t type_abi_alignment(Type *canonical) +{ + assert(canonical && canonical->canonical == canonical); + switch (canonical->type_kind) + { + case TYPE_POISONED: + case TYPE_TYPEDEF: + case TYPE_VOID: + UNREACHABLE; + case TYPE_META_TYPE: + return 0; + case TYPE_ENUM: + return canonical->decl->enums.type_info->type->canonical->builtin.abi_alignment; + case TYPE_ERROR: + return type_error->canonical->builtin.abi_alignment; + case TYPE_STRUCT: + case TYPE_UNION: + return canonical->decl->strukt.abi_alignment; + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + return canonical->builtin.abi_alignment; + case TYPE_FUNC: + case TYPE_POINTER: + case TYPE_VARARRAY: + case TYPE_STRING: + return t_usz.canonical->builtin.abi_alignment; + case TYPE_ARRAY: + return type_abi_alignment(canonical->array.base); + case TYPE_SUBARRAY: + TODO + case TYPE_ERROR_UNION: + TODO + } + UNREACHABLE } static inline void create_type_cache(Type *canonical_type) @@ -375,7 +401,7 @@ static void type_create(const char *name, Type *location, TypeKind kind, unsigne .type_kind = kind, .builtin.bytesize = (bitsize + 7) / 8, .builtin.bitsize = bitsize, - .builtin.min_alignment = align, + .builtin.abi_alignment = align, .builtin.pref_alignment = pref_align, .name = name, .canonical = location, @@ -403,7 +429,7 @@ void builtin_setup(Target *target) type_string.type_kind = TYPE_STRING; */ #define DEF_TYPE(_name, _shortname, _type, _bits, _align) \ -type_create(#_name, &_shortname, _type, _bits, target->align_min_ ## _align, target->align_ ## _align) +type_create(#_name, &_shortname, _type, _bits, target->align_ ## _align, target->align_pref_ ## _align) DEF_TYPE(bool, t_u1, TYPE_BOOL, 1, byte); DEF_TYPE(float, t_f32, TYPE_F32, 32, float); @@ -424,7 +450,7 @@ type_create(#_name, &_shortname, _type, _bits, target->align_min_ ## _align, tar #undef DEF_TYPE - type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer); + type_create("void*", &t_voidstar, TYPE_POINTER, target->width_pointer, target->align_pref_pointer, target->align_pointer); create_type_cache(type_void); type_void->type_cache[0] = &t_voidstar; t_voidstar.pointer = type_void; diff --git a/src/target_info/target_info.c b/src/target_info/target_info.c index 6b0a93802..0a3186237 100644 --- a/src/target_info/target_info.c +++ b/src/target_info/target_info.c @@ -42,6 +42,7 @@ TargetInfo target_info_new() .asm_supported = false, .float_128 = false, .float_16 = false, + .align_pointer = 8, .align_byte = 8, .align_c_int = 32, .align_c_long = 32, diff --git a/src/utils/lib.h b/src/utils/lib.h index eb73ba974..c86aabbf7 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -267,6 +267,7 @@ static inline void* _expand(void *vec, size_t element_size) #define VECEACH(_vec, _index) \ for (unsigned _index = 0, __vecsize = vec_size(_vec); _index < __vecsize; _index++) + #define VECNEW(_type, _capacity) ((_type *)(_vec_new(sizeof(_type), _capacity) + 1)) #define VECADD(_vec, _value) \ ({ \