Refactoring, optimize negation in if statement.

This commit is contained in:
Christoffer Lerno
2025-06-25 12:33:17 +02:00
parent 8a4e7b6ce8
commit f67da4f315
6 changed files with 52 additions and 50 deletions

View File

@@ -1570,7 +1570,7 @@ typedef struct DynamicScope_
ScopeId scope_id;
bool allow_dead_code : 1;
bool is_dead : 1;
bool is_invalid : 1;
bool is_poisoned : 1;
EndJump end_jump;
ScopeFlags flags;
unsigned label_start;
@@ -1739,9 +1739,7 @@ struct SemaContext_
Ast *yield_body;
BlockExit** block_exit_ref;
Type *expected_block_type;
Ast **returns;
// Reusable returns cache.
Ast **returns_cache;
Ast **block_returns;
Expr **macro_varargs;
Decl **macro_params;
bool macro_has_ensures;

View File

@@ -368,7 +368,7 @@ static void llvm_emit_if_stmt(GenContext *c, Ast *ast)
LLVMBasicBlockRef then_block = exit_block;
LLVMBasicBlockRef else_block = exit_block;
Ast *then_body = astptr(ast->if_stmt.then_body);
Ast *then_body = astptrzero(ast->if_stmt.then_body);
// Only generate a target if
if (ast_is_not_empty(then_body))
{
@@ -376,8 +376,7 @@ static void llvm_emit_if_stmt(GenContext *c, Ast *ast)
}
// We have an optional else block.
AstId else_id = ast->if_stmt.else_body;
Ast *else_body = else_id ? astptr(else_id) : NULL;
Ast *else_body = astptrzero(ast->if_stmt.else_body);
if (ast_is_not_empty(else_body))
{
else_block = llvm_basic_block_new(c, "if.else");

View File

@@ -2294,11 +2294,11 @@ static inline Type *context_unify_returns(SemaContext *context)
// 1. Loop through the returns.
bool optional = false;
unsigned returns = vec_size(context->returns);
unsigned returns = vec_size(context->block_returns);
if (!returns) return type_void;
for (unsigned i = 0; i < returns; i++)
{
Ast *return_stmt = context->returns[i];
Ast *return_stmt = context->block_returns[i];
Type *rtype;
if (!return_stmt)
{
@@ -2334,7 +2334,7 @@ static inline Type *context_unify_returns(SemaContext *context)
ASSERT(return_stmt);
SEMA_ERROR(return_stmt, "Cannot find a common parent type of %s and %s",
type_quoted_error_string(rtype), type_quoted_error_string(common_type));
Ast *prev = context->returns[i - 1];
Ast *prev = context->block_returns[i - 1];
ASSERT(prev);
SEMA_NOTE(prev, "The previous return was here.");
return NULL;
@@ -2351,7 +2351,7 @@ static inline Type *context_unify_returns(SemaContext *context)
if (all_returns_need_casts)
{
ASSERT(common_type != type_wildcard);
FOREACH(Ast *, return_stmt, context->returns)
FOREACH(Ast *, return_stmt, context->block_returns)
{
if (!return_stmt) continue;
Expr *ret_expr = return_stmt->return_stmt.expr;
@@ -2676,7 +2676,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
params = macro_context.macro_params;
bool is_no_return = sig->attrs.noreturn;
if (!vec_size(macro_context.returns))
if (!vec_size(macro_context.block_returns))
{
if (rtype && rtype != type_void && !macro_context.active_scope.end_jump.active)
{
@@ -2688,14 +2688,14 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
}
else if (is_no_return)
{
SEMA_ERROR(context->returns[0], "Return used despite macro being marked '@noreturn'.");
SEMA_ERROR(context->block_returns[0], "Return used despite macro being marked '@noreturn'.");
goto EXIT_FAIL;
}
if (rtype)
{
bool inferred_len = type_len_is_inferred(rtype);
FOREACH(Ast *, return_stmt, macro_context.returns)
FOREACH(Ast *, return_stmt, macro_context.block_returns)
{
if (!return_stmt)
{
@@ -2772,7 +2772,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
{
must_use = sig->attrs.nodiscard || (optional_return && !sig->attrs.maydiscard);
}
unsigned returns_found = vec_size(macro_context.returns);
unsigned returns_found = vec_size(macro_context.block_returns);
// We may have zero normal macro returns but the active scope still has a "jump end".
// In this case it is triggered by the @body()
if (!returns_found && macro_context.active_scope.end_jump.active)
@@ -2781,7 +2781,7 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
}
if (returns_found == 1 && !implicit_void_return)
{
Ast *ret = macro_context.returns[0];
Ast *ret = macro_context.block_returns[0];
Expr *result = ret ? ret->return_stmt.expr : NULL;
if (!result) goto NOT_CT;
if (!expr_is_runtime_const(result)) goto NOT_CT;
@@ -8845,7 +8845,7 @@ static inline bool sema_expr_analyse_rethrow(SemaContext *context, Expr *expr)
RETURN_SEMA_ERROR(expr, "Rethrow is only allowed in macros with an optional or inferred return type. "
"Did you mean to use '!!' instead?");
}
vec_add(context->returns, NULL);
vec_add(context->block_returns, NULL);
expr->rethrow_expr.in_block = context->block_exit_ref;
expr->rethrow_expr.cleanup = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer, false);
}

View File

@@ -564,7 +564,6 @@ END:
*/
static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *statement)
{
bool is_macro = (context->active_scope.flags & SCOPE_MACRO) != 0;
ASSERT(context->active_scope.flags & SCOPE_MACRO);
statement->ast_kind = AST_BLOCK_EXIT_STMT;
SET_JUMP_END(context, statement);
@@ -580,22 +579,20 @@ static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *state
{
if (!sema_analyse_expr(context, ret_expr)) return false;
}
if (is_macro && !sema_check_return_matches_opt_returns(context, ret_expr)) return false;
if (!sema_check_return_matches_opt_returns(context, ret_expr)) return false;
}
else
{
if (block_type && type_no_optional(block_type) != type_void)
// What if we already had an expected type and we do an empty return.
if (block_type && !type_is_void(type_no_optional(block_type)))
{
SEMA_ERROR(statement, "Expected a return value of type %s here.", type_quoted_error_string(block_type));
return false;
RETURN_SEMA_ERROR(statement, "Expected a return value of type %s here.", type_quoted_error_string(block_type));
}
}
statement->return_stmt.block_exit_ref = context->block_exit_ref;
sema_inline_return_defers(context, statement, context->active_scope.defer_last, context->block_return_defer);
if (is_macro && !sema_analyse_macro_constant_ensures(context, ret_expr)) return false;
vec_add(context->returns, statement);
if (!sema_analyse_macro_constant_ensures(context, ret_expr)) return false;
vec_add(context->block_returns, statement);
return true;
}
@@ -1052,7 +1049,7 @@ static inline bool sema_analyse_last_cond(SemaContext *context, Expr *expr, Cond
}
if (!sema_analyse_catch_unwrap(context, expr))
{
context->active_scope.is_invalid = true;
context->active_scope.is_poisoned = true;
return false;
}
default:
@@ -1891,14 +1888,8 @@ SKIP_OVERLOAD:;
static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement)
{
// IMPROVE
// convert
// if (!x) A(); else B();
// into
// if (x) B(); else A();
bool else_jump;
bool then_jump;
bool then_jump = false;
bool success;
Expr *cond = exprptr(statement->if_stmt.cond);
@@ -1911,10 +1902,15 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement)
Ast *else_body = else_id ? astptr(else_id) : NULL;
CondResult result = COND_MISSING;
bool is_invalid = false;
bool reverse = false;
SCOPE_OUTER_START
success = sema_analyse_cond(context, cond, COND_TYPE_UNWRAP_BOOL, &result);
if (success && cond->expr_kind == EXPR_COND)
{
Expr **list = cond->cond_expr;
reverse = vec_size(list) == 1 && list[0]->expr_kind == EXPR_UNARY && list[0]->unary_expr.operator == UNARYOP_NOT;
}
if (success && !ast_ok(then))
{
SEMA_ERROR(then,
@@ -1943,7 +1939,11 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement)
context->active_scope.allow_dead_code = true;
bool warn = SEMA_WARN(statement, "This code will never execute.");
sema_note_prev_at(context->active_scope.end_jump.span, "This code is preventing it from exectuting");
if (!warn) return success = false;
if (!warn)
{
success = false;
goto END;
}
}
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
@@ -1968,12 +1968,12 @@ static inline bool sema_analyse_if_stmt(SemaContext *context, Ast *statement)
END:
context_pop_defers_and_replace_ast(context, statement);
is_invalid = context->active_scope.is_invalid;
is_invalid = context->active_scope.is_poisoned;
SCOPE_OUTER_END;
if (is_invalid) context->active_scope.is_invalid = is_invalid;
if (is_invalid) context->active_scope.is_poisoned = is_invalid;
if (!success)
{
if (then_jump && sema_catch_in_cond(cond)) context->active_scope.is_invalid = true;
if (then_jump && sema_catch_in_cond(cond)) context->active_scope.is_poisoned = true;
return false;
}
if (then_jump)
@@ -1992,6 +1992,12 @@ END:
{
SET_JUMP_END(context, statement);
}
if (reverse)
{
cond->cond_expr[0] = cond->cond_expr[0]->unary_expr.expr;
statement->if_stmt.else_body = statement->if_stmt.then_body;
statement->if_stmt.then_body = else_id;
}
return true;
}
@@ -3126,10 +3132,10 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state
bool sema_analyse_statement(SemaContext *context, Ast *statement)
{
if (context->active_scope.is_invalid) return false;
if (context->active_scope.is_poisoned) return false;
if (statement->ast_kind == AST_POISONED) return false;
EndJump end_jump = context->active_scope.end_jump;
unsigned returns = vec_size(context->returns);
unsigned returns = vec_size(context->block_returns);
if (!sema_analyse_statement_inner(context, statement)) return ast_poison(statement);
if (end_jump.active)
{
@@ -3145,7 +3151,7 @@ bool sema_analyse_statement(SemaContext *context, Ast *statement)
}
// Remove it
}
vec_resize(context->returns, returns);
vec_resize(context->block_returns, returns);
statement->ast_kind = AST_NOP_STMT;
}
return true;
@@ -3268,7 +3274,7 @@ bool sema_analyse_function_body(SemaContext *context, Decl *func)
vec_resize(context->ct_locals, 0);
// Clear returns
vec_resize(context->returns, 0);
vec_resize(context->block_returns, 0);
context->scope_id = 0;
context->continue_target = NULL;
context->next_target = 0;

View File

@@ -17,7 +17,7 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags)
}
bool scope_is_dead = context->active_scope.is_dead;
bool scope_is_invalid = context->active_scope.is_invalid;
bool scope_is_poisoned = context->active_scope.is_poisoned;
Ast *previous_defer = context->active_scope.in_defer;
AstId parent_defer = context->active_scope.defer_last;
unsigned last_local = context->active_scope.current_local;
@@ -38,7 +38,7 @@ void context_change_scope_with_flags(SemaContext *context, ScopeFlags flags)
.scope_id = ++context->scope_id,
.allow_dead_code = false,
.is_dead = scope_is_dead,
.is_invalid = scope_is_invalid,
.is_poisoned = scope_is_poisoned,
.depth = depth,
.current_local = last_local,
.label_start = label_start,

View File

@@ -182,21 +182,20 @@ voiderr: ; preds = %noerr_block15, %gua
store i8 0, ptr %xy, align 1
%25 = load i8, ptr %xy, align 1
%26 = trunc i8 %25 to i1
%not = xor i1 %26, true
br i1 %not, label %if.then, label %if.exit
br i1 %26, label %if.exit, label %if.else
if.then: ; preds = %voiderr
if.else: ; preds = %voiderr
%27 = call ptr @std.io.stdout()
%28 = call i64 @std.io.File.write(ptr %retparam23, ptr %27, ptr @.str.10, i64 2)
%not_err24 = icmp eq i64 %28, 0
%29 = call i1 @llvm.expect.i1(i1 %not_err24, i1 true)
br i1 %29, label %after_check26, label %assign_optional25
assign_optional25: ; preds = %if.then
assign_optional25: ; preds = %if.else
store i64 %28, ptr %error_var21, align 8
br label %guard_block27
after_check26: ; preds = %if.then
after_check26: ; preds = %if.else
br label %noerr_block28
guard_block27: ; preds = %assign_optional25