compiler: Construct initialized match payload values
d335f4853a2b54338a2a87b861bd3346b5a616efa351d9ad064881cea52dbff9
1 parent
b4557190
lib/std/lang/lower.rad
+22 -22
| 3368 | 3368 | return il::Val::Undef; |
|
| 3369 | 3369 | } |
|
| 3370 | 3370 | return emitRead(self, base, valOffset, payload); |
|
| 3371 | 3371 | } |
|
| 3372 | 3372 | ||
| 3373 | - | /// Compute the address of the payload in a tagged value aggregate. |
|
| 3374 | - | unsafe fn tvalPayloadAddr 'arena 'phase 'function (self: &mut FnLowerer 'arena 'phase 'function, base: il::Reg, valOffset: i32) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 3375 | - | return il::Val::Reg(emitPtrOffset(self, base, valOffset)); |
|
| 3373 | + | /// Select the payload value or address for the declared match mode. |
|
| 3374 | + | unsafe fn tvalPayloadBinding 'arena 'phase 'function ( |
|
| 3375 | + | self: &mut FnLowerer 'arena 'phase 'function, |
|
| 3376 | + | base: il::Reg, |
|
| 3377 | + | bindType: resolver::Type, |
|
| 3378 | + | matchBy: resolver::MatchBy, |
|
| 3379 | + | valOffset: i32 |
|
| 3380 | + | ) -> il::Val where 'arena: 'phase, 'phase: 'function { |
|
| 3381 | + | match matchBy { |
|
| 3382 | + | case resolver::MatchBy::Value => |
|
| 3383 | + | return tvalPayloadVal(self, base, bindType, valOffset), |
|
| 3384 | + | case resolver::MatchBy::Ref, resolver::MatchBy::MutRef => |
|
| 3385 | + | return il::Val::Reg(emitPtrOffset(self, base, valOffset)), |
|
| 3386 | + | } |
|
| 3376 | 3387 | } |
|
| 3377 | 3388 | ||
| 3378 | 3389 | /// Bind a variable to a tagged value's payload. |
|
| 3379 | 3390 | unsafe fn bindPayloadVariable 'arena 'phase 'function ( |
|
| 3380 | 3391 | self: &mut FnLowerer 'arena 'phase 'function, |
| 3384 | 3395 | matchBy: resolver::MatchBy, |
|
| 3385 | 3396 | valOffset: i32, |
|
| 3386 | 3397 | mutable: bool |
|
| 3387 | 3398 | ) -> Var throws (LowerError) where 'arena: 'phase, 'phase: 'function { |
|
| 3388 | 3399 | let base = emitValToReg(self, subjectVal); |
|
| 3389 | - | let mut payload: il::Val = undefined; |
|
| 3390 | - | ||
| 3391 | - | match matchBy { |
|
| 3392 | - | case resolver::MatchBy::Value => |
|
| 3393 | - | set payload = tvalPayloadVal(self, base, bindType, valOffset), |
|
| 3394 | - | case resolver::MatchBy::Ref, resolver::MatchBy::MutRef => |
|
| 3395 | - | set payload = tvalPayloadAddr(self, base, valOffset), |
|
| 3396 | - | }; |
|
| 3400 | + | let payload = tvalPayloadBinding(self, base, bindType, matchBy, valOffset); |
|
| 3397 | 3401 | return newVar(self, name, ilType(self.low, bindType), mutable, payload); |
|
| 3398 | 3402 | } |
|
| 3399 | 3403 | ||
| 3400 | 3404 | /// Bind an identifier from a matched subject. |
|
| 3401 | 3405 | unsafe fn bindMatchVariable 'arena 'phase 'function ( |
| 3623 | 3627 | // Build a MatchSubject for the nested field. |
|
| 3624 | 3628 | let ilTy = ilType(self.low, fieldType); |
|
| 3625 | 3629 | let kind = matchSubjectKind(fieldType); |
|
| 3626 | 3630 | ||
| 3627 | 3631 | // Determine the subject value. |
|
| 3628 | - | let mut val: il::Val = undefined; |
|
| 3629 | - | if let reg = derefBase { |
|
| 3630 | - | // Auto-deref: the loaded pointer is the address of the target value. |
|
| 3631 | - | set val = il::Val::Reg(reg); |
|
| 3632 | - | } else if isAggregateType(fieldType) { |
|
| 3633 | - | // Aggregate: use the pointer. |
|
| 3634 | - | set val = il::Val::Reg(fieldPtr); |
|
| 3635 | - | } else { |
|
| 3636 | - | // Scalar: load the value. |
|
| 3637 | - | set val = emitRead(self, base, fieldInfo.offset, fieldType); |
|
| 3638 | - | } |
|
| 3632 | + | // Auto-deref: the loaded pointer is the address of the target value. |
|
| 3633 | + | let subjectBase = derefBase else fieldPtr; |
|
| 3634 | + | // Aggregate: use the pointer. |
|
| 3635 | + | // Scalar: load the value. |
|
| 3636 | + | let val = il::Val::Reg(subjectBase) |
|
| 3637 | + | if derefBase <> nil or isAggregateType(fieldType) |
|
| 3638 | + | else emitRead(self, base, fieldInfo.offset, fieldType); |
|
| 3639 | 3639 | let nestedSubject = MatchSubject { |
|
| 3640 | 3640 | val, |
|
| 3641 | 3641 | type: fieldType, |
|
| 3642 | 3642 | ilType: ilTy, |
|
| 3643 | 3643 | bindType: fieldType, |
test/tests/match.payload.modes.rad
added
+47 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Read an optional payload by value. |
|
| 4 | + | fn byValue(value: ?u32) -> u32 { |
|
| 5 | + | match value { |
|
| 6 | + | payload => return payload, |
|
| 7 | + | case nil => return 0, |
|
| 8 | + | } |
|
| 9 | + | } |
|
| 10 | + | ||
| 11 | + | /// Read an optional payload through a shared reference. |
|
| 12 | + | fn byRef(value: &?u32) -> u32 { |
|
| 13 | + | match value { |
|
| 14 | + | payload => return *payload, |
|
| 15 | + | case nil => return 0, |
|
| 16 | + | } |
|
| 17 | + | } |
|
| 18 | + | ||
| 19 | + | /// Update an optional payload through a mutable reference. |
|
| 20 | + | fn byMutRef(value: &mut ?u32) -> u32 { |
|
| 21 | + | match value { |
|
| 22 | + | payload => { |
|
| 23 | + | let previous = *payload; |
|
| 24 | + | set *payload += 1; |
|
| 25 | + | return previous; |
|
| 26 | + | } |
|
| 27 | + | case nil => return 0, |
|
| 28 | + | } |
|
| 29 | + | } |
|
| 30 | + | ||
| 31 | + | /// Compare all payload modes and confirm mutation of the original optional. |
|
| 32 | + | @default fn main() -> u32 { |
|
| 33 | + | let mut absent: ?u32 = nil; |
|
| 34 | + | assert byValue(absent) == 0; |
|
| 35 | + | assert byRef(&absent) == 0; |
|
| 36 | + | assert byMutRef(&mut absent) == 0; |
|
| 37 | + | assert absent == nil; |
|
| 38 | + | for initial in 1..10 { |
|
| 39 | + | let mut present: ?u32 = initial; |
|
| 40 | + | assert byValue(present) == initial; |
|
| 41 | + | assert byRef(&present) == initial; |
|
| 42 | + | assert byMutRef(&mut present) == initial; |
|
| 43 | + | assert byValue(present) == initial + 1; |
|
| 44 | + | assert byRef(&present) == initial + 1; |
|
| 45 | + | } |
|
| 46 | + | return 0; |
|
| 47 | + | } |