compiler: Compute fixed type layouts in checked code

25a7fab888a2a9c88863776a244768897aab5ab7a9024e05c53882a6a1d8fe29
Alexis Sellier committed ago 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(&region), 16, 8);
285 +
    for class in [types::PointerClass::Owned, types::PointerClass::Ref,
286 +
                  types::PointerClass::Unsafe, types::PointerClass::Region(&region)] {
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] {