mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Remove assert(try). Prevent nextcase with expression if there is no switch. Fix issue with multiple declarations and compile time variables. Nextcase default. Dropped support for non-const-int ranges.
This commit is contained in:
committed by
Christoffer Lerno
parent
3b0370c8bb
commit
dddeca1856
@@ -6,6 +6,7 @@
|
||||
- Exhaustive switches with enums has better analysis.
|
||||
- Globals may now be initialized with optional values.
|
||||
- New generic syntax.
|
||||
- Added `nextcase default`.
|
||||
- Added `$embed` to embed binary data.
|
||||
- Ad hoc generics are now allowed.
|
||||
- Allow inferred type on method first argument.
|
||||
@@ -18,7 +19,10 @@
|
||||
- `$assert` now uses `$assert <expr> : <optional message>`
|
||||
- `$error` is syntax sugar for `$assert false : "Some message"`
|
||||
- `$include`, `$echo` no longer has mandatory `()` around the arguments.
|
||||
- `assert` no longer allows "try unwrap"
|
||||
- Updated cpu arguments for x86
|
||||
- Removed support for ranged case statements that were floats or enums, or non-constant.
|
||||
- `nextcase` with a constant expression that does not match any case is an error.
|
||||
- Dropped support for LLVM 13-14.
|
||||
- Updated grammar and lexer definition.
|
||||
- Removal of `$elif`.
|
||||
@@ -148,6 +152,7 @@
|
||||
- Updates to how variadics are implemented.
|
||||
- Fixes to shift checks.
|
||||
- Fixes to string parsing.
|
||||
- Fixed issues with ranged cases.
|
||||
- Disallow trailing ',' in function parameter list.
|
||||
- Fixed errors on flexible array slices.
|
||||
- Fix of `readdir` issues on macOS.
|
||||
|
||||
@@ -722,6 +722,8 @@ nextcase_stmt
|
||||
| NEXTCASE expr ';'
|
||||
| NEXTCASE CONST_IDENT ':' type ';'
|
||||
| NEXTCASE type ';'
|
||||
| NEXTCASE CONST_IDENT ':' DEFAULT ';'
|
||||
| NEXTCASE DEFAULT ';'
|
||||
| NEXTCASE ';'
|
||||
;
|
||||
|
||||
@@ -753,19 +755,14 @@ ct_if_stmt
|
||||
| CT_IF constant_expr ':' opt_stmt_list CT_ELSE opt_stmt_list CT_ENDIF
|
||||
;
|
||||
|
||||
assert_expr
|
||||
: try_unwrap_chain
|
||||
| expr
|
||||
;
|
||||
|
||||
assert_expr_list
|
||||
: ',' expr
|
||||
| ',' expr ',' assert_expr_list
|
||||
: expr
|
||||
| expr ',' assert_expr_list
|
||||
;
|
||||
|
||||
assert_stmt
|
||||
: ASSERT '(' assert_expr ')' ';'
|
||||
| ASSERT '(' assert_expr assert_expr_list ';'
|
||||
: ASSERT '(' expr ')' ';'
|
||||
| ASSERT '(' expr ',' assert_expr_list ')' ';'
|
||||
;
|
||||
|
||||
asm_stmts
|
||||
@@ -811,7 +808,7 @@ asm_stmt
|
||||
;
|
||||
|
||||
asm_block_stmt
|
||||
: ASM '(' expr ')'
|
||||
: ASM '(' constant_expr ')'
|
||||
| ASM '{' asm_stmts '}'
|
||||
| ASM '{' '}'
|
||||
;
|
||||
|
||||
@@ -1229,8 +1229,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *expr;
|
||||
Expr *to_expr;
|
||||
ExprId expr;
|
||||
ExprId to_expr;
|
||||
Ast *body;
|
||||
void *backend_block;
|
||||
} AstCaseStmt;
|
||||
@@ -1347,7 +1347,8 @@ typedef struct
|
||||
struct
|
||||
{
|
||||
Label label;
|
||||
Expr *expr;
|
||||
ExprId expr;
|
||||
bool is_default;
|
||||
};
|
||||
struct
|
||||
{
|
||||
|
||||
@@ -606,8 +606,8 @@ RETRY:
|
||||
case AST_CASE_STMT:
|
||||
copy_reg_ref(c, source, ast);
|
||||
MACRO_COPY_AST(ast->case_stmt.body);
|
||||
MACRO_COPY_EXPR(ast->case_stmt.expr);
|
||||
MACRO_COPY_EXPR(ast->case_stmt.to_expr);
|
||||
MACRO_COPY_EXPRID(ast->case_stmt.expr);
|
||||
MACRO_COPY_EXPRID(ast->case_stmt.to_expr);
|
||||
break;
|
||||
case AST_COMPOUND_STMT:
|
||||
MACRO_COPY_ASTID(ast->compound_stmt.first_stmt);
|
||||
@@ -673,7 +673,7 @@ RETRY:
|
||||
MACRO_COPY_ASTID(ast->if_stmt.then_body);
|
||||
break;
|
||||
case AST_NEXTCASE_STMT:
|
||||
MACRO_COPY_EXPR(ast->nextcase_stmt.expr);
|
||||
MACRO_COPY_EXPRID(ast->nextcase_stmt.expr);
|
||||
break;
|
||||
case AST_NOP_STMT:
|
||||
break;
|
||||
|
||||
@@ -624,10 +624,10 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
|
||||
LLVMBasicBlockRef block = case_stmt->case_stmt.backend_block;
|
||||
if (case_stmt == default_case) continue;
|
||||
BEValue be_value;
|
||||
llvm_emit_expr(c, &be_value, case_stmt->case_stmt.expr);
|
||||
llvm_emit_exprid(c, &be_value, case_stmt->case_stmt.expr);
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
BEValue equals;
|
||||
Expr *to_expr = case_stmt->case_stmt.to_expr;
|
||||
Expr *to_expr = exprptrzero(case_stmt->case_stmt.to_expr);
|
||||
if (to_expr)
|
||||
{
|
||||
BEValue to_value;
|
||||
@@ -827,13 +827,13 @@ static void llvm_emit_switch_body(GenContext *c, BEValue *switch_value, Ast *swi
|
||||
{
|
||||
LLVMValueRef case_value;
|
||||
BEValue be_value;
|
||||
Expr *from = case_stmt->case_stmt.expr;
|
||||
Expr *from = exprptr(case_stmt->case_stmt.expr);
|
||||
assert(expr_is_const(from));
|
||||
llvm_emit_expr(c, &be_value, case_stmt->case_stmt.expr);
|
||||
llvm_emit_expr(c, &be_value, from);
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
case_value = be_value.value;
|
||||
LLVMAddCase(switch_stmt, case_value, block);
|
||||
Expr *to_expr =case_stmt->case_stmt.to_expr;
|
||||
Expr *to_expr = exprptrzero(case_stmt->case_stmt.to_expr);
|
||||
if (to_expr)
|
||||
{
|
||||
BEValue to_value;
|
||||
@@ -1440,6 +1440,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
|
||||
{
|
||||
BEValue value;
|
||||
FOREACH_BEGIN(Decl *decl, ast->decls_stmt)
|
||||
if (!decl) continue;
|
||||
llvm_emit_local_decl(c, decl, &value);
|
||||
FOREACH_END();
|
||||
break;
|
||||
|
||||
@@ -274,18 +274,6 @@ static inline Expr *parse_try_unwrap_chain(ParseContext *c)
|
||||
return try_unwrap_chain;
|
||||
}
|
||||
|
||||
/**
|
||||
* assert_expr ::= try_unwrap_chain | expr
|
||||
*/
|
||||
Expr *parse_assert_expr(ParseContext *c)
|
||||
{
|
||||
if (tok_is(c, TOKEN_TRY))
|
||||
{
|
||||
return parse_try_unwrap_chain(c);
|
||||
}
|
||||
return parse_expr(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* cond_list ::= ((expr | decl-expr) COMMA)* (expr | decl-expr | try_unwrap_chain | catch_unwrap )
|
||||
*
|
||||
|
||||
@@ -134,10 +134,7 @@ static inline Path *parse_module_path(ParseContext *c)
|
||||
* | CONST_IDENT
|
||||
* ;
|
||||
*
|
||||
* module_params
|
||||
* : module_param
|
||||
* | module_params ',' module_param
|
||||
* ;
|
||||
* module_params ::= '(<' module_param (',' module_param)* '>)'
|
||||
*/
|
||||
static inline bool parse_optional_module_params(ParseContext *c, const char ***tokens_ref)
|
||||
{
|
||||
|
||||
@@ -563,15 +563,16 @@ static inline Ast *parse_case_stmt(ParseContext *c, TokenType case_type, TokenTy
|
||||
{
|
||||
Ast *ast = new_ast(AST_CASE_STMT, c->span);
|
||||
advance(c);
|
||||
ASSIGN_EXPR_OR_RET(ast->case_stmt.expr, parse_expr(c), poisoned_ast);
|
||||
ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), poisoned_ast);
|
||||
ast->case_stmt.expr = exprid(expr);
|
||||
// Change type -> type.typeid
|
||||
if (ast->case_stmt.expr->expr_kind == EXPR_TYPEINFO)
|
||||
if (expr->expr_kind == EXPR_TYPEINFO)
|
||||
{
|
||||
ast->case_stmt.expr->expr_kind = EXPR_TYPEID;
|
||||
expr->expr_kind = EXPR_TYPEID;
|
||||
}
|
||||
if (try_consume(c, TOKEN_DOTDOT))
|
||||
{
|
||||
ASSIGN_EXPR_OR_RET(ast->case_stmt.to_expr, parse_expr(c), poisoned_ast);
|
||||
ASSIGN_EXPRID_OR_RET(ast->case_stmt.to_expr, parse_expr(c), poisoned_ast);
|
||||
}
|
||||
if (!try_consume(c, TOKEN_COLON))
|
||||
{
|
||||
@@ -594,7 +595,7 @@ static inline Ast *parse_default_stmt(ParseContext *c, TokenType case_type, Toke
|
||||
TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':' after 'default'.", poisoned_ast);
|
||||
RANGE_EXTEND_PREV(ast);
|
||||
ASSIGN_AST_OR_RET(ast->case_stmt.body, parse_case_stmts(c, case_type, default_type), poisoned_ast);
|
||||
ast->case_stmt.expr = NULL;
|
||||
ast->case_stmt.expr = 0;
|
||||
return ast;
|
||||
}
|
||||
|
||||
@@ -806,7 +807,14 @@ static inline Ast* parse_nextcase_stmt(ParseContext *c)
|
||||
parse_optional_label_target(c, &ast->nextcase_stmt.label);
|
||||
advance_and_verify(c, TOKEN_COLON);
|
||||
}
|
||||
ASSIGN_EXPR_OR_RET(ast->nextcase_stmt.expr, parse_expr(c), poisoned_ast);
|
||||
if (try_consume(c, TOKEN_DEFAULT))
|
||||
{
|
||||
ast->nextcase_stmt.is_default = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSIGN_EXPRID_OR_RET(ast->nextcase_stmt.expr, parse_expr(c), poisoned_ast);
|
||||
}
|
||||
CONSUME_EOS_OR_RET(poisoned_ast);
|
||||
return ast;
|
||||
}
|
||||
@@ -1076,7 +1084,7 @@ static inline Ast *parse_assert_stmt(ParseContext *c)
|
||||
Ast *ast = ast_new_curr(c, AST_ASSERT_STMT);
|
||||
advance_and_verify(c, TOKEN_ASSERT);
|
||||
TRY_CONSUME_OR_RET(TOKEN_LPAREN, "'assert' needs a '(' here, did you forget it?", poisoned_ast);
|
||||
ASSIGN_EXPRID_OR_RET(ast->assert_stmt.expr, parse_assert_expr(c), poisoned_ast);
|
||||
ASSIGN_EXPRID_OR_RET(ast->assert_stmt.expr, parse_expr(c), poisoned_ast);
|
||||
|
||||
if (try_consume(c, TOKEN_COMMA))
|
||||
{
|
||||
|
||||
@@ -44,7 +44,6 @@ Expr *parse_integer(ParseContext *c, Expr *left);
|
||||
Expr *parse_decl_or_expr(ParseContext *c, Decl **decl_ref);
|
||||
void recover_top_level(ParseContext *c);
|
||||
Expr *parse_cond(ParseContext *c);
|
||||
Expr *parse_assert_expr(ParseContext *c);
|
||||
Ast* parse_compound_stmt(ParseContext *c);
|
||||
Ast *parse_short_body(ParseContext *c, TypeInfoId return_type, bool require_eos);
|
||||
|
||||
|
||||
@@ -6188,25 +6188,19 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr)
|
||||
{
|
||||
if (context->call_env.kind != CALL_ENV_FUNCTION && context->call_env.kind != CALL_ENV_CHECKS)
|
||||
{
|
||||
SEMA_ERROR(expr, "Rethrow cannot be used outside of a function.");
|
||||
return false;
|
||||
RETURN_SEMA_ERROR(expr, "Rethrow cannot be used outside of a function.");
|
||||
}
|
||||
|
||||
Expr *inner = expr->rethrow_expr.inner;
|
||||
if (!sema_analyse_expr(context, inner)) return false;
|
||||
|
||||
if (context->active_scope.in_defer)
|
||||
{
|
||||
SEMA_ERROR(expr, "Returns are not allowed inside of defers.");
|
||||
return false;
|
||||
}
|
||||
if (context->active_scope.in_defer) RETURN_SEMA_ERROR(expr, "Rethrows are not allowed inside of defers.");
|
||||
|
||||
expr->type = type_no_optional(inner->type);
|
||||
|
||||
if (!IS_OPTIONAL(inner))
|
||||
{
|
||||
SEMA_ERROR(expr, "No optional to rethrow before '!' in the expression, please remove '!'.");
|
||||
return false;
|
||||
RETURN_SEMA_ERROR(expr, "No optional to rethrow before '!' in the expression, please remove '!'.");
|
||||
}
|
||||
|
||||
if (context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO))
|
||||
@@ -6221,8 +6215,8 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr)
|
||||
expr->rethrow_expr.in_block = NULL;
|
||||
if (context->rtype && context->rtype->type_kind != TYPE_OPTIONAL)
|
||||
{
|
||||
SEMA_ERROR(expr, "This expression implicitly returns with an optional result, but the function does not allow optional results. Did you mean to use '!!' instead?");
|
||||
return false;
|
||||
RETURN_SEMA_ERROR(expr, "This expression implicitly returns with an optional result, "
|
||||
"but the function does not allow optional results. Did you mean to use '!!' instead?");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -165,8 +165,8 @@ static void sema_trace_stmt_liveness(Ast *ast)
|
||||
}
|
||||
return;
|
||||
case AST_CASE_STMT:
|
||||
sema_trace_expr_liveness(ast->case_stmt.expr);
|
||||
sema_trace_expr_liveness(ast->case_stmt.to_expr);
|
||||
sema_trace_exprid_liveness(ast->case_stmt.expr);
|
||||
sema_trace_exprid_liveness(ast->case_stmt.to_expr);
|
||||
sema_trace_stmt_liveness(ast->case_stmt.body);
|
||||
return;
|
||||
case AST_DEFAULT_STMT:
|
||||
|
||||
@@ -88,12 +88,6 @@ static inline bool sema_analyse_assert_stmt(SemaContext *context, Ast *statement
|
||||
FOREACH_END();
|
||||
}
|
||||
|
||||
// Handle force unwrapping using assert, e.g. assert(try x)
|
||||
if (expr->expr_kind == EXPR_TRY_UNWRAP_CHAIN)
|
||||
{
|
||||
return sema_analyse_try_unwrap_chain(context, expr, COND_TYPE_UNWRAP_BOOL);
|
||||
}
|
||||
|
||||
// Check the conditional inside
|
||||
if (!sema_analyse_cond_expr(context, expr)) return false;
|
||||
|
||||
@@ -1011,15 +1005,19 @@ static inline bool sema_analyse_cond(SemaContext *context, Expr *expr, CondType
|
||||
|
||||
static inline bool sema_analyse_decls_stmt(SemaContext *context, Ast *statement)
|
||||
{
|
||||
bool should_nop = false;
|
||||
FOREACH_BEGIN(Decl *decl, statement->decls_stmt)
|
||||
bool should_nop = true;
|
||||
FOREACH_BEGIN_IDX(i, Decl *decl, statement->decls_stmt)
|
||||
VarDeclKind kind = decl->var.kind;
|
||||
if (kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT)
|
||||
{
|
||||
if (!sema_analyse_var_decl_ct(context, decl)) return false;
|
||||
statement->decls_stmt[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_var_decl(context, decl, true)) return false;
|
||||
should_nop = false;
|
||||
}
|
||||
assert(!should_nop);
|
||||
if (!sema_analyse_var_decl(context, decl, true)) return false;
|
||||
FOREACH_END();
|
||||
if (should_nop) statement->ast_kind = AST_NOP_STMT;
|
||||
return true;
|
||||
@@ -1238,10 +1236,8 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement)
|
||||
static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statement)
|
||||
{
|
||||
// Pull out the relevant data.
|
||||
DeclId index_id = statement->foreach_stmt.index;
|
||||
DeclId var_id = statement->foreach_stmt.variable;
|
||||
Decl *var = var_id ? declptr(var_id) : NULL;
|
||||
Decl *index = index_id ? declptr(index_id) : NULL;
|
||||
Decl *var = declptr(statement->foreach_stmt.variable);
|
||||
Decl *index = declptrzero(statement->foreach_stmt.index);
|
||||
Expr *enumerator = exprptr(statement->foreach_stmt.enumeration);
|
||||
AstId body = statement->foreach_stmt.body;
|
||||
AstId first_stmt = 0;
|
||||
@@ -1798,7 +1794,7 @@ static bool context_labels_exist_in_scope(SemaContext *context)
|
||||
static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
|
||||
{
|
||||
context->active_scope.jump_end = true;
|
||||
if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr)
|
||||
if (!context->next_target && !statement->nextcase_stmt.label.name && !statement->nextcase_stmt.expr && !statement->nextcase_stmt.is_default)
|
||||
{
|
||||
if (context->next_switch)
|
||||
{
|
||||
@@ -1831,8 +1827,28 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Plain next.
|
||||
if (!statement->nextcase_stmt.expr)
|
||||
// Handle jump to default.
|
||||
Ast **cases = parent->switch_stmt.cases;
|
||||
if (statement->nextcase_stmt.is_default)
|
||||
{
|
||||
Ast *default_ast = NULL;
|
||||
FOREACH_BEGIN(Ast *cs, cases)
|
||||
if (cs->ast_kind == AST_DEFAULT_STMT)
|
||||
{
|
||||
default_ast = cs;
|
||||
break;
|
||||
}
|
||||
FOREACH_END();
|
||||
if (!default_ast) RETURN_SEMA_ERROR(statement, "There is no 'default' in the switch to jump to.");
|
||||
statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true);
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(default_ast);
|
||||
statement->nextcase_stmt.switch_expr = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
Expr *value = exprptrzero(statement->nextcase_stmt.expr);
|
||||
statement->nextcase_stmt.switch_expr = NULL;
|
||||
if (!value)
|
||||
{
|
||||
assert(context->next_target);
|
||||
statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true);
|
||||
@@ -1840,12 +1856,17 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
|
||||
return true;
|
||||
}
|
||||
|
||||
Expr *cond = exprptr(parent->switch_stmt.cond);
|
||||
if (statement->nextcase_stmt.expr->expr_kind == EXPR_TYPEINFO)
|
||||
Expr *cond = exprptrzero(parent->switch_stmt.cond);
|
||||
|
||||
if (!cond)
|
||||
{
|
||||
TypeInfo *type_info = statement->nextcase_stmt.expr->type_expr;
|
||||
RETURN_SEMA_ERROR(statement, "'nextcase' cannot be used with an expressionless switch.");
|
||||
}
|
||||
|
||||
if (value->expr_kind == EXPR_TYPEINFO)
|
||||
{
|
||||
TypeInfo *type_info = value->type_expr;
|
||||
if (!sema_resolve_type_info(context, type_info)) return false;
|
||||
Ast **cases;
|
||||
statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true);
|
||||
if (cond->type->canonical != type_typeid)
|
||||
{
|
||||
@@ -1855,70 +1876,50 @@ static bool sema_analyse_nextcase_stmt(SemaContext *context, Ast *statement)
|
||||
}
|
||||
cases = parent->switch_stmt.cases;
|
||||
|
||||
Ast *default_stmt = NULL;
|
||||
Type *type = type_info->type->canonical;
|
||||
VECEACH(cases, i)
|
||||
{
|
||||
Ast *case_stmt = cases[i];
|
||||
if (case_stmt->ast_kind == AST_DEFAULT_STMT)
|
||||
{
|
||||
default_stmt = case_stmt;
|
||||
break;
|
||||
}
|
||||
Expr *expr = case_stmt->case_stmt.expr;
|
||||
if (case_stmt->ast_kind == AST_DEFAULT_STMT) continue;
|
||||
Expr *expr = exprptr(case_stmt->case_stmt.expr);
|
||||
if (expr_is_const(expr) && expr->const_expr.typeid == type)
|
||||
{
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(case_stmt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (default_stmt)
|
||||
{
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(default_stmt);
|
||||
return true;
|
||||
}
|
||||
SEMA_ERROR(type_info, "There is no case for type '%s'.", type_to_error_string(type_info->type));
|
||||
return false;
|
||||
}
|
||||
|
||||
Expr *target = statement->nextcase_stmt.expr;
|
||||
|
||||
Type *expected_type = parent->ast_kind == AST_SWITCH_STMT ? cond->type : type_anyfault;
|
||||
|
||||
if (!sema_analyse_expr_rhs(context, expected_type, target, false)) return false;
|
||||
if (!sema_analyse_expr_rhs(context, expected_type, value, false)) return false;
|
||||
|
||||
statement->nextcase_stmt.defer_id = context_get_defers(context, context->active_scope.defer_last, parent->switch_stmt.defer, true);
|
||||
|
||||
if (expr_is_const(target))
|
||||
if (expr_is_const(value))
|
||||
{
|
||||
Ast *default_stmt = NULL;
|
||||
VECEACH(parent->switch_stmt.cases, i)
|
||||
{
|
||||
Ast *case_stmt = parent->switch_stmt.cases[i];
|
||||
if (case_stmt->ast_kind == AST_DEFAULT_STMT)
|
||||
{
|
||||
default_stmt = case_stmt;
|
||||
break;
|
||||
}
|
||||
ExprConst *const_expr = &case_stmt->case_stmt.expr->const_expr;
|
||||
ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &case_stmt->case_stmt.to_expr->const_expr : const_expr;
|
||||
if (expr_const_in_range(&target->const_expr, const_expr, to_const_expr))
|
||||
Expr *from = exprptr(case_stmt->case_stmt.expr);
|
||||
if (case_stmt->ast_kind == AST_DEFAULT_STMT) continue;
|
||||
if (!expr_is_const(from)) goto VARIABLE_JUMP;
|
||||
ExprConst *const_expr = &from->const_expr;
|
||||
ExprConst *to_const_expr = case_stmt->case_stmt.to_expr ? &exprptr(case_stmt->case_stmt.to_expr)->const_expr : const_expr;
|
||||
if (expr_const_in_range(&value->const_expr, const_expr, to_const_expr))
|
||||
{
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(case_stmt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (default_stmt)
|
||||
{
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(default_stmt);
|
||||
return true;
|
||||
}
|
||||
SEMA_ERROR(statement, "'nextcase' needs to jump to an exact case statement.");
|
||||
SEMA_ERROR(value, "There is no 'case %s' in the switch, please check if a case is missing or if this value is incorrect.", expr_const_to_error_string(&value->const_expr));
|
||||
return false;
|
||||
}
|
||||
|
||||
VARIABLE_JUMP:
|
||||
statement->nextcase_stmt.case_switch_stmt = astid(parent);
|
||||
statement->nextcase_stmt.switch_expr = target;
|
||||
statement->nextcase_stmt.switch_expr = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2013,7 +2014,7 @@ static inline bool sema_analyse_compound_statement_no_scope(SemaContext *context
|
||||
|
||||
static inline bool sema_check_type_case(SemaContext *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index)
|
||||
{
|
||||
Expr *expr = case_stmt->case_stmt.expr;
|
||||
Expr *expr = exprptr(case_stmt->case_stmt.expr);
|
||||
if (!sema_analyse_expr_rhs(context, type_typeid, expr, false)) return false;
|
||||
|
||||
if (expr_is_const(expr))
|
||||
@@ -2023,7 +2024,7 @@ static inline bool sema_check_type_case(SemaContext *context, Type *switch_type,
|
||||
{
|
||||
Ast *other = cases[i];
|
||||
if (other->ast_kind != AST_CASE_STMT) continue;
|
||||
Expr *other_expr = other->case_stmt.expr;
|
||||
Expr *other_expr = exprptr(other->case_stmt.expr);
|
||||
if (expr_is_const(other_expr) && other_expr->const_expr.typeid == my_type)
|
||||
{
|
||||
SEMA_ERROR(case_stmt, "The same type appears more than once.");
|
||||
@@ -2038,59 +2039,52 @@ static inline bool sema_check_type_case(SemaContext *context, Type *switch_type,
|
||||
static inline bool sema_check_value_case(SemaContext *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool *if_chained, bool *max_ranged)
|
||||
{
|
||||
assert(switch_type);
|
||||
Expr *expr = case_stmt->case_stmt.expr;
|
||||
Expr *to_expr = case_stmt->case_stmt.to_expr;
|
||||
Expr *expr = exprptr(case_stmt->case_stmt.expr);
|
||||
Expr *to_expr = exprptrzero(case_stmt->case_stmt.to_expr);
|
||||
|
||||
// 1. Try to do implicit conversion to the correct type.
|
||||
if (!sema_analyse_expr_rhs(context, switch_type, expr, false)) return false;
|
||||
if (to_expr && !sema_analyse_expr_rhs(context, switch_type, to_expr, false)) return false;
|
||||
|
||||
if (expr->expr_kind != EXPR_CONST || (to_expr && to_expr->expr_kind != EXPR_CONST))
|
||||
bool is_range = to_expr != NULL;
|
||||
bool first_is_const = expr_is_const(expr);
|
||||
if (!is_range && !first_is_const)
|
||||
{
|
||||
*if_chained = true;
|
||||
return true;
|
||||
}
|
||||
if (is_range && (!expr_is_const_int(expr) || !expr_is_const(to_expr)))
|
||||
{
|
||||
sema_error_at(extend_span_with_token(expr->span, to_expr->span), "Ranges must be constant integers.");
|
||||
return false;
|
||||
}
|
||||
ExprConst *const_expr = &expr->const_expr;
|
||||
ExprConst *to_const_expr = to_expr ? &to_expr->const_expr : const_expr;
|
||||
|
||||
if (!*max_ranged && to_const_expr != const_expr)
|
||||
if (!*max_ranged && is_range)
|
||||
{
|
||||
if (const_expr->const_kind == CONST_ENUM)
|
||||
if (int_comp(const_expr->ixx, to_const_expr->ixx, BINARYOP_GT))
|
||||
{
|
||||
assert(to_const_expr->const_kind == CONST_ENUM);
|
||||
if (to_const_expr->enum_err_val->enum_constant.ordinal < const_expr->enum_err_val->enum_constant.ordinal)
|
||||
{
|
||||
sema_error_at(extend_span_with_token(expr->span, to_expr->span),
|
||||
"A enum range must be have the enum with a lower ordinal followed by the one with a higher ordinal. "
|
||||
"It would work if you swapped their order.");
|
||||
return false;
|
||||
}
|
||||
sema_error_at(extend_span_with_token(expr->span, to_expr->span),
|
||||
"The range is not valid because the first value (%s) is greater than the second (%s). "
|
||||
"It would work if you swapped their order.",
|
||||
int_to_str(const_expr->ixx, 10),
|
||||
int_to_str(to_const_expr->ixx, 10));
|
||||
return false;
|
||||
}
|
||||
else if (type_is_integer(expr->type))
|
||||
Int128 range = int_sub(to_const_expr->ixx, const_expr->ixx).i;
|
||||
Int128 max_range = { .low = active_target.switchrange_max_size };
|
||||
if (i128_comp(range, max_range, type_i128) == CMP_GT)
|
||||
{
|
||||
if (int_comp(const_expr->ixx, to_const_expr->ixx, BINARYOP_GT))
|
||||
{
|
||||
sema_error_at(extend_span_with_token(expr->span, to_expr->span),
|
||||
"The range is not valid because the first value (%s) is greater than the second (%s). "
|
||||
"It would work if you swapped their order.",
|
||||
int_to_str(const_expr->ixx, 10),
|
||||
int_to_str(to_const_expr->ixx, 10));
|
||||
return false;
|
||||
}
|
||||
Int128 range = int_sub(to_const_expr->ixx, const_expr->ixx).i;
|
||||
Int128 max_range = { .low = active_target.switchrange_max_size };
|
||||
if (i128_comp(range, max_range, type_i128) == CMP_GT)
|
||||
{
|
||||
*max_ranged = true;
|
||||
}
|
||||
*max_ranged = true;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < index; i++)
|
||||
{
|
||||
Ast *other = cases[i];
|
||||
if (other->ast_kind != AST_CASE_STMT) continue;
|
||||
ExprConst *other_const = &other->case_stmt.expr->const_expr;
|
||||
ExprConst *other_to_const = other->case_stmt.to_expr ? &other->case_stmt.to_expr->const_expr : other_const;
|
||||
ExprConst *other_const = &exprptr(other->case_stmt.expr)->const_expr;
|
||||
ExprConst *other_to_const = other->case_stmt.to_expr ? &exprptr(other->case_stmt.to_expr)->const_expr : other_const;
|
||||
if (expr_const_in_range(const_expr, other_const, other_to_const))
|
||||
{
|
||||
SEMA_ERROR(case_stmt, "The same case value appears more than once.");
|
||||
@@ -2117,7 +2111,7 @@ INLINE const char *create_missing_enums_in_switch_error(Ast **cases, unsigned ca
|
||||
FOREACH_BEGIN(Decl *decl, enums)
|
||||
for (unsigned i = 0; i < case_count; i++)
|
||||
{
|
||||
Expr *e = cases[i]->case_stmt.expr;
|
||||
Expr *e = exprptr(cases[i]->case_stmt.expr);
|
||||
assert(expr_is_const_enum(e));
|
||||
if (e->const_expr.enum_err_val == decl) goto CONTINUE;
|
||||
}
|
||||
@@ -2217,11 +2211,11 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc
|
||||
Ast *next = (i < case_count - 1) ? cases[i + 1] : NULL;
|
||||
PUSH_NEXT(next, statement);
|
||||
Ast *body = stmt->case_stmt.body;
|
||||
if (stmt->ast_kind == AST_CASE_STMT && body && type_switch && var_holder && expr_is_const(stmt->case_stmt.expr))
|
||||
if (stmt->ast_kind == AST_CASE_STMT && body && type_switch && var_holder && expr_is_const(exprptr(stmt->case_stmt.expr)))
|
||||
{
|
||||
if (any_switch->is_assign)
|
||||
{
|
||||
Type *real_type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid);
|
||||
Type *real_type = type_get_ptr(exprptr(stmt->case_stmt.expr)->const_expr.typeid);
|
||||
Decl *new_var = decl_new_var(any_switch->new_ident, any_switch->span,
|
||||
type_info_new_base(any_switch->is_deref
|
||||
? real_type->pointer : real_type, any_switch->span),
|
||||
@@ -2239,9 +2233,10 @@ static bool sema_analyse_switch_body(SemaContext *context, Ast *statement, Sourc
|
||||
}
|
||||
else
|
||||
{
|
||||
Type *type = type_get_ptr(stmt->case_stmt.expr->const_expr.typeid);
|
||||
Expr *expr = exprptr(stmt->case_stmt.expr);
|
||||
Type *type = type_get_ptr(expr->const_expr.typeid);
|
||||
Decl *alias = decl_new_var(var_holder->name, var_holder->span,
|
||||
type_info_new_base(type, stmt->case_stmt.expr->span),
|
||||
type_info_new_base(type, expr->span),
|
||||
VARDECL_LOCAL);
|
||||
Expr *ident_converted = expr_variable(var_holder);
|
||||
if (!cast(ident_converted, type)) return false;
|
||||
@@ -2315,15 +2310,12 @@ 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)
|
||||
Expr *expr = exprptr(stmt->case_stmt.expr);
|
||||
Expr *to_expr = exprptrzero(stmt->case_stmt.to_expr);
|
||||
if (to_expr && !type_is_integer(type))
|
||||
{
|
||||
if (!type_is_integer(type) && !type_is_float(type))
|
||||
{
|
||||
SEMA_ERROR(to_expr, "$case ranges are only allowed for floats and integers.");
|
||||
goto FAILED;
|
||||
}
|
||||
SEMA_ERROR(to_expr, "$case ranges are only allowed for integers.");
|
||||
goto FAILED;
|
||||
}
|
||||
if (is_type)
|
||||
{
|
||||
@@ -2367,12 +2359,12 @@ static inline bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statem
|
||||
{
|
||||
Ast *other_stmt = cases[j];
|
||||
if (other_stmt->ast_kind == AST_DEFAULT_STMT) continue;
|
||||
ExprConst *other_const = &other_stmt->case_stmt.expr->const_expr;
|
||||
ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &other_stmt->case_stmt.to_expr->const_expr : other_const;
|
||||
ExprConst *other_const = &exprptr(other_stmt->case_stmt.expr)->const_expr;
|
||||
ExprConst *other_const_to = other_stmt->case_stmt.to_expr ? &exprptr(other_stmt->case_stmt.to_expr)->const_expr : other_const;
|
||||
if (expr_const_in_range(const_expr, other_const, other_const_to))
|
||||
{
|
||||
SEMA_ERROR(stmt, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
|
||||
SEMA_NOTE(cases[j]->case_stmt.expr, "The previous $case was here.");
|
||||
SEMA_NOTE(exprptr(cases[j]->case_stmt.expr), "The previous $case was here.");
|
||||
goto FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "0.4.560"
|
||||
#define COMPILER_VERSION "0.4.561"
|
||||
@@ -5,7 +5,7 @@ fn void foo()
|
||||
{
|
||||
int a;
|
||||
$switch ($typeof(a))
|
||||
$case int..float: // #error: $case ranges are only allowed for floats
|
||||
$case int..float: // #error: $case ranges are only allowed for integers
|
||||
io::printn("Hello");
|
||||
$default:
|
||||
io::printn("World");
|
||||
|
||||
@@ -9,6 +9,6 @@ fn int! bar()
|
||||
foo()!;
|
||||
|};
|
||||
}
|
||||
defer foo()!; // #error: Returns are not allowed inside of defers.
|
||||
defer foo()!; // #error: Rethrows are not allowed inside of defers.
|
||||
return 1;
|
||||
}
|
||||
10
test/test_suite/errors/mixed_decl.c3
Normal file
10
test/test_suite/errors/mixed_decl.c3
Normal file
@@ -0,0 +1,10 @@
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int a, $b;
|
||||
int $c, $d;
|
||||
a++;
|
||||
$b++;
|
||||
$d++;
|
||||
}
|
||||
11
test/test_suite/errors/try_unwrap_using_assert.c3
Normal file
11
test/test_suite/errors/try_unwrap_using_assert.c3
Normal file
@@ -0,0 +1,11 @@
|
||||
module test;
|
||||
|
||||
extern fn int! maybe();
|
||||
|
||||
fn int tester(int n)
|
||||
{
|
||||
int! num = maybe();
|
||||
assert(try num, "Hello"); // #error: An expression was expected
|
||||
int x = num;
|
||||
return num;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
|
||||
extern fn int! maybe();
|
||||
|
||||
fn int tester(int n)
|
||||
{
|
||||
int! num = maybe();
|
||||
assert(try num, "Hello");
|
||||
int x = num;
|
||||
return num;
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
define i32 @test.tester(i32 %0) #0 {
|
||||
entry:
|
||||
%num = alloca i32, align 4
|
||||
%num.f = alloca i64, align 8
|
||||
%retparam = alloca i32, align 4
|
||||
%x = alloca i32, align 4
|
||||
%1 = call i64 @maybe(ptr %retparam)
|
||||
%not_err = icmp eq i64 %1, 0
|
||||
%2 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
|
||||
br i1 %2, label %after_check, label %assign_optional
|
||||
|
||||
assign_optional: ; preds = %entry
|
||||
store i64 %1, ptr %num.f, align 8
|
||||
br label %after_assign
|
||||
|
||||
after_check: ; preds = %entry
|
||||
%3 = load i32, ptr %retparam, align 4
|
||||
store i32 %3, ptr %num, align 4
|
||||
store i64 0, ptr %num.f, align 8
|
||||
br label %after_assign
|
||||
|
||||
after_assign: ; preds = %after_check, %assign_optional
|
||||
%4 = load i32, ptr %num, align 4
|
||||
store i32 %4, ptr %x, align 4
|
||||
%5 = load i32, ptr %num, align 4
|
||||
ret i32 %5
|
||||
}
|
||||
55
test/test_suite/statements/nextcase_const.c3t
Normal file
55
test/test_suite/statements/nextcase_const.c3t
Normal file
@@ -0,0 +1,55 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
|
||||
fn void! main()
|
||||
{
|
||||
int a;
|
||||
switch (a)
|
||||
{
|
||||
case a++:
|
||||
nextcase 0;
|
||||
case 10:
|
||||
nextcase a + 1;
|
||||
default:
|
||||
a--;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
define i64 @test.main() #0 {
|
||||
entry:
|
||||
%a = alloca i32, align 4
|
||||
%switch = alloca i32, align 4
|
||||
store i32 0, ptr %a, align 4
|
||||
%0 = load i32, ptr %a, align 4
|
||||
store i32 %0, ptr %switch, align 4
|
||||
br label %switch.entry
|
||||
switch.entry: ; preds = %switch.case2, %switch.case, %entry
|
||||
%1 = load i32, ptr %switch, align 4
|
||||
%2 = load i32, ptr %a, align 4
|
||||
%add = add i32 %2, 1
|
||||
store i32 %add, ptr %a, align 4
|
||||
%eq = icmp eq i32 %2, %1
|
||||
br i1 %eq, label %switch.case, label %next_if
|
||||
switch.case: ; preds = %switch.entry
|
||||
store i32 0, ptr %switch, align 4
|
||||
br label %switch.entry
|
||||
next_if: ; preds = %switch.entry
|
||||
%eq1 = icmp eq i32 10, %1
|
||||
br i1 %eq1, label %switch.case2, label %next_if4
|
||||
switch.case2: ; preds = %next_if
|
||||
%3 = load i32, ptr %a, align 4
|
||||
%add3 = add i32 %3, 1
|
||||
store i32 %add3, ptr %switch, align 4
|
||||
br label %switch.entry
|
||||
next_if4: ; preds = %next_if
|
||||
br label %switch.default
|
||||
switch.default: ; preds = %next_if4
|
||||
%4 = load i32, ptr %a, align 4
|
||||
%sub = sub i32 %4, 1
|
||||
store i32 %sub, ptr %a, align 4
|
||||
br label %switch.exit
|
||||
switch.exit: ; preds = %switch.default
|
||||
ret i64 0
|
||||
}
|
||||
121
test/test_suite/statements/nextcase_default.c3t
Normal file
121
test/test_suite/statements/nextcase_default.c3t
Normal file
@@ -0,0 +1,121 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int a = 0;
|
||||
int b = 0;
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
b += 1;
|
||||
nextcase default;
|
||||
case 2:
|
||||
b *= 2;
|
||||
default:
|
||||
b *= 3;
|
||||
}
|
||||
switch FOO: (a)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
b -= 1;
|
||||
switch (a)
|
||||
{
|
||||
case 0:
|
||||
b /= 2;
|
||||
nextcase FOO: default;
|
||||
default:
|
||||
b /= 3;
|
||||
}
|
||||
nextcase;
|
||||
case 2:
|
||||
b *= 2;
|
||||
default:
|
||||
b *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
entry:
|
||||
%a = alloca i32, align 4
|
||||
%b = alloca i32, align 4
|
||||
%switch = alloca i32, align 4
|
||||
%switch3 = alloca i32, align 4
|
||||
%switch6 = alloca i32, align 4
|
||||
store i32 0, ptr %a, align 4
|
||||
store i32 0, ptr %b, align 4
|
||||
%0 = load i32, ptr %a, align 4
|
||||
store i32 %0, ptr %switch, align 4
|
||||
br label %switch.entry
|
||||
switch.entry: ; preds = %entry
|
||||
%1 = load i32, ptr %switch, align 4
|
||||
switch i32 %1, label %switch.default [
|
||||
i32 0, label %switch.case
|
||||
i32 1, label %switch.case
|
||||
i32 2, label %switch.case1
|
||||
]
|
||||
switch.case: ; preds = %switch.entry, %switch.entry
|
||||
%2 = load i32, ptr %b, align 4
|
||||
%add = add i32 %2, 1
|
||||
store i32 %add, ptr %b, align 4
|
||||
br label %switch.default
|
||||
switch.case1: ; preds = %switch.entry
|
||||
%3 = load i32, ptr %b, align 4
|
||||
%mul = mul i32 %3, 2
|
||||
store i32 %mul, ptr %b, align 4
|
||||
br label %switch.exit
|
||||
switch.default: ; preds = %switch.case, %switch.entry
|
||||
%4 = load i32, ptr %b, align 4
|
||||
%mul2 = mul i32 %4, 3
|
||||
store i32 %mul2, ptr %b, align 4
|
||||
br label %switch.exit
|
||||
switch.exit: ; preds = %switch.default, %switch.case1
|
||||
%5 = load i32, ptr %a, align 4
|
||||
store i32 %5, ptr %switch3, align 4
|
||||
br label %switch.entry4
|
||||
switch.entry4: ; preds = %switch.exit
|
||||
%6 = load i32, ptr %switch3, align 4
|
||||
switch i32 %6, label %switch.default14 [
|
||||
i32 0, label %switch.case5
|
||||
i32 1, label %switch.case5
|
||||
i32 2, label %switch.case12
|
||||
]
|
||||
switch.case5: ; preds = %switch.entry4, %switch.entry4
|
||||
%7 = load i32, ptr %b, align 4
|
||||
%sub = sub i32 %7, 1
|
||||
store i32 %sub, ptr %b, align 4
|
||||
%8 = load i32, ptr %a, align 4
|
||||
store i32 %8, ptr %switch6, align 4
|
||||
br label %switch.entry7
|
||||
switch.entry7: ; preds = %switch.case5
|
||||
%9 = load i32, ptr %switch6, align 4
|
||||
switch i32 %9, label %switch.default9 [
|
||||
i32 0, label %switch.case8
|
||||
]
|
||||
switch.case8: ; preds = %switch.entry7
|
||||
%10 = load i32, ptr %b, align 4
|
||||
%sdiv = sdiv i32 %10, 2
|
||||
store i32 %sdiv, ptr %b, align 4
|
||||
br label %switch.default14
|
||||
switch.default9: ; preds = %switch.entry7
|
||||
%11 = load i32, ptr %b, align 4
|
||||
%sdiv10 = sdiv i32 %11, 3
|
||||
store i32 %sdiv10, ptr %b, align 4
|
||||
br label %switch.exit11
|
||||
switch.exit11: ; preds = %switch.default9
|
||||
br label %switch.case12
|
||||
switch.case12: ; preds = %switch.entry4, %switch.exit11
|
||||
%12 = load i32, ptr %b, align 4
|
||||
%mul13 = mul i32 %12, 2
|
||||
store i32 %mul13, ptr %b, align 4
|
||||
br label %switch.exit16
|
||||
switch.default14: ; preds = %switch.case8, %switch.entry4
|
||||
%13 = load i32, ptr %b, align 4
|
||||
%mul15 = mul i32 %13, 10
|
||||
store i32 %mul15, ptr %b, align 4
|
||||
br label %switch.exit16
|
||||
switch.exit16: ; preds = %switch.default14, %switch.case12
|
||||
ret void
|
||||
14
test/test_suite/statements/nextcase_missing_case.c3
Normal file
14
test/test_suite/statements/nextcase_missing_case.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void! main()
|
||||
{
|
||||
int a;
|
||||
switch (a)
|
||||
{
|
||||
case 1:
|
||||
nextcase 0; // #error: There is no 'case 0'
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
11
test/test_suite/statements/nextcase_no_switch.c3
Normal file
11
test/test_suite/statements/nextcase_no_switch.c3
Normal file
@@ -0,0 +1,11 @@
|
||||
fn void main()
|
||||
{
|
||||
int x = 0;
|
||||
switch
|
||||
{
|
||||
case true:
|
||||
nextcase false; // #error: cannot be used with
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
22
test/test_suite/switch/bad_ranges.c3
Normal file
22
test/test_suite/switch/bad_ranges.c3
Normal file
@@ -0,0 +1,22 @@
|
||||
module test;
|
||||
|
||||
fn void! test1()
|
||||
{
|
||||
double a;
|
||||
switch (a)
|
||||
{
|
||||
case 1.3 .. 4.5: // #error: Ranges must be constant integers
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
fn void! test2()
|
||||
{
|
||||
int a;
|
||||
switch (a)
|
||||
{
|
||||
case 2 .. 3:
|
||||
case a .. 3: // #error: Ranges must be constant integers
|
||||
default:
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user