Use nested pattern matching in compiler
5cee4d49ee4b6592c4d4522fb7c85e865ae9e89a9ac43fd2f918e2cc075f2a78
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. |