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:
Christoffer Lerno
2023-07-10 12:05:17 +02:00
committed by Christoffer Lerno
parent 3b0370c8bb
commit dddeca1856
23 changed files with 390 additions and 206 deletions

View File

@@ -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.

View File

@@ -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 '{' '}'
;

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 )
*

View File

@@ -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)
{

View File

@@ -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))
{

View File

@@ -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);

View File

@@ -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?");
}
}

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "0.4.560"
#define COMPILER_VERSION "0.4.561"

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -0,0 +1,10 @@
import std::io;
fn void main()
{
int a, $b;
int $c, $d;
a++;
$b++;
$d++;
}

View 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;
}

View File

@@ -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
}

View 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
}

View 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

View 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:
}
}

View File

@@ -0,0 +1,11 @@
fn void main()
{
int x = 0;
switch
{
case true:
nextcase false; // #error: cannot be used with
default:
break;
}
}

View 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:
}
}