lang: Lower linear references
a8787514f6fd1e016bf3bb59c64dd718d324a9d43747de93529bf07d4fa36f30
1 parent
7e3db9f1
lib/std/lang/lower.rad
+72 -47
| 3255 | 3255 | set payload = tvalPayloadAddr(self, base, valOffset), |
|
| 3256 | 3256 | }; |
|
| 3257 | 3257 | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3258 | 3258 | } |
|
| 3259 | 3259 | ||
| 3260 | + | /// Bind an identifier from a matched subject. |
|
| 3260 | 3261 | fn bindMatchVariable( |
|
| 3261 | 3262 | self: *mut FnLowerer, |
|
| 3262 | 3263 | subject: *MatchSubject, |
|
| 3263 | 3264 | binding: *ast::Node, |
|
| 3264 | 3265 | mutable: bool |
| 3356 | 3357 | }; |
|
| 3357 | 3358 | try bindFieldVariable(self, elem, base, fieldInfo, subject.by, failBlock); |
|
| 3358 | 3359 | } |
|
| 3359 | 3360 | } |
|
| 3360 | 3361 | ||
| 3362 | + | /// Bind fields from a record pattern. |
|
| 3361 | 3363 | fn bindRecordPatternFields(self: *mut FnLowerer, subject: *MatchSubject, pattern: *ast::Node, lit: ast::RecordLit, failBlock: BlockId) throws (LowerError) { |
|
| 3362 | 3364 | // No fields to bind (e.g., `{ .. }`). |
|
| 3363 | 3365 | if lit.fields.len == 0 { |
|
| 3364 | 3366 | return; |
|
| 3365 | 3367 | } |
| 3855 | 3857 | } |
|
| 3856 | 3858 | ||
| 3857 | 3859 | /// Lower a `let-else` statement. |
|
| 3858 | 3860 | fn lowerLetElse(self: *mut FnLowerer, letElse: ast::LetElse) throws (LowerError) { |
|
| 3859 | 3861 | let subject = try lowerMatchSubject(self, letElse.pattern.scrutinee); |
|
| 3860 | - | let mut mergeBlock: BlockId = undefined; |
|
| 3862 | + | let mut successBlock: BlockId = undefined; |
|
| 3861 | 3863 | if letElse.pattern.guard == nil { |
|
| 3862 | - | set mergeBlock = try createBlock(self, "merge"); |
|
| 3864 | + | set successBlock = try createBlock(self, "success"); |
|
| 3863 | 3865 | } |
|
| 3864 | - | // Else branch executes when the pattern fails to match. |
|
| 3865 | 3866 | let elseBlock = try createBlock(self, "else"); |
|
| 3866 | 3867 | ||
| 3867 | - | // Evaluate pattern and jump to @end or @else. |
|
| 3868 | - | try lowerPatternMatch(self, &subject, &letElse.pattern, &mut mergeBlock, "merge", elseBlock); |
|
| 3868 | + | try lowerPatternMatch( |
|
| 3869 | + | self, |
|
| 3870 | + | &subject, |
|
| 3871 | + | &letElse.pattern, |
|
| 3872 | + | &mut successBlock, |
|
| 3873 | + | "success", |
|
| 3874 | + | elseBlock, |
|
| 3875 | + | ); |
|
| 3876 | + | let mut bindingVar: ?Var = nil; |
|
| 3877 | + | if let case ast::PatternKind::Binding = letElse.pattern.kind { |
|
| 3878 | + | set bindingVar = lookupLocalVar(self, letElse.pattern.pattern); |
|
| 3879 | + | } |
|
| 3880 | + | let mergeBlock = try createBlock(self, "merge"); |
|
| 3881 | + | try emitJmp(self, mergeBlock); |
|
| 3882 | + | ||
| 3869 | 3883 | try switchToAndSeal(self, elseBlock); |
|
| 3870 | - | try lowerNode(self, letElse.elseBranch); |
|
| 3884 | + | if try typeOf(self, letElse.elseBranch) == resolver::Type::Never { |
|
| 3885 | + | try lowerNode(self, letElse.elseBranch); |
|
| 3886 | + | } else { |
|
| 3887 | + | let fallback = try lowerExpr(self, letElse.elseBranch); |
|
| 3888 | + | if let variable = bindingVar { |
|
| 3889 | + | defVar(self, variable, fallback); |
|
| 3890 | + | } |
|
| 3891 | + | try emitJmp(self, mergeBlock); |
|
| 3892 | + | } |
|
| 3871 | 3893 | ||
| 3872 | - | // Continue in @merge. The else branch must diverge, so @merge has only |
|
| 3873 | - | // one predecessor. |
|
| 3874 | 3894 | try switchToAndSeal(self, mergeBlock); |
|
| 3875 | 3895 | } |
|
| 3876 | 3896 | ||
| 3877 | 3897 | /// Lower a `while let` loop as a match-driven loop. |
|
| 3878 | 3898 | fn lowerWhileLet(self: *mut FnLowerer, w: ast::WhileLet) throws (LowerError) { |
| 4050 | 4070 | } |
|
| 4051 | 4071 | ||
| 4052 | 4072 | /// Check if a resolver type lowers to an aggregate in memory. |
|
| 4053 | 4073 | fn isAggregateType(typ: resolver::Type) -> bool { |
|
| 4054 | 4074 | match typ { |
|
| 4055 | - | case resolver::Type::Nominal(_) => { |
|
| 4056 | - | // Void unions are small enough to pass by value. |
|
| 4057 | - | return not resolver::isVoidUnion(typ); |
|
| 4058 | - | } |
|
| 4059 | 4075 | case resolver::Type::Slice { .. }, |
|
| 4060 | - | resolver::Type::TraitObject { .. }, |
|
| 4061 | - | resolver::Type::Array(_), |
|
| 4062 | - | resolver::Type::Nil => return true, |
|
| 4076 | + | resolver::Type::TraitObject { .. } => return true, |
|
| 4063 | 4077 | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4064 | 4078 | // Optional pointers are scalar due to NPO. |
|
| 4065 | 4079 | return false; |
|
| 4066 | 4080 | } |
|
| 4067 | 4081 | case resolver::Type::Optional(_) => { |
|
| 4068 | 4082 | // All other optionals, including optional slices, are aggregates. |
|
| 4069 | 4083 | return true; |
|
| 4070 | 4084 | } |
|
| 4085 | + | case resolver::Type::Nominal(_) => { |
|
| 4086 | + | // Void unions are small enough to pass by value. |
|
| 4087 | + | return not resolver::isVoidUnion(typ); |
|
| 4088 | + | } |
|
| 4089 | + | case resolver::Type::Array(_), |
|
| 4090 | + | resolver::Type::Nil => return true, |
|
| 4071 | 4091 | else => return false, |
|
| 4072 | 4092 | } |
|
| 4073 | 4093 | } |
|
| 4074 | 4094 | ||
| 4075 | 4095 | /// Check if a resolver type is a small aggregate that can be |
| 4217 | 4237 | /// Build a `nil` value for an optional type. |
|
| 4218 | 4238 | /// |
|
| 4219 | 4239 | /// For optional pointers (`?*T`), returns an immediate `0` (null pointer). |
|
| 4220 | 4240 | /// For other optionals, builds a tagged aggregate with tag set to `0` (absent). |
|
| 4221 | 4241 | fn buildNilOptional(self: *mut FnLowerer, optType: resolver::Type) -> il::Val throws (LowerError) { |
|
| 4222 | - | match optType { |
|
| 4223 | - | case resolver::Type::Optional(resolver::Type::Pointer { .. }) => { |
|
| 4224 | - | return il::Val::Imm(0); |
|
| 4225 | - | } |
|
| 4226 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4227 | - | return try buildSliceValue(self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0)); |
|
| 4228 | - | } |
|
| 4229 | - | case resolver::Type::Optional(inner) => { |
|
| 4230 | - | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4231 | - | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4232 | - | } |
|
| 4233 | - | else => throw LowerError::ExpectedOptional, |
|
| 4242 | + | let case resolver::Type::Optional(inner) = optType |
|
| 4243 | + | else throw LowerError::ExpectedOptional; |
|
| 4244 | + | if let case resolver::Type::Pointer { .. } = *inner { |
|
| 4245 | + | return il::Val::Imm(0); |
|
| 4246 | + | } |
|
| 4247 | + | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
|
| 4248 | + | return try buildSliceValue( |
|
| 4249 | + | self, item, mutable, il::Val::Imm(0), il::Val::Imm(0), il::Val::Imm(0) |
|
| 4250 | + | ); |
|
| 4234 | 4251 | } |
|
| 4252 | + | let valOffset = resolver::getOptionalValOffset(*inner) as i32; |
|
| 4253 | + | return try buildTagged(self, resolver::getTypeLayout(optType), 0, nil, *inner, 1, valOffset); |
|
| 4235 | 4254 | } |
|
| 4236 | 4255 | ||
| 4237 | 4256 | /// Build a result value for throwing functions. |
|
| 4238 | 4257 | fn buildResult( |
|
| 4239 | 4258 | self: *mut FnLowerer, |
| 4256 | 4275 | ptrVal: il::Val, |
|
| 4257 | 4276 | lenVal: il::Val, |
|
| 4258 | 4277 | capVal: il::Val |
|
| 4259 | 4278 | ) -> il::Val throws (LowerError) { |
|
| 4260 | 4279 | let sliceType = resolver::Type::Slice { |
|
| 4261 | - | class: types::PointerClass::Owned, item: elemTy, mutable, |
|
| 4280 | + | class: types::PointerClass::Unsafe, |
|
| 4281 | + | item: elemTy, |
|
| 4282 | + | mutable, |
|
| 4262 | 4283 | }; |
|
| 4263 | 4284 | let dst = try emitReserve(self, sliceType); |
|
| 4264 | 4285 | let ptrTy = resolver::Type::Pointer { |
|
| 4265 | - | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4286 | + | class: types::PointerClass::Unsafe, |
|
| 4287 | + | target: elemTy, |
|
| 4288 | + | mutable, |
|
| 4266 | 4289 | }; |
|
| 4267 | 4290 | ||
| 4268 | 4291 | try emitStore(self, dst, SLICE_PTR_OFFSET, ptrTy, ptrVal); |
|
| 4269 | 4292 | try emitStore(self, dst, SLICE_LEN_OFFSET, resolver::Type::U32, lenVal); |
|
| 4270 | 4293 | try emitStore(self, dst, SLICE_CAP_OFFSET, resolver::Type::U32, capVal); |
| 4450 | 4473 | a: il::Reg, |
|
| 4451 | 4474 | b: il::Reg, |
|
| 4452 | 4475 | offset: i32 |
|
| 4453 | 4476 | ) -> il::Val throws (LowerError) { |
|
| 4454 | 4477 | let ptrTy = resolver::Type::Pointer { |
|
| 4455 | - | class: types::PointerClass::Owned, target: elemTy, mutable, |
|
| 4478 | + | class: types::PointerClass::Unsafe, |
|
| 4479 | + | target: elemTy, |
|
| 4480 | + | mutable, |
|
| 4456 | 4481 | }; |
|
| 4457 | 4482 | let ptrEq = try emitEqAtOffset(self, a, b, offset + SLICE_PTR_OFFSET, ptrTy); |
|
| 4458 | 4483 | let lenEq = try emitEqAtOffset(self, a, b, offset + SLICE_LEN_OFFSET, resolver::Type::U32); |
|
| 4459 | 4484 | ||
| 4460 | 4485 | return emitTypedBinOp(self, il::BinOp::And, il::Type::W32, ptrEq, lenEq); |
| 4700 | 4725 | a: il::Reg, |
|
| 4701 | 4726 | b: il::Reg, |
|
| 4702 | 4727 | offset: i32 |
|
| 4703 | 4728 | ) -> il::Val throws (LowerError) { |
|
| 4704 | 4729 | match typ { |
|
| 4705 | - | case resolver::Type::Optional(resolver::Type::Slice { item, mutable, .. }) => { |
|
| 4706 | - | // Optional slices use null pointer optimization. |
|
| 4707 | - | return try lowerSliceEq(self, item, mutable, a, b, offset); |
|
| 4708 | - | } |
|
| 4730 | + | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4731 | + | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4709 | 4732 | case resolver::Type::Optional(inner) => { |
|
| 4733 | + | if let case resolver::Type::Slice { item, mutable, .. } = *inner { |
|
| 4734 | + | // Optional slices use null pointer optimization. |
|
| 4735 | + | return try lowerSliceEq(self, item, mutable, a, b, offset); |
|
| 4736 | + | } |
|
| 4710 | 4737 | return try lowerOptionalEq(self, *inner, a, b, offset); |
|
| 4711 | 4738 | } |
|
| 4712 | - | case resolver::Type::Slice { item, mutable, .. } => |
|
| 4713 | - | return try lowerSliceEq(self, item, mutable, a, b, offset), |
|
| 4714 | 4739 | case resolver::Type::Array(arr) => |
|
| 4715 | 4740 | return try lowerArrayEq(self, arr, a, b, offset), |
|
| 4716 | 4741 | case resolver::Type::Nominal(resolver::NominalType::Record(recInfo)) => |
|
| 4717 | 4742 | return try lowerRecordEq(self, recInfo, a, b, offset), |
|
| 4718 | 4743 | case resolver::Type::Nominal(resolver::NominalType::Union(unionInfo)) => |
| 5090 | 5115 | else => throw LowerError::UnexpectedNodeValue(arrayNode), |
|
| 5091 | 5116 | } |
|
| 5092 | 5117 | let result = dataBuilderFinish(&b); |
|
| 5093 | 5118 | let alignment = resolver::getTypeLayout(*item).alignment; |
|
| 5094 | 5119 | return try lowerConstDataAsSlice( |
|
| 5095 | - | self, result.values, alignment, not mutable, item, mutable, length |
|
| 5120 | + | self, result.values, alignment, not mutable, |
|
| 5121 | + | item, mutable, length |
|
| 5096 | 5122 | ); |
|
| 5097 | 5123 | } |
|
| 5098 | 5124 | let data = try lowerExpr(self, arrayNode); |
|
| 5099 | 5125 | let count = il::Val::Imm(length as i64); |
|
| 5100 | 5126 | return try buildSliceValue(self, item, mutable, data, count, count); |
| 5120 | 5146 | case resolver::Type::Slice { item, .. } => { |
|
| 5121 | 5147 | set elemType = *item; |
|
| 5122 | 5148 | let sliceLen = loadSliceLen(self, baseReg); |
|
| 5123 | 5149 | // Runtime safety check: index must be strictly less than slice length. |
|
| 5124 | 5150 | try emitTrapUnlessCmp(self, il::CmpOp::Ult, il::Type::W32, indexVal, sliceLen); |
|
| 5125 | - | ||
| 5126 | 5151 | set dataReg = loadSlicePtr(self, baseReg); |
|
| 5127 | 5152 | } |
|
| 5128 | 5153 | case resolver::Type::Array(arrInfo) => { |
|
| 5129 | 5154 | set elemType = *arrInfo.item; |
|
| 5130 | 5155 | // Runtime safety check: index must be strictly less than array length. |
| 6147 | 6172 | /// String literals are stored as global data and the result is a slice |
|
| 6148 | 6173 | /// pointing to the data with the appropriate length. |
|
| 6149 | 6174 | fn lowerStringLit(self: *mut FnLowerer, node: *ast::Node, s: *[u8]) -> il::Val throws (LowerError) { |
|
| 6150 | 6175 | // Get the slice type from the node. |
|
| 6151 | 6176 | let sliceTy = try typeOf(self, node); |
|
| 6152 | - | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6153 | - | throw LowerError::ExpectedSliceOrArray; |
|
| 6154 | - | }; |
|
| 6177 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6178 | + | else throw LowerError::ExpectedSliceOrArray; |
|
| 6155 | 6179 | // Build the string data value. |
|
| 6156 | 6180 | let ptr = try! alloc::alloc( |
|
| 6157 | 6181 | self.low.arena, @sizeOf(il::DataValue), @alignOf(il::DataValue) |
|
| 6158 | 6182 | ) as *mut il::DataValue; |
|
| 6159 | 6183 | ||
| 6160 | 6184 | set *ptr = il::DataValue { item: il::DataItem::Str(s), count: 1 }; |
|
| 6161 | 6185 | ||
| 6162 | - | return try lowerConstDataAsSlice(self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len); |
|
| 6186 | + | return try lowerConstDataAsSlice( |
|
| 6187 | + | self, @sliceOf(ptr, 1), 1, true, item, mutable, s.len |
|
| 6188 | + | ); |
|
| 6163 | 6189 | } |
|
| 6164 | 6190 | ||
| 6165 | 6191 | /// Lower a builtin call expression. |
|
| 6166 | 6192 | fn lowerBuiltinCall(self: *mut FnLowerer, node: *ast::Node, kind: ast::Builtin, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6167 | 6193 | match kind { |
| 6179 | 6205 | fn lowerSliceOf(self: *mut FnLowerer, node: *ast::Node, args: *mut [*ast::Node]) -> il::Val throws (LowerError) { |
|
| 6180 | 6206 | if args.len <> 2 and args.len <> 3 { |
|
| 6181 | 6207 | throw LowerError::InvalidArgCount; |
|
| 6182 | 6208 | } |
|
| 6183 | 6209 | let sliceTy = try typeOf(self, node); |
|
| 6184 | - | let case resolver::Type::Slice { item, mutable, .. } = sliceTy else { |
|
| 6185 | - | throw LowerError::ExpectedSliceOrArray; |
|
| 6186 | - | }; |
|
| 6210 | + | let case resolver::Type::Slice { item, mutable, .. } = sliceTy |
|
| 6211 | + | else throw LowerError::ExpectedSliceOrArray; |
|
| 6187 | 6212 | let ptrVal = try lowerExpr(self, args[0]); |
|
| 6188 | 6213 | let lenVal = try lowerExpr(self, args[1]); |
|
| 6189 | 6214 | let mut capVal = lenVal; |
|
| 6190 | 6215 | if args.len == 3 { |
|
| 6191 | 6216 | set capVal = try lowerExpr(self, args[2]); |
| 7287 | 7312 | resolver::Type::U32 => return il::Type::W32, |
|
| 7288 | 7313 | case resolver::Type::I64, |
|
| 7289 | 7314 | resolver::Type::U64, |
|
| 7290 | 7315 | resolver::Type::Pointer { .. }, |
|
| 7291 | 7316 | resolver::Type::Slice { .. }, |
|
| 7317 | + | resolver::Type::TraitObject { .. }, |
|
| 7292 | 7318 | resolver::Type::Array(_), |
|
| 7293 | 7319 | resolver::Type::Optional(_), |
|
| 7294 | - | resolver::Type::Fn(_), |
|
| 7295 | - | resolver::Type::TraitObject { .. } => return il::Type::W64, |
|
| 7320 | + | resolver::Type::Fn(_) => return il::Type::W64, |
|
| 7296 | 7321 | case resolver::Type::Nominal(_) => { |
|
| 7297 | 7322 | if resolver::isVoidUnion(typ) { |
|
| 7298 | 7323 | return il::Type::W8; |
|
| 7299 | 7324 | } |
|
| 7300 | 7325 | return il::Type::W64; |