Improve inference on ?? #1943.

This commit is contained in:
Christoffer Lerno
2025-02-10 16:20:33 +01:00
parent b46d3947dd
commit 86680279fa
5 changed files with 138 additions and 39 deletions

View File

@@ -21,6 +21,7 @@
- Distinct inline void causes unexpected error if used in slice #1946.
- Allow `fn int test() => @pool() { return 1; }` short function syntax usage #1906.
- Test runner will also check for leaks.
- Improve inference on `??` #1943.
### Fixes
- Fix issue requiring prefix on a generic interface declaration.

View File

@@ -55,14 +55,14 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bo
bool lvalue);
static inline bool sema_expr_analyse_compound_literal(SemaContext *context, Expr *expr);
static inline bool sema_expr_analyse_builtin(SemaContext *context, Expr *expr, bool throw_error);
static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr, bool *failed_ref);
static inline bool sema_expr_analyse_binary(SemaContext *context, Type *infer_type, Expr *expr, bool *failed_ref);
static inline bool sema_expr_resolve_ct_eval(SemaContext *context, Expr *expr);
static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Expr *expr);
static inline bool sema_expr_analyse_ct_identifier(SemaContext *context, Expr *expr);
static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_type, Expr *expr);
static inline bool sema_expr_analyse_cast(SemaContext *context, Expr *expr, bool *invalid_cast_ref);
static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr, Expr *left, Expr *right);
static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *infer_type);
static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr, bool *failed_ref, CheckType check);
static inline bool sema_expr_analyse_embed(SemaContext *context, Expr *expr, bool allow_fail);
@@ -6007,7 +6007,7 @@ static bool sema_binary_analyse_ct_op_assign(SemaContext *context, Expr *expr, E
}
expr->binary_expr.operator = binaryop_assign_base_op(expr->binary_expr.operator);
if (!sema_expr_analyse_binary(context, expr, NULL)) return false;
if (!sema_expr_analyse_binary(context, NULL, expr, NULL)) return false;
expr->resolve_status = RESOLVE_DONE;
if (!sema_cast_const(expr))
@@ -6041,7 +6041,7 @@ static bool sema_binary_analyse_ct_subscript_op_assign(SemaContext *context, Exp
BinaryOp op = binaryop_assign_base_op(expr->binary_expr.operator);
expr->binary_expr = (ExprBinary) { .left = exprid(value), .right = expr->binary_expr.right, .operator = op };
if (!sema_expr_analyse_binary(context, expr, NULL)) return false;
if (!sema_expr_analyse_binary(context, NULL, expr, NULL)) return false;
if (!sema_expr_analyse_ct_subscript_set_value(context, left, left_var, expr)) return false;
return true;
@@ -7862,13 +7862,12 @@ INLINE bool expr_is_ungrouped_ternary(Expr *expr)
return expr->expr_kind == EXPR_TERNARY && !expr->ternary_expr.grouped;
}
static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr, Expr *left, Expr *right)
static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr, Expr *left, Expr *right, Type *infer_type)
{
bool lhs_is_embed = left->expr_kind == EXPR_EMBED;
if (expr_is_ungrouped_ternary(left) || expr_is_ungrouped_ternary(right))
{
SEMA_ERROR(expr, "Unclear precedence using ternary with ??, please use () to remove ambiguity.");
return false;
RETURN_SEMA_ERROR(expr, "Unclear precedence using ternary with ??, please use () to remove ambiguity.");
}
if (lhs_is_embed)
{
@@ -7876,7 +7875,7 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr,
}
else
{
if (!sema_analyse_expr(context, left)) return false;
if (!sema_analyse_inferred_expr(context, infer_type, left)) return false;
}
Type *type = left->type;
@@ -7888,13 +7887,12 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr,
return true;
}
RETURN_SEMA_ERROR(left, "No optional to use '\?\?' with, please remove the '\?\?'.");
return false;
}
bool active_scope_jump = context->active_scope.jump_end;
// First we analyse the "else" and try to implictly cast.
if (!sema_analyse_expr(context, right)) return false;
if (!sema_analyse_inferred_expr(context, infer_type, right)) return false;
if (left->expr_kind == EXPR_OPTIONAL)
{
@@ -7916,9 +7914,7 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr,
Type *common = type_find_max_type(type, else_type);
if (!common)
{
SEMA_ERROR(right, "Cannot find a common type for %s and %s.", type_quoted_error_string(type),
type_quoted_error_string(else_type));
return false;
RETURN_SEMA_ERROR(right, "Cannot find a common type for %s and %s.", type_quoted_error_string(type), type_quoted_error_string(else_type));
}
if (!cast_implicit(context, left, common, false)) return false;
if (!cast_implicit(context, right, common, false)) return false;
@@ -7943,7 +7939,7 @@ static inline bool sema_expr_analyse_ct_and_or(SemaContext *context, Expr *expr,
return true;
}
static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr, bool *failed_ref)
static inline bool sema_expr_analyse_binary(SemaContext *context, Type *infer_type, Expr *expr, bool *failed_ref)
{
ASSERT_SPAN(expr, expr->resolve_status == RESOLVE_RUNNING);
Expr *left = exprptr(expr->binary_expr.left);
@@ -7958,7 +7954,7 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr, bo
switch (operator)
{
case BINARYOP_ELSE:
return sema_expr_analyse_or_error(context, expr, left, right);
return sema_expr_analyse_or_error(context, expr, left, right, infer_type);
case BINARYOP_CT_CONCAT:
return sema_expr_analyse_ct_concat(context, expr, left, right);
case BINARYOP_CT_OR:
@@ -9340,7 +9336,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr
UNREACHABLE
case EXPR_BINARY:
main_expr->resolve_status = RESOLVE_RUNNING;
if (!sema_expr_analyse_binary(context, main_expr, &failed))
if (!sema_expr_analyse_binary(context, NULL, main_expr, &failed))
{
if (!failed) goto FAIL;
success = false;
@@ -9942,7 +9938,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr,
if (!sema_expr_resolve_ct_eval(context, expr)) return false;
return sema_analyse_expr_dispatch(context, expr, check);
case EXPR_BINARY:
return sema_expr_analyse_binary(context, expr, NULL);
return sema_expr_analyse_binary(context, NULL, expr, NULL);
case EXPR_TERNARY:
return sema_expr_analyse_ternary(context, NULL, expr);
case EXPR_UNARY:
@@ -10591,6 +10587,9 @@ RETRY:
case EXPR_LAMBDA:
if (!sema_expr_analyse_lambda(context, to, expr)) return expr_poison(expr);
break;
case EXPR_BINARY:
if (!sema_expr_analyse_binary(context, to, expr, NULL)) return expr_poison(expr);
break;
case EXPR_TERNARY:
if (!sema_expr_analyse_ternary(context, to, expr)) return expr_poison(expr);
break;

View File

@@ -82,14 +82,17 @@ entry:
%not_err = icmp eq i64 %optval, 0
%18 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %18, label %after_check, label %else_block
after_check: ; preds = %entry
%19 = load i32, ptr %w, align 4
%sext13 = sext i32 %19 to i64
br label %phi_block
else_block: ; preds = %entry
br label %phi_block
phi_block: ; preds = %else_block, %after_check
%val = phi i32 [ %19, %after_check ], [ 1, %else_block ]
%sext13 = sext i32 %val to i64
store i64 %sext13, ptr %z, align 8
%val = phi i64 [ %sext13, %after_check ], [ 1, %else_block ]
store i64 %val, ptr %z, align 8
ret void
}

View File

@@ -57,45 +57,45 @@ phi_block: ; preds = %else_block, %after_
%6 = call i64 @testError(ptr %retparam5)
%not_err6 = icmp eq i64 %6, 0
%7 = call i1 @llvm.expect.i1(i1 %not_err6, i1 true)
br i1 %7, label %after_check7, label %else_block8
br i1 %7, label %after_check7, label %else_block9
after_check7: ; preds = %phi_block
%8 = load i32, ptr %retparam5, align 4
%shl = shl i32 1, %8
%9 = freeze i32 %shl
br label %phi_block9
%sifp8 = sitofp i32 %9 to double
br label %phi_block10
else_block8: ; preds = %phi_block
br label %phi_block9
else_block9: ; preds = %phi_block
br label %phi_block10
phi_block9: ; preds = %else_block8, %after_check7
%val10 = phi i32 [ %9, %after_check7 ], [ 100, %else_block8 ]
%sifp11 = sitofp i32 %val10 to double
store double %sifp11, ptr %y, align 8
phi_block10: ; preds = %else_block9, %after_check7
%val11 = phi double [ %sifp8, %after_check7 ], [ 1.000000e+02, %else_block9 ]
store double %val11, ptr %y, align 8
%10 = call i64 @testError(ptr %retparam12)
%not_err13 = icmp eq i64 %10, 0
%11 = call i1 @llvm.expect.i1(i1 %not_err13, i1 true)
br i1 %11, label %after_check14, label %else_block15
br i1 %11, label %after_check14, label %else_block16
after_check14: ; preds = %phi_block9
after_check14: ; preds = %phi_block10
%12 = load i32, ptr %retparam12, align 4
%ashr = ashr i32 %12, 1
%13 = freeze i32 %ashr
br label %phi_block16
%sifp15 = sitofp i32 %13 to double
br label %phi_block17
else_block15: ; preds = %phi_block9
br label %phi_block16
else_block16: ; preds = %phi_block10
br label %phi_block17
phi_block16: ; preds = %else_block15, %after_check14
%val17 = phi i32 [ %13, %after_check14 ], [ 100, %else_block15 ]
%sifp18 = sitofp i32 %val17 to double
store double %sifp18, ptr %z, align 8
phi_block17: ; preds = %else_block16, %after_check14
%val18 = phi double [ %sifp15, %after_check14 ], [ 1.000000e+02, %else_block16 ]
store double %val18, ptr %z, align 8
%14 = call i64 @testError(ptr %retparam19)
%not_err20 = icmp eq i64 %14, 0
%15 = call i1 @llvm.expect.i1(i1 %not_err20, i1 true)
br i1 %15, label %after_check21, label %else_block27
after_check21: ; preds = %phi_block16
after_check21: ; preds = %phi_block17
%16 = load i32, ptr %retparam19, align 4
%sifp22 = sitofp i32 %16 to double
%17 = call i64 @testError(ptr %retparam23)
@@ -109,7 +109,7 @@ after_check25: ; preds = %after_check21
%fmul = fmul double %sifp22, %sifp26
br label %phi_block28
else_block27: ; preds = %after_check21, %phi_block16
else_block27: ; preds = %after_check21, %phi_block17
br label %phi_block28
phi_block28: ; preds = %else_block27, %after_check25

View File

@@ -0,0 +1,96 @@
// #target: macos-x64
enum Foo
{
ABC,
DEF
}
fn int main()
{
int a;
Foo! x = ABC;
Foo f = x ?? DEF;
return 0;
}
/* expect: test.ll
define void @top_down_casts.test() #0 {
entry:
%x = alloca i32, align 4
%y = alloca i32, align 4
%z = alloca i64, align 8
%w = alloca i32, align 4
%w.f = alloca i64, align 8
store i32 0, ptr %x, align 4
store i32 0, ptr %y, align 4
%0 = load i32, ptr %x, align 4
%sext = sext i32 %0 to i64
%1 = load i32, ptr %y, align 4
%sext1 = sext i32 %1 to i64
%mul = mul i64 %sext, %sext1
store i64 %mul, ptr %z, align 8
%2 = load i32, ptr %x, align 4
%3 = load i32, ptr %y, align 4
%sdiv = sdiv i32 %2, %3
%sext2 = sext i32 %sdiv to i64
store i64 %sext2, ptr %z, align 8
%4 = load i32, ptr %x, align 4
%sext3 = sext i32 %4 to i64
%5 = load i32, ptr %y, align 4
%sext4 = sext i32 %5 to i64
%add = add i64 %sext3, %sext4
store i64 %add, ptr %z, align 8
%6 = load i32, ptr %x, align 4
%sext5 = sext i32 %6 to i64
%7 = load i32, ptr %y, align 4
%sext6 = sext i32 %7 to i64
%sub = sub i64 %sext5, %sext6
store i64 %sub, ptr %z, align 8
%8 = load i32, ptr %x, align 4
%9 = load i32, ptr %y, align 4
%smod = srem i32 %8, %9
%sext7 = sext i32 %smod to i64
store i64 %sext7, ptr %z, align 8
%10 = load i32, ptr %x, align 4
%sext8 = sext i32 %10 to i64
%11 = load i32, ptr %y, align 4
%zext = zext i32 %11 to i64
%shl = shl i64 %sext8, %zext
%12 = freeze i64 %shl
store i64 %12, ptr %z, align 8
%13 = load i32, ptr %x, align 4
%sext9 = sext i32 %13 to i64
%14 = load i32, ptr %y, align 4
%zext10 = zext i32 %14 to i64
%ashr = ashr i64 %sext9, %zext10
%15 = freeze i64 %ashr
store i64 %15, ptr %z, align 8
%16 = load i32, ptr %x, align 4
%bnot = xor i32 %16, -1
%sext11 = sext i32 %bnot to i64
store i64 %sext11, ptr %z, align 8
%17 = load i32, ptr %x, align 4
%neg = sub i32 0, %17
%sext12 = sext i32 %neg to i64
store i64 %sext12, ptr %z, align 8
store i64 0, ptr %w.f, align 8
store i32 0, ptr %w, align 4
%optval = load i64, ptr %w.f, align 8
%not_err = icmp eq i64 %optval, 0
%18 = call i1 @llvm.expect.i1(i1 %not_err, i1 true)
br i1 %18, label %after_check, label %else_block
after_check: ; preds = %entry
%19 = load i32, ptr %w, align 4
%sext13 = sext i32 %19 to i64
br label %phi_block
else_block: ; preds = %entry
br label %phi_block
phi_block: ; preds = %else_block, %after_check
%val = phi i64 [ %sext13, %after_check ], [ 1, %else_block ]
store i64 %val, ptr %z, align 8
ret void
}