Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Source.LOCATION | |
| 3 | ||
| 4 | use Semantic.Types.Type | |
| 5 | ||
| 6 | // A function referred to by name becomes a delegate directly - | |
| 7 | // `ldftn` on the method, no closure - so its type is fixed by its | |
| 8 | // declaration. Where a formal's shape differs from it only in which | |
| 9 | // carrier an optional position uses, no such delegate exists: the | |
| 10 | // optional carriers convert at a value boundary, and a delegate's | |
| 11 | // parameter and return are type arguments, where nothing converts. | |
| 12 | // | |
| 13 | // The reference is eta-expanded instead. `f` written into a | |
| 14 | // `(T) -> U?` slot becomes the literal `(a) => f(a)`, spliced into | |
| 15 | // the tree in the reference's place, and the ordinary literal path | |
| 16 | // compiles it under the slot: the return boundary coerces one | |
| 17 | // carrier into the other, and the delegate the slot binds against | |
| 18 | // is the closure's own. | |
| 19 | class FUNCTION_REFERENCE_ADAPTER( | |
| 20 | _symbol_table: Semantic.SYMBOL_TABLE | |
| 21 | ) is | |
| 22 | _index: int | |
| 23 | ||
| 24 | super() | |
| 25 | ||
| 26 | // Whether `source` - the shape a function reference walked to - | |
| 27 | // and `target` - the formal it is going into - describe the same | |
| 28 | // call, differing only in the optional carrier of one or more | |
| 29 | // positions. A position still carrying an inference placeholder | |
| 30 | // says nothing either way and is passed over; a position that | |
| 31 | // disagrees on anything but the carrier rules the pair out, so | |
| 32 | // an ordinary mismatch is still reported rather than adapted. | |
| 33 | is_carrier_only_mismatch(source: Type?, target: Type?) -> bool static is | |
| 34 | if !source? \/ !target? \/ !source.is_function \/ !target.is_function then | |
| 35 | return false | |
| 36 | fi | |
| 37 | ||
| 38 | if source.arguments.count != target.arguments.count then | |
| 39 | return false | |
| 40 | fi | |
| 41 | ||
| 42 | let last = source.arguments.count - 1 | |
| 43 | let any_carrier_pair mut = false | |
| 44 | ||
| 45 | for i in 0..source.arguments.count do | |
| 46 | let from = source.arguments[i] | |
| 47 | let to = target.arguments[i] | |
| 48 | ||
| 49 | if from.is_error \/ to.is_error then | |
| 50 | return false | |
| 51 | fi | |
| 52 | ||
| 53 | if _is_carrier_pair(from, to) then | |
| 54 | any_carrier_pair = true | |
| 55 | continue | |
| 56 | fi | |
| 57 | ||
| 58 | if from.contains_inferred \/ to.contains_inferred \/ from.is_wild \/ to.is_wild then | |
| 59 | continue | |
| 60 | fi | |
| 61 | ||
| 62 | // The return is covariant, every parameter | |
| 63 | // contravariant, exactly as assignability reads them. | |
| 64 | let compatible = | |
| 65 | if i == last then | |
| 66 | to.is_assignable_from(from) | |
| 67 | else | |
| 68 | from.is_assignable_from(to) | |
| 69 | fi | |
| 70 | ||
| 71 | if !compatible then | |
| 72 | return false | |
| 73 | fi | |
| 74 | od | |
| 75 | ||
| 76 | return any_carrier_pair | |
| 77 | si | |
| 78 | ||
| 79 | // Two optional types over the same inner type held in different | |
| 80 | // carriers: `MAYBE[T]` against a reference `T?` or a | |
| 81 | // `Nullable[T]`. Both directions, since a reference may be going | |
| 82 | // either way across the boundary. | |
| 83 | _is_carrier_pair(left: Type, right: Type) -> bool static is | |
| 84 | if !left.is_optional \/ !right.is_optional then | |
| 85 | return false | |
| 86 | fi | |
| 87 | ||
| 88 | if left.is_maybe == right.is_maybe then | |
| 89 | return false | |
| 90 | fi | |
| 91 | ||
| 92 | let left_inner = left.optional_inner_type | |
| 93 | let right_inner = right.optional_inner_type | |
| 94 | ||
| 95 | if !left_inner? \/ !right_inner? then | |
| 96 | return false | |
| 97 | fi | |
| 98 | ||
| 99 | // A type variable or a placeholder at the inner position | |
| 100 | // says nothing about whether the two agree - the carrier | |
| 101 | // is what is being compared, and it already differs. | |
| 102 | return | |
| 103 | left_inner.contains_inferred \/ | |
| 104 | right_inner.contains_inferred \/ | |
| 105 | left_inner.is_wild \/ | |
| 106 | right_inner.is_wild \/ | |
| 107 | left_inner.is_equivalent_to(right_inner) | |
| 108 | si | |
| 109 | ||
| 110 | // Builds `(a0, .., an) => reference(a0, .., an)` over the | |
| 111 | // reference, and declares the closure the literal needs. The | |
| 112 | // caller puts the literal where the reference stood - a call | |
| 113 | // holds one in its argument list by index, an operator in a | |
| 114 | // named slot - and re-walks it in the reference's place. Null | |
| 115 | // when the reference is not one this can wrap. | |
| 116 | try_adapt( | |
| 117 | reference: Trees.Expressions.Expression, | |
| 118 | arity: int | |
| 119 | ) -> Trees.Expressions.FUNCTION? is | |
| 120 | if isa Trees.Expressions.FUNCTION(reference) then | |
| 121 | return null | |
| 122 | fi | |
| 123 | ||
| 124 | let enclosing = _symbol_table.current_function | |
| 125 | ||
| 126 | if !enclosing? then | |
| 127 | return null | |
| 128 | fi | |
| 129 | ||
| 130 | let location = reference.location | |
| 131 | ||
| 132 | let formals = Collections.LIST[Trees.Expressions.Expression]() | |
| 133 | let actuals = Collections.LIST[Trees.Expressions.Expression]() | |
| 134 | let names = Collections.LIST[string]() | |
| 135 | ||
| 136 | for i in 0..arity do | |
| 137 | let name = "$adapted_{_index}_{i}" | |
| 138 | ||
| 139 | names.add(name) | |
| 140 | ||
| 141 | formals.add( | |
| 142 | Trees.Expressions.VARIABLE( | |
| 143 | location, | |
| 144 | Trees.Identifiers.Identifier(location, name), | |
| 145 | Trees.TypeExpressions.INFER(location), | |
| 146 | null | |
| 147 | ) | |
| 148 | ) | |
| 149 | ||
| 150 | actuals.add( | |
| 151 | Trees.Expressions.IDENTIFIER( | |
| 152 | location, | |
| 153 | Trees.Identifiers.Identifier(location, name) | |
| 154 | ) | |
| 155 | ) | |
| 156 | od | |
| 157 | ||
| 158 | // The reference itself becomes the literal's callee, so it | |
| 159 | // is walked once, in its new home. Whatever the earlier walk | |
| 160 | // pushed onto it was a function-type slot it no longer sits | |
| 161 | // in. | |
| 162 | reference.set_expected_type(null, null) | |
| 163 | ||
| 164 | let call = | |
| 165 | Trees.Expressions.CALL( | |
| 166 | location, | |
| 167 | reference, | |
| 168 | Trees.Expressions.LIST(location, actuals) | |
| 169 | ) | |
| 170 | ||
| 171 | let body = Trees.Bodies.EXPRESSION(location, call) | |
| 172 | ||
| 173 | let literal = | |
| 174 | Trees.Expressions.FUNCTION( | |
| 175 | location, | |
| 176 | Trees.Expressions.LIST(location, formals), | |
| 177 | Trees.TypeExpressions.INFER(location), | |
| 178 | body, | |
| 179 | false | |
| 180 | ) | |
| 181 | ||
| 182 | let closure = | |
| 183 | cast Semantic.Symbols.Closure?( | |
| 184 | enclosing.declare_closure( | |
| 185 | location, | |
| 186 | "$adapted_{_index}", | |
| 187 | cast Semantic.Scope?(_symbol_table.current_closure_context)!, | |
| 188 | _symbol_table.current_scope, | |
| 189 | false, | |
| 190 | null | |
| 191 | ) | |
| 192 | ) | |
| 193 | ||
| 194 | _index = _index + 1 | |
| 195 | ||
| 196 | if !closure? then | |
| 197 | return null | |
| 198 | fi | |
| 199 | ||
| 200 | closure.mark_synthesized() | |
| 201 | ||
| 202 | closure.is_carrier_adapter = true | |
| 203 | ||
| 204 | _symbol_table.associate_node_with_scope(literal, closure) | |
| 205 | ||
| 206 | closure.start_declaring_arguments() | |
| 207 | ||
| 208 | for name in names do | |
| 209 | closure.declare_variable(location, name, false, null).mark_synthesized() | |
| 210 | od | |
| 211 | ||
| 212 | closure.end_declaring_arguments() | |
| 213 | ||
| 214 | // The body is a scope carrier in its own right, as it is | |
| 215 | // under any other literal. | |
| 216 | _symbol_table.associate_node_with_scope(body, Semantic.BLOCK_SCOPE(closure)) | |
| 217 | ||
| 218 | return literal | |
| 219 | si | |
| 220 | si | |
| 221 | si |