From 9c145996b0c2d1fafde19da4f3c23e0a8f34d983 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 20 Mar 2023 16:54:45 +0100 Subject: [PATCH] $elif deprecated. --- lib/std/collections/enumset.c3 | 15 +++--- lib/std/collections/object.c3 | 21 ++++---- lib/std/core/builtin_comparison.c3 | 47 ++++++++++-------- lib/std/core/dstring.c3 | 15 +++--- lib/std/core/varstring.c3 | 15 +++--- lib/std/io/os/chdir.c3 | 9 ++-- lib/std/io/os/getcwd.c3 | 9 ++-- lib/std/libc/libc.c3 | 34 +++++++------ lib/std/libc/os/errno.c3 | 13 +++-- lib/std/math/math.c3 | 9 ++-- resources/testfragments/parsertest.c3 | 9 ++-- src/compiler/parse_global.c | 17 ++++--- src/compiler/parse_stmt.c | 9 +++- src/compiler/sema_passes.c | 12 +++-- src/compiler/sema_stmts.c | 18 ++++--- src/version.h | 2 +- test/test_suite/compile_time/ct_if.c3t | 53 +++++++++++---------- test/test_suite/compile_time/ct_if_fails.c3 | 21 ++++---- test/test_suite/macros/userland_bitcast.c3t | 11 +++-- 19 files changed, 197 insertions(+), 142 deletions(-) diff --git a/lib/std/collections/enumset.c3 b/lib/std/collections/enumset.c3 index c5c3578bb..e9eeba340 100644 --- a/lib/std/collections/enumset.c3 +++ b/lib/std/collections/enumset.c3 @@ -8,25 +8,26 @@ module std::collections::enumset; -$if (Enum.elements > 128) +$switch +$case (Enum.elements > 128): typedef EnumSetType @private = char[(Enum.elements + 7) / 8]; const IS_CHAR_ARRAY = true; -$elif (Enum.elements > 64) +$case (Enum.elements > 64): typedef EnumSetType @private = uint128; const IS_CHAR_ARRAY = false; -$elif (Enum.elements > 32 || $$C_INT_SIZE > 32) +$case (Enum.elements > 32 || $$C_INT_SIZE > 32): typedef EnumSetType @private = ulong; const IS_CHAR_ARRAY = false; -$elif (Enum.elements > 16 || $$C_INT_SIZE > 16) +$case (Enum.elements > 16 || $$C_INT_SIZE > 16): typedef EnumSetType @private = uint; const IS_CHAR_ARRAY = false; -$elif (Enum.elements > 8 || $$C_INT_SIZE > 8) +$case (Enum.elements > 8 || $$C_INT_SIZE > 8): typedef EnumSetType @private = ushort; const IS_CHAR_ARRAY = false; -$else +$default: typedef EnumSetType @private = char; const IS_CHAR_ARRAY = false; -$endif +$endswitch typedef EnumSet = distinct EnumSetType; diff --git a/lib/std/collections/object.c3 b/lib/std/collections/object.c3 index 369591f64..55679c703 100644 --- a/lib/std/collections/object.c3 +++ b/lib/std/collections/object.c3 @@ -212,24 +212,27 @@ fn void Object.set_object(Object* o, String key, Object* new_object) @private macro Object* object_from_value(value) @private { var $Type = $typeof(value); - $if (types::is_int($Type)) + +$switch + $case types::is_int($Type): return new_int(value); - $elif (types::is_float($Type)) + $case types::is_float($Type): return new_float(value); - $elif ($Type.typeid == String.typeid) + $case $Type.typeid == String.typeid: return new_string(value); - $elif ($Type.typeid == bool.typeid) + $case $Type.typeid == bool.typeid: return new_bool(value); - $elif ($Type.typeid == Object*.typeid) + $case $Type.typeid == Object*.typeid: return value; - $elif ($Type.typeid == void*.typeid) + $case $Type.typeid == void*.typeid: assert(value == null); return &NULL_OBJECT; - $elif ($checks(String s = value)) + $case $checks(String s = value): return new_string(value); - $else + $default: $assert(false, "Unsupported object type."); - $endif +$endswitch + } macro Object* Object.set(Object* o, String key, value) diff --git a/lib/std/core/builtin_comparison.c3 b/lib/std/core/builtin_comparison.c3 index 847f9fbde..b9f7f05ba 100644 --- a/lib/std/core/builtin_comparison.c3 +++ b/lib/std/core/builtin_comparison.c3 @@ -8,13 +8,14 @@ module std::core::builtin; **/ macro less(a, b) @builtin { - $if ($defined(a.less)) +$switch + $case $defined(a.less): return a.less(b); - $elif ($defined(a.compare_to)) + $case $defined(a.compare_to): return a.compare_to(b) < 0; - $else + $default: return a < b; - $endif +$endswitch } /** @@ -22,13 +23,14 @@ macro less(a, b) @builtin **/ macro less_eq(a, b) @builtin { - $if ($defined(a.less)) +$switch + $case $defined(a.less): return !b.less(a); - $elif ($defined(a.compare_to)) + $case $defined(a.compare_to): return a.compare_to(b) <= 0; - $else + $default: return a <= b; - $endif +$endswitch } /** @@ -36,13 +38,14 @@ macro less_eq(a, b) @builtin **/ macro greater(a, b) @builtin { - $if ($defined(a.less)) +$switch + $case $defined(a.less): return b.less(a); - $elif ($defined(a.compare_to)) + $case $defined(a.compare_to): return a.compare_to(b) > 0; - $else + $default: return a > b; - $endif +$endswitch } /** @@ -50,13 +53,14 @@ macro greater(a, b) @builtin **/ macro greater_eq(a, b) @builtin { - $if ($defined(a.less)) +$switch + $case $defined(a.less): return !a.less(b); - $elif ($defined(a.compare_to)) + $case $defined(a.compare_to): return a.compare_to(b) >= 0; - $else + $default: return a >= b; - $endif +$endswitch } /** @@ -64,15 +68,16 @@ macro greater_eq(a, b) @builtin **/ macro bool equals(a, b) @builtin { - $if ($defined(a.equals)) +$switch + $case $defined(a.equals): return a.equals(b); - $elif ($defined(a.compare_to)) + $case $defined(a.compare_to): return a.compare_to(b) == 0; - $elif ($defined(a.less)) + $case $defined(a.less): return !a.less(b) && !b.less(a); - $else + $default: return a == b; - $endif +$endswitch } macro min(x, ...) @builtin diff --git a/lib/std/core/dstring.c3 b/lib/std/core/dstring.c3 index 9c60f73a5..805d9c0ea 100644 --- a/lib/std/core/dstring.c3 +++ b/lib/std/core/dstring.c3 @@ -302,13 +302,14 @@ macro void DString.append(DString* str, value) $case Char32: str.append_char32(value); $default: - $if (@convertible(value, Char32)) - str.append_char32(value); - $elif (@convertible(value, String)) - str.append_chars(value); - $else - $assert(false, "Unsupported type for append – use printf instead."); - $endif + $switch + $case @convertible(value, Char32): + str.append_char32(value); + $case @convertible(value, String): + str.append_chars(value); + $default: + $assert(false, "Unsupported type for append – use printf instead."); + $endswitch $endswitch } diff --git a/lib/std/core/varstring.c3 b/lib/std/core/varstring.c3 index e3da0add8..e71e48e05 100644 --- a/lib/std/core/varstring.c3 +++ b/lib/std/core/varstring.c3 @@ -278,13 +278,14 @@ macro void VarString.append(VarString* str, value) $case Char32: str.append_char32(value); $default: - $if (@convertible(value, Char32)) - str.append_char32(value); - $elif (@convertible(value, String)) - str.append_chars(value); - $else - $assert(false, "Unsupported type for append – use printf instead."); - $endif + $switch + $case @convertible(value, Char32): + str.append_char32(value); + $case @convertible(value, String): + str.append_chars(value); + $default: + $assert(false, "Unsupported type for append – use printf instead."); + $endswitch $endswitch } diff --git a/lib/std/io/os/chdir.c3 b/lib/std/io/os/chdir.c3 index cb3bcd6d4..b5ce77f47 100644 --- a/lib/std/io/os/chdir.c3 +++ b/lib/std/io/os/chdir.c3 @@ -2,14 +2,15 @@ module std::io::os; import libc; -$if (!env::COMPILER_LIBC_AVAILABLE) +$switch + $case !env::COMPILER_LIBC_AVAILABLE: fn void! native_chdir(Path path) { unreachable("'getcwd' not available"); } -$elif (env::OS_TYPE == OsType.WIN32) + $case env::os_is_win32(): macro void! native_chdir(Path path) { @@ -20,7 +21,7 @@ macro void! native_chdir(Path path) return IoError.GENERAL_ERROR!; } -$else + $default: extern fn int _chdir(ZString) @extern("chdir"); macro void! native_chdir(Path p) @@ -39,4 +40,4 @@ macro void! native_chdir(Path p) } } -$endif \ No newline at end of file +$endswitch \ No newline at end of file diff --git a/lib/std/io/os/getcwd.c3 b/lib/std/io/os/getcwd.c3 index 66ec433ba..fd23aa42f 100644 --- a/lib/std/io/os/getcwd.c3 +++ b/lib/std/io/os/getcwd.c3 @@ -1,14 +1,15 @@ module std::io::os; import libc; -$if (!env::COMPILER_LIBC_AVAILABLE) +$switch +$case !env::COMPILER_LIBC_AVAILABLE: fn String! getcwd(Allocator* using = mem::heap()) { unreachable("'getcwd' not available"); } -$elif (env::OS_TYPE == OsType.WIN32) +$case env::os_is_win32(): extern fn Char16* _wgetcwd(Char16* buffer, int maxlen); extern fn usz wcslen(Char16* str); @@ -30,7 +31,7 @@ macro String! getcwd(Allocator* using = mem::heap()) return str::utf16to8(str16, using); } -$else +$default: extern fn ZString _getcwd(char* pwd, usz len) @extern("getcwd"); macro String! getcwd(Allocator* using = mem::heap()) @@ -50,4 +51,4 @@ macro String! getcwd(Allocator* using = mem::heap()) return res.copy(using); } -$endif \ No newline at end of file +$endswitch \ No newline at end of file diff --git a/lib/std/libc/libc.c3 b/lib/std/libc/libc.c3 index 651e92ab7..ceaf1197d 100644 --- a/lib/std/libc/libc.c3 +++ b/lib/std/libc/libc.c3 @@ -151,7 +151,8 @@ $endif typedef Fpos = long; typedef CFile = void*; -$if (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX) +$switch +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX: extern CFile __stdin @extern("stdin"); extern CFile __stdout @extern("stdout"); extern CFile __stderr @extern("stderr"); @@ -161,7 +162,7 @@ $if (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX) macro CFile stdin() { return __stdin; } macro CFile stdout() { return __stdout; } macro CFile stderr() { return __stderr; } -$elif (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX) +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX: extern CFile __stdinp; extern CFile __stdoutp; extern CFile __stderrp; @@ -170,18 +171,18 @@ $elif (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX) macro CFile stdin() { return __stdinp; } macro CFile stdout() { return __stdoutp; } macro CFile stderr() { return __stderrp; } -$elif (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.WIN32) +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.WIN32: extern fn CFile __acrt_iob_func(CInt c); extern fn usz _msize(void* ptr); macro usz malloc_size(void* ptr) { return _msize(ptr); } macro CFile stdin() { return __acrt_iob_func(0); } macro CFile stdout() { return __acrt_iob_func(1); } macro CFile stderr() { return __acrt_iob_func(2); } -$else +$default: macro CFile stdin() { return (CFile*)(uptr)0; } macro CFile stdout() { return (CFile*)(uptr)1; } macro CFile stderr() { return (CFile*)(uptr)2; } -$endif +$endswitch const HAS_MALLOC_SIZE = env::OS_TYPE == OsType.LINUX @@ -385,7 +386,7 @@ const Errno ENOEXEC = 8; // Exec format error const Errno EBADF = 9; // Bad file number const Errno ECHILD = 10; // No child processes -$if (env::OS_TYPE == OsType.MACOSX) +$if (env::OS_TYPE == MACOSX) const Errno EAGAIN = 35; // Try again Macos $else const Errno EAGAIN = 11; // Try again @@ -415,7 +416,9 @@ const Errno EPIPE = 32; // Broken pipe const Errno EDOM = 33; // Math argument out of domain of func const Errno ERANGE = 34; // Math result not representable -$if (env::OS_TYPE == OsType.MACOSX) +$switch (env::OS_TYPE) + +$case MACOSX: const Errno EDEADLK = 11; // Resource deadlock would occur MacOS const Errno ENAMETOOLONG = 63; // File name too long MacOS const Errno ELOOP = 62; // Too many symbolic links encountered @@ -426,7 +429,7 @@ const Errno ENETUNREACH = 51; // Network is unreachable MacOS const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint -$elif (env::OS_TYPE == OsType.WIN32) +$case WIN32: const Errno EDEADLK = 36; // Resource deadlock would occur Win32 const Errno ENAMETOOLONG = 38; // File name too long Win32 const Errno ELOOP = 114; // Too many symbolic links encountered @@ -437,7 +440,7 @@ const Errno ENETUNREACH = 118; // Network is unreachable const Errno ENETRESET = 117; // Network dropped connection because of reset const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint -$else +$default: const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?) const Errno ENAMETOOLONG = 36; // File name too long Linux (others?) const Errno ELOOP = 40; // Too many symbolic links encountered @@ -448,7 +451,7 @@ const Errno ENETUNREACH = 101; // Network is unreachable const Errno ENETRESET = 102; // Network dropped connection because of reset const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint -$endif +$endswitch @@ -523,27 +526,30 @@ const Errno ECONNREFUSED = 111; /* Connection refused */ const Errno EHOSTDOWN = 112; /* Host is down */ const Errno EHOSTUNREACH = 113; /* No route to host */ */ -$if (env::OS_TYPE == OsType.MACOSX) + +$switch (env::OS_TYPE) + +$case MACOSX: const Errno ETIMEDOUT = 60; // Connection timed out const Errno EINPROGRESS = 36; // Operation now in progress MacOS const Errno EALREADY = 37; // Operation already in progress MacOS const Errno EDQUOT = 69; // Quota exceeded, MacOS const Errno EWOULDBLOCK = 35; // Operation would block -$elif (env::OS_TYPE == OsType.WIN32) +$case WIN32: const Errno ETIMEDOUT = 138; // Connection timed out const Errno EALREADY = 103; // Operation already in progress const Errno EINPROGRESS = 112; // Operation now in progress Win32 const Errno EDQUOT = -122; // Quota exceeded, not in Win32 const Errno EWOULDBLOCK = 140; // Operation would block -$else +$default: const Errno ETIMEDOUT = 110; // Connection timed out const Errno EALREADY = 114; // Operation already in progress const Errno EINPROGRESS = 115; // Operation now in progress const Errno EDQUOT = 122; // Quota exceeded const Errno EWOULDBLOCK = 41; // Operation would block -$endif +$endswitch /* const Errno ESTALE = 116; /* Stale NFS file handle */ diff --git a/lib/std/libc/os/errno.c3 b/lib/std/libc/os/errno.c3 index f3d019c9e..df755ce3a 100644 --- a/lib/std/libc/os/errno.c3 +++ b/lib/std/libc/os/errno.c3 @@ -1,18 +1,21 @@ module libc::os; -$if (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.LINUX) + +$switch + +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX: extern fn int* __errno_location(); macro int errno() => *__errno_location(); macro void errno_set(int err) => *(__errno_location()) = err; -$elif (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.MACOSX) +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOSX: extern fn int* __error(); macro int errno() => *__error(); macro void errno_set(int err) => *(__error()) = err; -$elif (env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OsType.WIN32) +$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WIN32: macro int errno() { @@ -26,10 +29,10 @@ macro void errno_set(int err) => _set_errno(err); extern fn void _get_errno(int* result); extern fn void _set_errno(int err); -$else +$default: tlocal int _errno_c3 = 0; fn void errno_set(int err) => _errno_c3 = err; fn int errno() => _errno_c3; -$endif +$endswitch diff --git a/lib/std/math/math.c3 b/lib/std/math/math.c3 index 22ea5dd9c..a6953e53b 100644 --- a/lib/std/math/math.c3 +++ b/lib/std/math/math.c3 @@ -407,13 +407,14 @@ macro sqrt(x) => $$sqrt(values::promote_int(x)); macro tan(x) { var $Type = $typeof(x); - $if (types::is_vector($Type)) +$switch + $case types::is_vector($Type): return $$sin(x) / $$cos(x); - $elif ($Type.typeid == float.typeid) + $case $Type.typeid == float.typeid: return _tanf(x); - $else + $default: return _tan(x); - $endif +$endswitch } /** diff --git a/resources/testfragments/parsertest.c3 b/resources/testfragments/parsertest.c3 index 7d38e3217..eeeb0c585 100644 --- a/resources/testfragments/parsertest.c3 +++ b/resources/testfragments/parsertest.c3 @@ -109,18 +109,21 @@ generic boor2(i) return 10001; } -$if ($e > 0) +$switch + +$case $e > 0: { fn void foo() {} } -$elif ($e < 0) +$case $e < 0: { fn void foo() { printf("HELO"); } } -$else +$default: { fn void foo() { printf("OLEH"); } } +$endswitch $if ($e > 0) { diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 394b06ffa..3a5aacc37 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -5,17 +5,16 @@ #include "compiler_internal.h" #include "parser_internal.h" +static bool context_next_is_path_prefix_start(ParseContext *c); static Decl *parse_const_declaration(ParseContext *c, bool is_global); static inline Decl *parse_func_definition(ParseContext *c, AstId docs, bool is_interface); static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl); -INLINE bool parse_decl_initializer(ParseContext *c, Decl *decl); static inline Decl *parse_static_top_level(ParseContext *c); static Decl *parse_include(ParseContext *c); static bool parse_attributes_for_global(ParseContext *c, Decl *decl); - +INLINE bool parse_decl_initializer(ParseContext *c, Decl *decl); INLINE Decl *decl_new_var_current(ParseContext *c, TypeInfo *type, VarDeclKind kind); -static bool context_next_is_path_prefix_start(ParseContext *c); INLINE Decl *decl_new_var_current(ParseContext *c, TypeInfo *type, VarDeclKind kind) { @@ -125,8 +124,10 @@ static inline Decl *parse_ct_if_top_level(ParseContext *c) if (!parse_top_level_block(c, &ct->ct_if_decl.then, TOKEN_CT_ENDIF, TOKEN_CT_ELIF, TOKEN_CT_ELSE)) return poisoned_decl; CtIfDecl *ct_if_decl = &ct->ct_if_decl; + // Chain elif while (tok_is(c, TOKEN_CT_ELIF)) { + sema_warning_at(c->span, "$elif is deprecated, use $switch instead."); Decl *ct_elif = decl_new_ct(DECL_CT_ELIF, c->span); advance_and_verify(c, TOKEN_CT_ELIF); ASSIGN_EXPR_OR_RET(ct_elif->ct_elif_decl.expr, parse_const_paren_expr(c), poisoned_decl); @@ -135,6 +136,7 @@ static inline Decl *parse_ct_if_top_level(ParseContext *c) ct_if_decl->elif = ct_elif; ct_if_decl = &ct_elif->ct_elif_decl; } + // final else if (tok_is(c, TOKEN_CT_ELSE)) { Decl *ct_else = decl_new_ct(DECL_CT_ELSE, c->span); @@ -179,7 +181,7 @@ static inline Decl *parse_ct_case(ParseContext *c) } /** - * ct_switch_top_level ::= CT_SWITCH const_paren_expr '{' ct_case* '}' + * ct_switch_top_level ::= CT_SWITCH const_paren_expr? ct_case* CT_ENDSWITCH * @param c * @return the declaration if successfully parsed, NULL otherwise. */ @@ -187,8 +189,11 @@ static inline Decl *parse_ct_switch_top_level(ParseContext *c) { Decl *ct = decl_new_ct(DECL_CT_SWITCH, c->span); advance_and_verify(c, TOKEN_CT_SWITCH); - ASSIGN_EXPR_OR_RET(ct->ct_switch_decl.expr, parse_const_paren_expr(c), poisoned_decl); - consume_deprecated_symbol(c, TOKEN_COLON); + if (!tok_is(c, TOKEN_CT_CASE) && !tok_is(c, TOKEN_CT_DEFAULT) && !tok_is(c, TOKEN_CT_ENDSWITCH)) + { + ASSIGN_EXPR_OR_RET(ct->ct_switch_decl.expr, parse_const_paren_expr(c), poisoned_decl); + consume_deprecated_symbol(c, TOKEN_COLON); + } while (!try_consume(c, TOKEN_CT_ENDSWITCH)) { ASSIGN_DECL_OR_RET(Decl *result, parse_ct_case(c), poisoned_decl); diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 7d4368320..35fa7768b 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -930,7 +930,9 @@ static inline Ast* parse_ct_else_stmt(ParseContext *c) static inline Ast* parse_ct_if_stmt(ParseContext *c, bool is_elif) { Ast *ast = ast_new_curr(c, AST_CT_IF_STMT); + if (is_elif) sema_warning_at(c->span, "$elif is deprecated, use $switch instead."); advance_and_verify(c, is_elif ? TOKEN_CT_ELIF : TOKEN_CT_IF); + ASSIGN_EXPR_OR_RET(ast->ct_if_stmt.expr, parse_const_paren_expr(c), poisoned_ast); consume_deprecated_symbol(c, TOKEN_COLON); if (!parse_ct_compound_stmt(c, &ast->ct_if_stmt.then)) return poisoned_ast; @@ -1076,8 +1078,11 @@ static inline Ast* parse_ct_switch_stmt(ParseContext *c) { Ast *ast = ast_new_curr(c, AST_CT_SWITCH_STMT); advance_and_verify(c, TOKEN_CT_SWITCH); - ASSIGN_EXPRID_OR_RET(ast->ct_switch_stmt.cond, parse_const_paren_expr(c), poisoned_ast); - consume_deprecated_symbol(c, TOKEN_COLON); + if (!tok_is(c, TOKEN_CT_CASE) && !tok_is(c, TOKEN_CT_DEFAULT) && !tok_is(c, TOKEN_CT_ENDSWITCH)) + { + ASSIGN_EXPRID_OR_RET(ast->ct_switch_stmt.cond, parse_const_paren_expr(c), poisoned_ast); + consume_deprecated_symbol(c, TOKEN_COLON); + } Ast **cases = NULL; while (!try_consume(c, TOKEN_CT_ENDSWITCH)) diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 6664d0fac..8a99540ba 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -178,10 +178,10 @@ static inline bool sema_analyse_top_level_if(SemaContext *context, Decl *ct_if) static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_switch) { Expr *cond = ct_switch->ct_switch_decl.expr; - if (!sema_analyse_ct_expr(context, cond)) return false; - Type *type = cond->type; + if (cond && !sema_analyse_ct_expr(context, cond)) return false; + Type *type = cond ? cond->type : type_bool; bool is_type = type == type_typeid; - ExprConst *switch_expr_const = &cond->const_expr; + ExprConst *switch_expr_const = cond ? &cond->const_expr : NULL; Decl **cases = ct_switch->ct_switch_decl.cases; unsigned case_count = vec_size(cases); @@ -213,6 +213,12 @@ static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_ SEMA_ERROR(expr, "The $case must have a constant expression."); return false; } + if (!cond) + { + if (!expr->const_expr.b) continue; + if (matched_case == case_count) matched_case = (int)i; + continue; + } if (to_expr && to_expr->expr_kind != EXPR_CONST) { SEMA_ERROR(to_expr, "The $case must have a constant expression."); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 87c086100..508496158 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2193,12 +2193,12 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem { unsigned ct_context = sema_context_push_ct_stack(context); // Evaluate the switch statement - Expr *cond = exprptr(statement->ct_switch_stmt.cond); - if (!sema_analyse_ct_expr(context, cond)) goto FAILED; + Expr *cond = exprptrzero(statement->ct_switch_stmt.cond); + if (cond && !sema_analyse_ct_expr(context, cond)) goto FAILED; // If we have a type, then we do different evaluation // compared to when it is a value. - Type *type = cond->type; + Type *type = cond ? cond->type : type_bool; bool is_type = false; switch (type_flatten(type)->type_kind) { @@ -2214,11 +2214,12 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem if (expr_is_const_string(cond)) break; FALLTHROUGH; default: + assert(cond); SEMA_ERROR(cond, "Only types, strings, enums, integers, floats and booleans may be used with '$switch'."); goto FAILED; } - ExprConst *switch_expr_const = &cond->const_expr; + ExprConst *switch_expr_const = cond ? &cond->const_expr : NULL; Ast **cases = statement->ct_switch_stmt.body; unsigned case_count = vec_size(cases); @@ -2234,7 +2235,6 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem { case AST_CASE_STMT: { - Expr *expr = stmt->case_stmt.expr; Expr *to_expr = stmt->case_stmt.to_expr; if (to_expr) @@ -2264,6 +2264,12 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem SEMA_ERROR(expr, "The $case must have a constant expression."); goto FAILED; } + if (!cond) + { + if (!expr->const_expr.b) continue; + if (matched_case == case_count) matched_case = (int)i; + continue; + } if (to_expr && !expr_is_const(to_expr)) { SEMA_ERROR(to_expr, "The $case must have a constant expression."); @@ -2292,7 +2298,7 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem } if (expr_const_in_range(switch_expr_const, const_expr, const_to_expr)) { - matched_case = (int) i; + matched_case = (int)i; } break; } diff --git a/src/version.h b/src/version.h index f02a4ee9a..bcc822d77 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.114" \ No newline at end of file +#define COMPILER_VERSION "0.4.115" \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_if.c3t b/test/test_suite/compile_time/ct_if.c3t index 49a0cdf57..23c4dc0cb 100644 --- a/test/test_suite/compile_time/ct_if.c3t +++ b/test/test_suite/compile_time/ct_if.c3t @@ -1,60 +1,63 @@ $if (0) $else - $if (0) - $elif (0) - $elif (0) - $else + $switch + $case false: + $case false: + $case false: + $default: int x = 1; - $endif + $endswitch $endif -$if (0) +$switch +$case false: $assert(false); -$elif (0) +$case false: $assert(false); -$else +$default: $assert(true); -$endif +$endswitch $if (1) $assert(true); int d = 5; -$elif (0) -$assert(false); $else $assert(false); $endif -$if (0) +$switch +$case false: $assert(true); -$elif (1) +$case true: $assert(true); int c = 5; -$else +$default: $assert(false); -$endif +$endswitch -$if (0) +$switch +$case false: $assert(true); -$elif (1) +$case true: $assert(true); int b = 4; -$elif (0) +$case false: $assert(false); -$else +$default: $assert(false); -$endif +$endswitch -$if (0) +$switch +$case false: $assert(true); -$elif (0) +$case false: $assert(false); -$elif (1) +$case true: $assert(true); int a = 3; -$else +$default: $assert(false); -$endif +$endswitch // #expect: ct_if.ll diff --git a/test/test_suite/compile_time/ct_if_fails.c3 b/test/test_suite/compile_time/ct_if_fails.c3 index 3501558c1..660df3b05 100644 --- a/test/test_suite/compile_time/ct_if_fails.c3 +++ b/test/test_suite/compile_time/ct_if_fails.c3 @@ -16,13 +16,16 @@ $else // #error: Expected the start of a global declaration here $endif -$if (1) -$elif (2) -$else -$endif +$switch +$case true: +$case true: +$default: +$endswitch + +$switch +$case true: +$case true: +$case true: +$default: +$endswitch -$if (1) -$elif (2) -$elif (3) -$else -$endif diff --git a/test/test_suite/macros/userland_bitcast.c3t b/test/test_suite/macros/userland_bitcast.c3t index 7b8d268c7..2fb7f7e61 100644 --- a/test/test_suite/macros/userland_bitcast.c3t +++ b/test/test_suite/macros/userland_bitcast.c3t @@ -6,35 +6,36 @@ macro testbitcast(expr, $Type) $Type x @noinit; var $size = (usz)($sizeof(expr)); - $if ($alignof(expr) >= 8 && $Type.alignof >= 8) +$switch + $case $alignof(expr) >= 8 && $Type.alignof >= 8: ulong *b = (ulong*)(&expr); ulong *to = (ulong*)(&x); for (usz i = 0; i < $size; i += 8) { to[i] = b[i]; } - $elif ($alignof(expr) >= 4 && $Type.alignof >= 4) + $case $alignof(expr) >= 4 && $Type.alignof >= 4: uint* b = (uint*)(&expr); uint* to = (uint*)(&x); for (usz i = 0; i < $size; i += 4) { to[i] = b[i]; } - $elif ($alignof(expr) >= 2 && $Type.alignof >= 2) + $case $alignof(expr) >= 2 && $Type.alignof >= 2: ushort* b = (ushort*)(&expr); ushort* to = (ushort*)(&x); for (usz i = 0; i < $size; i += 2) { to[i] = b[i]; } - $else + $default: char* b = (char*)(&expr); char* to = (char*)(&x); for (usz i = 0; i < $size; i++) { to[i] = b[i]; } - $endif +$endswitch return x; }