Use nested pattern matching in compiler

5cee4d49ee4b6592c4d4522fb7c85e865ae9e89a9ac43fd2f918e2cc075f2a78
Alexis Sellier committed ago 1 parent abd72ad2
lib/std/lang/lower.rad +51 -74
1310 1310
            let calleeSym = resolver::nodeData(self.resolver, call.callee).sym
1311 1311
                else throw LowerError::MissingSymbol(call.callee);
1312 1312
            match calleeSym.data {
1313 1313
                case resolver::SymbolData::Variant { .. } =>
1314 1314
                    try lowerConstUnionVariantInto(self, node, calleeSym, ty, call.args, dataPrefix, b),
1315 -
                case resolver::SymbolData::Type(nominal) => {
1316 -
                    let case resolver::NominalType::Record(recInfo) = *nominal
1317 -
                        else throw LowerError::MissingConst(node);
1315 +
                case resolver::SymbolData::Type(resolver::NominalType::Record(recInfo)) => {
1318 1316
                    try lowerConstRecordCtorInto(self, call.args, recInfo, dataPrefix, b);
1319 1317
                }
1320 1318
                else => throw LowerError::MissingConst(node),
1321 1319
            }
1322 1320
        }
1419 1417
    recLit: ast::RecordLit,
1420 1418
    ty: resolver::Type,
1421 1419
    dataPrefix: *[u8],
1422 1420
    b: *mut DataValueBuilder
1423 1421
) throws (LowerError) {
1424 -
    let case resolver::Type::Nominal(nomType) = ty else {
1425 -
        throw LowerError::ExpectedRecord;
1426 -
    };
1427 -
    match *nomType {
1428 -
        case resolver::NominalType::Record(recInfo) => {
1422 +
    match ty {
1423 +
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
1429 1424
            try lowerConstRecordCtorInto(self, recLit.fields, recInfo, dataPrefix, b);
1430 1425
        }
1431 -
        case resolver::NominalType::Union(_) => {
1426 +
        case resolver::Type::Nominal(resolver::NominalType::Union(_)) => {
1432 1427
            let typeName = recLit.typeName else {
1433 1428
                throw LowerError::ExpectedVariant;
1434 1429
            };
1435 1430
            let sym = resolver::nodeData(self.resolver, typeName).sym else {
1436 1431
                throw LowerError::MissingSymbol(typeName);
1509 1504
            });
1510 1505
        }
1511 1506
        return;
1512 1507
    }
1513 1508
1514 -
    let case resolver::Type::Nominal(payloadNominal) = payloadType else {
1515 -
        throw LowerError::MissingMetadata;
1516 -
    };
1517 -
    let case resolver::NominalType::Record(payloadRec) = *payloadNominal else {
1509 +
    let case resolver::Type::Nominal(resolver::NominalType::Record(payloadRec)) = payloadType else {
1518 1510
        throw LowerError::ExpectedRecord;
1519 1511
    };
1520 1512
    let payloadLayout = payloadRec.layout;
1521 1513
    try lowerConstRecordCtorInto(self, payloadArgs, payloadRec, dataPrefix, b);
1522 1514
2969 2961
/// Get the register to compare against `0` for optional `nil` checking.
2970 2962
/// For null-ptr-optimized types, loads the data pointer, or returns it
2971 2963
/// directly for scalar pointers. For aggregates, returns the tag register.
2972 2964
fn optionalNilReg(self: *mut FnLowerer, val: il::Val, typ: resolver::Type) -> il::Reg throws (LowerError) {
2973 2965
    let reg = emitValToReg(self, val);
2974 -
    let case resolver::Type::Optional(inner) = typ else return reg;
2975 2966
2976 -
    if let case resolver::Type::Slice { .. } = *inner {
2977 -
        let ptrReg = nextReg(self);
2978 -
        emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET);
2979 -
        return ptrReg;
2980 -
    }
2981 -
    if let case resolver::Type::Pointer { .. } = *inner {
2982 -
        return reg;
2967 +
    match typ {
2968 +
        case resolver::Type::Optional(resolver::Type::Slice { .. }) => {
2969 +
            let ptrReg = nextReg(self);
2970 +
            emitLoadW64At(self, ptrReg, reg, SLICE_PTR_OFFSET);
2971 +
            return ptrReg;
2972 +
        }
2973 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => return reg,
2974 +
        case resolver::Type::Optional(_) => return tvalTagReg(self, reg),
2975 +
        else => return reg,
2983 2976
    }
2984 -
    return tvalTagReg(self, reg);
2985 2977
}
2986 2978
2987 2979
/// Lower an optional nil check (`opt == nil` or `opt != nil`).
2988 2980
fn lowerNilCheck(self: *mut FnLowerer, opt: *ast::Node, isEq: bool) -> il::Val throws (LowerError) {
2989 2981
    let optTy = resolver::typeFor(self.low.resolver, opt) else {
3800 3792
// Record and Aggregate Type Helpers //
3801 3793
///////////////////////////////////////
3802 3794
3803 3795
/// Extract the nominal record info from a resolver type.
3804 3796
fn recordInfoFromType(typ: resolver::Type) -> ?resolver::RecordType {
3805 -
    let case resolver::Type::Nominal(info) = typ else {
3806 -
        return nil;
3807 -
    };
3808 -
    let case resolver::NominalType::Record(recInfo) = *info else {
3809 -
        return nil;
3810 -
    };
3797 +
    let case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) = typ
3798 +
        else return nil;
3799 +
3811 3800
    return recInfo;
3812 3801
}
3813 3802
3814 3803
/// Extract the nominal union info from a resolver type.
3815 3804
fn unionInfoFromType(typ: resolver::Type) -> ?resolver::UnionType {
3816 -
    let case resolver::Type::Nominal(info) = typ else {
3817 -
        return nil;
3818 -
    };
3819 -
    let case resolver::NominalType::Union(unionInfo) = *info else {
3820 -
        return nil;
3821 -
    };
3805 +
    let case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) = typ
3806 +
        else return nil;
3807 +
3822 3808
    return unionInfo;
3823 3809
}
3824 3810
3825 3811
/// Return the effective type of a node after any coercion applied by
3826 3812
/// the resolver. `lowerExpr` already materializes the coercion in the
3847 3833
        }
3848 3834
        case resolver::Type::Slice { .. } => return true,
3849 3835
        case resolver::Type::TraitObject { .. } => return true,
3850 3836
        case resolver::Type::Array(_) => return true,
3851 3837
        case resolver::Type::Nil => return true,
3852 -
        else => {
3853 -
            // Optional pointers are scalar (single word). All other
3854 -
            // optionals, including optional slices with NPO, are aggregates.
3855 -
            if let case resolver::Type::Optional(inner) = typ {
3856 -
                if let case resolver::Type::Pointer { .. } = *inner {
3857 -
                    return false;
3858 -
                }
3859 -
                return true;
3860 -
            }
3838 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
3839 +
            // Optional pointers are scalar due to NPO.
3861 3840
            return false;
3862 3841
        }
3842 +
        case resolver::Type::Optional(_) => {
3843 +
            // All other optionals, including optional slices, are aggregates.
3844 +
            return true;
3845 +
        }
3846 +
        else => return false,
3863 3847
    }
3864 3848
}
3865 3849
3866 3850
/// Check if a resolver type is a small aggregate that can be
3867 3851
/// passed or returned by value in a register.
4022 4006
/// Build a `nil` value for an optional type.
4023 4007
///
4024 4008
/// For optional pointers (`?*T`), returns an immediate `0` (null pointer).
4025 4009
/// For other optionals, builds a tagged aggregate with tag set to `0` (absent).
4026 4010
fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) {
4027 -
    let case resolver::Type::Optional(inner) = optType else {
4028 -
        throw LowerError::ExpectedOptional;
4029 -
    };
4030 -
    if let case resolver::Type::Pointer { .. } = *inner {
4031 -
        return il::Val::Imm(0);
4032 -
    }
4033 -
    if let case resolver::Type::Slice { item, mutable } = *inner {
4034 -
        return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4011 +
    match optType {
4012 +
        case resolver::Type::Optional(resolver::Type::Pointer { .. }) => {
4013 +
            return il::Val::Imm(0);
4014 +
        }
4015 +
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => {
4016 +
            return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0));
4017 +
        }
4018 +
        case resolver::Type::Optional(inner) => {
4019 +
            let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4020 +
            return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4021 +
        }
4022 +
        else => throw LowerError::ExpectedOptional,
4035 4023
    }
4036 -
    let valOffset = resolver::getOptionalValOffset(*inner) as i32;
4037 -
    return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset);
4038 4024
}
4039 4025
4040 4026
/// Build a result value for throwing functions.
4041 4027
fn buildResult(
4042 4028
    self: *mut FnLowerer,
4490 4476
    a: il::Reg,
4491 4477
    b: il::Reg,
4492 4478
    offset: i32
4493 4479
) -> il::Val throws (LowerError) {
4494 4480
    match typ {
4481 +
        case resolver::Type::Optional(resolver::Type::Slice { item, mutable }) => {
4482 +
            // Optional slices use null pointer optimization.
4483 +
            return try lowerSliceEq(self, item, mutable, a, b, offset);
4484 +
        }
4495 4485
        case resolver::Type::Optional(inner) => {
4496 -
            if let case resolver::Type::Slice { item, mutable } = *inner {
4497 -
                // Optional slices use null pointer optimization.
4498 -
                return try lowerSliceEq(self, item, mutable, a, b, offset);
4499 -
            }
4500 4486
            return try lowerOptionalEq(self, *inner, a, b, offset);
4501 4487
        }
4502 4488
        case resolver::Type::Slice { item, mutable } =>
4503 4489
            return try lowerSliceEq(self, item, mutable, a, b, offset),
4504 4490
        case resolver::Type::Array(arr) =>
4505 4491
            return try lowerArrayEq(self, arr, a, b, offset),
4506 -
        case resolver::Type::Nominal(nom) => {
4507 -
            match *nom {
4508 -
                case resolver::NominalType::Record(recInfo) =>
4509 -
                    return try lowerRecordEq(self, recInfo, a, b, offset),
4510 -
                case resolver::NominalType::Union(unionInfo) =>
4511 -
                    return try lowerUnionEq(self, unionInfo, a, b, offset),
4512 -
                else =>
4513 -
                    throw LowerError::ExpectedRecord,
4514 -
            }
4515 -
        }
4492 +
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) =>
4493 +
            return try lowerRecordEq(self, recInfo, a, b, offset),
4494 +
        case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) =>
4495 +
            return try lowerUnionEq(self, unionInfo, a, b, offset),
4516 4496
        else => {
4517 4497
            let recInfo = recordInfoFromType(typ) else {
4518 4498
                throw LowerError::ExpectedRecord;
4519 4499
            };
4520 4500
            return try lowerRecordEq(self, recInfo, a, b, offset);
4526 4506
/// record literals like `Union::Variant { field: value }`.
4527 4507
fn lowerRecordLit(self: *mut FnLowerer, node: *ast::Node, lit: ast::RecordLit) -> il::Val throws (LowerError) {
4528 4508
    let typ = resolver::typeFor(self.low.resolver, node) else {
4529 4509
        throw LowerError::MissingType(node);
4530 4510
    };
4531 -
    let case resolver::Type::Nominal(nominal) = typ else {
4532 -
        throw LowerError::UnexpectedType(&typ);
4533 -
    };
4534 -
    match *nominal {
4535 -
        case resolver::NominalType::Record(recInfo) => {
4511 +
    match typ {
4512 +
        case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => {
4536 4513
            let dst = try emitReserve(self, typ);
4537 4514
            try lowerRecordFields(self, dst, &recInfo, lit.fields, 0);
4538 4515
4539 4516
            return il::Val::Reg(dst);
4540 4517
        }
4541 -
        case resolver::NominalType::Union(_) => {
4518 +
        case resolver::Type::Nominal(resolver::NominalType::Union(_)) => {
4542 4519
            // TODO: This can be inlined once we have a real register allocator.
4543 4520
            return try lowerUnionRecordLit(self, typ, lit);
4544 4521
        }
4545 -
        else => throw LowerError::ExpectedRecord,
4522 +
        else => throw LowerError::UnexpectedType(&typ),
4546 4523
    }
4547 4524
}
4548 4525
4549 4526
/// Lower a union variant record literal like `Union::Variant { field: value }`.
4550 4527
fn lowerUnionRecordLit(self: *mut FnLowerer, typ: resolver::Type, lit: ast::RecordLit) -> il::Val throws (LowerError) {
lib/std/lang/resolver.rad +17 -37
1500 1500
    return tag;
1501 1501
}
1502 1502
1503 1503
/// Check if a type is a union without payloads.
1504 1504
pub fn isVoidUnion(ty: Type) -> bool {
1505 -
    let case Type::Nominal(info) = ty
1506 -
        else return false;
1507 -
    let case NominalType::Union(unionType) = *info
1505 +
    let case Type::Nominal(NominalType::Union(unionType)) = ty
1508 1506
        else return false;
1509 1507
    return unionType.isAllVoid;
1510 1508
}
1511 1509
1512 1510
/// Check if a type should be treated as an address-like value.
1857 1855
    }
1858 1856
}
1859 1857
1860 1858
/// Get the record info from a record type.
1861 1859
pub fn getRecord(ty: Type) -> ?RecordType {
1862 -
    let case Type::Nominal(info) = ty else return nil;
1863 -
    let case NominalType::Record(recInfo) = *info else return nil;
1864 -
1860 +
    let case Type::Nominal(NominalType::Record(recInfo)) = ty else return nil;
1865 1861
    return recInfo;
1866 1862
}
1867 1863
1868 1864
/// Auto-dereference a type: if it's a pointer, return the target type.
1869 1865
pub fn autoDeref(ty: Type) -> Type {
1874 1870
}
1875 1871
1876 1872
/// Get field info for a record-like type (records, slices) by field index.
1877 1873
pub fn getRecordField(ty: Type, index: u32) -> ?RecordField {
1878 1874
    match ty {
1879 -
        case Type::Nominal(info) => {
1880 -
            let case NominalType::Record(recInfo) = *info else return nil;
1875 +
        case Type::Nominal(NominalType::Record(recInfo)) => {
1881 1876
            if index >= recInfo.fields.len {
1882 1877
                return nil;
1883 1878
            }
1884 1879
            return recInfo.fields[index];
1885 1880
        }
2887 2882
            // Constructor calls (union variants, unlabeled records) are constant
2888 2883
            // if all payload args are themselves constant.
2889 2884
            if let sym = symbolFor(self, call.callee) {
2890 2885
                match sym.data {
2891 2886
                    case SymbolData::Variant { .. } => {}
2892 -
                    case SymbolData::Type(ty) => {
2893 -
                        if let case NominalType::Record(recInfo) = *ty {
2894 -
                            if recInfo.labeled {
2895 -
                                return false;
2896 -
                            }
2897 -
                        } else {
2887 +
                    case SymbolData::Type(NominalType::Record(recInfo)) => {
2888 +
                        if recInfo.labeled {
2898 2889
                            return false;
2899 2890
                        }
2900 2891
                    },
2901 2892
                    else => return false,
2902 2893
                }
3084 3075
    let sym = symbolFor(self, node) else {
3085 3076
        // The function declaration failed to type check, therefore
3086 3077
        // no symbol was associated with it.
3087 3078
        return;
3088 3079
    };
3089 -
    let case SymbolData::Value { type, .. } = sym.data else {
3080 +
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
3090 3081
        panic "resolveFnDeclBody: unexpected symbol data for function";
3091 3082
    };
3092 -
    let case Type::Fn(fnType) = type else {
3093 -
        panic "resolveFnDeclBody: unexpected type for function";
3094 -
    };
3095 3083
    let retTy = *fnType.returnType;
3096 3084
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3097 3085
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3098 3086
3099 3087
    if isIntrinsic and not isExtern {
3604 3592
3605 3593
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
3606 3594
        // for this method (eg. unknown method name). Skip gracefully.
3607 3595
        let sym = symbolFor(self, methodNode)
3608 3596
            else continue;
3609 -
        let case SymbolData::Value { type, .. } = sym.data
3597 +
        let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3610 3598
            else panic "resolveInstanceMethodBodies: expected value symbol";
3611 -
        let case Type::Fn(fnType) = type
3612 -
            else panic "resolveInstanceMethodBodies: expected function type";
3613 3599
3614 3600
        // Enter function scope.
3615 3601
        enterFn(self, methodNode, fnType);
3616 3602
3617 3603
        // Bind the receiver parameter.
4214 4200
    let subjectTy = try infer(self, sw.subject);
4215 4201
    let subject = unwrapMatchSubject(subjectTy);
4216 4202
4217 4203
    if let case Type::Optional(inner) = subject.effectiveTy {
4218 4204
        try resolveMatchOptional(self, node, sw, inner);
4219 -
    } else if let case Type::Nominal(info) = subject.effectiveTy {
4220 -
        if let case NominalType::Union(u) = *info {
4221 -
            try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4222 -
        } else {
4223 -
            try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4224 -
        }
4205 +
    } else if let case Type::Nominal(NominalType::Union(u)) = subject.effectiveTy {
4206 +
        try resolveMatchUnion(self, node, sw, subject.effectiveTy, u, subject.by);
4225 4207
    } else {
4226 4208
        try resolveMatchGeneric(self, node, sw, subject.effectiveTy);
4227 4209
    }
4228 4210
4229 4211
    // Mark last non-guarded prong as exhaustive.
5356 5338
{
5357 5339
    let parentTy = try infer(self, access.parent);
5358 5340
    let subjectTy = autoDeref(parentTy);
5359 5341
5360 5342
    match subjectTy {
5361 -
        case Type::Nominal(nominalTy) => {
5362 -
            if let case NominalType::Record(recordType) = *nominalTy {
5363 -
                let fieldNode = access.child;
5364 -
                let fieldName = try nodeName(self, fieldNode);
5365 -
                let fieldIndex = findRecordField(&recordType, fieldName)
5366 -
                    else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5367 -
                let fieldTy = recordType.fields[fieldIndex].fieldType;
5343 +
        case Type::Nominal(NominalType::Record(recordType)) => {
5344 +
            let fieldNode = access.child;
5345 +
            let fieldName = try nodeName(self, fieldNode);
5346 +
            let fieldIndex = findRecordField(&recordType, fieldName)
5347 +
                else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5348 +
            let fieldTy = recordType.fields[fieldIndex].fieldType;
5368 5349
5369 -
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5350 +
            setRecordFieldIndex(self, fieldNode, fieldIndex);
5370 5351
5371 -
                return setNodeType(self, node, fieldTy);
5372 -
            }
5352 +
            return setNodeType(self, node, fieldTy);
5373 5353
        }
5374 5354
        case Type::Array(arrayInfo) => {
5375 5355
            let fieldNode = access.child;
5376 5356
            let fieldName = try nodeName(self, fieldNode);
5377 5357
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.