Use nested deref patterns to simplify compiler code

9875993b24336e99dfecf83f56db50e87e73f277493e711f0f4e6e68dc536dae
Now that the seed supports auto-deref patterns, use them throughout the
compiler to collapse sequential let-case/if-let-case chains that
destructure through pointers.

Resolver:
- getRecord(): Type::Nominal(NominalType::Record(recInfo))
- isVoidUnion(): Type::Nominal(NominalType::Union(unionType))
- resolveMatch(): Type::Nominal(NominalType::Union(u))
- resolveFnDeclBody(): SymbolData::Value { type: Type::Fn(fnType), .. }
- resolveInstanceMethodBodies(): same nested record pattern

Lowerer:
- recordInfoFromType()/unionInfoFromType(): single nested pattern
- isAggregateType(): Type::Optional(Type::Pointer { .. })
- optionalNilReg(): match with nested Optional patterns
- buildNilOptional(): match with nested Optional patterns
- lowerConstUnionVariantInto(): Type::Nominal(NominalType::Record(..))
- lowerAggregateEq(): Type::Optional(Type::Slice { .. })

Resolver tests:
- Collapsed SymbolData::Value + Type::Fn patterns (~10 instances)
- Collapsed Type::Nominal + NominalType::Record patterns
Alexis Sellier committed ago 1 parent 97607e7a
lib/std/lang/lower.rad +37 -48
1509 1509
            });
1510 1510
        }
1511 1511
        return;
1512 1512
    }
1513 1513
1514 -
    let case resolver::Type::Nominal(payloadNominal) = payloadType else {
1515 -
        throw LowerError::MissingMetadata;
1516 -
    };
1517 -
    let case resolver::NominalType::Record(payloadRec) = *payloadNominal else {
1514 +
    let case resolver::Type::Nominal(resolver::NominalType::Record(payloadRec)) = payloadType else {
1518 1515
        throw LowerError::ExpectedRecord;
1519 1516
    };
1520 1517
    let payloadLayout = payloadRec.layout;
1521 1518
    try lowerConstRecordCtorInto(self, payloadArgs, payloadRec, dataPrefix, b);
1522 1519
2980 2977
/// Get the register to compare against `0` for optional `nil` checking.
2981 2978
/// For null-ptr-optimized types, loads the data pointer, or returns it
2982 2979
/// directly for scalar pointers. For aggregates, returns the tag register.
2983 2980
fn optionalNilReg(self: *mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) {
2984 2981
    let reg = emitValToReg(self, val);
2985 -
    let case resolver::Type::Optional(inner) = typ else return reg;
2986 2982
2987 -
    if let case resolver::Type::Slice { .. } = *inner {
2988 -
        let ptrReg = nextReg(self);
2989 -
        emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET);
2990 -
        return ptrReg;
2991 -
    }
2992 -
    if let case resolver::Type::Pointer { .. } = *inner {
2993 -
        return reg;
2983 +
    match typ {
2984 +
        case resolver::Type::Optional(resolver::Type::Slice { .. }) => {
2985 +
            let ptrReg = nextReg(self);
2986 +
            emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET);
2987 +
            return ptrReg;
2988 +
        }
2989 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => return reg,
2990 +
        case resolver::Type::Optional(_) => return tvalTagReg(self, reg),
2991 +
        else => return reg,
2994 2992
    }
2995 -
    return tvalTagReg(self, reg);
2996 2993
}
2997 2994
2998 2995
/// Lower an optional nil check (`opt == nil` or `opt != nil`).
2999 2996
fn lowerNilCheck(self: *mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) {
3000 2997
    let optTy = resolver::typeFor(self.low.resolver, opt) else {
3808 3805
// Record and Aggregate Type Helpers //
3809 3806
///////////////////////////////////////
3810 3807
3811 3808
/// Extract the nominal record info from a resolver type.
3812 3809
fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType {
3813 -
    let case resolver::Type::Nominal(info) = typ else {
3814 -
        return nil;
3815 -
    };
3816 -
    let case resolver::NominalType::Record(recInfo) = *info else {
3817 -
        return nil;
3818 -
    };
3810 +
    let case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) = typ
3811 +
        else return nil;
3819 3812
    return recInfo;
3820 3813
}
3821 3814
3822 3815
/// Extract the nominal union info from a resolver type.
3823 3816
fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType {
3824 -
    let case resolver::Type::Nominal(info) = typ else {
3825 -
        return nil;
3826 -
    };
3827 -
    let case resolver::NominalType::Union(unionInfo) = *info else {
3828 -
        return nil;
3829 -
    };
3817 +
    let case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) = typ
3818 +
        else return nil;
3830 3819
    return unionInfo;
3831 3820
}
3832 3821
3833 3822
/// Return the effective type of a node after any coercion applied by
3834 3823
/// the resolver. `lowerExpr` already materializes the coercion in the
3855 3844
        }
3856 3845
        case resolver::Type::Slice { .. } => return true,
3857 3846
        case resolver::Type::TraitObject { .. } => return true,
3858 3847
        case resolver::Type::Array(_) => return true,
3859 3848
        case resolver::Type::Nil => return true,
3860 -
        else => {
3861 -
            // Optional pointers are scalar (single word). All other
3862 -
            // optionals, including optional slices with NPO, are aggregates.
3863 -
            if let case resolver::Type::Optional(inner) = typ {
3864 -
                if let case resolver::Type::Pointer { .. } = *inner {
3865 -
                    return false;
3866 -
                }
3867 -
                return true;
3868 -
            }
3849 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
3850 +
            // Optional pointers are scalar (single word) due to NPO.
3869 3851
            return false;
3870 3852
        }
3853 +
        case resolver::Type::Optional(_) => {
3854 +
            // All other optionals, including optional slices, are aggregates.
3855 +
            return true;
3856 +
        }
3857 +
        else => return false,
3871 3858
    }
3872 3859
}
3873 3860
3874 3861
/// Check if a resolver type is a small aggregate that can be
3875 3862
/// passed or returned by value in a register.
4030 4017
/// Build a `nil` value for an optional type.
4031 4018
///
4032 4019
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4033 4020
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4034 4021
fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4035 -
    let case resolver::Type::Optional(inner) = optType else {
4036 -
        throw LowerError::ExpectedOptional;
4037 -
    };
4038 -
    if let case resolver::Type::Pointer { .. } = *inner {
4039 -
        return il::Val::Imm(0);
4040 -
    }
4041 -
    if let case resolver::Type::Slice { item, mutable } = *inner {
4042 -
        return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4022 +
    match optType {
4023 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4024 +
            return il::Val::Imm(0);
4025 +
        }
4026 +
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => {
4027 +
            return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4028 +
        }
4029 +
        case resolver::Type::Optional(inner) => {
4030 +
            let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4031 +
            return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4032 +
        }
4033 +
        else => throw LowerError::ExpectedOptional,
4043 4034
    }
4044 -
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4045 -
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4046 4035
}
4047 4036
4048 4037
/// Build a result value for throwing functions.
4049 4038
fn buildResult(
4050 4039
    self: *mut FnLowerer,
4498 4487
    a: il::Reg,
4499 4488
    b: il::Reg,
4500 4489
    offset: i32
4501 4490
) -> il::Val throws (LowerError) {
4502 4491
    match typ {
4492 +
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => {
4493 +
            // Optional slices use null pointer optimization.
4494 +
            return try lowerSliceEq(self, item, mutable, a, b, offset);
4495 +
        }
4503 4496
        case resolver::Type::Optional(inner) => {
4504 -
            if let case resolver::Type::Slice { item, mutable } = *inner {
4505 -
                // Optional slices use null pointer optimization.
4506 -
                return try lowerSliceEq(self, item, mutable, a, b, offset);
4507 -
            }
4508 4497
            return try lowerOptionalEq(self, *inner, a, b, offset);
4509 4498
        }
4510 4499
        case resolver::Type::Slice { item, mutable } =>
4511 4500
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4512 4501
        case resolver::Type::Array(arr) =>
lib/std/lang/resolver.rad +6 -19
1512 1512
    return tag;
1513 1513
}
1514 1514
1515 1515
/// Check if a type is a union without payloads.
1516 1516
pub fn isVoidUnion(ty: Type) -> bool {
1517 -
    let case Type::Nominal(info) = ty
1518 -
        else return false;
1519 -
    let case NominalType::Union(unionType) = *info
1517 +
    let case Type::Nominal(NominalType::Union(unionType)) = ty
1520 1518
        else return false;
1521 1519
    return unionType.isAllVoid;
1522 1520
}
1523 1521
1524 1522
/// Check if a type should be treated as an address-like value.
1869 1867
    }
1870 1868
}
1871 1869
1872 1870
/// Get the record info from a record type.
1873 1871
pub fn getRecord(ty: Type) -> ?RecordType {
1874 -
    let case Type::Nominal(info) = ty else return nil;
1875 -
    let case NominalType::Record(recInfo) = *info else return nil;
1876 -
1872 +
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
1877 1873
    return recInfo;
1878 1874
}
1879 1875
1880 1876
/// Auto-dereference a type: if it's a pointer, return the target type.
1881 1877
pub fn autoDeref(ty: Type) -> Type {
3096 3092
    let sym = symbolFor(self, node) else {
3097 3093
        // The function declaration failed to type check, therefore
3098 3094
        // no symbol was associated with it.
3099 3095
        return;
3100 3096
    };
3101 -
    let case SymbolData::Value { type, .. } = sym.data else {
3097 +
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
3102 3098
        panic "resolveFnDeclBody: unexpected symbol data for function";
3103 3099
    };
3104 -
    let case Type::Fn(fnType) = type else {
3105 -
        panic "resolveFnDeclBody: unexpected type for function";
3106 -
    };
3107 3100
    let retTy = *fnType.returnType;
3108 3101
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3109 3102
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3110 3103
3111 3104
    if isIntrinsic and not isExtern {
3616 3609
3617 3610
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
3618 3611
        // for this method (eg. unknown method name). Skip gracefully.
3619 3612
        let sym = symbolFor(self, methodNode)
3620 3613
            else continue;
3621 -
        let case SymbolData::Value { type, .. } = sym.data
3614 +
        let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3622 3615
            else panic "resolveInstanceMethodBodies: expected value symbol";
3623 -
        let case Type::Fn(fnType) = type
3624 -
            else panic "resolveInstanceMethodBodies: expected function type";
3625 3616
3626 3617
        // Enter function scope.
3627 3618
        enterFn(self, methodNode, fnType);
3628 3619
3629 3620
        // Bind the receiver parameter.
4224 4215
    let subjectTy = try infer(self, sw.subject);
4225 4216
    let subject = unwrapMatchSubject(subjectTy);
4226 4217
4227 4218
    if let case Type::Optional(inner) = subject.effectiveTy {
4228 4219
        try resolveMatchOptional(self, node, sw, inner);
4229 -
    } else if let case Type::Nominal(info) = subject.effectiveTy {
4230 -
        if let case NominalType::Union(u) = *info {
4231 -
            try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4232 -
        } else {
4233 -
            try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4234 -
        }
4220 +
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
4221 +
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4235 4222
    } else {
4236 4223
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4237 4224
    }
4238 4225
4239 4226
    // Mark last non-guarded prong as exhaustive.
lib/std/lang/resolver/tests.rad +13 -37
353 353
        else panic "getUnionVariantPayload: not a union";
354 354
    for i in 0..unionType.variants.len {
355 355
        if mem::eq(unionType.variants[i].name, variantName) {
356 356
            let payloadType = unionType.variants[i].valueType;
357 357
            // Unwrap single-field unlabeled records to get the inner type.
358 -
            if let case super::Type::Nominal(nominalInfo) = payloadType {
359 -
                if let case super::NominalType::Record(recInfo) = *nominalInfo {
360 -
                    if not recInfo.labeled and recInfo.fields.len == 1 {
361 -
                        return recInfo.fields[0].fieldType;
362 -
                    }
358 +
            if let case super::Type::Nominal(super::NominalType::Record(recInfo)) = payloadType {
359 +
                if not recInfo.labeled and recInfo.fields.len == 1 {
360 +
                    return recInfo.fields[0].fieldType;
363 361
                }
364 362
            }
365 363
            return payloadType;
366 364
        }
367 365
    }
1466 1464
    let callStmt = try getBlockStmt(blockNode, 1);
1467 1465
1468 1466
    { // Verify the function symbol captures an empty parameter list and void return.
1469 1467
        let sym = super::symbolFor(&a, fnNode)
1470 1468
            else throw testing::TestError::Failed;
1471 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1472 -
            else throw testing::TestError::Failed;
1473 -
1474 -
        let case super::Type::Fn(fnTy) = valType
1469 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1475 1470
            else throw testing::TestError::Failed;
1476 1471
        try testing::expect(fnTy.paramTypes.len == 0);
1477 1472
        try testing::expect(*fnTy.returnType == super::Type::Void);
1478 1473
    }
1479 1474
    { // Checking that the type of the call matches the function return type.
1480 1475
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1481 1476
1482 1477
        let fnSym = super::symbolFor(&a, fnNode)
1483 1478
            else throw testing::TestError::Failed;
1484 -
        let case super::SymbolData::Value { type: fnValType, .. } = fnSym.data
1485 -
            else throw testing::TestError::Failed;
1486 -
        let case super::Type::Fn(fnTy) = fnValType
1479 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1487 1480
            else throw testing::TestError::Failed;
1488 1481
        try expectType(&a, callExpr, *fnTy.returnType);
1489 1482
    }
1490 1483
}
1491 1484
1502 1495
    let callStmt = try getBlockStmt(blockNode, 1);
1503 1496
1504 1497
    { // Function returns i32 with no parameters.
1505 1498
        let sym = super::symbolFor(&a, fnNode)
1506 1499
            else throw testing::TestError::Failed;
1507 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1508 -
            else throw testing::TestError::Failed;
1509 -
        let case super::Type::Fn(fnTy) = valType
1500 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1510 1501
            else throw testing::TestError::Failed;
1511 1502
        try testing::expect(fnTy.paramTypes.len == 0);
1512 1503
        try testing::expect(*fnTy.returnType == super::Type::I32);
1513 1504
    }
1514 1505
    { // Call expression should inherit the function's return type.
1515 1506
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::I32);
1516 1507
1517 1508
        let fnSym = super::symbolFor(&a, fnNode)
1518 1509
            else throw testing::TestError::Failed;
1519 -
        let case super::SymbolData::Value { type: fnValType, .. } = fnSym.data
1520 -
            else throw testing::TestError::Failed;
1521 -
        let case super::Type::Fn(fnTy) = fnValType
1510 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1522 1511
            else throw testing::TestError::Failed;
1523 1512
        try expectType(&a, callExpr, *fnTy.returnType);
1524 1513
    }
1525 1514
}
1526 1515
1537 1526
    let callStmt = try getBlockStmt(blockNode, 2);
1538 1527
1539 1528
    { // Single parameter propagates nominal type onto the symbol and parameter node.
1540 1529
        let sym = super::symbolFor(&a, fnNode)
1541 1530
            else throw testing::TestError::Failed;
1542 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1543 -
            else throw testing::TestError::Failed;
1544 -
        let case super::Type::Fn(fnTy) = valType
1531 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1545 1532
            else throw testing::TestError::Failed;
1546 1533
        try testing::expect(fnTy.paramTypes.len == 1);
1547 1534
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1548 1535
        try testing::expect(*fnTy.returnType == super::Type::Void);
1549 1536
1556 1543
    }
1557 1544
    { // Call should resolve to void, matching the function's return type.
1558 1545
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1559 1546
        let fnSym = super::symbolFor(&a, fnNode)
1560 1547
            else throw testing::TestError::Failed;
1561 -
        let case super::SymbolData::Value { type: fnValType, .. } = fnSym.data
1562 -
            else throw testing::TestError::Failed;
1563 -
        let case super::Type::Fn(fnTy) = fnValType
1548 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1564 1549
            else throw testing::TestError::Failed;
1565 1550
        try expectType(&a, callExpr, *fnTy.returnType);
1566 1551
    }
1567 1552
}
1568 1553
1579 1564
    let callStmt = try getBlockStmt(blockNode, 3);
1580 1565
1581 1566
    { // Ensure multi-parameter signatures record both argument types.
1582 1567
        let sym = super::symbolFor(&a, fnNode)
1583 1568
            else throw testing::TestError::Failed;
1584 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1585 -
            else throw testing::TestError::Failed;
1586 -
        let case super::Type::Fn(fnTy) = valType
1569 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1587 1570
            else throw testing::TestError::Failed;
1588 1571
        try testing::expect(fnTy.paramTypes.len == 2);
1589 1572
        try testing::expect(*fnTy.paramTypes[0] == super::Type::I8);
1590 1573
        try testing::expect(*fnTy.paramTypes[1] == super::Type::I32);
1591 1574
        try testing::expect(*fnTy.returnType == super::Type::Void);
1601 1584
    }
1602 1585
    { // Call expression should again mirror the function return type.
1603 1586
        let callExpr = try expectExprStmtType(&a, callStmt, super::Type::Void);
1604 1587
        let fnSym = super::symbolFor(&a, fnNode)
1605 1588
            else throw testing::TestError::Failed;
1606 -
        let case super::SymbolData::Value { type: fnValType, .. } = fnSym.data
1607 -
            else throw testing::TestError::Failed;
1608 -
        let case super::Type::Fn(fnTy) = fnValType
1589 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = fnSym.data
1609 1590
            else throw testing::TestError::Failed;
1610 1591
        try expectType(&a, callExpr, *fnTy.returnType);
1611 1592
    }
1612 1593
}
1613 1594
1623 1604
    let fnNode = try getBlockStmt(blockNode, 0);
1624 1605
1625 1606
    { // Function symbol should be visible for recursive calls within its own body.
1626 1607
        let sym = super::symbolFor(&a, fnNode)
1627 1608
            else throw testing::TestError::Failed;
1628 -
        let case super::SymbolData::Value { type: valType, .. } = sym.data
1629 -
            else throw testing::TestError::Failed;
1630 -
1631 -
        let case super::Type::Fn(fnTy) = valType
1609 +
        let case super::SymbolData::Value { type: super::Type::Fn(fnTy), .. } = sym.data
1632 1610
            else throw testing::TestError::Failed;
1633 1611
        try testing::expect(fnTy.paramTypes.len == 1);
1634 1612
        try testing::expect(*fnTy.paramTypes[0] == super::Type::Bool);
1635 1613
        try testing::expect(*fnTy.returnType == super::Type::Bool);
1636 1614
    }
3694 3672
3695 3673
    // Verify the array constant has the correct type with length 3.
3696 3674
    let arrStmt = try getBlockStmt(result.root, 1);
3697 3675
    let sym = super::symbolFor(&a, arrStmt)
3698 3676
        else throw testing::TestError::Failed;
3699 -
    let case super::SymbolData::Constant { type: constType, .. } = sym.data
3700 -
        else throw testing::TestError::Failed;
3701 -
    let case super::Type::Array(arrType) = constType
3677 +
    let case super::SymbolData::Constant { type: super::Type::Array(arrType), .. } = sym.data
3702 3678
        else throw testing::TestError::Failed;
3703 3679
    try testing::expect(arrType.length == 3);
3704 3680
}
3705 3681
3706 3682
/// Test that a record field can use a constant as its array length.