diff --git a/releasenotes.md b/releasenotes.md index 351943b7a..c613401bd 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -10,6 +10,7 @@ - Improve error message in the case of `MyInterface x = foo;` #1522 - Deprecate `@adhoc`, allow non-nested ad hoc generic types. - Constant bytes <=> char[] conversion should work #1514. +- Infer now works across ternary. ### Fixes - `Unsupported int[*] $x = { 1, 2, 3, 4 }` #1489. @@ -23,6 +24,7 @@ - Compiler error when any/interface initialized using {} #1533. - Bug when defers and $if were combined in a macro, which would cause miscompilation. - Fixes to the CSV reader. +- Crash returning struct or vector from function using ternary expression #1537. ### Stdlib changes - Remove unintended print of `char[]` as String diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index dc74d27df..96842babc 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -41,7 +41,7 @@ static inline bool sema_expr_analyse_ct_eval(SemaContext *context, Expr *expr, C 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, CheckType check); static inline bool sema_expr_analyse_hash_identifier(SemaContext *context, Type *infer_type, Expr *expr); -static inline bool sema_expr_analyse_ternary(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); static inline bool sema_expr_analyse_unary(SemaContext *context, Expr *expr, bool *failed_ref, CheckType check); @@ -187,7 +187,7 @@ static inline void sema_expr_flatten_const_ident(Expr *expr); static inline bool sema_analyse_expr_check(SemaContext *context, Expr *expr, CheckType check); static inline Expr **sema_prepare_splat_insert(Expr **exprs, unsigned added, unsigned insert_point); -static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool is_dead); +static inline bool sema_analyse_maybe_dead_expr(SemaContext *, Expr *expr, bool is_dead, Type *infer_type); // -- implementations @@ -836,7 +836,7 @@ static inline bool sema_cast_ident_rvalue(SemaContext *context, Expr *expr) UNREACHABLE } -static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) +static inline bool sema_expr_analyse_ternary(SemaContext *context, Type *infer_type, Expr *expr) { Expr *left = exprptrzero(expr->ternary_expr.then_expr); Expr *cond = exprptr(expr->ternary_expr.cond); @@ -845,7 +845,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) if (left) { if (!sema_analyse_cond_expr(context, cond, &path)) return expr_poison(expr); - if (!sema_analyse_maybe_dead_expr(context, left, path == COND_FALSE)) return expr_poison(expr); + if (!sema_analyse_maybe_dead_expr(context, left, path == COND_FALSE, infer_type)) return expr_poison(expr); } else { @@ -869,7 +869,7 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) } Expr *right = exprptr(expr->ternary_expr.else_expr); - if (!sema_analyse_maybe_dead_expr(context, right, path == COND_TRUE)) return expr_poison(expr); + if (!sema_analyse_maybe_dead_expr(context, right, path == COND_TRUE, infer_type)) return expr_poison(expr); bool is_optional = false; Type *left_canonical = left->type->canonical; @@ -889,6 +889,14 @@ static inline bool sema_expr_analyse_ternary(SemaContext *context, Expr *expr) false)) return false; } + if (type_storage_type(left->type) == STORAGE_COMPILE_TIME) + { + if (left->type == type_untypedlist) + { + RETURN_SEMA_ERROR(expr, "The ternary would be an 'untyped list', you need to explicitly type one or both branches to a runtime type."); + } + RETURN_SEMA_ERROR(expr, "A ternary must always return a runtime type, but it was %s.", type_quoted_error_string(left_canonical)); + } if (path != COND_MISSING) { expr_replace(expr, path == COND_TRUE ? left : right); @@ -4601,11 +4609,14 @@ static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, E return true; } -static inline bool sema_analyse_maybe_dead_expr(SemaContext *context, Expr *expr, bool is_dead) +static inline bool sema_analyse_maybe_dead_expr(SemaContext *context, Expr *expr, bool is_dead, Type *infer_type) { - if (!is_dead || context->active_scope.is_dead) return sema_analyse_expr(context, expr); + if (!is_dead || context->active_scope.is_dead) + { + return infer_type ? sema_analyse_inferred_expr(context, infer_type, expr) : sema_analyse_expr(context, expr); + } context->active_scope.is_dead = true; - bool success = sema_analyse_expr(context, expr); + bool success = infer_type ? sema_analyse_inferred_expr(context, infer_type, expr) : sema_analyse_expr(context, expr); context->active_scope.is_dead = false; return success; } @@ -9073,7 +9084,7 @@ static inline bool sema_analyse_expr_dispatch(SemaContext *context, Expr *expr, case EXPR_BINARY: return sema_expr_analyse_binary(context, expr); case EXPR_TERNARY: - return sema_expr_analyse_ternary(context, expr); + return sema_expr_analyse_ternary(context, NULL, expr); case EXPR_UNARY: case EXPR_POST_UNARY: return sema_expr_analyse_unary(context, expr, NULL, check); @@ -9528,6 +9539,9 @@ bool sema_analyse_inferred_expr(SemaContext *context, Type *infer_type, Expr *ex case EXPR_LAMBDA: if (!sema_expr_analyse_lambda(context, infer_type, expr)) return expr_poison(expr); break; + case EXPR_TERNARY: + if (!sema_expr_analyse_ternary(context, infer_type, expr)) return expr_poison(expr); + break; case EXPR_HASH_IDENT: if (!sema_expr_analyse_hash_identifier(context, infer_type, expr)) return expr_poison(expr); break; diff --git a/test/test_suite/expressions/ternary_infer.c3t b/test/test_suite/expressions/ternary_infer.c3t new file mode 100644 index 000000000..d7ce610d1 --- /dev/null +++ b/test/test_suite/expressions/ternary_infer.c3t @@ -0,0 +1,23 @@ +// #target: macos-x64 +module test; +fn int[<2>] foo(int x) +{ + return x > 0 ? {0, 0} : {255, 255}; +} + + +fn int main() +{ + return 0; +} +/* #expect: test.ll + +define double @test.foo(i32 %0) #0 { +entry: + %taddr = alloca <2 x i32>, align 8 + %gt = icmp sgt i32 %0, 0 + %ternary = select i1 %gt, <2 x i32> zeroinitializer, <2 x i32> + store <2 x i32> %ternary, ptr %taddr, align 8 + %1 = load double, ptr %taddr, align 8 + ret double %1 +} \ No newline at end of file