Appearance
| 1 | namespace IR.Values is | |
| 2 | use Semantic.Types.Type | |
| 3 | ||
| 4 | // `a =~ b` where either operand is optional. | |
| 5 | // | |
| 6 | // The left operand is always tested when it is optional, because an | |
| 7 | // absent one has no receiver to dispatch on: absent on the left | |
| 8 | // answers whether the right is absent too, so two absent values are | |
| 9 | // equal. | |
| 10 | // | |
| 11 | // The right operand is tested here only when the operator declares | |
| 12 | // its parameter non-optional, which is `guards_argument`. An | |
| 13 | // operator declaring `T?` is written to answer for an absent | |
| 14 | // argument itself, so it is handed one and decides. | |
| 15 | // | |
| 16 | // Presence arrives as a bool-valued child rather than being tested | |
| 17 | // here, so the three optional lowerings all reach the same shape: a | |
| 18 | // reference optional compares itself against null, a `NULLABLE[T]` | |
| 19 | // or `MAYBE[T]` reads its `has_value` member. A null child means the | |
| 20 | // operand is not optional, and so is always present. | |
| 21 | // | |
| 22 | // `!~` wraps the whole thing in a NOT, so it negates these arms | |
| 23 | // along with the body's answer. | |
| 24 | // | |
| 25 | // <left presence> // each presence child is read up to twice, | |
| 26 | // brtrue <present> // so the caller hands over repeatable | |
| 27 | // // loads rather than the expressions | |
| 28 | // <right presence> | |
| 29 | // ldc.i4.0 | |
| 30 | // ceq // both absent | |
| 31 | // br <end> | |
| 32 | // <present>: | |
| 33 | // <right presence> // only when guards_argument | |
| 34 | // brtrue <both> | |
| 35 | // ldc.i4.0 // left present, right absent | |
| 36 | // br <end> | |
| 37 | // <both>: | |
| 38 | // <equals call> | |
| 39 | // <end>: | |
| 40 | // | |
| 41 | // Built by COMPILE_OPERATORS. The branch offsets are written long | |
| 42 | // rather than short: the call arm carries an arbitrary user body. | |
| 43 | class NULL_SAFE_EQUALITY: Value is | |
| 44 | left_presence: Value? | |
| 45 | right_presence: Value? | |
| 46 | equals_call: Value | |
| 47 | guards_argument: bool | |
| 48 | _result_type: Type | |
| 49 | ||
| 50 | type: Type => _result_type | |
| 51 | is_lightweight_pure: bool => false | |
| 52 | ||
| 53 | init( | |
| 54 | left_presence: Value?, | |
| 55 | right_presence: Value?, | |
| 56 | equals_call: Value, | |
| 57 | guards_argument: bool, | |
| 58 | result_type: Type | |
| 59 | ) is | |
| 60 | super.init() | |
| 61 | ||
| 62 | self.left_presence = left_presence | |
| 63 | self.right_presence = right_presence | |
| 64 | self.equals_call = equals_call | |
| 65 | self.guards_argument = guards_argument | |
| 66 | self._result_type = result_type | |
| 67 | si | |
| 68 | ||
| 69 | gen(context: IR.CONTEXT) is | |
| 70 | let left_present = IR.LABEL() | |
| 71 | let both_present = IR.LABEL() | |
| 72 | let end_label = IR.LABEL() | |
| 73 | ||
| 74 | let body = context.current_srm_body_emitter! | |
| 75 | ||
| 76 | if left_presence? then | |
| 77 | gen(left_presence, context) | |
| 78 | body.branch(System.Reflection.Metadata.ILOpCode.BRTRUE, left_present) | |
| 79 | ||
| 80 | if right_presence? then | |
| 81 | gen(right_presence, context) | |
| 82 | body.ldc_i4(0) | |
| 83 | body.op(System.Reflection.Metadata.ILOpCode.CEQ) | |
| 84 | else | |
| 85 | // a right operand that can never be absent does not | |
| 86 | // match an absent left one | |
| 87 | body.ldc_i4(0) | |
| 88 | fi | |
| 89 | ||
| 90 | body.branch(System.Reflection.Metadata.ILOpCode.BR, end_label) | |
| 91 | body.mark_label(left_present) | |
| 92 | fi | |
| 93 | ||
| 94 | if guards_argument /\ right_presence? then | |
| 95 | gen(right_presence, context) | |
| 96 | body.branch(System.Reflection.Metadata.ILOpCode.BRTRUE, both_present) | |
| 97 | body.ldc_i4(0) | |
| 98 | body.branch(System.Reflection.Metadata.ILOpCode.BR, end_label) | |
| 99 | body.mark_label(both_present) | |
| 100 | fi | |
| 101 | ||
| 102 | gen(equals_call, context) | |
| 103 | ||
| 104 | body.mark_label(end_label) | |
| 105 | si | |
| 106 | ||
| 107 | to_string() -> string => | |
| 108 | "null-safe-equality:[{type}]({left_presence},{right_presence},{equals_call},guards-argument={guards_argument})" | |
| 109 | si | |
| 110 | si |