From f180a0d44a035d2548a34beb8c9a8b486abe05bd Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 12 Aug 2021 01:08:22 +0200 Subject: [PATCH] Change anyerror { i64, i64 } -> i64. Cleaned up platform data and max tls / vector align. Initial work on bitstruct (just parsing). Updated try / catch semantics. --- resources/lib/std/env.c3 | 2 +- resources/lib/std/mem.c3 | 10 +- src/compiler/ast.c | 95 ++-- src/compiler/compiler.c | 6 +- src/compiler/compiler_internal.h | 131 +++++- src/compiler/context.c | 4 +- src/compiler/copying.c | 33 +- src/compiler/enums.h | 25 +- src/compiler/headers.c | 11 +- src/compiler/lexer.c | 20 +- src/compiler/llvm_codegen.c | 45 +- src/compiler/llvm_codegen_c_abi_riscv.c | 2 +- src/compiler/llvm_codegen_c_abi_x64.c | 22 +- src/compiler/llvm_codegen_c_abi_x86.c | 16 +- src/compiler/llvm_codegen_debug_info.c | 8 +- src/compiler/llvm_codegen_expr.c | 184 ++++++-- src/compiler/llvm_codegen_function.c | 4 +- src/compiler/llvm_codegen_internal.h | 4 +- src/compiler/llvm_codegen_stmt.c | 14 +- src/compiler/llvm_codegen_type.c | 53 +-- src/compiler/parse_expr.c | 199 ++++++-- src/compiler/parse_global.c | 171 ++++--- src/compiler/parse_stmt.c | 11 +- src/compiler/parser_internal.h | 4 +- src/compiler/sema_casts.c | 29 +- src/compiler/sema_decls.c | 144 ++++-- src/compiler/sema_expr.c | 179 +++---- src/compiler/sema_internal.h | 3 + src/compiler/sema_name_resolution.c | 37 +- src/compiler/sema_stmts.c | 435 ++++++++++++++---- src/compiler/sema_types.c | 11 +- src/compiler/symtab.c | 1 + src/compiler/target.c | 236 +++++----- src/compiler/target.h | 54 +-- src/compiler/tokens.c | 8 + src/compiler/types.c | 143 +++--- src/utils/errors.h | 2 +- .../invalid_bitstruct_member_types.c3 | 26 ++ .../invalid_bitstruct_name_other_ident.c3 | 20 + .../bitstruct/invalid_bitstruct_type.c3 | 7 + test/test_suite/errors/else_checks.c3t | 167 +++---- test/test_suite/errors/error_decl_fails.c3 | 12 - test/test_suite/errors/error_decl_ok.c3 | 9 +- .../test_suite/errors/error_semantic_fails.c3 | 6 +- test/test_suite/errors/error_throw.c3 | 5 +- test/test_suite/errors/error_union.c3 | 5 +- .../errors/general_error_regression.c3t | 147 ++++++ test/test_suite/errors/rethrow.c3t | 41 +- test/test_suite/errors/rethrow_mingw.c3t | 38 +- .../errors/simple_static_failable.c3t | 29 +- test/test_suite/errors/try_assign.c3t | 147 +++--- test/test_suite/errors/try_catch_if.c3t | 99 ++++ .../errors/try_catch_unwrapping_while_if.c3 | 44 +- .../errors/try_with_assign_to_failable.c3 | 10 +- .../errors/try_with_chained_unwrap.c3t | 50 +- .../errors/try_with_chained_unwrap_errors.c3 | 93 ++++ test/test_suite/errors/try_with_unwrap.c3t | 142 ++---- test/test_suite/errors/try_with_unwrapper.c3t | 187 ++++++++ .../test_suite/errors/try_with_weird_stuff.c3 | 16 + test/test_suite/functions/test_regression.c3t | 3 +- .../functions/test_regression_mingw.c3t | 3 +- .../methods/enum_distinct_err_methods.c3t | 18 +- .../statements/foreach_with_error.c3t | 16 +- 63 files changed, 2572 insertions(+), 1124 deletions(-) create mode 100644 test/test_suite/bitstruct/invalid_bitstruct_member_types.c3 create mode 100644 test/test_suite/bitstruct/invalid_bitstruct_name_other_ident.c3 create mode 100644 test/test_suite/bitstruct/invalid_bitstruct_type.c3 delete mode 100644 test/test_suite/errors/error_decl_fails.c3 create mode 100644 test/test_suite/errors/general_error_regression.c3t create mode 100644 test/test_suite/errors/try_catch_if.c3t create mode 100644 test/test_suite/errors/try_with_chained_unwrap_errors.c3 create mode 100644 test/test_suite/errors/try_with_unwrapper.c3t create mode 100644 test/test_suite/errors/try_with_weird_stuff.c3 diff --git a/resources/lib/std/env.c3 b/resources/lib/std/env.c3 index 3a63bbd55..e04eee38a 100644 --- a/resources/lib/std/env.c3 +++ b/resources/lib/std/env.c3 @@ -9,6 +9,6 @@ enum CompilerOptLevel } const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)(${COMPILER_OPT_LEVEL}); -const bool LITTLE_ENDIAN = ${PLATFORM_LITTLE_ENDIAN}; +const bool BIG_ENDIAN = ${PLATFORM_BIG_ENDIAN}; const bool I128_SUPPORT = ${PLATFORM_I128_SUPPORTED}; const bool COMPILER_SAFE_MODE = ${COMPILER_SAFE_MODE}; diff --git a/resources/lib/std/mem.c3 b/resources/lib/std/mem.c3 index e014c4955..910bead58 100644 --- a/resources/lib/std/mem.c3 +++ b/resources/lib/std/mem.c3 @@ -11,14 +11,10 @@ enum AllocationKind REALLOC, FREE, } -enum AllocationFailureKind -{ - OUT_OF_MEMORY -} errtype AllocationFailure { - AllocationFailureKind failureKind; + OUT_OF_MEMORY } define AllocatorFunction = func void!(void *data, void** pointer, usize bytes, usize alignment, AllocationKind kind); @@ -35,12 +31,12 @@ func void! system_malloc_function(void *unused, void** pointer, usize bytes, usi { case ALLOC: void* data = _malloc(bytes); - if (!data) return AllocationFailure({ OUT_OF_MEMORY })!; + if (!data) return AllocationFailure.OUT_OF_MEMORY!; *pointer = data; return; case REALLOC: void* data = _realloc(*pointer, bytes); - if (!data) return AllocationFailure({ OUT_OF_MEMORY })!; + if (!data) return AllocationFailure.OUT_OF_MEMORY!; *pointer = data; return; case FREE: diff --git a/src/compiler/ast.c b/src/compiler/ast.c index c0d79842f..1bb1b1b0e 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -52,6 +52,8 @@ const char *decl_to_name(Decl *decl) { switch (decl->decl_kind) { + case DECL_BITSTRUCT: + return "bitstruct"; case DECL_POISONED: return "poisoned decl"; case DECL_CT_ASSERT: @@ -83,8 +85,10 @@ const char *decl_to_name(Decl *decl) return "enum"; case DECL_ENUM_CONSTANT: return "enum value"; - case DECL_ERR: - return "error"; + case DECL_ERRVALUE: + return "err value"; + case DECL_ERRTYPE: + return "errtype"; case DECL_FUNC: return "function"; case DECL_GENERIC: @@ -100,6 +104,9 @@ const char *decl_to_name(Decl *decl) case DECL_VAR: switch (decl->var.kind) { + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + UNREACHABLE case VARDECL_CONST: return "constant"; case VARDECL_GLOBAL: @@ -122,8 +129,8 @@ const char *decl_to_name(Decl *decl) return "compile time variable"; case VARDECL_LOCAL_CT_TYPE: return "compile time type variable"; - case VARDECL_ALIAS: - return "alias"; + case VARDECL_UNWRAPPED: + return "unwrapped"; } UNREACHABLE } @@ -162,7 +169,7 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_STRUCT: kind = TYPE_STRUCT; break; - case DECL_ERR: + case DECL_ERRTYPE: kind = TYPE_ERRTYPE; break; case DECL_ENUM: @@ -174,9 +181,13 @@ Decl *decl_new_with_type(TokenId name, DeclKind decl_type, Visibility visibility case DECL_TYPEDEF: kind = TYPE_TYPEDEF; break; + case DECL_BITSTRUCT: + kind = TYPE_BITSTRUCT; + break; case DECL_POISONED: case DECL_VAR: case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: case DECL_ARRAY_VALUE: case DECL_IMPORT: case DECL_MACRO: @@ -213,7 +224,7 @@ const char *decl_var_to_string(VarDeclKind kind) return "member"; case VARDECL_PARAM: return "param"; - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: return "alias"; case VARDECL_PARAM_CT: return "$param"; @@ -227,6 +238,9 @@ const char *decl_var_to_string(VarDeclKind kind) return "$local"; case VARDECL_LOCAL_CT_TYPE: return "$Local"; + case VARDECL_REWRAPPED: + case VARDECL_ERASE: + UNREACHABLE } UNREACHABLE } @@ -416,6 +430,12 @@ void fprint_type_recursive(Context *context, FILE *file, Type *type, int indent) } switch (type->type_kind) { + case TYPE_BITSTRUCT: + DUMP("(bitstruct"); + return; + case TYPE_ERRTYPE: + DUMPF("(errtype %s)", type->name); + return; case TYPE_DISTINCT: DUMPF("(distinct %s)", type->name); return; @@ -443,9 +463,6 @@ void fprint_type_recursive(Context *context, FILE *file, Type *type, int indent) case TYPE_ENUM: DUMPF("(enum %s)", type->name); return; - case TYPE_ERRTYPE: - DUMPF("(errtype %s)", type->name); - return; case TYPE_TYPEDEF: DUMPF("(typedef %s", type->name); DUMPTYPE(type->canonical); @@ -488,7 +505,7 @@ void fprint_type_recursive(Context *context, FILE *file, Type *type, int indent) case TYPE_STRLIT: DUMP("(ct string)"); return; - case TYPE_ERR_UNION: + case TYPE_ANYERR: DUMP("(any-error)"); return; } @@ -603,6 +620,26 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPDECL(expr->decl_expr); break; + case EXPR_TRY_UNWRAP_CHAIN: + DUMP("(try-unwrap-chain"); + DUMPEXPC(expr); + DUMPEXPRS(expr->try_unwrap_chain_expr); + break; + case EXPR_CATCH_UNWRAP: + DUMP("(catch-unwrap"); + DUMPEXPC(expr); + DUMPEXPRS(expr->catch_unwrap_expr.exprs); + break; + case EXPR_TRY_UNWRAP: + DUMP("(try-unwrap"); + DUMPEXPC(expr); + DUMPEXPR(expr->try_unwrap_expr.init); + break; + case EXPR_TRY_DECL: + DUMP("(try-decl"); + DUMPEXPC(expr); + DUMPDECL(expr->try_decl_expr.decl); + break; case EXPR_NOP: DUMP("(nop)"); return; @@ -644,9 +681,9 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPEXPR(expr->len_expr.inner); DUMPEND(); - case EXPR_DECL_LIST: + case EXPR_COND: DUMP("(decllist"); - DUMPEXPRS(expr->dexpr_list_expr); + DUMPEXPRS(expr->cond_expr); DUMPEND(); case EXPR_FAILABLE: DUMP("(failable"); @@ -710,16 +747,6 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMPEXPC(expr); DUMPEXPR(expr->post_expr.expr); DUMPEND(); - case EXPR_CATCH_OLD: - DUMP("(catch"); - DUMPEXPC(expr); - DUMPEXPR(expr->trycatch_expr); - DUMPEND(); - case EXPR_TRY_OLD: - DUMP("(try"); - DUMPEXPC(expr); - DUMPEXPR(expr->trycatch_expr); - DUMPEND(); case EXPR_TRY: DUMPF("(try %d", expr->try_expr.is_try); DUMPEXPC(expr); @@ -895,6 +922,9 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) if (!decl) return; switch (decl->decl_kind) { + case DECL_BITSTRUCT: + DUMP("(bitstruct)"); + return; case DECL_INTERFACE: DUMPF("(interface %s", decl->name); DUMPDECLS(decl->interface_decl.members); @@ -920,9 +950,12 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) case VARDECL_LOCAL_CT_TYPE: DUMPTI(decl->var.type_info); break; - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: DUMPDECL(decl->var.alias); break; + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + UNREACHABLE } DUMPEND(); case DECL_CT_ASSERT: @@ -980,9 +1013,10 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPTI(decl->enums.type_info); DUMPDECLS(decl->enums.values); DUMPEND(); - case DECL_ERR: - DUMPF("(error %s", decl->name); - DUMPDECLS(decl->strukt.members); + case DECL_ERRTYPE: + DUMPF("(errtype %s", decl->name); + DUMPTI(decl->enums.type_info); + DUMPDECLS(decl->enums.values); DUMPEND(); case DECL_ENUM_CONSTANT: if (!decl->enum_constant.expr) @@ -993,6 +1027,15 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent) DUMPF("(enum-constant %s", decl->name); DUMPEXPR(decl->enum_constant.expr); DUMPEND(); + case DECL_ERRVALUE: + DUMPF("(errvalue %s)", decl->name); + if (!decl->enum_constant.expr) + { + return; + } + DUMPF("(enum-constant %s", decl->name); + DUMPEXPR(decl->enum_constant.expr); + DUMPEND(); case DECL_TYPEDEF: DUMPF("(typedef %s", decl->name); if (decl->typedef_decl.is_func) diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 44fef6dfa..653cc05d0 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -158,6 +158,7 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_POISONED: case DECL_ARRAY_VALUE: case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: case DECL_IMPORT: case DECL_LABEL: case DECL_CT_ASSERT: @@ -184,13 +185,14 @@ static void register_generic_decls(Module *module, Decl **decls) case DECL_ENUM: case DECL_GENERIC: case DECL_INTERFACE: - case DECL_ERR: + case DECL_ERRTYPE: case DECL_FUNC: case DECL_MACRO: case DECL_STRUCT: case DECL_TYPEDEF: case DECL_UNION: case DECL_VAR: + case DECL_BITSTRUCT: break; } if (decl->visibility > VISIBLE_MODULE) @@ -282,7 +284,7 @@ void compiler_compile(void) setup_int_define("C_LONG_SIZE", platform_target.width_c_long); setup_int_define("C_LONG_LONG_SIZE", platform_target.width_c_long_long); setup_bool_define("C_CHAR_IS_SIGNED", platform_target.signed_c_char); - setup_bool_define("PLATFORM_LITTLE_ENDIAN", platform_target.little_endian); + setup_bool_define("PLATFORM_BIG_ENDIAN", platform_target.big_endian); setup_bool_define("PLATFORM_I128_SUPPORTED", platform_target.int128); setup_int_define("COMPILER_OPT_LEVEL", (int)active_target.optimization_level); setup_int_define("COMPILER_SIZE_OPT_LEVEL", (int)active_target.size_optimization_level); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 1633f0255..6b2803088 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -287,6 +287,12 @@ typedef struct } StructDecl; +typedef struct +{ + TypeInfo *base_type; + Decl **members; +} BitStructDecl; + typedef struct VarDecl_ { VarDeclKind kind : 4; @@ -305,11 +311,13 @@ typedef struct VarDecl_ { void *backend_debug_ref; unsigned scope_depth; + Expr *start; }; union { void *failable_ref; struct ABIArgInfo_ *abi_info; + Expr *end; }; } VarDecl; @@ -536,6 +544,7 @@ typedef struct Decl_ StructDecl strukt; EnumDecl enums; DistinctDecl distinct_decl; + BitStructDecl bitstruct; }; }; ImportDecl import; @@ -857,11 +866,58 @@ typedef struct typedef struct { - bool is_try; + bool is_try : 1; Expr *expr; Expr *init; } ExprTryAssign; +typedef struct +{ + bool is_try : 1; + Decl *decl; +} ExprTryDecl; + +typedef struct +{ + union + { + struct + { + Expr *variable; + TypeInfo *type; + }; + struct + { + Decl *decl; + Expr *lhs; + }; + }; + Expr **exprs; +} ExprCatchUnwrap; + +typedef struct +{ + union + { + struct + { + Expr *variable; + TypeInfo *type; + Expr *init; + }; + struct + { + bool assign_existing : 1; + Expr *failable; + union + { + Decl *decl; + Expr *lhs; + }; + }; + }; +} ExprTryUnwrap; + typedef struct { Expr *inner; @@ -896,11 +952,15 @@ struct Expr_ ExprBinary binary_expr; ExprTernary ternary_expr; ExprUnary unary_expr; + Expr** try_unwrap_chain_expr; ExprPostUnary post_expr; + ExprTryUnwrap try_unwrap_expr; ExprCall call_expr; ExprSlice slice_expr; ExprTryExpr try_expr; ExprTryAssign try_assign_expr; + ExprTryDecl try_decl_expr; + ExprCatchUnwrap catch_unwrap_expr; ExprSubscript subscript_expr; ExprAccess access_expr; ExprDesignator designator_expr; @@ -923,7 +983,7 @@ struct Expr_ ExprMacroBlock macro_block; Expr* failable_expr; - Expr** dexpr_list_expr; + Expr** cond_expr; }; }; @@ -1348,6 +1408,7 @@ typedef struct Context_ STable local_symbols; Decl **global_decls; Decl **enums; + Decl **errtypes; Decl **types; Decl **generic_defines; Decl **functions; @@ -1708,6 +1769,29 @@ static inline bool decl_is_struct_type(Decl *decl); static inline bool decl_is_callable_type(Decl *decl); static inline bool decl_is_user_defined_type(Decl *decl); static inline DeclKind decl_from_token(TokenType type); +static inline bool decl_var_is_assignable(Decl *decl) +{ + switch (decl->var.kind) + { + case VARDECL_GLOBAL: + case VARDECL_LOCAL: + case VARDECL_PARAM: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_PARAM_REF: + case VARDECL_LOCAL_CT: + case VARDECL_LOCAL_CT_TYPE: + case VARDECL_UNWRAPPED: + return true; + case VARDECL_CONST: + case VARDECL_MEMBER: + case VARDECL_PARAM_EXPR: + return false; + case VARDECL_REWRAPPED: + case VARDECL_ERASE: + UNREACHABLE + } +} static inline Decl *decl_flatten(Decl *decl) { if (decl->decl_kind == DECL_DEFINE && decl->define_decl.define_kind != DEFINE_TYPE_GENERIC) @@ -1815,7 +1899,8 @@ bool sema_add_member(Context *context, Decl *decl); bool sema_add_local(Context *context, Decl *decl); bool sema_unwrap_var(Context *context, Decl *decl); bool sema_rewrap_var(Context *context, Decl *decl); - +bool sema_erase_var(Context *context, Decl *decl); +bool sema_erase_unwrapped(Context *context, Decl *decl); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable); ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size); bool sema_analyse_expr(Context *context, Type *to, Expr *expr); @@ -1882,6 +1967,8 @@ int target_alloca_addr_space(); bool token_is_type(TokenType type); bool token_is_any_type(TokenType type); bool token_is_symbol(TokenType type); +bool token_is_ident_keyword(TokenType token_type); +bool token_is_ct_ident_keyword(TokenType token_type); const char *token_type_to_string(TokenType type); static inline TokenType advance_token(TokenId *token) { @@ -2116,6 +2203,21 @@ static inline void advance_and_verify(Context *context, TokenType token_type) advance(context); } +static inline Type *type_flatten_for_bitstruct(Type *type) +{ + type = type->canonical; + RETRY: + while (type->type_kind == TYPE_DISTINCT) + { + type = type->decl->distinct_decl.base_type; + } + if (type->type_kind == TYPE_ENUM) + { + type = type->decl->enums.type_info->type->canonical; + goto RETRY; + } + return type; +} static inline Type *type_flatten_distinct(Type *type) { @@ -2142,6 +2244,10 @@ static inline Type *type_flatten(Type *type) type = type->decl->enums.type_info->type; continue; } + if (type->type_kind == TYPE_ANYERR || type->type_kind == TYPE_ERRTYPE) + { + type = type_iptr->canonical; + } return type; } } @@ -2181,7 +2287,6 @@ static inline bool type_is_structlike(Type *type) { case TYPE_UNION: case TYPE_STRUCT: - case TYPE_ERRTYPE: return true; default: return false; @@ -2193,22 +2298,30 @@ static inline Type *type_lowering(Type *type) { Type *canonical = type_flatten(type); if (canonical->type_kind == TYPE_ENUM) return canonical->decl->enums.type_info->type->canonical; - if (canonical->type_kind == TYPE_TYPEID) return type_usize->canonical; + if (canonical->type_kind == TYPE_TYPEID) return type_iptr->canonical; + if (canonical->type_kind == TYPE_ERRTYPE) return type_iptr->canonical; + if (canonical->type_kind == TYPE_BITSTRUCT) return type_lowering(canonical->decl->bitstruct.base_type->type); return canonical; } static inline Decl *decl_raw(Decl *decl) { - if (decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS) return decl; + if (decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_UNWRAPPED) return decl; decl = decl->var.alias; - assert(decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_ALIAS); + assert(decl->decl_kind != DECL_VAR || decl->var.kind != VARDECL_UNWRAPPED); return decl; } static inline bool decl_is_struct_type(Decl *decl) { DeclKind kind = decl->decl_kind; - return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR); + return (kind == DECL_UNION) | (kind == DECL_STRUCT); +} + +static inline bool decl_is_enum_kind(Decl *decl) +{ + DeclKind kind = decl->decl_kind; + return (kind == DECL_ENUM) | (kind == DECL_ERRTYPE); } static inline bool decl_is_callable_type(Decl *decl) @@ -2220,7 +2333,7 @@ static inline bool decl_is_callable_type(Decl *decl) static inline bool decl_is_user_defined_type(Decl *decl) { DeclKind kind = decl->decl_kind; - return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ERR) + return (kind == DECL_UNION) | (kind == DECL_STRUCT) | (kind == DECL_ENUM) | (kind == DECL_DISTINCT); } diff --git a/src/compiler/context.c b/src/compiler/context.c index 3c0cfe54b..b24df2d53 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -187,7 +187,8 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_STRUCT: case DECL_UNION: case DECL_TYPEDEF: - case DECL_ERR: + case DECL_ERRTYPE: + case DECL_BITSTRUCT: vec_add(context->types, decl); decl_set_external_name(decl); break; @@ -202,6 +203,7 @@ void context_register_global_decl(Context *context, Decl *decl) case DECL_ARRAY_VALUE: vec_add(context->incr_array, decl); return; + case DECL_ERRVALUE: case DECL_ENUM_CONSTANT: case DECL_IMPORT: case DECL_CT_ELSE: diff --git a/src/compiler/copying.c b/src/compiler/copying.c index f262d87fe..c03ca0a0e 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -77,12 +77,26 @@ Expr *copy_expr(Expr *source_expr) case EXPR_UNDEF: case EXPR_NOP: return expr; + case EXPR_TRY_DECL: + MACRO_COPY_DECL(expr->try_decl_expr.decl); + return expr; case EXPR_DECL: MACRO_COPY_DECL(expr->decl_expr); return expr; case EXPR_CT_CALL: MACRO_COPY_EXPR_LIST(expr->ct_call_expr.arguments); return expr; + case EXPR_TRY_UNWRAP: + MACRO_COPY_EXPR(expr->try_unwrap_expr.init); + MACRO_COPY_TYPE(expr->try_unwrap_expr.type); + return expr; + case EXPR_TRY_UNWRAP_CHAIN: + MACRO_COPY_EXPR_LIST(expr->try_unwrap_chain_expr); + return expr; + case EXPR_CATCH_UNWRAP: + MACRO_COPY_EXPR_LIST(expr->catch_unwrap_expr.exprs); + MACRO_COPY_TYPE(expr->catch_unwrap_expr.type); + return expr; case EXPR_PLACEHOLDER: case EXPR_CONST_IDENTIFIER: case EXPR_CT_IDENT: @@ -111,10 +125,6 @@ Expr *copy_expr(Expr *source_expr) case EXPR_LEN: MACRO_COPY_EXPR(expr->len_expr.inner); return expr; - case EXPR_CATCH_OLD: - case EXPR_TRY_OLD: - MACRO_COPY_EXPR(expr->trycatch_expr); - return expr; case EXPR_TRY_ASSIGN: MACRO_COPY_EXPR(expr->try_assign_expr.expr); MACRO_COPY_EXPR(expr->try_assign_expr.init); @@ -122,8 +132,8 @@ Expr *copy_expr(Expr *source_expr) case EXPR_TRY: MACRO_COPY_EXPR(expr->try_expr.expr); return expr; - case EXPR_DECL_LIST: - MACRO_COPY_EXPR_LIST(expr->dexpr_list_expr); + case EXPR_COND: + MACRO_COPY_EXPR_LIST(expr->cond_expr); return expr; case EXPR_FAILABLE: MACRO_COPY_EXPR(expr->failable_expr); @@ -496,12 +506,12 @@ Decl *copy_decl(Decl *decl) break; case DECL_UNION: case DECL_STRUCT: - case DECL_ERR: copy_decl_type(copy); MACRO_COPY_DECL_LIST(copy->strukt.members); MACRO_COPY_DECL_LIST(copy->methods); break; case DECL_ENUM: + case DECL_ERRTYPE: copy_decl_type(copy); MACRO_COPY_DECL_LIST(copy->methods); MACRO_COPY_DECL_LIST(copy->enums.parameters); @@ -521,7 +531,7 @@ Decl *copy_decl(Decl *decl) break; case DECL_VAR: MACRO_COPY_TYPE(copy->var.type_info); - if (copy->var.kind == VARDECL_ALIAS) + if (copy->var.kind == VARDECL_UNWRAPPED) { MACRO_COPY_DECL(copy->var.alias); } @@ -530,6 +540,9 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_EXPR(copy->var.init_expr); } break; + case DECL_BITSTRUCT: + TODO + break; case DECL_LABEL: TODO break; @@ -537,6 +550,10 @@ Decl *copy_decl(Decl *decl) MACRO_COPY_EXPR(copy->enum_constant.expr); MACRO_COPY_EXPR_LIST(copy->enum_constant.args); break; + case DECL_ERRVALUE: + MACRO_COPY_EXPR(copy->enum_constant.expr); + MACRO_COPY_EXPR_LIST(copy->enum_constant.args); + break; case DECL_TYPEDEF: if (copy->typedef_decl.is_func) { diff --git a/src/compiler/enums.h b/src/compiler/enums.h index aa6849440..542ec00cd 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -84,9 +84,12 @@ typedef enum typedef enum { CAST_ERROR, + CAST_ERBOOL, CAST_EUBOOL, CAST_EUER, + CAST_EUINT, CAST_EREU, + CAST_ERINT, CAST_XIERR, CAST_PTRPTR, CAST_PTRXI, @@ -132,6 +135,7 @@ typedef enum DECL_POISONED = 0, DECL_ATTRIBUTE, DECL_ARRAY_VALUE, + DECL_BITSTRUCT, DECL_CT_CASE, DECL_CT_ELIF, DECL_CT_ELSE, @@ -142,7 +146,8 @@ typedef enum DECL_DISTINCT, DECL_ENUM, DECL_ENUM_CONSTANT, - DECL_ERR, + DECL_ERRTYPE, + DECL_ERRVALUE, DECL_FUNC, DECL_GENERIC, DECL_IMPORT, @@ -178,12 +183,12 @@ typedef enum EXPR_MACRO_BODY_EXPANSION, EXPR_CALL, EXPR_CAST, - EXPR_CATCH_OLD, + EXPR_CATCH_UNWRAP, EXPR_COMPOUND_LITERAL, EXPR_CONST, EXPR_CONST_IDENTIFIER, EXPR_CT_IDENT, - EXPR_DECL_LIST, + EXPR_COND, EXPR_DECL, EXPR_DESIGNATOR, EXPR_ELSE, @@ -208,9 +213,11 @@ typedef enum EXPR_SLICE_ASSIGN, EXPR_SUBSCRIPT, EXPR_TERNARY, - EXPR_TRY_OLD, EXPR_TRY, + EXPR_TRY_UNWRAP, + EXPR_TRY_UNWRAP_CHAIN, EXPR_TRY_ASSIGN, + EXPR_TRY_DECL, EXPR_TYPEID, EXPR_TYPEINFO, EXPR_TYPEOF, @@ -409,6 +416,7 @@ typedef enum TOKEN_ASSERT, TOKEN_ASM, TOKEN_ATTRIBUTE, + TOKEN_BITSTRUCT, TOKEN_BREAK, TOKEN_CASE, TOKEN_CATCH_OLD, @@ -520,8 +528,9 @@ typedef enum TYPE_FUNC, TYPE_STRUCT, TYPE_UNION, + TYPE_BITSTRUCT, TYPE_ERRTYPE, - TYPE_ERR_UNION, + TYPE_ANYERR, TYPE_TYPEDEF, TYPE_STRLIT, TYPE_DISTINCT, @@ -577,7 +586,9 @@ typedef enum VARDECL_PARAM_EXPR = 8, VARDECL_LOCAL_CT = 9, VARDECL_LOCAL_CT_TYPE = 10, - VARDECL_ALIAS = 11, + VARDECL_UNWRAPPED = 11, + VARDECL_ERASE = 12, + VARDECL_REWRAPPED = 13, } VarDeclKind; typedef enum @@ -602,6 +613,7 @@ typedef enum ATTR_MEMBER = 1 << 8, ATTR_INTERFACE = 1 << 9, ATTR_CALL = 1 << 10, + ATTR_BITSTRUCT = 1 << 11, } AttributeDomain; typedef enum @@ -623,6 +635,7 @@ typedef enum ATTRIBUTE_VECCALL, ATTRIBUTE_REGCALL, ATTRIBUTE_FASTCALL, + ATTRIBUTE_OVERLAP, ATTRIBUTE_NONE, NUMBER_OF_ATTRIBUTES = ATTRIBUTE_NONE, } AttributeType; diff --git a/src/compiler/headers.c b/src/compiler/headers.c index c11ac2219..45080a9d8 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -32,6 +32,8 @@ static void header_print_type(FILE *file, Type *type) { case TYPE_POISONED: UNREACHABLE + case TYPE_BITSTRUCT: + TODO case TYPE_VOID: OUTPUT("void"); return; @@ -90,6 +92,7 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("*"); return; case TYPE_ENUM: + case TYPE_ERRTYPE: OUTPUT("enum %s__", type->decl->external_name); return; case TYPE_FUNC: @@ -103,9 +106,7 @@ static void header_print_type(FILE *file, Type *type) case TYPE_DISTINCT: header_print_type(file, type->decl->distinct_decl.base_type); return; - case TYPE_ERRTYPE: - break; - case TYPE_ERR_UNION: + case TYPE_ANYERR: break; case TYPE_TYPEDEF: break; @@ -192,11 +193,13 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) { case NON_TYPE_DECLS: case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: case DECL_POISONED: case DECL_VAR: case DECL_INTERFACE: UNREACHABLE case DECL_FUNC: + case DECL_BITSTRUCT: TODO case DECL_TYPEDEF: case DECL_DISTINCT: @@ -210,7 +213,7 @@ static void header_gen_decl(FILE *file, int indent, Decl *decl) case DECL_ENUM: header_gen_enum(file, indent, decl); return; - case DECL_ERR: + case DECL_ERRTYPE: header_gen_err(file, indent, decl); return; } diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 8f4b72d35..459fb6898 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -633,15 +633,15 @@ static inline bool scan_char(Lexer *lexer) lexer->lexing_start = start; return add_error_token(lexer, "Expected a four character hex value after \\u."); } - if (platform_target.little_endian) + if (platform_target.big_endian) { - bytes.b[width++] = (uint8_t)(hex & 0xFF); bytes.b[width++] = (uint8_t)(hex >> 8); + bytes.b[width++] = (uint8_t)(hex & 0xFF); } else { - bytes.b[width++] = (uint8_t)(hex >> 8); bytes.b[width++] = (uint8_t)(hex & 0xFF); + bytes.b[width++] = (uint8_t)(hex >> 8); } break; } @@ -653,19 +653,19 @@ static inline bool scan_char(Lexer *lexer) lexer->lexing_start = start; return add_error_token(lexer, "Expected an eight character hex value after \\U."); } - if (platform_target.little_endian) + if (platform_target.big_endian) { - bytes.b[width++] = (uint8_t)(hex & 0xFF); - bytes.b[width++] = (uint8_t)((hex >> 8) & 0xFF); - bytes.b[width++] = (uint8_t)((hex >> 16) & 0xFF); bytes.b[width++] = (uint8_t)(hex >> 24); + bytes.b[width++] = (uint8_t)((hex >> 16) & 0xFF); + bytes.b[width++] = (uint8_t)((hex >> 8) & 0xFF); + bytes.b[width++] = (uint8_t)(hex & 0xFF); } else { - bytes.b[width++] = (uint8_t)(hex >> 24); - bytes.b[width++] = (uint8_t)((hex >> 16) & 0xFF); - bytes.b[width++] = (uint8_t)((hex >> 8) & 0xFF); bytes.b[width++] = (uint8_t)(hex & 0xFF); + bytes.b[width++] = (uint8_t)((hex >> 8) & 0xFF); + bytes.b[width++] = (uint8_t)((hex >> 16) & 0xFF); + bytes.b[width++] = (uint8_t)(hex >> 24); } break; } diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 6b1d125bb..ffc521a39 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -684,9 +684,9 @@ void llvm_codegen_setup() intrinsics_setup = true; } -void gencontext_emit_introspection_type(GenContext *context, Decl *decl) +void gencontext_emit_introspection_type(GenContext *c, Decl *decl) { - llvm_get_type(context, decl->type); + llvm_get_type(c, decl->type); if (decl_is_struct_type(decl)) { Decl **decls = decl->strukt.members; @@ -695,14 +695,31 @@ void gencontext_emit_introspection_type(GenContext *context, Decl *decl) Decl *member_decl = decls[i]; if (decl_is_struct_type(member_decl)) { - gencontext_emit_introspection_type(context, member_decl); + gencontext_emit_introspection_type(c, member_decl); } } } - LLVMValueRef global_name = LLVMAddGlobal(context->module, llvm_get_type(context, type_char), decl->name ? decl->name : "anon"); + if (decl_is_enum_kind(decl)) + { + unsigned elements = vec_size(decl->enums.values); + LLVMTypeRef element_type = llvm_get_type(c, type_voidptr); + LLVMTypeRef elements_type = LLVMArrayType(element_type, elements); + scratch_buffer_clear(); + scratch_buffer_append(decl->external_name); + scratch_buffer_append("$elements"); + LLVMValueRef enum_elements = LLVMAddGlobal(c->module, elements_type, scratch_buffer_to_string()); + LLVMSetGlobalConstant(enum_elements, 1); + LLVMSetInitializer(enum_elements, LLVMConstNull(elements_type)); + for (unsigned i = 0; i < elements; i++) + { + LLVMValueRef index[2] = { llvm_const_int(c, type_usize, i) }; + decl->enums.values[i]->backend_ref = LLVMConstInBoundsGEP(enum_elements, index, 1); + } + } + LLVMValueRef global_name = LLVMAddGlobal(c->module, llvm_get_type(c, type_char), decl->name ? decl->name : "anon"); LLVMSetGlobalConstant(global_name, 1); - LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(context, type_char), 1, false)); - decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(context, type_typeid)); + LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); + decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); switch (decl->visibility) { @@ -730,10 +747,12 @@ void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value) void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type) { + type = type_flatten(type); + assert(llvm_value || type == type_void); value->value = llvm_value; value->alignment = type_abi_alignment(type); value->kind = BE_VALUE; - value->type = type_flatten(type); + value->type = type; } bool llvm_value_is_const(BEValue *value) @@ -768,7 +787,6 @@ void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type) void llvm_value_fold_failable(GenContext *c, BEValue *value) { - if (value->kind == BE_ADDRESS_FAILABLE) { LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); @@ -860,7 +878,7 @@ void llvm_value_rvalue(GenContext *c, BEValue *value) } -static void gencontext_emit_type_decls(GenContext *context, Decl *decl) +static void llvm_emit_type_decls(GenContext *context, Decl *decl) { switch (decl->decl_kind) { @@ -878,17 +896,19 @@ static void gencontext_emit_type_decls(GenContext *context, Decl *decl) // TODO break; case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: // TODO break;; case DECL_STRUCT: case DECL_UNION: - case DECL_ERR: + case DECL_ENUM: + case DECL_ERRTYPE: + case DECL_BITSTRUCT: gencontext_emit_introspection_type(context, decl); break; case DECL_INTERFACE: // TODO break; - case DECL_ENUM: // TODO break; case NON_TYPE_DECLS: @@ -982,7 +1002,7 @@ void *llvm_gen(Module *module) } VECEACH(context->types, i) { - gencontext_emit_type_decls(gen_context, context->types[i]); + llvm_emit_type_decls(gen_context, context->types[i]); } VECEACH(context->functions, i) { @@ -1084,6 +1104,7 @@ void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue { case BE_BOOLEAN: value->value = LLVMBuildZExt(c->builder, value->value, c->byte_type, ""); + value->kind = BE_VALUE; FALLTHROUGH; case BE_VALUE: llvm_store_aligned(c, destination, value->value, alignment ?: type_abi_alignment(value->type)); diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index 5e0733295..4e80128f8 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -160,7 +160,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig } - if (is_fixed && platform_target.riscv.flen && (type->type_kind == TYPE_STRUCT || type->type_kind == TYPE_ERRTYPE)) + if (is_fixed && platform_target.riscv.flen && type->type_kind == TYPE_STRUCT) { AbiType *field1 = NULL; AbiType *field2 = NULL; diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 7899753ec..9eb10471b 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -137,14 +137,14 @@ ABIArgInfo *x64_classify_reg_call_struct_type_check(Type *type, Registers *neede assert(x64_type_is_structure(type)); // These are all passed in two registers. - if (type->type_kind == TYPE_ERR_UNION || type->type_kind == TYPE_SUBARRAY || type->type_kind == TYPE_VIRTUAL || type->type_kind == TYPE_VIRTUAL_ANY) + if (type->type_kind == TYPE_SUBARRAY || type->type_kind == TYPE_VIRTUAL || type->type_kind == TYPE_VIRTUAL_ANY) { needed_registers->int_registers += 2; return abi_arg_new_direct(); } // Struct, err type handled => - assert(type->type_kind == TYPE_STRUCT || type->type_kind == TYPE_ERRTYPE); + assert(type->type_kind == TYPE_STRUCT); // Variable array structs are always passed by pointer. if (type->decl->has_variable_array) return x64_indirect_return_result(type); @@ -404,13 +404,15 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: + case TYPE_ANYERR: + case TYPE_ERRTYPE: + case TYPE_BITSTRUCT: UNREACHABLE case TYPE_VOID: *current = CLASS_NO_CLASS; break; case TYPE_I128: case TYPE_U128: - case TYPE_ERR_UNION: case TYPE_SUBARRAY: case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: @@ -443,7 +445,6 @@ static void x64_classify(Type *type, ByteSize offset_base, X64Class *lo_class, X break; case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERRTYPE: x64_classify_struct_union(type, offset_base, current, lo_class, hi_class, named); break; case TYPE_ARRAY: @@ -477,7 +478,7 @@ bool x64_bits_contain_no_user_data(Type *type, unsigned start, unsigned end) // No overlap return true; } - if (type->type_kind == TYPE_STRUCT || type->type_kind == TYPE_ERRTYPE || type->type_kind == TYPE_UNION) + if (type->type_kind == TYPE_STRUCT || type->type_kind == TYPE_UNION) { Decl **members = type->decl->strukt.members; VECEACH(members, i) @@ -499,7 +500,7 @@ bool x64_contains_float_at_offset(Type *type, unsigned offset) if (offset == 0 && type->type_kind == TYPE_F32) return true; // If this is a struct, recurse into the field at the specified offset. - if (type->type_kind == TYPE_ERRTYPE || type->type_kind == TYPE_STRUCT) + if (type->type_kind == TYPE_STRUCT) { Decl *member = x64_get_member_at_offset(type->decl, offset); offset -= member->offset; @@ -562,7 +563,6 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty } break; case TYPE_STRUCT: - case TYPE_ERRTYPE: { Decl *member = x64_get_member_at_offset(type->decl, offset); if (member) @@ -571,9 +571,6 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty } break; } - case TYPE_ERR_UNION: - if (offset < 16) return abi_type_new_plain(type_ulong); - break; case TYPE_VIRTUAL_ANY: if (offset < 8) return abi_type_new_plain(type_ulong); if (offset < 16) return abi_type_new_plain(type_voidptr); @@ -605,6 +602,9 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: + case TYPE_ANYERR: + case TYPE_ERRTYPE: + case TYPE_BITSTRUCT: UNREACHABLE case TYPE_I128: case TYPE_U128: @@ -850,9 +850,7 @@ bool x64_type_is_structure(Type *type) { switch (type->type_kind) { - case TYPE_ERR_UNION: case TYPE_STRUCT: - case TYPE_ERRTYPE: case TYPE_SUBARRAY: case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index afdc4ebb3..904cba5ae 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -117,15 +117,16 @@ static bool x86_should_return_type_in_reg(Type *type) case TYPE_ENUM: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: + case TYPE_ERRTYPE: + case TYPE_TYPEID: + case TYPE_ANYERR: + case TYPE_BITSTRUCT: UNREACHABLE case ALL_INTS: case ALL_FLOATS: case TYPE_BOOL: case TYPE_POINTER: - case TYPE_TYPEID: - case TYPE_ERR_UNION: case TYPE_SUBARRAY: - case TYPE_ERRTYPE: case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: return true; @@ -292,10 +293,6 @@ static inline bool x86_can_expand_indirect_aggregate_arg(Type *type) // arguments. If so, we prefer to do the latter to avoid inhibiting // optimizations. - // Error unions can always be expanded since they are two pointers wide. - if (type->canonical->type_kind == TYPE_ERR_UNION) return true; - - if (type->canonical->type_kind == TYPE_ERRTYPE) return true; if (!type_is_union_struct(type)) return false; ByteSize size = 0; @@ -593,11 +590,14 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type) case TYPE_TYPEDEF: case TYPE_VOID: case TYPE_ENUM: + case TYPE_ANYERR: + case TYPE_ERRTYPE: case TYPE_DISTINCT: case TYPE_FUNC: case TYPE_TYPEID: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: + case TYPE_BITSTRUCT: UNREACHABLE case ALL_FLOATS: case ALL_INTS: @@ -606,14 +606,12 @@ static ABIArgInfo *x86_classify_argument(CallABI call, Regs *regs, Type *type) return x86_classify_primitives(call, regs, type); case TYPE_VECTOR: return x86_classify_vector(regs, type); - case TYPE_ERRTYPE: case TYPE_STRUCT: case TYPE_UNION: case TYPE_SUBARRAY: case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: case TYPE_ARRAY: - case TYPE_ERR_UNION: return x86_classify_aggregate(call, regs, type); case TYPE_TYPEINFO: UNREACHABLE diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index e766edece..4cf808531 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -520,9 +520,12 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * return type->backend_debug_type = llvm_debug_pointer_type(c, type); case TYPE_ENUM: return type->backend_debug_type = llvm_debug_enum_type(c, type, scope); + case TYPE_ERRTYPE: + return type->backend_debug_type = llvm_debug_enum_type(c, type, scope); case TYPE_FUNC: return type->backend_debug_type = llvm_debug_func_type(c, type); - case TYPE_ERRTYPE: + case TYPE_BITSTRUCT: + TODO case TYPE_STRUCT: case TYPE_UNION: return type->backend_debug_type = llvm_debug_structlike_type(c, type, scope); @@ -533,7 +536,8 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * return type->backend_debug_type = llvm_debug_array_type(c, type); case TYPE_SUBARRAY: return type->backend_debug_type = llvm_debug_subarray_type(c, type); - case TYPE_ERR_UNION: + case TYPE_ANYERR: + // TODO return type->backend_debug_type = llvm_debug_errunion_type(c, type); case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 3ed5006bd..0df9ecef1 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -15,10 +15,8 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_ LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value) { - assert(value->kind == BE_ADDRESS); - LLVMValueRef val = LLVMBuildStructGEP2(c->builder, llvm_get_type(c, value->type), value->value, 0, "err_domain"); - LLVMValueRef domain = llvm_emit_load_aligned(c, llvm_get_type(c, type_uptr), val, value->alignment, ""); - return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_uptr), "not_err"); + llvm_value_rvalue(c, value); + return LLVMBuildICmp(c->builder, LLVMIntEQ, value->value, llvm_get_zero(c, type_anyerr), "not_err"); } LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size) @@ -114,12 +112,7 @@ LLVMValueRef llvm_coerce_int_ptr(GenContext *c, LLVMValueRef value, LLVMTypeRef // 4. Are int types not matching? if (to_int_type != from) { - if (platform_target.little_endian) - { - // Little-endian targets preserve the low bits. No shifts required. - value = LLVMBuildIntCast2(c->builder, value, to_int_type, false, ""); - } - else + if (platform_target.big_endian) { // Big endian, preserve the high bits. ByteSize to_size = llvm_abi_size(c, to_int_type); @@ -135,6 +128,11 @@ LLVMValueRef llvm_coerce_int_ptr(GenContext *c, LLVMValueRef value, LLVMTypeRef value = LLVMBuildShl(c->builder, value, LLVMConstInt(from, (to_size - from_size) * 8, false), ""); } } + else + { + // Little-endian targets preserve the low bits. No shifts required. + value = LLVMBuildIntCast2(c->builder, value, to_int_type, false, ""); + } } if (to_is_pointer) { @@ -435,7 +433,6 @@ static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *par llvm_value_addr(c, value); llvm_value_set_address_align(value, llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), found->type, value->alignment); break; - case TYPE_ERRTYPE: case TYPE_STRUCT: llvm_value_struct_gep(c, value, value, index); break; @@ -503,6 +500,9 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_ switch (cast_kind) { + case CAST_ERBOOL: + case CAST_EUINT: + case CAST_ERINT: case CAST_VRBOOL: case CAST_VRPTR: case CAST_PTRVR: @@ -2033,7 +2033,8 @@ static void gencontext_emit_typeid(GenContext *context, BEValue *be_value, Expr void gencontext_emit_trycatch_expr(GenContext *c, BEValue *value, Expr *expr) { - + TODO +/* LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error_block"); LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block"); LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_trycatch_block"); @@ -2071,6 +2072,7 @@ void gencontext_emit_trycatch_expr(GenContext *c, BEValue *value, Expr *expr) LLVMAddIncoming(phi, logic_values, blocks, 2); llvm_value_set(value, phi, expr->type); + */ } void llvm_emit_try_assign_try_catch(GenContext *c, bool is_try, BEValue *be_value, BEValue *var_addr, BEValue *catch_addr, Expr *rhs) @@ -2149,7 +2151,7 @@ void llvm_emit_try_assign_expr(GenContext *c, BEValue *be_value, Expr *expr) } } -void gencontext_emit_try_expr(GenContext *c, BEValue *value, Expr *expr) +void llvm_emit_try_expr(GenContext *c, BEValue *value, Expr *expr) { LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error_block"); @@ -2567,6 +2569,10 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case TYPE_DISTINCT: case TYPE_STRLIT: case TYPE_INFERRED_ARRAY: + case TYPE_ENUM: + case TYPE_ERRTYPE: + case TYPE_ANYERR: + case TYPE_BITSTRUCT: UNREACHABLE break; case TYPE_BOOL: @@ -2574,15 +2580,11 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM case ALL_UNSIGNED_INTS: case ALL_REAL_FLOATS: case TYPE_POINTER: - case TYPE_ENUM: - case TYPE_ERRTYPE: vec_add(*values, LLVMBuildLoad2(context->builder, llvm_get_type(context, param_type), expand_ptr, "loadexpanded")); return; case TYPE_TYPEDEF: param_type = param_type->canonical; goto REDO; - case TYPE_ERR_UNION: - TODO case TYPE_STRUCT: gencontext_expand_struct_to_args(context, param_type, expand_ptr, values); break; @@ -3191,7 +3193,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 17c. If we have an error var, or we aren't interested in the error variable // - then it's straightforward. We just jump to the catch block. - LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after.errcheck"); if (error_var || !c->error_var) { llvm_emit_cond_br(c, &no_err, after_block, c->catch_block); @@ -3311,7 +3313,9 @@ static inline void llvm_emit_macro_block(GenContext *context, BEValue *be_value, case VARDECL_MEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: UNREACHABLE case VARDECL_PARAM_REF: { @@ -3444,15 +3448,137 @@ static void llvm_emit_macro_body_expansion(GenContext *c, BEValue *value, Expr * llvm_emit_stmt(c, body_expr->body_expansion_expr.ast); } +void llvm_emit_try_unwrap(GenContext *c, BEValue *value, Expr *expr) +{ + if (!expr->try_unwrap_expr.failable) + { + LLVMValueRef fail_ref = decl_failable_ref(expr->try_unwrap_expr.decl); + LLVMValueRef errv = llvm_emit_load_aligned(c, llvm_get_type(c, type_anyerr), fail_ref, 0, "load.err"); + LLVMValueRef result = LLVMBuildICmp(c->builder, LLVMIntEQ, errv, llvm_get_zero(c, type_anyerr), "result"); + llvm_value_set_bool(value, result); + return; + } + BEValue addr; + if (expr->try_unwrap_expr.assign_existing) + { + llvm_emit_expr(c, &addr, expr->try_unwrap_expr.lhs); + } + else + { + llvm_emit_local_decl(c, expr->try_unwrap_expr.decl); + llvm_value_set_decl_address(&addr, expr->try_unwrap_expr.decl); + } + assert(llvm_value_is_addr(&addr)); + llvm_emit_try_assign_try_catch(c, true, value, &addr, NULL, expr->try_unwrap_expr.failable); +} + +void llvm_emit_catch_unwrap(GenContext *c, BEValue *value, Expr *expr) +{ + BEValue addr; + if (expr->catch_unwrap_expr.lhs) + { + llvm_emit_expr(c, &addr, expr->catch_unwrap_expr.lhs); + } + else if (expr->catch_unwrap_expr.decl) + { + llvm_emit_local_decl(c, expr->catch_unwrap_expr.decl); + llvm_value_set_decl_address(&addr, expr->catch_unwrap_expr.decl); + } + else + { + LLVMValueRef temp_err = llvm_emit_alloca_aligned(c, type_anyerr, "temp_err"); + llvm_value_set_address(&addr, temp_err, type_anyerr); + } + + PUSH_ERROR(); + + LLVMBasicBlockRef catch_block = llvm_basic_block_new(c, "end_block"); + + c->error_var = addr.value; + c->catch_block = catch_block; + + VECEACH(expr->catch_unwrap_expr.exprs, i) + { + BEValue val; + LLVMBasicBlockRef block = llvm_basic_block_new(c, "testblock"); + llvm_emit_br(c, block); + llvm_emit_block(c, block); + llvm_emit_expr(c, &val, expr->catch_unwrap_expr.exprs[i]); + llvm_value_fold_failable(c, &val); + } + + POP_ERROR(); + + llvm_store_bevalue_raw(c, &addr, llvm_get_zero(c, type_anyerr)); + llvm_emit_br(c, catch_block); + llvm_emit_block(c, catch_block); + llvm_value_rvalue(c, &addr); + llvm_value_set(value, addr.value, type_anyerr); +} + +void llvm_emit_try_unwrap_chain(GenContext *c, BEValue *value, Expr *expr) +{ + Expr **exprs = expr->try_unwrap_chain_expr; + unsigned elements = vec_size(exprs); + assert(elements > 0); + + LLVMBasicBlockRef next_block = NULL; + LLVMBasicBlockRef end_block = llvm_basic_block_new(c, "end_chain"); + LLVMBasicBlockRef fail_block = llvm_basic_block_new(c, "fail_chain"); + + if (elements == 1) + { + llvm_emit_expr(c, value, exprs[0]); + assert(llvm_value_is_bool(value)); + return; + } + else + { + for (unsigned i = 0; i < elements; i++) + { + if (next_block) + { + llvm_emit_br(c, next_block); + llvm_emit_block(c, next_block); + } + next_block = llvm_basic_block_new(c, "chain_next"); + Expr *link = exprs[i]; + BEValue res; + llvm_emit_expr(c, &res, link); + assert(llvm_value_is_bool(&res)); + llvm_emit_cond_br(c, &res, next_block, fail_block); + } + llvm_emit_block(c, next_block); + llvm_emit_br(c, end_block); + llvm_emit_block(c, fail_block); + llvm_emit_br(c, end_block); + } + + // Finally set up our phi + llvm_emit_block(c, end_block); + LLVMValueRef chain_result = LLVMBuildPhi(c->builder, c->bool_type, "chain.phi"); + LLVMValueRef logic_values[2] = { LLVMConstInt(c->bool_type, 1, 0), LLVMConstNull(c->bool_type) }; + LLVMBasicBlockRef blocks[2] = { next_block, fail_block }; + LLVMAddIncoming(chain_result, logic_values, blocks, 2); + + llvm_value_set_bool(value, chain_result); + +} + void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) { EMIT_LOC(c, expr); switch (expr->expr_kind) { - case EXPR_DESIGNATOR: case EXPR_MEMBER_ACCESS: + assert(expr->access_expr.ref->decl_kind == DECL_ERRVALUE); + llvm_value_set(value, + LLVMBuildPtrToInt(c->builder, expr->access_expr.ref->backend_ref, llvm_get_type(c, type_anyerr), ""), + type_anyerr); + return; + case EXPR_DESIGNATOR: case EXPR_POISONED: - case EXPR_DECL_LIST: + case EXPR_COND: case EXPR_TYPEINFO: case EXPR_ENUM_CONSTANT: case EXPR_MACRO_EXPANSION: @@ -3461,7 +3587,17 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) case EXPR_PLACEHOLDER: case EXPR_CT_CALL: case EXPR_FLATPATH: + case EXPR_TRY_DECL: UNREACHABLE + case EXPR_TRY_UNWRAP_CHAIN: + llvm_emit_try_unwrap_chain(c, value, expr); + return; + case EXPR_TRY_UNWRAP: + llvm_emit_try_unwrap(c, value, expr); + return; + case EXPR_CATCH_UNWRAP: + llvm_emit_catch_unwrap(c, value, expr); + return; case EXPR_UNDEF: // Should never reach this. UNREACHABLE @@ -3481,15 +3617,11 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) gencontext_emit_failable(c, value, expr); return; case EXPR_TRY: - gencontext_emit_try_expr(c, value, expr); + llvm_emit_try_expr(c, value, expr); return; case EXPR_TRY_ASSIGN: llvm_emit_try_assign_expr(c, value, expr); return; - case EXPR_TRY_OLD: - case EXPR_CATCH_OLD: - gencontext_emit_trycatch_expr(c, value, expr); - return; case EXPR_NOP: return; case EXPR_ELSE: diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index b6fc6e7af..9fa19f262 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -633,14 +633,15 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) decl->backend_ref = LLVMAddGlobal(context->module, llvm_get_type(context, decl->type), decl->extname ?: decl->external_name); LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility); break; + case DECL_BITSTRUCT: case DECL_STRUCT: case DECL_UNION: - case DECL_ERR: llvm_emit_methods(context, decl->methods); llvm_get_type(context, decl->type); // TODO // Fix typeid break; case DECL_ENUM: + case DECL_ERRTYPE: llvm_emit_methods(context, decl->methods); // TODO // Fix typeid return; @@ -649,6 +650,7 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case NON_TYPE_DECLS: case DECL_INTERFACE: case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: return; } } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 748feb4c3..820ea4ec9 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -298,14 +298,14 @@ static inline LLVMValueRef gencontext_emit_load(GenContext *c, Type *type, LLVMV static inline LLVMValueRef decl_failable_ref(Decl *decl) { assert(decl->decl_kind == DECL_VAR); - if (decl->var.kind == VARDECL_ALIAS) return decl_failable_ref(decl->var.alias); + if (decl->var.kind == VARDECL_UNWRAPPED) return decl_failable_ref(decl->var.alias); if (!decl->var.failable) return NULL; return decl->var.failable_ref; } static inline LLVMValueRef decl_ref(Decl *decl) { - if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS) return decl_ref(decl->var.alias); + if (decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED) return decl_ref(decl->var.alias); return decl->backend_ref; } diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 5347b72ac..044e0fd66 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -110,25 +110,25 @@ LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl) void llvm_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr) { - assert(expr->expr_kind == EXPR_DECL_LIST); - VECEACH(expr->dexpr_list_expr, i) + assert(expr->expr_kind == EXPR_COND); + VECEACH(expr->cond_expr, i) { BEValue value; - llvm_emit_expr(context, &value, expr->dexpr_list_expr[i]); + llvm_emit_expr(context, &value, expr->cond_expr[i]); } } void gencontext_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr *expr, bool bool_cast) { - assert(expr->expr_kind == EXPR_DECL_LIST); - ByteSize size = vec_size(expr->dexpr_list_expr); + assert(expr->expr_kind == EXPR_COND); + ByteSize size = vec_size(expr->cond_expr); ByteSize last_index = size - 1; for (ByteSize i = 0; i < last_index; i++) { BEValue value; - llvm_emit_expr(context, &value, expr->dexpr_list_expr[i]); + llvm_emit_expr(context, &value, expr->cond_expr[i]); } - Expr *last = expr->dexpr_list_expr[last_index]; + Expr *last = expr->cond_expr[last_index]; Type *type = last->type; llvm_emit_expr(context, be_value, last); if (last->expr_kind == EXPR_DECL) diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index f78d9f0a3..e56fb7d18 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -12,10 +12,13 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) { case DECL_VAR: case DECL_ENUM_CONSTANT: + case DECL_ERRVALUE: case DECL_POISONED: case DECL_INTERFACE: case NON_TYPE_DECLS: UNREACHABLE + case DECL_BITSTRUCT: + return llvm_get_type(c, decl->bitstruct.base_type->type); case DECL_FUNC: { VECEACH(decl->func_decl.function_signature.params, i) @@ -85,31 +88,8 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) } case DECL_ENUM: return llvm_get_type(c, decl->type); - case DECL_ERR: - { - LLVMTypeRef err_type = LLVMStructCreateNamed(c->context, decl->name); - // Avoid recursive issues. - decl->type->backend_type = err_type; - LLVMTypeRef *types = NULL; - unsigned size = 0; - VECEACH(decl->strukt.members, i) - { - Type *type = decl->strukt.members[i]->type->canonical; - unsigned alignment = type_abi_alignment(type); - if (size % alignment != 0) - { - size += alignment - size % alignment; - } - size += type_size(type); - vec_add(types, llvm_get_type(c, type)); - } - if (decl->strukt.padding) - { - vec_add(types, llvm_const_padding_type(c, decl->strukt.padding)); - } - LLVMStructSetBody(err_type, types, vec_size(types), false); - return err_type; - } + case DECL_ERRTYPE: + return llvm_get_type(c, type_iptr); } UNREACHABLE } @@ -159,15 +139,9 @@ static void param_expand(GenContext *context, LLVMTypeRef** params_ref, Type *ty return; } case TYPE_ENUM: - param_expand(context, params_ref, type_lowering(type)); - return; - case TYPE_ERR_UNION: - param_expand(context, params_ref, type_usize->canonical); - param_expand(context, params_ref, type_usize->canonical); - return; + case TYPE_ANYERR: case TYPE_ERRTYPE: - // TODO - param_expand(context, params_ref, type_usize->canonical); + param_expand(context, params_ref, type_lowering(type)); return; case TYPE_UNION: { @@ -338,23 +312,18 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) case TYPE_INFERRED_ARRAY: UNREACHABLE case TYPE_TYPEID: - return any_type->backend_type = LLVMIntTypeInContext(c->context, any_type->builtin.bitsize); + case TYPE_ANYERR: + case TYPE_ERRTYPE: + return any_type->backend_type = llvm_get_type(c, type_iptr); case TYPE_TYPEDEF: return any_type->backend_type = llvm_get_type(c, any_type->canonical); case TYPE_DISTINCT: return any_type->backend_type = llvm_get_type(c, any_type->decl->distinct_decl.base_type); case TYPE_ENUM: return any_type->backend_type = llvm_get_type(c, any_type->decl->enums.type_info->type->canonical); - case TYPE_ERR_UNION: - { - LLVMTypeRef elements[2] = { llvm_get_type(c, type_usize->canonical), llvm_get_type(c, type_usize->canonical) }; - LLVMTypeRef strukt = LLVMStructCreateNamed(c->context, "error_union"); - LLVMStructSetBody(strukt, elements, 2, false); - return any_type->backend_type = strukt; - } case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERRTYPE: + case TYPE_BITSTRUCT: return any_type->backend_type = llvm_type_from_decl(c, any_type->decl); case TYPE_FUNC: return any_type->backend_type = llvm_func_type(c, any_type); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 2085d6b46..212841377 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -59,6 +59,162 @@ static inline Expr *parse_expr_or_initializer_list(Context *context) return parse_expr(context); } +static inline bool next_is_try_unwrap(Context *context) +{ + return tok_is(context, TOKEN_TRY) && context->next_tok.type != TOKEN_LPAREN; +} + +static inline bool next_is_catch_unwrap(Context *context) +{ + return tok_is(context, TOKEN_CATCH) && context->next_tok.type != TOKEN_LPAREN; +} + +static inline Expr *parse_for_try_expr(Context *context) +{ + return parse_precedence(context, PREC_AND + 1); +} + +/** + * catch_unwrap ::= CATCH IDENT | (type? IDENT '=' (expr | '(' expr (',' expr) ')')) + */ +static inline Expr *parse_catch_unwrap(Context *context) +{ + advance_and_verify(context, TOKEN_CATCH); + Expr *expr = expr_new(EXPR_CATCH_UNWRAP, source_span_from_token_id(context->prev_tok)); + expr->catch_unwrap_expr.type = parse_next_is_decl(context) ? TRY_TYPE_OR(parse_type(context), poisoned_expr) : NULL; + expr->catch_unwrap_expr.variable = TRY_EXPR_OR(parse_for_try_expr(context), poisoned_expr); + if (!try_consume(context, TOKEN_EQ)) + { + if (expr->catch_unwrap_expr.type) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a '=' here."); + return poisoned_expr; + } + vec_add(expr->catch_unwrap_expr.exprs, expr->catch_unwrap_expr.variable); + expr->catch_unwrap_expr.variable = NULL; + RANGE_EXTEND_PREV(expr); + return expr; + } + if (try_consume(context, TOKEN_LPAREN)) + { + do + { + Expr *init_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + vec_add(expr->catch_unwrap_expr.exprs, init_expr); + } while (try_consume(context, TOKEN_COMMA)); + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); + } + else + { + Expr *init_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + vec_add(expr->catch_unwrap_expr.exprs, init_expr); + } + RANGE_EXTEND_PREV(expr); + return expr; +} + +/** + * try_unwrap ::= TRY (IDENT | type? IDENT '=' non_and_expr) + */ +static inline Expr *parse_try_unwrap(Context *context) +{ + Expr *expr = EXPR_NEW_TOKEN(EXPR_TRY_UNWRAP, context->tok); + advance_and_verify(context, TOKEN_TRY); + if (parse_next_is_decl(context)) + { + expr->try_unwrap_expr.type = TRY_TYPE_OR(parse_type(context), poisoned_expr); + } + expr->try_unwrap_expr.variable = TRY_EXPR_OR(parse_for_try_expr(context), poisoned_expr); + if (expr->try_unwrap_expr.type && expr->try_unwrap_expr.variable->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(expr->try_unwrap_expr.variable, "Expected a variable name after the type."); + return poisoned_expr; + } + if (try_consume(context, TOKEN_EQ)) + { + expr->try_unwrap_expr.init = TRY_EXPR_OR(parse_for_try_expr(context), poisoned_expr); + } + RANGE_EXTEND_PREV(expr); + return expr; +} + +/** + * try_unwrap_chain ::= try_unwrap ('&&' (try_unwrap | non_and_expr))* + * try_unwrap ::= TRY (IDENT | type? IDENT '=' non_and_expr) + */ +static inline Expr *parse_try_unwrap_chain(Context *context) +{ + Expr **unwraps = NULL; + Expr *first_unwrap = TRY_EXPR_OR(parse_try_unwrap(context), poisoned_expr); + vec_add(unwraps, first_unwrap); + while (try_consume(context, TOKEN_AND)) + { + if (next_is_try_unwrap(context)) + { + Expr *expr = TRY_EXPR_OR(parse_try_unwrap(context), poisoned_expr); + vec_add(unwraps, expr); + continue; + } + Expr *next_unwrap = TRY_EXPR_OR(parse_for_try_expr(context), poisoned_expr); + vec_add(unwraps, next_unwrap); + } + Expr *try_unwrap_chain = EXPR_NEW_EXPR(EXPR_TRY_UNWRAP_CHAIN, first_unwrap); + try_unwrap_chain->try_unwrap_chain_expr = unwraps; + RANGE_EXTEND_PREV(try_unwrap_chain); + return try_unwrap_chain; +} + +/** + * cond_list ::= ((expr | decl-expr) COMMA)* (expr | decl-expr | try_unwrap_chain | catch_unwrap ) + * + * @return bool + */ +Expr *parse_cond(Context *context) +{ + Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_COND, context->tok); + decl_expr->cond_expr = NULL; + while (1) + { + if (next_is_try_unwrap(context)) + { + Expr *try_unwrap = TRY_EXPR_OR(parse_try_unwrap_chain(context), poisoned_expr); + vec_add(decl_expr->cond_expr, try_unwrap); + if (tok_is(context, TOKEN_COMMA)) + { + SEMA_ERROR(try_unwrap, "The 'try' must be placed last, can you change it?"); + return poisoned_expr; + } + break; + } + if (next_is_catch_unwrap(context)) + { + Expr *catch_unwrap = TRY_EXPR_OR(parse_catch_unwrap(context), poisoned_expr); + vec_add(decl_expr->cond_expr, catch_unwrap); + if (tok_is(context, TOKEN_COMMA)) + { + SEMA_ERROR(catch_unwrap, "The 'catch' must be placed last, can you change it?"); + return poisoned_expr; + } + break; + } + if (parse_next_is_decl(context)) + { + Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_expr); + Expr *expr = expr_new(EXPR_DECL, decl->span); + expr->decl_expr = decl; + vec_add(decl_expr->cond_expr, expr); + } + else + { + Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + vec_add(decl_expr->cond_expr, expr); + } + if (!try_consume(context, TOKEN_COMMA)) break; + } + RANGE_EXTEND_PREV(decl_expr); + return decl_expr; +} + inline Expr* parse_expr(Context *context) { return parse_precedence(context, PREC_ASSIGNMENT); @@ -654,37 +810,30 @@ static Expr *parse_identifier_starting_expression(Context *context, Expr *left) } } -static Expr *parse_try_old_expr(Context *context, Expr *left) -{ - assert(!left && "Unexpected left hand side"); - Expr *try_expr = EXPR_NEW_TOKEN(TOKEN_IS(TOKEN_TRY_OLD) ? EXPR_TRY_OLD : EXPR_CATCH_OLD, context->tok); - advance(context); - CONSUME_OR(TOKEN_LPAREN, poisoned_expr); - try_expr->trycatch_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); - CONSUME_OR(TOKEN_RPAREN, poisoned_expr); - return try_expr; -} + static Expr *parse_try_expr(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - bool try = TOKEN_IS(TOKEN_TRY); - Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok); + bool is_try = TOKEN_IS(TOKEN_TRY); advance(context); - Expr *expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY), poisoned_expr); - if (try_consume(context, TOKEN_EQ)) + Expr *try_expr = expr_new(EXPR_TRY, source_span_from_token_id(context->prev_tok)); + if (!try_consume(context, TOKEN_LPAREN)) { - Expr *init = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr); - try_expr->expr_kind = EXPR_TRY_ASSIGN; - try_expr->try_assign_expr.expr = expr; - try_expr->try_assign_expr.is_try = try; - try_expr->try_assign_expr.init = init; - } - else - { - try_expr->try_expr.expr = expr; - try_expr->try_expr.is_try = try; + if (is_try) + { + SEMA_ERROR(try_expr, "An unwrapping 'try' can only occur as the last element of a conditional, did you want 'try(expr)'?"); + return poisoned_expr; + } + else + { + SEMA_ERROR(try_expr, "An unwrapping 'catch' can only occur as the last element of a conditional, did you want 'catch(expr)'?"); + return poisoned_expr; + } } + try_expr->try_expr.expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); + try_expr->try_expr.is_try = is_try; + CONSUME_OR(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(try_expr); return try_expr; } @@ -1108,8 +1257,6 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_LBRAPIPE] = { parse_expr_block, NULL, PREC_NONE }, [TOKEN_TRY] = { parse_try_expr, NULL, PREC_NONE }, [TOKEN_CATCH] = { parse_try_expr, NULL, PREC_NONE }, - [TOKEN_TRY_OLD] = { parse_try_old_expr, NULL, PREC_NONE }, - [TOKEN_CATCH_OLD] = { parse_try_old_expr, NULL, PREC_NONE }, [TOKEN_BANGBANG] = { NULL, parse_bangbang_expr, PREC_CALL }, [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 8f9f0607c..a1b6c1d74 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -82,6 +82,7 @@ void recover_top_level(Context *context) case TOKEN_ATTRIBUTE: case TOKEN_DEFINE: case TOKEN_ERRTYPE: + case TOKEN_BITSTRUCT: return; case TOKEN_IDENT: // Incr arrays only case TOKEN_CONST: @@ -425,12 +426,17 @@ static inline bool parse_specified_import(Context *context, Path *path) } -static inline bool consume_ident(Context *context, const char* name) +bool consume_ident(Context *context, const char* name) { if (try_consume(context, TOKEN_IDENT)) return true; if (TOKEN_IS(TOKEN_TYPE_IDENT) || TOKEN_IS(TOKEN_CONST_IDENT)) { - SEMA_TOKEN_ERROR(context->tok, "A %s cannot start with a capital letter.", name); + SEMA_TOKEN_ERROR(context->tok, "A %s must start with a lower case letter.", name); + return false; + } + if (token_is_keyword(context->tok.type)) + { + SEMA_TOKEN_ERROR(context->tok, "This is a reserved keyword, did you accidentally use it?"); return false; } SEMA_TOKEN_ERROR(context->tok, "A %s was expected.", name); @@ -439,7 +445,7 @@ static inline bool consume_ident(Context *context, const char* name) static bool consume_type_name(Context *context, const char* type) { - if (TOKEN_IS(TOKEN_IDENT)) + if (TOKEN_IS(TOKEN_IDENT) || token_is_keyword(context->tok.type)) { SEMA_TOKEN_ERROR(context->tok, "Names of %ss must start with an upper case letter.", type); return false; @@ -856,39 +862,6 @@ static inline Decl *parse_incremental_array(Context *context) -/** - * decl_expr_list - * : expression - * | declaration - * | decl_expr_list ',' expression - * | decl_expr_list ',' declaration - * ; - * - * @return bool - */ -Expr *parse_decl_expr_list(Context *context) -{ - Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_DECL_LIST, context->tok); - decl_expr->dexpr_list_expr = NULL; - while (1) - { - if (parse_next_is_decl(context)) - { - Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_expr); - Expr *expr = expr_new(EXPR_DECL, decl->span); - expr->decl_expr = decl; - vec_add(decl_expr->dexpr_list_expr, expr); - } - else - { - Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr); - vec_add(decl_expr->dexpr_list_expr, expr); - } - if (!try_consume(context, TOKEN_COMMA)) break; - } - RANGE_EXTEND_PREV(decl_expr); - return decl_expr; -} bool parse_next_is_decl(Context *context) @@ -897,8 +870,7 @@ bool parse_next_is_decl(Context *context) switch (context->tok.type) { case TYPELIKE_TOKENS: - return (next_tok == TOKEN_BANG) | (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_IDENT) - | (next_tok == TOKEN_CONST_IDENT); + return next_tok != TOKEN_DOT && next_tok != TOKEN_LPAREN && next_tok != TOKEN_LBRACE; case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; return context_next_is_type_with_path_prefix(context); @@ -1362,6 +1334,70 @@ static inline Decl *parse_struct_declaration(Context *context, Visibility visibi return decl; } +/** + * body ::= '{' (TYPE IDENT ':' expr '..' expr EOS)* '}' + * @param context + * @param decl + * @return + */ +static inline bool parse_bitstruct_body(Context *context, Decl *decl) +{ + CONSUME_OR(TOKEN_LBRACE, false); + + while (!try_consume(context, TOKEN_RBRACE)) + { + TypeInfo *type = TRY_TYPE_OR(parse_type(context), false); + + if (!try_consume(context, TOKEN_IDENT)) + { + if (try_consume(context, TOKEN_CONST_IDENT) || try_consume(context, TOKEN_TYPE_IDENT)) + { + SEMA_TOKID_ERROR(context->prev_tok, "Expected a field name with an initial lower case."); + return false; + } + SEMA_TOKEN_ERROR(context->tok, "Expected a field name at this position."); + return false; + } + Decl *member_decl = decl_new_var(context->prev_tok, type, VARDECL_MEMBER, VISIBLE_LOCAL); + CONSUME_OR(TOKEN_COLON, false); + member_decl->var.start = TRY_EXPR_OR(parse_constant_expr(context), false); + CONSUME_OR(TOKEN_DOTDOT, false); + member_decl->var.end = TRY_EXPR_OR(parse_constant_expr(context), false); + CONSUME_OR(TOKEN_EOS, false); + vec_add(decl->bitstruct.members, member_decl); + } + + return true; +} +/** + * bitstruct_declaration = 'bitstruct' IDENT ':' type bitstruct_body + */ +static inline Decl *parse_bitstruct_declaration(Context *context, Visibility visibility) +{ + advance_and_verify(context, TOKEN_BITSTRUCT); + + TokenId name = context->tok.id; + + if (!consume_type_name(context, "bitstruct")) return poisoned_decl; + Decl *decl = decl_new_with_type(name, DECL_BITSTRUCT, visibility); + + CONSUME_OR(TOKEN_COLON, poisoned_decl); + + decl->bitstruct.base_type = TRY_TYPE_OR(parse_type(context), poisoned_decl); + + if (!parse_attributes(context, &decl->attributes)) + { + return poisoned_decl; + } + + if (!parse_bitstruct_body(context, decl)) + { + return poisoned_decl; + } + + return decl; + +} static inline Decl *parse_top_level_const_declaration(Context *context, Visibility visibility) { @@ -1710,29 +1746,53 @@ static inline Decl *parse_error_declaration(Context *context, Visibility visibil { advance_and_verify(context, TOKEN_ERRTYPE); - Decl *err_decl = decl_new_with_type(context->tok.id, DECL_ERR, visibility); + Decl *decl = decl_new_with_type(context->tok.id, DECL_ERRTYPE, visibility); if (!consume_type_name(context, "error type")) return poisoned_decl; - if (try_consume(context, TOKEN_LBRACE)) + TypeInfo *type = NULL; + + CONSUME_OR(TOKEN_LBRACE, poisoned_decl); + + decl->enums.type_info = type_info_new_base(type_iptr->canonical, decl->span); + while (!try_consume(context, TOKEN_RBRACE)) { - while (!try_consume(context, TOKEN_RBRACE)) + Decl *enum_const = DECL_NEW(DECL_ERRVALUE, decl->visibility); + const char *name = TOKSTR(context->tok); + VECEACH(decl->enums.values, i) { - TypeInfo *type = TRY_TYPE_OR(parse_type(context), poisoned_decl); - if (!TOKEN_IS(TOKEN_IDENT)) + Decl *other_constant = decl->enums.values[i]; + if (other_constant->name == name) { - SEMA_TOKEN_ERROR(context->tok, "Expected an identifier here."); - return poisoned_decl; + SEMA_TOKEN_ERROR(context->tok, "This enum constant is declared twice."); + SEMA_PREV(other_constant, "The previous declaration was here."); + decl_poison(enum_const); + break; } - Decl *member = decl_new_var(context->tok.id, type, VARDECL_MEMBER, visibility); - advance(context); - vec_add(err_decl->strukt.members, member); - TRY_CONSUME_EOS_OR(poisoned_decl); } - return err_decl; + if (!consume_const_name(context, "enum constant")) + { + return poisoned_decl; + } + if (try_consume(context, TOKEN_LPAREN)) + { + Expr **result = NULL; + if (!parse_arg_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; + enum_const->enum_constant.args = result; + CONSUME_OR(TOKEN_RPAREN, poisoned_decl); + } + if (try_consume(context, TOKEN_EQ)) + { + enum_const->enum_constant.expr = TRY_EXPR_OR(parse_expr(context), poisoned_decl); + } + vec_add(decl->enums.values, enum_const); + // Allow trailing ',' + if (!try_consume(context, TOKEN_COMMA)) + { + EXPECT_OR(TOKEN_RBRACE, poisoned_decl); + } } - TRY_CONSUME_EOS_OR(poisoned_decl); - return err_decl; + return decl; } /** @@ -1797,7 +1857,7 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili if (!parse_enum_spec(context, &type, &decl->enums.parameters, visibility)) return poisoned_decl; } - CONSUME_OR(TOKEN_LBRACE, false); + CONSUME_OR(TOKEN_LBRACE, poisoned_decl); decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span); while (!try_consume(context, TOKEN_RBRACE)) @@ -2056,7 +2116,7 @@ static inline bool parse_doc_errors(Context *context, Ast *docs) static inline bool parse_doc_contract(Context *context, Ast *docs) { - docs->doc_directive.contract.decl_exprs = TRY_EXPR_OR(parse_decl_expr_list(context), false); + docs->doc_directive.contract.decl_exprs = TRY_EXPR_OR(parse_cond(context), false); if (try_consume(context, TOKEN_COLON)) { docs->doc_directive.contract.comment = TRY_EXPR_OR(parse_expr(context), false); @@ -2211,6 +2271,9 @@ Decl *parse_top_level_statement(Context *context) return poisoned_decl; } break; + case TOKEN_BITSTRUCT: + decl = TRY_DECL_OR(parse_bitstruct_declaration(context, visibility), poisoned_decl); + break; case TOKEN_CONST: decl = TRY_DECL_OR(parse_top_level_const_declaration(context, visibility), poisoned_decl); break; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 2bb10aa7f..61383c574 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -277,7 +277,7 @@ static inline Ast* parse_while_stmt(Context *context) while_ast->while_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, while_ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - while_ast->while_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + while_ast->while_stmt.cond = TRY_EXPR_OR(parse_cond(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); while_ast->while_stmt.body = TRY_AST(parse_stmt(context)); return while_ast; @@ -308,7 +308,7 @@ static inline Ast* parse_if_stmt(Context *context) advance_and_verify(context, TOKEN_IF); if_ast->if_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, if_ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - if_ast->if_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + if_ast->if_stmt.cond = TRY_EXPR_OR(parse_cond(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); Ast *stmt = TRY_AST(parse_stmt(context)); if_ast->if_stmt.then_body = stmt; @@ -436,7 +436,7 @@ static inline Ast* parse_switch_stmt(Context *context) advance_and_verify(context, TOKEN_SWITCH); switch_ast->switch_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, switch_ast), poisoned_ast); CONSUME_OR(TOKEN_LPAREN, poisoned_ast); - switch_ast->switch_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + switch_ast->switch_stmt.cond = TRY_EXPR_OR(parse_cond(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT, false)) return poisoned_ast; @@ -463,7 +463,7 @@ static inline Ast* parse_for_stmt(Context *context) if (!TOKEN_IS(TOKEN_EOS)) { - ast->for_stmt.init = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + ast->for_stmt.init = TRY_EXPR_OR(parse_cond(context), poisoned_ast); } else { @@ -645,7 +645,7 @@ static inline Ast *parse_try_stmt(Context *context) Ast *stmt = AST_NEW_TOKEN(AST_TRY_STMT, context->tok); advance_and_verify(context, TOKEN_TRY_OLD); TRY_CONSUME(TOKEN_LPAREN, "Expected a '(' after 'try'."); - stmt->try_old_stmt.decl_expr = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); + stmt->try_old_stmt.decl_expr = TRY_EXPR_OR(parse_cond(context), poisoned_ast); TRY_CONSUME(TOKEN_RPAREN, "Expected a ')' after 'try'."); stmt->try_old_stmt.body = TRY_AST(parse_stmt(context)); return stmt; @@ -1148,6 +1148,7 @@ Ast *parse_stmt(Context *context) case TOKEN_UNDERSCORE: case TOKEN_PRIVATE: case TOKEN_PLACEHOLDER: + case TOKEN_BITSTRUCT: SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type)); advance(context); return poisoned_ast; diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index cbbad956d..b4bff8bad 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -35,6 +35,8 @@ Ast *parse_stmt(Context *context); Path *parse_path_prefix(Context *context, bool *had_error); Expr *parse_type_expression_with_path(Context *context, Path *path); Expr *parse_expr(Context *context); +bool consume_ident(Context *context, const char* name); +Expr *parse_try_expr_after_try(Context *context, bool is_try); TypeInfo *parse_type(Context *context); TypeInfo *parse_type_with_base(Context *context, TypeInfo *type_info); Expr* parse_constant_expr(Context *context); @@ -43,7 +45,7 @@ Expr *parse_initializer(Context *context); void parse_imports(Context *context); Decl *parse_decl(Context *context); void recover_top_level(Context *context); -Expr *parse_decl_expr_list(Context *context); +Expr *parse_cond(Context *context); Ast* parse_compound_stmt(Context *context); Ast *parse_jump_stmt_no_eos(Context *context); bool parse_attributes(Context *context, Attr ***attributes_ref); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 37ad2685a..7197aebb2 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -383,7 +383,7 @@ CastKind cast_to_bool_kind(Type *type) return CAST_VRBOOL; case TYPE_BOOL: return CAST_BOOLBOOL; - case TYPE_ERR_UNION: + case TYPE_ANYERR: return CAST_EUBOOL; case TYPE_SUBARRAY: return CAST_SABOOL; @@ -393,18 +393,20 @@ CastKind cast_to_bool_kind(Type *type) return CAST_FPBOOL; case TYPE_POINTER: return CAST_PTRBOOL; + case TYPE_ERRTYPE: + return CAST_ERBOOL; case TYPE_POISONED: case TYPE_VOID: case TYPE_STRUCT: case TYPE_UNION: case TYPE_STRLIT: - case TYPE_ERRTYPE: case TYPE_ENUM: case TYPE_FUNC: case TYPE_ARRAY: case TYPE_TYPEID: case TYPE_TYPEINFO: case TYPE_VECTOR: + case TYPE_BITSTRUCT: return CAST_ERROR; } UNREACHABLE @@ -436,9 +438,11 @@ bool cast_may_explicit(Type *from_type, Type *to_type) case TYPE_BOOL: // May convert to any integer / distinct integer / float, no enums return type_is_integer(to) || type_is_float(to); - case TYPE_ERR_UNION: - // May convert to a bool, or an error type. - return to == type_bool || to_kind == TYPE_ERRTYPE; + case TYPE_BITSTRUCT: + return false; + case TYPE_ANYERR: + // May convert to a bool, an error type or an integer + return to == type_bool || to_kind == TYPE_ERRTYPE || type_is_integer(to); case TYPE_IXX: case ALL_SIGNED_INTS: case ALL_UNSIGNED_INTS: @@ -461,8 +465,8 @@ bool cast_may_explicit(Type *from_type, Type *to_type) case TYPE_VIRTUAL: return to_kind == TYPE_POINTER; case TYPE_ERRTYPE: - // Allow only MyError.A -> error - return to->type_kind == TYPE_ERR_UNION; + // Allow MyError.A -> error, to an integer or to bool + return to->type_kind == TYPE_ANYERR || type_is_any_integer(to) || to == type_bool; case TYPE_ARRAY: if (to_kind == TYPE_VECTOR) { @@ -552,6 +556,8 @@ bool cast_may_implicit(Type *from_type, Type *to_type) return false; } + if (to == type_anyerr && from->type_kind == TYPE_ERRTYPE) return true; + // 3. Handle ints if (type_is_integer(to)) { @@ -780,14 +786,17 @@ bool cast(Expr *expr, Type *to_type) case TYPE_FUNC: case TYPE_TYPEDEF: UNREACHABLE + case TYPE_BITSTRUCT: + UNREACHABLE case TYPE_BOOL: // Bool may convert into integers and floats but only explicitly. if (type_is_integer(canonical)) return bool_to_int(expr, canonical, to_type); if (type_is_float(canonical)) return bool_to_float(expr, canonical, to_type); break; - case TYPE_ERR_UNION: + case TYPE_ANYERR: if (canonical->type_kind == TYPE_BOOL) return insert_cast(expr, CAST_EUBOOL, to_type); if (canonical->type_kind == TYPE_ERRTYPE) return insert_cast(expr, CAST_EUER, to_type); + if (type_is_integer(canonical)) return insert_cast(expr, CAST_EUINT, to_type); break; case TYPE_IXX: if (type_is_integer(canonical)) return int_literal_to_int(expr, canonical, to_type); @@ -833,7 +842,9 @@ bool cast(Expr *expr, Type *to_type) if (canonical->type_kind == TYPE_POINTER) return enum_to_pointer(expr, from_type, to_type); break; case TYPE_ERRTYPE: - if (canonical->type_kind == TYPE_ERR_UNION) return insert_cast(expr, CAST_EREU, to_type); + if (canonical->type_kind == TYPE_ANYERR) return insert_cast(expr, CAST_EREU, to_type); + if (canonical == type_bool) return insert_cast(expr, CAST_ERBOOL, to_type); + if (type_is_integer(to_type)) return insert_cast(expr, CAST_ERINT, to_type); break; case TYPE_STRUCT: case TYPE_UNION: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index ca980c309..ee590ba88 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -279,7 +279,7 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl) case DECL_UNION: domain = ATTR_UNION; break; - case DECL_ERR: + case DECL_ERRTYPE: domain = ATTR_ERROR; break; default: @@ -359,6 +359,75 @@ static bool sema_analyse_struct_union(Context *context, Decl *decl) return decl_ok(decl); } +static bool sema_analyse_bitstruct(Context *context, Decl *decl) +{ + VECEACH(decl->attributes, i) + { + Attr *attr = decl->attributes[i]; + + AttributeType attribute = sema_analyse_attribute(context, attr, ATTR_BITSTRUCT); + if (attribute == ATTRIBUTE_NONE) return decl_poison(decl); + + bool had = false; + int overlap = -1; +#define SET_ATTR(_X) had = decl->func_decl._X; decl->func_decl._X = true; break + switch (attribute) + { + case ATTRIBUTE_OVERLAP: + had = overlap != -1; + overlap = 1; + break; + case ATTRIBUTE_OPAQUE: + had = decl->is_opaque; + decl->is_opaque = true; + break; + default: + UNREACHABLE + } +#undef SET_ATTR + if (had) + { + SEMA_TOKID_ERROR(attr->name, "Attribute occurred twice, please remove one."); + return decl_poison(decl); + } + } + + DEBUG_LOG("Beginning analysis of %s.", decl->name ? decl->name : "anon"); + if (!sema_resolve_type_info(context, decl->bitstruct.base_type)) return false; + Type *type = decl->bitstruct.base_type->type->canonical; + Type *base_type = type->type_kind == TYPE_ARRAY ? type->array.base : type; + if (!type_is_integer(base_type)) + { + SEMA_ERROR(decl->bitstruct.base_type, "The type of the bitstruct cannot be %s but must be an integer or an array of integers.", + type_quoted_error_string(decl->bitstruct.base_type->type)); + return false; + } + Decl **members = decl->bitstruct.members; + bool success = true; + SCOPE_START + VECEACH(members, i) + { + Decl *member = members[i]; + if (!sema_resolve_type_info(context, member->var.type_info)) + { + success = false; + continue; + } + Type *member_type = type_flatten_for_bitstruct(member->var.type_info->type); + if (!type_is_integer(member_type) && member_type != type_bool) + { + SEMA_ERROR(member->var.type_info, "%s is not supported in a bitstruct, only enums, integer and boolean values may be used.", + type_quoted_error_string(member->var.type_info->type)); + success = false; + continue; + } + } + SCOPE_END; + if (!success) return decl_poison(decl); + TODO + return decl_ok(decl); +} + static inline bool sema_analyse_function_param(Context *context, Decl *param, bool is_function, bool *has_default) { @@ -495,9 +564,9 @@ static inline bool sema_analyse_distinct(Context *context, Decl *decl) SEMA_ERROR(decl, "You cannot create a distinct type from a virtual type."); return false; case TYPE_ERRTYPE: - SEMA_ERROR(decl, "You cannot create a distinct type from an error."); + SEMA_ERROR(decl, "You cannot create a distinct type from an error type."); return false; - case TYPE_ERR_UNION: + case TYPE_ANYERR: SEMA_ERROR(decl, "You cannot create a distinct type from an error union."); return false; case TYPE_VOID: @@ -509,6 +578,7 @@ static inline bool sema_analyse_distinct(Context *context, Decl *decl) case ALL_REAL_FLOATS: case TYPE_POINTER: case TYPE_ENUM: + case TYPE_BITSTRUCT: case TYPE_STRUCT: case TYPE_UNION: case TYPE_ARRAY: @@ -601,6 +671,29 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl) return success; } +static inline bool sema_analyse_error(Context *context, Decl *decl) +{ + bool success = true; + unsigned enums = vec_size(decl->enums.values); + + for (unsigned i = 0; i < enums; i++) + { + Decl *enum_value = decl->enums.values[i]; + enum_value->type = decl->type; + DEBUG_LOG("* Checking error value %s.", enum_value->name); + enum_value->enum_constant.ordinal = i; + DEBUG_LOG("* Ordinal: %d", i); + assert(enum_value->resolve_status == RESOLVE_NOT_DONE); + assert(enum_value->decl_kind == DECL_ERRVALUE); + + // Start evaluating the constant + enum_value->resolve_status = RESOLVE_RUNNING; + enum_value->enum_constant.expr = NULL; + enum_value->resolve_status = RESOLVE_DONE; + } + return success; +} + static inline const char *name_by_decl(Decl *method_like) { switch (method_like->decl_kind) @@ -692,6 +785,8 @@ static const char *attribute_domain_to_string(AttributeDomain domain) { switch (domain) { + case ATTR_BITSTRUCT: + return "bitstruct"; case ATTR_INTERFACE: return "interface"; case ATTR_MEMBER: @@ -729,7 +824,7 @@ AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDoma [ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, [ATTRIBUTE_EXTNAME] = ~ATTR_CALL, [ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR, - [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION | ATTR_ERROR, + [ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION, [ATTRIBUTE_NORETURN] = ATTR_FUNC, [ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, @@ -743,6 +838,7 @@ AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDoma [ATTRIBUTE_VECCALL] = ATTR_FUNC, [ATTRIBUTE_REGCALL] = ATTR_FUNC, [ATTRIBUTE_FASTCALL] = ATTR_FUNC, + [ATTRIBUTE_OVERLAP] = ATTR_BITSTRUCT }; if ((attribute_domain[type] & domain) != domain) @@ -1048,7 +1144,9 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_MEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: UNREACHABLE } param->resolve_status = RESOLVE_DONE; @@ -1080,7 +1178,9 @@ static inline bool sema_analyse_macro(Context *context, Decl *decl) case VARDECL_MEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: UNREACHABLE } param->resolve_status = RESOLVE_DONE; @@ -1359,31 +1459,6 @@ static inline bool sema_analyse_define(Context *c, Decl *decl) } -/** - * Semantic analysis on an error first checks the internals as if it was - * a struct, then checks that the size is not exceeded and adds padding. - */ -static inline bool sema_analyse_error(Context *context, Decl *decl) -{ - // 1. Step one is to analyze the error as it it was a regular struct. - if (!sema_analyse_struct_union(context, decl)) return false; - - // 2. Because an error is always pointer sized, we check so that it isn't exceeded. - ByteSize error_full_size = type_size(type_uptr); - if (decl->strukt.size > error_full_size) - { - SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", error_full_size, decl->strukt.size); - return false; - } - - // 3. If the size is smaller than than pointer sized, we add padding. - if (decl->strukt.size < error_full_size) - { - decl->strukt.padding = error_full_size - decl->strukt.size; - decl->strukt.size = error_full_size; - } - return true; -} bool sema_analyse_decl(Context *context, Decl *decl) @@ -1404,6 +1479,10 @@ bool sema_analyse_decl(Context *context, Decl *decl) { case DECL_INTERFACE: TODO + case DECL_BITSTRUCT: + if (!sema_analyse_bitstruct(context, decl)) return decl_poison(decl); + decl_set_external_name(decl); + break; case DECL_STRUCT: case DECL_UNION: if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl); @@ -1431,7 +1510,7 @@ bool sema_analyse_decl(Context *context, Decl *decl) if (!sema_analyse_enum(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; - case DECL_ERR: + case DECL_ERRTYPE: if (!sema_analyse_error(context, decl)) return decl_poison(decl); decl_set_external_name(decl); break; @@ -1451,6 +1530,7 @@ bool sema_analyse_decl(Context *context, Decl *decl) case DECL_CT_CASE: case DECL_CT_IF: case DECL_CT_ASSERT: + case DECL_ERRVALUE: UNREACHABLE } decl->resolve_status = RESOLVE_DONE; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7355f0f07..5e7238be4 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -44,7 +44,6 @@ static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) } static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr); -static inline bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr); static inline bool expr_const_int_valid(Expr *expr, Type *type) { if (expr_const_int_overflowed(&expr->const_expr)) @@ -190,7 +189,7 @@ int sema_check_comp_time_bool(Context *context, Expr *expr) return expr->const_expr.b; } -static bool expr_is_ltype(Expr *expr) +bool expr_is_ltype(Expr *expr) { switch (expr->expr_kind) { @@ -213,8 +212,15 @@ static bool expr_is_ltype(Expr *expr) case VARDECL_PARAM_REF: return true; case VARDECL_CONST: - default: + case VARDECL_MEMBER: + case VARDECL_PARAM_CT: + case VARDECL_PARAM_CT_TYPE: + case VARDECL_PARAM_EXPR: return false; + case VARDECL_UNWRAPPED: + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + UNREACHABLE } } case EXPR_UNARY: @@ -248,6 +254,9 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_GENERIC: SEMA_ERROR(expr, "Expected generic followed by (...) or prefixed by '&'."); return expr_poison(expr); + case DECL_ERRVALUE: + SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); + return expr_poison(expr); case DECL_ENUM_CONSTANT: expr_replace(expr, decl->enum_constant.expr); return true; @@ -264,6 +273,9 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_INTERFACE: SEMA_ERROR(expr, "Expected interface followed by '.'."); return expr_poison(expr); + case DECL_BITSTRUCT: + SEMA_ERROR(expr, "Expected bitstruct followed by (...) or '.'."); + return expr_poison(expr); case DECL_STRUCT: SEMA_ERROR(expr, "Expected struct followed by (...) or '.'."); return expr_poison(expr); @@ -273,8 +285,8 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_ENUM: SEMA_ERROR(expr, "Expected enum name followed by '.' and an enum value."); return expr_poison(expr); - case DECL_ERR: - SEMA_ERROR(expr, "Did you forget a '!' after '%s'?", decl->name); + case DECL_ERRTYPE: + SEMA_ERROR(expr, "Expected errtype name followed by '.' and an error value."); return expr_poison(expr); case DECL_ARRAY_VALUE: UNREACHABLE @@ -307,7 +319,7 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case VARDECL_PARAM: case VARDECL_GLOBAL: case VARDECL_LOCAL: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: break; case VARDECL_MEMBER: SEMA_ERROR(expr, "Expected '%s' followed by a method call or property.", decl->name); @@ -321,6 +333,9 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case VARDECL_LOCAL_CT_TYPE: TODO break; + case VARDECL_ERASE: + case VARDECL_REWRAPPED: + UNREACHABLE } return true; } @@ -330,7 +345,7 @@ static ExprFailableStatus expr_is_failable(Expr *expr) if (expr->expr_kind != EXPR_IDENTIFIER) return FAILABLE_NO; Decl *decl = expr->identifier_expr.decl; if (decl->decl_kind != DECL_VAR) return FAILABLE_NO; - if (decl->var.kind == VARDECL_ALIAS && decl->var.alias->var.failable) return FAILABLE_UNWRAPPED; + if (decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->var.failable) return FAILABLE_UNWRAPPED; return decl->var.failable ? FAILABLE_YES : FAILABLE_NO; } @@ -478,6 +493,7 @@ static inline bool find_possible_inferred_identifier(Type *to, Expr *expr) switch (parent_decl->decl_kind) { case DECL_ENUM: + case DECL_ERRTYPE: return sema_expr_analyse_enum_constant(expr, expr->identifier_expr.identifier, parent_decl); case DECL_UNION: case DECL_STRUCT: @@ -1146,7 +1162,9 @@ static inline bool sema_expr_analyse_call_invocation(Context *context, Expr *cal case VARDECL_MEMBER: case VARDECL_LOCAL_CT: case VARDECL_LOCAL_CT_TYPE: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: UNREACHABLE } if (param->type) @@ -2280,7 +2298,44 @@ static inline bool sema_expr_analyse_type_access(Expr *expr, TypeInfo *parent, b return true; } break; - case DECL_ERR: + case DECL_ERRTYPE: + if (type == TOKEN_CONST_IDENT) + { + if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) + { + SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name); + return false; + } + return true; + } + if (name == kw_elements) + { + expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values)); + return true; + } + if (name == kw_max) + { + Expr *max = enum_minmax_value(decl, BINARYOP_GT); + if (!max) + { + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + return true; + } + expr_replace(expr, max); + return true; + } + if (name == kw_min) + { + Expr *min = enum_minmax_value(decl, BINARYOP_LT); + if (!min) + { + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); + return true; + } + expr_replace(expr, min); + return true; + } + break; case DECL_UNION: case DECL_STRUCT: case DECL_DISTINCT: @@ -2336,7 +2391,7 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr, } if (name == kw_ordinal && !is_macro) { - if (ref->decl_kind == DECL_ENUM_CONSTANT) + if (ref->decl_kind == DECL_ENUM_CONSTANT || ref->decl_kind == DECL_ERRVALUE) { expr_rewrite_to_int_const(expr, type_compint, ref->enum_constant.ordinal); return true; @@ -2398,7 +2453,6 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr, { case DECL_STRUCT: case DECL_UNION: - case DECL_ERR: VECEACH(flat_ref->strukt.members, i) { Decl *member = ref->strukt.members[i]; @@ -2976,7 +3030,6 @@ static inline void sema_update_const_initializer_with_designator( switch (const_init->type->type_kind) { case TYPE_STRUCT: - case TYPE_ERRTYPE: sema_update_const_initializer_with_designator_struct(const_init, curr, end, value); return; case TYPE_UNION: @@ -3337,7 +3390,6 @@ static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to case TYPE_STRUCT: case TYPE_UNION: case TYPE_ARRAY: - case TYPE_ERRTYPE: case TYPE_INFERRED_ARRAY: return sema_expr_analyse_initializer(context, to, assigned, expr); default: @@ -3477,7 +3529,7 @@ static inline bool sema_expr_analyse_ct_identifier_lvalue(Context *context, Expr if (!decl) { - SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", expr->ct_ident_expr.identifier); + SEMA_ERROR(expr, "The compile time variable '%s' was not defined in this scope.", TOKSTR(expr->ct_ident_expr.identifier)); return expr_poison(expr); } @@ -3565,16 +3617,6 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E if (!sema_analyse_expr_value(context, NULL, left)) return false; - if (left->expr_kind == EXPR_TRY) - { - if (expr_is_ltype(left->try_expr.expr)) - { - SEMA_ERROR(left, "This part will be evaluated to a boolean, so if you want to test an assignment, " - "you need to use () around the assignment, like this: '%s (variable = expression)'.", - left->expr_kind == EXPR_TRY ? "try" : "catch"); - return false; - } - } // 2. Check assignability if (!expr_is_ltype(left)) @@ -4479,13 +4521,14 @@ static bool sema_expr_analyse_comp(Context *context, Expr *expr, Expr *left, Exp case TYPE_FUNC: case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_STRLIT: case TYPE_ARRAY: case TYPE_SUBARRAY: case TYPE_TYPEID: case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: + case TYPE_BITSTRUCT: // Only != and == allowed. goto ERR; case ALL_INTS: @@ -4590,7 +4633,9 @@ static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_consta // May not be reached due to EXPR_CT_IDENT being handled elsewhere. UNREACHABLE; case VARDECL_MEMBER: - case VARDECL_ALIAS: + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + case VARDECL_ERASE: UNREACHABLE } UNREACHABLE @@ -4808,9 +4853,10 @@ static bool sema_expr_analyse_not(Expr *expr, Expr *inner) case TYPE_IXX: case TYPE_FXX: case TYPE_TYPEDEF: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_DISTINCT: case TYPE_INFERRED_ARRAY: + case TYPE_BITSTRUCT: UNREACHABLE case TYPE_FUNC: case TYPE_ARRAY: @@ -5048,21 +5094,6 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr } -static inline bool sema_expr_analyse_try_old(Context *context, Expr *expr) -{ - Expr *inner = expr->trycatch_expr; - bool success = sema_analyse_expr(context, NULL, inner); - expr->pure = inner->pure; - expr->constant = false; - if (!success) return false; - if (!inner->failable) - { - SEMA_ERROR(expr->trycatch_expr, "Expected a failable expression to 'try'."); - return false; - } - expr_set_type(expr, type_bool); - return true; -} static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bool implicitly_create_variable) { @@ -5152,27 +5183,10 @@ static inline bool sema_expr_analyse_try(Context *context, Expr *expr) SEMA_ERROR(expr->try_expr.expr, "Expected a failable expression to '%s'.", expr->expr_kind == EXPR_TRY ? "try" : "catch"); return false; } - expr_set_type(expr, type_bool); + expr_set_type(expr, expr->try_expr.is_try ? type_bool : type_anyerr); return true; } -static inline bool sema_expr_analyse_catch_old(Context *context, Expr *expr) -{ - Expr *inner = expr->trycatch_expr; - bool success = sema_analyse_expr(context, NULL, inner); - expr->pure = inner->pure; - expr->constant = false; - if (!success) return false; - if (!inner->failable) - { - SEMA_ERROR(expr->trycatch_expr, "Expected a failable expression to 'catch'."); - return false; - } - expr_set_type(expr, type_bool); - return true; -} - - static inline bool sema_expr_analyse_else(Context *context, Type *to, Expr *expr) { Expr *inner = expr->else_expr.expr; @@ -5335,16 +5349,6 @@ static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr * { Expr *inner = expr->failable_expr; - // Syntactic sugar: Foo! => Foo({})! - if (inner->expr_kind == EXPR_TYPEINFO) - { - TypeInfo *type = inner->type_expr; - *inner = (Expr) { .expr_kind = EXPR_COMPOUND_LITERAL, .span = inner->span }; - inner->expr_compound_literal.type_info = type; - Expr *initializer_list = expr_new(EXPR_INITIALIZER_LIST, inner->span); - initializer_list->initializer_expr.init_type = INITIALIZER_UNKNOWN; - inner->expr_compound_literal.initializer = initializer_list; - } if (!sema_analyse_expr(context, NULL, inner)) return false; expr->pure = inner->pure; @@ -5359,7 +5363,7 @@ static inline bool sema_expr_analyse_failable(Context *context, Type *to, Expr * SEMA_ERROR(inner, "It looks like you added one too many '!' after the error."); return false; } - if (type->type_kind != TYPE_ERRTYPE && type->type_kind != TYPE_ERR_UNION) + if (type->type_kind != TYPE_ERRTYPE && type->type_kind != TYPE_ANYERR) { SEMA_ERROR(inner, "You cannot use the '!' operator on expressions of type '%s'", type_to_error_string(type)); return false; @@ -5770,7 +5774,7 @@ static inline bool sema_analyse_ct_call_parameters(Context *context, Expr *expr) { case DECL_DISTINCT: case DECL_ENUM: - case DECL_ERR: + case DECL_ERRTYPE: case DECL_FUNC: case DECL_STRUCT: case DECL_TYPEDEF: @@ -6056,7 +6060,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * { switch (expr->expr_kind) { - case EXPR_DECL_LIST: + case EXPR_COND: case EXPR_UNDEF: case EXPR_ENUM_CONSTANT: case EXPR_MEMBER_ACCESS: @@ -6064,7 +6068,12 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * case EXPR_MACRO_BODY_EXPANSION: case EXPR_FLATPATH: case EXPR_NOP: + case EXPR_TRY_UNWRAP_CHAIN: + case EXPR_TRY_UNWRAP: + case EXPR_CATCH_UNWRAP: UNREACHABLE + case EXPR_TRY_DECL: + TODO case EXPR_DECL: return sema_expr_analyse_decl(context, to, expr); case EXPR_CT_CALL: @@ -6094,10 +6103,6 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return sema_expr_analyse_try(context, expr); case EXPR_TRY_ASSIGN: return sema_expr_analyse_try_assign(context, expr, false); - case EXPR_CATCH_OLD: - return sema_expr_analyse_catch_old(context, expr); - case EXPR_TRY_OLD: - return sema_expr_analyse_try_old(context, expr); case EXPR_TYPEOF: return sema_expr_analyse_typeof(context, expr); case EXPR_ELSE: @@ -6199,15 +6204,21 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr) } break; case EXPR_MEMBER_ACCESS: - if (expr->access_expr.ref->decl_kind == DECL_ENUM_CONSTANT) + switch (expr->access_expr.ref->decl_kind) { - Type *original_type = expr->access_expr.ref->type; - expr_replace(expr, expr->access_expr.ref->enum_constant.expr); - expr->original_type = original_type; - return true; + case DECL_ERRVALUE: + return true; + case DECL_ENUM_CONSTANT: + { + Type *original_type = expr->access_expr.ref->type; + expr_replace(expr, expr->access_expr.ref->enum_constant.expr); + expr->original_type = original_type; + return true; + } + default: + SEMA_ERROR(expr, "A member must be followed by '.' plus a property like 'sizeof'."); + return false; } - SEMA_ERROR(expr, "A member must be followed by '.' plus a property like 'sizeof'."); - return false; case EXPR_LEN: if (expr->type != type_void) return true; SEMA_ERROR(expr, "Expected () after 'len' for subarrays."); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index b7e915f56..500f23b71 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -53,3 +53,6 @@ void context_change_scope_with_flags(Context *context, ScopeFlags flags); void c_abi_func_create(FunctionSignature *signature); AttributeType sema_analyse_attribute(Context *context, Attr *attr, AttributeDomain domain); +bool expr_is_ltype(Expr *expr); +bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr); + diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index efb487334..fb16ab8cf 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -123,10 +123,23 @@ static Decl *sema_resolve_no_path_symbol(Context *context, const char *symbol, } while (current >= first) { - if (current[0]->name == symbol) return current[0]; + if (current[0]->name == symbol) + { + // We patch special behaviour here. + if (current[0]->decl_kind == DECL_VAR) + { + VarDeclKind kind = current[0]->var.kind; + + // In this case, we erase the value from parent scopes, so it isn't visible here. + if (kind == VARDECL_ERASE) goto JUMP_ERASED; + if (kind == VARDECL_REWRAPPED) return current[0]->var.alias; + } + return current[0]; + } current--; } } + JUMP_ERASED: // Search in file scope. decl = stable_get(&context->local_symbols, symbol); @@ -449,7 +462,7 @@ bool sema_add_local(Context *context, Decl *decl) bool sema_unwrap_var(Context *context, Decl *decl) { Decl *alias = decl_copy(decl); - alias->var.kind = VARDECL_ALIAS; + alias->var.kind = VARDECL_UNWRAPPED; alias->var.alias = decl; alias->var.failable = false; alias->resolve_status = RESOLVE_DONE; @@ -458,8 +471,26 @@ bool sema_unwrap_var(Context *context, Decl *decl) bool sema_rewrap_var(Context *context, Decl *decl) { - assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_ALIAS && decl->var.alias->var.failable); + assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_UNWRAPPED && decl->var.alias->var.failable); return sema_append_local(context, decl->var.alias); } +bool sema_erase_var(Context *context, Decl *decl) +{ + Decl *erased = decl_copy(decl); + erased->var.kind = VARDECL_ERASE; + erased->resolve_status = RESOLVE_DONE; + return sema_append_local(context, erased); +} + +bool sema_erase_unwrapped(Context *context, Decl *decl) +{ + assert(decl->var.failable); + Decl *rewrapped = decl_copy(decl); + rewrapped->var.kind = VARDECL_REWRAPPED; + rewrapped->var.alias = decl; + rewrapped->var.failable = true; + rewrapped->resolve_status = RESOLVE_DONE; + return sema_append_local(context, rewrapped); +} diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 999488fcd..23a455b7d 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -6,73 +6,40 @@ #pragma mark --- Helper functions -static void sema_add_unwraps_from_try(Context *c, Expr *last_expr) + + +static void sema_unwrappable_from_catch_in_else(Context *c, Expr *cond) { - if (last_expr->expr_kind == EXPR_DECL_LIST) + assert(cond->expr_kind == EXPR_COND); + + Expr *last = VECLAST(cond->cond_expr); + while (last->expr_kind == EXPR_CAST) { - unsigned elements = vec_size(last_expr->dexpr_list_expr); - if (!elements) return; - last_expr = last_expr->dexpr_list_expr[elements - 1]; + last = last->cast_expr.expr; } - // Flatten group. - while (last_expr->expr_kind == EXPR_GROUP) last_expr = last_expr->group_expr; - if (last_expr->expr_kind == EXPR_TRY && last_expr->try_expr.is_try && - last_expr->try_expr.expr->expr_kind == EXPR_IDENTIFIER) + if (!last || last->expr_kind != EXPR_CATCH_UNWRAP) return; + + Expr **unwrapped = last->catch_unwrap_expr.exprs; + + VECEACH(unwrapped, i) { - Decl *var = last_expr->try_expr.expr->identifier_expr.decl; - if (var->decl_kind != DECL_VAR || !var->var.failable) return; - sema_unwrap_var(c, var); - return; - } - if (last_expr->expr_kind != EXPR_BINARY || last_expr->binary_expr.operator != BINARYOP_AND) return; - sema_add_unwraps_from_try(c, last_expr->binary_expr.left); - sema_add_unwraps_from_try(c, last_expr->binary_expr.right); -} + Expr *expr = unwrapped[i]; + if (expr->expr_kind != EXPR_IDENTIFIER) continue; + Decl *decl = expr->identifier_expr.decl; + if (decl->decl_kind != DECL_VAR) continue; + assert(decl->var.failable && "The variable should always be failable at this point."); -static Decl *sema_find_unwrappable_from_catch(Context *c, Expr *last_expr) -{ - // 1. Flatten a decl list. - if (last_expr->expr_kind == EXPR_DECL_LIST) - { - unsigned elements = vec_size(last_expr->dexpr_list_expr); - if (!elements) return NULL; - last_expr = last_expr->dexpr_list_expr[elements - 1]; - } + // 5. Locals and globals may be unwrapped + switch (decl->var.kind) + { + case VARDECL_LOCAL: + case VARDECL_GLOBAL: + sema_unwrap_var(c, decl); + break; + default: + continue; + } - // Flatten groups on top. - while (last_expr->expr_kind == EXPR_GROUP) last_expr = last_expr->group_expr; - - Expr *variable = NULL; - - // 1. Get the variable in "if (check x)" - if (last_expr->expr_kind == EXPR_TRY && !last_expr->try_expr.is_try) - { - variable = last_expr->try_expr.expr; - } - // 2. Get the variable in "if (check err = x)" - if (last_expr->expr_kind == EXPR_TRY_ASSIGN && !last_expr->try_assign_expr.is_try) - { - variable = last_expr->try_assign_expr.init; - } - - // 3. Not found or not identifier -> exit - if (!variable || variable->expr_kind != EXPR_IDENTIFIER) return NULL; - - Decl *decl = variable->identifier_expr.decl; - - // 4. Not a var declaration, when could this happen...? Possibly redundant check. - if (decl->decl_kind != DECL_VAR) return NULL; - - assert(decl->var.failable && "The variable should always be failable at this point."); - - // 5. Locals and globals may be unwrapped - switch (decl->var.kind) - { - case VARDECL_LOCAL: - case VARDECL_GLOBAL: - return decl; - default: - return NULL; } } @@ -149,6 +116,297 @@ static inline bool sema_analyse_unreachable_stmt(Context *context) return true; } +static inline bool sema_analyse_try_unwrap(Context *context, Expr *expr) +{ + assert(expr->expr_kind == EXPR_TRY_UNWRAP); + Expr *ident = expr->try_unwrap_expr.variable; + Expr *failable = expr->try_unwrap_expr.init; + + // Case A. Unwrapping a single variable. + if (!failable) + { + if (!sema_analyse_expr(context, NULL, ident)) return false; + if (ident->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(ident, "Only single identifiers may be unwrapped using 'try var', maybe you wanted 'try (expr)' instead?"); + return false; + } + Decl *decl = ident->identifier_expr.decl; + if (decl->decl_kind != DECL_VAR) + { + SEMA_ERROR(ident, "Expected this to be the name of a failable variable, but it isn't. Did you mistype?"); + return false; + } + if (!decl->var.failable) + { + if (decl->var.kind == VARDECL_UNWRAPPED) + { + SEMA_ERROR(ident, "This variable is already unwrapped, so you cannot use 'try' on it again, please remove the 'try'."); + return false; + } + SEMA_ERROR(ident, "Expected this variable to be a failable, otherwise it can't be used for unwrap, maybe you didn't intend to use 'try'?"); + return false; + } + expr->try_unwrap_expr.decl = decl; + expr_set_type(expr, type_bool); + sema_unwrap_var(context, decl); + expr->resolve_status = RESOLVE_DONE; + return true; + } + + // Case B. We are unwrapping to a variable that may or may not exist. + bool implicit_declaration = false; + TypeInfo *var_type = expr->try_unwrap_expr.type; + + // 1. Check if we are doing an implicit declaration. + if (!var_type && ident->expr_kind == EXPR_IDENTIFIER) + { + Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false); + if (!decl) implicit_declaration = true; + } + + // 2. If we have a type for the variable, resolve it. + if (var_type && !sema_resolve_type_info(context, var_type)) return false; + + // 3. We interpret this as an assignment to an existing variable. + if (!var_type && !implicit_declaration) + { + // 3a. Resolve the identifier. + if (!sema_analyse_expr_value(context, NULL, ident)) return false; + + // 3b. Make sure it's assignable + if (!expr_is_ltype(ident)) + { + SEMA_ERROR(ident, "'try' expected an assignable variable or expression here, did you make a mistake?"); + return false; + } + + // 3c. It can't be failable either. + if (ident->failable) + { + if (ident->expr_kind == EXPR_IDENTIFIER) + { + SEMA_ERROR(ident, "This is a failable variable, you should only have non-failable variables on the left side unless you use 'try' without '='."); + } + else + { + SEMA_ERROR(ident, "This is a failable expression, it can't go on the left hand side of a 'try'."); + } + return false; + } + + // 3d. We can now analyse the expression using the variable type. + if (!sema_analyse_expr_of_required_type(context, ident->type, failable, true)) return false; + + expr->try_unwrap_expr.assign_existing = true; + expr->try_unwrap_expr.lhs = ident; + } + else + { + // 4. We are creating a new variable + + // 4a. If we had a variable type, then our expression must be an identifier. + if (ident->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(ident, "A variable name was expected here."); + return false; + } + + if (ident->identifier_expr.path) + { + sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path."); + return false; + } + + TokenId ident_token = ident->identifier_expr.identifier; + + if (TOKTYPE(ident_token) != TOKEN_IDENT) + { + SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); + return false; + } + + // 4a. Type may be assigned or inferred. + Type *type = var_type ? var_type->type : NULL; + + // 4b. Evaluate the expression + if (!sema_analyse_expr_of_required_type(context, type, failable, true)) return false; + + // 4c. Create a type_info if needed. + if (!var_type) + { + var_type = type_info_new_base(failable->type, failable->span); + } + + // 4d. A new declaration is created. + Decl *decl = decl_new_var(ident_token, var_type, VARDECL_LOCAL, VISIBLE_LOCAL); + + // 4e. Analyse it + if (!sema_analyse_local_decl(context, decl)) return false; + + expr->try_unwrap_expr.decl = decl; + } + + if (!failable->failable) + { + SEMA_ERROR(failable, "Expected a failable expression to 'try' here. If it isn't a failable, remove 'try'."); + return false; + } + + expr->try_unwrap_expr.failable = failable; + expr_set_type(expr, type_bool); + expr->resolve_status = RESOLVE_DONE; + return true; +} +static inline bool sema_analyse_try_unwrap_chain(Context *context, Expr *expr) +{ + assert(expr->expr_kind == EXPR_TRY_UNWRAP_CHAIN); + VECEACH(expr->try_unwrap_chain_expr, i) + { + Expr *chain = expr->try_unwrap_chain_expr[i]; + if (chain->expr_kind == EXPR_TRY_UNWRAP) + { + if (!sema_analyse_try_unwrap(context, chain)) return false; + continue; + } + if (!sema_analyse_expr_of_required_type(context, type_bool, chain, false)) return false; + } + expr_set_type(expr, type_bool); + expr->resolve_status = RESOLVE_DONE; + return true; +} +static inline bool sema_analyse_catch_unwrap(Context *context, Expr *expr) +{ + Expr *ident = expr->catch_unwrap_expr.variable; + + bool implicit_declaration = false; + TypeInfo *type = expr->catch_unwrap_expr.type; + + if (!type && !ident) + { + expr->catch_unwrap_expr.lhs = NULL; + expr->catch_unwrap_expr.decl = NULL; + goto RESOLVE_EXPRS; + } + if (!type && ident->expr_kind == EXPR_IDENTIFIER) + { + Decl *decl = sema_resolve_normal_symbol(context, ident->identifier_expr.identifier, NULL, false); + if (!decl) implicit_declaration = true; + } + + if (!type && !implicit_declaration) + { + if (!sema_analyse_expr_value(context, NULL, ident)) return false; + + if (!expr_is_ltype(ident)) + { + SEMA_ERROR(ident, "'catch' expected an assignable variable or expression here, did you make a mistake?"); + return false; + } + + if (ident->type->canonical != type_anyerr) + { + SEMA_ERROR(ident, "Expected the variable to have the type %s, not %s.", type_quoted_error_string(type_anyerr), + type_quoted_error_string(type->type)); + return false; + } + + expr->catch_unwrap_expr.lhs = ident; + expr->catch_unwrap_expr.decl = NULL; + } + else + { + type = type ?: type_info_new_base(type_anyerr, expr->span); + + if (!sema_resolve_type_info(context, type)) return false; + + if (type->type->canonical != type_anyerr) + { + SEMA_ERROR(type, "Expected the type to be %s, not %s.", type_quoted_error_string(type_anyerr), + type_quoted_error_string(type->type)); + return false; + } + if (ident->expr_kind != EXPR_IDENTIFIER) + { + SEMA_ERROR(ident, "A variable name was expected here."); + return false; + } + + if (ident->identifier_expr.path) + { + sema_error_range(ident->identifier_expr.path->span, "The variable may not have a path."); + return false; + } + + TokenId ident_token = ident->identifier_expr.identifier; + + if (TOKTYPE(ident_token) != TOKEN_IDENT) + { + SEMA_ERROR(ident, "Expected a variable starting with a lower case letter."); + return false; + } + + // 4d. A new declaration is created. + Decl *decl = decl_new_var(ident_token, type, VARDECL_LOCAL, VISIBLE_LOCAL); + decl->var.init_expr = expr_new(EXPR_UNDEF, decl->span); + + // 4e. Analyse it + if (!sema_analyse_local_decl(context, decl)) return false; + + expr->catch_unwrap_expr.decl = decl; + expr->catch_unwrap_expr.lhs = NULL; + } +RESOLVE_EXPRS:; + Expr **exprs = expr->catch_unwrap_expr.exprs; + VECEACH(exprs, i) + { + Expr *fail = exprs[i]; + if (!sema_analyse_expr(context, NULL, fail)) return false; + if (!fail->failable) + { + SEMA_ERROR(fail, "This expression is not failable, did you add it by mistake?"); + return false; + } + } + expr_set_type(expr, type_anyerr); + expr->resolve_status = RESOLVE_DONE; + return true; +} + +static void sema_remove_unwraps_from_try(Context *c, Expr *cond) +{ + assert(cond->expr_kind == EXPR_COND); + Expr *last = VECLAST(cond->cond_expr); + if (!last || last->expr_kind != EXPR_TRY_UNWRAP_CHAIN) return; + Expr **chain = last->try_unwrap_chain_expr; + VECEACH(chain, i) + { + Expr *expr = chain[i]; + if (expr->expr_kind != EXPR_TRY_UNWRAP) continue; + if (expr->try_unwrap_expr.assign_existing) continue; + if (expr->try_unwrap_expr.failable) + { + sema_erase_var(c, expr->try_unwrap_expr.decl); + } + else + { + sema_erase_unwrapped(c, expr->try_unwrap_expr.decl); + } + } +} + +static inline bool sema_analyse_last_cond(Context *context, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_TRY_UNWRAP_CHAIN: + return sema_analyse_try_unwrap_chain(context, expr); + case EXPR_CATCH_UNWRAP: + return sema_analyse_catch_unwrap(context, expr); + default: + return sema_analyse_expr(context, NULL, expr); + } +} /** * An decl-expr-list is a list of a mixture of declarations and expressions. * The last declaration or expression is propagated. So for example: @@ -157,11 +415,11 @@ static inline bool sema_analyse_unreachable_stmt(Context *context) * * In this case the final value is 4.0 and the type is float. */ -static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) +static inline bool sema_analyse_cond_list(Context *context, Expr *expr, bool may_unwrap) { - assert(expr->expr_kind == EXPR_DECL_LIST); + assert(expr->expr_kind == EXPR_COND); - Expr **dexprs = expr->dexpr_list_expr; + Expr **dexprs = expr->cond_expr; unsigned entries = vec_size(dexprs); // 1. Special case, there are no entries, so the type is void @@ -172,12 +430,15 @@ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) } // 2. Walk through each of our declarations / expressions as if they were regular expressions. - for (unsigned i = 0; i < entries; i++) + for (unsigned i = 0; i < entries - 1; i++) { if (!sema_analyse_expr(context, NULL, dexprs[i])) return false; } + if (!sema_analyse_last_cond(context, dexprs[entries - 1])) return false; + expr_set_type(expr, dexprs[entries - 1]->type); + expr->resolve_status = RESOLVE_DONE; return true; } @@ -195,10 +456,10 @@ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) */ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_bool) { - assert(expr->expr_kind == EXPR_DECL_LIST && "Conditional expressions should always be of type EXPR_DECL_LIST"); + assert(expr->expr_kind == EXPR_COND && "Conditional expressions should always be of type EXPR_DECL_LIST"); // 1. Analyse the declaration list. - if (!sema_analyse_decl_expr_list(context, expr)) return false; + if (!sema_analyse_cond_list(context, expr, true)) return false; // 2. If we get "void", either through a void call or an empty list, // signal that. @@ -210,7 +471,7 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_ // 3. We look at the last element (which is guaranteed to exist because // the type was not void. - Expr *last = VECLAST(expr->dexpr_list_expr); + Expr *last = VECLAST(expr->cond_expr); if (last->expr_kind == EXPR_DECL) { @@ -285,9 +546,6 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) // 4. Push break / continue - which is independent of the scope. PUSH_BREAKCONT(statement); - // 5. Add any try unwraps. - sema_add_unwraps_from_try(context, cond); - // 6. Analyse the statement success = sema_analyse_statement(context, body); @@ -583,7 +841,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) is_infinite = statement->for_stmt.cond == NULL; if (statement->for_stmt.init) { - success = sema_analyse_decl_expr_list(context, statement->for_stmt.init); + success = sema_analyse_cond_list(context, statement->for_stmt.init, false); } if (success && statement->for_stmt.cond) @@ -808,13 +1066,13 @@ static bool sema_rewrite_foreach_to_for(Context *context, Ast *statement, Expr * iterator->var.init_expr = enumerator; // Generate Foo *value, FooIterator ".iterator" = foo.iterator(); - Expr *init_expr = expr_new(EXPR_DECL_LIST, enumerator->span); + Expr *init_expr = expr_new(EXPR_COND, enumerator->span); Expr *expr = expr_new(EXPR_DECL, value->span); expr->decl_expr = value; - vec_add(init_expr->dexpr_list_expr, expr); + vec_add(init_expr->cond_expr, expr); expr = expr_new(EXPR_DECL, enumerator->span); expr->decl_expr = iterator; - vec_add(init_expr->dexpr_list_expr, expr); + vec_add(init_expr->cond_expr, expr); init_expr->resolve_status = RESOLVE_DONE; expr_set_type(init_expr, iterator->type); @@ -1095,8 +1353,6 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) } SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); - - sema_add_unwraps_from_try(context, statement->if_stmt.cond); success = success && sema_analyse_statement(context, statement->if_stmt.then_body); then_jump = context->active_scope.jump_end; @@ -1106,8 +1362,8 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) if (statement->if_stmt.else_body) { SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label); - Decl *possible_unwrap = sema_find_unwrappable_from_catch(context, statement->if_stmt.cond); - if (possible_unwrap) sema_unwrap_var(context, possible_unwrap); + sema_remove_unwraps_from_try(context, statement->if_stmt.cond); + sema_unwrappable_from_catch_in_else(context, statement->if_stmt.cond); success = success && sema_analyse_statement(context, statement->if_stmt.else_body); else_jump = context->active_scope.jump_end; SCOPE_END; @@ -1118,8 +1374,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) SCOPE_OUTER_END; if (then_jump) { - Decl *possible_unwrap = sema_find_unwrappable_from_catch(context, statement->if_stmt.cond); - if (possible_unwrap) sema_unwrap_var(context, possible_unwrap); + sema_unwrappable_from_catch_in_else(context, statement->if_stmt.cond); } if (then_jump && else_jump && !statement->flow.has_break) { @@ -1551,7 +1806,7 @@ static bool sema_analyse_switch_body(Context *context, Ast *statement, SourceSpa switch (switch_type_flattened->type_kind) { case TYPE_TYPEID: - case TYPE_ERR_UNION: + case TYPE_ANYERR: use_type_id = true; break; case ALL_INTS: @@ -1735,7 +1990,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) if (!sema_analyse_cond(context, cond, false)) return false; - Type *switch_type = VECLAST(cond->dexpr_list_expr)->type->canonical; + Type *switch_type = VECLAST(cond->cond_expr)->type->canonical; statement->switch_stmt.defer = context->active_scope.defer_last; if (!sema_analyse_switch_body(context, statement, cond->span, switch_type->canonical, @@ -1806,7 +2061,7 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement) const char *error_type = type_to_error_string(error_expr->type); if (error_expr->expr_kind == EXPR_IDENTIFIER && error_expr->identifier_expr.decl->decl_kind == DECL_VAR - && error_expr->identifier_expr.decl->var.kind == VARDECL_ALIAS) + && error_expr->identifier_expr.decl->var.kind == VARDECL_UNWRAPPED) { SEMA_ERROR(error_expr, "'%s' is unwrapped to '%s' here, so it cannot be caught.", @@ -1853,7 +2108,7 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement) if (unwrapped && !statement->flow.has_break && statement->flow.no_exit) { Decl *decl = decl_copy(unwrapped); - decl->var.kind = VARDECL_ALIAS; + decl->var.kind = VARDECL_UNWRAPPED; decl->var.alias = unwrapped; decl->var.failable = false; sema_unwrap_var(context, decl); @@ -1864,9 +2119,9 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement) static bool sema_analyse_try_stmt(Context *context, Ast *stmt) { - assert(stmt->try_old_stmt.decl_expr->expr_kind == EXPR_DECL_LIST); + assert(stmt->try_old_stmt.decl_expr->expr_kind == EXPR_COND); - Expr **dexprs = stmt->try_old_stmt.decl_expr->dexpr_list_expr; + Expr **dexprs = stmt->try_old_stmt.decl_expr->cond_expr; TODO /*TODO SCOPE_START @@ -2109,11 +2364,11 @@ static bool sema_analyse_requires(Context *context, Ast *docs, Ast ***asserts) return false; } } - assert(declexpr->expr_kind == EXPR_DECL_LIST); + assert(declexpr->expr_kind == EXPR_COND); - VECEACH(declexpr->dexpr_list_expr, j) + VECEACH(declexpr->cond_expr, j) { - Expr *expr = declexpr->dexpr_list_expr[j]; + Expr *expr = declexpr->cond_expr[j]; if (expr->expr_kind == EXPR_DECL) { SEMA_ERROR(expr, "Only expressions are allowed."); diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index 63389c576..0349b417b 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -92,8 +92,9 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) switch (decl->decl_kind) { case DECL_STRUCT: + case DECL_BITSTRUCT: case DECL_UNION: - case DECL_ERR: + case DECL_ERRTYPE: case DECL_ENUM: case DECL_TYPEDEF: case DECL_DISTINCT: @@ -115,6 +116,7 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info) } FALLTHROUGH; case DECL_FUNC: + case DECL_ERRVALUE: case DECL_ENUM_CONSTANT: case DECL_ARRAY_VALUE: case DECL_IMPORT: @@ -150,18 +152,19 @@ bool sema_resolve_type(Context *context, Type *type) case TYPE_BOOL: case TYPE_TYPEID: case TYPE_VIRTUAL_ANY: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_STRLIT: case TYPE_VECTOR: return true; case TYPE_POINTER: return sema_resolve_type(context, type->pointer); + case TYPE_BITSTRUCT: + case TYPE_DISTINCT: case TYPE_ENUM: + case TYPE_ERRTYPE: case TYPE_FUNC: case TYPE_STRUCT: case TYPE_UNION: - case TYPE_ERRTYPE: - case TYPE_DISTINCT: break; case TYPE_ARRAY: case TYPE_SUBARRAY: diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d05f1c62b..086c94964 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -149,6 +149,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_VECCALL] = KW_DEF("veccall"); attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("regcall"); attribute_list[ATTRIBUTE_FASTCALL] = KW_DEF("fastcall"); + attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("overlap"); } static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash) diff --git a/src/compiler/target.c b/src/compiler/target.c index 1ff795ca0..313f3ab82 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -9,8 +9,8 @@ static ArchType arch_from_llvm_string(StringSlice string); static EnvironmentType environment_type_from_llvm_string(StringSlice string); static bool arch_is_supported(ArchType arch); static unsigned os_target_c_type_bits(OsType os, ArchType arch, CType type); -static unsigned os_target_alignment_of_int(OsType os, ArchType arch, int bits); -static unsigned os_target_alignment_of_float(OsType os, ArchType arch, int bits); +static AlignData os_target_alignment_of_int(OsType os, ArchType arch, int bits); +static AlignData os_target_alignment_of_float(OsType os, ArchType arch, int bits); static OsType os_from_llvm_string(StringSlice string); static VendorType vendor_from_llvm_string(StringSlice string); static ObjectFormatType object_format_from_os(OsType os); @@ -208,20 +208,60 @@ static inline bool os_is_apple(OsType os_type) os_type == OS_TYPE_MACOSX || os_type == OS_TYPE_IOS; } -static AlignSize os_arch_max_alignment_of_vector(OsType os, ArchType arch) +static AlignSize os_arch_max_alignment_of_vector(OsType os, ArchType arch, EnvironmentType type, ARMVariant variant) { switch (arch) { case ARCH_TYPE_AARCH64: - return 128 / 8; + // aarch64 uses 128 bits. + // See Clang: AArch64TargetInfo::AArch64TargetInfo + return 16; case ARCH_TYPE_ARM: case ARCH_TYPE_ARMB: - // Only if aapcs and not android - REMINDER("Check if AAPCS"); - return 64 / 8; + if (type == ENV_TYPE_ANDROID) return 0; + switch (variant) + { + case ARM_AAPCS: + case ARM_AAPCS_LINUX: + return 8; // AAPCS (not AAPCS16!) uses 64 bits + case ARM_AAPCS16: + case ARM_APCS_GNU: + break; + } + break; + case ARCH_TYPE_X86: + if (os == OS_TYPE_WIN32) /* COFF */ + { + return 8192; + } + if (os_is_apple(os)) + { + // With AVX512 - 512, AVX - 256 otherwise AVX - 128 + return 256; + } + break; default: - return 0; + break; } + // No max alignment default. + return 0; +} + +static AlignSize os_arch_max_alignment_of_tls(OsType os, ArchType arch, EnvironmentType type) +{ + switch (arch) + { + case ARCH_TYPE_X86: + if (os == OS_TYPE_WIN32) /* COFF */ + { + return 8192; + } + break; + default: + break; + } + // No max alignment default. + return 0; } static bool os_target_signed_c_char_type(OsType os, ArchType arch) @@ -398,6 +438,18 @@ static inline void target_setup_x64_abi(BuildTarget *target) { platform_target.x64.pass_int128_vector_in_mem = true; } + switch (platform_target.x64.avx_level) + { + case AVX_NONE: + platform_target.x64.align_simd_default = 128; + break; + case AVX: + platform_target.x64.align_simd_default = 256; + break; + case AVX_512: + platform_target.x64.align_simd_default = 512; + break; + } } static char *arch_to_target_triple[ARCH_OS_TARGET_LAST + 1] = { @@ -684,7 +736,10 @@ static unsigned os_target_supports_float16(OsType os, ArchType arch) { case ARCH_TYPE_AARCH64: return true; + case ARCH_TYPE_ARM: + // Supported on fullfp16 and mve.fp in Clang default: + // Supported by AMDGPU return false; } } @@ -845,7 +900,17 @@ static unsigned os_target_c_type_bits(OsType os, ArchType arch, CType type) } -static unsigned os_target_alignment_of_int(OsType os, ArchType arch, int bits) +typedef enum { + MACHO_MANGLING, + ELF_MANGLING, + MIPS_MANGLING, + WIN86_MANGLING, + WIN_MANGLING, + XCOFF_MANGLING +} Mangling; + + +static AlignData os_target_alignment_of_int(OsType os, ArchType arch, int bits) { switch (arch) { @@ -854,36 +919,34 @@ static unsigned os_target_alignment_of_int(OsType os, ArchType arch, int bits) UNREACHABLE case ARCH_TYPE_ARM: case ARCH_TYPE_THUMB: - if ((os_is_apple(os) || os == OS_TYPE_NETBSD) && bits > 32) return 4; - return bits > 64 ? 8 : bits / 8; case ARCH_TYPE_ARMB: case ARCH_TYPE_THUMBEB: - if (os == OS_TYPE_NETBSD && bits > 32) return 4; - return bits > 64 ? 8 : bits / 8; + if ((os_is_apple(os) || os == OS_TYPE_NETBSD) && bits > 32) return (AlignData) { 32, MIN(64, bits) }; + return (AlignData) { MIN(64, bits), MIN(64, bits) }; case ARCH_TYPE_PPC64: case ARCH_TYPE_PPC: case ARCH_TYPE_PPC64LE: - case ARCH_TYPE_RISCV32: case ARCH_TYPE_X86_64: - case ARCH_TYPE_WASM32: case ARCH_TYPE_WASM64: - return bits > 64 ? 8 : bits / 8; - return bits > 32 ? 4 : bits / 8; + case ARCH_TYPE_RISCV32: + case ARCH_TYPE_WASM32: + return (AlignData) { MIN(64, bits), MIN(64, bits) }; + case ARCH_TYPE_RISCV64: + return (AlignData) { bits, bits }; case ARCH_TYPE_AARCH64: case ARCH_TYPE_AARCH64_BE: - case ARCH_TYPE_RISCV64: - return bits / 8; + if (bits < 32 && !os_is_apple(os) && os != OS_TYPE_WIN32) return (AlignData){ bits, 32 }; + return (AlignData) { bits, bits }; case ARCH_TYPE_X86: - if (bits >= 64) - { - return (os == OS_TYPE_WIN32 || os == OS_TYPE_NACL) ? 8 : 4; - } - return bits / 8; + if (bits < 32) return (AlignData) { bits, bits }; + if (os == OS_TYPE_ELFIAMCU) return (AlignData) { 32, 32 }; + if (os == OS_TYPE_WIN32 || os == OS_TYPE_NACL) return (AlignData) { 64, 64 }; + return (AlignData) { 32, 64 }; } UNREACHABLE } -static unsigned arch_little_endian(ArchType arch) +static unsigned arch_big_endian(ArchType arch) { switch (arch) { @@ -898,53 +961,21 @@ static unsigned arch_little_endian(ArchType arch) case ARCH_TYPE_RISCV64: case ARCH_TYPE_WASM32: case ARCH_TYPE_WASM64: - return true; - case ARCH_TYPE_ARMB: - case ARCH_TYPE_THUMBEB: - case ARCH_TYPE_AARCH64_BE: - case ARCH_TYPE_PPC64: - case ARCH_TYPE_PPC: return false; - case ARCH_UNSUPPORTED: - UNREACHABLE - } - UNREACHABLE -} - -static unsigned os_target_pref_alignment_of_int(OsType os, ArchType arch, int bits) -{ - switch (arch) - { - case ARCH_TYPE_UNKNOWN: - case ARCH_UNSUPPORTED: - UNREACHABLE - case ARCH_TYPE_X86: - if (os == OS_TYPE_ELFIAMCU && bits > 32) return 4; - return bits > 64 ? 8 : bits / 8; - case ARCH_TYPE_AARCH64: - if (bits < 32 && !os_is_apple(os) && os != OS_TYPE_WIN32) return 4; - return bits / 8; - case ARCH_TYPE_AARCH64_BE: - return bits < 32 ? 4 : bits / 8; - case ARCH_TYPE_ARM: case ARCH_TYPE_ARMB: - case ARCH_TYPE_PPC: - case ARCH_TYPE_PPC64LE: - case ARCH_TYPE_PPC64: - case ARCH_TYPE_RISCV32: - case ARCH_TYPE_THUMB: case ARCH_TYPE_THUMBEB: - case ARCH_TYPE_X86_64: - case ARCH_TYPE_WASM32: - case ARCH_TYPE_WASM64: - return bits < 64 ? bits / 8 : 8; - case ARCH_TYPE_RISCV64: - return bits / 8; + case ARCH_TYPE_AARCH64_BE: + case ARCH_TYPE_PPC64: + case ARCH_TYPE_PPC: + return true; + case ARCH_UNSUPPORTED: + UNREACHABLE } UNREACHABLE } -static unsigned os_target_alignment_of_float(OsType os, ArchType arch, int bits) + +static AlignData os_target_alignment_of_float(OsType os, ArchType arch, int bits) { switch (arch) { @@ -952,12 +983,12 @@ static unsigned os_target_alignment_of_float(OsType os, ArchType arch, int bits) case ARCH_UNSUPPORTED: UNREACHABLE case ARCH_TYPE_X86: - if (os == OS_TYPE_ELFIAMCU && bits >= 32) return 4; + if (os == OS_TYPE_ELFIAMCU && bits >= 32) return (AlignData) { 32, 32 }; if (os == OS_TYPE_WIN32 || os == OS_TYPE_NACL) { - return bits / 8; + return (AlignData) { bits, bits }; } - return bits == 64 ? 4 : bits / 8; + return (AlignData) { MIN(32, bits), bits }; case ARCH_TYPE_AARCH64: case ARCH_TYPE_AARCH64_BE: case ARCH_TYPE_PPC64: @@ -967,24 +998,19 @@ static unsigned os_target_alignment_of_float(OsType os, ArchType arch, int bits) case ARCH_TYPE_RISCV64: case ARCH_TYPE_WASM32: case ARCH_TYPE_WASM64: - return bits / 8; + return (AlignData) { bits , bits }; case ARCH_TYPE_ARM: case ARCH_TYPE_THUMB: - if ((os_is_apple(os) || os == OS_TYPE_NETBSD) && bits == 64) - { - return 4; - } - return bits / 8; case ARCH_TYPE_THUMBEB: case ARCH_TYPE_ARMB: - if (os == OS_TYPE_NETBSD && bits == 64) + if ((os_is_apple(os) || os == OS_TYPE_NETBSD) && bits == 64) { - return 4; + return (AlignData) { 32, bits }; } - return bits / 8; + return (AlignData) { bits , bits }; case ARCH_TYPE_X86_64: - if (bits == 128 && os == OS_TYPE_ELFIAMCU) return 4; - return bits / 8; + if (bits == 128 && os == OS_TYPE_ELFIAMCU) return (AlignData) { 32, 32 }; + return (AlignData) { bits , bits }; } UNREACHABLE } @@ -1101,6 +1127,7 @@ static unsigned os_target_pref_alignment_of_float(OsType os, ArchType arch, int UNREACHABLE } + void *llvm_target_machine_create(void) { char *err = NULL; @@ -1218,29 +1245,14 @@ void target_setup(BuildTarget *target) platform_target.environment_type = environment_type_from_llvm_string(target_triple_string); platform_target.float_abi = false; - platform_target.little_endian = arch_little_endian(platform_target.arch); + // TLS should not be supported for: + // ARM Cygwin + // NVPTX + platform_target.tls_supported = true; + platform_target.big_endian = arch_big_endian(platform_target.arch); platform_target.width_pointer = arch_pointer_bit_width(platform_target.os, platform_target.arch); platform_target.alloca_address_space = 0; - // Todo PIC or no PIC depending on architecture. - switch (platform_target.arch) - { - case ARCH_TYPE_X86_64: - case ARCH_TYPE_X86: - case ARCH_TYPE_PPC64LE: - case ARCH_TYPE_ARM: - case ARCH_TYPE_RISCV32: - case ARCH_TYPE_RISCV64: - case ARCH_TYPE_AARCH64: - platform_target.max_size_for_return = platform_target.width_pointer * 2 / 8; - break; - case ARCH_TYPE_PPC64: - platform_target.max_size_for_return = 0; - break; - default: - FATAL_ERROR("Unsupported architecture."); - } - platform_target.object_format = object_format_from_os(platform_target.os); platform_target.int128 = os_target_supports_int128(platform_target.os, platform_target.arch); @@ -1250,23 +1262,20 @@ void target_setup(BuildTarget *target) platform_target.vec64i = os_target_supports_vec(platform_target.os, platform_target.arch, 64, true); platform_target.float128 = os_target_supports_float128(platform_target.os, platform_target.arch); platform_target.float16 = os_target_supports_float16(platform_target.os, platform_target.arch); - platform_target.align_byte = os_target_alignment_of_int(platform_target.os, platform_target.arch, 8); - platform_target.align_short = os_target_alignment_of_int(platform_target.os, platform_target.arch, 16); - platform_target.align_int = os_target_alignment_of_int(platform_target.os, platform_target.arch, 32); - platform_target.align_long = os_target_alignment_of_int(platform_target.os, platform_target.arch, 64); - platform_target.align_i128 = os_target_alignment_of_int(platform_target.os, platform_target.arch, 128); - platform_target.align_half = os_target_alignment_of_float(platform_target.os, platform_target.arch, 16); - platform_target.align_float = os_target_alignment_of_float(platform_target.os, platform_target.arch, 32); - platform_target.align_double = os_target_alignment_of_float(platform_target.os, platform_target.arch, 64); - platform_target.align_f128 = os_target_alignment_of_float(platform_target.os, platform_target.arch, 128); - platform_target.align_int = os_target_alignment_of_int(platform_target.os, platform_target.arch, 32); - platform_target.align_pointer = platform_target.width_pointer / 8; + for (BitSizes i = BITS8; i < BITSIZES_LEN; i++) + { + unsigned bits = 8 << (i - 1); + platform_target.integers[i] = os_target_alignment_of_int(platform_target.os, platform_target.arch, bits); + platform_target.floats[i] = os_target_alignment_of_float(platform_target.os, platform_target.arch, bits); + } + platform_target.integers[BIT1] = platform_target.integers[BITS8]; + + platform_target.align_pointer = (AlignData) { platform_target.width_pointer, platform_target.width_pointer }; platform_target.width_c_short = os_target_c_type_bits(platform_target.os, platform_target.arch, CTYPE_SHORT); platform_target.width_c_int = os_target_c_type_bits(platform_target.os, platform_target.arch, CTYPE_INT); platform_target.width_c_long = os_target_c_type_bits(platform_target.os, platform_target.arch, CTYPE_LONG); platform_target.width_c_long_long = os_target_c_type_bits(platform_target.os, platform_target.arch, CTYPE_LONG_LONG); platform_target.signed_c_char = os_target_signed_c_char_type(platform_target.os, platform_target.arch); - platform_target.align_max_vector = os_arch_max_alignment_of_vector(platform_target.os, platform_target.arch); /** * x86-64: CMOV, CMPXCHG8B, FPU, FXSR, MMX, FXSR, SCE, SSE, SSE2 * x86-64-v2: (close to Nehalem) CMPXCHG16B, LAHF-SAHF, POPCNT, SSE3, SSE4.1, SSE4.2, SSSE3 @@ -1336,7 +1345,10 @@ void target_setup(BuildTarget *target) platform_target.abi = ABI_UNKNOWN; break; } - + platform_target.align_max_vector = os_arch_max_alignment_of_vector(platform_target.os, platform_target.arch, platform_target.environment_type, platform_target.arm.variant); + platform_target.align_max_tls = os_arch_max_alignment_of_tls(platform_target.os, + platform_target.arch, + platform_target.environment_type); platform_target.pic = arch_os_pic_default(platform_target.arch, platform_target.os); platform_target.pie = arch_os_pie_default(platform_target.arch, platform_target.os, platform_target.environment_type); platform_target.pic_required = arch_os_pic_default_forced(platform_target.arch, platform_target.os); diff --git a/src/compiler/target.h b/src/compiler/target.h index 4b0eb04a9..6227fc69a 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -234,6 +234,24 @@ typedef enum ARM_ABI_AAPCS_VFP, } ARMABIVariant; +typedef struct +{ + unsigned align; + unsigned pref_align; +} AlignData; + +typedef enum +{ + BIT1, + BITS8, + BITS16, + BITS32, + BITS64, + BITS128, + BITS256, + BITSIZES_LEN +} BitSizes; + typedef struct { const char *target_triple; @@ -247,6 +265,8 @@ typedef struct ObjectFormatType object_format; int alloca_address_space; ABI abi; + AlignData integers[BITSIZES_LEN]; + AlignData floats[BITSIZES_LEN]; PicGeneration pic : 3; PieGeneration pie : 3; bool pic_required : 1; @@ -266,6 +286,7 @@ typedef struct } x86; struct { + unsigned align_simd_default : 16; AVXLevel avx_level : 3; bool no_mmx : 1; bool no_sse : 1; @@ -314,7 +335,7 @@ typedef struct bool has_vector : 1; } systemz; }; - bool little_endian; + bool big_endian; bool tls_supported; bool asm_supported; bool float128; @@ -324,34 +345,9 @@ typedef struct bool vec128f; bool vec64f; bool int128; - unsigned align_pref_pointer; - unsigned align_pref_byte; - unsigned align_pref_short; - unsigned align_pref_int; - unsigned align_pref_long; - unsigned align_pref_i128; - 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; - unsigned align_int; - unsigned align_long; - unsigned align_i128; - unsigned align_half; - unsigned align_float; - unsigned align_double; - unsigned align_f128; - unsigned align_c_long_double; - unsigned align_c_int; - unsigned align_c_long; - unsigned align_c_long_long; - unsigned align_simd_default; + AlignData align_pointer; unsigned align_max_vector; - unsigned align_global_min; - unsigned align_new; + unsigned align_max_tls; unsigned align_large_array; unsigned width_pointer; unsigned width_c_short; @@ -366,8 +362,8 @@ typedef struct unsigned sse_reg_param_max; unsigned builtin_ms_valist; unsigned aarch64sve_types; - unsigned max_size_for_return; char *platform_name; + // MinGlobalAlign is used by Clang for SystemZ and Lanai targets. } PlatformTarget; diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index c59a3d3c3..a24c76ebd 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -186,6 +186,8 @@ const char *token_type_to_string(TokenType type) return "assert"; case TOKEN_ATTRIBUTE: return "attribute"; + case TOKEN_BITSTRUCT: + return "bitstruct"; case TOKEN_BREAK: return "break"; case TOKEN_CASE: @@ -386,3 +388,9 @@ bool token_is_any_type(TokenType type) { return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT || type == TOKEN_VIRTUAL; } + +bool token_is_ident_keyword(TokenType type) +{ + if (token_is_type(type)) return true; + return type >= TOKEN_ALIAS && type <= TOKEN_WHILE; +} diff --git a/src/compiler/types.c b/src/compiler/types.c index 7135b9b7d..990f8ba1f 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -114,6 +114,7 @@ const char *type_to_error_string(Type *type) case TYPE_POISONED: return "poisoned"; case TYPE_ENUM: + case TYPE_ERRTYPE: case TYPE_TYPEDEF: case TYPE_STRUCT: case TYPE_VOID: @@ -121,10 +122,11 @@ const char *type_to_error_string(Type *type) case ALL_INTS: case ALL_FLOATS: case TYPE_UNION: - case TYPE_ERRTYPE: case TYPE_DISTINCT: + case TYPE_BITSTRUCT: case TYPE_VIRTUAL_ANY: case TYPE_VIRTUAL: + case TYPE_ANYERR: return type->name; case TYPE_FUNC: return strcat_arena("func ", type->func.mangled_function_signature); @@ -153,8 +155,6 @@ const char *type_to_error_string(Type *type) case TYPE_SUBARRAY: asprintf(&buffer, "%s[]", type_to_error_string(type->array.base)); return buffer; - case TYPE_ERR_UNION: - return "error"; } UNREACHABLE } @@ -189,6 +189,8 @@ ByteSize type_size(Type *type) { switch (type->type_kind) { + case TYPE_BITSTRUCT: + return type_size(type->decl->bitstruct.base_type->type); case TYPE_DISTINCT: return type_size(type->decl->distinct_decl.base_type); case TYPE_VECTOR: @@ -207,10 +209,10 @@ ByteSize type_size(Type *type) UNREACHABLE; case TYPE_TYPEDEF: return type_size(type->canonical); - case TYPE_ENUM: - return type->decl->enums.type_info->type->canonical->builtin.bytesize; case TYPE_ERRTYPE: return type_size(type_usize->canonical); + case TYPE_ENUM: + return type->decl->enums.type_info->type->canonical->builtin.bytesize; case TYPE_STRUCT: case TYPE_UNION: assert(type->decl->resolve_status == RESOLVE_DONE); @@ -221,7 +223,7 @@ ByteSize type_size(Type *type) case TYPE_TYPEID: case ALL_INTS: case ALL_FLOATS: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_VIRTUAL_ANY: return type->builtin.bytesize; case TYPE_VIRTUAL: @@ -340,6 +342,8 @@ bool type_is_abi_aggregate(Type *type) return type_is_abi_aggregate(type->decl->distinct_decl.base_type); case TYPE_TYPEDEF: return type_is_abi_aggregate(type->canonical); + case TYPE_BITSTRUCT: + return type_is_abi_aggregate(type->decl->bitstruct.base_type->type); case ALL_FLOATS: case TYPE_VOID: case ALL_INTS: @@ -350,13 +354,13 @@ bool type_is_abi_aggregate(Type *type) case TYPE_FUNC: case TYPE_STRLIT: case TYPE_VECTOR: - return false; + case TYPE_ANYERR: case TYPE_ERRTYPE: + return false; case TYPE_STRUCT: case TYPE_UNION: case TYPE_SUBARRAY: case TYPE_ARRAY: - case TYPE_ERR_UNION: case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: return true; @@ -495,6 +499,9 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; goto RETRY; + case TYPE_BITSTRUCT: + type = type->decl->bitstruct.base_type->type; + goto RETRY; case TYPE_FXX: case TYPE_POISONED: case TYPE_IXX: @@ -506,18 +513,15 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) case TYPE_SUBARRAY: case TYPE_INFERRED_ARRAY: return false; - case TYPE_ERR_UNION: - DEBUG_LOG("Should error be passed as homogenous aggregate?"); - FALLTHROUGH; case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: *base = type_iptr->canonical; *elements = 2; return true; + case TYPE_ANYERR: case TYPE_ERRTYPE: - *base = type_iptr->canonical; - *elements = 1; - return true; + type = type_iptr; + goto RETRY; case TYPE_TYPEDEF: type = type->canonical; goto RETRY; @@ -653,6 +657,8 @@ AlignSize type_abi_alignment(Type *type) case TYPE_TYPEINFO: case TYPE_INFERRED_ARRAY: UNREACHABLE; + case TYPE_BITSTRUCT: + return type_abi_alignment(type->decl->bitstruct.base_type->type); case TYPE_VECTOR: { ByteSize width = type_size(type->vector.base) * type->vector.len; @@ -673,7 +679,7 @@ AlignSize type_abi_alignment(Type *type) case TYPE_ENUM: return type->decl->enums.type_info->type->canonical->builtin.abi_alignment; case TYPE_ERRTYPE: - return t.usz.canonical->builtin.abi_alignment; + return t.iptr.canonical->builtin.abi_alignment; case TYPE_STRUCT: case TYPE_UNION: return type->decl->alignment; @@ -682,15 +688,15 @@ AlignSize type_abi_alignment(Type *type) case TYPE_BOOL: case ALL_INTS: case ALL_FLOATS: - case TYPE_ERR_UNION: case TYPE_VIRTUAL_ANY: + case TYPE_ANYERR: return type->builtin.abi_alignment; case TYPE_VIRTUAL: return type_virtual_generic->builtin.abi_alignment; case TYPE_FUNC: case TYPE_POINTER: case TYPE_STRLIT: - return t.usz.canonical->builtin.abi_alignment; + return t.iptr.canonical->builtin.abi_alignment; case TYPE_ARRAY: return type_abi_alignment(type->array.base); case TYPE_SUBARRAY: @@ -997,9 +1003,11 @@ Type *type_get_vector(Type *vector_type, unsigned len) static void type_create(const char *name, Type *location, TypeKind kind, unsigned bitsize, unsigned align, unsigned pref_align) { + assert(align); + unsigned byte_size = (bitsize + 7) / 8; *location = (Type) { .type_kind = kind, - .builtin.bytesize = (bitsize + 7) / 8, + .builtin.bytesize = byte_size, .builtin.bitsize = bitsize, .builtin.abi_alignment = align, .builtin.pref_alignment = pref_align ?: align, @@ -1011,6 +1019,24 @@ static void type_create(const char *name, Type *location, TypeKind kind, unsigne global_context_add_type(location); } +static void type_init(const char *name, Type *location, TypeKind kind, unsigned bitsize, AlignData align) +{ + assert(align.align); + unsigned byte_size = (bitsize + 7) / 8; + *location = (Type) { + .type_kind = kind, + .builtin.bytesize = byte_size, + .builtin.bitsize = bitsize, + .builtin.abi_alignment = align.align / 8, + .builtin.pref_alignment = (align.pref_align ?: align.align) / 8, + .name = name, + .canonical = location, + }; + location->name = name; + location->canonical = location; + global_context_add_type(location); +} + static void type_create_alias(const char *name, Type *location, Type *canonical) { *location = (Type) { @@ -1035,6 +1061,7 @@ static void type_append_name_to_scratch(Type *type) case TYPE_STRUCT: case TYPE_UNION: case TYPE_DISTINCT: + case TYPE_BITSTRUCT: scratch_buffer_append(type->decl->external_name); break; case TYPE_POINTER: @@ -1061,7 +1088,7 @@ static void type_append_name_to_scratch(Type *type) case TYPE_F64: case TYPE_F128: case TYPE_TYPEID: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_VIRTUAL_ANY: case TYPE_VECTOR: scratch_buffer_append(type->name); @@ -1121,51 +1148,59 @@ Type *type_find_function_type(FunctionSignature *signature) return func_type; } +static inline void type_init_int(const char *name, Type *type, TypeKind kind, BitSizes bits) +{ + int actual_bits = bits ? 8 << (bits - 1) : 1; + type_init(name, type, kind, actual_bits, platform_target.integers[bits]); +} + +static inline void type_create_float(const char *name, Type *type, TypeKind kind, BitSizes bits) +{ + int actual_bits = bits ? 8 << (bits - 1) : 1; + type_init(name, type, kind, actual_bits, platform_target.floats[bits]); +} void type_setup(PlatformTarget *target) { stable_init(&function_types, 0x1000); max_alignment_vector = target->align_max_vector; - /*TODO - * decl_string = (Decl) { .decl_kind = DECL_BUILTIN, .name.string = "string" }; - create_type(&decl_string, &type_string); - type_string.type_kind = TYPE_STRING; -*/ -#define DEF_TYPE(name_, shortname_, type_, bits_, aligned_) \ -type_create(#name_, &(shortname_), type_, bits_, target->align_ ## aligned_, target->align_pref_ ## aligned_) - DEF_TYPE(bool, t.u1, TYPE_BOOL, 1, byte); - DEF_TYPE(float, t.f32, TYPE_F32, 32, float); - DEF_TYPE(double, t.f64, TYPE_F64, 64, double); + type_create_float("float16", &t.f16, TYPE_F16, BITS16); + type_create_float("float", &t.f32, TYPE_F32, BITS32); + type_create_float("double", &t.f64, TYPE_F64, BITS64); + type_create_float("float128", &t.f128, TYPE_F128, BITS128); - DEF_TYPE(ichar, t.i8, TYPE_I8, 8, byte); - DEF_TYPE(short, t.i16, TYPE_I16, 16, short); - DEF_TYPE(int, t.i32, TYPE_I32, 32, int); - DEF_TYPE(long, t.i64, TYPE_I64, 64, long); - DEF_TYPE(i128, t.i128, TYPE_I128, 128, i128); - DEF_TYPE(char, t.u8, TYPE_U8, 8, byte); - DEF_TYPE(ushort, t.u16, TYPE_U16, 16, short); - DEF_TYPE(uint, t.u32, TYPE_U32, 32, int); - DEF_TYPE(ulong, t.u64, TYPE_U64, 64, long); - DEF_TYPE(u128, t.u128, TYPE_U128, 128, i128); + type_init_int("ichar", &t.i8, TYPE_I8, BITS8); + type_init_int("short", &t.i16, TYPE_I16, BITS16); + type_init_int("int", &t.i32, TYPE_I32, BITS32); + type_init_int("long", &t.i64, TYPE_I64, BITS64); + type_init_int("int128", &t.i128, TYPE_I128, BITS128); - DEF_TYPE(void, t.u0, TYPE_VOID, 8, byte); - DEF_TYPE(string, t.str, TYPE_STRLIT, target->width_pointer, pointer); + type_init_int("bool", &t.u1, TYPE_BOOL, BITS8); + type_init_int("char", &t.u8, TYPE_U8, BITS8); + type_init_int("ushort", &t.u16, TYPE_U16, BITS16); + type_init_int("uint", &t.u32, TYPE_U32, BITS32); + type_init_int("ulong", &t.u64, TYPE_U64, BITS64); + type_init_int("uint128", &t.u128, TYPE_U128, BITS128); -#undef DEF_TYPE + type_init_int("void", &t.u0, TYPE_VOID, BITS8); - type_create("typeinfo", &t.typeinfo, TYPE_TYPEINFO, 0, 0, 0); - type_create("typeid", &t.typeid, TYPE_TYPEID, target->width_pointer, target->align_pointer, target->align_pref_pointer); - type_create("void*", &t.voidstar, TYPE_POINTER, target->width_pointer, target->align_pointer, target->align_pref_pointer); + type_init("string", &t.str, TYPE_STRLIT, target->width_pointer, target->align_pointer); + + + type_create("typeinfo", &t.typeinfo, TYPE_TYPEINFO, 1, 1, 1); + type_init("typeid", &t.typeid, TYPE_TYPEID, target->width_pointer, target->align_pointer); + + type_init("void*", &t.voidstar, TYPE_POINTER, target->width_pointer, target->align_pointer); create_type_cache(type_void); type_void->type_cache[0] = &t.voidstar; t.voidstar.pointer = type_void; - type_create("virtual*", &t.virtual, TYPE_VIRTUAL_ANY, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); - type_create("virtual_generic", &t.virtual_generic, TYPE_VIRTUAL, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); + type_init("virtual*", &t.virtual, TYPE_VIRTUAL_ANY, target->width_pointer * 2, target->align_pointer); + type_init("virtual_generic", &t.virtual_generic, TYPE_VIRTUAL, target->width_pointer * 2, target->align_pointer); - type_create("compint", &t.ixx, TYPE_IXX, 0, 0, 0); - type_create("compfloat", &t.fxx, TYPE_FXX, 0, 0, 0); + type_create("compint", &t.ixx, TYPE_IXX, 1, 1, 1); + type_create("compfloat", &t.fxx, TYPE_FXX, 1, 1, 1); type_create_alias("usize", &t.usz, type_int_unsigned_by_bitsize(target->width_pointer)); type_create_alias("isize", &t.isz, type_int_signed_by_bitsize(target->width_pointer)); @@ -1178,7 +1213,7 @@ type_create(#name_, &(shortname_), type_, bits_, target->align_ ## aligned_, tar alignment_subarray = MAX(type_abi_alignment(&t.voidstar), type_abi_alignment(t.usz.canonical)); size_subarray = alignment_subarray * 2; - type_create("anyerr", &t.anyerr, TYPE_ERR_UNION, target->width_pointer * 2, target->align_pointer, target->align_pref_pointer); + type_init("anyerr", &t.anyerr, TYPE_ANYERR, target->width_pointer, target->align_pointer); } bool type_is_scalar(Type *type) @@ -1206,10 +1241,13 @@ bool type_is_scalar(Type *type) case TYPE_POINTER: case TYPE_ENUM: case TYPE_ERRTYPE: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: return true; + case TYPE_BITSTRUCT: + type = type->decl->bitstruct.base_type->type; + goto RETRY; case TYPE_DISTINCT: type = type->decl->distinct_decl.base_type; goto RETRY; @@ -1442,6 +1480,7 @@ Type *type_find_max_type(Type *type, Type *other) case TYPE_TYPEINFO: case TYPE_VIRTUAL: case TYPE_VIRTUAL_ANY: + case TYPE_BITSTRUCT: return NULL; case TYPE_IXX: if (other->type_kind == TYPE_DISTINCT && type_is_numeric(other->decl->distinct_decl.base_type)) return other; @@ -1466,7 +1505,7 @@ Type *type_find_max_type(Type *type, Type *other) return NULL; case TYPE_FUNC: case TYPE_UNION: - case TYPE_ERR_UNION: + case TYPE_ANYERR: case TYPE_TYPEID: case TYPE_STRUCT: TODO diff --git a/src/utils/errors.h b/src/utils/errors.h index e44c582c0..66b399539 100644 --- a/src/utils/errors.h +++ b/src/utils/errors.h @@ -27,7 +27,7 @@ void error_exit(const char *format, ...) __attribute__((noreturn)); #define ASSERT(_condition, _string, ...) while (!(_condition)) { FATAL_ERROR(_string, ##__VA_ARGS__); } -#define UNREACHABLE FATAL_ERROR("Cannot reach %s:%d", __func__, __LINE__); +#define UNREACHABLE FATAL_ERROR("Should be unreachable"); #if defined(__GNUC__) && __GNUC__ >= 7 #define FALLTHROUGH __attribute__ ((fallthrough)) diff --git a/test/test_suite/bitstruct/invalid_bitstruct_member_types.c3 b/test/test_suite/bitstruct/invalid_bitstruct_member_types.c3 new file mode 100644 index 000000000..21a1aaa19 --- /dev/null +++ b/test/test_suite/bitstruct/invalid_bitstruct_member_types.c3 @@ -0,0 +1,26 @@ + +bitstruct Test : int +{ + bool x : 0..0; + float a : 1..3; // #error: 'float' is not supported in a bitstruct, only enums, integer and boolean values may be used. +} + +define Baz = distinct float; +define Foo = distinct bool; +enum Boo +{ + BAR +} +bitstruct Test2 : int +{ + Foo x : 0..0; + Boo y : 4..6; + Baz a : 1..3; // #error: 'Baz' is not supported in a bitstruct, only enums, integer and boolean values may be used. +} + + +bitstruct Test3 : int +{ + Foo x : 0..0; + Brob a : 1..3; // #error: 'Brob' could not be found, did you spell it right? +} diff --git a/test/test_suite/bitstruct/invalid_bitstruct_name_other_ident.c3 b/test/test_suite/bitstruct/invalid_bitstruct_name_other_ident.c3 new file mode 100644 index 000000000..29aee7ed2 --- /dev/null +++ b/test/test_suite/bitstruct/invalid_bitstruct_name_other_ident.c3 @@ -0,0 +1,20 @@ +bitstruct test : int // #error: Names of bitstructs must start with an upper case letter. +{ + int a : 1..3; + int b : 5..10; + uint c : 20..20; +} + +bitstruct $if : int // #error: 'bitstruct' should be followed by the name of the bitstruct. +{ + int a : 1..3; + int b : 5..10; + uint c : 20..20; +} + +bitstruct if : int // #error: Names of bitstructs must start with an upper case letter. +{ + int a : 1..3; + int b : 5..10; + uint c : 20..20; +} \ No newline at end of file diff --git a/test/test_suite/bitstruct/invalid_bitstruct_type.c3 b/test/test_suite/bitstruct/invalid_bitstruct_type.c3 new file mode 100644 index 000000000..d4d616ec0 --- /dev/null +++ b/test/test_suite/bitstruct/invalid_bitstruct_type.c3 @@ -0,0 +1,7 @@ + +bitstruct Test : float // #error: The type of the bitstruct cannot be 'float' but must be an integer or an array of integers. +{ + int a : 1..3; + int b : 5..10; + uint c : 20..20; +} \ No newline at end of file diff --git a/test/test_suite/errors/else_checks.c3t b/test/test_suite/errors/else_checks.c3t index ae802856d..4e565a43b 100644 --- a/test/test_suite/errors/else_checks.c3t +++ b/test/test_suite/errors/else_checks.c3t @@ -12,125 +12,100 @@ func void test() // #expect: else_checks.ll -define void @else_checks.test() +; Function Attrs: nounwind +declare i64 @testError(i32*) #0 + +; Function Attrs: nounwind +define void @else_checks.test() #0 { entry: %x = alloca double, align 8 %retparam = alloca i32, align 4 - %result = alloca %error_union, align 8 %retparam1 = alloca i32, align 4 - %result2 = alloca %error_union, align 8 %y = alloca double, align 8 - %retparam6 = alloca i32, align 4 - %result7 = alloca %error_union, align 8 + %retparam4 = alloca i32, align 4 %z = alloca double, align 8 - %retparam15 = alloca i32, align 4 - %result16 = alloca %error_union, align 8 + %retparam11 = alloca i32, align 4 %w = alloca double, align 8 - %retparam24 = alloca i32, align 4 - %result25 = alloca %error_union, align 8 - %retparam29 = alloca i32, align 4 - %result30 = alloca %error_union, align 8 - %0 = call { i64, i64 } @testError(i32* %retparam) - %1 = bitcast %error_union* %result to { i64, i64 }* - store { i64, i64 } %0, { i64, i64 }* %1, align 8 - %err_domain = getelementptr inbounds %error_union, %error_union* %result, i32 0, i32 0 - %2 = load i64, i64* %err_domain, align 8 - %not_err = icmp eq i64 %2, 0 - br i1 %not_err, label %after_check, label %else_block + %retparam18 = alloca i32, align 4 + %retparam21 = alloca i32, align 4 + %0 = call i64 @testError(i32* %retparam) + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after.errcheck, label %else_block -after_check: - %3 = load i32, i32* %retparam, align 4 - %4 = call { i64, i64 } @testError(i32* %retparam1) - %5 = bitcast %error_union* %result2 to { i64, i64 }* - store { i64, i64 } %4, { i64, i64 }* %5, align 8 - %err_domain3 = getelementptr inbounds %error_union, %error_union* %result2, i32 0, i32 0 - %6 = load i64, i64* %err_domain3, align 8 - %not_err4 = icmp eq i64 %6, 0 - br i1 %not_err4, label %after_check5, label %else_block +after.errcheck: ; preds = %entry + %1 = load i32, i32* %retparam, align 4 + %2 = call i64 @testError(i32* %retparam1) + %not_err2 = icmp eq i64 %2, 0 + br i1 %not_err2, label %after.errcheck3, label %else_block -after_check5: - %7 = load i32, i32* %retparam1, align 4 - %add = add i32 %3, %7 +after.errcheck3: ; preds = %after.errcheck + %3 = load i32, i32* %retparam1, align 4 + %add = add i32 %1, %3 br label %phi_block -else_block: +else_block: ; preds = %after.errcheck, %entry br label %phi_block -phi_block: - %val = phi i32 [ %add, %after_check5 ], [ 100, %else_block ] +phi_block: ; preds = %else_block, %after.errcheck3 + %val = phi i32 [ %add, %after.errcheck3 ], [ 100, %else_block ] %sifp = sitofp i32 %val to double store double %sifp, double* %x, align 8 - %8 = call { i64, i64 } @testError(i32* %retparam6) - %9 = bitcast %error_union* %result7 to { i64, i64 }* - store { i64, i64 } %8, { i64, i64 }* %9, align 8 - %err_domain8 = getelementptr inbounds %error_union, %error_union* %result7, i32 0, i32 0 - %10 = load i64, i64* %err_domain8, align 8 - %not_err9 = icmp eq i64 %10, 0 - br i1 %not_err9, label %after_check10, label %else_block11 + %4 = call i64 @testError(i32* %retparam4) + %not_err5 = icmp eq i64 %4, 0 + br i1 %not_err5, label %after.errcheck6, label %else_block7 -after_check10: - %11 = load i32, i32* %retparam6, align 4 - %shl = shl i32 1, %11 - %12 = freeze i32 %shl - br label %phi_block12 +after.errcheck6: ; preds = %phi_block + %5 = load i32, i32* %retparam4, align 4 + %shl = shl i32 1, %5 + %6 = freeze i32 %shl + br label %phi_block8 -else_block11: - br label %phi_block12 +else_block7: ; preds = %phi_block + br label %phi_block8 -phi_block12: - %val13 = phi i32 [ %12, %after_check10 ], [ 100, %else_block11 ] - %sifp14 = sitofp i32 %val13 to double - store double %sifp14, double* %y, align 8 - %13 = call { i64, i64 } @testError(i32* %retparam15) - %14 = bitcast %error_union* %result16 to { i64, i64 }* - store { i64, i64 } %13, { i64, i64 }* %14, align 8 - %err_domain17 = getelementptr inbounds %error_union, %error_union* %result16, i32 0, i32 0 - %15 = load i64, i64* %err_domain17, align 8 - %not_err18 = icmp eq i64 %15, 0 - br i1 %not_err18, label %after_check19, label %else_block20 +phi_block8: ; preds = %else_block7, %after.errcheck6 + %val9 = phi i32 [ %6, %after.errcheck6 ], [ 100, %else_block7 ] + %sifp10 = sitofp i32 %val9 to double + store double %sifp10, double* %y, align 8 + %7 = call i64 @testError(i32* %retparam11) + %not_err12 = icmp eq i64 %7, 0 + br i1 %not_err12, label %after.errcheck13, label %else_block14 -after_check19: - %16 = load i32, i32* %retparam15, align 4 - %ashr = ashr i32 %16, 1 - %17 = freeze i32 %ashr - br label %phi_block21 +after.errcheck13: ; preds = %phi_block8 + %8 = load i32, i32* %retparam11, align 4 + %ashr = ashr i32 %8, 1 + %9 = freeze i32 %ashr + br label %phi_block15 -else_block20: - br label %phi_block21 +else_block14: ; preds = %phi_block8 + br label %phi_block15 -phi_block21: - %val22 = phi i32 [ %17, %after_check19 ], [ 100, %else_block20 ] - %sifp23 = sitofp i32 %val22 to double - store double %sifp23, double* %z, align 8 - %18 = call { i64, i64 } @testError(i32* %retparam24) - %19 = bitcast %error_union* %result25 to { i64, i64 }* - store { i64, i64 } %18, { i64, i64 }* %19, align 8 - %err_domain26 = getelementptr inbounds %error_union, %error_union* %result25, i32 0, i32 0 - %20 = load i64, i64* %err_domain26, align 8 - %not_err27 = icmp eq i64 %20, 0 - br i1 %not_err27, label %after_check28, label %else_block34 +phi_block15: ; preds = %else_block14, %after.errcheck13 + %val16 = phi i32 [ %9, %after.errcheck13 ], [ 100, %else_block14 ] + %sifp17 = sitofp i32 %val16 to double + store double %sifp17, double* %z, align 8 + %10 = call i64 @testError(i32* %retparam18) + %not_err19 = icmp eq i64 %10, 0 + br i1 %not_err19, label %after.errcheck20, label %else_block24 -after_check28: - %21 = load i32, i32* %retparam24, align 4 - %22 = call { i64, i64 } @testError(i32* %retparam29) - %23 = bitcast %error_union* %result30 to { i64, i64 }* - store { i64, i64 } %22, { i64, i64 }* %23, align 8 - %err_domain31 = getelementptr inbounds %error_union, %error_union* %result30, i32 0, i32 0 - %24 = load i64, i64* %err_domain31, align 8 - %not_err32 = icmp eq i64 %24, 0 - br i1 %not_err32, label %after_check33, label %else_block34 +after.errcheck20: ; preds = %phi_block15 + %11 = load i32, i32* %retparam18, align 4 + %12 = call i64 @testError(i32* %retparam21) + %not_err22 = icmp eq i64 %12, 0 + br i1 %not_err22, label %after.errcheck23, label %else_block24 -after_check33: - %25 = load i32, i32* %retparam29, align 4 - %mul = mul i32 %21, %25 - br label %phi_block35 +after.errcheck23: ; preds = %after.errcheck20 + %13 = load i32, i32* %retparam21, align 4 + %mul = mul i32 %11, %13 + br label %phi_block25 -else_block34: - br label %phi_block35 +else_block24: ; preds = %after.errcheck20, %phi_block15 + br label %phi_block25 -phi_block35: - %val36 = phi i32 [ %mul, %after_check33 ], [ 100, %else_block34 ] - %sifp37 = sitofp i32 %val36 to double - store double %sifp37, double* %w, align 8 +phi_block25: ; preds = %else_block24, %after.errcheck23 + %val26 = phi i32 [ %mul, %after.errcheck23 ], [ 100, %else_block24 ] + %sifp27 = sitofp i32 %val26 to double + store double %sifp27, double* %w, align 8 ret void -} \ No newline at end of file +} + diff --git a/test/test_suite/errors/error_decl_fails.c3 b/test/test_suite/errors/error_decl_fails.c3 deleted file mode 100644 index 669ca10e5..000000000 --- a/test/test_suite/errors/error_decl_fails.c3 +++ /dev/null @@ -1,12 +0,0 @@ -module foo; - - - -errtype TheError -{ - union // #error: A type name was expected here - { - int a; - int b; - } -} \ No newline at end of file diff --git a/test/test_suite/errors/error_decl_ok.c3 b/test/test_suite/errors/error_decl_ok.c3 index ea65c1ccc..81188ebe4 100644 --- a/test/test_suite/errors/error_decl_ok.c3 +++ b/test/test_suite/errors/error_decl_ok.c3 @@ -2,19 +2,18 @@ module errors; errtype TheError { - int a; + A } errtype TheError2 { - char a; - char b; + A, + B } errtype TheError3 { - void *a; + C, D } -errtype OtherError; diff --git a/test/test_suite/errors/error_semantic_fails.c3 b/test/test_suite/errors/error_semantic_fails.c3 index ec4915f92..fcc6161c2 100644 --- a/test/test_suite/errors/error_semantic_fails.c3 +++ b/test/test_suite/errors/error_semantic_fails.c3 @@ -1,5 +1,5 @@ -errtype TooBig // #error: Error type may not exceed pointer +errtype Repeater { - usize a; - ichar b; + A, + A // #error: This enum constant is declared twice. } diff --git a/test/test_suite/errors/error_throw.c3 b/test/test_suite/errors/error_throw.c3 index 00a6122d6..7ea855f2b 100644 --- a/test/test_suite/errors/error_throw.c3 +++ b/test/test_suite/errors/error_throw.c3 @@ -2,10 +2,11 @@ module foo; errtype Blurg { + Z } func void main() { - int! i = Blurg!; - int! j = Blurg({})!; + int! i = Blurg.Z!; + int! j = Blurg.Z!; } \ No newline at end of file diff --git a/test/test_suite/errors/error_union.c3 b/test/test_suite/errors/error_union.c3 index 973c3d531..6a26bc0f1 100644 --- a/test/test_suite/errors/error_union.c3 +++ b/test/test_suite/errors/error_union.c3 @@ -1,6 +1,9 @@ module foo; -errtype Blurg; +errtype Blurg +{ + X, Y, Z +} func void main() { diff --git a/test/test_suite/errors/general_error_regression.c3t b/test/test_suite/errors/general_error_regression.c3t new file mode 100644 index 000000000..2ce1e5cb3 --- /dev/null +++ b/test/test_suite/errors/general_error_regression.c3t @@ -0,0 +1,147 @@ +module foo; +import std::io; + +errtype Foo +{ + X, + Y, + Z, + W, + W1 + + +} + +errtype Foob +{ + X1, + Y2 +} + +define Bar = distinct int; + +enum MyEnum +{ + A, + B +} + +func void Foo.hello(Foo *f) +{ + io::println("Hello from Foo"); +} + +func void Bar.hello(Bar *b) +{ + io::println("Hello from Bar"); +} + +func void MyEnum.hello(MyEnum *myenum) +{ + io::println("Hello from MyEnum"); +} +func void main() +{ + Foo f = Foo.X; + Foo ef = Foo.Y; + anyerr x = f; + ulong z = (ulong)(x); + io::printf("1: %p\n", z); + x = ef; + z = (ulong)(x); + io::printf("2: %p\n", z); + x = Foo.W; + z = (ulong)(x); + io::printf("21: %p\n", z); + x = Foo.W1; + z = (ulong)(x); + io::printf("22: %p\n", z); + x = Foob.X1; + z = (ulong)(x); + io::printf("3: %p\n", z); + x = Foob.Y2; + z = (ulong)(x); + io::printf("4: %p\n", z); + Bar b; + MyEnum a = MyEnum.A; + f.hello(); + b.hello(); + a.hello(); +} + +// #expect: foo.ll + +define void @foo.Foo__hello(i64* %0) #0 { +entry: + %f = alloca i64*, align 8 + store i64* %0, i64** %f, align 8 + %1 = call i32 @"std::io.println"(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.6, i32 0, i32 0)) #1 + ret void +} + +define void @foo.Bar__hello(i32* %0) #0 { +entry: + %b = alloca i32*, align 8 + store i32* %0, i32** %b, align 8 + %1 = call i32 @"std::io.println"(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str.7, i32 0, i32 0)) #1 + ret void +} + +define void @foo.MyEnum__hello(i32* %0) #0 { +entry: + %myenum = alloca i32*, align 8 + store i32* %0, i32** %myenum, align 8 + %1 = call i32 @"std::io.println"(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str.8, i32 0, i32 0)) #1 + ret void +} + +define void @main() #0 { +entry: + %f = alloca i64, align 8 + %ef = alloca i64, align 8 + %x = alloca i64, align 8 + %z = alloca i64, align 8 + %b = alloca i32, align 4 + %a = alloca i32, align 4 + store i64 ptrtoint ([5 x i8*]* @"foo.Foo$elements" to i64), i64* %f, align 8 + store i64 ptrtoint ([5 x i8*]* getelementptr inbounds ([5 x i8*], [5 x i8*]* @"foo.Foo$elements", i64 1) to i64), i64* %ef, align 8 + %0 = load i64, i64* %f, align 8 + store i64 %0, i64* %x, align 8 + %1 = load i64, i64* %x, align 8 + store i64 %1, i64* %z, align 8 + %2 = load i64, i64* %z, align 8 + %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0), i64 %2) + %4 = load i64, i64* %ef, align 8 + store i64 %4, i64* %x, align 8 + %5 = load i64, i64* %x, align 8 + store i64 %5, i64* %z, align 8 + %6 = load i64, i64* %z, align 8 + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0), i64 %6) + store i64 ptrtoint ([5 x i8*]* getelementptr inbounds ([5 x i8*], [5 x i8*]* @"foo.Foo$elements", i64 3) to i64), i64* %x, align 8 + %8 = load i64, i64* %x, align 8 + store i64 %8, i64* %z, align 8 + %9 = load i64, i64* %z, align 8 + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i64 %9) + store i64 ptrtoint ([5 x i8*]* getelementptr inbounds ([5 x i8*], [5 x i8*]* @"foo.Foo$elements", i64 4) to i64), i64* %x, align 8 + %11 = load i64, i64* %x, align 8 + store i64 %11, i64* %z, align 8 + %12 = load i64, i64* %z, align 8 + %13 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.3, i32 0, i32 0), i64 %12) + store i64 ptrtoint ([2 x i8*]* @"foo.Foob$elements" to i64), i64* %x, align 8 + %14 = load i64, i64* %x, align 8 + store i64 %14, i64* %z, align 8 + %15 = load i64, i64* %z, align 8 + %16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.4, i32 0, i32 0), i64 %15) + store i64 ptrtoint ([2 x i8*]* getelementptr inbounds ([2 x i8*], [2 x i8*]* @"foo.Foob$elements", i64 1) to i64), i64* %x, align 8 + %17 = load i64, i64* %x, align 8 + store i64 %17, i64* %z, align 8 + %18 = load i64, i64* %z, align 8 + %19 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.5, i32 0, i32 0), i64 %18) + store i32 0, i32* %b, align 4 + store i32 0, i32* %a, align 4 + call void @foo.Foo__hello(i64* %f) + call void @foo.Bar__hello(i32* %b) + call void @foo.MyEnum__hello(i32* %a) + ret void +} + diff --git a/test/test_suite/errors/rethrow.c3t b/test/test_suite/errors/rethrow.c3t index 750ca4af6..8c941eed7 100644 --- a/test/test_suite/errors/rethrow.c3t +++ b/test/test_suite/errors/rethrow.c3t @@ -8,42 +8,27 @@ func void! test() // #expect: rethrow.ll -entry: - %i = alloca i32, align 4 - %i.f = alloca %error_union, align 8 - %error_var = alloca %error_union, align 8 - %tempcoerce = alloca { i64, i64 }, align 8 - %tempaddr = alloca %error_union, align 8 - %tempcoerce1 = alloca { i64, i64 }, align 8 - store %error_union zeroinitializer, %error_union* %i.f, align 8 + %i = alloca i32, align 4 + %i.f = alloca i64, align 8 + %error_var = alloca i64, align 8 + store i64 0, i64* %i.f, align 8 store i32 0, i32* %i, align 4 - %err_domain = getelementptr inbounds %error_union, %error_union* %i.f, i32 0, i32 0 - %0 = load i64, i64* %err_domain, align 8 + %0 = load i64, i64* %i.f, align 8 %not_err = icmp eq i64 %0, 0 br i1 %not_err, label %after_check, label %error -error: - %1 = bitcast %error_union* %error_var to i8* - %2 = bitcast %error_union* %i.f to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 16, i1 false) +error: ; preds = %entry + store i64 %0, i64* %error_var, align 8 br label %guard_block -after_check: - %3 = load i32, i32* %i, align 4 +after_check: ; preds = %entry + %1 = load i32, i32* %i, align 4 br label %noerr_block guard_block: ; preds = %error - %4 = bitcast { i64, i64 }* %tempcoerce to i8* - %5 = bitcast %error_union* %error_var to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 8 %5, i32 16, i1 false) - %6 = load { i64, i64 }, { i64, i64 }* %tempcoerce, align 8 - ret { i64, i64 } %6 + %2 = load i64, i64* %error_var, align 8 + ret i64 %2 noerr_block: ; preds = %after_check - store %error_union zeroinitializer, %error_union* %tempaddr, align 8 - %7 = bitcast { i64, i64 }* %tempcoerce1 to i8* - %8 = bitcast %error_union* %tempaddr to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %7, i8* align 8 %8, i32 16, i1 false) - %9 = load { i64, i64 }, { i64, i64 }* %tempcoerce1, align 8 - ret { i64, i64 } %9 -} \ No newline at end of file + ret i64 0 +} diff --git a/test/test_suite/errors/rethrow_mingw.c3t b/test/test_suite/errors/rethrow_mingw.c3t index 53b3bb6ee..bff6903c0 100644 --- a/test/test_suite/errors/rethrow_mingw.c3t +++ b/test/test_suite/errors/rethrow_mingw.c3t @@ -10,35 +10,31 @@ func void! test() // #expect: rethrow.ll -define void @rethrow.test(%error_union* sret align 8 %0) +define i64 @rethrow.test() #0 { entry: %i = alloca i32, align 4 - %i.f = alloca %error_union, align 8 - %error_var = alloca %error_union, align 8 - store %error_union zeroinitializer, %error_union* %i.f, align 8 + %i.f = alloca i64, align 8 + %error_var = alloca i64, align 8 + store i64 0, i64* %i.f, align 8 store i32 0, i32* %i, align 4 - %err_domain = getelementptr inbounds %error_union, %error_union* %i.f, i32 0, i32 0 - %1 = load i64, i64* %err_domain, align 8 - %not_err = icmp eq i64 %1, 0 + %0 = load i64, i64* %i.f, align 8 + %not_err = icmp eq i64 %0, 0 br i1 %not_err, label %after_check, label %error -error: - %2 = bitcast %error_union* %error_var to i8* - %3 = bitcast %error_union* %i.f to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %2, i8* align 8 %3, i32 16, i1 false) +error: ; preds = %entry + store i64 %0, i64* %error_var, align 8 br label %guard_block -after_check: - %4 = load i32, i32* %i, align 4 +after_check: ; preds = %entry + %1 = load i32, i32* %i, align 4 br label %noerr_block -guard_block: - %5 = bitcast %error_union* %0 to i8* - %6 = bitcast %error_union* %error_var to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %5, i8* align 8 %6, i32 16, i1 false) - ret void +guard_block: ; preds = %error + %2 = load i64, i64* %error_var, align 8 + ret i64 %2 + +noerr_block: ; preds = %after_check + ret i64 0 +} -noerr_block: - store %error_union zeroinitializer, %error_union* %0, align 8 - ret void diff --git a/test/test_suite/errors/simple_static_failable.c3t b/test/test_suite/errors/simple_static_failable.c3t index bb0ff8510..913ecf662 100644 --- a/test/test_suite/errors/simple_static_failable.c3t +++ b/test/test_suite/errors/simple_static_failable.c3t @@ -2,33 +2,26 @@ module foo; -errtype Blurg; +errtype Blurg +{ + Y +} func void main() { static int! x = 120; - int! i = Blurg!; + int! i = Blurg.Y!; } // #expect: foo.ll -%error_union = type { i64, i64 } -%Blurg = type { [8 x i8] } - -@Blurg = linkonce_odr constant i8 1 -@main.x.f = hidden global %error_union zeroinitializer, align 8 -@main.x = hidden global i32 120, align 4 - -define void @main() +define void @main() #0 { +entry: %i = alloca i32, align 4 - %i.f = alloca %error_union, align 8 - %literal = alloca %Blurg, align 8 - %0 = bitcast %Blurg* %literal to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false) - %1 = load %Blurg, %Blurg* %literal, align 8 - %2 = bitcast %error_union* %i.f to %Blurg* - store %Blurg %1, %Blurg* %2, align 1 + %i.f = alloca i64, align 8 + store i64 ptrtoint ([1 x i8*]* @"foo.Blurg$elements" to i64), i64* %i.f, align 8 br label %after_assign -after_assign: +after_assign: ; preds = %entry ret void +} diff --git a/test/test_suite/errors/try_assign.c3t b/test/test_suite/errors/try_assign.c3t index a14c9fbc5..e7b1d7016 100644 --- a/test/test_suite/errors/try_assign.c3t +++ b/test/test_suite/errors/try_assign.c3t @@ -1,7 +1,7 @@ // #target: x64-darwin extern func int! err(); -errtype FooErr { int x; } +errtype FooErr { QBERT } extern func int printf(char* fmt, ...); func void main() @@ -10,7 +10,7 @@ func void main() int! z = 234; int! w; int gh = 1; - if ((try x = z) && (try gh = w)) + if (try x = z && try gh = w) { printf("Success %d && %d!\n", x, gh); } @@ -27,123 +27,126 @@ func void main() // #expect: try_assign.ll +define void @main() #0 { entry: %x = alloca i32, align 4 %z = alloca i32, align 4 - %z.f = alloca %error_union, align 8 + %z.f = alloca i64, align 8 %w = alloca i32, align 4 - %w.f = alloca %error_union, align 8 + %w.f = alloca i64, align 8 %gh = alloca i32, align 4 - %e = alloca %error_union, align 8 + %e = alloca i64, align 8 store i32 123, i32* %x, align 4 - store %error_union zeroinitializer, %error_union* %z.f, align 8 + store i64 0, i64* %z.f, align 8 store i32 234, i32* %z, align 4 - store %error_union zeroinitializer, %error_union* %z.f, align 8 - store %error_union zeroinitializer, %error_union* %w.f, align 8 + store i64 0, i64* %z.f, align 8 + store i64 0, i64* %w.f, align 8 store i32 0, i32* %w, align 4 store i32 1, i32* %gh, align 4 - %err_domain = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0 - %0 = load i64, i64* %err_domain, align 8 + %0 = load i64, i64* %z.f, align 8 %not_err = icmp eq i64 %0, 0 br i1 %not_err, label %after_check, label %catch_landing -after_check: +after_check: ; preds = %entry %1 = load i32, i32* %z, align 4 store i32 %1, i32* %x, align 4 br label %phi_try_catch -catch_landing: +catch_landing: ; preds = %entry br label %phi_try_catch -phi_try_catch: +phi_try_catch: ; preds = %catch_landing, %after_check %val = phi i1 [ true, %after_check ], [ false, %catch_landing ] - br i1 %val, label %and.rhs, label %and.phi + br i1 %val, label %chain_next, label %fail_chain -and.rhs: - %err_domain1 = getelementptr inbounds %error_union, %error_union* %w.f, i32 0, i32 0 - %2 = load i64, i64* %err_domain1, align 8 - %not_err2 = icmp eq i64 %2, 0 - br i1 %not_err2, label %after_check3, label %catch_landing4 +chain_next: ; preds = %phi_try_catch + %2 = load i64, i64* %w.f, align 8 + %not_err1 = icmp eq i64 %2, 0 + br i1 %not_err1, label %after_check2, label %catch_landing3 -after_check3: +after_check2: ; preds = %chain_next %3 = load i32, i32* %w, align 4 store i32 %3, i32* %gh, align 4 - br label %phi_try_catch5 + br label %phi_try_catch4 -catch_landing4: - br label %phi_try_catch5 +catch_landing3: ; preds = %chain_next + br label %phi_try_catch4 -phi_try_catch5: - %val6 = phi i1 [ true, %after_check3 ], [ false, %catch_landing4 ] - br label %and.phi +phi_try_catch4: ; preds = %catch_landing3, %after_check2 + %val5 = phi i1 [ true, %after_check2 ], [ false, %catch_landing3 ] + br i1 %val5, label %chain_next6, label %fail_chain -and.phi: - %val7 = phi i1 [ false, %phi_try_catch ], [ %val6, %phi_try_catch5 ] - br i1 %val7, label %if.then, label %if.exit +chain_next6: ; preds = %phi_try_catch4 + br label %end_chain -if.then: +fail_chain: ; preds = %phi_try_catch4, %phi_try_catch + br label %end_chain + +end_chain: ; preds = %fail_chain, %chain_next6 + %chain.phi = phi i1 [ true, %chain_next6 ], [ false, %fail_chain ] + br i1 %chain.phi, label %if.then, label %if.exit + +if.then: ; preds = %end_chain %4 = load i32, i32* %x, align 4 %5 = load i32, i32* %gh, align 4 %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str, i32 0, i32 0), i32 %4, i32 %5) br label %if.exit -if.exit: - %err_domain8 = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0 - %7 = load i64, i64* %err_domain8, align 8 - %not_err9 = icmp eq i64 %7, 0 - br i1 %not_err9, label %after_check10, label %catch_landing14 +if.exit: ; preds = %if.then, %end_chain + %7 = load i64, i64* %z.f, align 8 + %not_err7 = icmp eq i64 %7, 0 + br i1 %not_err7, label %after_check8, label %catch_landing11 -after_check10: +after_check8: ; preds = %if.exit %8 = load i32, i32* %z, align 4 - %err_domain11 = getelementptr inbounds %error_union, %error_union* %w.f, i32 0, i32 0 - %9 = load i64, i64* %err_domain11, align 8 - %not_err12 = icmp eq i64 %9, 0 - br i1 %not_err12, label %after_check13, label %catch_landing14 + %9 = load i64, i64* %w.f, align 8 + %not_err9 = icmp eq i64 %9, 0 + br i1 %not_err9, label %after_check10, label %catch_landing11 -after_check13: +after_check10: ; preds = %after_check8 %10 = load i32, i32* %w, align 4 %add = add i32 %8, %10 store i32 %add, i32* %x, align 4 - br label %phi_try_catch15 + br label %phi_try_catch12 -catch_landing14: - br label %phi_try_catch15 +catch_landing11: ; preds = %after_check8, %if.exit + br label %phi_try_catch12 -phi_try_catch15: - %val16 = phi i1 [ true, %after_check13 ], [ false, %catch_landing14 ] - br i1 %val16, label %if.then17, label %if.exit18 +phi_try_catch12: ; preds = %catch_landing11, %after_check10 + %val13 = phi i1 [ true, %after_check10 ], [ false, %catch_landing11 ] + br i1 %val13, label %if.then14, label %if.exit15 -if.then17: +if.then14: ; preds = %phi_try_catch12 %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0)) - br label %if.exit18 + br label %if.exit15 -if.exit18: - %12 = bitcast %error_union* %e to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %12, i8 0, i64 16, i1 false) - %err_domain19 = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0 - %13 = load i64, i64* %err_domain19, align 8 - %not_err20 = icmp eq i64 %13, 0 - br i1 %not_err20, label %after_check21, label %error +if.exit15: ; preds = %if.then14, %phi_try_catch12 + store i64 0, i64* %e, align 8 + br label %testblock -error: - %14 = bitcast %error_union* %e to i8* - %15 = bitcast %error_union* %z.f to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %14, i8* align 8 %15, i32 16, i1 false) - br label %catch_landing22 +testblock: ; preds = %if.exit15 + %12 = load i64, i64* %z.f, align 8 + %not_err16 = icmp eq i64 %12, 0 + br i1 %not_err16, label %after_check17, label %error -after_check21: - br label %phi_try_catch23 +error: ; preds = %testblock + store i64 %12, i64* %e, align 8 + br label %end_block -catch_landing22: - br label %phi_try_catch23 +after_check17: ; preds = %testblock + store i64 0, i64* %e, align 8 + br label %end_block -phi_try_catch23: - %val24 = phi i1 [ false, %after_check21 ], [ true, %catch_landing22 ] - br i1 %val24, label %if.then25, label %if.exit26 +end_block: ; preds = %after_check17, %error + %13 = load i64, i64* %e, align 8 + %intbool = icmp ne i64 %13, 0 + br i1 %intbool, label %if.then18, label %if.exit19 -if.then25: - %16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0)) - br label %if.exit26 +if.then18: ; preds = %end_block + %14 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0)) + br label %if.exit19 -if.exit26: +if.exit19: ; preds = %if.then18, %end_block ret void +} + diff --git a/test/test_suite/errors/try_catch_if.c3t b/test/test_suite/errors/try_catch_if.c3t new file mode 100644 index 000000000..bb844ea59 --- /dev/null +++ b/test/test_suite/errors/try_catch_if.c3t @@ -0,0 +1,99 @@ +// #target: x64-darwin + +func int hello(int x) +{ + return x + 1; +} + +extern func int printf(char *c, ...); + +func int! tester() +{ + printf("In tester\n"); + return 222; +} + +errtype Foo +{ + A +} +func void test1() +{ + int! a = 123; + if (catch err = (a, tester())) + { + printf("Err\n"); + } + else + { + printf("Noerr %d\n", a); + } +} +func void main() +{ + test1(); +} + +// #expect: try_catch_if.ll + +define i64 @try_catch_if.tester(i32* %0) #0 { +entry: + %reterr = alloca i64, align 8 + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0)) + store i32 222, i32* %0, align 4 + ret i64 0 +} + +define void @try_catch_if.test1() #0 { +entry: + %a = alloca i32, align 4 + %a.f = alloca i64, align 8 + %err = alloca i64, align 8 + %retparam = alloca i32, align 4 + store i64 0, i64* %a.f, align 8 + store i32 123, i32* %a, align 4 + store i64 0, i64* %a.f, align 8 + br label %testblock + +testblock: ; preds = %entry + %0 = load i64, i64* %a.f, align 8 + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after_check, label %error + +error: ; preds = %testblock + store i64 %0, i64* %err, align 8 + br label %end_block + +after_check: ; preds = %testblock + br label %testblock1 + +testblock1: ; preds = %after_check + %1 = call i64 @try_catch_if.tester(i32* %retparam) + %not_err2 = icmp eq i64 %1, 0 + br i1 %not_err2, label %after.errcheck, label %error3 + +error3: ; preds = %testblock1 + store i64 %1, i64* %err, align 8 + br label %end_block + +after.errcheck: ; preds = %testblock1 + store i64 0, i64* %err, align 8 + br label %end_block + +end_block: ; preds = %after.errcheck, %error3, %error + %2 = load i64, i64* %err, align 8 + %intbool = icmp ne i64 %2, 0 + br i1 %intbool, label %if.then, label %if.else + +if.then: ; preds = %end_block + %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.1, i32 0, i32 0)) + br label %if.exit + +if.else: ; preds = %end_block + %4 = load i32, i32* %a, align 4 + %5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0), i32 %4) + br label %if.exit + +if.exit: ; preds = %if.else, %if.then + ret void +} \ No newline at end of file diff --git a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 index a924eea42..05646762a 100644 --- a/test/test_suite/errors/try_catch_unwrapping_while_if.c3 +++ b/test/test_suite/errors/try_catch_unwrapping_while_if.c3 @@ -1,8 +1,10 @@ - extern func int printf(char* fmt, ...); extern func int! err(); -errtype FooErr { int x; } +errtype FooErr +{ + X +} func void test1() { int! z = 234; @@ -10,35 +12,16 @@ func void test1() { int y = z; z = 12; - z = FooErr({1})!; // #error: The variable is unwrapped in this context + z = FooErr.X!; // #error: The variable is unwrapped in this context } } -func void test2() -{ - int! z = 234; - if (try z || 1) - { - z = 12; - z = FooErr({1})!; - } -} -func void test3() -{ - int! z = 234; - int! w = 123; - if (try z || try w) - { - int y = z; // #error: 'int!' cannot be converted into 'int' - y = w; - } -} func void test4() { int! z = 234; int! w = 123; - if (try z && ((try w) && 1)) + if (try z && try w && 1) { int y = z; y = w; @@ -49,7 +32,16 @@ func void test5() { int! z = 234; int! w = 123; - if (try z && try z) + if (try z && try w) + { + int y = z; + } +} +func void test5b() +{ + int! z = 234; + int! w = 123; + if (try z && try z) // #error: This variable is already unwrapped, so you cannot use 'try' on it again, please remove the 'try'. { int y = z; } @@ -83,12 +75,12 @@ func void test8() { int! z = 234; int! w = 123; - if (catch z && 1) + if (catch z) { } else { - int y = z; // #error: 'int!' cannot be converted into 'int' + int y = z; } } func void test9() diff --git a/test/test_suite/errors/try_with_assign_to_failable.c3 b/test/test_suite/errors/try_with_assign_to_failable.c3 index 87a33b8d0..8e7550614 100644 --- a/test/test_suite/errors/try_with_assign_to_failable.c3 +++ b/test/test_suite/errors/try_with_assign_to_failable.c3 @@ -2,13 +2,5 @@ func void test() { int! z; int! w; - try w = z; // #error: A 'try' assignment is not possible with failable on the left hand side, did you intend 'try (variable = expr) + try w = z; // #error: An unwrapping 'try' can only occur as the last element of a conditional, did you want 'try(expr) } - -func int! err(); -func void test2() -{ - int! z; - int! w; - try err() = z; // #error: This expression is not assignable, did you make a mistake -} \ No newline at end of file diff --git a/test/test_suite/errors/try_with_chained_unwrap.c3t b/test/test_suite/errors/try_with_chained_unwrap.c3t index d37814a3b..12c2ddff1 100644 --- a/test/test_suite/errors/try_with_chained_unwrap.c3t +++ b/test/test_suite/errors/try_with_chained_unwrap.c3t @@ -23,45 +23,35 @@ entry: %val = alloca i32, align 4 %retparam = alloca i32, align 4 %retparam1 = alloca i8*, align 8 - %result = alloca %error_union, align 8 - %result2 = alloca %error_union, align 8 store i32 0, i32* %val, align 4 - %0 = call { i64, i64 } @readLine(i8** %retparam1) - %1 = bitcast %error_union* %result to { i64, i64 }* - store { i64, i64 } %0, { i64, i64 }* %1, align 8 - %err_domain = getelementptr inbounds %error_union, %error_union* %result, i32 0, i32 0 - %2 = load i64, i64* %err_domain, align 8 - %not_err = icmp eq i64 %2, 0 - br i1 %not_err, label %after_check, label %catch_landing + %0 = call i64 @readLine(i8** %retparam1) + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after.errcheck, label %catch_landing -after_check: - %3 = load i8*, i8** %retparam1, align 8 - %4 = call { i64, i64 } @atoi(i32* %retparam, i8* %3) - %5 = bitcast %error_union* %result2 to { i64, i64 }* - store { i64, i64 } %4, { i64, i64 }* %5, align 8 - %err_domain3 = getelementptr inbounds %error_union, %error_union* %result2, i32 0, i32 0 - %6 = load i64, i64* %err_domain3, align 8 - %not_err4 = icmp eq i64 %6, 0 - br i1 %not_err4, label %after_check5, label %catch_landing +after.errcheck: ; preds = %entry + %1 = load i8*, i8** %retparam1, align 8 + %2 = call i64 @atoi(i32* %retparam, i8* %1) + %not_err2 = icmp eq i64 %2, 0 + br i1 %not_err2, label %after.errcheck3, label %catch_landing -after_check5: - %7 = load i32, i32* %retparam, align 4 - store i32 %7, i32* %val, align 4 +after.errcheck3: ; preds = %after.errcheck + %3 = load i32, i32* %retparam, align 4 + store i32 %3, i32* %val, align 4 br label %phi_try_catch -catch_landing: +catch_landing: ; preds = %after.errcheck, %entry br label %phi_try_catch -phi_try_catch: - %val6 = phi i1 [ true, %after_check5 ], [ false, %catch_landing ] - br i1 %val6, label %if.then, label %if.exit +phi_try_catch: ; preds = %catch_landing, %after.errcheck3 + %val4 = phi i1 [ true, %after.errcheck3 ], [ false, %catch_landing ] + br i1 %val4, label %if.then, label %if.exit -if.then: - %8 = load i32, i32* %val, align 4 - %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str, i32 0, i32 0), i32 %8) +if.then: ; preds = %phi_try_catch + %4 = load i32, i32* %val, align 4 + %5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str, i32 0, i32 0), i32 %4) ret void -if.exit: - %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str.1, i32 0, i32 0)) +if.exit: ; preds = %phi_try_catch + %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str.1, i32 0, i32 0)) ret void } \ No newline at end of file diff --git a/test/test_suite/errors/try_with_chained_unwrap_errors.c3 b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 new file mode 100644 index 000000000..56481ff9d --- /dev/null +++ b/test/test_suite/errors/try_with_chained_unwrap_errors.c3 @@ -0,0 +1,93 @@ +func void test() +{ + int! a; + int b; + if (try int b = a) {} // #error: 'b' would shadow a previous declaration. +} +func void test2() +{ + int! a; + int b; + if (try b = a) {} + if (try test2 = a) {} // #error: 'try' expected an assignable variable or expression here, did you make a mistake? +} + +const int BAZ = 1; + +func void test3() +{ + + int! a; + int b; + if (try BAZ = a) {} // #error: 'try' expected an assignable variable or expression here, did you make a mistake? +} + + +func void test4() +{ + + int! a; + int b; + if (try b = 1) {} // #error: Expected a failable expression to 'try' here. If it isn't a failable, remove 'try' +} + +func void test5() +{ + + int! a; + int b; + if (try a = a) {} // #error: This is a failable variable, you should only have non-failable variables on the left side unless you use 'try' without '=' +} + +func void test6() +{ + + int! a; + int b; + int*! x; + if (try *x = a) {} // #error: This is a failable expression, it can't go on the left hand side of a 'try'. +} + + +func void test7() +{ + int! a; + int b; + int*! x; + if (try foo::z = a) {} // #error: The variable may not have a path. +} + +func void test8() +{ + int! a; + int b; + if (b == 0, try b = a && try b = a && b > 0) {} + if (try b = a && try b = a && b > 0) {} + if (try c = a && try c = a) { c++; } +} + +func void test9() +{ + int! a = 11; + if (try z = a) + { + int g = z++; + } + else + { + z++; // #error: 'z' could not be found, did you spell it right? + } +} + +func void test10() +{ + int! a = 11; + if (try a) + { + int g = a; + } + else + { + int g = a; // #error: 'int!' cannot be converted into 'int' + } +} \ No newline at end of file diff --git a/test/test_suite/errors/try_with_unwrap.c3t b/test/test_suite/errors/try_with_unwrap.c3t index fd4bce98e..7131c30e5 100644 --- a/test/test_suite/errors/try_with_unwrap.c3t +++ b/test/test_suite/errors/try_with_unwrap.c3t @@ -1,3 +1,4 @@ +// #target: x64-darwin extern func char*! readLine(); extern func int! atoi(char*); @@ -21,106 +22,63 @@ func void main() // #expect: try_with_unwrap.ll define void @main() #0 { - entry: - %line = alloca i8*, align 8 - %line.f = alloca %error_union, align 8 - %retparam = alloca i8*, align 8 - %result = alloca %error_union, align 8 - %val4 = alloca i32, align 4 - %val.f = alloca %error_union, align 8 - %retparam5 = alloca i32, align 4 - %result6 = alloca %error_union, align 8 - %0 = call { i64, i64 } @readLine(i8** %retparam) - %1 = bitcast %error_union* %result to { i64, i64 }* - store { i64, i64 } %0, { i64, i64 }* %1, align 8 - %err_domain = getelementptr inbounds %error_union, %error_union* %result, i32 0, i32 0 - %2 = load i64, i64* %err_domain, align 8 - %not_err = icmp eq i64 %2, 0 - br i1 %not_err, label %after_check, label %error +entry: + %line = alloca i8*, align 8 + %line.f = alloca i64, align 8 + %retparam = alloca i8*, align 8 + %val = alloca i32, align 4 + %val.f = alloca i64, align 8 + %retparam1 = alloca i32, align 4 + %0 = call i64 @readLine(i8** %retparam) + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after.errcheck, label %error - error: ; preds = %entry - %3 = bitcast %error_union* %line.f to i8* - %4 = bitcast %error_union* %result to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 16, i1 false) - br label %after_assign +error: ; preds = %entry + store i64 %0, i64* %line.f, align 8 + br label %after_assign - after_check: ; preds = %entry - %5 = load i8*, i8** %retparam, align 8 - store i8* %5, i8** %line, align 8 - store %error_union zeroinitializer, %error_union* %line.f, align 8 - br label %after_assign +after.errcheck: ; preds = %entry + %1 = load i8*, i8** %retparam, align 8 + store i8* %1, i8** %line, align 8 + store i64 0, i64* %line.f, align 8 + br label %after_assign - after_assign: ; preds = %after_check, %error - %err_domain1 = getelementptr inbounds %error_union, %error_union* %line.f, i32 0, i32 0 - %6 = load i64, i64* %err_domain1, align 8 - %not_err2 = icmp eq i64 %6, 0 - br i1 %not_err2, label %after_check3, label %error_block +after_assign: ; preds = %after.errcheck, %error + %load.err = load i64, i64* %line.f, align 8 + %result = icmp eq i64 %load.err, 0 + br i1 %result, label %if.then, label %if.exit9 - after_check3: ; preds = %after_assign - br label %noerr_block +if.then: ; preds = %after_assign + %2 = load i8*, i8** %line, align 8 + %3 = call i64 @atoi(i32* %retparam1, i8* %2) + %not_err2 = icmp eq i64 %3, 0 + br i1 %not_err2, label %after.errcheck4, label %error3 - noerr_block: ; preds = %after_check3 - br label %phi_trycatch_block +error3: ; preds = %if.then + store i64 %3, i64* %val.f, align 8 + br label %after_assign5 - error_block: ; preds = %after_assign - br label %phi_trycatch_block +after.errcheck4: ; preds = %if.then + %4 = load i32, i32* %retparam1, align 4 + store i32 %4, i32* %val, align 4 + store i64 0, i64* %val.f, align 8 + br label %after_assign5 - phi_trycatch_block: ; preds = %error_block, %noerr_block - %val = phi i8 [ 1, %noerr_block ], [ 0, %error_block ] - %7 = trunc i8 %val to i1 - br i1 %7, label %if.then, label %if.exit20 +after_assign5: ; preds = %after.errcheck4, %error3 + %load.err6 = load i64, i64* %val.f, align 8 + %result7 = icmp eq i64 %load.err6, 0 + br i1 %result7, label %if.then8, label %if.exit - if.then: ; preds = %phi_trycatch_block - %8 = load i8*, i8** %line, align 8 - %9 = call { i64, i64 } @atoi(i32* %retparam5, i8* %8) - %10 = bitcast %error_union* %result6 to { i64, i64 }* - store { i64, i64 } %9, { i64, i64 }* %10, align 8 - %err_domain7 = getelementptr inbounds %error_union, %error_union* %result6, i32 0, i32 0 - %11 = load i64, i64* %err_domain7, align 8 - %not_err8 = icmp eq i64 %11, 0 - br i1 %not_err8, label %after_check10, label %error9 +if.then8: ; preds = %after_assign5 + %5 = load i32, i32* %val, align 4 + %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str, i32 0, i32 0), i32 %5) + ret void - error9: ; preds = %if.then - %12 = bitcast %error_union* %val.f to i8* - %13 = bitcast %error_union* %result6 to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %12, i8* align 8 %13, i32 16, i1 false) - br label %after_assign11 +if.exit: ; preds = %after_assign5 + br label %if.exit9 - after_check10: ; preds = %if.then - %14 = load i32, i32* %retparam5, align 4 - store i32 %14, i32* %val4, align 4 - store %error_union zeroinitializer, %error_union* %val.f, align 8 - br label %after_assign11 +if.exit9: ; preds = %if.exit, %after_assign + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str.1, i32 0, i32 0)) + ret void +} - after_assign11: ; preds = %after_check10, %error9 - %err_domain12 = getelementptr inbounds %error_union, %error_union* %val.f, i32 0, i32 0 - %15 = load i64, i64* %err_domain12, align 8 - %not_err13 = icmp eq i64 %15, 0 - br i1 %not_err13, label %after_check14, label %error_block16 - - after_check14: ; preds = %after_assign11 - br label %noerr_block15 - - noerr_block15: ; preds = %after_check14 - br label %phi_trycatch_block17 - - error_block16: ; preds = %after_assign11 - br label %phi_trycatch_block17 - - phi_trycatch_block17: ; preds = %error_block16, %noerr_block15 - %val18 = phi i8 [ 1, %noerr_block15 ], [ 0, %error_block16 ] - %16 = trunc i8 %val18 to i1 - br i1 %16, label %if.then19, label %if.exit - - if.then19: ; preds = %phi_trycatch_block17 - %17 = load i32, i32* %val4, align 4 - %18 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([25 x i8], [25 x i8]* @.str, i32 0, i32 0), i32 %17) - ret void - - if.exit: ; preds = %phi_trycatch_block17 - br label %if.exit20 - - if.exit20: ; preds = %if.exit, %phi_trycatch_block - %19 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([31 x i8], [31 x i8]* @.str.1, i32 0, i32 0)) - ret void - } \ No newline at end of file diff --git a/test/test_suite/errors/try_with_unwrapper.c3t b/test/test_suite/errors/try_with_unwrapper.c3t new file mode 100644 index 000000000..e29a7a149 --- /dev/null +++ b/test/test_suite/errors/try_with_unwrapper.c3t @@ -0,0 +1,187 @@ +// #target: x64-darwin + +func int hello(int x) +{ + return x + 1; +} + +extern func int printf(char *c, ...); + +func int! tester() +{ + printf("In tester\n"); + return 222; +} + +errtype Foo +{ + A +} +func void test1() +{ + int! a = 11; + if (try int b = a && try int c = tester()) + { + printf("%d\n", hello(b)); + printf("%d\n", c); + } +} +func void main() +{ + test1(); +} + +func void test2() +{ + int! a; + if (try int b = a && hello(b)) + { + hello(b + 1); + } +} + +// #expect: try_with_unwrapper.ll + + +; Function Attrs: nounwind +define i32 @try_with_unwrapper.hello(i32 %0) #0 { +entry: + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %add = add i32 %1, 1 + ret i32 %add +} + +; Function Attrs: nounwind +declare i32 @printf(i8*, ...) #0 + +; Function Attrs: nounwind +define i64 @try_with_unwrapper.tester(i32* %0) #0 { +entry: + %reterr = alloca i64, align 8 + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i32 0, i32 0)) + store i32 222, i32* %0, align 4 + ret i64 0 +} + +; Function Attrs: nounwind +define void @try_with_unwrapper.test1() #0 { +entry: + %a = alloca i32, align 4 + %a.f = alloca i64, align 8 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %retparam = alloca i32, align 4 + store i64 0, i64* %a.f, align 8 + store i32 11, i32* %a, align 4 + store i64 0, i64* %a.f, align 8 + store i32 0, i32* %b, align 4 + %0 = load i64, i64* %a.f, align 8 + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after_check, label %catch_landing + +after_check: ; preds = %entry + %1 = load i32, i32* %a, align 4 + store i32 %1, i32* %b, align 4 + br label %phi_try_catch + +catch_landing: ; preds = %entry + br label %phi_try_catch + +phi_try_catch: ; preds = %catch_landing, %after_check + %val = phi i1 [ true, %after_check ], [ false, %catch_landing ] + br i1 %val, label %chain_next, label %fail_chain + +chain_next: ; preds = %phi_try_catch + store i32 0, i32* %c, align 4 + %2 = call i64 @try_with_unwrapper.tester(i32* %retparam) + %not_err1 = icmp eq i64 %2, 0 + br i1 %not_err1, label %after.errcheck, label %catch_landing2 + +after.errcheck: ; preds = %chain_next + %3 = load i32, i32* %retparam, align 4 + store i32 %3, i32* %c, align 4 + br label %phi_try_catch3 + +catch_landing2: ; preds = %chain_next + br label %phi_try_catch3 + +phi_try_catch3: ; preds = %catch_landing2, %after.errcheck + %val4 = phi i1 [ true, %after.errcheck ], [ false, %catch_landing2 ] + br i1 %val4, label %chain_next5, label %fail_chain + +chain_next5: ; preds = %phi_try_catch3 + br label %end_chain + +fail_chain: ; preds = %phi_try_catch3, %phi_try_catch + br label %end_chain + +end_chain: ; preds = %fail_chain, %chain_next5 + %chain.phi = phi i1 [ true, %chain_next5 ], [ false, %fail_chain ] + br i1 %chain.phi, label %if.then, label %if.exit + +if.then: ; preds = %end_chain + %4 = load i32, i32* %b, align 4 + %5 = call i32 @try_with_unwrapper.hello(i32 %4) + %6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0), i32 %5) + %7 = load i32, i32* %c, align 4 + %8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0), i32 %7) + br label %if.exit + +if.exit: ; preds = %if.then, %end_chain + ret void +} + + +; Function Attrs: nounwind +define void @try_with_unwrapper.test2() #0 { +entry: + %a = alloca i32, align 4 + %a.f = alloca i64, align 8 + %b = alloca i32, align 4 + store i64 0, i64* %a.f, align 8 + store i32 0, i32* %a, align 4 + store i32 0, i32* %b, align 4 + %0 = load i64, i64* %a.f, align 8 + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after_check, label %catch_landing + +after_check: ; preds = %entry + %1 = load i32, i32* %a, align 4 + store i32 %1, i32* %b, align 4 + br label %phi_try_catch + +catch_landing: ; preds = %entry + br label %phi_try_catch + +phi_try_catch: ; preds = %catch_landing, %after_check + %val = phi i1 [ true, %after_check ], [ false, %catch_landing ] + br i1 %val, label %chain_next, label %fail_chain + +chain_next: ; preds = %phi_try_catch + %2 = load i32, i32* %b, align 4 + %3 = call i32 @try_with_unwrapper.hello(i32 %2) + %intbool = icmp ne i32 %3, 0 + br i1 %intbool, label %chain_next1, label %fail_chain + +chain_next1: ; preds = %chain_next + br label %end_chain + +fail_chain: ; preds = %chain_next, %phi_try_catch + br label %end_chain + +end_chain: ; preds = %fail_chain, %chain_next1 + %chain.phi = phi i1 [ true, %chain_next1 ], [ false, %fail_chain ] + br i1 %chain.phi, label %if.then, label %if.exit + +if.then: ; preds = %end_chain + %4 = load i32, i32* %b, align 4 + %add = add i32 %4, 1 + %5 = call i32 @try_with_unwrapper.hello(i32 %add) + br label %if.exit + +if.exit: ; preds = %if.then, %end_chain + ret void +} + diff --git a/test/test_suite/errors/try_with_weird_stuff.c3 b/test/test_suite/errors/try_with_weird_stuff.c3 new file mode 100644 index 000000000..e4a95d478 --- /dev/null +++ b/test/test_suite/errors/try_with_weird_stuff.c3 @@ -0,0 +1,16 @@ +func void test1() +{ + + int! a; + int b; + int*! x; + if (try int &a = a) {} // #error: Expected a variable name after the type. +} + +func void test2() +{ + int! a; + int b; + int*! x; + if (try foo::z = a, b += 1) {} // #error: The 'try' must be placed last, can you change it? +} \ No newline at end of file diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index c3253b21b..5410a9ed3 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -216,7 +216,8 @@ Type argh = 234; errtype MyErr { - Type x; + X, + Y } enum Hello : int diff --git a/test/test_suite/functions/test_regression_mingw.c3t b/test/test_suite/functions/test_regression_mingw.c3t index e833b2a99..c8c7e7ba1 100644 --- a/test/test_suite/functions/test_regression_mingw.c3t +++ b/test/test_suite/functions/test_regression_mingw.c3t @@ -216,7 +216,8 @@ Type argh = 234; errtype MyErr { - Type x; + X, + Y } enum Hello : int diff --git a/test/test_suite/methods/enum_distinct_err_methods.c3t b/test/test_suite/methods/enum_distinct_err_methods.c3t index f448cf570..35a2af87a 100644 --- a/test/test_suite/methods/enum_distinct_err_methods.c3t +++ b/test/test_suite/methods/enum_distinct_err_methods.c3t @@ -3,7 +3,10 @@ module foo; import std::io; -errtype Foo; +errtype Foo +{ + X +} define Bar = distinct int; @@ -39,9 +42,9 @@ func void main() // #expect: foo.ll -define void @foo.Foo__hello(%Foo* %0) - %f = alloca %Foo*, align 8 - store %Foo* %0, %Foo** %f, align 8 +define void @foo.Foo__hello(i64* %0) + %f = alloca i64*, align 8 + store i64* %0, i64** %f, align 8 %1 = call i32 @"std::io.println"(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i32 0, i32 0)) ret void @@ -59,14 +62,13 @@ define void @foo.MyEnum__hello(i32* %0) define void @main() entry: - %f = alloca %Foo, align 8 + %f = alloca i64, align 8 %b = alloca i32, align 4 %a = alloca i32, align 4 - %0 = bitcast %Foo* %f to i8* - call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 8, i1 false) + store i64 0, i64* %f, align 8 store i32 0, i32* %b, align 4 store i32 0, i32* %a, align 4 - call void @foo.Foo__hello(%Foo* %f) + call void @foo.Foo__hello(i64* %f) call void @foo.Bar__hello(i32* %b) call void @foo.MyEnum__hello(i32* %a) ret void diff --git a/test/test_suite/statements/foreach_with_error.c3t b/test/test_suite/statements/foreach_with_error.c3t index 16a62a71c..d9872c55d 100644 --- a/test/test_suite/statements/foreach_with_error.c3t +++ b/test/test_suite/statements/foreach_with_error.c3t @@ -15,16 +15,15 @@ func void test() entry: %x = alloca [3 x i32], align 4 - %x.f = alloca %error_union, align 8 + %x.f = alloca i64, align 8 %g = alloca i32, align 4 %idx = alloca i64, align 8 %z = alloca i32, align 4 - store %error_union zeroinitializer, %error_union* %x.f, align 8 + store i64 0, i64* %x.f, align 8 %0 = bitcast [3 x i32]* %x to i8* call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 12, i1 false) store i32 0, i32* %g, align 4 - %err_domain = getelementptr inbounds %error_union, %error_union* %x.f, i32 0, i32 0 - %1 = load i64, i64* %err_domain, align 8 + %1 = load i64, i64* %x.f, align 8 %not_err = icmp eq i64 %1, 0 br i1 %not_err, label %after_check, label %foreach.exit @@ -45,12 +44,11 @@ foreach.body: %6 = load i32, i32* %z, align 4 %add = add i32 %5, %6 store i32 %add, i32* %g, align 4 - %err_domain1 = getelementptr inbounds %error_union, %error_union* %x.f, i32 0, i32 0 - %7 = load i64, i64* %err_domain1, align 8 - %not_err2 = icmp eq i64 %7, 0 - br i1 %not_err2, label %after_check3, label %voiderr + %7 = load i64, i64* %x.f, align 8 + %not_err1 = icmp eq i64 %7, 0 + br i1 %not_err1, label %after_check2, label %voiderr -after_check3: +after_check2: %arridx = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 0 store i32 1, i32* %arridx, align 4 br label %voiderr