Fix optional-to-union equality
c5ab32c890c475331da6c6f292b755135a25543ef15c97a778f5f0be77aa8a9a
1 parent
a32f7985
lib/std/lang/lower.rad
+4 -0
| 4130 | 4130 | /// Check if a node is a void union variant literal (e.g. `Color::Red`). |
|
| 4131 | 4131 | /// If so, returns the variant's tag index. This enables optimized comparisons |
|
| 4132 | 4132 | /// that only check the tag instead of doing full aggregate comparison. |
|
| 4133 | 4133 | unsafe fn voidVariantIndex(res: &resolver::Resolver, node: *ast::Node) -> ?i64 { |
|
| 4134 | 4134 | let data = resolver::nodeData(res, node); |
|
| 4135 | + | // Optional equality checks both the presence tag and the union value. |
|
| 4136 | + | if let case resolver::Coercion::OptionalLift(_) = data.coercion { |
|
| 4137 | + | return nil; |
|
| 4138 | + | } |
|
| 4135 | 4139 | let sym = data.sym else { |
|
| 4136 | 4140 | return nil; |
|
| 4137 | 4141 | }; |
|
| 4138 | 4142 | let case resolver::SymbolData::Variant { type: payloadType, index, .. } = sym.data else { |
|
| 4139 | 4143 | return nil; |
test/tests/optional.union.eq.rad
added
+41 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Scalar union tags used in optional comparisons. |
|
| 4 | + | union Error: Copy { First, Symbol, Allocation } |
|
| 5 | + | ||
| 6 | + | /// A union with both empty and populated variants. |
|
| 7 | + | union Value: Copy { Empty, Number(u64) } |
|
| 8 | + | ||
| 9 | + | /// Lift a union value into an optional. |
|
| 10 | + | fn optionalValue(value: Value) -> ?Value { |
|
| 11 | + | return value; |
|
| 12 | + | } |
|
| 13 | + | ||
| 14 | + | /// Check equality and inequality in both operand orders. |
|
| 15 | + | fn check(error: ?Error, expected: Error, equal: bool) { |
|
| 16 | + | assert (error == expected) == equal; |
|
| 17 | + | assert (expected == error) == equal; |
|
| 18 | + | assert (error <> expected) == not equal; |
|
| 19 | + | assert (expected <> error) == not equal; |
|
| 20 | + | } |
|
| 21 | + | ||
| 22 | + | @default fn main() -> u64 { |
|
| 23 | + | for value in [Error::First, Error::Symbol, Error::Allocation] { |
|
| 24 | + | check(nil, value, false); |
|
| 25 | + | check(value, value, true); |
|
| 26 | + | } |
|
| 27 | + | check(Error::Symbol, Error::Allocation, false); |
|
| 28 | + | let symbol: ?Error = Error::Symbol; |
|
| 29 | + | assert symbol == Error::Symbol and Error::Symbol == symbol; |
|
| 30 | + | assert symbol <> Error::First and Error::First <> symbol; |
|
| 31 | + | let absent: ?Error = nil; |
|
| 32 | + | assert absent <> Error::First and Error::First <> absent; |
|
| 33 | + | let empty = optionalValue(Value::Empty); |
|
| 34 | + | let number = optionalValue(Value::Number(42)); |
|
| 35 | + | let missing: ?Value = nil; |
|
| 36 | + | assert empty == Value::Empty and Value::Empty == empty; |
|
| 37 | + | assert number <> Value::Empty and Value::Empty <> number; |
|
| 38 | + | assert missing <> Value::Empty and Value::Empty <> missing; |
|
| 39 | + | assert number == Value::Number(42) and Value::Number(42) == number; |
|
| 40 | + | return 0; |
|
| 41 | + | } |