From f360a37ccd0e5c9a4f14f556f94bd8895ff7edaa Mon Sep 17 00:00:00 2001 From: kalsprite Date: Wed, 19 Aug 2026 17:36:50 -0700 Subject: [PATCH 1/2] cmplx & quat take type from args --- src/check_builtin.cpp | 13 ++++- tests/internal/test_complex_type_hint.odin | 55 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 tests/internal/test_complex_type_hint.odin diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 20fa2f92c..350636b95 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -3604,7 +3604,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As default: GB_PANIC("Invalid type"); break; } - if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + // Only a complex hint of the same element type, or context-typing an untyped constant. + // Castability is the rule for a conversion the programmer wrote; used here it adopted any + // castable hint, which silently narrowed f64 to f32 and left the value with no element type + // at all when the hint was `any` or a union + if (type_hint != nullptr && is_type_complex(type_hint) && + (is_type_untyped(operand->type) || + are_types_identical(core_type(operand->type), core_type(type_hint)))) { operand->type = type_hint; } @@ -3803,7 +3809,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As default: GB_PANIC("Invalid type"); break; } - if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + // see the note in BuiltinProc_complex + if (type_hint != nullptr && is_type_quaternion(type_hint) && + (is_type_untyped(operand->type) || + are_types_identical(core_type(operand->type), core_type(type_hint)))) { operand->type = type_hint; } diff --git a/tests/internal/test_complex_type_hint.odin b/tests/internal/test_complex_type_hint.odin new file mode 100644 index 000000000..cffd7b06c --- /dev/null +++ b/tests/internal/test_complex_type_hint.odin @@ -0,0 +1,55 @@ +package test_internal + +import "core:testing" + +// `complex` and `quaternion` took their result type from any hint they were *castable* to, which is +// the rule for a conversion the programmer writes. Used as an assignment rule it silently narrowed +// f64 to f32, produced a quaternion from a two-component builtin with w and k uninitialised, and +// left the value with no element type at all when the hint was `any` or a union -- a compiler panic +@(test) +complex_takes_its_type_from_its_arguments :: proc(t: ^testing.T) { + a, b: f64 = 3, 4 + c, d: f32 = 3, 4 + + // the element type follows the arguments, not the hint + x: complex128 = complex(a, b) + y: complex64 = complex(c, d) + testing.expect_value(t, real(x), f64(3)) + testing.expect_value(t, imag(x), f64(4)) + testing.expect_value(t, real(y), f32(3)) + testing.expect_value(t, imag(y), f32(4)) + + // a distinct complex of the same width still takes the hint, which core:c/libc relies on + CD :: distinct complex128 + z: CD = complex(a, b) + testing.expect_value(t, real(complex128(z)), f64(3)) + + // an untyped constant is still context-typed + u: complex64 = complex(1, 2) + testing.expect_value(t, real(u), f32(1)) + + q: quaternion256 = quaternion(w = a, x = b, y = a, z = b) + testing.expect_value(t, real(q), f64(3)) + testing.expect_value(t, imag(q), f64(4)) + testing.expect_value(t, jmag(q), f64(3)) + testing.expect_value(t, kmag(q), f64(4)) +} + +// a hint that is not a complex type is no longer adopted, so these reach the ordinary assignment +// rule instead of the backend. `any` and a union both used to panic the compiler +@(test) +complex_into_any_and_union :: proc(t: ^testing.T) { + U :: union { complex128 } + + a, b: f64 = 3, 4 + + v: any = complex(a, b) + if c, ok := v.(complex128); testing.expect(t, ok, "any did not hold a complex128") { + testing.expect_value(t, real(c), f64(3)) + } + + u: U = complex(a, b) + if c, ok := u.(complex128); testing.expect(t, ok, "union did not hold a complex128") { + testing.expect_value(t, imag(c), f64(4)) + } +} From 8d890cc705975ee7d1d6677cc2db57dfca42f70a Mon Sep 17 00:00:00 2001 From: kalsprite Date: Wed, 19 Aug 2026 18:04:34 -0700 Subject: [PATCH 2/2] fix typo --- core/math/cmplx/cmplx.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/math/cmplx/cmplx.odin b/core/math/cmplx/cmplx.odin index f5c572cc6..3a1c3f511 100644 --- a/core/math/cmplx/cmplx.odin +++ b/core/math/cmplx/cmplx.odin @@ -253,7 +253,7 @@ exp_complex64 :: proc "contextless" (x: complex64) -> complex64 { if re < 0 { return complex(0, math.copy_sign(0, im)) } else { - return complex(math.inf_f64(1.0), math.nan_f64()) + return complex(math.inf_f32(1.0), math.nan_f32()) } } case math.is_nan(re):