compiler: Initialize optional constant data with resolved layouts
f7523bce189f84259056da20d742a2661b4974c360bbc18fdf096a73ac53a925
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 | + | } |