compiler: Initialize optional constant data with resolved layouts

f7523bce189f84259056da20d742a2661b4974c360bbc18fdf096a73ac53a925
Alexis Sellier committed ago 1 parent b02adb25
lib/std/lang/lower.rad +38 -1
1418 1418
    }
1419 1419
    dataSliceHeader(b, dataName, arrInfo.length);
1420 1420
}
1421 1421
1422 1422
/// Lower a constant expression payload into a builder without slot padding.
1423 -
/// Compute the type layout only when undefined data needs a byte count.
1423 +
/// Compute the type layout when nil or undefined data needs a byte count.
1424 1424
unsafe fn lowerConstDataPayloadInto(
1425 1425
    self: &mut Lowerer,
1426 1426
    node: *ast::Node,
1427 1427
    ty: resolver::Type,
1428 1428
    dataPrefix: *[u8],
1429 1429
    b: &mut DataValueBuilder
1430 1430
) throws (LowerError) {
1431 +
    // Optional coercions define the tag and payload layout of constant values.
1432 +
    if let case resolver::Type::Optional(inner) = ty {
1433 +
        if let coercion = resolver::coercionFor(self.resolver, node) {
1434 +
            if let case resolver::Coercion::OptionalLift(target) = coercion {
1435 +
                if resolver::typesEqual(target, ty) {
1436 +
                    if resolver::isNullableType(*inner) {
1437 +
                        try lowerConstDataPayloadInto(self, node, *inner, dataPrefix, b);
1438 +
                    } else {
1439 +
                        let layout = resolver::getTypeLayout(ty);
1440 +
                        let offset = resolver::getOptionalValOffset(*inner);
1441 +
                        dataBuilderPush(b, il::DataValue {
1442 +
                            item: il::DataItem::Val { typ: il::Type::W8, val: 1 }, count: 1,
1443 +
                        });
1444 +
                        if offset > 1 {
1445 +
                            dataBuilderPush(b, il::DataValue { item: il::DataItem::Undef, count: offset - 1 });
1446 +
                        }
1447 +
                        try lowerConstDataInto(self, node, *inner, layout.size - offset, dataPrefix, b);
1448 +
                    }
1449 +
                    return;
1450 +
                }
1451 +
            }
1452 +
        }
1453 +
    }
1431 1454
    // Function pointer references in constant data.
1432 1455
    if let case resolver::Type::Fn(_) = ty {
1433 1456
        let sym = resolver::nodeData(self.resolver, node).sym
1434 1457
            else throw LowerError::MissingSymbol(node);
1435 1458
        let modId = resolver::moduleIdForSymbol(self.resolver, sym);
1448 1471
                return;
1449 1472
            }
1450 1473
        }
1451 1474
    }
1452 1475
    match node.value {
1476 +
        case ast::NodeValue::Nil => {
1477 +
            let case resolver::Type::Optional(_) = ty else throw LowerError::NilInNonOptional;
1478 +
            let layout = resolver::getTypeLayout(ty);
1479 +
            dataBuilderPush(b, il::DataValue {
1480 +
                item: il::DataItem::Val { typ: il::Type::W8, val: 0 }, count: layout.size,
1481 +
            });
1482 +
        },
1453 1483
        case ast::NodeValue::Undef => {
1454 1484
            let layout = resolver::getTypeLayout(ty);
1455 1485
            dataBuilderPush(b, il::DataValue {
1456 1486
                item: il::DataItem::Undef,
1457 1487
                count: layout.size
1552 1582
    let case resolver::Type::Array(arrInfo) = ty
1553 1583
        else throw LowerError::ExpectedArray;
1554 1584
    let length = arrInfo.length;
1555 1585
    let elemTy = *arrInfo.item;
1556 1586
1587 +
    if let case resolver::Type::Optional(_) = elemTy {
1588 +
        for _ in 0..length {
1589 +
            try lowerConstDataPayloadInto(self, repeat.item, elemTy, dataPrefix, b);
1590 +
        }
1591 +
        return;
1592 +
    }
1593 +
1557 1594
    if let case ast::NodeValue::Undef = repeat.item.value {
1558 1595
        let elemLayout = resolver::getTypeLayout(elemTy);
1559 1596
        dataBuilderPush(b, il::DataValue {
1560 1597
            item: il::DataItem::Undef,
1561 1598
            count: elemLayout.size * length
test/tests/data.optional.nil.rad added +33 -0
1 +
//! returns: 0
2 +
3 +
/// Payload with alignment padding inside a tagged optional.
4 +
record Payload: Copy {
5 +
    /// Small prefix.
6 +
    prefix: u8,
7 +
    /// Aligned value.
8 +
    value: u64,
9 +
}
10 +
11 +
/// Initialized nullable pointer table.
12 +
unsafe static POINTERS: [?*u32; 3] = [nil; 3];
13 +
/// Initialized nullable slice.
14 +
unsafe static SLICE: ?*[u8] = nil;
15 +
/// Initialized tagged record optionals.
16 +
unsafe static RECORDS: [?Payload; 2] = [nil, nil];
17 +
/// Initialized nested optional.
18 +
unsafe static NESTED: ??u32 = nil;
19 +
20 +
/// Check initialized values and storage layouts.
21 +
@default unsafe fn main() -> u32 {
22 +
    for pointer in &POINTERS[..] {
23 +
        assert pointer == nil;
24 +
    }
25 +
    assert SLICE == nil;
26 +
    assert RECORDS[0] == nil;
27 +
    assert RECORDS[1] == nil;
28 +
    assert NESTED == nil;
29 +
    set RECORDS[1] = Payload { prefix: 2, value: 40 };
30 +
    let result = RECORDS[1] else panic;
31 +
    assert result.prefix as u32 + result.value as u32 == 42;
32 +
    return 0;
33 +
}
test/tests/data.optional.value.rad added +65 -0
1 +
//! returns: 0
2 +
3 +
/// Aligned record payload in a present optional.
4 +
record Payload: Copy {
5 +
    /// Small prefix before the aligned value.
6 +
    prefix: u8,
7 +
    /// Value used to check payload alignment.
8 +
    number: u64,
9 +
}
10 +
11 +
/// Tagged payload with both empty and initialized variants.
12 +
union Value: Copy { Empty, Number(i64), Pair { left: u8, right: u64 } }
13 +
14 +
/// Optional payload in a tagged instruction.
15 +
union Instruction: Copy { Return { value: ?Value } }
16 +
17 +
/// Optional constant reused as a nested optional payload.
18 +
constant PRESENT: ?u32 = 42;
19 +
/// Absent inner payload used in a present outer optional.
20 +
constant ABSENT: ?u32 = nil;
21 +
/// Present aligned record initializer.
22 +
unsafe static RECORD: ?Payload = Payload { prefix: 2, number: 40 };
23 +
/// Present outer optional that contains a present value.
24 +
unsafe static NESTED: ??u32 = PRESENT;
25 +
/// Present outer optional that contains an absent value.
26 +
unsafe static NESTED_NIL: ??u32 = ABSENT;
27 +
/// Repeated present optional values.
28 +
unsafe static REPEATED: [?u32; 2] = [PRESENT; 2];
29 +
/// Present nullable slice initializer.
30 +
unsafe static TEXT: ?*[u8] = "answer";
31 +
32 +
/// Check initialized values and storage layouts.
33 +
@default unsafe fn main() -> u32 {
34 +
    let instructions = &[
35 +
        Instruction::Return { value: Value::Number(42) },
36 +
        Instruction::Return { value: Value::Pair { left: 2, right: 40 } },
37 +
        Instruction::Return { value: Value::Empty }
38 +
    ];
39 +
    let case Instruction::Return { value } = instructions[0] else panic;
40 +
    let present = value else panic;
41 +
    let case Value::Number(number) = present else panic;
42 +
    assert number == 42;
43 +
    let case Instruction::Return { value: second } = instructions[1] else panic;
44 +
    let pair = second else panic;
45 +
    let case Value::Pair { left, right } = pair else panic;
46 +
    assert left as u64 + right == 42;
47 +
    let case Instruction::Return { value: third } = instructions[2] else panic;
48 +
    let empty = third else panic;
49 +
    let case Value::Empty = empty else panic;
50 +
    let rec = RECORD else panic;
51 +
    assert rec.prefix as u64 + rec.number == 42;
52 +
    let inner = NESTED else panic;
53 +
    let answer = inner else panic;
54 +
    assert answer == 42;
55 +
    let missing = NESTED_NIL else panic;
56 +
    assert missing == nil;
57 +
    for item in &REPEATED[..] {
58 +
        let value = item else panic;
59 +
        assert value == 42;
60 +
    }
61 +
    let text = TEXT else panic;
62 +
    assert text.len == 6;
63 +
    assert number == 42;
64 +
    return 0;
65 +
}