refactor(lang): unify core type representation
364e23465dec9c6886f404a4f1a71c7399f10485f207ea32830e57c4308cd5cb
Represent pointers, slices, arrays, and optionals as core type applications. Share pointer shape and properties, and centralize argument traversal, substitution, layout checks, equality, printing, and lowering. Preserve nullable pointer representations and typed relocation checks while reducing duplicated type-specific branches.
1 parent
b5fc83f3
lib/std/lang/lower.rad
+223 -210
| 267 | 267 | /// Trait object v-table pointer offset. |
|
| 268 | 268 | constant TRAIT_OBJ_VTABLE_OFFSET: i32 = resolver::PTR_SIZE as i32; |
|
| 269 | 269 | ||
| 270 | 270 | // Tagged Value Layout (optionals, tagged unions) |
|
| 271 | 271 | // |
|
| 272 | - | // Optionals and unions use 1-byte tags. Results use 8-byte tags. |
|
| 272 | + | // Tagged optionals and unions use 1-byte tags. Results use 8-byte tags. |
|
| 273 | 273 | // |
|
| 274 | 274 | // `{ tag: u8, [padding], payload: T }` |
|
| 275 | 275 | // |
|
| 276 | 276 | // Optionals use `tag: 0` for `nil` and `tag: 1` otherwise. |
|
| 277 | - | // When `T` is a pointer, the entire optional is stored as a single pointer. |
|
| 277 | + | // Nullable pointer-like payloads use their null data pointer as the nil niche. |
|
| 278 | 278 | // |
|
| 279 | 279 | // Tagged unions have a payload the size of the maximum variant size. |
|
| 280 | 280 | ||
| 281 | 281 | /// Offset of tag in tagged value data structure. |
|
| 282 | 282 | constant TVAL_TAG_OFFSET: i32 = 0; |
| 676 | 676 | ||
| 677 | 677 | /// Classifies a match subject by how it should be compared and destructured. |
|
| 678 | 678 | union MatchSubjectKind { |
|
| 679 | 679 | /// Regular value: direct equality comparison. |
|
| 680 | 680 | Regular, |
|
| 681 | - | /// Optional with null pointer optimization: `?*T`, `?*[T]`. |
|
| 681 | + | /// Optional with null pointer optimization. |
|
| 682 | 682 | OptionalPtr, |
|
| 683 | - | /// Optional aggregate `?T`: tagged union with payload. |
|
| 683 | + | /// Tagged optional aggregate. |
|
| 684 | 684 | OptionalAggregate, |
|
| 685 | 685 | /// Union type: tag compared against variant indices. |
|
| 686 | 686 | Union(resolver::UnionType), |
|
| 687 | 687 | } |
|
| 688 | 688 | ||
| 689 | 689 | /// Determine the kind of a match subject from its type. |
|
| 690 | 690 | fn matchSubjectKind(type: resolver::Type) -> MatchSubjectKind { |
|
| 691 | - | if resolver::isOptionalPointer(type) { |
|
| 692 | - | return MatchSubjectKind::OptionalPtr; |
|
| 693 | - | } |
|
| 694 | - | if resolver::isOptionalAggregate(type) { |
|
| 695 | - | return MatchSubjectKind::OptionalAggregate; |
|
| 691 | + | if let payload = resolver::optionalTypeArgument(type) { |
|
| 692 | + | return MatchSubjectKind::OptionalPtr |
|
| 693 | + | if resolver::isNullableType(*payload) |
|
| 694 | + | else MatchSubjectKind::OptionalAggregate; |
|
| 696 | 695 | } |
|
| 697 | 696 | if let info = unionInfoFromType(type) { |
|
| 698 | 697 | return MatchSubjectKind::Union(info); |
|
| 699 | 698 | } |
|
| 700 | 699 | return MatchSubjectKind::Regular; |
| 1076 | 1075 | case resolver::Type::U64 => namePush(builder, "u64"), |
|
| 1077 | 1076 | case resolver::Type::I8 => namePush(builder, "i8"), |
|
| 1078 | 1077 | case resolver::Type::I16 => namePush(builder, "i16"), |
|
| 1079 | 1078 | case resolver::Type::I32 => namePush(builder, "i32"), |
|
| 1080 | 1079 | case resolver::Type::I64 => namePush(builder, "i64"), |
|
| 1081 | - | case resolver::Type::Pointer(pointer) => { |
|
| 1082 | - | match pointer.class { |
|
| 1083 | - | case types::PointerClass::Owned => namePush(builder, "*"), |
|
| 1084 | - | case types::PointerClass::Ref => namePush(builder, "&"), |
|
| 1085 | - | case types::PointerClass::Unsafe => namePush(builder, "*unsafe "), |
|
| 1086 | - | } |
|
| 1087 | - | if pointer.mutable { namePush(builder, "mut "); } |
|
| 1088 | - | namePushType(self, builder, *pointer.target); |
|
| 1089 | - | } |
|
| 1090 | - | case resolver::Type::Slice(slice) => { |
|
| 1091 | - | match slice.class { |
|
| 1092 | - | case types::PointerClass::Owned => namePush(builder, "*"), |
|
| 1093 | - | case types::PointerClass::Ref => namePush(builder, "&"), |
|
| 1094 | - | case types::PointerClass::Unsafe => namePush(builder, "*unsafe "), |
|
| 1080 | + | case resolver::Type::Core(core) => { |
|
| 1081 | + | match core { |
|
| 1082 | + | case resolver::CoreType::Pointer { shape, properties, target } => { |
|
| 1083 | + | match properties.class { |
|
| 1084 | + | case types::PointerClass::Owned => namePush(builder, "*"), |
|
| 1085 | + | case types::PointerClass::Ref => namePush(builder, "&"), |
|
| 1086 | + | case types::PointerClass::Unsafe => |
|
| 1087 | + | namePush(builder, "*unsafe "), |
|
| 1088 | + | } |
|
| 1089 | + | if properties.mutable { namePush(builder, "mut "); } |
|
| 1090 | + | match shape { |
|
| 1091 | + | case resolver::PointerShape::Thin => |
|
| 1092 | + | namePushType(self, builder, *target), |
|
| 1093 | + | case resolver::PointerShape::Slice => { |
|
| 1094 | + | namePush(builder, "["); |
|
| 1095 | + | namePushType(self, builder, *target); |
|
| 1096 | + | namePush(builder, "]"); |
|
| 1097 | + | } |
|
| 1098 | + | } |
|
| 1099 | + | } |
|
| 1100 | + | case resolver::CoreType::Array { item, length } => { |
|
| 1101 | + | namePush(builder, "["); |
|
| 1102 | + | namePushType(self, builder, *item); |
|
| 1103 | + | namePush(builder, "; "); |
|
| 1104 | + | namePushType(self, builder, *length); |
|
| 1105 | + | namePush(builder, "]"); |
|
| 1106 | + | } |
|
| 1107 | + | case resolver::CoreType::Optional { payload } => { |
|
| 1108 | + | namePush(builder, "?"); |
|
| 1109 | + | namePushType(self, builder, *payload); |
|
| 1110 | + | } |
|
| 1095 | 1111 | } |
|
| 1096 | - | if slice.mutable { namePush(builder, "mut "); } |
|
| 1097 | - | namePush(builder, "["); |
|
| 1098 | - | namePushType(self, builder, *slice.item); |
|
| 1099 | - | namePush(builder, "]"); |
|
| 1100 | - | } |
|
| 1101 | - | case resolver::Type::Array(array) => { |
|
| 1102 | - | namePush(builder, "["); |
|
| 1103 | - | namePushType(self, builder, *array.item); |
|
| 1104 | - | namePush(builder, "; "); |
|
| 1105 | - | namePushType(self, builder, *array.length); |
|
| 1106 | - | namePush(builder, "]"); |
|
| 1107 | 1112 | } |
|
| 1108 | 1113 | case resolver::Type::ConstArgument { value, .. } => { |
|
| 1109 | 1114 | if value.negative { namePush(builder, "-"); } |
|
| 1110 | 1115 | namePushU64(builder, value.magnitude); |
|
| 1111 | 1116 | } |
|
| 1112 | - | case resolver::Type::Optional(inner) => { |
|
| 1113 | - | namePush(builder, "?"); |
|
| 1114 | - | namePushType(self, builder, *inner); |
|
| 1115 | - | } |
|
| 1116 | 1117 | case resolver::Type::Fn(info) => { |
|
| 1117 | 1118 | if info.isUnsafe { namePush(builder, "unsafe "); } |
|
| 1118 | 1119 | namePush(builder, "fn("); |
|
| 1119 | 1120 | for param, i in info.paramTypes { |
|
| 1120 | 1121 | if i > 0 { namePush(builder, ", "); } |
| 1167 | 1168 | case resolver::NominalType::Placeholder(_) => |
|
| 1168 | 1169 | panic "namePushType: unresolved nominal type", |
|
| 1169 | 1170 | } |
|
| 1170 | 1171 | } |
|
| 1171 | 1172 | case resolver::Type::TraitObject(object) => { |
|
| 1172 | - | match object.class { |
|
| 1173 | + | match object.properties.class { |
|
| 1173 | 1174 | case types::PointerClass::Owned => namePush(builder, "*"), |
|
| 1174 | 1175 | case types::PointerClass::Ref => namePush(builder, "&"), |
|
| 1175 | - | case types::PointerClass::Unsafe => namePush(builder, "*unsafe "), |
|
| 1176 | + | case types::PointerClass::Unsafe => |
|
| 1177 | + | namePush(builder, "*unsafe "), |
|
| 1176 | 1178 | } |
|
| 1177 | - | if object.mutable { namePush(builder, "mut "); } |
|
| 1179 | + | if object.properties.mutable { namePush(builder, "mut "); } |
|
| 1178 | 1180 | namePush(builder, "opaque "); |
|
| 1179 | 1181 | namePushTrait(self, builder, object.traitInfo); |
|
| 1180 | 1182 | } |
|
| 1181 | 1183 | else => panic "namePushType: non-concrete type", |
|
| 1182 | 1184 | } |
| 1667 | 1669 | } |
|
| 1668 | 1670 | throw LowerError::MissingConst(node); |
|
| 1669 | 1671 | }; |
|
| 1670 | 1672 | ||
| 1671 | 1673 | if let case resolver::ConstValue::String(s) = val { |
|
| 1672 | - | if let case resolver::Type::Slice(_) = ty { |
|
| 1674 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, .. }) = ty { |
|
| 1673 | 1675 | let strSym = try getOrCreateStringData(self, s, dataPrefix); |
|
| 1674 | 1676 | dataSliceHeader(b, strSym, s.len); |
|
| 1675 | 1677 | return; |
|
| 1676 | 1678 | } |
|
| 1677 | 1679 | } |
| 1742 | 1744 | addr: ast::AddressOf, |
|
| 1743 | 1745 | ty: resolver::Type, |
|
| 1744 | 1746 | dataPrefix: *[u8], |
|
| 1745 | 1747 | b: *mut DataValueBuilder |
|
| 1746 | 1748 | ) throws (LowerError) { |
|
| 1747 | - | let case resolver::Type::Slice(slice) = ty |
|
| 1749 | + | let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, properties: slice, .. }) = ty |
|
| 1748 | 1750 | else throw LowerError::ExpectedSliceOrArray; |
|
| 1749 | 1751 | let targetTy = resolver::typeFor(self.resolver, addr.target) |
|
| 1750 | 1752 | else throw LowerError::MissingType(addr.target); |
|
| 1751 | - | let case resolver::Type::Array(arrInfo) = targetTy |
|
| 1753 | + | let case resolver::Type::Core(resolver::CoreType::Array { length, .. }) = targetTy |
|
| 1752 | 1754 | else throw LowerError::ExpectedArray; |
|
| 1753 | 1755 | ||
| 1754 | 1756 | let mut nested = dataBuilder(self.allocator); |
|
| 1755 | 1757 | let layout = resolver::getTypeLayout(targetTy); |
|
| 1756 | 1758 | try lowerConstDataInto(self, addr.target, targetTy, layout.size, dataPrefix, &mut nested); |
| 1765 | 1767 | set dataName = try pushDeclData(self, layout.size, layout.alignment, readOnly, backing.values, dataPrefix); |
|
| 1766 | 1768 | } |
|
| 1767 | 1769 | } else { |
|
| 1768 | 1770 | set dataName = try pushDeclData(self, layout.size, layout.alignment, readOnly, backing.values, dataPrefix); |
|
| 1769 | 1771 | } |
|
| 1770 | - | let length = resolver::concreteArrayLength(arrInfo.length) |
|
| 1772 | + | let concreteLength = resolver::concreteArrayLength(length) |
|
| 1771 | 1773 | else throw LowerError::MissingMetadata; |
|
| 1772 | - | dataSliceHeader(b, dataName, length); |
|
| 1774 | + | dataSliceHeader(b, dataName, concreteLength); |
|
| 1773 | 1775 | } |
|
| 1774 | 1776 | ||
| 1775 | 1777 | /// Lower a constant expression payload into a builder without slot padding. |
|
| 1776 | 1778 | fn lowerConstDataPayloadInto( |
|
| 1777 | 1779 | self: *mut Lowerer, |
| 1880 | 1882 | elems: *mut [*ast::Node], |
|
| 1881 | 1883 | ty: resolver::Type, |
|
| 1882 | 1884 | dataPrefix: *[u8], |
|
| 1883 | 1885 | b: *mut DataValueBuilder |
|
| 1884 | 1886 | ) throws (LowerError) { |
|
| 1885 | - | let case resolver::Type::Array(arrInfo) = ty |
|
| 1887 | + | let case resolver::Type::Core(resolver::CoreType::Array { item, .. }) = ty |
|
| 1886 | 1888 | else throw LowerError::ExpectedArray; |
|
| 1887 | - | let elemTy = *arrInfo.item; |
|
| 1889 | + | let elemTy = *item; |
|
| 1888 | 1890 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1889 | 1891 | ||
| 1890 | 1892 | for elem in elems { |
|
| 1891 | 1893 | try lowerConstDataInto(self, elem, elemTy, elemLayout.size, dataPrefix, b); |
|
| 1892 | 1894 | } |
| 1898 | 1900 | repeat: ast::ArrayRepeatLit, |
|
| 1899 | 1901 | ty: resolver::Type, |
|
| 1900 | 1902 | dataPrefix: *[u8], |
|
| 1901 | 1903 | b: *mut DataValueBuilder |
|
| 1902 | 1904 | ) throws (LowerError) { |
|
| 1903 | - | let case resolver::Type::Array(arrInfo) = ty |
|
| 1905 | + | let case resolver::Type::Core(resolver::CoreType::Array { item, length }) = ty |
|
| 1904 | 1906 | else throw LowerError::ExpectedArray; |
|
| 1905 | - | let length = resolver::concreteArrayLength(arrInfo.length) |
|
| 1907 | + | let concreteLength = resolver::concreteArrayLength(length) |
|
| 1906 | 1908 | else throw LowerError::MissingMetadata; |
|
| 1907 | - | let elemTy = *arrInfo.item; |
|
| 1909 | + | let elemTy = *item; |
|
| 1908 | 1910 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 1909 | 1911 | ||
| 1910 | 1912 | if let case ast::NodeValue::Undef = repeat.item.value { |
|
| 1911 | 1913 | dataBuilderPush(b, il::DataValue { |
|
| 1912 | 1914 | item: il::DataItem::Undef, |
|
| 1913 | - | count: elemLayout.size * length |
|
| 1915 | + | count: elemLayout.size * concreteLength |
|
| 1914 | 1916 | }); |
|
| 1915 | 1917 | } else if let val = resolver::constValueEntry(self.resolver, repeat.item) { |
|
| 1916 | 1918 | if let case resolver::ConstValue::String(_) = val { |
|
| 1917 | 1919 | // A string used as a slice is represented by a three-word slice |
|
| 1918 | 1920 | // header, not by the bytes of the string itself. |
|
| 1919 | - | for _ in 0..length { |
|
| 1921 | + | for _ in 0..concreteLength { |
|
| 1920 | 1922 | try lowerConstDataInto(self, repeat.item, elemTy, elemLayout.size, dataPrefix, b); |
|
| 1921 | 1923 | } |
|
| 1922 | 1924 | } else { |
|
| 1923 | 1925 | dataBuilderPush(b, il::DataValue { |
|
| 1924 | 1926 | item: constValueToDataItem(self, val, elemTy), |
|
| 1925 | - | count: length |
|
| 1927 | + | count: concreteLength |
|
| 1926 | 1928 | }); |
|
| 1927 | 1929 | } |
|
| 1928 | 1930 | } else { |
|
| 1929 | - | for _ in 0..length { |
|
| 1931 | + | for _ in 0..concreteLength { |
|
| 1930 | 1932 | try lowerConstDataInto(self, repeat.item, elemTy, elemLayout.size, dataPrefix, b); |
|
| 1931 | 1933 | } |
|
| 1932 | 1934 | } |
|
| 1933 | 1935 | } |
|
| 1934 | 1936 |
| 2793 | 2795 | } |
|
| 2794 | 2796 | let isNil = pattern.value == ast::NodeValue::Nil; |
|
| 2795 | 2797 | ||
| 2796 | 2798 | match subject.kind { |
|
| 2797 | 2799 | case MatchSubjectKind::OptionalPtr if isNil => { |
|
| 2798 | - | // Null pointer optimization: branch on the data pointer being null. |
|
| 2800 | + | // Null pointer optimization branches on the niche data pointer. |
|
| 2799 | 2801 | let nilReg = try optionalNilReg(self, subject.val, subject.type); |
|
| 2800 | 2802 | try emitBrCmp(self, il::CmpOp::Eq, il::Type::W64, il::Val::Reg(nilReg), il::Val::Imm(0), matchBlock, fallthrough); |
|
| 2801 | 2803 | } |
|
| 2802 | 2804 | case MatchSubjectKind::OptionalAggregate => { |
|
| 2803 | 2805 | let base = emitValToReg(self, subject.val); |
|
| 2804 | 2806 | ||
| 2805 | - | if isNil { // Optional aggregate: `nil` means tag is zero. |
|
| 2807 | + | if isNil { |
|
| 2806 | 2808 | let tagReg = tvalTagReg(self, base); |
|
| 2807 | 2809 | try emitBr(self, tagReg, fallthrough, matchBlock); |
|
| 2808 | 2810 | } else { |
|
| 2809 | - | // `lowerExpr` applies the resolver's optional-lift coercion, |
|
| 2810 | - | // materializing the tagged representation for value patterns. |
|
| 2811 | + | // `lowerExpr` applies the optional-lift coercion and |
|
| 2812 | + | // materializes the tagged representation for value patterns. |
|
| 2811 | 2813 | let pattVal = try lowerExpr(self, pattern); |
|
| 2812 | 2814 | let pattReg = emitValToReg(self, pattVal); |
|
| 2813 | 2815 | let eq = try lowerOptionalEq(self, subject.bindType, base, pattReg, 0); |
|
| 2814 | 2816 | let eqReg = emitValToReg(self, eq); |
|
| 2815 | 2817 |
| 2817 | 2819 | } |
|
| 2818 | 2820 | } |
|
| 2819 | 2821 | case MatchSubjectKind::Union(unionInfo) => { |
|
| 2820 | 2822 | assert not isNil; |
|
| 2821 | 2823 | ||
| 2822 | - | let case resolver::NodeExtra::UnionVariant { tag: variantTag, .. } = |
|
| 2823 | - | resolver::nodeData(self.low.resolver, pattern).extra |
|
| 2824 | - | else { |
|
| 2824 | + | let case resolver::NodeExtra::UnionVariant { |
|
| 2825 | + | tag: variantTag, |
|
| 2826 | + | .. |
|
| 2827 | + | } = resolver::nodeData(self.low.resolver, pattern).extra else { |
|
| 2825 | 2828 | throw LowerError::ExpectedVariant; |
|
| 2826 | 2829 | }; |
|
| 2827 | 2830 | // Void unions are passed by value (the tag itself). |
|
| 2828 | 2831 | // Non-void unions are passed by reference (need to load tag). |
|
| 2829 | 2832 | // When matching by reference, always load from the pointer. |
|
| 2830 | 2833 | if unionInfo.isAllVoid { |
|
| 2831 | 2834 | let mut tagVal = subject.val; |
|
| 2832 | 2835 | match subject.by { |
|
| 2833 | - | case resolver::MatchBy::Ref, resolver::MatchBy::MutRef => { |
|
| 2836 | + | case resolver::MatchBy::Ref, |
|
| 2837 | + | resolver::MatchBy::MutRef => { |
|
| 2834 | 2838 | let base = emitValToReg(self, subject.val); |
|
| 2835 | 2839 | set tagVal = loadTag(self, base, 0, il::Type::W8); |
|
| 2836 | 2840 | } |
|
| 2837 | 2841 | case resolver::MatchBy::Value => {} |
|
| 2838 | 2842 | } |
| 2842 | 2846 | let tagReg = tvalTagReg(self, base); |
|
| 2843 | 2847 | ||
| 2844 | 2848 | try emitBrCmp(self, il::CmpOp::Eq, il::Type::W8, il::Val::Reg(tagReg), il::Val::Imm(variantTag as i64), matchBlock, fallthrough); |
|
| 2845 | 2849 | } |
|
| 2846 | 2850 | } |
|
| 2847 | - | else => { // Value comparison. |
|
| 2851 | + | else => { |
|
| 2848 | 2852 | assert not isNil; |
|
| 2849 | 2853 | let pattVal = try lowerExpr(self, pattern); |
|
| 2850 | 2854 | if isAggregateType(subject.type) { |
|
| 2851 | 2855 | // Aggregate types need structural comparison rather than |
|
| 2852 | 2856 | // scalar compare. |
| 2878 | 2882 | for i in 0..(patterns.len - 1) { |
|
| 2879 | 2883 | let pattern = patterns[i]; |
|
| 2880 | 2884 | let nextArm = try createBlock(self, "arm"); |
|
| 2881 | 2885 | try emitPatternMatch(self, subject, pattern, matchBlock, nextArm); |
|
| 2882 | 2886 | ||
| 2883 | - | // Seal the intermediate arm block: all predecessor edges are known |
|
| 2887 | + | // Seal the intermediate arm block: all predecessor edges are known. |
|
| 2884 | 2888 | // This ensures SSA construction can resolve variable uses through |
|
| 2885 | - | // single-predecessor optimization instead of creating unresolved block |
|
| 2886 | - | // parameters. |
|
| 2889 | + | // single-predecessor optimization instead of creating unresolved |
|
| 2890 | + | // block parameters. |
|
| 2887 | 2891 | try switchToAndSeal(self, nextArm); |
|
| 2888 | 2892 | } |
|
| 2889 | 2893 | // Handle last pattern: go to fallthrough block on failure. |
|
| 2890 | 2894 | let last = patterns[patterns.len - 1]; |
|
| 2891 | 2895 | try emitPatternMatch(self, subject, last, matchBlock, fallthrough); |
|
| 2892 | 2896 | } |
|
| 2893 | - | ||
| 2894 | 2897 | /// Emit a match binding pattern. |
|
| 2895 | 2898 | /// Binding patterns always match for regular values, but for optionals they |
|
| 2896 | 2899 | /// check for the presence of a value. Jumps to `valuePresent` on success, |
|
| 2897 | 2900 | /// `valueAbsent` on failure. |
|
| 2898 | 2901 | fn emitBindingTest( |
| 3534 | 3537 | if unwrapped.by == resolver::MatchBy::Value and isAggregateType(unwrapped.effectiveTy) { |
|
| 3535 | 3538 | set val = try emitStackVal(self, unwrapped.effectiveTy, val); |
|
| 3536 | 3539 | } |
|
| 3537 | 3540 | ||
| 3538 | 3541 | let mut bindType = unwrapped.effectiveTy; |
|
| 3539 | - | if let case resolver::Type::Optional(inner) = unwrapped.effectiveTy { |
|
| 3540 | - | set bindType = *inner; |
|
| 3542 | + | if let case resolver::Type::Core( |
|
| 3543 | + | resolver::CoreType::Optional { payload } |
|
| 3544 | + | ) = unwrapped.effectiveTy { |
|
| 3545 | + | set bindType = *payload; |
|
| 3541 | 3546 | } |
|
| 3542 | 3547 | let ilType = ilType(self.low, unwrapped.effectiveTy); |
|
| 3543 | 3548 | let kind = matchSubjectKind(unwrapped.effectiveTy); |
|
| 3544 | 3549 | ||
| 3545 | 3550 | return MatchSubject { val, type: unwrapped.effectiveTy, ilType, bindType, kind, by: unwrapped.by }; |
| 3573 | 3578 | let tagReg = nextReg(self); |
|
| 3574 | 3579 | emitLoadW64At(self, tagReg, base, TVAL_TAG_OFFSET); |
|
| 3575 | 3580 | return tagReg; |
|
| 3576 | 3581 | } |
|
| 3577 | 3582 | ||
| 3578 | - | /// Get the register to compare against `0` for optional `nil` checking. |
|
| 3579 | - | /// For null-ptr-optimized types, loads the data pointer, or returns it |
|
| 3580 | - | /// directly for scalar pointers. For aggregates, returns the tag register. |
|
| 3583 | + | /// Return the nil discriminator: thin pointers use their value, slices use their data pointer, and tagged optionals use their tag. |
|
| 3581 | 3584 | fn optionalNilReg(self: *mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) { |
|
| 3582 | 3585 | let reg = emitValToReg(self, val); |
|
| 3583 | - | ||
| 3584 | - | if let case resolver::Type::Optional(inner) = typ { |
|
| 3585 | - | if let case resolver::Type::Slice(_) = *inner { |
|
| 3586 | - | let ptrReg = nextReg(self); |
|
| 3587 | - | emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET); |
|
| 3588 | - | return ptrReg; |
|
| 3589 | - | } |
|
| 3590 | - | if let case resolver::Type::Pointer(_) = *inner { |
|
| 3591 | - | return reg; |
|
| 3586 | + | if let inner = resolver::optionalTypeArgument(typ) { |
|
| 3587 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape, .. }) = *inner { |
|
| 3588 | + | match shape { |
|
| 3589 | + | case resolver::PointerShape::Thin => return reg, |
|
| 3590 | + | case resolver::PointerShape::Slice => { |
|
| 3591 | + | let ptrReg = nextReg(self); |
|
| 3592 | + | emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET); |
|
| 3593 | + | return ptrReg; |
|
| 3594 | + | } |
|
| 3595 | + | } |
|
| 3592 | 3596 | } |
|
| 3593 | 3597 | return tvalTagReg(self, reg); |
|
| 3594 | 3598 | } |
|
| 3595 | 3599 | return reg; |
|
| 3596 | 3600 | } |
| 3603 | 3607 | return il::Val::Imm(1) if isEq else il::Val::Imm(0); |
|
| 3604 | 3608 | } |
|
| 3605 | 3609 | let val = try lowerExpr(self, opt); |
|
| 3606 | 3610 | let cmpReg = try optionalNilReg(self, val, optTy); |
|
| 3607 | 3611 | ||
| 3608 | - | // Null-pointer-optimized types compare a 64-bit pointer against zero. |
|
| 3609 | - | // Aggregate optionals compare an 8-bit tag byte against zero. |
|
| 3610 | - | let cmpType = il::Type::W64 if resolver::isOptionalPointer(optTy) else il::Type::W8; |
|
| 3612 | + | let inner = resolver::optionalTypeArgument(optTy) |
|
| 3613 | + | else throw LowerError::ExpectedOptional; |
|
| 3614 | + | let cmpType = il::Type::W64 if resolver::isNullableType(*inner) else il::Type::W8; |
|
| 3611 | 3615 | ||
| 3612 | 3616 | let op = il::BinOp::Eq if isEq else il::BinOp::Ne; |
|
| 3613 | 3617 | return emitTypedBinOp(self, op, cmpType, il::Val::Reg(cmpReg), il::Val::Imm(0)); |
|
| 3614 | 3618 | } |
|
| 3615 | 3619 |
| 3728 | 3732 | /// tested against the subject element, branching to `failBlock` on mismatch. |
|
| 3729 | 3733 | fn bindArrayPatternElements( |
|
| 3730 | 3734 | self: *mut FnLowerer, |
|
| 3731 | 3735 | subject: *MatchSubject, |
|
| 3732 | 3736 | items: *mut [*ast::Node], |
|
| 3733 | - | failBlock: BlockId |
|
| 3737 | + | failBlock: BlockId, |
|
| 3734 | 3738 | ) throws (LowerError) { |
|
| 3735 | - | let case resolver::Type::Array(arrInfo) = subject.type |
|
| 3739 | + | let case resolver::Type::Core(resolver::CoreType::Array { item, .. }) = subject.type |
|
| 3736 | 3740 | else throw LowerError::ExpectedSliceOrArray; |
|
| 3737 | 3741 | ||
| 3738 | - | let elemTy = *arrInfo.item; |
|
| 3742 | + | let elemTy = *item; |
|
| 3739 | 3743 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 3740 | 3744 | let stride = elemLayout.size as i32; |
|
| 3741 | 3745 | let base = emitValToReg(self, subject.val); |
|
| 3742 | 3746 | ||
| 3743 | 3747 | for elem, i in items { |
| 3812 | 3816 | } |
|
| 3813 | 3817 | // Plain nested record destructuring pattern. |
|
| 3814 | 3818 | // Auto-deref: if the field is a pointer, load it first. |
|
| 3815 | 3819 | let mut derefType = fieldInfo.fieldType; |
|
| 3816 | 3820 | let mut nestedBase = emitPtrOffset(self, base, fieldInfo.offset); |
|
| 3817 | - | if let case resolver::Type::Pointer(pointer) = fieldInfo.fieldType { |
|
| 3821 | + | if let case resolver::Type::Core( |
|
| 3822 | + | resolver::CoreType::Pointer { shape: resolver::PointerShape::Thin, target, .. } |
|
| 3823 | + | ) = fieldInfo.fieldType { |
|
| 3818 | 3824 | let ptrReg = nextReg(self); |
|
| 3819 | 3825 | emitLoadW64At(self, ptrReg, nestedBase, 0); |
|
| 3820 | 3826 | set nestedBase = ptrReg; |
|
| 3821 | - | set derefType = *pointer.target; |
|
| 3827 | + | set derefType = *target; |
|
| 3822 | 3828 | } |
|
| 3823 | 3829 | let recInfo = resolver::getRecord(derefType) |
|
| 3824 | 3830 | else throw LowerError::ExpectedRecord; |
|
| 3825 | 3831 | ||
| 3826 | 3832 | try bindNestedRecordFields(self, nestedBase, lit, recInfo, matchBy, failBlock); |
| 3848 | 3854 | ||
| 3849 | 3855 | // Auto-deref: when the field is a pointer and the pattern destructures |
|
| 3850 | 3856 | // the pointed-to value, load the pointer and use the target type. |
|
| 3851 | 3857 | // The loaded pointer becomes the base address for the nested subject. |
|
| 3852 | 3858 | let mut derefBase: ?il::Reg = nil; |
|
| 3853 | - | if let case resolver::Type::Pointer(pointer) = fieldType { |
|
| 3859 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Thin, target, .. }) = fieldType { |
|
| 3854 | 3860 | if resolver::isDestructuringPattern(pattern) { |
|
| 3855 | 3861 | let ptrReg = nextReg(self); |
|
| 3856 | 3862 | emitLoadW64At(self, ptrReg, fieldPtr, 0); |
|
| 3857 | 3863 | set derefBase = ptrReg; |
|
| 3858 | - | set fieldType = *pointer.target; |
|
| 3864 | + | set fieldType = *target; |
|
| 3859 | 3865 | } |
|
| 3860 | 3866 | } |
|
| 3861 | 3867 | // Build a MatchSubject for the nested field. |
|
| 3862 | 3868 | let ilTy = ilType(self.low, fieldType); |
|
| 3863 | 3869 | let kind = matchSubjectKind(fieldType); |
| 4463 | 4469 | return ty; |
|
| 4464 | 4470 | } |
|
| 4465 | 4471 | ||
| 4466 | 4472 | /// Check if a resolver type lowers to an aggregate in memory. |
|
| 4467 | 4473 | fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4468 | - | match typ { |
|
| 4469 | - | case resolver::Type::Optional(resolver::Type::Pointer(_)) => { |
|
| 4470 | - | // Optional pointers are scalar due to NPO. |
|
| 4474 | + | if let payload = resolver::optionalTypeArgument(typ) { |
|
| 4475 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Thin, .. }) = *payload { |
|
| 4476 | + | // Optional thin pointers are scalar due to NPO. |
|
| 4471 | 4477 | return false; |
|
| 4472 | 4478 | } |
|
| 4473 | - | case resolver::Type::Optional(_) => { |
|
| 4474 | - | // All other optionals, including optional slices, are aggregates. |
|
| 4475 | - | return true; |
|
| 4476 | - | } |
|
| 4477 | - | case resolver::Type::Nominal(_) => { |
|
| 4479 | + | // All other optionals, including optional slices, are aggregates. |
|
| 4480 | + | return true; |
|
| 4481 | + | } |
|
| 4482 | + | match typ { |
|
| 4483 | + | case resolver::Type::Nominal(_) => |
|
| 4478 | 4484 | // Void unions are small enough to pass by value. |
|
| 4479 | - | return not resolver::isVoidUnion(typ); |
|
| 4480 | - | } |
|
| 4481 | - | case resolver::Type::Slice(_), |
|
| 4482 | - | resolver::Type::TraitObject(_), |
|
| 4483 | - | resolver::Type::Array(_), |
|
| 4484 | - | resolver::Type::Nil => return true, |
|
| 4485 | + | return not resolver::isVoidUnion(typ), |
|
| 4486 | + | case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, .. }) => return true, |
|
| 4487 | + | case resolver::Type::TraitObject(_) => return true, |
|
| 4488 | + | case resolver::Type::Nil => return true, |
|
| 4489 | + | case resolver::Type::Core(resolver::CoreType::Array { .. }) => return true, |
|
| 4485 | 4490 | else => return false, |
|
| 4486 | 4491 | } |
|
| 4487 | 4492 | } |
|
| 4488 | 4493 | ||
| 4489 | 4494 | /// Check if a resolver type is a small aggregate that can be |
| 4607 | 4612 | } |
|
| 4608 | 4613 | } |
|
| 4609 | 4614 | return il::Val::Reg(dst); |
|
| 4610 | 4615 | } |
|
| 4611 | 4616 | ||
| 4612 | - | /// Wrap a value in an optional type. |
|
| 4613 | - | /// |
|
| 4614 | - | /// For optional pointers (`?*T`), the value is returned as-is since pointers |
|
| 4615 | - | /// use zero to represent `nil`. For other optionals, builds a tagged aggregate. |
|
| 4616 | - | /// with the tag set to `1`, and the value as payload. |
|
| 4617 | + | /// Wrap an optional payload: nullable pointers keep their representation, while other payloads use a present tag and stored value. |
|
| 4617 | 4618 | fn wrapInOptional(self: *mut FnLowerer, val: il::Val, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4618 | - | let case resolver::Type::Optional(inner) = optType else { |
|
| 4619 | - | throw LowerError::ExpectedOptional; |
|
| 4620 | - | }; |
|
| 4621 | - | // Null-pointer-optimized (NPO) types are used as-is -- valid values are never null. |
|
| 4619 | + | let inner = resolver::optionalTypeArgument(optType) |
|
| 4620 | + | else throw LowerError::ExpectedOptional; |
|
| 4622 | 4621 | if resolver::isNullableType(*inner) { |
|
| 4623 | 4622 | return val; |
|
| 4624 | 4623 | } |
|
| 4625 | - | let layout = resolver::getTypeLayout(optType); |
|
| 4626 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4627 | - | ||
| 4628 | - | return try buildTagged(self, layout, 1, val, *inner, 1, valOffset); |
|
| 4624 | + | return try buildTagged(self, resolver::getTypeLayout(optType), 1, val, *inner, 1, resolver::getOptionalValOffset(*inner) as i32); |
|
| 4629 | 4625 | } |
|
| 4630 | 4626 | ||
| 4631 | - | /// Build a `nil` value for an optional type. |
|
| 4632 | - | /// |
|
| 4633 | - | /// For optional pointers (`?*T`), returns an immediate `0` (null pointer). |
|
| 4634 | - | /// For other optionals, builds a tagged aggregate with tag set to `0` (absent). |
|
| 4627 | + | /// Build nil: thin pointers use zero, slices use a zero data pointer and length, and tagged optionals use an absent tag. |
|
| 4635 | 4628 | fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4636 | - | let case resolver::Type::Optional(inner) = optType |
|
| 4629 | + | let inner = resolver::optionalTypeArgument(optType) |
|
| 4637 | 4630 | else throw LowerError::ExpectedOptional; |
|
| 4638 | - | if let case resolver::Type::Pointer(_) = *inner { |
|
| 4639 | - | return il::Val::Imm(0); |
|
| 4640 | - | } |
|
| 4641 | - | if let case resolver::Type::Slice(slice) = *inner { |
|
| 4642 | - | return try buildSliceValue( |
|
| 4643 | - | self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0) |
|
| 4644 | - | ); |
|
| 4631 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape, properties, target }) = *inner { |
|
| 4632 | + | match shape { |
|
| 4633 | + | case resolver::PointerShape::Thin => return il::Val::Imm(0), |
|
| 4634 | + | case resolver::PointerShape::Slice => |
|
| 4635 | + | return try buildSliceValue(self, target, properties.mutable, il::Val::Imm(0), il::Val::Imm(0)), |
|
| 4636 | + | } |
|
| 4645 | 4637 | } |
|
| 4646 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4647 | - | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4638 | + | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, resolver::getOptionalValOffset(*inner) as i32); |
|
| 4648 | 4639 | } |
|
| 4649 | 4640 | ||
| 4650 | 4641 | /// Build a result value for throwing functions. |
|
| 4651 | 4642 | fn buildResult( |
|
| 4652 | 4643 | self: *mut FnLowerer, |
| 4667 | 4658 | elemTy: *resolver::Type, |
|
| 4668 | 4659 | mutable: bool, |
|
| 4669 | 4660 | ptrVal: il::Val, |
|
| 4670 | 4661 | lenVal: il::Val |
|
| 4671 | 4662 | ) -> il::Val throws (LowerError) { |
|
| 4672 | - | let sliceType = resolver::Type::Slice(resolver::SliceType { |
|
| 4673 | - | class: types::PointerClass::Unsafe, |
|
| 4674 | - | item: elemTy, |
|
| 4663 | + | let sliceType = resolver::sliceType( |
|
| 4664 | + | types::PointerClass::Unsafe, |
|
| 4665 | + | elemTy, |
|
| 4675 | 4666 | mutable, |
|
| 4676 | - | }); |
|
| 4667 | + | ); |
|
| 4677 | 4668 | let dst = try emitReserve(self, sliceType); |
|
| 4678 | - | let ptrTy = resolver::Type::Pointer(resolver::PointerType { |
|
| 4679 | - | class: types::PointerClass::Unsafe, |
|
| 4680 | - | target: elemTy, |
|
| 4669 | + | let ptrTy = resolver::pointerType( |
|
| 4670 | + | types::PointerClass::Unsafe, |
|
| 4671 | + | elemTy, |
|
| 4681 | 4672 | mutable, |
|
| 4682 | - | }); |
|
| 4673 | + | ); |
|
| 4683 | 4674 | ||
| 4684 | 4675 | try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal); |
|
| 4685 | 4676 | try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal); |
|
| 4686 | 4677 | ||
| 4687 | 4678 | return il::Val::Reg(dst); |
| 4866 | 4857 | mutable: bool, |
|
| 4867 | 4858 | a: il::Reg, |
|
| 4868 | 4859 | b: il::Reg, |
|
| 4869 | 4860 | offset: i32 |
|
| 4870 | 4861 | ) -> il::Val throws (LowerError) { |
|
| 4871 | - | let ptrTy = resolver::Type::Pointer(resolver::PointerType { |
|
| 4872 | - | class: types::PointerClass::Unsafe, |
|
| 4873 | - | target: elemTy, |
|
| 4862 | + | let ptrTy = resolver::pointerType( |
|
| 4863 | + | types::PointerClass::Unsafe, |
|
| 4864 | + | elemTy, |
|
| 4874 | 4865 | mutable, |
|
| 4875 | - | }); |
|
| 4866 | + | ); |
|
| 4876 | 4867 | let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy); |
|
| 4877 | 4868 | let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32); |
|
| 4878 | 4869 | ||
| 4879 | 4870 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq); |
|
| 4880 | 4871 | } |
| 4905 | 4896 | ||
| 4906 | 4897 | // For simple inner types (no unions/nested optionals), use branchless comparison. |
|
| 4907 | 4898 | // Unions and nested optionals may contain uninitialized payload bytes |
|
| 4908 | 4899 | // when nil, so they need a guarded comparison. |
|
| 4909 | 4900 | let isUnion = unionInfoFromType(inner) <> nil; |
|
| 4910 | - | let mut isOptional = false; |
|
| 4911 | - | if let case resolver::Type::Optional(_) = inner { |
|
| 4912 | - | set isOptional = true; |
|
| 4913 | - | } |
|
| 4914 | - | if not isUnion and not isOptional { |
|
| 4901 | + | if not isUnion and not resolver::isOptionalType(inner) { |
|
| 4915 | 4902 | let tagEq = emitTypedBinOp(self, il::BinOp::Eq, il::Type::W8, tagA, tagB); |
|
| 4916 | 4903 | let tagNil = emitTypedBinOp(self, il::BinOp::Eq, il::Type::W8, tagA, il::Val::Imm(0)); |
|
| 4917 | 4904 | let payloadEq = try emitEqAtOffset(self, a, b, offset + valOffset, inner); |
|
| 4918 | 4905 | ||
| 4919 | 4906 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, tagEq, |
| 5089 | 5076 | } |
|
| 5090 | 5077 | ||
| 5091 | 5078 | /// Compare two array values for equality, element by element. |
|
| 5092 | 5079 | fn lowerArrayEq( |
|
| 5093 | 5080 | self: *mut FnLowerer, |
|
| 5094 | - | arr: resolver::ArrayType, |
|
| 5081 | + | item: *resolver::Type, |
|
| 5082 | + | lengthArgument: *resolver::Type, |
|
| 5095 | 5083 | a: il::Reg, |
|
| 5096 | 5084 | b: il::Reg, |
|
| 5097 | 5085 | offset: i32 |
|
| 5098 | 5086 | ) -> il::Val throws (LowerError) { |
|
| 5099 | - | let elemLayout = resolver::getTypeLayout(*arr.item); |
|
| 5087 | + | let elemLayout = resolver::getTypeLayout(*item); |
|
| 5100 | 5088 | let stride = elemLayout.size as i32; |
|
| 5101 | 5089 | let mut result: ?il::Val = nil; |
|
| 5102 | - | let length = resolver::concreteArrayLength(arr.length) |
|
| 5090 | + | let length = resolver::concreteArrayLength(lengthArgument) |
|
| 5103 | 5091 | else throw LowerError::MissingMetadata; |
|
| 5104 | 5092 | ||
| 5105 | 5093 | for i in 0..length { |
|
| 5106 | 5094 | let elemOffset = offset + (i as i32) * stride; |
|
| 5107 | - | let cmp = try emitEqAtOffset(self, a, b, elemOffset, *arr.item); |
|
| 5095 | + | let cmp = try emitEqAtOffset(self, a, b, elemOffset, *item); |
|
| 5108 | 5096 | set result = emitLogicalAnd(self, result, cmp); |
|
| 5109 | 5097 | } |
|
| 5110 | 5098 | if let r = result { |
|
| 5111 | 5099 | return r; |
|
| 5112 | 5100 | } |
| 5120 | 5108 | typ: resolver::Type, |
|
| 5121 | 5109 | a: il::Reg, |
|
| 5122 | 5110 | b: il::Reg, |
|
| 5123 | 5111 | offset: i32 |
|
| 5124 | 5112 | ) -> il::Val throws (LowerError) { |
|
| 5113 | + | let mut effective = typ; |
|
| 5114 | + | if let payload = resolver::optionalTypeArgument(typ) { |
|
| 5115 | + | set effective = *payload; |
|
| 5116 | + | } |
|
| 5117 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, properties, target }) = effective { |
|
| 5118 | + | return try lowerSliceEq( |
|
| 5119 | + | self, target, properties.mutable, a, b, offset |
|
| 5120 | + | ); |
|
| 5121 | + | } |
|
| 5125 | 5122 | match typ { |
|
| 5126 | - | case resolver::Type::Slice(slice) => |
|
| 5127 | - | return try lowerSliceEq(self, slice.item, slice.mutable, a, b, offset), |
|
| 5128 | - | case resolver::Type::Optional(inner) => { |
|
| 5129 | - | if let case resolver::Type::Slice(slice) = *inner { |
|
| 5130 | - | // Optional slices use null pointer optimization. |
|
| 5131 | - | return try lowerSliceEq(self, slice.item, slice.mutable, a, b, offset); |
|
| 5132 | - | } |
|
| 5133 | - | return try lowerOptionalEq(self, *inner, a, b, offset); |
|
| 5134 | - | } |
|
| 5135 | - | case resolver::Type::Array(arr) => |
|
| 5136 | - | return try lowerArrayEq(self, arr, a, b, offset), |
|
| 5123 | + | case resolver::Type::Core( |
|
| 5124 | + | resolver::CoreType::Optional { payload } |
|
| 5125 | + | ) => return try lowerOptionalEq(self, *payload, a, b, offset), |
|
| 5126 | + | case resolver::Type::Core(resolver::CoreType::Array { item, length }) => |
|
| 5127 | + | return try lowerArrayEq(self, item, length, a, b, offset), |
|
| 5137 | 5128 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => |
|
| 5138 | 5129 | return try lowerRecordEq(self, recInfo, a, b, offset), |
|
| 5139 | 5130 | case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) => |
|
| 5140 | 5131 | return try lowerUnionEq(self, unionInfo, a, b, offset), |
|
| 5141 | 5132 | else => { |
| 5237 | 5228 | /// Lower an array literal expression like `[1, 2, 3]`. |
|
| 5238 | 5229 | fn lowerArrayLit(self: *mut FnLowerer, node: *ast::Node, elements: *mut [*ast::Node]) -> il::Val |
|
| 5239 | 5230 | throws (LowerError) |
|
| 5240 | 5231 | { |
|
| 5241 | 5232 | let typ = try typeOf(self, node); |
|
| 5242 | - | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 5243 | - | throw LowerError::ExpectedArray; |
|
| 5244 | - | }; |
|
| 5245 | - | let elemTy = *arrInfo.item; |
|
| 5233 | + | let case resolver::Type::Core(resolver::CoreType::Array { item, .. }) = typ |
|
| 5234 | + | else throw LowerError::ExpectedArray; |
|
| 5235 | + | let elemTy = *item; |
|
| 5246 | 5236 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 5247 | 5237 | let dst = try emitReserve(self, typ); |
|
| 5248 | 5238 | ||
| 5249 | 5239 | for elemNode, i in elements { |
|
| 5250 | 5240 | let elemVal = try lowerExpr(self, elemNode); |
| 5260 | 5250 | // TODO: Beyond a certain length, lower this to a loop. |
|
| 5261 | 5251 | fn lowerArrayRepeatLit(self: *mut FnLowerer, node: *ast::Node, repeat: ast::ArrayRepeatLit) -> il::Val |
|
| 5262 | 5252 | throws (LowerError) |
|
| 5263 | 5253 | { |
|
| 5264 | 5254 | let typ = try typeOf(self, node); |
|
| 5265 | - | let case resolver::Type::Array(arrInfo) = typ else { |
|
| 5266 | - | throw LowerError::ExpectedArray; |
|
| 5267 | - | }; |
|
| 5268 | - | let elemTy = *arrInfo.item; |
|
| 5269 | - | let length = resolver::concreteArrayLength(arrInfo.length) |
|
| 5255 | + | let case resolver::Type::Core(resolver::CoreType::Array { item, length: lengthArgument }) = typ |
|
| 5256 | + | else throw LowerError::ExpectedArray; |
|
| 5257 | + | let elemTy = *item; |
|
| 5258 | + | let length = resolver::concreteArrayLength(lengthArgument) |
|
| 5270 | 5259 | else throw LowerError::MissingMetadata; |
|
| 5271 | 5260 | let elemLayout = resolver::getTypeLayout(elemTy); |
|
| 5272 | 5261 | let dst = try emitReserve(self, typ); |
|
| 5273 | 5262 | ||
| 5274 | 5263 | // Evaluate the repeated item once. |
| 5492 | 5481 | self: *mut FnLowerer, |
|
| 5493 | 5482 | sliceNode: *ast::Node, |
|
| 5494 | 5483 | arrayNode: *ast::Node |
|
| 5495 | 5484 | ) -> il::Val throws (LowerError) { |
|
| 5496 | 5485 | let sliceTy = try typeOf(self, sliceNode); |
|
| 5497 | - | let case resolver::Type::Slice(slice) = sliceTy else { |
|
| 5498 | - | throw LowerError::UnexpectedType(&sliceTy); |
|
| 5499 | - | }; |
|
| 5486 | + | let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, properties: slice, target: item }) = sliceTy |
|
| 5487 | + | else throw LowerError::UnexpectedType(&sliceTy); |
|
| 5500 | 5488 | let arrayTy = try typeOf(self, arrayNode); |
|
| 5501 | - | let case resolver::Type::Array(arrayInfo) = arrayTy else { |
|
| 5502 | - | throw LowerError::ExpectedArray; |
|
| 5503 | - | }; |
|
| 5504 | - | let length = resolver::concreteArrayLength(arrayInfo.length) |
|
| 5489 | + | let case resolver::Type::Core(resolver::CoreType::Array { length: lengthArgument, .. }) = arrayTy |
|
| 5490 | + | else throw LowerError::ExpectedArray; |
|
| 5491 | + | let length = resolver::concreteArrayLength(lengthArgument) |
|
| 5505 | 5492 | else throw LowerError::MissingMetadata; |
|
| 5506 | 5493 | if length == 0 { |
|
| 5507 | 5494 | return try buildSliceValue( |
|
| 5508 | - | self, slice.item, slice.mutable, il::Val::Imm(0), il::Val::Imm(0) |
|
| 5495 | + | self, |
|
| 5496 | + | item, |
|
| 5497 | + | slice.mutable, |
|
| 5498 | + | il::Val::Imm(0), |
|
| 5499 | + | il::Val::Imm(0), |
|
| 5509 | 5500 | ); |
|
| 5510 | 5501 | } |
|
| 5511 | 5502 | if resolver::isConstExpr(self.low.resolver, arrayNode) { |
|
| 5512 | 5503 | let mut b = dataBuilder(self.low.allocator); |
|
| 5513 | 5504 | match arrayNode.value { |
| 5516 | 5507 | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 5517 | 5508 | try lowerConstArrayRepeatInto(self.low, repeat, arrayTy, self.fnName, &mut b), |
|
| 5518 | 5509 | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5519 | 5510 | } |
|
| 5520 | 5511 | let result = dataBuilderFinish(&b); |
|
| 5521 | - | let alignment = resolver::getTypeLayout(*slice.item).alignment; |
|
| 5512 | + | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5522 | 5513 | return try lowerConstDataAsSlice( |
|
| 5523 | - | self, result.values, alignment, not slice.mutable, |
|
| 5524 | - | slice.item, slice.mutable, length |
|
| 5514 | + | self, |
|
| 5515 | + | result.values, |
|
| 5516 | + | alignment, |
|
| 5517 | + | not slice.mutable, |
|
| 5518 | + | item, |
|
| 5519 | + | slice.mutable, |
|
| 5520 | + | length, |
|
| 5525 | 5521 | ); |
|
| 5526 | 5522 | } |
|
| 5527 | 5523 | let data = try lowerExpr(self, arrayNode); |
|
| 5528 | 5524 | let count = il::Val::Imm(length as i64); |
|
| 5529 | - | return try buildSliceValue(self, slice.item, slice.mutable, data, count); |
|
| 5525 | + | return try buildSliceValue(self, item, slice.mutable, data, count); |
|
| 5530 | 5526 | } |
|
| 5531 | 5527 | ||
| 5532 | 5528 | /// Lower the common element pointer computation for subscript operations. |
|
| 5533 | 5529 | /// Handles both arrays and slices by resolving the container type, extracting |
|
| 5534 | 5530 | /// the data pointer (for slices), and emitting an [`il::Instr::Elem`] to compute |
| 5544 | 5540 | ||
| 5545 | 5541 | let mut dataReg = baseReg; |
|
| 5546 | 5542 | let mut elemType: resolver::Type = undefined; |
|
| 5547 | 5543 | ||
| 5548 | 5544 | match subjectTy { |
|
| 5549 | - | case resolver::Type::Slice(slice) => { |
|
| 5550 | - | set elemType = *slice.item; |
|
| 5545 | + | case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, target, .. }) => { |
|
| 5546 | + | set elemType = *target; |
|
| 5551 | 5547 | let sliceLen = loadSliceLen(self, baseReg); |
|
| 5552 | 5548 | // Runtime safety check: index must be strictly less than slice length. |
|
| 5553 | - | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, sliceLen); |
|
| 5549 | + | try emitTrapUnlessCmp( |
|
| 5550 | + | self, il::CmpOp::Ult, il::Type::W32, indexVal, sliceLen |
|
| 5551 | + | ); |
|
| 5554 | 5552 | ||
| 5555 | 5553 | set dataReg = loadSlicePtr(self, baseReg); |
|
| 5556 | 5554 | } |
|
| 5557 | - | case resolver::Type::Array(arrInfo) => { |
|
| 5558 | - | set elemType = *arrInfo.item; |
|
| 5555 | + | case resolver::Type::Core(resolver::CoreType::Array { item, length: lengthArgument }) => { |
|
| 5556 | + | set elemType = *item; |
|
| 5559 | 5557 | // Runtime safety check: index must be strictly less than array length. |
|
| 5560 | 5558 | // Skip when the index is a compile-time constant, since we check |
|
| 5561 | 5559 | // that in the resolver. |
|
| 5562 | 5560 | if not resolver::isConstExpr(self.low.resolver, index) { |
|
| 5563 | - | let length = resolver::concreteArrayLength(arrInfo.length) |
|
| 5561 | + | let length = resolver::concreteArrayLength(lengthArgument) |
|
| 5564 | 5562 | else throw LowerError::MissingMetadata; |
|
| 5565 | 5563 | let arrLen = il::Val::Imm(length as i64); |
|
| 5566 | - | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen); |
|
| 5564 | + | try emitTrapUnlessCmp( |
|
| 5565 | + | self, il::CmpOp::Ult, il::Type::W32, indexVal, arrLen |
|
| 5566 | + | ); |
|
| 5567 | 5567 | } |
|
| 5568 | 5568 | } |
|
| 5569 | 5569 | else => throw LowerError::ExpectedSliceOrArray, |
|
| 5570 | 5570 | } |
|
| 5571 | 5571 | let elemLayout = resolver::getTypeLayout(elemType); |
| 5837 | 5837 | let r = try resolveSliceRangePtr(self, container, range, info); |
|
| 5838 | 5838 | let itemType = try specializeType(self, *info.itemType); |
|
| 5839 | 5839 | let elemSize = resolver::getTypeLayout(itemType).size; |
|
| 5840 | 5840 | let rhsTy = try typeOf(self, rhs); |
|
| 5841 | 5841 | ||
| 5842 | - | if let case resolver::Type::Slice(_) = rhsTy { |
|
| 5842 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, .. }) = rhsTy { |
|
| 5843 | 5843 | // Copy from source slice. |
|
| 5844 | 5844 | let srcReg = emitValToReg(self, try lowerExpr(self, rhs)); |
|
| 5845 | 5845 | let srcData = loadSlicePtr(self, srcReg); |
|
| 5846 | 5846 | let srcLen = loadSliceLen(self, srcReg); |
|
| 5847 | 5847 |
| 6595 | 6595 | /// String literals are stored as global data and the result is a slice |
|
| 6596 | 6596 | /// pointing to the data with the appropriate length. |
|
| 6597 | 6597 | fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6598 | 6598 | // Get the slice type from the node. |
|
| 6599 | 6599 | let sliceTy = try typeOf(self, node); |
|
| 6600 | - | let case resolver::Type::Slice(slice) = sliceTy |
|
| 6600 | + | let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, properties: slice, target: item }) = sliceTy |
|
| 6601 | 6601 | else throw LowerError::ExpectedSliceOrArray; |
|
| 6602 | 6602 | // Build the string data value. |
|
| 6603 | 6603 | let ptr = try! alloc::alloc( |
|
| 6604 | 6604 | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6605 | 6605 | ) as *mut il::DataValue; |
|
| 6606 | 6606 | ||
| 6607 | 6607 | set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 }; |
|
| 6608 | 6608 | ||
| 6609 | 6609 | return try lowerConstDataAsSlice( |
|
| 6610 | - | self, @sliceOf(ptr, 1), 1, true, slice.item, slice.mutable, s.len |
|
| 6610 | + | self, |
|
| 6611 | + | @sliceOf(ptr, 1), |
|
| 6612 | + | 1, |
|
| 6613 | + | true, |
|
| 6614 | + | item, |
|
| 6615 | + | slice.mutable, |
|
| 6616 | + | s.len, |
|
| 6611 | 6617 | ); |
|
| 6612 | 6618 | } |
|
| 6613 | 6619 | ||
| 6614 | 6620 | /// Lower a builtin call expression. |
|
| 6615 | 6621 | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
| 6638 | 6644 | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6639 | 6645 | if args.len <> 2 { |
|
| 6640 | 6646 | throw LowerError::InvalidArgCount; |
|
| 6641 | 6647 | } |
|
| 6642 | 6648 | let sliceTy = try typeOf(self, node); |
|
| 6643 | - | let case resolver::Type::Slice(slice) = sliceTy |
|
| 6649 | + | let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, properties: slice, target: item }) = sliceTy |
|
| 6644 | 6650 | else throw LowerError::ExpectedSliceOrArray; |
|
| 6645 | 6651 | let ptrVal = try lowerExpr(self, args[0]); |
|
| 6646 | 6652 | let lenVal = try lowerExpr(self, args[1]); |
|
| 6647 | - | return try buildSliceValue(self, slice.item, slice.mutable, ptrVal, lenVal); |
|
| 6653 | + | return try buildSliceValue( |
|
| 6654 | + | self, item, slice.mutable, ptrVal, lenVal |
|
| 6655 | + | ); |
|
| 6648 | 6656 | } |
|
| 6649 | 6657 | ||
| 6650 | - | /// Lower `@relocate(destination, source)` as an overlap-safe byte move. |
|
| 6658 | + | /// Lower `@relocate(destination, source)` as an overlap-safe typed move. |
|
| 6651 | 6659 | fn lowerRelocate( |
|
| 6652 | 6660 | self: *mut FnLowerer, |
|
| 6653 | 6661 | args: *mut [*ast::Node], |
|
| 6654 | 6662 | ) -> il::Val throws (LowerError) { |
|
| 6655 | 6663 | if args.len <> 2 { |
|
| 6656 | 6664 | throw LowerError::InvalidArgCount; |
|
| 6657 | 6665 | } |
|
| 6666 | + | let destinationType = try typeOf(self, args[0]); |
|
| 6667 | + | let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Slice, target: item, .. }) = destinationType |
|
| 6668 | + | else throw LowerError::ExpectedSliceOrArray; |
|
| 6658 | 6669 | ||
| 6659 | 6670 | let destinationSlice = emitValToReg(self, try lowerExpr(self, args[0])); |
|
| 6660 | 6671 | let destination = loadSlicePtr(self, destinationSlice); |
|
| 6661 | 6672 | let destinationLen = loadSliceLen(self, destinationSlice); |
|
| 6662 | 6673 | let sourceSlice = emitValToReg(self, try lowerExpr(self, args[1])); |
|
| 6663 | 6674 | let source = loadSlicePtr(self, sourceSlice); |
|
| 6664 | 6675 | let sourceLen = loadSliceLen(self, sourceSlice); |
|
| 6665 | 6676 | ||
| 6666 | - | // The destination must hold every source byte. |
|
| 6677 | + | // The destination must hold every source element. |
|
| 6667 | 6678 | try emitTrapIfLt(self, il::Type::W32, destinationLen, sourceLen); |
|
| 6679 | + | let itemSize = resolver::getTypeLayout(*item).size; |
|
| 6680 | + | let mut byteLen = sourceLen; |
|
| 6681 | + | if itemSize <> 1 { |
|
| 6682 | + | set byteLen = emitTypedBinOp(self, il::BinOp::Mul, il::Type::W32, sourceLen, il::Val::Imm(itemSize as i64)); |
|
| 6683 | + | } |
|
| 6668 | 6684 | ||
| 6669 | 6685 | let forwardBlock = try createBlock(self, "relocate.forward"); |
|
| 6670 | 6686 | let backwardBlock = try createBlock(self, "relocate.backward"); |
|
| 6671 | 6687 | let doneBlock = try createBlock(self, "relocate.done"); |
|
| 6672 | 6688 | try emitBrCmp( |
| 6683 | 6699 | try switchToAndSeal(self, forwardBlock); |
|
| 6684 | 6700 | try emitByteCopyLoop( |
|
| 6685 | 6701 | self, |
|
| 6686 | 6702 | destination, |
|
| 6687 | 6703 | source, |
|
| 6688 | - | sourceLen, |
|
| 6704 | + | byteLen, |
|
| 6689 | 6705 | "relocate.forward", |
|
| 6690 | 6706 | ); |
|
| 6691 | 6707 | try emitJmp(self, doneBlock); |
|
| 6692 | 6708 | ||
| 6693 | 6709 | // Moving toward a higher address must copy from the end for overlap safety. |
| 6698 | 6714 | "relocate.backward", |
|
| 6699 | 6715 | il::Param { value: remainingReg, type: il::Type::W32 }, |
|
| 6700 | 6716 | ); |
|
| 6701 | 6717 | let backwardBody = try createBlock(self, "relocate.backward"); |
|
| 6702 | 6718 | let backwardDone = try createBlock(self, "relocate.backward"); |
|
| 6703 | - | try emitJmpWithArg(self, backwardHeader, sourceLen); |
|
| 6719 | + | try emitJmpWithArg(self, backwardHeader, byteLen); |
|
| 6704 | 6720 | ||
| 6705 | 6721 | switchToBlock(self, backwardHeader); |
|
| 6706 | 6722 | try emitBrCmp( |
|
| 6707 | 6723 | self, |
|
| 6708 | 6724 | il::CmpOp::Ult, |
| 7209 | 7225 | /// If the parent is already a pointer type, the value is used directly. |
|
| 7210 | 7226 | /// If the parent is a value type (eg. a local record), its address is taken. |
|
| 7211 | 7227 | fn lowerReceiver(self: *mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val |
|
| 7212 | 7228 | throws (LowerError) |
|
| 7213 | 7229 | { |
|
| 7214 | - | if let case resolver::Type::Pointer(_) = parentTy { |
|
| 7230 | + | if let case resolver::Type::Core(resolver::CoreType::Pointer { shape: resolver::PointerShape::Thin, .. }) = parentTy { |
|
| 7215 | 7231 | // Already a pointer: lower and use directly. |
|
| 7216 | 7232 | return try lowerExpr(self, parent); |
|
| 7217 | 7233 | } |
|
| 7218 | 7234 | // Value type: take its address by lowering it and returning the slot pointer. |
|
| 7219 | 7235 | // Aggregate types are already lowered as pointers to stack slots. |
| 7675 | 7691 | case ast::NodeValue::Char(c) => { |
|
| 7676 | 7692 | set val = il::Val::Imm(c as i64); |
|
| 7677 | 7693 | } |
|
| 7678 | 7694 | case ast::NodeValue::Nil => { |
|
| 7679 | 7695 | let typ = try typeOf(self, node); |
|
| 7680 | - | if let case resolver::Type::Optional(_) = typ { |
|
| 7696 | + | if resolver::isOptionalType(typ) { |
|
| 7681 | 7697 | set val = try buildNilOptional(self, typ); |
|
| 7682 | 7698 | } else if let case resolver::Type::Nil = typ { |
|
| 7683 | 7699 | // Standalone `nil` without a concrete optional type. We can't |
|
| 7684 | 7700 | // generate a proper value representation. |
|
| 7685 | 7701 | throw LowerError::MissingType(node); |
| 7729 | 7745 | else => set val = try lowerFieldAccess(self, access), |
|
| 7730 | 7746 | } |
|
| 7731 | 7747 | } else { |
|
| 7732 | 7748 | let parentTy = try typeOf(self, access.parent); |
|
| 7733 | 7749 | let mut handled = false; |
|
| 7734 | - | if let case resolver::Type::Array(array) = parentTy { |
|
| 7750 | + | if let case resolver::Type::Core(resolver::CoreType::Array { length: lengthArgument, .. }) = parentTy { |
|
| 7735 | 7751 | if let case ast::NodeValue::Ident(name) = access.child.value; |
|
| 7736 | 7752 | mem::eq(name, "len") |
|
| 7737 | 7753 | { |
|
| 7738 | - | let length = resolver::concreteArrayLength(array.length) |
|
| 7754 | + | let length = resolver::concreteArrayLength(lengthArgument) |
|
| 7739 | 7755 | else throw LowerError::MissingMetadata; |
|
| 7740 | 7756 | set val = il::Val::Imm(length as i64); |
|
| 7741 | 7757 | set handled = true; |
|
| 7742 | 7758 | } |
|
| 7743 | 7759 | } |
| 7833 | 7849 | resolver::Type::U16 => return il::Type::W16, |
|
| 7834 | 7850 | case resolver::Type::I32, |
|
| 7835 | 7851 | resolver::Type::U32 => return il::Type::W32, |
|
| 7836 | 7852 | case resolver::Type::I64, |
|
| 7837 | 7853 | resolver::Type::U64, |
|
| 7838 | - | resolver::Type::Pointer(_), |
|
| 7839 | - | resolver::Type::Slice(_), |
|
| 7854 | + | resolver::Type::Core(_), |
|
| 7840 | 7855 | resolver::Type::TraitObject(_), |
|
| 7841 | - | resolver::Type::Array(_), |
|
| 7842 | - | resolver::Type::Optional(_), |
|
| 7843 | 7856 | resolver::Type::Fn(_) => return il::Type::W64, |
|
| 7844 | 7857 | case resolver::Type::Nominal(_) => { |
|
| 7845 | 7858 | if resolver::isVoidUnion(typ) { |
|
| 7846 | 7859 | return il::Type::W8; |
|
| 7847 | 7860 | } |
lib/std/lang/resolver.rad
+706 -744
| 180 | 180 | name: *[u8], |
|
| 181 | 181 | valueType: Type, |
|
| 182 | 182 | symbol: *mut Symbol, |
|
| 183 | 183 | } |
|
| 184 | 184 | ||
| 185 | - | /// Array type payload. |
|
| 186 | - | export record ArrayType { |
|
| 187 | - | /// Element type. |
|
| 188 | - | item: *Type, |
|
| 189 | - | /// Typed constant descriptor for the array length. |
|
| 190 | - | length: *Type, |
|
| 191 | - | } |
|
| 192 | - | ||
| 193 | 185 | /// Anonymous record whose field layout depends on rigid parameters. |
|
| 194 | 186 | export record GenericRecordType { |
|
| 195 | 187 | /// Fields in declaration order. |
|
| 196 | 188 | fields: *[RecordField], |
|
| 197 | 189 | /// Whether the fields have labels. |
| 470 | 462 | args: *[*Type], |
|
| 471 | 463 | /// Source node for the application. |
|
| 472 | 464 | site: *ast::Node, |
|
| 473 | 465 | } |
|
| 474 | 466 | ||
| 475 | - | /// Pointer-like address payload. |
|
| 476 | - | export record PointerType { |
|
| 467 | + | /// Shared ownership, safety, and mutability metadata for pointer-like values. |
|
| 468 | + | export record PointerProperties { |
|
| 477 | 469 | /// Ownership and safety class. |
|
| 478 | 470 | class: types::PointerClass, |
|
| 479 | - | /// Pointer target type. |
|
| 480 | - | target: *Type, |
|
| 481 | - | /// Whether the pointer is mutable. |
|
| 471 | + | /// Whether the referenced value may be mutated. |
|
| 482 | 472 | mutable: bool, |
|
| 483 | 473 | } |
|
| 484 | 474 | ||
| 485 | - | /// Pointer-like slice payload. |
|
| 486 | - | export record SliceType { |
|
| 487 | - | /// Ownership and safety class. |
|
| 488 | - | class: types::PointerClass, |
|
| 489 | - | /// Slice element type. |
|
| 490 | - | item: *Type, |
|
| 491 | - | /// Whether the slice is mutable. |
|
| 492 | - | mutable: bool, |
|
| 475 | + | /// Storage shape of an intrinsic pointer-like value. |
|
| 476 | + | export union PointerShape { |
|
| 477 | + | /// One-word pointer to a single value. |
|
| 478 | + | Thin, |
|
| 479 | + | /// Two-word pointer and length pair. |
|
| 480 | + | Slice, |
|
| 481 | + | } |
|
| 482 | + | ||
| 483 | + | /// Semantic application of an intrinsic core type. |
|
| 484 | + | export union CoreType { |
|
| 485 | + | /// Pointer or slice application. |
|
| 486 | + | Pointer { |
|
| 487 | + | /// Storage shape and operation set. |
|
| 488 | + | shape: PointerShape, |
|
| 489 | + | /// Ownership, safety, and mutability metadata. |
|
| 490 | + | properties: PointerProperties, |
|
| 491 | + | /// Referenced value or slice element type. |
|
| 492 | + | target: *Type, |
|
| 493 | + | }, |
|
| 494 | + | /// Repeated element aggregate. |
|
| 495 | + | Array { |
|
| 496 | + | /// Element type. |
|
| 497 | + | item: *Type, |
|
| 498 | + | /// Typed constant descriptor for the array length. |
|
| 499 | + | length: *Type, |
|
| 500 | + | }, |
|
| 501 | + | /// Union-like value-or-nil application. |
|
| 502 | + | Optional { |
|
| 503 | + | /// Payload type. |
|
| 504 | + | payload: *Type, |
|
| 505 | + | }, |
|
| 493 | 506 | } |
|
| 494 | 507 | ||
| 495 | 508 | /// Erased pointer-like type payload. |
|
| 496 | 509 | export record TraitObjectType { |
|
| 497 | - | /// Ownership and safety class. |
|
| 498 | - | class: types::PointerClass, |
|
| 510 | + | /// Ownership, safety, and mutability metadata. |
|
| 511 | + | properties: PointerProperties, |
|
| 499 | 512 | /// Trait definition. |
|
| 500 | 513 | traitInfo: *TraitType, |
|
| 501 | - | /// Whether the pointer is mutable. |
|
| 502 | - | mutable: bool, |
|
| 503 | 514 | } |
|
| 504 | 515 | ||
| 505 | 516 | /// Describes a type computed during semantic analysis. |
|
| 506 | 517 | export union Type { |
|
| 507 | 518 | /// A type that couldn't be decided. |
| 515 | 526 | /// Range types, eg. `start..end`. |
|
| 516 | 527 | Range { |
|
| 517 | 528 | start: ?*Type, |
|
| 518 | 529 | end: ?*Type, |
|
| 519 | 530 | }, |
|
| 520 | - | /// Pointer-like address. |
|
| 521 | - | Pointer(PointerType), |
|
| 522 | - | /// Pointer-like slice. |
|
| 523 | - | Slice(SliceType), |
|
| 524 | - | /// Eg. `[i32; 32]`. |
|
| 525 | - | Array(ArrayType), |
|
| 531 | + | /// Intrinsic pointer, slice, array, or optional application. |
|
| 532 | + | Core(CoreType), |
|
| 526 | 533 | /// Rigid integer constant parameter within a generic declaration. |
|
| 527 | 534 | ConstParameter(*GenericParamType), |
|
| 528 | 535 | /// Canonical typed integer generic argument. |
|
| 529 | 536 | ConstArgument { |
|
| 530 | 537 | type: *Type, |
| 533 | 540 | /// Symbolic integer expression awaiting constant-parameter substitution. |
|
| 534 | 541 | GenericConstExpr { |
|
| 535 | 542 | type: *Type, |
|
| 536 | 543 | expr: *ast::Node, |
|
| 537 | 544 | }, |
|
| 538 | - | /// Eg. `?T`. |
|
| 539 | - | Optional(*Type), |
|
| 540 | 545 | /// Eg. `fn id(i32) -> i32`. |
|
| 541 | 546 | Fn(*FnType), |
|
| 542 | 547 | /// Named, ie. user-defined types, includes union variants. |
|
| 543 | 548 | Nominal(*NominalType), |
|
| 544 | 549 | /// Rigid type parameter within a generic declaration. |
| 549 | 554 | GenericDataApply(*GenericDataApplyType), |
|
| 550 | 555 | /// An erased pointer-like type with a v-table. |
|
| 551 | 556 | TraitObject(TraitObjectType), |
|
| 552 | 557 | } |
|
| 553 | 558 | ||
| 559 | + | /// Construct a fixed-size intrinsic pointer application. |
|
| 560 | + | export fn pointerType(class: types::PointerClass, target: *Type, mutable: bool) -> Type { |
|
| 561 | + | return Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties: PointerProperties { class, mutable }, target }); |
|
| 562 | + | } |
|
| 563 | + | ||
| 564 | + | /// Construct a direct intrinsic slice application. |
|
| 565 | + | export fn sliceType(class: types::PointerClass, item: *Type, mutable: bool) -> Type { |
|
| 566 | + | return Type::Core(CoreType::Pointer { shape: PointerShape::Slice, properties: PointerProperties { class, mutable }, target: item }); |
|
| 567 | + | } |
|
| 568 | + | ||
| 569 | + | /// Construct an intrinsic repeated aggregate application. |
|
| 570 | + | export fn arrayType(item: *Type, length: *Type) -> Type { |
|
| 571 | + | return Type::Core(CoreType::Array { item, length }); |
|
| 572 | + | } |
|
| 573 | + | ||
| 574 | + | /// Construct an intrinsic value-or-nil application. |
|
| 575 | + | export fn optionalType(payload: *Type) -> Type { |
|
| 576 | + | return Type::Core(CoreType::Optional { payload }); |
|
| 577 | + | } |
|
| 578 | + | ||
| 579 | + | /// Return the value-type argument of a core application. |
|
| 580 | + | fn coreValueArgument(core: CoreType) -> *Type { |
|
| 581 | + | match core { |
|
| 582 | + | case CoreType::Pointer { target, .. } => return target, |
|
| 583 | + | case CoreType::Array { item, .. } => return item, |
|
| 584 | + | case CoreType::Optional { payload } => return payload, |
|
| 585 | + | } |
|
| 586 | + | } |
|
| 587 | + | ||
| 588 | + | /// Return the constant argument of a core application, when present. |
|
| 589 | + | fn coreConstantArgument(core: CoreType) -> ?*Type { |
|
| 590 | + | if let case CoreType::Array { length, .. } = core { |
|
| 591 | + | return length; |
|
| 592 | + | } |
|
| 593 | + | return nil; |
|
| 594 | + | } |
|
| 595 | + | ||
| 596 | + | /// Reconstruct a core application with transformed generic arguments. |
|
| 597 | + | fn coreWithArguments(core: CoreType, valueArgument: *Type, constantArgument: ?*Type) -> CoreType { |
|
| 598 | + | match core { |
|
| 599 | + | case CoreType::Pointer { shape, properties, .. } => |
|
| 600 | + | return CoreType::Pointer { shape, properties, target: valueArgument }, |
|
| 601 | + | case CoreType::Array { .. } => { |
|
| 602 | + | let length = constantArgument |
|
| 603 | + | else panic "coreWithArguments: missing array length"; |
|
| 604 | + | return CoreType::Array { item: valueArgument, length }; |
|
| 605 | + | } |
|
| 606 | + | case CoreType::Optional { .. } => |
|
| 607 | + | return CoreType::Optional { payload: valueArgument }, |
|
| 608 | + | } |
|
| 609 | + | } |
|
| 610 | + | ||
| 554 | 611 | /// Return a concrete `u32` array length from its typed descriptor. |
|
| 555 | 612 | export fn concreteArrayLength(length: *Type) -> ?u32 { |
|
| 556 | 613 | let case Type::ConstArgument { type, value } = *length else return nil; |
|
| 557 | 614 | if *type <> Type::U32 or value.negative |
|
| 558 | 615 | or value.magnitude > parser::U32_MAX as u64 |
| 1137 | 1194 | loopDepth: u32, |
|
| 1138 | 1195 | } |
|
| 1139 | 1196 | ||
| 1140 | 1197 | /// Unwrap a pointer type for pattern matching. |
|
| 1141 | 1198 | export fn unwrapMatchSubject(ty: Type) -> MatchSubject { |
|
| 1142 | - | if let case Type::Pointer(pointer) = ty { |
|
| 1143 | - | let by = MatchBy::MutRef if pointer.mutable else MatchBy::Ref; |
|
| 1144 | - | return MatchSubject { effectiveTy: *pointer.target, by }; |
|
| 1199 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = ty { |
|
| 1200 | + | let by = MatchBy::MutRef if properties.mutable else MatchBy::Ref; |
|
| 1201 | + | return MatchSubject { effectiveTy: *target, by }; |
|
| 1145 | 1202 | } |
|
| 1146 | 1203 | return MatchSubject { effectiveTy: ty, by: MatchBy::Value }; |
|
| 1147 | 1204 | } |
|
| 1148 | 1205 | ||
| 1149 | 1206 | /// Global resolver state. |
| 1289 | 1346 | } |
|
| 1290 | 1347 | ||
| 1291 | 1348 | /// Return whether a type contains a rigid generic parameter. |
|
| 1292 | 1349 | export fn containsGenericParameter(ty: Type) -> bool { |
|
| 1293 | 1350 | match ty { |
|
| 1294 | - | case Type::Pointer(pointer) => |
|
| 1295 | - | return containsGenericParameter(*pointer.target), |
|
| 1296 | - | case Type::Slice(slice) => |
|
| 1297 | - | return containsGenericParameter(*slice.item), |
|
| 1351 | + | case Type::Core(core) => { |
|
| 1352 | + | if containsGenericParameter(*coreValueArgument(core)) { |
|
| 1353 | + | return true; |
|
| 1354 | + | } |
|
| 1355 | + | if let argument = coreConstantArgument(core) { |
|
| 1356 | + | return containsGenericParameter(*argument); |
|
| 1357 | + | } |
|
| 1358 | + | return false; |
|
| 1359 | + | } |
|
| 1298 | 1360 | case Type::Parameter(_), Type::ConstParameter(_), |
|
| 1299 | 1361 | Type::GenericConstExpr { .. } => return true, |
|
| 1300 | - | case Type::Array(array) => |
|
| 1301 | - | return containsGenericParameter(*array.item) |
|
| 1302 | - | or containsGenericParameter(*array.length), |
|
| 1303 | - | case Type::Optional(inner) => return containsGenericParameter(*inner), |
|
| 1304 | 1362 | // Symbolic anonymous records require materialization even when their |
|
| 1305 | 1363 | // own fields happen not to mention a rigid parameter. |
|
| 1306 | 1364 | case Type::GenericRecord(_) => return true, |
|
| 1307 | 1365 | case Type::GenericDataApply(_) => return true, |
|
| 1308 | 1366 | case Type::Fn(info) => { |
| 1336 | 1394 | } |
|
| 1337 | 1395 | else => return false, |
|
| 1338 | 1396 | } |
|
| 1339 | 1397 | } |
|
| 1340 | 1398 | ||
| 1341 | - | /// Return whether a by-value type reaches an in-progress nominal placeholder. |
|
| 1342 | - | fn hasUnresolvedNominalLayout(ty: Type) -> bool { |
|
| 1399 | + | /// Condition that prevents ordinary by-value layout. |
|
| 1400 | + | union LayoutObstacle { |
|
| 1401 | + | /// A path reaches an in-progress nominal declaration. |
|
| 1402 | + | UnresolvedNominal, |
|
| 1403 | + | /// A path reaches an opaque value. |
|
| 1404 | + | Opaque, |
|
| 1405 | + | /// Layout depends on unresolved generic arguments. |
|
| 1406 | + | Symbolic, |
|
| 1407 | + | } |
|
| 1408 | + | ||
| 1409 | + | /// Return whether a type reaches one layout obstacle by value. |
|
| 1410 | + | fn hasLayoutObstacle(ty: Type, obstacle: LayoutObstacle) -> bool { |
|
| 1343 | 1411 | match ty { |
|
| 1344 | - | case Type::Pointer(_), Type::Slice(_) => return false, |
|
| 1345 | - | case Type::Array(array) => return hasUnresolvedNominalLayout(*array.item), |
|
| 1346 | - | case Type::Optional(inner) => return hasUnresolvedNominalLayout(*inner), |
|
| 1412 | + | // Pointer and slice layouts do not depend on their targets. |
|
| 1413 | + | case Type::Core(CoreType::Pointer { .. }), |
|
| 1414 | + | Type::TraitObject(_), |
|
| 1415 | + | Type::Fn(_) => return false, |
|
| 1416 | + | case Type::Core(core) => { |
|
| 1417 | + | if hasLayoutObstacle(*coreValueArgument(core), obstacle) { |
|
| 1418 | + | return true; |
|
| 1419 | + | } |
|
| 1420 | + | if obstacle == LayoutObstacle::Symbolic { |
|
| 1421 | + | if let argument = coreConstantArgument(core) { |
|
| 1422 | + | return containsGenericParameter(*argument); |
|
| 1423 | + | } |
|
| 1424 | + | } |
|
| 1425 | + | return false; |
|
| 1426 | + | } |
|
| 1347 | 1427 | case Type::Nominal(info) => { |
|
| 1428 | + | if obstacle <> LayoutObstacle::UnresolvedNominal { |
|
| 1429 | + | return false; |
|
| 1430 | + | } |
|
| 1348 | 1431 | if let case NominalType::Placeholder(_) = *info { |
|
| 1349 | 1432 | return true; |
|
| 1350 | 1433 | } |
|
| 1351 | 1434 | return false; |
|
| 1352 | 1435 | } |
|
| 1353 | 1436 | case Type::GenericRecord(rec) => { |
|
| 1437 | + | if obstacle == LayoutObstacle::Symbolic { |
|
| 1438 | + | return true; |
|
| 1439 | + | } |
|
| 1354 | 1440 | for field in rec.fields { |
|
| 1355 | - | if hasUnresolvedNominalLayout(field.fieldType) { |
|
| 1441 | + | if hasLayoutObstacle(field.fieldType, obstacle) { |
|
| 1356 | 1442 | return true; |
|
| 1357 | 1443 | } |
|
| 1358 | 1444 | } |
|
| 1359 | 1445 | return false; |
|
| 1360 | 1446 | } |
|
| 1447 | + | case Type::Opaque => return obstacle == LayoutObstacle::Opaque, |
|
| 1448 | + | case Type::Parameter(_), Type::ConstParameter(_), |
|
| 1449 | + | Type::GenericConstExpr { .. }, Type::GenericDataApply(_) => |
|
| 1450 | + | return obstacle == LayoutObstacle::Symbolic, |
|
| 1361 | 1451 | else => return false, |
|
| 1362 | 1452 | } |
|
| 1363 | 1453 | } |
|
| 1364 | 1454 | ||
| 1365 | 1455 | /// Materialize concrete generic data applications within a type while |
| 1368 | 1458 | self: *mut Resolver, |
|
| 1369 | 1459 | ty: Type, |
|
| 1370 | 1460 | site: *ast::Node, |
|
| 1371 | 1461 | ) -> Type throws (ResolveError) { |
|
| 1372 | 1462 | match ty { |
|
| 1373 | - | case Type::Pointer(pointer) => { |
|
| 1374 | - | let inner = try materializeConcreteGenericData(self, *pointer.target, site); |
|
| 1375 | - | return Type::Pointer(PointerType { |
|
| 1376 | - | class: pointer.class, |
|
| 1377 | - | target: allocType(self, inner), |
|
| 1378 | - | mutable: pointer.mutable, |
|
| 1379 | - | }); |
|
| 1380 | - | } |
|
| 1381 | - | case Type::Slice(slice) => { |
|
| 1382 | - | let inner = try materializeConcreteGenericData(self, *slice.item, site); |
|
| 1383 | - | return Type::Slice(SliceType { |
|
| 1384 | - | class: slice.class, |
|
| 1385 | - | item: allocType(self, inner), |
|
| 1386 | - | mutable: slice.mutable, |
|
| 1387 | - | }); |
|
| 1388 | - | } |
|
| 1389 | - | case Type::Array(array) => { |
|
| 1390 | - | let item = try materializeConcreteGenericData(self, *array.item, site); |
|
| 1391 | - | let length = try materializeConcreteGenericData( |
|
| 1392 | - | self, *array.length, site |
|
| 1463 | + | case Type::Core(core) => { |
|
| 1464 | + | let argument = try materializeConcreteGenericData( |
|
| 1465 | + | self, *coreValueArgument(core), site |
|
| 1393 | 1466 | ); |
|
| 1394 | - | return Type::Array(ArrayType { |
|
| 1395 | - | item: allocType(self, item), |
|
| 1396 | - | length: allocType(self, length), |
|
| 1397 | - | }); |
|
| 1398 | - | } |
|
| 1399 | - | case Type::Optional(inner) => { |
|
| 1400 | - | let value = try materializeConcreteGenericData(self, *inner, site); |
|
| 1401 | - | return Type::Optional(allocType(self, value)); |
|
| 1467 | + | let mut constantArgument: ?*Type = nil; |
|
| 1468 | + | if let descriptor = coreConstantArgument(core) { |
|
| 1469 | + | let concrete = try materializeConcreteGenericData( |
|
| 1470 | + | self, *descriptor, site |
|
| 1471 | + | ); |
|
| 1472 | + | set constantArgument = allocType(self, concrete); |
|
| 1473 | + | } |
|
| 1474 | + | return Type::Core(coreWithArguments( |
|
| 1475 | + | core, |
|
| 1476 | + | allocType(self, argument), |
|
| 1477 | + | constantArgument, |
|
| 1478 | + | )); |
|
| 1402 | 1479 | } |
|
| 1403 | 1480 | case Type::GenericDataApply(app) => { |
|
| 1404 | 1481 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 1405 | 1482 | let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 }; |
|
| 1406 | 1483 | let mut concrete = true; |
| 1560 | 1637 | export fn copyStructuralTypeToArena( |
|
| 1561 | 1638 | ty: Type, |
|
| 1562 | 1639 | arena: *mut alloc::Arena, |
|
| 1563 | 1640 | ) -> Type { |
|
| 1564 | 1641 | match ty { |
|
| 1565 | - | case Type::Pointer(pointer) => { |
|
| 1566 | - | let target = copyStructuralTypeToArena(*pointer.target, arena); |
|
| 1567 | - | return Type::Pointer(PointerType { |
|
| 1568 | - | class: pointer.class, |
|
| 1569 | - | target: allocArenaType(arena, target), |
|
| 1570 | - | mutable: pointer.mutable, |
|
| 1571 | - | }); |
|
| 1572 | - | } |
|
| 1573 | - | case Type::Slice(slice) => { |
|
| 1574 | - | let item = copyStructuralTypeToArena(*slice.item, arena); |
|
| 1575 | - | return Type::Slice(SliceType { |
|
| 1576 | - | class: slice.class, |
|
| 1577 | - | item: allocArenaType(arena, item), |
|
| 1578 | - | mutable: slice.mutable, |
|
| 1579 | - | }); |
|
| 1580 | - | } |
|
| 1581 | - | case Type::Array(array) => { |
|
| 1582 | - | let item = copyStructuralTypeToArena(*array.item, arena); |
|
| 1583 | - | let length = copyStructuralTypeToArena(*array.length, arena); |
|
| 1584 | - | return Type::Array(ArrayType { |
|
| 1585 | - | item: allocArenaType(arena, item), |
|
| 1586 | - | length: allocArenaType(arena, length), |
|
| 1587 | - | }); |
|
| 1588 | - | } |
|
| 1589 | - | case Type::Optional(inner) => { |
|
| 1590 | - | let stored = copyStructuralTypeToArena(*inner, arena); |
|
| 1591 | - | return Type::Optional(allocArenaType(arena, stored)); |
|
| 1642 | + | case Type::Core(core) => { |
|
| 1643 | + | let argument = copyStructuralTypeToArena( |
|
| 1644 | + | *coreValueArgument(core), arena |
|
| 1645 | + | ); |
|
| 1646 | + | let mut constantArgument: ?*Type = nil; |
|
| 1647 | + | if let descriptor = coreConstantArgument(core) { |
|
| 1648 | + | let stored = copyStructuralTypeToArena(*descriptor, arena); |
|
| 1649 | + | set constantArgument = allocArenaType(arena, stored); |
|
| 1650 | + | } |
|
| 1651 | + | return Type::Core(coreWithArguments( |
|
| 1652 | + | core, |
|
| 1653 | + | allocArenaType(arena, argument), |
|
| 1654 | + | constantArgument, |
|
| 1655 | + | )); |
|
| 1592 | 1656 | } |
|
| 1593 | 1657 | case Type::Fn(info) => { |
|
| 1594 | 1658 | let a = alloc::arenaAllocator(arena); |
|
| 1595 | 1659 | let mut params = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 }; |
|
| 1596 | 1660 | let mut throwList = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 }; |
| 1663 | 1727 | else throw ResolveError::Failure; |
|
| 1664 | 1728 | return Type::ConstArgument { type, value }; |
|
| 1665 | 1729 | } |
|
| 1666 | 1730 | } |
|
| 1667 | 1731 | } |
|
| 1668 | - | case Type::Pointer(pointer) => { |
|
| 1669 | - | let inner = try substituteTypeWithContext(context, *pointer.target, sub); |
|
| 1670 | - | return Type::Pointer(PointerType { |
|
| 1671 | - | class: pointer.class, |
|
| 1672 | - | target: allocSubstitutedType(context, inner), |
|
| 1673 | - | mutable: pointer.mutable, |
|
| 1674 | - | }); |
|
| 1675 | - | } |
|
| 1676 | - | case Type::Slice(slice) => { |
|
| 1677 | - | let inner = try substituteTypeWithContext(context, *slice.item, sub); |
|
| 1678 | - | return Type::Slice(SliceType { |
|
| 1679 | - | class: slice.class, |
|
| 1680 | - | item: allocSubstitutedType(context, inner), |
|
| 1681 | - | mutable: slice.mutable, |
|
| 1682 | - | }); |
|
| 1683 | - | } |
|
| 1684 | - | case Type::Array(array) => { |
|
| 1685 | - | let item = try substituteTypeWithContext(context, *array.item, sub); |
|
| 1686 | - | let length = try substituteTypeWithContext( |
|
| 1687 | - | context, *array.length, sub |
|
| 1732 | + | case Type::Core(core) => { |
|
| 1733 | + | let argument = try substituteTypeWithContext( |
|
| 1734 | + | context, *coreValueArgument(core), sub |
|
| 1688 | 1735 | ); |
|
| 1689 | - | return Type::Array(ArrayType { |
|
| 1690 | - | item: allocSubstitutedType(context, item), |
|
| 1691 | - | length: allocSubstitutedType(context, length), |
|
| 1692 | - | }); |
|
| 1693 | - | } |
|
| 1694 | - | case Type::Optional(inner) => { |
|
| 1695 | - | let value = try substituteTypeWithContext(context, *inner, sub); |
|
| 1696 | - | return Type::Optional(allocSubstitutedType(context, value)); |
|
| 1736 | + | let mut constantArgument: ?*Type = nil; |
|
| 1737 | + | if let descriptor = coreConstantArgument(core) { |
|
| 1738 | + | let replacement = try substituteTypeWithContext( |
|
| 1739 | + | context, *descriptor, sub |
|
| 1740 | + | ); |
|
| 1741 | + | set constantArgument = |
|
| 1742 | + | allocSubstitutedType(context, replacement); |
|
| 1743 | + | } |
|
| 1744 | + | return Type::Core(coreWithArguments( |
|
| 1745 | + | core, |
|
| 1746 | + | allocSubstitutedType(context, argument), |
|
| 1747 | + | constantArgument, |
|
| 1748 | + | )); |
|
| 1697 | 1749 | } |
|
| 1698 | 1750 | case Type::GenericDataApply(app) => { |
|
| 1699 | 1751 | let a = alloc::arenaAllocator(substitutionArena(context)); |
|
| 1700 | 1752 | let mut args = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 }; |
|
| 1701 | 1753 | let mut symbolic = false; |
| 1753 | 1805 | context, field.fieldType, sub |
|
| 1754 | 1806 | ); |
|
| 1755 | 1807 | let mut storedFieldType = fieldType; |
|
| 1756 | 1808 | match *context { |
|
| 1757 | 1809 | case TypeSubstitutionContext::Resolution { resolver, site } => { |
|
| 1758 | - | if hasUnresolvedNominalLayout(fieldType) { |
|
| 1810 | + | if hasLayoutObstacle( |
|
| 1811 | + | fieldType, LayoutObstacle::UnresolvedNominal |
|
| 1812 | + | ) { |
|
| 1759 | 1813 | throw emitError( |
|
| 1760 | 1814 | resolver, site, ErrorKind::GenericRecursiveLayout |
|
| 1761 | 1815 | ); |
|
| 1762 | 1816 | } |
|
| 1763 | 1817 | try ensureStorableType(resolver, site, fieldType); |
| 2484 | 2538 | } |
|
| 2485 | 2539 | ||
| 2486 | 2540 | /// Get the layout of a type. |
|
| 2487 | 2541 | export fn getTypeLayout(ty: Type) -> Layout { |
|
| 2488 | 2542 | match ty { |
|
| 2489 | - | case Type::Pointer(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 2490 | - | case Type::Slice(_), Type::TraitObject(_) => |
|
| 2543 | + | case Type::Core(CoreType::Pointer { shape, .. }) => { |
|
| 2544 | + | match shape { |
|
| 2545 | + | case PointerShape::Thin => |
|
| 2546 | + | return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 2547 | + | case PointerShape::Slice => |
|
| 2548 | + | return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE }, |
|
| 2549 | + | } |
|
| 2550 | + | } |
|
| 2551 | + | case Type::Core(CoreType::Array { item, length }) => |
|
| 2552 | + | return getArrayLayout(item, length), |
|
| 2553 | + | case Type::Core(CoreType::Optional { payload }) => { |
|
| 2554 | + | if isNullableType(*payload) { |
|
| 2555 | + | return getTypeLayout(*payload); |
|
| 2556 | + | } |
|
| 2557 | + | let payloadLayout = getTypeLayout(*payload); |
|
| 2558 | + | let payloadOffset = getOptionalValOffset(*payload); |
|
| 2559 | + | let alignment = max(payloadLayout.alignment, 1); |
|
| 2560 | + | return Layout { |
|
| 2561 | + | size: mem::alignUp(payloadOffset + payloadLayout.size, alignment), |
|
| 2562 | + | alignment, |
|
| 2563 | + | }; |
|
| 2564 | + | } |
|
| 2565 | + | case Type::TraitObject(_) => |
|
| 2491 | 2566 | return Layout { size: PTR_SIZE * 2, alignment: PTR_SIZE }, |
|
| 2492 | 2567 | case Type::Void, Type::Never => return Layout { size: 0, alignment: 0 }, |
|
| 2493 | 2568 | case Type::Bool, Type::U8, Type::I8 => return Layout { size: 1, alignment: 1 }, |
|
| 2494 | 2569 | case Type::U16, Type::I16 => return Layout { size: 2, alignment: 2 }, |
|
| 2495 | 2570 | case Type::U32, Type::I32 => return Layout { size: 4, alignment: 4 }, |
|
| 2496 | 2571 | case Type::Int => return Layout { size: 8, alignment: 8 }, |
|
| 2497 | 2572 | case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 }, |
|
| 2498 | 2573 | case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 2499 | - | case Type::Array(arr) => return getArrayLayout(arr), |
|
| 2500 | - | case Type::Optional(inner) => return getOptionalLayout(*inner), |
|
| 2501 | 2574 | case Type::Nominal(info) => return getNominalLayout(*info), |
|
| 2502 | 2575 | else => { |
|
| 2503 | 2576 | panic "getTypeLayout: the given type cannot be layed out"; |
|
| 2504 | 2577 | } |
|
| 2505 | 2578 | } |
| 2517 | 2590 | } |
|
| 2518 | 2591 | } |
|
| 2519 | 2592 | return layout; |
|
| 2520 | 2593 | } |
|
| 2521 | 2594 | ||
| 2522 | - | /// Get the layout of a concrete array type. |
|
| 2523 | - | export fn getArrayLayout(arr: ArrayType) -> Layout { |
|
| 2524 | - | let length = concreteArrayLength(arr.length) |
|
| 2595 | + | /// Get the layout of a concrete array application. |
|
| 2596 | + | export fn getArrayLayout(item: *Type, lengthArgument: *Type) -> Layout { |
|
| 2597 | + | if hasLayoutObstacle(*lengthArgument, LayoutObstacle::Symbolic) { |
|
| 2598 | + | panic "getArrayLayout: symbolic array length"; |
|
| 2599 | + | } |
|
| 2600 | + | let length = concreteArrayLength(lengthArgument) |
|
| 2525 | 2601 | else panic "getArrayLayout: symbolic array length"; |
|
| 2526 | - | let itemLayout = getTypeLayout(*arr.item); |
|
| 2602 | + | let itemLayout = getTypeLayout(*item); |
|
| 2527 | 2603 | return Layout { |
|
| 2528 | 2604 | size: itemLayout.size * length, |
|
| 2529 | 2605 | alignment: itemLayout.alignment, |
|
| 2530 | 2606 | }; |
|
| 2531 | 2607 | } |
|
| 2532 | 2608 | ||
| 2533 | - | /// Get the layout of an optional type. |
|
| 2534 | - | export fn getOptionalLayout(inner: Type) -> Layout { |
|
| 2535 | - | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 2536 | - | if isNullableType(inner) { |
|
| 2537 | - | return getTypeLayout(inner); |
|
| 2538 | - | } |
|
| 2539 | - | let innerLayout = getTypeLayout(inner); |
|
| 2540 | - | let tagSize: u32 = 1; |
|
| 2541 | - | let valOffset = mem::alignUp(tagSize, innerLayout.alignment); |
|
| 2542 | - | let alignment = max(innerLayout.alignment, 1); |
|
| 2543 | 2609 | ||
| 2544 | - | return Layout { |
|
| 2545 | - | size: mem::alignUp(valOffset + innerLayout.size, alignment), |
|
| 2546 | - | alignment, |
|
| 2547 | - | }; |
|
| 2610 | + | /// Return the payload of an optional type. |
|
| 2611 | + | export fn optionalTypeArgument(ty: Type) -> ?*Type { |
|
| 2612 | + | if let case Type::Core(CoreType::Optional { payload }) = ty { |
|
| 2613 | + | return payload; |
|
| 2614 | + | } |
|
| 2615 | + | return nil; |
|
| 2548 | 2616 | } |
|
| 2549 | 2617 | ||
| 2550 | - | /// Get the payload offset within an optional aggregate. |
|
| 2551 | - | export fn getOptionalValOffset(inner: Type) -> u32 { |
|
| 2552 | - | let innerLayout = getTypeLayout(inner); |
|
| 2553 | - | return mem::alignUp(1, innerLayout.alignment); |
|
| 2618 | + | /// Get the byte offset of an optional payload. |
|
| 2619 | + | export fn getOptionalValOffset(payload: Type) -> u32 { |
|
| 2620 | + | return mem::alignUp(1, getTypeLayout(payload).alignment); |
|
| 2554 | 2621 | } |
|
| 2555 | 2622 | ||
| 2556 | 2623 | /// Check if a type is optional. |
|
| 2557 | 2624 | export fn isOptionalType(ty: Type) -> bool { |
|
| 2558 | - | match ty { |
|
| 2559 | - | case Type::Optional(_) => return true, |
|
| 2560 | - | else => return false, |
|
| 2561 | - | } |
|
| 2562 | - | } |
|
| 2563 | - | ||
| 2564 | - | /// Check if a type uses null pointer optimization. |
|
| 2565 | - | /// This applies to optional pointers `?*T` and optional slices `?*[T]`, |
|
| 2566 | - | /// where `nil` is represented as a null data pointer with no tag byte. |
|
| 2567 | - | export fn isOptionalPointer(ty: Type) -> bool { |
|
| 2568 | - | if let case Type::Optional(inner) = ty { |
|
| 2569 | - | return isNullableType(*inner); |
|
| 2570 | - | } |
|
| 2571 | - | return false; |
|
| 2572 | - | } |
|
| 2573 | - | ||
| 2574 | - | /// Check if a type uses the optional aggregate representation. |
|
| 2575 | - | export fn isOptionalAggregate(ty: Type) -> bool { |
|
| 2576 | - | if let case Type::Optional(inner) = ty { |
|
| 2577 | - | return not isNullableType(*inner); |
|
| 2578 | - | } |
|
| 2579 | - | return false; |
|
| 2625 | + | return optionalTypeArgument(ty) <> nil; |
|
| 2580 | 2626 | } |
|
| 2581 | 2627 | ||
| 2582 | 2628 | /// Check if a type can use null to represent `nil`. |
|
| 2629 | + | /// |
|
| 2583 | 2630 | /// Pointers and slices have a data pointer that is never null when valid. |
|
| 2584 | 2631 | export fn isNullableType(ty: Type) -> bool { |
|
| 2585 | - | match ty { |
|
| 2586 | - | case Type::Pointer(_), Type::Slice(_) => return true, |
|
| 2587 | - | else => return false, |
|
| 2632 | + | if let case Type::Core(CoreType::Pointer { .. }) = ty { |
|
| 2633 | + | return true; |
|
| 2588 | 2634 | } |
|
| 2635 | + | return false; |
|
| 2589 | 2636 | } |
|
| 2590 | 2637 | ||
| 2591 | 2638 | /// Get the layout of a nominal type. |
|
| 2592 | 2639 | export fn getNominalLayout(info: NominalType) -> Layout { |
|
| 2593 | 2640 | match info { |
| 2680 | 2727 | } |
|
| 2681 | 2728 | ||
| 2682 | 2729 | /// Check if a type should be treated as an address-like value. |
|
| 2683 | 2730 | fn isAddressType(ty: Type) -> bool { |
|
| 2684 | 2731 | match ty { |
|
| 2685 | - | case Type::Pointer(_), Type::Slice(_), Type::Fn(_) => return true, |
|
| 2732 | + | case Type::Core(CoreType::Pointer { .. }) => return true, |
|
| 2733 | + | case Type::Fn(_) => return true, |
|
| 2686 | 2734 | else => return false, |
|
| 2687 | 2735 | } |
|
| 2688 | 2736 | } |
|
| 2689 | 2737 | ||
| 2690 | 2738 | /// Return the representable range for an integer type. |
| 2753 | 2801 | ||
| 2754 | 2802 | /// Ensure all nested nominal types in a type are resolved. |
|
| 2755 | 2803 | fn ensureTypeResolved(self: *mut Resolver, ty: Type, site: *ast::Node) throws (ResolveError) { |
|
| 2756 | 2804 | match ty { |
|
| 2757 | 2805 | case Type::Nominal(info) => try ensureNominalResolved(self, info, site), |
|
| 2758 | - | case Type::Slice(slice) => try ensureTypeResolved(self, *slice.item, site), |
|
| 2759 | - | case Type::Pointer(_) => {}, // Pointers have fixed layout, don't recurse. |
|
| 2760 | - | case Type::Array(arr) => try ensureTypeResolved(self, *arr.item, site), |
|
| 2761 | - | case Type::Optional(inner) => try ensureTypeResolved(self, *inner, site), |
|
| 2806 | + | // Pointer and slice layouts never depend on their targets, which also |
|
| 2807 | + | // cuts recursive nominal layout cycles. |
|
| 2808 | + | case Type::Core(CoreType::Pointer { .. }) => {}, |
|
| 2809 | + | case Type::Core(core) => |
|
| 2810 | + | try ensureTypeResolved(self, *coreValueArgument(core), site), |
|
| 2762 | 2811 | else => {}, |
|
| 2763 | 2812 | } |
|
| 2764 | 2813 | } |
|
| 2765 | 2814 | ||
| 2766 | 2815 | /// Ensure a nominal type has its body resolved. |
| 2821 | 2870 | to == types::PointerClass::Owned |
|
| 2822 | 2871 | and from == types::PointerClass::Ref |
|
| 2823 | 2872 | ); |
|
| 2824 | 2873 | } |
|
| 2825 | 2874 | ||
| 2875 | + | /// Return whether pointer properties permit assignment. |
|
| 2876 | + | fn pointerPropertiesAssignable( |
|
| 2877 | + | to: PointerProperties, |
|
| 2878 | + | from: PointerProperties, |
|
| 2879 | + | ) -> bool { |
|
| 2880 | + | return pointerClassesAssignable(to.class, from.class) |
|
| 2881 | + | and (not to.mutable or from.mutable); |
|
| 2882 | + | } |
|
| 2883 | + | ||
| 2826 | 2884 | /// Check if the `from` type is assignable to the `to` type, and return a |
|
| 2827 | 2885 | /// coercion plan if so. |
|
| 2828 | 2886 | fn isAssignable(self: *mut Resolver, to: Type, from: Type, rval: *ast::Node) -> ?Coercion { |
|
| 2829 | 2887 | if to == Type::Unknown or from == Type::Unknown { |
|
| 2830 | 2888 | return nil; |
| 2840 | 2898 | return Coercion::Identity; |
|
| 2841 | 2899 | } |
|
| 2842 | 2900 | if typesEqual(to, from) { |
|
| 2843 | 2901 | return Coercion::Identity; |
|
| 2844 | 2902 | } |
|
| 2845 | - | if let case Type::Pointer(lhs) = to { |
|
| 2846 | - | let case Type::Pointer(rhs) = from else return nil; |
|
| 2847 | - | if not pointerClassesAssignable(lhs.class, rhs.class) { |
|
| 2848 | - | return nil; |
|
| 2849 | - | } |
|
| 2850 | - | // Allow coercion from `*T` to `*opaque`, and mutable counterparts. |
|
| 2851 | - | if *lhs.target == Type::Opaque { |
|
| 2852 | - | if lhs.mutable and not rhs.mutable { |
|
| 2903 | + | if let case Type::Core(lhs) = to { |
|
| 2904 | + | match lhs { |
|
| 2905 | + | case CoreType::Pointer { shape: lhsShape, properties: lhsProperties, target: lhsTarget } => { |
|
| 2906 | + | let case Type::Core(CoreType::Pointer { shape: rhsShape, properties: rhsProperties, target: rhsTarget }) = from else return nil; |
|
| 2907 | + | if lhsShape <> rhsShape |
|
| 2908 | + | or not pointerPropertiesAssignable( |
|
| 2909 | + | lhsProperties, rhsProperties |
|
| 2910 | + | ) |
|
| 2911 | + | { |
|
| 2912 | + | return nil; |
|
| 2913 | + | } |
|
| 2914 | + | // Allow pointer-like coercion from `T` to `opaque`, including |
|
| 2915 | + | // mutable counterparts. |
|
| 2916 | + | if *lhsTarget == Type::Opaque { |
|
| 2917 | + | return Coercion::Identity; |
|
| 2918 | + | } |
|
| 2919 | + | return isAssignable(self, *lhsTarget, *rhsTarget, rval); |
|
| 2920 | + | } |
|
| 2921 | + | case CoreType::Array { item: lhsItem, length: lhsLength } => { |
|
| 2922 | + | let case Type::Core(CoreType::Array { |
|
| 2923 | + | item: rhsItem, length: rhsLength |
|
| 2924 | + | }) = from else return nil; |
|
| 2925 | + | if not typesEqual(*lhsLength, *rhsLength) { |
|
| 2926 | + | return nil; |
|
| 2927 | + | } |
|
| 2928 | + | // For array literals, check each element individually for |
|
| 2929 | + | // assignability. |
|
| 2930 | + | match rval.value { |
|
| 2931 | + | case ast::NodeValue::ArrayLit(items) => { |
|
| 2932 | + | if let length = concreteArrayLength(lhsLength); length == 0 { |
|
| 2933 | + | return Coercion::Identity; |
|
| 2934 | + | } |
|
| 2935 | + | // TODO: This won't work, because we should be setting |
|
| 2936 | + | // coercions for every list item, but we don't. It's best |
|
| 2937 | + | // to not have an `isAssignable` function and just have |
|
| 2938 | + | // one that records coercions. |
|
| 2939 | + | if isListAssignable(self, *lhsItem, items) { |
|
| 2940 | + | return Coercion::Identity; |
|
| 2941 | + | } |
|
| 2942 | + | return nil; |
|
| 2943 | + | } |
|
| 2944 | + | case ast::NodeValue::ArrayRepeatLit(repeat) => |
|
| 2945 | + | return isAssignable( |
|
| 2946 | + | self, *lhsItem, *rhsItem, repeat.item |
|
| 2947 | + | ), |
|
| 2948 | + | else => { |
|
| 2949 | + | if typesEqual(*lhsItem, *rhsItem) { |
|
| 2950 | + | return Coercion::Identity; |
|
| 2951 | + | } |
|
| 2952 | + | return nil; |
|
| 2953 | + | } |
|
| 2954 | + | } |
|
| 2955 | + | } |
|
| 2956 | + | case CoreType::Optional { payload } => { |
|
| 2957 | + | if from == Type::Nil { |
|
| 2958 | + | return Coercion::OptionalLift(to); |
|
| 2959 | + | } |
|
| 2960 | + | if let _ = isAssignable(self, *payload, from, rval) { |
|
| 2961 | + | return Coercion::OptionalLift(to); |
|
| 2962 | + | } |
|
| 2963 | + | if let case Type::Core(CoreType::Optional { |
|
| 2964 | + | payload: fromPayload |
|
| 2965 | + | }) = from { |
|
| 2966 | + | return isAssignable(self, *payload, *fromPayload, rval); |
|
| 2967 | + | } |
|
| 2853 | 2968 | return nil; |
|
| 2854 | 2969 | } |
|
| 2855 | - | return Coercion::Identity; |
|
| 2856 | - | } |
|
| 2857 | - | if lhs.mutable and not rhs.mutable { |
|
| 2858 | - | return nil; |
|
| 2859 | 2970 | } |
|
| 2860 | - | return isAssignable(self, *lhs.target, *rhs.target, rval); |
|
| 2861 | 2971 | } |
|
| 2862 | 2972 | if let case Type::TraitObject(lhs) = to { |
|
| 2863 | - | if let case Type::Pointer(rhs) = from { |
|
| 2864 | - | if not pointerClassesAssignable(lhs.class, rhs.class) |
|
| 2865 | - | or (lhs.mutable and not rhs.mutable) |
|
| 2866 | - | { |
|
| 2973 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = from { |
|
| 2974 | + | if not pointerPropertiesAssignable(lhs.properties, properties) { |
|
| 2867 | 2975 | return nil; |
|
| 2868 | 2976 | } |
|
| 2869 | - | if let inst = findInstance(self, lhs.traitInfo, *rhs.target) { |
|
| 2870 | - | return Coercion::TraitObject { traitInfo: lhs.traitInfo, inst }; |
|
| 2977 | + | if let inst = findInstance(self, lhs.traitInfo, *target) { |
|
| 2978 | + | return Coercion::TraitObject { |
|
| 2979 | + | traitInfo: lhs.traitInfo, |
|
| 2980 | + | inst, |
|
| 2981 | + | }; |
|
| 2871 | 2982 | } |
|
| 2872 | 2983 | } |
|
| 2873 | 2984 | if let case Type::TraitObject(rhs) = from { |
|
| 2874 | - | if not pointerClassesAssignable(lhs.class, rhs.class) |
|
| 2875 | - | or lhs.traitInfo <> rhs.traitInfo |
|
| 2876 | - | { |
|
| 2877 | - | return nil; |
|
| 2878 | - | } |
|
| 2879 | - | if lhs.mutable and not rhs.mutable { |
|
| 2985 | + | if not pointerPropertiesAssignable( |
|
| 2986 | + | lhs.properties, rhs.properties |
|
| 2987 | + | ) or lhs.traitInfo <> rhs.traitInfo { |
|
| 2880 | 2988 | return nil; |
|
| 2881 | 2989 | } |
|
| 2882 | 2990 | return Coercion::Identity; |
|
| 2883 | 2991 | } |
|
| 2884 | 2992 | return nil; |
|
| 2885 | 2993 | } |
|
| 2886 | - | if let case Type::Slice(lhs) = to { |
|
| 2887 | - | let case Type::Slice(rhs) = from else return nil; |
|
| 2888 | - | if not pointerClassesAssignable(lhs.class, rhs.class) |
|
| 2889 | - | or (lhs.mutable and not rhs.mutable) |
|
| 2890 | - | { |
|
| 2891 | - | return nil; |
|
| 2892 | - | } |
|
| 2893 | - | // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts. |
|
| 2894 | - | if *lhs.item == Type::Opaque { |
|
| 2895 | - | return Coercion::Identity; |
|
| 2896 | - | } |
|
| 2897 | - | return isAssignable(self, *lhs.item, *rhs.item, rval); |
|
| 2898 | - | } |
|
| 2899 | - | match to { |
|
| 2900 | - | case Type::Array(lhs) => { |
|
| 2901 | - | let case Type::Array(rhs) = from |
|
| 2902 | - | else return nil; |
|
| 2903 | - | ||
| 2904 | - | if not typesEqual(*lhs.length, *rhs.length) { |
|
| 2905 | - | return nil; |
|
| 2906 | - | } |
|
| 2907 | - | // For array literals, check each element individually for |
|
| 2908 | - | // assignability. |
|
| 2909 | - | match rval.value { |
|
| 2910 | - | case ast::NodeValue::ArrayLit(items) => { |
|
| 2911 | - | if let length = concreteArrayLength(lhs.length); length == 0 { |
|
| 2912 | - | return Coercion::Identity; |
|
| 2913 | - | } |
|
| 2914 | - | // TODO: This won't work, because we should be setting coercions |
|
| 2915 | - | // for every list item, but we don't. It's best to not have an |
|
| 2916 | - | // `isAssignable` function and just have one that records coercions. |
|
| 2917 | - | if isListAssignable(self, *lhs.item, items) { |
|
| 2918 | - | return Coercion::Identity; |
|
| 2919 | - | } |
|
| 2920 | - | return nil; |
|
| 2921 | - | } |
|
| 2922 | - | case ast::NodeValue::ArrayRepeatLit(repeat) => { |
|
| 2923 | - | return isAssignable(self, *lhs.item, *rhs.item, repeat.item); |
|
| 2924 | - | } |
|
| 2925 | - | else => { |
|
| 2926 | - | if typesEqual(*lhs.item, *rhs.item) { |
|
| 2927 | - | return Coercion::Identity; |
|
| 2928 | - | } |
|
| 2929 | - | return nil; |
|
| 2930 | - | } |
|
| 2931 | - | } |
|
| 2932 | - | } |
|
| 2933 | - | ||
| 2934 | - | case Type::Optional(inner) => { |
|
| 2935 | - | if from == Type::Nil { |
|
| 2936 | - | return Coercion::OptionalLift(to); |
|
| 2937 | - | } |
|
| 2938 | - | if let _ = isAssignable(self, *inner, from, rval) { |
|
| 2939 | - | return Coercion::OptionalLift(to); |
|
| 2940 | - | } |
|
| 2941 | - | if let case Type::Optional(fromInner) = from { |
|
| 2942 | - | return isAssignable(self, *inner, *fromInner, rval); |
|
| 2943 | - | } |
|
| 2944 | - | return nil; |
|
| 2945 | - | } |
|
| 2946 | 2994 | ||
| 2995 | + | match to { |
|
| 2947 | 2996 | case Type::Fn(toInfo) => { |
|
| 2948 | 2997 | // Allow function type structural matching. |
|
| 2949 | 2998 | if let case Type::Fn(fromInfo) = from { |
|
| 2950 | 2999 | if fnTypeEqual(toInfo, fromInfo) { |
|
| 2951 | 3000 | return Coercion::Identity; |
| 3011 | 3060 | } |
|
| 3012 | 3061 | } |
|
| 3013 | 3062 | return true; |
|
| 3014 | 3063 | } |
|
| 3015 | 3064 | ||
| 3065 | + | /// Return whether two core applications are structurally equal. |
|
| 3066 | + | fn coreTypesEqual(left: CoreType, right: CoreType) -> bool { |
|
| 3067 | + | match left { |
|
| 3068 | + | case CoreType::Pointer { shape, properties, target } => { |
|
| 3069 | + | let case CoreType::Pointer { |
|
| 3070 | + | shape: rightShape, properties: rightProperties, target: rightTarget |
|
| 3071 | + | } = right else return false; |
|
| 3072 | + | return shape == rightShape and properties == rightProperties |
|
| 3073 | + | and typesEqual(*target, *rightTarget); |
|
| 3074 | + | } |
|
| 3075 | + | case CoreType::Array { item, length } => { |
|
| 3076 | + | let case CoreType::Array { |
|
| 3077 | + | item: rightItem, length: rightLength |
|
| 3078 | + | } = right else return false; |
|
| 3079 | + | return typesEqual(*item, *rightItem) |
|
| 3080 | + | and typesEqual(*length, *rightLength); |
|
| 3081 | + | } |
|
| 3082 | + | case CoreType::Optional { payload } => { |
|
| 3083 | + | let case CoreType::Optional { payload: rightPayload } = right |
|
| 3084 | + | else return false; |
|
| 3085 | + | return typesEqual(*payload, *rightPayload); |
|
| 3086 | + | } |
|
| 3087 | + | } |
|
| 3088 | + | } |
|
| 3089 | + | ||
| 3016 | 3090 | /// Check if two types are structurally equal. |
|
| 3017 | 3091 | export fn typesEqual(a: Type, b: Type) -> bool { |
|
| 3092 | + | if let case Type::Core(lhs) = a { |
|
| 3093 | + | let case Type::Core(rhs) = b else return false; |
|
| 3094 | + | return coreTypesEqual(lhs, rhs); |
|
| 3095 | + | } |
|
| 3018 | 3096 | if a == b { |
|
| 3019 | 3097 | return true; |
|
| 3020 | 3098 | } |
|
| 3021 | - | if let case Type::Pointer(av) = a { |
|
| 3022 | - | let case Type::Pointer(bv) = b else return false; |
|
| 3023 | - | return av.class == bv.class and av.mutable == bv.mutable |
|
| 3024 | - | and typesEqual(*av.target, *bv.target); |
|
| 3025 | - | } |
|
| 3026 | - | if let case Type::Slice(av) = a { |
|
| 3027 | - | let case Type::Slice(bv) = b else return false; |
|
| 3028 | - | return av.class == bv.class and av.mutable == bv.mutable |
|
| 3029 | - | and typesEqual(*av.item, *bv.item); |
|
| 3030 | - | } |
|
| 3031 | 3099 | if let case Type::TraitObject(av) = a { |
|
| 3032 | 3100 | let case Type::TraitObject(bv) = b else return false; |
|
| 3033 | - | return av.class == bv.class and av.mutable == bv.mutable |
|
| 3101 | + | return av.properties == bv.properties |
|
| 3034 | 3102 | and av.traitInfo == bv.traitInfo; |
|
| 3035 | 3103 | } |
|
| 3036 | 3104 | match a { |
|
| 3037 | 3105 | case Type::Range { start: aStart, end: aEnd } => { |
|
| 3038 | 3106 | let case Type::Range { start: bStart, end: bEnd } = b |
| 3049 | 3117 | let bValue = bEnd else return false; |
|
| 3050 | 3118 | return typesEqual(*aValue, *bValue); |
|
| 3051 | 3119 | } |
|
| 3052 | 3120 | return bEnd == nil; |
|
| 3053 | 3121 | } |
|
| 3054 | - | case Type::Array(aa) => { |
|
| 3055 | - | let case Type::Array(ab) = b else return false; |
|
| 3056 | - | return typesEqual(*aa.length, *ab.length) |
|
| 3057 | - | and typesEqual(*aa.item, *ab.item); |
|
| 3058 | - | } |
|
| 3059 | - | case Type::Optional(oa) => { |
|
| 3060 | - | let case Type::Optional(ob) = b else return false; |
|
| 3061 | - | return typesEqual(*oa, *ob); |
|
| 3062 | - | } |
|
| 3063 | 3122 | case Type::Fn(fa) => { |
|
| 3064 | 3123 | let case Type::Fn(fb) = b else return false; |
|
| 3065 | 3124 | return fnTypeEqual(fa, fb); |
|
| 3066 | 3125 | } |
|
| 3067 | 3126 | case Type::GenericDataApply(aa) => { |
| 3078 | 3137 | } |
|
| 3079 | 3138 | else => return false, |
|
| 3080 | 3139 | } |
|
| 3081 | 3140 | } |
|
| 3082 | 3141 | ||
| 3083 | - | /// Return whether `ty` is a direct reference. |
|
| 3084 | - | export fn isRefType(ty: Type) -> bool { |
|
| 3142 | + | /// Return the shared properties of a direct pointer-like value. |
|
| 3143 | + | fn pointerProperties(ty: Type) -> ?PointerProperties { |
|
| 3085 | 3144 | match ty { |
|
| 3086 | - | case Type::Pointer(PointerType { class: types::PointerClass::Ref, .. }), |
|
| 3087 | - | Type::Slice(SliceType { class: types::PointerClass::Ref, .. }), |
|
| 3088 | - | Type::TraitObject(TraitObjectType { class: types::PointerClass::Ref, .. }) => return true, |
|
| 3089 | - | else => return false, |
|
| 3145 | + | case Type::Core(CoreType::Pointer { properties, .. }) => |
|
| 3146 | + | return properties, |
|
| 3147 | + | case Type::TraitObject(info) => return info.properties, |
|
| 3148 | + | else => return nil, |
|
| 3090 | 3149 | } |
|
| 3091 | 3150 | } |
|
| 3092 | 3151 | ||
| 3152 | + | /// Return whether `ty` is a direct reference. |
|
| 3153 | + | export fn isRefType(ty: Type) -> bool { |
|
| 3154 | + | let properties = pointerProperties(ty) else return false; |
|
| 3155 | + | return properties.class == types::PointerClass::Ref; |
|
| 3156 | + | } |
|
| 3157 | + | ||
| 3093 | 3158 | /// Return whether a type contains a reference. |
|
| 3094 | 3159 | fn containsRef(ty: Type) -> bool { |
|
| 3095 | 3160 | if isRefType(ty) { |
|
| 3096 | 3161 | return true; |
|
| 3097 | 3162 | } |
|
| 3098 | - | if let case Type::Pointer(pointer) = ty { |
|
| 3099 | - | return containsRef(*pointer.target); |
|
| 3100 | - | } |
|
| 3101 | - | if let case Type::Slice(slice) = ty { |
|
| 3102 | - | return containsRef(*slice.item); |
|
| 3103 | - | } |
|
| 3104 | 3163 | match ty { |
|
| 3105 | - | case Type::Array(array) => return containsRef(*array.item), |
|
| 3106 | - | case Type::Optional(inner) => return containsRef(*inner), |
|
| 3164 | + | case Type::Core(core) => |
|
| 3165 | + | return containsRef(*coreValueArgument(core)), |
|
| 3107 | 3166 | case Type::GenericRecord(rec) => { |
|
| 3108 | 3167 | for field in rec.fields { |
|
| 3109 | 3168 | if containsRef(field.fieldType) { |
|
| 3110 | 3169 | return true; |
|
| 3111 | 3170 | } |
| 3119 | 3178 | } |
|
| 3120 | 3179 | } |
|
| 3121 | 3180 | ||
| 3122 | 3181 | /// Return whether a type is exact-linear. |
|
| 3123 | 3182 | export fn isLinear(ty: Type) -> bool { |
|
| 3183 | + | if let properties = pointerProperties(ty) { |
|
| 3184 | + | return properties.class == types::PointerClass::Owned; |
|
| 3185 | + | } |
|
| 3124 | 3186 | match ty { |
|
| 3125 | - | case Type::Pointer(PointerType { class: types::PointerClass::Owned, .. }), |
|
| 3126 | - | Type::Slice(SliceType { class: types::PointerClass::Owned, .. }), |
|
| 3127 | - | Type::TraitObject(TraitObjectType { class: types::PointerClass::Owned, .. }) => return true, |
|
| 3128 | - | case Type::Pointer(PointerType { class: types::PointerClass::Ref, .. }), |
|
| 3129 | - | Type::Pointer(PointerType { class: types::PointerClass::Unsafe, .. }), |
|
| 3130 | - | Type::Slice(SliceType { class: types::PointerClass::Ref, .. }), |
|
| 3131 | - | Type::Slice(SliceType { class: types::PointerClass::Unsafe, .. }), |
|
| 3132 | - | Type::TraitObject(TraitObjectType { class: types::PointerClass::Ref, .. }), |
|
| 3133 | - | Type::TraitObject(TraitObjectType { class: types::PointerClass::Unsafe, .. }) => return false, |
|
| 3134 | - | ||
| 3135 | - | case Type::Array(array) => return isLinear(*array.item), |
|
| 3136 | - | case Type::Optional(inner) => return isLinear(*inner), |
|
| 3187 | + | case Type::Core(core) => |
|
| 3188 | + | return isLinear(*coreValueArgument(core)), |
|
| 3137 | 3189 | case Type::Nominal(NominalType::Record(recInfo)) => { |
|
| 3138 | 3190 | if recInfo.declaredLinear { |
|
| 3139 | 3191 | return true; |
|
| 3140 | 3192 | } |
|
| 3141 | 3193 | for field in recInfo.fields { |
| 3160 | 3212 | } |
|
| 3161 | 3213 | } |
|
| 3162 | 3214 | ||
| 3163 | 3215 | /// Return whether `ty` is a direct unsafe pointer-like value. |
|
| 3164 | 3216 | fn isUnsafePointerType(ty: Type) -> bool { |
|
| 3165 | - | match ty { |
|
| 3166 | - | case Type::Pointer(PointerType { class: types::PointerClass::Unsafe, .. }), |
|
| 3167 | - | Type::Slice(SliceType { class: types::PointerClass::Unsafe, .. }), |
|
| 3168 | - | Type::TraitObject(TraitObjectType { class: types::PointerClass::Unsafe, .. }) => return true, |
|
| 3169 | - | else => return false, |
|
| 3170 | - | } |
|
| 3217 | + | let properties = pointerProperties(ty) else return false; |
|
| 3218 | + | return properties.class == types::PointerClass::Unsafe; |
|
| 3171 | 3219 | } |
|
| 3172 | 3220 | ||
| 3173 | 3221 | /// Get the record info from a record type. |
|
| 3174 | 3222 | export fn getRecord(ty: Type) -> ?RecordType { |
|
| 3175 | 3223 | let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil; |
|
| 3176 | 3224 | return recInfo; |
|
| 3177 | 3225 | } |
|
| 3178 | 3226 | ||
| 3179 | - | /// Auto-dereference a type: if it's a pointer, return the target type. |
|
| 3227 | + | /// Auto-dereference a type: if it is a pointer, return the target type. |
|
| 3180 | 3228 | export fn autoDeref(ty: Type) -> Type { |
|
| 3181 | - | if let case Type::Pointer(view) = ty { |
|
| 3182 | - | return *view.target; |
|
| 3229 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, target, .. }) = ty { |
|
| 3230 | + | return *target; |
|
| 3183 | 3231 | } |
|
| 3184 | 3232 | return ty; |
|
| 3185 | 3233 | } |
|
| 3186 | 3234 | ||
| 3187 | 3235 | /// Get field info for a record-like type (records, slices) by field index. |
|
| 3188 | 3236 | export fn getRecordField(ty: Type, index: u32) -> ?RecordField { |
|
| 3189 | - | if let case Type::Slice(slice) = ty { |
|
| 3237 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, properties, target }) = ty { |
|
| 3190 | 3238 | match index { |
|
| 3191 | 3239 | case 0 => return RecordField { |
|
| 3192 | 3240 | name: PTR_FIELD, |
|
| 3193 | - | fieldType: Type::Pointer(PointerType { |
|
| 3194 | - | class: slice.class, |
|
| 3195 | - | target: slice.item, |
|
| 3196 | - | mutable: slice.mutable, |
|
| 3197 | - | }), |
|
| 3241 | + | fieldType: pointerType( |
|
| 3242 | + | properties.class, target, properties.mutable |
|
| 3243 | + | ), |
|
| 3198 | 3244 | offset: 0, |
|
| 3199 | 3245 | }, |
|
| 3200 | 3246 | case 1 => return RecordField { |
|
| 3201 | 3247 | name: LEN_FIELD, |
|
| 3202 | 3248 | fieldType: Type::U32, |
| 3220 | 3266 | } |
|
| 3221 | 3267 | if left == right { |
|
| 3222 | 3268 | return true; |
|
| 3223 | 3269 | } |
|
| 3224 | 3270 | // Comparisons with optionals. |
|
| 3225 | - | if let case Type::Optional(l) = left { |
|
| 3226 | - | if let case Type::Optional(r) = right { |
|
| 3227 | - | return isComparable(*l, *r); |
|
| 3271 | + | if let case Type::Core(CoreType::Optional { payload: lhs }) = left { |
|
| 3272 | + | if let case Type::Core(CoreType::Optional { payload: rhs }) = right { |
|
| 3273 | + | return isComparable(*lhs, *rhs); |
|
| 3228 | 3274 | } else if right == Type::Nil { |
|
| 3229 | 3275 | return true; |
|
| 3230 | 3276 | } |
|
| 3231 | - | return isComparable(*l, right); |
|
| 3232 | - | } else if let case Type::Optional(_) = right { |
|
| 3233 | - | return isComparable(right, left); // Flip order. |
|
| 3277 | + | return isComparable(*lhs, right); |
|
| 3278 | + | } else if isOptionalType(right) { |
|
| 3279 | + | return isComparable(right, left); |
|
| 3234 | 3280 | } |
|
| 3235 | 3281 | // Pointer comparisons ignore mutability. |
|
| 3236 | - | if let case Type::Pointer(l) = left { |
|
| 3237 | - | if let case Type::Pointer(r) = right { |
|
| 3238 | - | return typesEqual(*l.target, *r.target); |
|
| 3282 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, target: lhs, .. }) = left { |
|
| 3283 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, target: rhs, .. }) = right { |
|
| 3284 | + | return typesEqual(*lhs, *rhs); |
|
| 3239 | 3285 | } |
|
| 3240 | 3286 | } |
|
| 3241 | 3287 | // Numeric types. |
|
| 3242 | 3288 | if isNumericType(left) and isNumericType(right) { |
|
| 3243 | 3289 | return true; |
| 3261 | 3307 | ||
| 3262 | 3308 | /// Check that a type is optional, otherwise throw an error. |
|
| 3263 | 3309 | fn checkOptional(self: *mut Resolver, node: *ast::Node) -> *Type |
|
| 3264 | 3310 | throws (ResolveError) |
|
| 3265 | 3311 | { |
|
| 3266 | - | if let case Type::Optional(inner) = try infer(self, node) { |
|
| 3267 | - | return inner; |
|
| 3312 | + | if let case Type::Core(CoreType::Optional { payload }) = |
|
| 3313 | + | try infer(self, node) |
|
| 3314 | + | { |
|
| 3315 | + | return payload; |
|
| 3268 | 3316 | } |
|
| 3269 | 3317 | throw emitError(self, node, ErrorKind::ExpectedOptional); |
|
| 3270 | 3318 | } |
|
| 3271 | 3319 | ||
| 3272 | 3320 | /// Check that a node's type is equal to the expected type. |
| 3750 | 3798 | self: *mut Resolver, |
|
| 3751 | 3799 | ty: Type, |
|
| 3752 | 3800 | visited: ?*GenericRootVisit, |
|
| 3753 | 3801 | ) -> bool { |
|
| 3754 | 3802 | match ty { |
|
| 3755 | - | case Type::Pointer(pointer) => |
|
| 3756 | - | return markGenericDataTypeRootedInner(self, *pointer.target, visited), |
|
| 3757 | - | case Type::Slice(slice) => |
|
| 3758 | - | return markGenericDataTypeRootedInner(self, *slice.item, visited), |
|
| 3759 | - | case Type::Array(array) => |
|
| 3760 | - | return markGenericDataTypeRootedInner(self, *array.item, visited), |
|
| 3761 | - | case Type::Optional(inner) => |
|
| 3762 | - | return markGenericDataTypeRootedInner(self, *inner, visited), |
|
| 3803 | + | case Type::Core(core) => { |
|
| 3804 | + | let mut changed = markGenericDataTypeRootedInner( |
|
| 3805 | + | self, *coreValueArgument(core), visited |
|
| 3806 | + | ); |
|
| 3807 | + | if let argument = coreConstantArgument(core) { |
|
| 3808 | + | set changed = markGenericDataTypeRootedInner( |
|
| 3809 | + | self, *argument, visited |
|
| 3810 | + | ) or changed; |
|
| 3811 | + | } |
|
| 3812 | + | return changed; |
|
| 3813 | + | } |
|
| 3763 | 3814 | case Type::Fn(info) => { |
|
| 3764 | 3815 | let mut changed = markGenericDataTypeRootedInner( |
|
| 3765 | 3816 | self, *info.returnType, visited |
|
| 3766 | 3817 | ); |
|
| 3767 | 3818 | for param in info.paramTypes { |
| 4100 | 4151 | members, substitution |
|
| 4101 | 4152 | } => { |
|
| 4102 | 4153 | set fieldType = try substituteType( |
|
| 4103 | 4154 | self, *members[i], substitution, typeNode |
|
| 4104 | 4155 | ); |
|
| 4105 | - | if hasUnresolvedNominalLayout(fieldType) { |
|
| 4156 | + | if hasLayoutObstacle( |
|
| 4157 | + | fieldType, LayoutObstacle::UnresolvedNominal |
|
| 4158 | + | ) { |
|
| 4106 | 4159 | throw emitError( |
|
| 4107 | 4160 | self, typeNode, ErrorKind::GenericRecursiveLayout |
|
| 4108 | 4161 | ); |
|
| 4109 | 4162 | } |
|
| 4110 | 4163 | try ensureStorableType(self, typeNode, fieldType); |
| 4175 | 4228 | members, substitution |
|
| 4176 | 4229 | } => { |
|
| 4177 | 4230 | set valueType = try substituteType( |
|
| 4178 | 4231 | self, *members[i], substitution, variantNode |
|
| 4179 | 4232 | ); |
|
| 4180 | - | if hasUnresolvedNominalLayout(valueType) { |
|
| 4233 | + | if hasLayoutObstacle( |
|
| 4234 | + | valueType, LayoutObstacle::UnresolvedNominal |
|
| 4235 | + | ) { |
|
| 4181 | 4236 | throw emitError( |
|
| 4182 | 4237 | self, variantNode, ErrorKind::GenericRecursiveLayout |
|
| 4183 | 4238 | ); |
|
| 4184 | 4239 | } |
|
| 4185 | 4240 | if let typeNode = variantDecl.type { |
| 4854 | 4909 | /// Reject nested references while allowing a direct parameter reference. |
|
| 4855 | 4910 | fn validateValueTypeReferences(self: *mut Resolver, node: *ast::Node, ty: Type) |
|
| 4856 | 4911 | throws (ResolveError) |
|
| 4857 | 4912 | { |
|
| 4858 | 4913 | if isRefType(ty) { |
|
| 4859 | - | if let case Type::Pointer(pointer) = ty { |
|
| 4860 | - | if containsRef(*pointer.target) { |
|
| 4861 | - | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 4862 | - | } |
|
| 4863 | - | } else if let case Type::Slice(slice) = ty { |
|
| 4864 | - | if containsRef(*slice.item) { |
|
| 4914 | + | if let case Type::Core(core) = ty { |
|
| 4915 | + | if containsRef(*coreValueArgument(core)) { |
|
| 4865 | 4916 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 4866 | 4917 | } |
|
| 4867 | 4918 | } |
|
| 4868 | 4919 | } else if containsRef(ty) { |
|
| 4869 | 4920 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
| 5024 | 5075 | case ast::NodeValue::Try(expr) => return try resolveTry(self, node, expr, hint), |
|
| 5025 | 5076 | case ast::NodeValue::Return { value } => return try resolveReturn(self, node, value), |
|
| 5026 | 5077 | case ast::NodeValue::Throw { expr } => return try resolveThrow(self, node, expr), |
|
| 5027 | 5078 | case ast::NodeValue::Panic { message } => { |
|
| 5028 | 5079 | // TODO: Have easy access to string type. |
|
| 5029 | - | try visitOptional(self, message, Type::Slice(SliceType { |
|
| 5030 | - | class: types::PointerClass::Owned, |
|
| 5031 | - | item: allocType(self, Type::U8), |
|
| 5032 | - | mutable: false, |
|
| 5033 | - | })); |
|
| 5080 | + | try visitOptional(self, message, sliceType(types::PointerClass::Owned, allocType(self, Type::U8), false)); |
|
| 5034 | 5081 | return setNodeType(self, node, Type::Never); |
|
| 5035 | 5082 | }, |
|
| 5036 | 5083 | case ast::NodeValue::Assert { condition, message } => { |
|
| 5037 | 5084 | try visit(self, condition, Type::Bool); |
|
| 5038 | 5085 | // TODO: Have easy access to string type. |
|
| 5039 | - | try visitOptional(self, message, Type::Slice(SliceType { |
|
| 5040 | - | class: types::PointerClass::Owned, |
|
| 5041 | - | item: allocType(self, Type::U8), |
|
| 5042 | - | mutable: false, |
|
| 5043 | - | })); |
|
| 5086 | + | try visitOptional(self, message, sliceType(types::PointerClass::Owned, allocType(self, Type::U8), false)); |
|
| 5044 | 5087 | return setNodeType(self, node, Type::Void); |
|
| 5045 | 5088 | }, |
|
| 5046 | 5089 | case ast::NodeValue::UnOp(unop) => return try resolveUnOp(self, node, unop), |
|
| 5047 | 5090 | case ast::NodeValue::ExprStmt(expr) => { |
|
| 5048 | 5091 | // Pass `Void` as expected type to indicate value is discarded. |
| 5055 | 5098 | // `super` by itself is invalid, must be used in scope access. |
|
| 5056 | 5099 | throw emitError(self, node, ErrorKind::InvalidModulePath); |
|
| 5057 | 5100 | }, |
|
| 5058 | 5101 | case ast::NodeValue::Nil => { |
|
| 5059 | 5102 | // Use the hint type if it's an optional, otherwise fall back to `Nil`. |
|
| 5060 | - | if let case Type::Optional(_) = hint { |
|
| 5103 | + | if isOptionalType(hint) { |
|
| 5061 | 5104 | return setNodeType(self, node, hint); |
|
| 5062 | 5105 | } |
|
| 5063 | 5106 | return setNodeType(self, node, Type::Nil); |
|
| 5064 | 5107 | }, |
|
| 5065 | 5108 | case ast::NodeValue::Undef => { |
| 5074 | 5117 | return setNodeType(self, node, Type::U8); |
|
| 5075 | 5118 | } |
|
| 5076 | 5119 | case ast::NodeValue::String(text) => { |
|
| 5077 | 5120 | setNodeConstValue(self, node, ConstValue::String(text)); |
|
| 5078 | 5121 | let byteTy = allocType(self, Type::U8); |
|
| 5079 | - | let sliceTy = allocType(self, Type::Slice(SliceType { |
|
| 5080 | - | class: types::PointerClass::Owned, |
|
| 5081 | - | item: byteTy, |
|
| 5082 | - | mutable: false, |
|
| 5083 | - | })); |
|
| 5122 | + | let sliceTy = allocType(self, sliceType(types::PointerClass::Owned, byteTy, false)); |
|
| 5084 | 5123 | return setNodeType(self, node, *sliceTy); |
|
| 5085 | 5124 | }, |
|
| 5086 | 5125 | case ast::NodeValue::Number(lit) => { |
|
| 5087 | 5126 | setNodeConstValue(self, node, ConstValue::Int(ConstInt { |
|
| 5088 | 5127 | magnitude: lit.magnitude, |
| 5253 | 5292 | }, |
|
| 5254 | 5293 | case ast::NodeValue::AddressOf(addr) => { |
|
| 5255 | 5294 | let ty = typeFor(self, node) else { |
|
| 5256 | 5295 | return false; |
|
| 5257 | 5296 | }; |
|
| 5258 | - | if let case Type::Slice(_) = ty { |
|
| 5297 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, .. }) = ty { |
|
| 5259 | 5298 | return isConstExpr(self, addr.target); |
|
| 5260 | 5299 | } |
|
| 5261 | 5300 | return false; |
|
| 5262 | 5301 | }, |
|
| 5263 | 5302 | case ast::NodeValue::RecordLit(lit) => { |
| 6414 | 6453 | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
|
| 6415 | 6454 | } |
|
| 6416 | 6455 | ||
| 6417 | 6456 | // Build the function type for the instance method. |
|
| 6418 | 6457 | // The receiver becomes the first parameter. |
|
| 6419 | - | let receiverPtrType = Type::Pointer(PointerType { |
|
| 6420 | - | class: receiverClass, |
|
| 6421 | - | target: allocType(self, concreteType), |
|
| 6422 | - | mutable: receiverMut, |
|
| 6423 | - | }); |
|
| 6458 | + | let receiverPtrType = pointerType(receiverClass, allocType(self, concreteType), receiverMut); |
|
| 6424 | 6459 | ||
| 6425 | 6460 | // Validate that the instance method's signature matches the |
|
| 6426 | 6461 | // trait method's signature exactly (params, return type, throws). |
|
| 6427 | 6462 | if sig.params.len <> expectedFn.paramTypes.len { |
|
| 6428 | 6463 | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
| 6615 | 6650 | attrs: ?ast::Attributes, |
|
| 6616 | 6651 | ) throws (ResolveError) { |
|
| 6617 | 6652 | // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a |
|
| 6618 | 6653 | // nominal type. |
|
| 6619 | 6654 | let fullReceiverTy = try infer(self, receiverType); |
|
| 6620 | - | let case Type::Pointer(receiver) = fullReceiverTy |
|
| 6621 | - | else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 6622 | - | let concreteType = *receiver.target; |
|
| 6655 | + | let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties: receiver, target }) = fullReceiverTy else { |
|
| 6656 | + | throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch); |
|
| 6657 | + | }; |
|
| 6658 | + | let concreteType = *target; |
|
| 6623 | 6659 | let case Type::Nominal(nominalTy) = concreteType |
|
| 6624 | 6660 | else throw emitError(self, receiverType, ErrorKind::ExpectedRecord); |
|
| 6625 | 6661 | try ensureNominalResolved(self, nominalTy, receiverType); |
|
| 6626 | 6662 | ||
| 6627 | 6663 | let methodName = try nodeName(self, name); |
| 6635 | 6671 | // Resolve parameter types. |
|
| 6636 | 6672 | let a = alloc::arenaAllocator(&mut self.arena); |
|
| 6637 | 6673 | let mut paramTypes = vec::Vec⟨*Type⟩ { data: &mut [], len: 0 }; |
|
| 6638 | 6674 | ||
| 6639 | 6675 | // Receiver is the first parameter. |
|
| 6640 | - | let receiverPtrType = Type::Pointer(PointerType { |
|
| 6641 | - | class: receiver.class, |
|
| 6642 | - | target: allocType(self, concreteType), |
|
| 6643 | - | mutable: receiver.mutable, |
|
| 6644 | - | }); |
|
| 6676 | + | let receiverPtrType = pointerType( |
|
| 6677 | + | receiver.class, allocType(self, concreteType), receiver.mutable |
|
| 6678 | + | ); |
|
| 6645 | 6679 | vec::append⟨*Type⟩( |
|
| 6646 | 6680 | &mut paramTypes, allocType(self, receiverPtrType), a |
|
| 6647 | 6681 | ); |
|
| 6648 | 6682 | ||
| 6649 | 6683 | for paramNode in sig.params { |
| 6957 | 6991 | pattern: *ast::Node, |
|
| 6958 | 6992 | scrutineeTy: Type, |
|
| 6959 | 6993 | mode: IdentMode, |
|
| 6960 | 6994 | matchBy: MatchBy |
|
| 6961 | 6995 | ) throws (ResolveError) { |
|
| 6962 | - | if let case Type::Pointer(pointer) = scrutineeTy; isDestructuringPattern(pattern) { |
|
| 6963 | - | try resolveCasePattern(self, pattern, *pointer.target, mode, matchBy); |
|
| 6996 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, target, .. }) = scrutineeTy; isDestructuringPattern(pattern) { |
|
| 6997 | + | try resolveCasePattern(self, pattern, *target, mode, matchBy); |
|
| 6964 | 6998 | return; |
|
| 6965 | 6999 | } |
|
| 6966 | 7000 | // TODO: Collapse these nested matches. |
|
| 6967 | 7001 | match scrutineeTy { |
|
| 6968 | 7002 | case Type::Nominal(info) => { |
| 6981 | 7015 | } else => {} |
|
| 6982 | 7016 | } |
|
| 6983 | 7017 | } else => {} |
|
| 6984 | 7018 | } |
|
| 6985 | 7019 | } |
|
| 6986 | - | case Type::Array(arrayInfo) => { |
|
| 7020 | + | case Type::Core(CoreType::Array { item, length }) => { |
|
| 6987 | 7021 | if let case ast::NodeValue::ArrayLit(items) = pattern.value { |
|
| 6988 | - | let length = concreteArrayLength(arrayInfo.length) |
|
| 6989 | - | else throw emitError(self, pattern, ErrorKind::ConstExprRequired); |
|
| 6990 | - | if items.len as u32 <> length { |
|
| 6991 | - | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch( |
|
| 6992 | - | CountMismatch { expected: length, actual: items.len as u32 } |
|
| 6993 | - | )); |
|
| 7022 | + | let lengthValue = concreteArrayLength(length) |
|
| 7023 | + | else throw emitError( |
|
| 7024 | + | self, pattern, ErrorKind::ConstExprRequired |
|
| 7025 | + | ); |
|
| 7026 | + | if items.len as u32 <> lengthValue { |
|
| 7027 | + | throw emitError( |
|
| 7028 | + | self, |
|
| 7029 | + | pattern, |
|
| 7030 | + | ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 7031 | + | expected: lengthValue, |
|
| 7032 | + | actual: items.len as u32, |
|
| 7033 | + | }), |
|
| 7034 | + | ); |
|
| 6994 | 7035 | } |
|
| 6995 | - | let elemTy = *arrayInfo.item; |
|
| 6996 | - | for item in items { |
|
| 6997 | - | try resolveCasePattern(self, item, elemTy, IdentMode::Bind, matchBy); |
|
| 7036 | + | let elemTy = *item; |
|
| 7037 | + | for child in items { |
|
| 7038 | + | try resolveCasePattern( |
|
| 7039 | + | self, child, elemTy, IdentMode::Bind, matchBy |
|
| 7040 | + | ); |
|
| 6998 | 7041 | } |
|
| 6999 | 7042 | setNodeType(self, pattern, scrutineeTy); |
|
| 7000 | 7043 | return; |
|
| 7001 | 7044 | } |
|
| 7002 | 7045 | } else => {} |
| 7063 | 7106 | } |
|
| 7064 | 7107 | } |
|
| 7065 | 7108 | // Extract item type and store pre-computed loop metadata for the lowerer. |
|
| 7066 | 7109 | let mut itemTy: Type = undefined; |
|
| 7067 | 7110 | match iterableTy { |
|
| 7068 | - | case Type::Slice(slice) => { |
|
| 7069 | - | set itemTy = *slice.item; |
|
| 7111 | + | case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, target, .. }) => { |
|
| 7112 | + | set itemTy = *target; |
|
| 7070 | 7113 | setForLoopInfo(self, node, ForLoopInfo::Collection { |
|
| 7071 | - | elemType: slice.item, length: nil, bindingName, indexName |
|
| 7114 | + | elemType: target, |
|
| 7115 | + | length: nil, |
|
| 7116 | + | bindingName, |
|
| 7117 | + | indexName, |
|
| 7072 | 7118 | }); |
|
| 7073 | 7119 | } |
|
| 7074 | 7120 | case Type::Range { start, .. } => { |
|
| 7075 | 7121 | // Iterable ranges must have a start, and since we enforce type |
|
| 7076 | 7122 | // equality for start and end, that is always the item type. |
| 7084 | 7130 | ||
| 7085 | 7131 | setForLoopInfo(self, node, ForLoopInfo::Range { |
|
| 7086 | 7132 | valType, range, bindingName, indexName |
|
| 7087 | 7133 | }); |
|
| 7088 | 7134 | } |
|
| 7089 | - | case Type::Array(arrayInfo) => { |
|
| 7090 | - | set itemTy = *arrayInfo.item; |
|
| 7135 | + | case Type::Core(CoreType::Array { item, length }) => { |
|
| 7136 | + | set itemTy = *item; |
|
| 7091 | 7137 | setForLoopInfo(self, node, ForLoopInfo::Collection { |
|
| 7092 | - | elemType: arrayInfo.item, |
|
| 7093 | - | length: arrayInfo.length, |
|
| 7138 | + | elemType: item, |
|
| 7139 | + | length, |
|
| 7094 | 7140 | bindingName, |
|
| 7095 | 7141 | indexName, |
|
| 7096 | 7142 | }); |
|
| 7097 | 7143 | } |
|
| 7098 | 7144 | else => throw emitError(self, forStmt.iterable, ErrorKind::ExpectedIterable), |
| 7283 | 7329 | throws (ResolveError) |
|
| 7284 | 7330 | { |
|
| 7285 | 7331 | let subjectTy = try infer(self, sw.subject); |
|
| 7286 | 7332 | let subject = unwrapMatchSubject(subjectTy); |
|
| 7287 | 7333 | ||
| 7288 | - | if let case Type::Optional(inner) = subject.effectiveTy { |
|
| 7289 | - | try resolveMatchOptional(self, node, sw, inner, subject.by); |
|
| 7290 | - | } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy { |
|
| 7334 | + | if let case Type::Core(CoreType::Optional { payload }) = |
|
| 7335 | + | subject.effectiveTy |
|
| 7336 | + | { |
|
| 7337 | + | try resolveMatchOptional(self, node, sw, payload, subject.by); |
|
| 7338 | + | } else if let case Type::Nominal(NominalType::Union(u)) = |
|
| 7339 | + | subject.effectiveTy |
|
| 7340 | + | { |
|
| 7291 | 7341 | try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by); |
|
| 7292 | 7342 | } else { |
|
| 7293 | 7343 | try resolveMatchGeneric(self, node, sw, subject.effectiveTy); |
|
| 7294 | 7344 | } |
|
| 7295 | 7345 |
| 7313 | 7363 | sw: ast::Match, |
|
| 7314 | 7364 | innerTy: *Type, |
|
| 7315 | 7365 | matchBy: MatchBy |
|
| 7316 | 7366 | ) -> Type throws (ResolveError) |
|
| 7317 | 7367 | { |
|
| 7318 | - | let subjectTy = Type::Optional(innerTy); |
|
| 7368 | + | let subjectTy = optionalType(innerTy); |
|
| 7319 | 7369 | let prongs = sw.prongs; |
|
| 7320 | 7370 | let mut hasValue = false; |
|
| 7321 | 7371 | let mut hasNil = false; |
|
| 7322 | 7372 | let mut catchAll = false; |
|
| 7323 | 7373 | let mut matchType = Type::Never; |
| 7533 | 7583 | ) -> Type throws (ResolveError) { |
|
| 7534 | 7584 | match prong.arm { |
|
| 7535 | 7585 | case ast::ProngArm::Binding(pat) => { |
|
| 7536 | 7586 | // For optionals, bind the unwrapped inner type. |
|
| 7537 | 7587 | let mut bindTy = subjectTy; |
|
| 7538 | - | if let case Type::Optional(inner) = subjectTy { |
|
| 7539 | - | set bindTy = *inner; |
|
| 7588 | + | if let case Type::Core(CoreType::Optional { payload }) = subjectTy { |
|
| 7589 | + | set bindTy = *payload; |
|
| 7540 | 7590 | } |
|
| 7541 | 7591 | try bindPatternVar(self, pat, bindTy, matchBy); |
|
| 7542 | 7592 | } |
|
| 7543 | 7593 | case ast::ProngArm::Case(patterns) => { |
|
| 7544 | 7594 | for pattern in patterns { |
| 7629 | 7679 | throws (ResolveError) |
|
| 7630 | 7680 | { |
|
| 7631 | 7681 | let mut bindTy = ty; |
|
| 7632 | 7682 | match matchBy { |
|
| 7633 | 7683 | case MatchBy::Value => {} |
|
| 7634 | - | case MatchBy::Ref => set bindTy = Type::Pointer(PointerType { |
|
| 7635 | - | class: types::PointerClass::Ref, |
|
| 7636 | - | target: allocType(self, ty), |
|
| 7637 | - | mutable: false, |
|
| 7638 | - | }), |
|
| 7639 | - | case MatchBy::MutRef => set bindTy = Type::Pointer(PointerType { |
|
| 7640 | - | class: types::PointerClass::Ref, |
|
| 7641 | - | target: allocType(self, ty), |
|
| 7642 | - | mutable: true, |
|
| 7643 | - | }), |
|
| 7684 | + | case MatchBy::Ref => set bindTy = pointerType(types::PointerClass::Ref, allocType(self, ty), false), |
|
| 7685 | + | case MatchBy::MutRef => set bindTy = pointerType(types::PointerClass::Ref, allocType(self, ty), true), |
|
| 7644 | 7686 | } |
|
| 7645 | 7687 | match binding.value { |
|
| 7646 | 7688 | case ast::NodeValue::Placeholder => { |
|
| 7647 | 7689 | // Nothing to do. |
|
| 7648 | 7690 | } |
| 7800 | 7842 | let exprTy = try infer(self, pat.scrutinee); |
|
| 7801 | 7843 | ||
| 7802 | 7844 | match pat.kind { |
|
| 7803 | 7845 | case ast::PatternKind::Binding => { |
|
| 7804 | 7846 | // Simple binding requires an optional expression. |
|
| 7805 | - | let case Type::Optional(inner) = exprTy else { |
|
| 7806 | - | throw emitError(self, pat.scrutinee, ErrorKind::ExpectedOptional); |
|
| 7847 | + | let case Type::Core(CoreType::Optional { payload }) = exprTy else { |
|
| 7848 | + | throw emitError( |
|
| 7849 | + | self, pat.scrutinee, ErrorKind::ExpectedOptional |
|
| 7850 | + | ); |
|
| 7807 | 7851 | }; |
|
| 7808 | - | let payloadTy = *inner; |
|
| 7852 | + | let payloadTy = *payload; |
|
| 7809 | 7853 | let _ = try bindValueIdent(self, pat.pattern, node, payloadTy, pat.mutable, 0, 0); |
|
| 7810 | 7854 | // The `else` branch supplies the binding when the optional is nil. |
|
| 7811 | 7855 | try checkAssignable(self, letElse.elseBranch, payloadTy); |
|
| 7812 | 7856 | ||
| 7813 | 7857 | return setNodeType(self, node, Type::Void); |
| 7853 | 7897 | expected: 2, |
|
| 7854 | 7898 | actual: args.len as u32, |
|
| 7855 | 7899 | })); |
|
| 7856 | 7900 | } |
|
| 7857 | 7901 | let ptrType = try visit(self, args[0], Type::Unknown); |
|
| 7858 | - | let case Type::Pointer(ptr) = ptrType else { |
|
| 7902 | + | let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = ptrType else { |
|
| 7859 | 7903 | throw emitError(self, node, ErrorKind::ExpectedPointer); |
|
| 7860 | 7904 | }; |
|
| 7861 | 7905 | let _ = try checkAssignable(self, args[1], Type::U32); |
|
| 7862 | - | return setNodeType(self, node, Type::Slice(SliceType { |
|
| 7863 | - | class: ptr.class, |
|
| 7864 | - | item: ptr.target, |
|
| 7865 | - | mutable: ptr.mutable, |
|
| 7866 | - | })); |
|
| 7906 | + | return setNodeType( |
|
| 7907 | + | self, |
|
| 7908 | + | node, |
|
| 7909 | + | sliceType(properties.class, target, properties.mutable), |
|
| 7910 | + | ); |
|
| 7867 | 7911 | } |
|
| 7868 | 7912 | if kind == ast::Builtin::Relocate { |
|
| 7869 | 7913 | if args.len <> 2 { |
|
| 7870 | 7914 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 7871 | 7915 | expected: 2, |
|
| 7872 | 7916 | actual: args.len as u32, |
|
| 7873 | 7917 | })); |
|
| 7874 | 7918 | } |
|
| 7875 | 7919 | let destinationType = try visit(self, args[0], Type::Unknown); |
|
| 7876 | - | let case Type::Slice(destination) = destinationType else { |
|
| 7920 | + | let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, properties: destination, target: item }) = destinationType else { |
|
| 7877 | 7921 | throw emitError(self, args[0], ErrorKind::ExpectedIndexable); |
|
| 7878 | 7922 | }; |
|
| 7879 | - | if not destination.mutable or not typesEqual(*destination.item, Type::U8) { |
|
| 7923 | + | if hasLayoutObstacle(*item, LayoutObstacle::Opaque) { |
|
| 7924 | + | throw emitError(self, args[0], ErrorKind::OpaqueTypeNotAllowed); |
|
| 7925 | + | } |
|
| 7926 | + | if not destination.mutable { |
|
| 7880 | 7927 | throw emitTypeMismatch(self, args[0], TypeMismatch { |
|
| 7881 | - | expected: Type::Slice(SliceType { |
|
| 7882 | - | class: destination.class, |
|
| 7883 | - | item: allocType(self, Type::U8), |
|
| 7884 | - | mutable: true, |
|
| 7885 | - | }), |
|
| 7928 | + | expected: sliceType(destination.class, item, true), |
|
| 7886 | 7929 | actual: destinationType, |
|
| 7887 | 7930 | }); |
|
| 7888 | 7931 | } |
|
| 7889 | 7932 | let sourceType = try visit(self, args[1], Type::Unknown); |
|
| 7890 | - | let case Type::Slice(source) = sourceType else { |
|
| 7933 | + | let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, properties: source, target: sourceItem }) = sourceType else { |
|
| 7891 | 7934 | throw emitError(self, args[1], ErrorKind::ExpectedIndexable); |
|
| 7892 | 7935 | }; |
|
| 7893 | - | if not source.mutable or not typesEqual(*source.item, Type::U8) { |
|
| 7936 | + | if not source.mutable or not typesEqual(*sourceItem, *item) { |
|
| 7894 | 7937 | throw emitTypeMismatch(self, args[1], TypeMismatch { |
|
| 7895 | - | expected: Type::Slice(SliceType { |
|
| 7896 | - | class: source.class, |
|
| 7897 | - | item: allocType(self, Type::U8), |
|
| 7898 | - | mutable: true, |
|
| 7899 | - | }), |
|
| 7938 | + | expected: sliceType(source.class, item, true), |
|
| 7900 | 7939 | actual: sourceType, |
|
| 7901 | 7940 | }); |
|
| 7902 | 7941 | } |
|
| 7903 | 7942 | return setNodeType(self, node, Type::Void); |
|
| 7904 | 7943 | } |
| 8012 | 8051 | } |
|
| 8013 | 8052 | if not containsGenericParameter(pattern) { |
|
| 8014 | 8053 | return true; |
|
| 8015 | 8054 | } |
|
| 8016 | 8055 | match pattern { |
|
| 8017 | - | case Type::Pointer(pointer) => { |
|
| 8018 | - | let case Type::Pointer(actualPointer) = actual else return false; |
|
| 8019 | - | return pointer.class == actualPointer.class |
|
| 8020 | - | and pointer.mutable == actualPointer.mutable |
|
| 8021 | - | and inferGenericArgument( |
|
| 8022 | - | self, |
|
| 8023 | - | *pointer.target, |
|
| 8024 | - | *actualPointer.target, |
|
| 8025 | - | params, |
|
| 8026 | - | inferred, |
|
| 8027 | - | ); |
|
| 8028 | - | } |
|
| 8029 | - | case Type::Slice(slice) => { |
|
| 8030 | - | let case Type::Slice(actualSlice) = actual else return false; |
|
| 8031 | - | return slice.class == actualSlice.class |
|
| 8032 | - | and slice.mutable == actualSlice.mutable |
|
| 8033 | - | and inferGenericArgument( |
|
| 8056 | + | case Type::Core(core) => { |
|
| 8057 | + | let case Type::Core(actualCore) = actual else return false; |
|
| 8058 | + | let placeholder = coreValueArgument(core); |
|
| 8059 | + | if not coreTypesEqual( |
|
| 8060 | + | coreWithArguments(core, placeholder, placeholder), |
|
| 8061 | + | coreWithArguments(actualCore, placeholder, placeholder), |
|
| 8062 | + | ) { return false; } |
|
| 8063 | + | if let patternConstant = coreConstantArgument(core) { |
|
| 8064 | + | let actualConstant = coreConstantArgument(actualCore) |
|
| 8065 | + | else return false; |
|
| 8066 | + | if not inferGenericArgument( |
|
| 8034 | 8067 | self, |
|
| 8035 | - | *slice.item, |
|
| 8036 | - | *actualSlice.item, |
|
| 8068 | + | *patternConstant, |
|
| 8069 | + | *actualConstant, |
|
| 8037 | 8070 | params, |
|
| 8038 | 8071 | inferred, |
|
| 8039 | - | ); |
|
| 8040 | - | } |
|
| 8041 | - | case Type::Optional(inner) => { |
|
| 8042 | - | let case Type::Optional(actualInner) = actual else return false; |
|
| 8043 | - | return inferGenericArgument( |
|
| 8044 | - | self, *inner, *actualInner, params, inferred |
|
| 8045 | - | ); |
|
| 8046 | - | } |
|
| 8047 | - | case Type::Array(array) => { |
|
| 8048 | - | let case Type::Array(actualArray) = actual else return false; |
|
| 8072 | + | ) { |
|
| 8073 | + | return false; |
|
| 8074 | + | } |
|
| 8075 | + | } |
|
| 8049 | 8076 | return inferGenericArgument( |
|
| 8050 | - | self, *array.length, *actualArray.length, params, inferred |
|
| 8051 | - | ) and inferGenericArgument( |
|
| 8052 | - | self, *array.item, *actualArray.item, params, inferred |
|
| 8077 | + | self, |
|
| 8078 | + | *coreValueArgument(core), |
|
| 8079 | + | *coreValueArgument(actualCore), |
|
| 8080 | + | params, |
|
| 8081 | + | inferred, |
|
| 8053 | 8082 | ); |
|
| 8054 | 8083 | } |
|
| 8055 | 8084 | case Type::GenericDataApply(application) => { |
|
| 8056 | 8085 | let case Type::Nominal(nominal) = actual else return false; |
|
| 8057 | 8086 | let concrete = genericDataSpecializationForNominal(self, nominal) |
| 8171 | 8200 | return nil; |
|
| 8172 | 8201 | } |
|
| 8173 | 8202 | let traitSym = try resolveNamePath(self, access.parent); |
|
| 8174 | 8203 | let case SymbolData::Trait(traitInfo) = traitSym.data else return nil; |
|
| 8175 | 8204 | let receiverTy = try infer(self, call.args[0]); |
|
| 8176 | - | let case Type::Pointer(receiver) = receiverTy else return nil; |
|
| 8177 | - | let case Type::Parameter(param) = *receiver.target else return nil; |
|
| 8205 | + | let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties: receiver, target }) = receiverTy else return nil; |
|
| 8206 | + | let case Type::Parameter(param) = *target else return nil; |
|
| 8178 | 8207 | let mut hasBound = false; |
|
| 8179 | 8208 | for bound in param.bounds { |
|
| 8180 | 8209 | if bound == traitInfo { |
|
| 8181 | 8210 | set hasBound = true; |
|
| 8182 | 8211 | break; |
| 8292 | 8321 | ); |
|
| 8293 | 8322 | let case Type::Fn(info) = calleeTy |
|
| 8294 | 8323 | else throw emitError(self, call.callee, ErrorKind::Internal); |
|
| 8295 | 8324 | if selected.method.mutable { |
|
| 8296 | 8325 | let mut isMutPtr = false; |
|
| 8297 | - | if let case Type::Pointer(pointer) = parentTy { |
|
| 8298 | - | set isMutPtr = pointer.mutable; |
|
| 8326 | + | if let properties = pointerProperties(parentTy) { |
|
| 8327 | + | set isMutPtr = properties.mutable; |
|
| 8299 | 8328 | } |
|
| 8300 | - | if not isMutPtr and not (try canBorrowMutFrom(self, access.parent)) { |
|
| 8329 | + | if not isMutPtr |
|
| 8330 | + | and not (try canBorrowMutFrom(self, access.parent)) |
|
| 8331 | + | { |
|
| 8301 | 8332 | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 8302 | 8333 | } |
|
| 8303 | 8334 | } |
|
| 8304 | 8335 | try checkUnsafeCall(self, call.callee, info); |
|
| 8305 | 8336 | try checkCallArgs(self, node, call, info, ctx); |
| 8317 | 8348 | if let case Type::TraitObject(traitObject) = subjectTy { |
|
| 8318 | 8349 | let methodName = try nodeName(self, access.child); |
|
| 8319 | 8350 | let method = findTraitMethod(traitObject.traitInfo, methodName) |
|
| 8320 | 8351 | else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName)); |
|
| 8321 | 8352 | // Reject mutable-receiver methods called on immutable trait objects. |
|
| 8322 | - | if method.mutable and not traitObject.mutable { |
|
| 8323 | - | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 8353 | + | if method.mutable and not traitObject.properties.mutable { |
|
| 8354 | + | throw emitError( |
|
| 8355 | + | self, access.parent, ErrorKind::ImmutableBinding |
|
| 8356 | + | ); |
|
| 8324 | 8357 | } |
|
| 8325 | 8358 | try checkCallArgs(self, node, call, method.fnType, ctx); |
|
| 8326 | 8359 | ||
| 8327 | 8360 | setTraitMethodCall(self, node, traitObject.traitInfo, method.index); |
|
| 8328 | 8361 | return setNodeType(self, node, *method.fnType.returnType); |
| 8335 | 8368 | // Reject mutable-receiver methods on immutable bindings. |
|
| 8336 | 8369 | // If the parent is already a mutable pointer, the receiver is fine. |
|
| 8337 | 8370 | // Otherwise, check that the parent can yield a mutable borrow. |
|
| 8338 | 8371 | if method.mutable { |
|
| 8339 | 8372 | let mut isMutPtr = false; |
|
| 8340 | - | if let case Type::Pointer(pointer) = parentTy { |
|
| 8341 | - | set isMutPtr = pointer.mutable; |
|
| 8373 | + | if let properties = pointerProperties(parentTy) { |
|
| 8374 | + | set isMutPtr = properties.mutable; |
|
| 8342 | 8375 | } |
|
| 8343 | - | if not isMutPtr and not (try canBorrowMutFrom(self, access.parent)) { |
|
| 8376 | + | if not isMutPtr |
|
| 8377 | + | and not (try canBorrowMutFrom(self, access.parent)) |
|
| 8378 | + | { |
|
| 8344 | 8379 | throw emitError(self, access.parent, ErrorKind::ImmutableBinding); |
|
| 8345 | 8380 | } |
|
| 8346 | 8381 | } |
|
| 8347 | 8382 | // Check arguments (excluding receiver). |
|
| 8348 | 8383 | try checkCallArgs(self, node, call, method.fnType, ctx); |
| 8382 | 8417 | try checkSliceRangeIndices(self, range); |
|
| 8383 | 8418 | ||
| 8384 | 8419 | let mut item: *Type = undefined; |
|
| 8385 | 8420 | let mut capacity: ?*Type = nil; |
|
| 8386 | 8421 | ||
| 8387 | - | if let case Type::Slice(slice) = subjectTy { |
|
| 8388 | - | if not slice.mutable { |
|
| 8389 | - | throw emitError(self, container, ErrorKind::ImmutableBinding); |
|
| 8422 | + | let case Type::Core(core) = subjectTy |
|
| 8423 | + | else throw emitError( |
|
| 8424 | + | self, container, ErrorKind::ExpectedIndexable |
|
| 8425 | + | ); |
|
| 8426 | + | match core { |
|
| 8427 | + | case CoreType::Pointer { shape: PointerShape::Slice, properties, target } => { |
|
| 8428 | + | if not properties.mutable { |
|
| 8429 | + | throw emitError( |
|
| 8430 | + | self, container, ErrorKind::ImmutableBinding |
|
| 8431 | + | ); |
|
| 8432 | + | } |
|
| 8433 | + | set item = target; |
|
| 8390 | 8434 | } |
|
| 8391 | - | set item = slice.item; |
|
| 8392 | - | } else { |
|
| 8393 | - | match subjectTy { |
|
| 8394 | - | case Type::Array(a) => { |
|
| 8395 | - | if let length = concreteArrayLength(a.length) { |
|
| 8396 | - | try validateArraySliceBounds(self, range, length, node); |
|
| 8397 | - | } |
|
| 8398 | - | set item = a.item; |
|
| 8399 | - | set capacity = a.length; |
|
| 8435 | + | case CoreType::Array { item: arrayItem, length } => { |
|
| 8436 | + | if let value = concreteArrayLength(length) { |
|
| 8437 | + | try validateArraySliceBounds( |
|
| 8438 | + | self, range, value, node |
|
| 8439 | + | ); |
|
| 8400 | 8440 | } |
|
| 8401 | - | else => throw emitError(self, container, ErrorKind::ExpectedIndexable), |
|
| 8441 | + | set item = arrayItem; |
|
| 8442 | + | set capacity = length; |
|
| 8402 | 8443 | } |
|
| 8444 | + | else => throw emitError( |
|
| 8445 | + | self, container, ErrorKind::ExpectedIndexable |
|
| 8446 | + | ), |
|
| 8403 | 8447 | } |
|
| 8404 | 8448 | // RHS is either a fill value or a source slice. |
|
| 8405 | 8449 | let rhsTy = try infer(self, assign.right); |
|
| 8406 | - | if let case Type::Slice(source) = rhsTy { |
|
| 8407 | - | if *source.item <> *item { |
|
| 8450 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, target, .. }) = rhsTy { |
|
| 8451 | + | if not typesEqual(*target, *item) { |
|
| 8408 | 8452 | throw emitTypeMismatch( |
|
| 8409 | 8453 | self, |
|
| 8410 | 8454 | assign.right, |
|
| 8411 | - | TypeMismatch { expected: *item, actual: *source.item }, |
|
| 8455 | + | TypeMismatch { expected: *item, actual: *target }, |
|
| 8412 | 8456 | ); |
|
| 8413 | 8457 | } |
|
| 8414 | 8458 | } else { |
|
| 8415 | 8459 | try checkAssignable(self, assign.right, *item); |
|
| 8416 | 8460 | } |
|
| 8417 | - | setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity }); |
|
| 8461 | + | setSliceRangeInfo(self, node, SliceRangeInfo { |
|
| 8462 | + | itemType: item, |
|
| 8463 | + | mutable: true, |
|
| 8464 | + | capacity, |
|
| 8465 | + | }); |
|
| 8418 | 8466 | setNodeType(self, assign.left, *item); |
|
| 8419 | 8467 | ||
| 8420 | 8468 | return setNodeType(self, node, Type::Void); |
|
| 8421 | 8469 | } |
|
| 8422 | 8470 | } |
| 8508 | 8556 | if isUnsafePointerType(containerTy) { |
|
| 8509 | 8557 | try requireUnsafe(self, container); |
|
| 8510 | 8558 | } |
|
| 8511 | 8559 | try checkIndex(self, indexNode); |
|
| 8512 | 8560 | let subjectTy = autoDeref(containerTy); |
|
| 8513 | - | if let case Type::Slice(slice) = subjectTy { |
|
| 8514 | - | return setNodeType(self, node, *slice.item); |
|
| 8515 | - | } |
|
| 8516 | - | ||
| 8517 | - | match subjectTy { |
|
| 8518 | - | case Type::Array(arrayInfo) => { |
|
| 8519 | - | return setNodeType(self, node, *arrayInfo.item); |
|
| 8520 | - | } |
|
| 8521 | - | else => { |
|
| 8522 | - | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
|
| 8561 | + | if let case Type::Core(core) = subjectTy { |
|
| 8562 | + | match core { |
|
| 8563 | + | case CoreType::Pointer { shape: PointerShape::Slice, target, .. } => |
|
| 8564 | + | return setNodeType(self, node, *target), |
|
| 8565 | + | case CoreType::Array { item, .. } => |
|
| 8566 | + | return setNodeType(self, node, *item), |
|
| 8567 | + | else => {} |
|
| 8523 | 8568 | } |
|
| 8524 | 8569 | } |
|
| 8570 | + | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
|
| 8525 | 8571 | } |
|
| 8526 | 8572 | ||
| 8527 | 8573 | /// Find a record field by name. |
|
| 8528 | 8574 | fn findRecordField(s: *RecordType, fieldName: *[u8]) -> ?u32 { |
|
| 8529 | 8575 | for field, i in s.fields { |
| 8689 | 8735 | fn resolveAnonRecordLit(self: *mut Resolver, node: *ast::Node, lit: ast::RecordLit, hint: Type) -> Type |
|
| 8690 | 8736 | throws (ResolveError) |
|
| 8691 | 8737 | { |
|
| 8692 | 8738 | // Unwrap optional hint to get the inner record type. |
|
| 8693 | 8739 | let mut innerHint = hint; |
|
| 8694 | - | if let case Type::Optional(inner) = hint { |
|
| 8695 | - | set innerHint = *inner; |
|
| 8740 | + | if let case Type::Core(CoreType::Optional { payload }) = hint { |
|
| 8741 | + | set innerHint = *payload; |
|
| 8696 | 8742 | } |
|
| 8697 | 8743 | let mut hintInfo: ?RecordType = nil; |
|
| 8698 | 8744 | if let case Type::Nominal(info) = innerHint { |
|
| 8699 | 8745 | try ensureNominalResolved(self, info, node); |
|
| 8700 | 8746 | if let case NominalType::Record(s) = *info { |
| 8741 | 8787 | setNodeType(self, fieldNode, fieldType); |
|
| 8742 | 8788 | } |
|
| 8743 | 8789 | return setNodeType(self, node, innerHint); |
|
| 8744 | 8790 | } |
|
| 8745 | 8791 | ||
| 8792 | + | /// Return an array element type supplied directly or through an optional hint. |
|
| 8793 | + | fn arrayItemFromHint(hint: Type) -> ?Type { |
|
| 8794 | + | let mut candidate = hint; |
|
| 8795 | + | if let case Type::Core(CoreType::Optional { payload }) = hint { |
|
| 8796 | + | set candidate = *payload; |
|
| 8797 | + | } |
|
| 8798 | + | if let case Type::Core(CoreType::Array { item, .. }) = candidate { |
|
| 8799 | + | return *item; |
|
| 8800 | + | } |
|
| 8801 | + | return nil; |
|
| 8802 | + | } |
|
| 8803 | + | ||
| 8746 | 8804 | /// Analyze an array literal expression. |
|
| 8747 | 8805 | fn resolveArrayLit(self: *mut Resolver, node: *ast::Node, items: *mut [*ast::Node], hint: Type) -> Type |
|
| 8748 | 8806 | throws (ResolveError) |
|
| 8749 | 8807 | { |
|
| 8750 | 8808 | let length = items.len; |
|
| 8751 | 8809 | let mut expectedTy: Type = Type::Unknown; |
|
| 8752 | 8810 | ||
| 8753 | - | if let case Type::Array(ary) = hint { |
|
| 8754 | - | set expectedTy = *ary.item; |
|
| 8755 | - | } else if let case Type::Optional(inner) = hint { |
|
| 8756 | - | if let case Type::Array(ary) = *inner { |
|
| 8757 | - | set expectedTy = *ary.item; |
|
| 8758 | - | } |
|
| 8759 | - | }; |
|
| 8811 | + | if let item = arrayItemFromHint(hint) { |
|
| 8812 | + | set expectedTy = item; |
|
| 8813 | + | } |
|
| 8760 | 8814 | for itemNode in items { |
|
| 8761 | 8815 | let itemTy = try visit(self, itemNode, expectedTy); |
|
| 8762 | 8816 | assert itemTy <> Type::Unknown; |
|
| 8763 | 8817 | ||
| 8764 | 8818 | // Set the expected type to the first type we encounter. |
| 8769 | 8823 | } |
|
| 8770 | 8824 | } |
|
| 8771 | 8825 | if expectedTy == Type::Unknown { |
|
| 8772 | 8826 | throw emitError(self, node, ErrorKind::CannotInferType); |
|
| 8773 | 8827 | }; |
|
| 8774 | - | let arrayTy = Type::Array(ArrayType { |
|
| 8775 | - | item: allocType(self, expectedTy), |
|
| 8776 | - | length: allocConcreteArrayLength(self, length), |
|
| 8777 | - | }); |
|
| 8828 | + | let arrayTy = arrayType(allocType(self, expectedTy), allocConcreteArrayLength(self, length)); |
|
| 8778 | 8829 | return setNodeType(self, node, arrayTy); |
|
| 8779 | 8830 | } |
|
| 8780 | 8831 | ||
| 8781 | 8832 | /// Analyze an array repeat literal expression. |
|
| 8782 | 8833 | fn resolveArrayRepeat(self: *mut Resolver, node: *ast::Node, lit: ast::ArrayRepeatLit, hint: Type) -> Type |
|
| 8783 | 8834 | throws (ResolveError) |
|
| 8784 | 8835 | { |
|
| 8785 | 8836 | let mut itemHint = hint; |
|
| 8786 | - | if let case Type::Array(ary) = hint { |
|
| 8787 | - | set itemHint = *ary.item; |
|
| 8788 | - | } else if let case Type::Optional(inner) = hint { |
|
| 8789 | - | if let case Type::Array(ary) = *inner { |
|
| 8790 | - | set itemHint = *ary.item; |
|
| 8791 | - | } |
|
| 8837 | + | if let item = arrayItemFromHint(hint) { |
|
| 8838 | + | set itemHint = item; |
|
| 8792 | 8839 | } |
|
| 8793 | 8840 | let valueTy = try visit(self, lit.item, itemHint); |
|
| 8794 | 8841 | let _ = try checkNumeric(self, lit.count); |
|
| 8795 | 8842 | let mut length: *Type = undefined; |
|
| 8796 | 8843 | if let value = constValueEntry(self, lit.count) { |
| 8805 | 8852 | { |
|
| 8806 | 8853 | set length = allocSymbolicArrayLength(self, lit.count); |
|
| 8807 | 8854 | } else { |
|
| 8808 | 8855 | throw emitError(self, lit.count, ErrorKind::ConstExprRequired); |
|
| 8809 | 8856 | } |
|
| 8810 | - | return setNodeType(self, node, Type::Array(ArrayType { |
|
| 8811 | - | item: allocType(self, valueTy), |
|
| 8857 | + | return setNodeType(self, node, arrayType( |
|
| 8858 | + | allocType(self, valueTy), |
|
| 8812 | 8859 | length, |
|
| 8813 | - | })); |
|
| 8860 | + | )); |
|
| 8814 | 8861 | } |
|
| 8815 | 8862 | ||
| 8816 | 8863 | /// Resolve union variant access. |
|
| 8817 | 8864 | fn resolveUnionVariantAccess( |
|
| 8818 | 8865 | self: *mut Resolver, |
| 8936 | 8983 | let parentTy = try infer(self, access.parent); |
|
| 8937 | 8984 | if isUnsafePointerType(parentTy) { |
|
| 8938 | 8985 | try requireUnsafe(self, access.parent); |
|
| 8939 | 8986 | } |
|
| 8940 | 8987 | let subjectTy = autoDeref(parentTy); |
|
| 8941 | - | if let case Type::Slice(slice) = subjectTy { |
|
| 8988 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, properties, target }) = subjectTy { |
|
| 8942 | 8989 | let fieldNode = access.child; |
|
| 8943 | 8990 | let fieldName = try nodeName(self, fieldNode); |
|
| 8944 | 8991 | if mem::eq(fieldName, PTR_FIELD) { |
|
| 8945 | 8992 | setRecordFieldIndex(self, fieldNode, 0); |
|
| 8946 | 8993 | return setNodeType( |
|
| 8947 | 8994 | self, |
|
| 8948 | 8995 | node, |
|
| 8949 | - | Type::Pointer(PointerType { |
|
| 8950 | - | class: slice.class, |
|
| 8951 | - | target: slice.item, |
|
| 8952 | - | mutable: slice.mutable, |
|
| 8953 | - | }), |
|
| 8996 | + | pointerType(properties.class, target, properties.mutable), |
|
| 8954 | 8997 | ); |
|
| 8955 | 8998 | } |
|
| 8956 | 8999 | if mem::eq(fieldName, LEN_FIELD) { |
|
| 8957 | 9000 | setRecordFieldIndex(self, fieldNode, 1); |
|
| 8958 | 9001 | return setNodeType(self, node, Type::U32); |
| 9022 | 9065 | if let method = findMethod(self, subjectTy, fieldName) { |
|
| 9023 | 9066 | return setNodeType(self, node, Type::Fn(method.fnType)); |
|
| 9024 | 9067 | } |
|
| 9025 | 9068 | throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName)); |
|
| 9026 | 9069 | } |
|
| 9027 | - | case Type::Array(arrayInfo) => { |
|
| 9070 | + | case Type::Core(CoreType::Array { length, .. }) => { |
|
| 9028 | 9071 | let fieldNode = access.child; |
|
| 9029 | 9072 | let fieldName = try nodeName(self, fieldNode); |
|
| 9030 | 9073 | ||
| 9031 | 9074 | if mem::eq(fieldName, LEN_FIELD) { |
|
| 9032 | - | if let length = concreteArrayLength(arrayInfo.length) { |
|
| 9075 | + | if let value = concreteArrayLength(length) { |
|
| 9033 | 9076 | setNodeConstValue( |
|
| 9034 | - | self, node, constInt(length as u64, 32, false, false) |
|
| 9077 | + | self, node, constInt(value as u64, 32, false, false) |
|
| 9035 | 9078 | ); |
|
| 9036 | 9079 | } |
|
| 9037 | 9080 | return setNodeType(self, node, Type::U32); |
|
| 9038 | 9081 | } |
|
| 9039 | - | throw emitError(self, node, ErrorKind::ArrayFieldUnknown(fieldName)); |
|
| 9082 | + | throw emitError( |
|
| 9083 | + | self, node, ErrorKind::ArrayFieldUnknown(fieldName) |
|
| 9084 | + | ); |
|
| 9040 | 9085 | } |
|
| 9041 | 9086 | else => { |
|
| 9042 | 9087 | // Check for standalone methods on any nominal type (e.g. unions). |
|
| 9043 | 9088 | if let case Type::Nominal(_) = subjectTy { |
|
| 9044 | 9089 | let fieldName = try nodeName(self, access.child); |
| 9065 | 9110 | if mutable { |
|
| 9066 | 9111 | return true; |
|
| 9067 | 9112 | } |
|
| 9068 | 9113 | // Check if the type is a mutable pointer or slice. |
|
| 9069 | 9114 | let ty = typeFor(self, node) else return false; |
|
| 9070 | - | if let case Type::Pointer(pointer) = ty { |
|
| 9071 | - | return pointer.mutable; |
|
| 9072 | - | } |
|
| 9073 | - | if let case Type::Slice(slice) = ty { |
|
| 9074 | - | return slice.mutable; |
|
| 9075 | - | } |
|
| 9076 | - | return false; |
|
| 9115 | + | let properties = pointerProperties(ty) else return false; |
|
| 9116 | + | return properties.mutable; |
|
| 9077 | 9117 | } |
|
| 9078 | 9118 | case ast::NodeValue::FieldAccess(access) => { |
|
| 9079 | 9119 | let _ = try infer(self, access.parent); |
|
| 9080 | 9120 | return try canBorrowMutFrom(self, access.parent); |
|
| 9081 | 9121 | } |
| 9094 | 9134 | case ast::NodeValue::Subscript { container, .. } => { |
|
| 9095 | 9135 | let containerTy = try infer(self, container); |
|
| 9096 | 9136 | // Subscript auto-derefs pointers, so check the actual indexed type. |
|
| 9097 | 9137 | let subjectTy = autoDeref(containerTy); |
|
| 9098 | 9138 | ||
| 9099 | - | if let case Type::Slice(slice) = subjectTy { |
|
| 9100 | - | return slice.mutable; |
|
| 9101 | - | } |
|
| 9102 | - | if let case Type::Array(_) = subjectTy { |
|
| 9103 | - | return try canBorrowMutFrom(self, container); |
|
| 9139 | + | if let case Type::Core(core) = subjectTy { |
|
| 9140 | + | match core { |
|
| 9141 | + | case CoreType::Pointer { shape: PointerShape::Slice, properties, .. } => |
|
| 9142 | + | return properties.mutable, |
|
| 9143 | + | case CoreType::Array { .. } => |
|
| 9144 | + | return try canBorrowMutFrom(self, container), |
|
| 9145 | + | else => {} |
|
| 9146 | + | } |
|
| 9104 | 9147 | } |
|
| 9105 | 9148 | return false; |
|
| 9106 | 9149 | } |
|
| 9107 | 9150 | case ast::NodeValue::ArrayLit(_), |
|
| 9108 | 9151 | ast::NodeValue::ArrayRepeatLit(_) => |
| 9111 | 9154 | } |
|
| 9112 | 9155 | case ast::NodeValue::Call(_) => { |
|
| 9113 | 9156 | // A call returning `*mut T` (or `&mut [T]`) yields a |
|
| 9114 | 9157 | // mutable place. Non-pointer returns cannot be mutably borrowed. |
|
| 9115 | 9158 | let ty = try infer(self, node); |
|
| 9116 | - | if let case Type::Pointer(pointer) = ty { |
|
| 9117 | - | return pointer.mutable; |
|
| 9118 | - | } |
|
| 9119 | - | if let case Type::Slice(slice) = ty { |
|
| 9120 | - | return slice.mutable; |
|
| 9121 | - | } |
|
| 9122 | - | return false; |
|
| 9159 | + | let properties = pointerProperties(ty) else return false; |
|
| 9160 | + | return properties.mutable; |
|
| 9123 | 9161 | } |
|
| 9124 | 9162 | case ast::NodeValue::Deref(inner) => { |
|
| 9125 | 9163 | let innerTy = try infer(self, inner); |
|
| 9126 | 9164 | ||
| 9127 | - | if let case Type::Pointer(pointer) = innerTy { |
|
| 9128 | - | return pointer.mutable; |
|
| 9129 | - | } |
|
| 9130 | - | if let case Type::Slice(slice) = innerTy { |
|
| 9131 | - | return slice.mutable; |
|
| 9165 | + | if let properties = pointerProperties(innerTy) { |
|
| 9166 | + | return properties.mutable; |
|
| 9132 | 9167 | } |
|
| 9133 | 9168 | // Record deref: mutability depends on the inner binding. |
|
| 9134 | 9169 | if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy { |
|
| 9135 | 9170 | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 9136 | 9171 | return try canBorrowMutFrom(self, inner); |
| 9165 | 9200 | try checkSliceRangeIndices(self, range); |
|
| 9166 | 9201 | ||
| 9167 | 9202 | let mut item: *Type = undefined; |
|
| 9168 | 9203 | let mut capacity: ?*Type = nil; |
|
| 9169 | 9204 | ||
| 9170 | - | if let case Type::Slice(slice) = subjectTy { |
|
| 9171 | - | if addr.mutable and not slice.mutable { |
|
| 9172 | - | throw emitError(self, addr.target, ErrorKind::ImmutableBinding); |
|
| 9173 | - | } |
|
| 9174 | - | set item = slice.item; |
|
| 9175 | - | } else { |
|
| 9176 | - | match subjectTy { |
|
| 9177 | - | case Type::Array(arrayInfo) => { |
|
| 9178 | - | if let length = concreteArrayLength(arrayInfo.length) { |
|
| 9179 | - | try validateArraySliceBounds(self, range, length, node); |
|
| 9180 | - | } |
|
| 9181 | - | set item = arrayInfo.item; |
|
| 9182 | - | set capacity = arrayInfo.length; |
|
| 9205 | + | let case Type::Core(core) = subjectTy |
|
| 9206 | + | else throw emitError( |
|
| 9207 | + | self, container, ErrorKind::ExpectedIndexable |
|
| 9208 | + | ); |
|
| 9209 | + | match core { |
|
| 9210 | + | case CoreType::Pointer { shape: PointerShape::Slice, properties, target } => { |
|
| 9211 | + | if addr.mutable and not properties.mutable { |
|
| 9212 | + | throw emitError( |
|
| 9213 | + | self, addr.target, ErrorKind::ImmutableBinding |
|
| 9214 | + | ); |
|
| 9183 | 9215 | } |
|
| 9184 | - | else => { |
|
| 9185 | - | throw emitError(self, container, ErrorKind::ExpectedIndexable); |
|
| 9216 | + | set item = target; |
|
| 9217 | + | } |
|
| 9218 | + | case CoreType::Array { item: arrayItem, length } => { |
|
| 9219 | + | if let value = concreteArrayLength(length) { |
|
| 9220 | + | try validateArraySliceBounds( |
|
| 9221 | + | self, range, value, node |
|
| 9222 | + | ); |
|
| 9186 | 9223 | } |
|
| 9224 | + | set item = arrayItem; |
|
| 9225 | + | set capacity = length; |
|
| 9187 | 9226 | } |
|
| 9227 | + | else => throw emitError( |
|
| 9228 | + | self, container, ErrorKind::ExpectedIndexable |
|
| 9229 | + | ), |
|
| 9188 | 9230 | } |
|
| 9189 | - | let sliceTy = Type::Slice(SliceType { |
|
| 9190 | - | class, |
|
| 9191 | - | item, |
|
| 9192 | - | mutable: addr.mutable, |
|
| 9193 | - | }); |
|
| 9231 | + | let sliceTy = sliceType(class, item, addr.mutable); |
|
| 9194 | 9232 | let alloc = allocType(self, sliceTy); |
|
| 9195 | 9233 | setSliceRangeInfo(self, node, SliceRangeInfo { |
|
| 9196 | 9234 | itemType: item, |
|
| 9197 | 9235 | mutable: addr.mutable, |
|
| 9198 | 9236 | capacity, |
| 9201 | 9239 | return setNodeType(self, node, *alloc); |
|
| 9202 | 9240 | } |
|
| 9203 | 9241 | } |
|
| 9204 | 9242 | // Derive a hint for the target type from the slice hint. |
|
| 9205 | 9243 | let mut targetHint: Type = Type::Unknown; |
|
| 9206 | - | if let case Type::Slice(slice) = hint { |
|
| 9207 | - | set targetHint = Type::Array(ArrayType { |
|
| 9208 | - | item: slice.item, |
|
| 9209 | - | length: allocConcreteArrayLength(self, 0), |
|
| 9210 | - | }); |
|
| 9244 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, target, .. }) = hint { |
|
| 9245 | + | set targetHint = arrayType( |
|
| 9246 | + | target, allocConcreteArrayLength(self, 0) |
|
| 9247 | + | ); |
|
| 9211 | 9248 | } |
|
| 9212 | 9249 | let targetTy = try visit(self, addr.target, targetHint); |
|
| 9213 | 9250 | ||
| 9214 | 9251 | // Mark local variable symbols as address-taken so the lowerer |
|
| 9215 | 9252 | // allocates a stack slot eagerly. |
| 9222 | 9259 | else => {} |
|
| 9223 | 9260 | } |
|
| 9224 | 9261 | } |
|
| 9225 | 9262 | } |
|
| 9226 | 9263 | ||
| 9227 | - | if let case Type::Array(arrayInfo) = targetTy { |
|
| 9264 | + | if let case Type::Core(CoreType::Array { item, .. }) = targetTy { |
|
| 9228 | 9265 | match addr.target.value { |
|
| 9229 | 9266 | case ast::NodeValue::ArrayLit(_), |
|
| 9230 | 9267 | ast::NodeValue::ArrayRepeatLit(_) => |
|
| 9231 | 9268 | { |
|
| 9232 | - | let sliceTy = Type::Slice(SliceType { |
|
| 9233 | - | class, |
|
| 9234 | - | item: arrayInfo.item, |
|
| 9235 | - | mutable: addr.mutable, |
|
| 9236 | - | }); |
|
| 9269 | + | let sliceTy = sliceType(class, item, addr.mutable); |
|
| 9237 | 9270 | return setNodeType(self, node, *allocType(self, sliceTy)); |
|
| 9238 | 9271 | } |
|
| 9239 | 9272 | else => {} |
|
| 9240 | 9273 | } |
|
| 9241 | 9274 | } |
|
| 9242 | - | let pointerTy = Type::Pointer(PointerType { |
|
| 9275 | + | let pointerTy = pointerType( |
|
| 9243 | 9276 | class, |
|
| 9244 | - | target: allocType(self, targetTy), |
|
| 9245 | - | mutable: addr.mutable, |
|
| 9246 | - | }); |
|
| 9277 | + | allocType(self, targetTy), |
|
| 9278 | + | addr.mutable, |
|
| 9279 | + | ); |
|
| 9247 | 9280 | return setNodeType(self, node, pointerTy); |
|
| 9248 | 9281 | } |
|
| 9249 | 9282 | ||
| 9250 | 9283 | /// Analyze a dereference expression. |
|
| 9251 | 9284 | fn resolveDeref(self: *mut Resolver, node: *ast::Node, targetNode: *ast::Node, hint: Type) -> Type |
|
| 9252 | 9285 | throws (ResolveError) |
|
| 9253 | 9286 | { |
|
| 9254 | 9287 | let operandTy = try visit(self, targetNode, hint); |
|
| 9255 | - | if let case Type::Pointer(pointer) = operandTy { |
|
| 9256 | - | if pointer.class == types::PointerClass::Unsafe { |
|
| 9288 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = operandTy { |
|
| 9289 | + | if properties.class == types::PointerClass::Unsafe { |
|
| 9257 | 9290 | try requireUnsafe(self, targetNode); |
|
| 9258 | 9291 | } |
|
| 9259 | 9292 | // Disallow dereferencing opaque pointers. |
|
| 9260 | - | if *pointer.target == Type::Opaque { |
|
| 9261 | - | throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref); |
|
| 9293 | + | if *target == Type::Opaque { |
|
| 9294 | + | throw emitError(self, node, ErrorKind::OpaqueTypeDeref); |
|
| 9262 | 9295 | } |
|
| 9263 | - | return setNodeType(self, node, *pointer.target); |
|
| 9296 | + | return setNodeType(self, node, *target); |
|
| 9264 | 9297 | } |
|
| 9265 | 9298 | // Auto-deref for single-field unlabeled records. |
|
| 9266 | 9299 | if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy { |
|
| 9267 | 9300 | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 9268 | 9301 | let fieldTy = recInfo.fields[0].fieldType; |
| 9271 | 9304 | } |
|
| 9272 | 9305 | } |
|
| 9273 | 9306 | throw emitError(self, targetNode, ErrorKind::ExpectedPointer); |
|
| 9274 | 9307 | } |
|
| 9275 | 9308 | ||
| 9276 | - | /// Check if a type is a pointer to opaque. |
|
| 9277 | - | fn isOpaquePointer(ty: Type) -> bool { |
|
| 9278 | - | if let case Type::Pointer(pointer) = ty { |
|
| 9279 | - | return *pointer.target == Type::Opaque; |
|
| 9280 | - | } |
|
| 9281 | - | return false; |
|
| 9282 | - | } |
|
| 9283 | - | ||
| 9284 | - | /// Check if a type is an opaque slice. |
|
| 9285 | - | fn isOpaqueSlice(ty: Type) -> bool { |
|
| 9286 | - | if let case Type::Slice(slice) = ty { |
|
| 9287 | - | return *slice.item == Type::Opaque; |
|
| 9288 | - | } |
|
| 9289 | - | return false; |
|
| 9290 | - | } |
|
| 9291 | - | ||
| 9292 | - | /// Check if an `as` cast between two types is valid. |
|
| 9293 | - | fn isValidCast(source: Type, target: Type) -> bool { |
|
| 9309 | + | /// Check if an `as` cast is valid. |
|
| 9310 | + | fn isValidCast(source: Type, target: Type, allowRefToUnsafe: bool) -> bool { |
|
| 9294 | 9311 | // Allow identity casts. |
|
| 9295 | - | if source == target { |
|
| 9312 | + | if typesEqual(source, target) { |
|
| 9296 | 9313 | return true; |
|
| 9297 | 9314 | } |
|
| 9298 | 9315 | // Allow numeric to numeric. |
|
| 9299 | 9316 | if isNumericType(source) and isNumericType(target) { |
|
| 9300 | 9317 | return true; |
| 9302 | 9319 | // Allow `void` union to numeric. |
|
| 9303 | 9320 | // TODO: Check that variant index fits in target type. |
|
| 9304 | 9321 | if isVoidUnion(source) and isNumericType(target) { |
|
| 9305 | 9322 | return true; |
|
| 9306 | 9323 | } |
|
| 9307 | - | // Allow address to numeric. |
|
| 9308 | - | if let case Type::Slice(_) = source { |
|
| 9309 | - | // Disallow slice to numeric; slices are fat pointers. |
|
| 9310 | - | } else if isAddressType(source) and isNumericType(target) { |
|
| 9324 | + | // Thin pointers and function addresses can be cast to numeric values. |
|
| 9325 | + | if isAddressType(source) and isNumericType(target) { |
|
| 9326 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Slice, .. }) = source { |
|
| 9327 | + | return false; |
|
| 9328 | + | } |
|
| 9311 | 9329 | return true; |
|
| 9312 | 9330 | } |
|
| 9313 | - | // Allow pointer casts if one side is `*opaque` or target types are castable. |
|
| 9314 | - | if let case Type::Pointer(sourcePointer) = source { |
|
| 9315 | - | if let case Type::Pointer(targetPointer) = target { |
|
| 9316 | - | if sourcePointer.class <> targetPointer.class { |
|
| 9317 | - | return false; |
|
| 9318 | - | } |
|
| 9319 | - | if targetPointer.mutable and not sourcePointer.mutable { |
|
| 9320 | - | return false; |
|
| 9321 | - | } |
|
| 9322 | - | if isOpaquePointer(source) or isOpaquePointer(target) { |
|
| 9323 | - | return true; |
|
| 9324 | - | } |
|
| 9325 | - | return isValidCast(*sourcePointer.target, *targetPointer.target); |
|
| 9326 | - | } |
|
| 9331 | + | let case Type::Core(CoreType::Pointer { shape: sourceShape, properties: sourceProperties, target: sourceTarget }) = source else return false; |
|
| 9332 | + | let case Type::Core(CoreType::Pointer { shape: targetShape, properties: targetProperties, target: targetTarget }) = target else return false; |
|
| 9333 | + | let classCompatible = sourceProperties.class == targetProperties.class |
|
| 9334 | + | or (allowRefToUnsafe |
|
| 9335 | + | and sourceProperties.class == types::PointerClass::Ref |
|
| 9336 | + | and targetProperties.class == types::PointerClass::Unsafe); |
|
| 9337 | + | if sourceShape <> targetShape |
|
| 9338 | + | or not classCompatible |
|
| 9339 | + | or (targetProperties.mutable and not sourceProperties.mutable) |
|
| 9340 | + | { |
|
| 9341 | + | return false; |
|
| 9327 | 9342 | } |
|
| 9328 | - | // Allow slice casts if one side is `*[opaque]`, target is `*[u8]`, |
|
| 9329 | - | // or element types are castable. |
|
| 9330 | - | if let case Type::Slice(sourceSlice) = source { |
|
| 9331 | - | if let case Type::Slice(targetSlice) = target { |
|
| 9332 | - | if sourceSlice.class <> targetSlice.class { |
|
| 9333 | - | return false; |
|
| 9334 | - | } |
|
| 9335 | - | if targetSlice.mutable and not sourceSlice.mutable { |
|
| 9336 | - | return false; |
|
| 9337 | - | } |
|
| 9338 | - | if isOpaqueSlice(source) or isOpaqueSlice(target) { |
|
| 9339 | - | return true; |
|
| 9340 | - | } |
|
| 9341 | - | if *targetSlice.item == Type::U8 { |
|
| 9342 | - | return true; |
|
| 9343 | - | } |
|
| 9344 | - | return isValidCast(*sourceSlice.item, *targetSlice.item); |
|
| 9345 | - | } |
|
| 9343 | + | if *sourceTarget == Type::Opaque or *targetTarget == Type::Opaque { |
|
| 9344 | + | return true; |
|
| 9345 | + | } |
|
| 9346 | + | match sourceShape { |
|
| 9347 | + | // Slices additionally allow casts to a byte target. |
|
| 9348 | + | case PointerShape::Slice if *targetTarget == Type::U8 => return true, |
|
| 9349 | + | else => return isValidCast(*sourceTarget, *targetTarget, false), |
|
| 9346 | 9350 | } |
|
| 9347 | - | return false; |
|
| 9348 | 9351 | } |
|
| 9349 | 9352 | ||
| 9350 | 9353 | /// Analyze an `as` cast expression. |
|
| 9351 | 9354 | fn resolveAs(self: *mut Resolver, node: *ast::Node, expr: ast::As) -> Type |
|
| 9352 | 9355 | throws (ResolveError) |
| 9358 | 9361 | } |
|
| 9359 | 9362 | ||
| 9360 | 9363 | assert sourceTy <> Type::Unknown; |
|
| 9361 | 9364 | assert targetTy <> Type::Unknown; |
|
| 9362 | 9365 | ||
| 9363 | - | let mut valid = isValidCast(sourceTy, targetTy); |
|
| 9364 | - | if let case Type::Pointer(sourcePointer) = sourceTy { |
|
| 9365 | - | if let case Type::Pointer(targetPointer) = targetTy { |
|
| 9366 | - | if sourcePointer.class == types::PointerClass::Ref and |
|
| 9367 | - | targetPointer.class == types::PointerClass::Unsafe and |
|
| 9368 | - | (not targetPointer.mutable or sourcePointer.mutable) and |
|
| 9369 | - | isValidCast(*sourcePointer.target, *targetPointer.target) |
|
| 9370 | - | { |
|
| 9371 | - | set valid = true; |
|
| 9372 | - | } |
|
| 9373 | - | } |
|
| 9374 | - | } |
|
| 9375 | - | if let case Type::Slice(sourceSlice) = sourceTy { |
|
| 9376 | - | if let case Type::Slice(targetSlice) = targetTy { |
|
| 9377 | - | if sourceSlice.class == types::PointerClass::Ref and |
|
| 9378 | - | targetSlice.class == types::PointerClass::Unsafe and |
|
| 9379 | - | (not targetSlice.mutable or sourceSlice.mutable) and |
|
| 9380 | - | isValidCast(*sourceSlice.item, *targetSlice.item) |
|
| 9381 | - | { |
|
| 9382 | - | set valid = true; |
|
| 9383 | - | } |
|
| 9384 | - | } |
|
| 9385 | - | } |
|
| 9366 | + | let valid = isValidCast(sourceTy, targetTy, true); |
|
| 9386 | 9367 | if valid { |
|
| 9387 | 9368 | // Propagate the constant value after applying the cast's target-width |
|
| 9388 | 9369 | // truncation and signed interpretation. |
|
| 9389 | 9370 | if let value = constValueEntry(self, expr.value) { |
|
| 9390 | 9371 | if let case ConstValue::Int(i) = value { |
| 9460 | 9441 | // If we're not catching the error, nor panicking on error, nor returning |
|
| 9461 | 9442 | // optional, then the current function must be able to propagate it. |
|
| 9462 | 9443 | let mut tryResultTy = resultTy; |
|
| 9463 | 9444 | if tryExpr.returnsOptional { |
|
| 9464 | 9445 | // `try?` converts errors to `nil` and wraps the result in an optional. |
|
| 9465 | - | if let case Type::Optional(_) = resultTy { |
|
| 9466 | - | // Already optional, no wrapping needed. |
|
| 9467 | - | } else { |
|
| 9468 | - | set tryResultTy = Type::Optional(allocType(self, resultTy)); |
|
| 9446 | + | if not isOptionalType(resultTy) { |
|
| 9447 | + | set tryResultTy = optionalType(allocType(self, resultTy)); |
|
| 9469 | 9448 | } |
|
| 9470 | 9449 | } else if tryExpr.catches.len > 0 { |
|
| 9471 | 9450 | // `try ... catch` -- one or more catch clauses. |
|
| 9472 | 9451 | set tryResultTy = try resolveTryCatches(self, node, tryExpr.catches, calleeInfo, resultTy, hint); |
|
| 9473 | 9452 | } else if not tryExpr.shouldPanic { |
| 9884 | 9863 | // We use the already-optional type from the other side rather than |
|
| 9885 | 9864 | // constructing a new optional, so that e.g. `?u8 == 42` coerces |
|
| 9886 | 9865 | // `42` to `?u8` (not `?i32`). We also record OptionalLift directly |
|
| 9887 | 9866 | // rather than using expectAssignable, because comparisons should |
|
| 9888 | 9867 | // allow e.g. `?*mut T == *T` where mutability differs. |
|
| 9889 | - | if let case Type::Optional(_) = leftTy { |
|
| 9868 | + | if isOptionalType(leftTy) { |
|
| 9890 | 9869 | if not isOptionalType(rightTy) { |
|
| 9891 | 9870 | setNodeCoercion(self, binop.right, Coercion::OptionalLift(leftTy)); |
|
| 9892 | 9871 | } |
|
| 9893 | - | } else if let case Type::Optional(_) = rightTy { |
|
| 9872 | + | } else if isOptionalType(rightTy) { |
|
| 9894 | 9873 | setNodeCoercion(self, binop.left, Coercion::OptionalLift(rightTy)); |
|
| 9895 | 9874 | } |
|
| 9896 | 9875 | set resultTy = Type::Bool; |
|
| 9897 | 9876 | }, |
|
| 9898 | 9877 | else => { |
| 9901 | 9880 | let leftTy = try infer(self, binop.left); |
|
| 9902 | 9881 | let rightTy = try visit(self, binop.right, leftTy); |
|
| 9903 | 9882 | ||
| 9904 | 9883 | // Allow arithmetic on owning pointers and unsafe pointers, but |
|
| 9905 | 9884 | // never on references. |
|
| 9906 | - | if let case Type::Pointer(leftPointer) = leftTy { |
|
| 9907 | - | if *leftPointer.target == Type::Opaque { |
|
| 9908 | - | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 9885 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = leftTy { |
|
| 9886 | + | if *target == Type::Opaque { |
|
| 9887 | + | throw emitError( |
|
| 9888 | + | self, node, ErrorKind::OpaquePointerArithmetic |
|
| 9889 | + | ); |
|
| 9909 | 9890 | } |
|
| 9910 | - | if leftPointer.class <> types::PointerClass::Ref |
|
| 9891 | + | if properties.class <> types::PointerClass::Ref |
|
| 9911 | 9892 | and isNumericType(rightTy) |
|
| 9912 | 9893 | { |
|
| 9913 | - | if leftPointer.class == types::PointerClass::Unsafe { |
|
| 9894 | + | if properties.class == types::PointerClass::Unsafe { |
|
| 9914 | 9895 | try requireUnsafe(self, node); |
|
| 9915 | 9896 | } |
|
| 9916 | 9897 | return setNodeType(self, node, leftTy); |
|
| 9917 | 9898 | } |
|
| 9918 | 9899 | } |
|
| 9919 | - | if let case Type::Pointer(rightPointer) = rightTy { |
|
| 9920 | - | if *rightPointer.target == Type::Opaque { |
|
| 9921 | - | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 9900 | + | if let case Type::Core(CoreType::Pointer { shape: PointerShape::Thin, properties, target }) = rightTy { |
|
| 9901 | + | if *target == Type::Opaque { |
|
| 9902 | + | throw emitError( |
|
| 9903 | + | self, node, ErrorKind::OpaquePointerArithmetic |
|
| 9904 | + | ); |
|
| 9922 | 9905 | } |
|
| 9923 | 9906 | if binop.op == ast::BinaryOp::Add |
|
| 9924 | - | and rightPointer.class <> types::PointerClass::Ref |
|
| 9907 | + | and properties.class <> types::PointerClass::Ref |
|
| 9925 | 9908 | and isNumericType(leftTy) |
|
| 9926 | 9909 | { |
|
| 9927 | - | if rightPointer.class == types::PointerClass::Unsafe { |
|
| 9910 | + | if properties.class == types::PointerClass::Unsafe { |
|
| 9928 | 9911 | try requireUnsafe(self, node); |
|
| 9929 | 9912 | } |
|
| 9930 | 9913 | return setNodeType(self, node, rightTy); |
|
| 9931 | 9914 | } |
|
| 9932 | 9915 | } |
| 10069 | 10052 | self, length, ErrorKind::ConstExprRequired |
|
| 10070 | 10053 | ); |
|
| 10071 | 10054 | } |
|
| 10072 | 10055 | } |
|
| 10073 | 10056 | } |
|
| 10074 | - | set ty = Type::Array(ArrayType { |
|
| 10075 | - | item: allocType(self, item), |
|
| 10076 | - | length: lengthType, |
|
| 10077 | - | }); |
|
| 10057 | + | set ty = arrayType(allocType(self, item), lengthType); |
|
| 10078 | 10058 | } |
|
| 10079 | 10059 | case ast::TypeSig::Slice { class, itemType, mutable } => { |
|
| 10080 | 10060 | let item = try resolveTypeSyntax(self, itemType, context); |
|
| 10081 | - | set ty = Type::Slice(SliceType { |
|
| 10082 | - | class, |
|
| 10083 | - | item: allocType(self, item), |
|
| 10084 | - | mutable, |
|
| 10085 | - | }); |
|
| 10061 | + | set ty = sliceType(class, allocType(self, item), mutable); |
|
| 10086 | 10062 | } |
|
| 10087 | 10063 | case ast::TypeSig::Pointer { class, valueType, mutable } => { |
|
| 10088 | 10064 | let target = try resolveTypeSyntax(self, valueType, context); |
|
| 10089 | - | set ty = Type::Pointer(PointerType { |
|
| 10090 | - | class, |
|
| 10091 | - | target: allocType(self, target), |
|
| 10092 | - | mutable, |
|
| 10093 | - | }); |
|
| 10065 | + | set ty = pointerType(class, allocType(self, target), mutable); |
|
| 10094 | 10066 | } |
|
| 10095 | 10067 | case ast::TypeSig::Optional { valueType } => { |
|
| 10096 | 10068 | let payload = try resolveTypeSyntax(self, valueType, context); |
|
| 10097 | - | set ty = Type::Optional(allocType(self, payload)); |
|
| 10069 | + | set ty = optionalType(allocType(self, payload)); |
|
| 10098 | 10070 | } |
|
| 10099 | 10071 | case ast::TypeSig::Nominal(name) => { |
|
| 10100 | 10072 | if let case TypeSyntaxContext::Symbolic = context { |
|
| 10101 | 10073 | if let case ast::NodeValue::GenericApply(app) = name.value { |
|
| 10102 | 10074 | let templateSym = try resolveGenericDataTarget(self, app.target); |
| 10301 | 10273 | if not traitInfo.objectSafe { |
|
| 10302 | 10274 | throw emitError(self, traitName, ErrorKind::TraitNotObjectSafe); |
|
| 10303 | 10275 | } |
|
| 10304 | 10276 | setNodeSymbol(self, traitName, sym); |
|
| 10305 | 10277 | set ty = Type::TraitObject(TraitObjectType { |
|
| 10306 | - | class, |
|
| 10278 | + | properties: PointerProperties { class, mutable }, |
|
| 10307 | 10279 | traitInfo, |
|
| 10308 | - | mutable, |
|
| 10309 | 10280 | }); |
|
| 10310 | 10281 | } |
|
| 10311 | 10282 | } |
|
| 10312 | 10283 | return setNodeType(self, node, ty); |
|
| 10313 | 10284 | } |
|
| 10314 | 10285 | ||
| 10315 | - | /// Check if a type can be used for inferrence. |
|
| 10286 | + | /// Check if a type can be used for inference. |
|
| 10316 | 10287 | fn isTypeInferrable(type: Type) -> bool { |
|
| 10317 | - | if let case Type::Pointer(pointer) = type { |
|
| 10318 | - | return isTypeInferrable(*pointer.target); |
|
| 10319 | - | } |
|
| 10320 | 10288 | match type { |
|
| 10321 | 10289 | case Type::Unknown, Type::Nil, Type::Undefined, Type::Int => return false, |
|
| 10322 | - | case Type::Array(ary) => return isTypeInferrable(*ary.item), |
|
| 10323 | - | case Type::Optional(opt) => return isTypeInferrable(*opt), |
|
| 10290 | + | case Type::Core(core) => |
|
| 10291 | + | return isTypeInferrable(*coreValueArgument(core)), |
|
| 10324 | 10292 | else => return true, |
|
| 10325 | 10293 | } |
|
| 10326 | 10294 | } |
|
| 10327 | 10295 | ||
| 10328 | 10296 | /// Analyze a standalone expression by wrapping it in a synthetic function. |
| 10934 | 10902 | ||
| 10935 | 10903 | for arg, i in call.args { |
|
| 10936 | 10904 | let expected = *info.paramTypes[i]; |
|
| 10937 | 10905 | let root = linearRootSymbol(checker.resolver, arg); |
|
| 10938 | 10906 | let mut argExclusive = isLinear(expected); |
|
| 10939 | - | if let case Type::Pointer(PointerType { class: types::PointerClass::Ref, mutable, .. }) = expected { |
|
| 10940 | - | set argExclusive = mutable; |
|
| 10941 | - | } else if let case Type::Slice(SliceType { |
|
| 10942 | - | class: types::PointerClass::Ref, mutable, .. |
|
| 10943 | - | }) = expected { |
|
| 10944 | - | set argExclusive = mutable; |
|
| 10945 | - | } else if let case Type::TraitObject(TraitObjectType { |
|
| 10946 | - | class: types::PointerClass::Ref, mutable, .. |
|
| 10947 | - | }) = expected { |
|
| 10948 | - | set argExclusive = mutable; |
|
| 10907 | + | if let properties = pointerProperties(expected); |
|
| 10908 | + | properties.class == types::PointerClass::Ref |
|
| 10909 | + | { |
|
| 10910 | + | set argExclusive = properties.mutable; |
|
| 10949 | 10911 | } |
|
| 10950 | 10912 | if not isUnsafePointerType(expected) { |
|
| 10951 | 10913 | if let rootSym = root { |
|
| 10952 | 10914 | for j in 0..rootsLen { |
|
| 10953 | 10915 | if let previous = roots[j] { |
lib/std/lang/resolver/printer.rad
+32 -24
| 102 | 102 | io::print("i32"); |
|
| 103 | 103 | } |
|
| 104 | 104 | case super::Type::I64 => { |
|
| 105 | 105 | io::print("i64"); |
|
| 106 | 106 | } |
|
| 107 | - | case super::Type::Pointer(pointer) => { |
|
| 108 | - | printPtrPrefix(pointer.class, pointer.mutable); |
|
| 109 | - | printTypeBody(*pointer.target, brief); |
|
| 110 | - | } |
|
| 111 | - | case super::Type::Slice(slice) => { |
|
| 112 | - | printPtrPrefix(slice.class, slice.mutable); |
|
| 113 | - | io::print("["); |
|
| 114 | - | printTypeBody(*slice.item, brief); |
|
| 115 | - | io::print("]"); |
|
| 116 | - | } |
|
| 117 | - | case super::Type::Array(array) => { |
|
| 118 | - | io::print("["); |
|
| 119 | - | printTypeBody(*array.item, brief); |
|
| 120 | - | io::print("; "); |
|
| 121 | - | if let length = super::concreteArrayLength(array.length) { |
|
| 122 | - | io::printU32(length); |
|
| 123 | - | } else { |
|
| 124 | - | printTypeBody(*array.length, brief); |
|
| 107 | + | case super::Type::Core(core) => { |
|
| 108 | + | match core { |
|
| 109 | + | case super::CoreType::Pointer { shape, properties, target } => { |
|
| 110 | + | printPtrPrefix(properties.class, properties.mutable); |
|
| 111 | + | match shape { |
|
| 112 | + | case super::PointerShape::Thin => |
|
| 113 | + | printTypeBody(*target, brief), |
|
| 114 | + | case super::PointerShape::Slice => { |
|
| 115 | + | io::print("["); |
|
| 116 | + | printTypeBody(*target, brief); |
|
| 117 | + | io::print("]"); |
|
| 118 | + | } |
|
| 119 | + | } |
|
| 120 | + | } |
|
| 121 | + | case super::CoreType::Array { item, length } => { |
|
| 122 | + | io::print("["); |
|
| 123 | + | printTypeBody(*item, brief); |
|
| 124 | + | io::print("; "); |
|
| 125 | + | if let value = super::concreteArrayLength(length) { |
|
| 126 | + | io::printU32(value); |
|
| 127 | + | } else { |
|
| 128 | + | printTypeBody(*length, brief); |
|
| 129 | + | } |
|
| 130 | + | io::print("]"); |
|
| 131 | + | } |
|
| 132 | + | case super::CoreType::Optional { payload } => { |
|
| 133 | + | io::print("?"); |
|
| 134 | + | printTypeBody(*payload, brief); |
|
| 135 | + | } |
|
| 125 | 136 | } |
|
| 126 | - | io::print("]"); |
|
| 127 | - | } |
|
| 128 | - | case super::Type::Optional(inner) => { |
|
| 129 | - | io::print("?"); |
|
| 130 | - | printTypeBody(*inner, brief); |
|
| 131 | 137 | } |
|
| 132 | 138 | case super::Type::Fn(fnType) => { |
|
| 133 | 139 | if fnType.isUnsafe { |
|
| 134 | 140 | io::print("unsafe "); |
|
| 135 | 141 | } |
| 196 | 202 | printTypeBody(*arg, brief); |
|
| 197 | 203 | } |
|
| 198 | 204 | io::print("]"); |
|
| 199 | 205 | } |
|
| 200 | 206 | case super::Type::TraitObject(object) => { |
|
| 201 | - | printPtrPrefix(object.class, object.mutable); |
|
| 207 | + | printPtrPrefix( |
|
| 208 | + | object.properties.class, object.properties.mutable |
|
| 209 | + | ); |
|
| 202 | 210 | io::print("opaque "); |
|
| 203 | 211 | io::print(object.traitInfo.name); |
|
| 204 | 212 | } |
|
| 205 | 213 | case super::Type::Range { start, end } => { |
|
| 206 | 214 | if let s = start { |
lib/std/lang/resolver/tests.rad
+85 -80
| 454 | 454 | ||
| 455 | 455 | /// Require an array type and return its element type. |
|
| 456 | 456 | fn expectArrayType(ty: super::Type, length: u32) -> super::Type |
|
| 457 | 457 | throws (testing::TestError) |
|
| 458 | 458 | { |
|
| 459 | - | let case super::Type::Array(info) = ty |
|
| 460 | - | else throw testing::TestError::Failed; |
|
| 461 | - | let actualLength = super::concreteArrayLength(info.length) |
|
| 459 | + | let case super::Type::Core(super::CoreType::Array { |
|
| 460 | + | item, length: lengthArgument |
|
| 461 | + | }) = ty else throw testing::TestError::Failed; |
|
| 462 | + | let actualLength = super::concreteArrayLength(lengthArgument) |
|
| 462 | 463 | else throw testing::TestError::Failed; |
|
| 463 | 464 | try testing::expect(actualLength == length); |
|
| 464 | 465 | ||
| 465 | - | return *info.item; |
|
| 466 | - | } |
|
| 467 | - | ||
| 468 | - | /// Require a slice type and return its element type. |
|
| 469 | - | fn expectSliceType(ty: super::Type, mutable: bool) -> super::Type |
|
| 470 | - | throws (testing::TestError) |
|
| 471 | - | { |
|
| 472 | - | let case super::Type::Slice(super::SliceType { |
|
| 473 | - | class: types::PointerClass::Owned, item, mutable: sliceMut |
|
| 474 | - | }) = ty |
|
| 475 | - | else throw testing::TestError::Failed; |
|
| 476 | - | try testing::expect(sliceMut == mutable); |
|
| 477 | - | ||
| 478 | 466 | return *item; |
|
| 479 | 467 | } |
|
| 480 | 468 | ||
| 481 | - | /// Require a pointer type and return its target type. |
|
| 482 | - | fn expectPointerType(ty: super::Type, mutable: bool) -> super::Type |
|
| 483 | - | throws (testing::TestError) |
|
| 484 | - | { |
|
| 485 | - | let case super::Type::Pointer(super::PointerType { |
|
| 486 | - | class: types::PointerClass::Owned, target, mutable: ptrMut |
|
| 487 | - | }) = ty |
|
| 469 | + | /// Require an owned pointer-like type and return its value argument. |
|
| 470 | + | fn expectPointerType( |
|
| 471 | + | ty: super::Type, |
|
| 472 | + | shape: super::PointerShape, |
|
| 473 | + | mutable: bool, |
|
| 474 | + | ) -> super::Type throws (testing::TestError) { |
|
| 475 | + | let case super::Type::Core(super::CoreType::Pointer { shape: actualShape, properties, target }) = ty |
|
| 488 | 476 | else throw testing::TestError::Failed; |
|
| 489 | - | try testing::expect(ptrMut == mutable); |
|
| 490 | - | ||
| 477 | + | try testing::expect(actualShape == shape); |
|
| 478 | + | try testing::expect(properties.class == types::PointerClass::Owned); |
|
| 479 | + | try testing::expect(properties.mutable == mutable); |
|
| 491 | 480 | return *target; |
|
| 492 | 481 | } |
|
| 493 | 482 | ||
| 494 | 483 | /// Verify that a node has a constant integer value with the expected magnitude. |
|
| 495 | 484 | fn expectConstInt(a: *super::Resolver, node: *ast::Node, expected: u32) |
| 541 | 530 | let mut a = testResolver(); |
|
| 542 | 531 | let result = try resolveExprStr(&mut a, "\"hello\""); |
|
| 543 | 532 | ||
| 544 | 533 | try expectNoErrors(&result); |
|
| 545 | 534 | let ty = try typeOf(&a, result.root); |
|
| 546 | - | let elemTy = try expectSliceType(ty, false); |
|
| 535 | + | let elemTy = try expectPointerType(ty, super::PointerShape::Slice, false); |
|
| 547 | 536 | try testing::expect(elemTy == super::Type::U8); |
|
| 548 | 537 | } |
|
| 549 | 538 | ||
| 550 | 539 | @test fn testResolveAsNumeric() throws (testing::TestError) { |
|
| 551 | 540 | { |
| 794 | 783 | let stmt = try getBlockStmt(result.root, 0); |
|
| 795 | 784 | let case ast::NodeValue::Let(decl) = stmt.value |
|
| 796 | 785 | else throw testing::TestError::Failed; |
|
| 797 | 786 | let arrayTy = try typeOf(&a, decl.value); |
|
| 798 | 787 | let elemTy = try expectArrayType(arrayTy, 2); |
|
| 799 | - | let case super::Type::Optional(inner) = elemTy |
|
| 788 | + | let case super::Type::Core(super::CoreType::Optional { payload }) = elemTy |
|
| 800 | 789 | else throw testing::TestError::Failed; |
|
| 801 | - | try testing::expect(*inner == super::Type::I32); |
|
| 790 | + | try testing::expect(*payload == super::Type::I32); |
|
| 802 | 791 | } |
|
| 803 | 792 | ||
| 804 | 793 | @test fn testResolveArrayLiteralOptionalMismatch() throws (testing::TestError) { |
|
| 805 | 794 | let mut a = testResolver(); |
|
| 806 | 795 | let result = try resolveProgramStr(&mut a, "let xs: [?bool; 2] = [1, 2];"); |
| 875 | 864 | ||
| 876 | 865 | let sliceStmt = try getBlockStmt(result.root, 1); |
|
| 877 | 866 | let case ast::NodeValue::Let(sliceDecl) = sliceStmt.value |
|
| 878 | 867 | else throw testing::TestError::Failed; |
|
| 879 | 868 | let sliceTy = try typeOf(&a, sliceDecl.value); |
|
| 880 | - | let elemTy = try expectSliceType(sliceTy, false); |
|
| 869 | + | let elemTy = try expectPointerType(sliceTy, super::PointerShape::Slice, false); |
|
| 881 | 870 | try testing::expect(elemTy == super::Type::I32); |
|
| 882 | 871 | ||
| 883 | 872 | let indexStmt = try getBlockStmt(result.root, 2); |
|
| 884 | 873 | try expectExprStmtType(&a, indexStmt, super::Type::I32); |
|
| 885 | 874 | } |
| 898 | 887 | ||
| 899 | 888 | let ptrStmt = try getBlockStmt(result.root, 3); |
|
| 900 | 889 | let case ast::NodeValue::ExprStmt(ptrExpr) = ptrStmt.value |
|
| 901 | 890 | else throw testing::TestError::Failed; |
|
| 902 | 891 | let ptrTy = try typeOf(&a, ptrExpr); |
|
| 903 | - | let targetTy = try expectPointerType(ptrTy, false); |
|
| 892 | + | let targetTy = try expectPointerType(ptrTy, super::PointerShape::Thin, false); |
|
| 904 | 893 | try testing::expect(targetTy == super::Type::I32); |
|
| 905 | 894 | } |
|
| 906 | 895 | ||
| 907 | 896 | @test fn testResolveSliceLiteralImmutable() throws (testing::TestError) { |
|
| 908 | 897 | let mut a = testResolver(); |
| 3695 | 3684 | let mut a = testResolver(); |
|
| 3696 | 3685 | let result = try resolveProgramStr(&mut a, "fn f(a: i32) { let o: *opaque = &a; let ptr: *i32 = o; }"); |
|
| 3697 | 3686 | let err = try expectError(&result); |
|
| 3698 | 3687 | let case super::ErrorKind::TypeMismatch(mismatch) = err.kind |
|
| 3699 | 3688 | else throw testing::TestError::Failed; |
|
| 3700 | - | let case super::Type::Pointer(super::PointerType { |
|
| 3701 | - | class: types::PointerClass::Owned, target: expectedTarget, .. |
|
| 3702 | - | }) = mismatch.expected |
|
| 3703 | - | else throw testing::TestError::Failed; |
|
| 3704 | - | let case super::Type::Pointer(super::PointerType { |
|
| 3705 | - | class: types::PointerClass::Owned, target: actualTarget, .. |
|
| 3706 | - | }) = mismatch.actual |
|
| 3707 | - | else throw testing::TestError::Failed; |
|
| 3689 | + | let case super::Type::Core(super::CoreType::Pointer { |
|
| 3690 | + | shape: super::PointerShape::Thin, properties: expectedProperties, target: expectedTarget |
|
| 3691 | + | }) = mismatch.expected else throw testing::TestError::Failed; |
|
| 3692 | + | let case super::Type::Core(super::CoreType::Pointer { |
|
| 3693 | + | shape: super::PointerShape::Thin, properties: actualProperties, target: actualTarget |
|
| 3694 | + | }) = mismatch.actual else throw testing::TestError::Failed; |
|
| 3695 | + | assert expectedProperties.class == types::PointerClass::Owned; |
|
| 3696 | + | assert actualProperties.class == types::PointerClass::Owned; |
|
| 3708 | 3697 | ||
| 3709 | 3698 | try testing::expect(*expectedTarget == super::Type::I32); |
|
| 3710 | 3699 | try testing::expect(*actualTarget == super::Type::Opaque); |
|
| 3711 | 3700 | } |
|
| 3712 | 3701 |
| 3822 | 3811 | ||
| 3823 | 3812 | // Verify the array constant has the correct type with length 3. |
|
| 3824 | 3813 | let arrStmt = try getBlockStmt(result.root, 1); |
|
| 3825 | 3814 | let sym = super::symbolFor(&a, arrStmt) |
|
| 3826 | 3815 | else throw testing::TestError::Failed; |
|
| 3827 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 3828 | - | else throw testing::TestError::Failed; |
|
| 3829 | - | let actualLength = super::concreteArrayLength(arrType.length) |
|
| 3816 | + | let case super::SymbolData::Constant { type: arrayType, .. } = sym.data |
|
| 3830 | 3817 | else throw testing::TestError::Failed; |
|
| 3831 | - | try testing::expect(actualLength == 3); |
|
| 3818 | + | let _ = try expectArrayType(arrayType, 3); |
|
| 3832 | 3819 | } |
|
| 3833 | 3820 | ||
| 3834 | 3821 | /// Test that a record field can use a constant as its array length. |
|
| 3835 | 3822 | @test fn testRecordFieldWithConstArrayLength() throws (testing::TestError) { |
|
| 3836 | 3823 | let mut a = testResolver(); |
| 4274 | 4261 | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4275 | 4262 | else throw testing::TestError::Failed; |
|
| 4276 | 4263 | } |
|
| 4277 | 4264 | } |
|
| 4278 | 4265 | ||
| 4266 | + | /// Test `@relocate` accepts mutable slices with a matching non-byte item type. |
|
| 4267 | + | @test fn testResolveRelocateTypedSlices() throws (testing::TestError) { |
|
| 4268 | + | let mut a = testResolver(); |
|
| 4269 | + | let program = "fn f(destination: *mut [i32], source: *mut [i32]) { @relocate(destination, source); }"; |
|
| 4270 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4271 | + | try expectNoErrors(&result); |
|
| 4272 | + | } |
|
| 4273 | + | ||
| 4274 | + | /// Test `@relocate` rejects mutable slices with different item types. |
|
| 4275 | + | @test fn testResolveRelocateItemMismatch() throws (testing::TestError) { |
|
| 4276 | + | let mut a = testResolver(); |
|
| 4277 | + | let program = "fn f(destination: *mut [i32], source: *mut [u32]) { @relocate(destination, source); }"; |
|
| 4278 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4279 | + | let err = try expectError(&result); |
|
| 4280 | + | let case super::ErrorKind::TypeMismatch(_) = err.kind |
|
| 4281 | + | else throw testing::TestError::Failed; |
|
| 4282 | + | } |
|
| 4283 | + | ||
| 4284 | + | /// Test `@relocate` rejects slices whose item size is unknown. |
|
| 4285 | + | @test fn testResolveRelocateOpaqueItems() throws (testing::TestError) { |
|
| 4286 | + | { |
|
| 4287 | + | let mut a = testResolver(); |
|
| 4288 | + | let program = "fn f(destination: *mut [opaque], source: *mut [opaque]) { @relocate(destination, source); }"; |
|
| 4289 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4290 | + | let _ = try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4291 | + | } { |
|
| 4292 | + | let mut a = testResolver(); |
|
| 4293 | + | let program = "fn f(destination: *mut [[opaque; 2]], source: *mut [[opaque; 2]]) { @relocate(destination, source); }"; |
|
| 4294 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4295 | + | let _ = try expectErrorKind(&result, super::ErrorKind::OpaqueTypeNotAllowed); |
|
| 4296 | + | } |
|
| 4297 | + | } |
|
| 4298 | + | ||
| 4279 | 4299 | /// Test `match &opt` produces immutable pointer bindings. |
|
| 4280 | 4300 | @test fn testResolveMatchRefUnionBinding() throws (testing::TestError) { |
|
| 4281 | 4301 | let mut a = testResolver(); |
|
| 4282 | 4302 | let program = "union Opt { Some(i32), None } fn f() { let opt = Opt::Some(42); match &opt { case Opt::Some(x) => { *x; } else => {} } }"; |
|
| 4283 | 4303 | let result = try resolveProgramStr(&mut a, program); |
| 4293 | 4313 | else throw testing::TestError::Failed; |
|
| 4294 | 4314 | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4295 | 4315 | else throw testing::TestError::Failed; |
|
| 4296 | 4316 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 4297 | 4317 | else throw testing::TestError::Failed; |
|
| 4298 | - | let case super::Type::Pointer(super::PointerType { |
|
| 4299 | - | class: types::PointerClass::Ref, target, mutable |
|
| 4300 | - | }) = payloadValType |
|
| 4318 | + | let case super::Type::Core(super::CoreType::Pointer { shape: super::PointerShape::Thin, properties, target }) = payloadValType |
|
| 4301 | 4319 | else throw testing::TestError::Failed; |
|
| 4302 | - | assert not mutable; |
|
| 4320 | + | assert properties.class == types::PointerClass::Ref; |
|
| 4321 | + | assert not properties.mutable; |
|
| 4303 | 4322 | assert *target == super::Type::I32; |
|
| 4304 | 4323 | } |
|
| 4305 | 4324 | ||
| 4306 | 4325 | /// Test `match &mut opt` produces mutable pointer bindings. |
|
| 4307 | 4326 | @test fn testResolveMatchMutRefUnionBinding() throws (testing::TestError) { |
| 4320 | 4339 | else throw testing::TestError::Failed; |
|
| 4321 | 4340 | let payloadSym = super::findSymbolInScope(scope, "x") |
|
| 4322 | 4341 | else throw testing::TestError::Failed; |
|
| 4323 | 4342 | let case super::SymbolData::Value { type: payloadValType, .. } = payloadSym.data |
|
| 4324 | 4343 | else throw testing::TestError::Failed; |
|
| 4325 | - | let case super::Type::Pointer(super::PointerType { |
|
| 4326 | - | class: types::PointerClass::Ref, target, mutable |
|
| 4327 | - | }) = payloadValType |
|
| 4344 | + | let case super::Type::Core(super::CoreType::Pointer { shape: super::PointerShape::Thin, properties, target }) = payloadValType |
|
| 4328 | 4345 | else throw testing::TestError::Failed; |
|
| 4329 | - | assert mutable; |
|
| 4346 | + | assert properties.class == types::PointerClass::Ref; |
|
| 4347 | + | assert properties.mutable; |
|
| 4330 | 4348 | assert *target == super::Type::I32; |
|
| 4331 | 4349 | } |
|
| 4332 | 4350 | ||
| 4333 | 4351 | /// Non-constant integer widening must use an explicit cast. |
|
| 4334 | 4352 | @test fn testResolveIntegerWideningRequiresCast() throws (testing::TestError) { |
| 4981 | 4999 | try expectNoErrors(&result); |
|
| 4982 | 5000 | ||
| 4983 | 5001 | let arrStmt = try getBlockStmt(result.root, 3); |
|
| 4984 | 5002 | let sym = super::symbolFor(&a, arrStmt) |
|
| 4985 | 5003 | else throw testing::TestError::Failed; |
|
| 4986 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 5004 | + | let case super::SymbolData::Constant { type: arrayType, .. } = sym.data |
|
| 4987 | 5005 | else throw testing::TestError::Failed; |
|
| 4988 | - | let actualLength = super::concreteArrayLength(arrType.length) |
|
| 4989 | - | else throw testing::TestError::Failed; |
|
| 4990 | - | try testing::expect(actualLength == 5); |
|
| 5006 | + | let _ = try expectArrayType(arrayType, 5); |
|
| 4991 | 5007 | } |
|
| 4992 | 5008 | ||
| 4993 | 5009 | /// Test cross-module constant expression: a constant in one module references |
|
| 4994 | 5010 | /// a constant from another module via scope access. |
|
| 4995 | 5011 | @test fn testCrossModuleConstExpr() throws (testing::TestError) { |
| 5066 | 5082 | try expectNoErrors(&result); |
|
| 5067 | 5083 | ||
| 5068 | 5084 | let arrStmt = try getBlockStmt(result.root, 2); |
|
| 5069 | 5085 | let sym = super::symbolFor(&a, arrStmt) |
|
| 5070 | 5086 | else throw testing::TestError::Failed; |
|
| 5071 | - | let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data |
|
| 5072 | - | else throw testing::TestError::Failed; |
|
| 5073 | - | let actualLength = super::concreteArrayLength(arrType.length) |
|
| 5087 | + | let case super::SymbolData::Constant { type: arrayType, .. } = sym.data |
|
| 5074 | 5088 | else throw testing::TestError::Failed; |
|
| 5075 | - | try testing::expect(actualLength == 4); |
|
| 5089 | + | let _ = try expectArrayType(arrayType, 4); |
|
| 5076 | 5090 | } |
|
| 5077 | 5091 | ||
| 5078 | 5092 | /// Test unsuffixed integer literals in constant expressions. |
|
| 5079 | 5093 | @test fn testConstExprUnsuffixedLiterals() throws (testing::TestError) { |
|
| 5080 | 5094 | try expectConstFold("constant A: u32 = 4 * 4;", 0, 16); |
| 5495 | 5509 | let sym = super::symbolFor(&a, block.statements[0]) |
|
| 5496 | 5510 | else throw testing::TestError::Failed; |
|
| 5497 | 5511 | let template = super::genericTemplateFor(&a, sym) |
|
| 5498 | 5512 | else throw testing::TestError::Failed; |
|
| 5499 | 5513 | let rigid = super::Type::Parameter(template.params[0]); |
|
| 5500 | - | let pointer = super::Type::Pointer(super::PointerType { |
|
| 5501 | - | class: types::PointerClass::Owned, |
|
| 5502 | - | target: super::allocType(&mut a, rigid), |
|
| 5503 | - | mutable: true, |
|
| 5504 | - | }); |
|
| 5505 | - | let symbolic = super::Type::Optional(super::allocType(&mut a, pointer)); |
|
| 5514 | + | let pointer = super::pointerType( types::PointerClass::Owned, super::allocType(&mut a, rigid), true, ); |
|
| 5515 | + | let symbolic = super::optionalType( super::allocType(&mut a, pointer), ); |
|
| 5506 | 5516 | let concrete = super::allocType(&mut a, super::Type::U32); |
|
| 5507 | 5517 | let args: [*super::Type; 1] = [concrete]; |
|
| 5508 | 5518 | let sub = super::Substitution { params: template.params, args: &args[..] }; |
|
| 5509 | 5519 | let replaced = try super::substituteType( |
|
| 5510 | 5520 | &mut a, symbolic, &sub, block.statements[0] |
|
| 5511 | 5521 | ) catch { |
|
| 5512 | 5522 | throw testing::TestError::Failed; |
|
| 5513 | 5523 | }; |
|
| 5514 | - | let case super::Type::Optional(inner) = replaced |
|
| 5524 | + | let case super::Type::Core(super::CoreType::Optional { payload }) = replaced |
|
| 5515 | 5525 | else throw testing::TestError::Failed; |
|
| 5516 | - | let case super::Type::Pointer(super::PointerType { |
|
| 5517 | - | class: types::PointerClass::Owned, target, mutable |
|
| 5518 | - | }) = *inner |
|
| 5526 | + | let case super::Type::Core(super::CoreType::Pointer { shape: super::PointerShape::Thin, properties, target }) = *payload |
|
| 5519 | 5527 | else throw testing::TestError::Failed; |
|
| 5520 | - | assert mutable; |
|
| 5528 | + | assert properties.class == types::PointerClass::Owned; |
|
| 5529 | + | assert properties.mutable; |
|
| 5521 | 5530 | assert *target == super::Type::U32; |
|
| 5522 | 5531 | } |
|
| 5523 | 5532 | ||
| 5524 | 5533 | /// Duplicate generic parameter names are rejected in their declaration scope. |
|
| 5525 | 5534 | @test fn testDuplicateGenericParameterRejected() throws (testing::TestError) { |
| 5539 | 5548 | "record Value {} fn invalid⟨T: Value⟩(value: T) {}", |
|
| 5540 | 5549 | ); |
|
| 5541 | 5550 | try expectErrorKind(&result, super::ErrorKind::GenericBoundNotTrait); |
|
| 5542 | 5551 | } |
|
| 5543 | 5552 | ||
| 5544 | - | /// Integer constant parameters specialize array layouts. |
|
| 5553 | + | /// Non-u32 integer constant parameters specialize array layouts. |
|
| 5545 | 5554 | @test fn testGenericConstParameterArrayLayout() throws (testing::TestError) { |
|
| 5546 | 5555 | let mut a = testResolver(); |
|
| 5547 | 5556 | let result = try resolveProgramStr( |
|
| 5548 | 5557 | &mut a, |
|
| 5549 | - | "record Buffer⟨constant N: u32⟩ { data: [u8; N] } instantiate Buffer⟨4⟩;", |
|
| 5558 | + | "record Buffer⟨constant N: u64⟩ { data: [u8; N] } instantiate Buffer⟨4⟩;", |
|
| 5550 | 5559 | ); |
|
| 5551 | 5560 | try expectNoErrors(&result); |
|
| 5552 | 5561 | let case ast::NodeValue::Block(block) = result.root.value |
|
| 5553 | 5562 | else throw testing::TestError::Failed; |
|
| 5554 | 5563 | let case ast::NodeValue::Instantiate(applications) = block.statements[1].value |
| 5557 | 5566 | else throw testing::TestError::Failed; |
|
| 5558 | 5567 | let case super::Type::Nominal(nominal) = resolved |
|
| 5559 | 5568 | else throw testing::TestError::Failed; |
|
| 5560 | 5569 | let case super::NominalType::Record(recordType) = *nominal |
|
| 5561 | 5570 | else throw testing::TestError::Failed; |
|
| 5562 | - | let case super::Type::Array(arrayType) = recordType.fields[0].fieldType |
|
| 5563 | - | else throw testing::TestError::Failed; |
|
| 5564 | - | let actualLength = super::concreteArrayLength(arrayType.length) |
|
| 5565 | - | else throw testing::TestError::Failed; |
|
| 5566 | - | assert actualLength == 4; |
|
| 5571 | + | let item = try expectArrayType(recordType.fields[0].fieldType, 4); |
|
| 5572 | + | assert item == super::Type::U8; |
|
| 5567 | 5573 | assert recordType.layout.size == 4; |
|
| 5568 | 5574 | } |
|
| 5569 | 5575 | ||
| 5570 | 5576 | /// Equivalent integer expressions share a canonical specialization. |
|
| 5571 | 5577 | @test fn testGenericConstParameterCanonical() throws (testing::TestError) { |
| 5744 | 5750 | else throw testing::TestError::Failed; |
|
| 5745 | 5751 | let case super::Type::Nominal(listType) = resolved |
|
| 5746 | 5752 | else throw testing::TestError::Failed; |
|
| 5747 | 5753 | let case super::NominalType::Record(recordType) = *listType |
|
| 5748 | 5754 | else throw testing::TestError::Failed; |
|
| 5749 | - | let case super::Type::Optional(optionalTarget) = recordType.fields[1].fieldType |
|
| 5755 | + | let case super::Type::Core(super::CoreType::Optional { payload }) = recordType.fields[1].fieldType |
|
| 5750 | 5756 | else throw testing::TestError::Failed; |
|
| 5751 | - | let case super::Type::Pointer(super::PointerType { |
|
| 5752 | - | class: types::PointerClass::Owned, target, .. |
|
| 5753 | - | }) = *optionalTarget |
|
| 5757 | + | let case super::Type::Core(super::CoreType::Pointer { shape: super::PointerShape::Thin, properties, target }) = *payload |
|
| 5754 | 5758 | else throw testing::TestError::Failed; |
|
| 5759 | + | assert properties.class == types::PointerClass::Owned; |
|
| 5755 | 5760 | let case super::Type::Nominal(nextType) = *target |
|
| 5756 | 5761 | else throw testing::TestError::Failed; |
|
| 5757 | 5762 | assert nextType == listType; |
|
| 5758 | 5763 | } |
|
| 5759 | 5764 |
test/tests/relocate.rad
+12 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | - | //! Test overlap-safe relocation between mutable byte slices. |
|
| 2 | + | //! Test overlap-safe relocation between mutable typed slices. |
|
| 3 | 3 | ||
| 4 | 4 | @default fn main() -> i32 { |
|
| 5 | 5 | let mut bytes: [u8; 6] = [1, 2, 3, 4, 5, 6]; |
|
| 6 | 6 | ||
| 7 | 7 | let rightDestination: *mut [u8] = &mut bytes[1..5]; |
| 31 | 31 | @relocate(emptyDestination, emptySource); |
|
| 32 | 32 | ||
| 33 | 33 | assert bytes[0] == 1; |
|
| 34 | 34 | assert bytes[5] == 6; |
|
| 35 | 35 | ||
| 36 | + | let mut words: [i32; 5] = [10, 20, 30, 40, 50]; |
|
| 37 | + | let wordDestination: *mut [i32] = &mut words[1..5]; |
|
| 38 | + | let wordSource: *mut [i32] = &mut words[0..4]; |
|
| 39 | + | @relocate(wordDestination, wordSource); |
|
| 40 | + | ||
| 41 | + | assert words[0] == 10; |
|
| 42 | + | assert words[1] == 10; |
|
| 43 | + | assert words[2] == 20; |
|
| 44 | + | assert words[3] == 30; |
|
| 45 | + | assert words[4] == 40; |
|
| 46 | + | ||
| 36 | 47 | return 0; |
|
| 37 | 48 | } |