compiler: Compute fixed type layouts in checked code
25a7fab888a2a9c88863776a244768897aab5ab7a9024e05c53882a6a1d8fe29
1 parent
ef74a568
lib/std/lang/resolver.rad
+17 -10
| 2117 | 2117 | return b; |
|
| 2118 | 2118 | } |
|
| 2119 | 2119 | ||
| 2120 | 2120 | /// Get the layout of a type. |
|
| 2121 | 2121 | export unsafe fn getTypeLayout(ty: Type) -> Layout { |
|
| 2122 | + | match ty { |
|
| 2123 | + | case Type::Array(arr) => return getArrayLayout(getTypeLayout(*arr.item), arr.length), |
|
| 2124 | + | case Type::Optional(inner) => { |
|
| 2125 | + | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 2126 | + | if isNullableType(*inner) { |
|
| 2127 | + | return getTypeLayout(*inner); |
|
| 2128 | + | } |
|
| 2129 | + | return getOptionalAggregateLayout(getTypeLayout(*inner)); |
|
| 2130 | + | } |
|
| 2131 | + | case Type::Nominal(info) => return getNominalLayout(*info), |
|
| 2132 | + | else => return fixedTypeLayout(ty), |
|
| 2133 | + | } |
|
| 2134 | + | } |
|
| 2135 | + | ||
| 2136 | + | /// Get a layout that does not depend on nested type or nominal metadata. |
|
| 2137 | + | fn fixedTypeLayout(ty: Type) -> Layout { |
|
| 2122 | 2138 | match ty { |
|
| 2123 | 2139 | case Type::Pointer { .. } => return Layout { |
|
| 2124 | 2140 | size: PTR_SIZE, alignment: PTR_SIZE |
|
| 2125 | 2141 | }, |
|
| 2126 | 2142 | case Type::Slice { .. }, Type::TraitObject { .. }, Type::Session(_) => |
| 2133 | 2149 | case Type::U64, Type::I64 => return Layout { size: 8, alignment: 8 }, |
|
| 2134 | 2150 | case Type::Fn(_) => return Layout { size: PTR_SIZE, alignment: PTR_SIZE }, |
|
| 2135 | 2151 | case Type::Cell { .. } => return Layout { |
|
| 2136 | 2152 | size: PTR_SIZE, alignment: PTR_SIZE |
|
| 2137 | 2153 | }, |
|
| 2138 | - | case Type::Array(arr) => return getArrayLayout(getTypeLayout(*arr.item), arr.length), |
|
| 2139 | - | case Type::Optional(inner) => { |
|
| 2140 | - | // Nullable types use null pointer optimization -- no tag byte needed. |
|
| 2141 | - | if isNullableType(*inner) { |
|
| 2142 | - | return getTypeLayout(*inner); |
|
| 2143 | - | } |
|
| 2144 | - | return getOptionalAggregateLayout(getTypeLayout(*inner)); |
|
| 2145 | - | } |
|
| 2146 | - | case Type::Nominal(info) => return getNominalLayout(*info), |
|
| 2147 | 2154 | else => { |
|
| 2148 | - | panic "getTypeLayout: the given type cannot be layed out"; |
|
| 2155 | + | panic "fixedTypeLayout: the given type has no fixed layout"; |
|
| 2149 | 2156 | } |
|
| 2150 | 2157 | } |
|
| 2151 | 2158 | } |
|
| 2152 | 2159 | ||
| 2153 | 2160 | /// Get the layout of a type or value. |
lib/std/lang/resolver/tests.rad
+54 -0
| 257 | 257 | assert err.kind == super::ErrorKind::ImmutableBinding, program; |
|
| 258 | 258 | } |
|
| 259 | 259 | } |
|
| 260 | 260 | } |
|
| 261 | 261 | ||
| 262 | + | /// Fixed layouts cover primitive and pointer-like representations. |
|
| 263 | + | @test unsafe fn testFixedTypeLayouts() throws (testing::TestError) { |
|
| 264 | + | for ty in [super::Type::Void, super::Type::Never] { |
|
| 265 | + | checkFixedTypeLayout(ty, 0, 0); |
|
| 266 | + | } |
|
| 267 | + | for ty in [super::Type::Bool, super::Type::U8, super::Type::I8] { |
|
| 268 | + | checkFixedTypeLayout(ty, 1, 1); |
|
| 269 | + | } |
|
| 270 | + | for ty in [super::Type::U16, super::Type::I16] { checkFixedTypeLayout(ty, 2, 2); } |
|
| 271 | + | for ty in [super::Type::U32, super::Type::I32] { checkFixedTypeLayout(ty, 4, 4); } |
|
| 272 | + | for ty in [super::Type::Int, super::Type::U64, super::Type::I64] { checkFixedTypeLayout(ty, 8, 8); } |
|
| 273 | + | let mut arena = testArena(); |
|
| 274 | + | let itemStorage = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
|
| 275 | + | set *itemStorage = super::Type::U32; |
|
| 276 | + | let item: *super::Type = itemStorage; |
|
| 277 | + | let region = types::Region { id: 1, origin: types::RegionOrigin::Block, name: "r", parent: nil }; |
|
| 278 | + | let traitInfo = super::TraitType { name: "T", moduleId: 0, methods: &mut [], supertraits: &mut [] }; |
|
| 279 | + | let signature = try! alloc::alloc(&mut arena, @sizeOf(super::FnType), @alignOf(super::FnType)) as *mut super::FnType; |
|
| 280 | + | set *signature = super::FnType { |
|
| 281 | + | regions: nil, paramTypes: &[], returnType: item, throwList: &[], isUnsafe: false, |
|
| 282 | + | }; |
|
| 283 | + | checkFixedTypeLayout(super::Type::Fn(signature), 8, 8); |
|
| 284 | + | checkFixedTypeLayout(super::Type::Session(®ion), 16, 8); |
|
| 285 | + | for class in [types::PointerClass::Owned, types::PointerClass::Ref, |
|
| 286 | + | types::PointerClass::Unsafe, types::PointerClass::Region(®ion)] { |
|
| 287 | + | checkFixedTypeLayout(super::Type::Cell { class, payload: item }, 8, 8); |
|
| 288 | + | for mutable in [false, true] { |
|
| 289 | + | checkFixedTypeLayout(super::Type::Pointer { class, target: item, mutable }, 8, 8); |
|
| 290 | + | checkFixedTypeLayout(super::Type::Slice { class, item, mutable }, 16, 8); |
|
| 291 | + | checkFixedTypeLayout(super::Type::TraitObject { class, traitInfo: &traitInfo, mutable }, 16, 8); |
|
| 292 | + | } |
|
| 293 | + | } |
|
| 294 | + | } |
|
| 295 | + | ||
| 296 | + | /// Check a fixed layout through direct, array, and optional type traversal. |
|
| 297 | + | unsafe fn checkFixedTypeLayout(ty: super::Type, size: u32, alignment: u32) { |
|
| 298 | + | let layout = super::getTypeLayout(ty); |
|
| 299 | + | assert layout.size == size and layout.alignment == alignment; |
|
| 300 | + | let mut arena = alloc::new(&mut FIXED_LAYOUT_STORAGE[..]); |
|
| 301 | + | let itemStorage = try! alloc::alloc(&mut arena, @sizeOf(super::Type), @alignOf(super::Type)) as *mut super::Type; |
|
| 302 | + | set *itemStorage = ty; |
|
| 303 | + | let item: *super::Type = itemStorage; |
|
| 304 | + | for length in [0 as u32, 1, 3] { |
|
| 305 | + | let array = super::getTypeLayout(super::Type::Array(super::ArrayType { item, length })); |
|
| 306 | + | assert array.size == size * length and array.alignment == alignment; |
|
| 307 | + | } |
|
| 308 | + | let optional = super::getTypeLayout(super::Type::Optional(item)); |
|
| 309 | + | let expected = layout if super::isNullableType(ty) else super::getOptionalAggregateLayout(layout); |
|
| 310 | + | assert optional.size == expected.size and optional.alignment == expected.alignment; |
|
| 311 | + | } |
|
| 312 | + | ||
| 313 | + | /// Storage for one owned type used by array and optional layout fixtures. |
|
| 314 | + | static FIXED_LAYOUT_STORAGE: [u8; 256] = [0; 256]; |
|
| 315 | + | ||
| 262 | 316 | /// Array layouts retain element alignment for empty and populated arrays. |
|
| 263 | 317 | @test fn testArrayLayoutFromElementLayout() throws (testing::TestError) { |
|
| 264 | 318 | for alignment in [1 as u32, 2, 4, 8, 16] { |
|
| 265 | 319 | let item = super::Layout { size: alignment, alignment }; |
|
| 266 | 320 | for count in [0 as u32, 1, 3] { |