Fix optional-to-union equality

c5ab32c890c475331da6c6f292b755135a25543ef15c97a778f5f0be77aa8a9a
Alexis Sellier committed ago 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 +
}