refactor(lang): Share generic type substitution

3225bfc0f4e8414a142c1c31c309d6172bf96408739626c07578605ac8253188
Keep resolver-owned semantic work and read-only lowering under one
recursive type traversal. Select allocation and error behavior through a
private context union, and write lowering results only to lowerer-owned
arenas.
Alexis Sellier committed ago 1 parent a0848143
lib/std/lang/lower.rad +9 -261
361 361
        }
362 362
    }
363 363
    return maxSize;
364 364
}
365 365
366 -
/// Allocate one persistent structural type component.
367 -
fn allocPersistentType(
368 -
    self: *mut Lowerer,
369 -
    ty: resolver::Type,
370 -
) -> *resolver::Type {
371 -
    let stored = try! alloc::alloc(
372 -
        self.arena,
373 -
        @sizeOf(resolver::Type),
374 -
        @alignOf(resolver::Type),
375 -
    ) as *mut resolver::Type;
376 -
    set *stored = ty;
377 -
    return stored;
378 -
}
379 -
380 -
/// Copy a concrete structural type into persistent lowering storage.
381 -
///
382 -
/// Nominal pointers retain their canonical identity; only structural
383 -
/// components that may live in the function arena are copied.
384 -
fn persistStructuralType(
385 -
    self: *mut Lowerer,
386 -
    ty: resolver::Type,
387 -
) -> resolver::Type {
388 -
    match ty {
389 -
        case resolver::Type::Pointer(pointer) => {
390 -
            let target = persistStructuralType(self, *pointer.target);
391 -
            return resolver::Type::Pointer(resolver::PointerType {
392 -
                class: pointer.class,
393 -
                target: allocPersistentType(self, target),
394 -
                mutable: pointer.mutable,
395 -
            });
396 -
        }
397 -
        case resolver::Type::Slice(slice) => {
398 -
            let item = persistStructuralType(self, *slice.item);
399 -
            return resolver::Type::Slice(resolver::SliceType {
400 -
                class: slice.class,
401 -
                item: allocPersistentType(self, item),
402 -
                mutable: slice.mutable,
403 -
            });
404 -
        }
405 -
        case resolver::Type::Array(array) => {
406 -
            let item = persistStructuralType(self, *array.item);
407 -
            return resolver::Type::Array(resolver::ArrayType {
408 -
                item: allocPersistentType(self, item),
409 -
                length: array.length,
410 -
            });
411 -
        }
412 -
        case resolver::Type::Optional(inner) => {
413 -
            let stored = persistStructuralType(self, *inner);
414 -
            return resolver::Type::Optional(allocPersistentType(self, stored));
415 -
        }
416 -
        case resolver::Type::Fn(info) => {
417 -
            let mut params: *mut [*resolver::Type] = &mut [];
418 -
            let mut throwList: *mut [*resolver::Type] = &mut [];
419 -
            for param in info.paramTypes {
420 -
                let stored = persistStructuralType(self, *param);
421 -
                params.append(allocPersistentType(self, stored), self.allocator);
422 -
            }
423 -
            for thrown in info.throwList {
424 -
                let stored = persistStructuralType(self, *thrown);
425 -
                throwList.append(
426 -
                    allocPersistentType(self, stored), self.allocator
427 -
                );
428 -
            }
429 -
            let returnType = persistStructuralType(self, *info.returnType);
430 -
            let storedInfo = try! alloc::alloc(
431 -
                self.arena,
432 -
                @sizeOf(resolver::FnType),
433 -
                @alignOf(resolver::FnType),
434 -
            ) as *mut resolver::FnType;
435 -
            set *storedInfo = resolver::FnType {
436 -
                paramTypes: &params[..],
437 -
                returnType: allocPersistentType(self, returnType),
438 -
                throwList: &throwList[..],
439 -
                isUnsafe: info.isUnsafe,
440 -
                localCount: info.localCount,
441 -
            };
442 -
            return resolver::Type::Fn(storedInfo);
443 -
        }
444 -
        case resolver::Type::Range { start, end } => {
445 -
            let mut storedStart: ?*resolver::Type = nil;
446 -
            let mut storedEnd: ?*resolver::Type = nil;
447 -
            if let value = start {
448 -
                let stored = persistStructuralType(self, *value);
449 -
                set storedStart = allocPersistentType(self, stored);
450 -
            }
451 -
            if let value = end {
452 -
                let stored = persistStructuralType(self, *value);
453 -
                set storedEnd = allocPersistentType(self, stored);
454 -
            }
455 -
            return resolver::Type::Range {
456 -
                start: storedStart,
457 -
                end: storedEnd,
458 -
            };
459 -
        }
460 -
        else => return ty,
461 -
    }
462 -
}
463 -
464 366
/// Get or assign a globally unique tag for a concrete error type.
465 367
fn getOrAssignErrorTag(
466 368
    self: *mut Lowerer,
467 369
    errType: resolver::Type,
468 370
) -> u32 {
473 375
        }
474 376
    }
475 377
    let tag = self.errTagCounter;
476 378
    set self.errTagCounter += 1;
477 379
    self.errTags.append(ErrTagEntry {
478 -
        ty: persistStructuralType(self, errType),
380 +
        ty: resolver::copyStructuralTypeToArena(errType, self.arena),
479 381
        tag,
480 382
    }, self.allocator);
481 383
    return tag;
482 384
}
483 385
2345 2247
    let reg = il::Reg { n: self.regCounter };
2346 2248
    set self.regCounter += 1;
2347 2249
    return reg;
2348 2250
}
2349 2251
2350 -
/// Allocate a structural type component in the current function arena.
2351 -
fn allocSpecializedType(
2352 -
    self: *mut FnLowerer,
2353 -
    ty: resolver::Type,
2354 -
) -> *resolver::Type {
2355 -
    let value = try! alloc::alloc(
2356 -
        self.low.fnArena,
2357 -
        @sizeOf(resolver::Type),
2358 -
        @alignOf(resolver::Type),
2359 -
    ) as *mut resolver::Type;
2360 -
    set *value = ty;
2361 -
    return value;
2362 -
}
2363 -
2364 2252
/// Apply the current generic function specialization without mutating resolver
2365 2253
/// metadata or allocating from resolver-owned arenas.
2366 2254
fn specializeType(
2367 2255
    self: *mut FnLowerer,
2368 2256
    ty: resolver::Type,
2369 2257
) -> resolver::Type throws (LowerError) {
2370 2258
    let sub = self.low.specialization else return ty;
2371 -
    if not resolver::containsGenericParameter(ty) {
2372 -
        return ty;
2373 -
    }
2374 -
    match ty {
2375 -
        case resolver::Type::Parameter(param) =>
2376 -
            return resolver::substitutionArg(sub, param),
2377 -
        case resolver::Type::ConstParameter(param) =>
2378 -
            return resolver::substitutionArg(sub, param),
2379 -
        case resolver::Type::GenericConstExpr { type, expr } => {
2380 -
            let value = resolver::constIntWithSubstitution(
2381 -
                self.low.resolver, expr, *type, sub
2382 -
            ) else throw LowerError::MissingMetadata;
2383 -
            return resolver::Type::ConstArgument { type, value };
2384 -
        }
2385 -
        case resolver::Type::Pointer(pointer) => {
2386 -
            let target = try specializeType(self, *pointer.target);
2387 -
            return resolver::Type::Pointer(resolver::PointerType {
2388 -
                class: pointer.class,
2389 -
                target: allocSpecializedType(self, target),
2390 -
                mutable: pointer.mutable,
2391 -
            });
2392 -
        }
2393 -
        case resolver::Type::Slice(slice) => {
2394 -
            let item = try specializeType(self, *slice.item);
2395 -
            return resolver::Type::Slice(resolver::SliceType {
2396 -
                class: slice.class,
2397 -
                item: allocSpecializedType(self, item),
2398 -
                mutable: slice.mutable,
2399 -
            });
2400 -
        }
2401 -
        case resolver::Type::Array(array) => {
2402 -
            let item = try specializeType(self, *array.item);
2403 -
            return resolver::Type::Array(resolver::ArrayType {
2404 -
                item: allocSpecializedType(self, item),
2405 -
                length: array.length,
2406 -
            });
2407 -
        }
2408 -
        case resolver::Type::GenericArray { item, length } => {
2409 -
            let concreteItem = try specializeType(self, *item);
2410 -
            let value = resolver::constIntWithSubstitution(
2411 -
                self.low.resolver, length, resolver::Type::U32, sub
2412 -
            ) else throw LowerError::MissingMetadata;
2413 -
            return resolver::Type::Array(resolver::ArrayType {
2414 -
                item: allocSpecializedType(self, concreteItem),
2415 -
                length: value.magnitude as u32,
2416 -
            });
2417 -
        }
2418 -
        case resolver::Type::Optional(inner) => {
2419 -
            let value = try specializeType(self, *inner);
2420 -
            return resolver::Type::Optional(allocSpecializedType(self, value));
2421 -
        }
2422 -
        case resolver::Type::GenericDataApply(app) => {
2423 -
            let mut args: *mut [*resolver::Type] = &mut [];
2424 -
            for arg in app.args {
2425 -
                let concrete = try specializeType(self, *arg);
2426 -
                if resolver::containsGenericParameter(concrete) {
2427 -
                    throw LowerError::MissingMetadata;
2428 -
                }
2429 -
                args.append(allocSpecializedType(self, concrete), self.allocator);
2430 -
            }
2431 -
            let specialization = resolver::findGenericDataSpecialization(
2432 -
                self.low.resolver, app.template, &args[..]
2433 -
            ) else throw LowerError::MissingMetadata;
2434 -
            return resolver::Type::Nominal(specialization.nominal);
2435 -
        }
2436 -
        case resolver::Type::GenericRecord(genericRecord) => {
2437 -
            let mut fields: *mut [resolver::RecordField] = &mut [];
2438 -
            let mut offset: u32 = 0;
2439 -
            let mut alignment: u32 = 1;
2440 -
            for field in genericRecord.fields {
2441 -
                let fieldType = try specializeType(self, field.fieldType);
2442 -
                if resolver::containsGenericParameter(fieldType) {
2443 -
                    throw LowerError::MissingMetadata;
2444 -
                }
2445 -
                let fieldLayout = resolver::getTypeLayout(fieldType);
2446 -
                set offset = mem::alignUp(offset, fieldLayout.alignment);
2447 -
                fields.append(resolver::RecordField {
2448 -
                    name: field.name,
2449 -
                    fieldType: persistStructuralType(self.low, fieldType),
2450 -
                    offset: offset as i32,
2451 -
                }, self.low.allocator);
2452 -
                set offset += fieldLayout.size;
2453 -
                if fieldLayout.alignment > alignment {
2454 -
                    set alignment = fieldLayout.alignment;
2455 -
                }
2456 -
            }
2457 -
            let nominal = try! alloc::alloc(
2458 -
                self.low.arena,
2459 -
                @sizeOf(resolver::NominalType),
2460 -
                @alignOf(resolver::NominalType),
2461 -
            ) as *mut resolver::NominalType;
2462 -
            set *nominal = resolver::NominalType::Record(resolver::RecordType {
2463 -
                fields: &fields[..],
2464 -
                labeled: genericRecord.labeled,
2465 -
                layout: resolver::Layout {
2466 -
                    size: mem::alignUp(offset, alignment),
2467 -
                    alignment,
2468 -
                },
2469 -
                declaredLinear: false,
2470 -
            });
2471 -
            return resolver::Type::Nominal(nominal);
2472 -
        }
2473 -
        case resolver::Type::Fn(info) => {
2474 -
            let mut params: *mut [*resolver::Type] = &mut [];
2475 -
            let mut throwTypes: *mut [*resolver::Type] = &mut [];
2476 -
            for param in info.paramTypes {
2477 -
                let concrete = try specializeType(self, *param);
2478 -
                params.append(allocSpecializedType(self, concrete), self.allocator);
2479 -
            }
2480 -
            for thrown in info.throwList {
2481 -
                let concrete = try specializeType(self, *thrown);
2482 -
                throwTypes.append(
2483 -
                    allocSpecializedType(self, concrete), self.allocator
2484 -
                );
2485 -
            }
2486 -
            let result = try specializeType(self, *info.returnType);
2487 -
            let fnType = try! alloc::alloc(
2488 -
                self.low.fnArena,
2489 -
                @sizeOf(resolver::FnType),
2490 -
                @alignOf(resolver::FnType),
2491 -
            ) as *mut resolver::FnType;
2492 -
            set *fnType = resolver::FnType {
2493 -
                paramTypes: &params[..],
2494 -
                returnType: allocSpecializedType(self, result),
2495 -
                throwList: &throwTypes[..],
2496 -
                isUnsafe: info.isUnsafe,
2497 -
                localCount: info.localCount,
2498 -
            };
2499 -
            return resolver::Type::Fn(fnType);
2500 -
        }
2501 -
        case resolver::Type::Range { start, end } => {
2502 -
            let mut concreteStart: ?*resolver::Type = nil;
2503 -
            let mut concreteEnd: ?*resolver::Type = nil;
2504 -
            if let value = start {
2505 -
                let concrete = try specializeType(self, *value);
2506 -
                set concreteStart = allocSpecializedType(self, concrete);
2507 -
            }
2508 -
            if let value = end {
2509 -
                let concrete = try specializeType(self, *value);
2510 -
                set concreteEnd = allocSpecializedType(self, concrete);
2511 -
            }
2512 -
            return resolver::Type::Range {
2513 -
                start: concreteStart,
2514 -
                end: concreteEnd,
2515 -
            };
2516 -
        }
2517 -
        else => return ty,
2518 -
    }
2259 +
    let specialized = resolver::substituteTypeReadOnly(
2260 +
        self.low.resolver,
2261 +
        ty,
2262 +
        sub,
2263 +
        self.low.fnArena,
2264 +
        self.low.arena,
2265 +
    ) else throw LowerError::MissingMetadata;
2266 +
    return specialized;
2519 2267
}
2520 2268
2521 2269
/// Look up the resolved type of an AST node, or throw `MissingType`.
2522 2270
fn typeOf(self: *mut FnLowerer, node: *ast::Node) -> resolver::Type throws (LowerError) {
2523 2271
    let ty = resolver::typeFor(self.low.resolver, node)
lib/std/lang/resolver.rad +309 -77
1437 1437
        return Type::ConstParameter(param);
1438 1438
    }
1439 1439
    return Type::Parameter(param);
1440 1440
}
1441 1441
1442 -
/// Recursively replace rigid parameters in a resolved type.
1443 -
export fn substituteType(
1444 -
    self: *mut Resolver,
1442 +
/// Ownership and error policy for type substitution.
1443 +
union TypeSubstitutionContext {
1444 +
    /// Resolver-owned substitution that may emit diagnostics.
1445 +
    Resolution {
1446 +
        /// Mutable resolver that owns the result.
1447 +
        resolver: *mut Resolver,
1448 +
        /// Source site for structural type diagnostics.
1449 +
        site: *ast::Node,
1450 +
    },
1451 +
    /// Read-only substitution into caller-owned arenas.
1452 +
    ReadOnly {
1453 +
        /// Immutable resolver metadata.
1454 +
        resolver: *Resolver,
1455 +
        /// Arena for function-local structural types.
1456 +
        structuralArena: *mut alloc::Arena,
1457 +
        /// Arena for structural types that outlive the function.
1458 +
        persistentArena: *mut alloc::Arena,
1459 +
    },
1460 +
}
1461 +
1462 +
/// Return the arena for temporary structural substitution results.
1463 +
fn substitutionArena(context: *mut TypeSubstitutionContext) -> *mut alloc::Arena {
1464 +
    match *context {
1465 +
        case TypeSubstitutionContext::Resolution { resolver, .. } =>
1466 +
            return &mut resolver.arena,
1467 +
        case TypeSubstitutionContext::ReadOnly { structuralArena, .. } =>
1468 +
            return structuralArena,
1469 +
    }
1470 +
}
1471 +
1472 +
/// Allocate a nested substituted type with the selected ownership policy.
1473 +
fn allocSubstitutedType(
1474 +
    context: *mut TypeSubstitutionContext,
1475 +
    ty: Type,
1476 +
) -> *Type {
1477 +
    if let case TypeSubstitutionContext::Resolution { resolver, .. } = *context {
1478 +
        return allocType(resolver, ty);
1479 +
    }
1480 +
    let stored = try! alloc::alloc(
1481 +
        substitutionArena(context), @sizeOf(Type), @alignOf(Type)
1482 +
    ) as *mut Type;
1483 +
    set *stored = ty;
1484 +
    return stored;
1485 +
}
1486 +
1487 +
/// Allocate one type component in an explicit arena.
1488 +
fn allocArenaType(arena: *mut alloc::Arena, ty: Type) -> *Type {
1489 +
    let stored = try! alloc::alloc(
1490 +
        arena, @sizeOf(Type), @alignOf(Type)
1491 +
    ) as *mut Type;
1492 +
    set *stored = ty;
1493 +
    return stored;
1494 +
}
1495 +
1496 +
/// Copy a concrete structural type into the given arena.
1497 +
///
1498 +
/// Nominal pointers retain their canonical identity.
1499 +
export fn copyStructuralTypeToArena(
1500 +
    ty: Type,
1501 +
    arena: *mut alloc::Arena,
1502 +
) -> Type {
1503 +
    match ty {
1504 +
        case Type::Pointer(pointer) => {
1505 +
            let target = copyStructuralTypeToArena(*pointer.target, arena);
1506 +
            return Type::Pointer(PointerType {
1507 +
                class: pointer.class,
1508 +
                target: allocArenaType(arena, target),
1509 +
                mutable: pointer.mutable,
1510 +
            });
1511 +
        }
1512 +
        case Type::Slice(slice) => {
1513 +
            let item = copyStructuralTypeToArena(*slice.item, arena);
1514 +
            return Type::Slice(SliceType {
1515 +
                class: slice.class,
1516 +
                item: allocArenaType(arena, item),
1517 +
                mutable: slice.mutable,
1518 +
            });
1519 +
        }
1520 +
        case Type::Array(array) => {
1521 +
            let item = copyStructuralTypeToArena(*array.item, arena);
1522 +
            return Type::Array(ArrayType {
1523 +
                item: allocArenaType(arena, item),
1524 +
                length: array.length,
1525 +
            });
1526 +
        }
1527 +
        case Type::Optional(inner) => {
1528 +
            let stored = copyStructuralTypeToArena(*inner, arena);
1529 +
            return Type::Optional(allocArenaType(arena, stored));
1530 +
        }
1531 +
        case Type::Fn(info) => {
1532 +
            let a = alloc::arenaAllocator(arena);
1533 +
            let mut params: *mut [*Type] = &mut [];
1534 +
            let mut throwList: *mut [*Type] = &mut [];
1535 +
            for param in info.paramTypes {
1536 +
                let stored = copyStructuralTypeToArena(*param, arena);
1537 +
                params.append(allocArenaType(arena, stored), a);
1538 +
            }
1539 +
            for thrown in info.throwList {
1540 +
                let stored = copyStructuralTypeToArena(*thrown, arena);
1541 +
                throwList.append(allocArenaType(arena, stored), a);
1542 +
            }
1543 +
            let returnType = copyStructuralTypeToArena(*info.returnType, arena);
1544 +
            let storedInfo = try! alloc::alloc(
1545 +
                arena, @sizeOf(FnType), @alignOf(FnType)
1546 +
            ) as *mut FnType;
1547 +
            set *storedInfo = FnType {
1548 +
                paramTypes: &params[..],
1549 +
                returnType: allocArenaType(arena, returnType),
1550 +
                throwList: &throwList[..],
1551 +
                isUnsafe: info.isUnsafe,
1552 +
                localCount: info.localCount,
1553 +
            };
1554 +
            return Type::Fn(storedInfo);
1555 +
        }
1556 +
        case Type::Range { start, end } => {
1557 +
            let mut storedStart: ?*Type = nil;
1558 +
            let mut storedEnd: ?*Type = nil;
1559 +
            if let value = start {
1560 +
                let stored = copyStructuralTypeToArena(*value, arena);
1561 +
                set storedStart = allocArenaType(arena, stored);
1562 +
            }
1563 +
            if let value = end {
1564 +
                let stored = copyStructuralTypeToArena(*value, arena);
1565 +
                set storedEnd = allocArenaType(arena, stored);
1566 +
            }
1567 +
            return Type::Range { start: storedStart, end: storedEnd };
1568 +
        }
1569 +
        else => return ty,
1570 +
    }
1571 +
}
1572 +
1573 +
/// Recursively replace rigid parameters under the selected policy.
1574 +
fn substituteTypeWithContext(
1575 +
    context: *mut TypeSubstitutionContext,
1445 1576
    ty: Type,
1446 1577
    sub: *Substitution,
1447 -
    site: *ast::Node,
1448 1578
) -> Type throws (ResolveError) {
1449 1579
    if not containsGenericParameter(ty) {
1450 1580
        return ty;
1451 1581
    }
1452 1582
    match ty {
1453 1583
        case Type::Parameter(param) => return substitutionArg(sub, param),
1454 1584
        case Type::ConstParameter(param) => return substitutionArg(sub, param),
1455 1585
        case Type::GenericConstExpr { type, expr } => {
1456 -
            let value = constValueWithSubstitution(self, expr, sub)
1457 -
                else throw emitError(self, expr, ErrorKind::ConstExprRequired);
1458 -
            let case ConstValue::Int(int) = value
1459 -
                else throw emitError(self, expr, ErrorKind::ConstExprRequired);
1460 -
            if not validateConstIntRange(value, *type) {
1461 -
                throw emitError(self, expr, ErrorKind::NumericLiteralOverflow);
1586 +
            match *context {
1587 +
                case TypeSubstitutionContext::Resolution { resolver, .. } => {
1588 +
                    let value = constValueWithSubstitution(resolver, expr, sub)
1589 +
                        else throw emitError(resolver, expr, ErrorKind::ConstExprRequired);
1590 +
                    let case ConstValue::Int(int) = value
1591 +
                        else throw emitError(resolver, expr, ErrorKind::ConstExprRequired);
1592 +
                    if not validateConstIntRange(value, *type) {
1593 +
                        throw emitError(resolver, expr, ErrorKind::NumericLiteralOverflow);
1594 +
                    }
1595 +
                    let case ConstValue::Int(canonical) = castConstInt(int, *type)
1596 +
                        else throw emitError(resolver, expr, ErrorKind::Internal);
1597 +
                    return Type::ConstArgument { type, value: canonical };
1598 +
                }
1599 +
                case TypeSubstitutionContext::ReadOnly { resolver, .. } => {
1600 +
                    let value = constIntWithSubstitution(resolver, expr, *type, sub)
1601 +
                        else throw ResolveError::Failure;
1602 +
                    return Type::ConstArgument { type, value };
1603 +
                }
1462 1604
            }
1463 -
            let case ConstValue::Int(canonical) = castConstInt(int, *type)
1464 -
                else throw emitError(self, expr, ErrorKind::Internal);
1465 -
            return Type::ConstArgument { type, value: canonical };
1466 1605
        }
1467 1606
        case Type::Pointer(pointer) => {
1468 -
            let inner = try substituteType(self, *pointer.target, sub, site);
1607 +
            let inner = try substituteTypeWithContext(context, *pointer.target, sub);
1469 1608
            return Type::Pointer(PointerType {
1470 1609
                class: pointer.class,
1471 -
                target: allocType(self, inner),
1610 +
                target: allocSubstitutedType(context, inner),
1472 1611
                mutable: pointer.mutable,
1473 1612
            });
1474 1613
        }
1475 1614
        case Type::Slice(slice) => {
1476 -
            let inner = try substituteType(self, *slice.item, sub, site);
1615 +
            let inner = try substituteTypeWithContext(context, *slice.item, sub);
1477 1616
            return Type::Slice(SliceType {
1478 1617
                class: slice.class,
1479 -
                item: allocType(self, inner),
1618 +
                item: allocSubstitutedType(context, inner),
1480 1619
                mutable: slice.mutable,
1481 1620
            });
1482 1621
        }
1483 1622
        case Type::Array(array) => {
1484 -
            let item = try substituteType(self, *array.item, sub, site);
1623 +
            let item = try substituteTypeWithContext(context, *array.item, sub);
1485 1624
            return Type::Array(ArrayType {
1486 -
                item: allocType(self, item),
1625 +
                item: allocSubstitutedType(context, item),
1487 1626
                length: array.length,
1488 1627
            });
1489 1628
        }
1490 1629
        case Type::GenericArray { item, length } => {
1491 -
            let concreteItem = try substituteType(self, *item, sub, site);
1492 -
            let value = constValueWithSubstitution(self, length, sub)
1493 -
                else throw emitError(self, length, ErrorKind::ConstExprRequired);
1494 -
            if not validateConstIntRange(value, Type::U32) {
1495 -
                throw emitError(self, length, ErrorKind::NumericLiteralOverflow);
1630 +
            let concreteItem = try substituteTypeWithContext(context, *item, sub);
1631 +
            let mut arrayLength: u32 = 0;
1632 +
            match *context {
1633 +
                case TypeSubstitutionContext::Resolution { resolver, .. } => {
1634 +
                    let value = constValueWithSubstitution(resolver, length, sub)
1635 +
                        else throw emitError(resolver, length, ErrorKind::ConstExprRequired);
1636 +
                    if not validateConstIntRange(value, Type::U32) {
1637 +
                        throw emitError(resolver, length, ErrorKind::NumericLiteralOverflow);
1638 +
                    }
1639 +
                    let case ConstValue::Int(int) = value
1640 +
                        else throw emitError(resolver, length, ErrorKind::ConstExprRequired);
1641 +
                    set arrayLength = int.magnitude as u32;
1642 +
                }
1643 +
                case TypeSubstitutionContext::ReadOnly { resolver, .. } => {
1644 +
                    let value = constIntWithSubstitution(
1645 +
                        resolver, length, Type::U32, sub
1646 +
                    ) else throw ResolveError::Failure;
1647 +
                    set arrayLength = value.magnitude as u32;
1648 +
                }
1496 1649
            }
1497 -
            let case ConstValue::Int(int) = value
1498 -
                else throw emitError(self, length, ErrorKind::ConstExprRequired);
1499 1650
            return Type::Array(ArrayType {
1500 -
                item: allocType(self, concreteItem),
1501 -
                length: int.magnitude as u32,
1651 +
                item: allocSubstitutedType(context, concreteItem),
1652 +
                length: arrayLength,
1502 1653
            });
1503 1654
        }
1504 1655
        case Type::Optional(inner) => {
1505 -
            let value = try substituteType(self, *inner, sub, site);
1506 -
            return Type::Optional(allocType(self, value));
1656 +
            let value = try substituteTypeWithContext(context, *inner, sub);
1657 +
            return Type::Optional(allocSubstitutedType(context, value));
1507 1658
        }
1508 1659
        case Type::GenericDataApply(app) => {
1509 -
            let a = alloc::arenaAllocator(&mut self.arena);
1660 +
            let a = alloc::arenaAllocator(substitutionArena(context));
1510 1661
            let mut args: *mut [*Type] = &mut [];
1511 1662
            let mut symbolic = false;
1512 1663
            for arg in app.args {
1513 -
                let replacement = try substituteType(self, *arg, sub, site);
1664 +
                let replacement = try substituteTypeWithContext(context, *arg, sub);
1514 1665
                set symbolic = symbolic or containsGenericParameter(replacement);
1515 -
                args.append(allocType(self, replacement), a);
1516 -
            }
1517 -
            if symbolic {
1518 -
                let application = try! alloc::alloc(
1519 -
                    &mut self.arena,
1520 -
                    @sizeOf(GenericDataApplyType),
1521 -
                    @alignOf(GenericDataApplyType),
1522 -
                ) as *mut GenericDataApplyType;
1523 -
                set *application = GenericDataApplyType {
1524 -
                    template: app.template,
1525 -
                    args: &args[..],
1526 -
                    site: app.site,
1527 -
                };
1528 -
                return Type::GenericDataApply(application);
1666 +
                args.append(allocSubstitutedType(context, replacement), a);
1667 +
            }
1668 +
            match *context {
1669 +
                case TypeSubstitutionContext::Resolution { resolver, .. } => {
1670 +
                    if symbolic {
1671 +
                        let application = try! alloc::alloc(
1672 +
                            &mut resolver.arena,
1673 +
                            @sizeOf(GenericDataApplyType),
1674 +
                            @alignOf(GenericDataApplyType),
1675 +
                        ) as *mut GenericDataApplyType;
1676 +
                        set *application = GenericDataApplyType {
1677 +
                            template: app.template,
1678 +
                            args: &args[..],
1679 +
                            site: app.site,
1680 +
                        };
1681 +
                        return Type::GenericDataApply(application);
1682 +
                    }
1683 +
                    let nominal = try specializeGenericData(
1684 +
                        resolver, app.site, app.template, &args[..], false
1685 +
                    );
1686 +
                    return Type::Nominal(nominal);
1687 +
                }
1688 +
                case TypeSubstitutionContext::ReadOnly { resolver, .. } => {
1689 +
                    if symbolic {
1690 +
                        throw ResolveError::Failure;
1691 +
                    }
1692 +
                    let specialization = findGenericDataSpecialization(
1693 +
                        resolver, app.template, &args[..]
1694 +
                    ) else throw ResolveError::Failure;
1695 +
                    return Type::Nominal(specialization.nominal);
1696 +
                }
1529 1697
            }
1530 -
            let nominal = try specializeGenericData(
1531 -
                self, app.site, app.template, &args[..], false
1532 -
            );
1533 -
            return Type::Nominal(nominal);
1534 1698
        }
1535 1699
        case Type::GenericRecord(rec) => {
1536 -
            let a = alloc::arenaAllocator(&mut self.arena);
1700 +
            let mut persistentArena = substitutionArena(context);
1701 +
            if let case TypeSubstitutionContext::ReadOnly {
1702 +
                persistentArena: arena,
1703 +
                ..
1704 +
            } = *context {
1705 +
                set persistentArena = arena;
1706 +
            }
1707 +
            let a = alloc::arenaAllocator(persistentArena);
1537 1708
            let mut fields: *mut [RecordField] = &mut [];
1538 1709
            let mut offset: u32 = 0;
1539 1710
            let mut alignment: u32 = 1;
1540 1711
            for field in rec.fields {
1541 -
                let fieldType = try substituteType(self, field.fieldType, sub, site);
1542 -
                if hasUnresolvedNominalLayout(fieldType) {
1543 -
                    throw emitError(self, site, ErrorKind::GenericRecursiveLayout);
1712 +
                let fieldType = try substituteTypeWithContext(
1713 +
                    context, field.fieldType, sub
1714 +
                );
1715 +
                let mut storedFieldType = fieldType;
1716 +
                match *context {
1717 +
                    case TypeSubstitutionContext::Resolution { resolver, site } => {
1718 +
                        if hasUnresolvedNominalLayout(fieldType) {
1719 +
                            throw emitError(
1720 +
                                resolver, site, ErrorKind::GenericRecursiveLayout
1721 +
                            );
1722 +
                        }
1723 +
                        try ensureStorableType(resolver, site, fieldType);
1724 +
                        try ensureTypeResolved(resolver, fieldType, site);
1725 +
                    }
1726 +
                    case TypeSubstitutionContext::ReadOnly { .. } => {
1727 +
                        if containsGenericParameter(fieldType) {
1728 +
                            throw ResolveError::Failure;
1729 +
                        }
1730 +
                        set storedFieldType = copyStructuralTypeToArena(
1731 +
                            fieldType, persistentArena
1732 +
                        );
1733 +
                    }
1544 1734
                }
1545 -
                try ensureStorableType(self, site, fieldType);
1546 -
                try ensureTypeResolved(self, fieldType, site);
1547 1735
                let fieldLayout = getTypeLayout(fieldType);
1548 1736
                set offset = mem::alignUp(offset, fieldLayout.alignment);
1549 1737
                fields.append(RecordField {
1550 1738
                    name: field.name,
1551 -
                    fieldType,
1739 +
                    fieldType: storedFieldType,
1552 1740
                    offset: offset as i32,
1553 1741
                }, a);
1554 1742
                set offset += fieldLayout.size;
1555 1743
                set alignment = max(alignment, fieldLayout.alignment);
1556 1744
            }
1557 -
            let layout = Layout {
1558 -
                size: mem::alignUp(offset, alignment),
1559 -
                alignment,
1560 -
            };
1561 -
            return Type::Nominal(allocNominalType(self, NominalType::Record(RecordType {
1745 +
            let nominal = try! alloc::alloc(
1746 +
                persistentArena, @sizeOf(NominalType), @alignOf(NominalType)
1747 +
            ) as *mut NominalType;
1748 +
            set *nominal = NominalType::Record(RecordType {
1562 1749
                fields: &fields[..],
1563 1750
                labeled: rec.labeled,
1564 -
                layout,
1751 +
                layout: Layout {
1752 +
                    size: mem::alignUp(offset, alignment),
1753 +
                    alignment,
1754 +
                },
1565 1755
                declaredLinear: false,
1566 -
            })));
1756 +
            });
1757 +
            return Type::Nominal(nominal);
1567 1758
        }
1568 1759
        case Type::Fn(info) => {
1569 -
            let a = alloc::arenaAllocator(&mut self.arena);
1760 +
            let arena = substitutionArena(context);
1761 +
            let a = alloc::arenaAllocator(arena);
1570 1762
            let mut params: *mut [*Type] = &mut [];
1571 1763
            let mut throwTypes: *mut [*Type] = &mut [];
1572 1764
            for param in info.paramTypes {
1573 -
                let concrete = try substituteType(self, *param, sub, site);
1574 -
                params.append(allocType(self, concrete), a);
1765 +
                let concrete = try substituteTypeWithContext(context, *param, sub);
1766 +
                params.append(allocSubstitutedType(context, concrete), a);
1575 1767
            }
1576 1768
            for thrown in info.throwList {
1577 -
                let concrete = try substituteType(self, *thrown, sub, site);
1578 -
                throwTypes.append(allocType(self, concrete), a);
1769 +
                let concrete = try substituteTypeWithContext(context, *thrown, sub);
1770 +
                throwTypes.append(allocSubstitutedType(context, concrete), a);
1579 1771
            }
1580 -
            let result = try substituteType(self, *info.returnType, sub, site);
1581 -
            return Type::Fn(allocFnType(self, FnType {
1772 +
            let result = try substituteTypeWithContext(
1773 +
                context, *info.returnType, sub
1774 +
            );
1775 +
            let fnType = try! alloc::alloc(
1776 +
                arena, @sizeOf(FnType), @alignOf(FnType)
1777 +
            ) as *mut FnType;
1778 +
            set *fnType = FnType {
1582 1779
                paramTypes: &params[..],
1583 -
                returnType: allocType(self, result),
1780 +
                returnType: allocSubstitutedType(context, result),
1584 1781
                throwList: &throwTypes[..],
1585 1782
                isUnsafe: info.isUnsafe,
1586 1783
                localCount: info.localCount,
1587 -
            }));
1784 +
            };
1785 +
            return Type::Fn(fnType);
1588 1786
        }
1589 1787
        case Type::Range { start, end } => {
1590 1788
            let mut newStart: ?*Type = nil;
1591 1789
            let mut newEnd: ?*Type = nil;
1592 1790
            if let value = start {
1593 -
                let concrete = try substituteType(self, *value, sub, site);
1594 -
                set newStart = allocType(self, concrete);
1791 +
                let concrete = try substituteTypeWithContext(context, *value, sub);
1792 +
                set newStart = allocSubstitutedType(context, concrete);
1595 1793
            }
1596 1794
            if let value = end {
1597 -
                let concrete = try substituteType(self, *value, sub, site);
1598 -
                set newEnd = allocType(self, concrete);
1795 +
                let concrete = try substituteTypeWithContext(context, *value, sub);
1796 +
                set newEnd = allocSubstitutedType(context, concrete);
1599 1797
            }
1600 1798
            return Type::Range { start: newStart, end: newEnd };
1601 1799
        }
1602 1800
        else => return ty,
1603 1801
    }
1604 1802
}
1605 1803
1804 +
/// Recursively replace rigid parameters in a resolved type.
1805 +
export fn substituteType(
1806 +
    self: *mut Resolver,
1807 +
    ty: Type,
1808 +
    sub: *Substitution,
1809 +
    site: *ast::Node,
1810 +
) -> Type throws (ResolveError) {
1811 +
    let mut context = TypeSubstitutionContext::Resolution {
1812 +
        resolver: self,
1813 +
        site,
1814 +
    };
1815 +
    return try substituteTypeWithContext(&mut context, ty, sub);
1816 +
}
1817 +
1818 +
/// Substitute a type using immutable metadata and caller-owned arenas.
1819 +
///
1820 +
/// Return `nil` when required specialization metadata is missing.
1821 +
export fn substituteTypeReadOnly(
1822 +
    self: *Resolver,
1823 +
    ty: Type,
1824 +
    sub: *Substitution,
1825 +
    structuralArena: *mut alloc::Arena,
1826 +
    persistentArena: *mut alloc::Arena,
1827 +
) -> ?Type {
1828 +
    let mut context = TypeSubstitutionContext::ReadOnly {
1829 +
        resolver: self,
1830 +
        structuralArena,
1831 +
        persistentArena,
1832 +
    };
1833 +
    return try substituteTypeWithContext(&mut context, ty, sub) catch {
1834 +
        return nil;
1835 +
    };
1836 +
}
1837 +
1606 1838
/// Allocate a nominal type descriptor and return a pointer to it.
1607 1839
fn allocNominalType(self: *mut Resolver, info: NominalType) -> *mut NominalType {
1608 1840
    // Nb. We don't attempt to de-duplicate nominal type entries,
1609 1841
    // since they don't carry node information and we create
1610 1842
    // placeholder entries when binding symbols.