Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Logging | |
| 3 | use Source.LOCATION | |
| 4 | use Ghul.Pipes | |
| 5 | ||
| 6 | // What a `..` marker on a formal's declared type asks for. | |
| 7 | enum PackSite is | |
| 8 | NONE, | |
| 9 | CALLABLE, | |
| 10 | SPREAD, | |
| 11 | si | |
| 12 | ||
| 13 | // Collects every `..` argument-pack marker written inside one type | |
| 14 | // expression, so a formal's declared type can be checked as a whole. | |
| 15 | class ARGUMENT_PACK_MARKER_FINDER: Visitor is | |
| 16 | marked: Collections.LIST[Trees.TypeExpressions.NAMED] | |
| 17 | ||
| 18 | init() is | |
| 19 | super.init() | |
| 20 | ||
| 21 | marked = Collections.LIST[Trees.TypeExpressions.NAMED]() | |
| 22 | si | |
| 23 | ||
| 24 | visit(named: Trees.TypeExpressions.NAMED) is | |
| 25 | if named.is_argument_pack then | |
| 26 | marked.add(named) | |
| 27 | fi | |
| 28 | si | |
| 29 | si | |
| 30 | ||
| 31 | // Where a `..` marker is allowed on a formal argument's type, and | |
| 32 | // what the formal it is written on means. | |
| 33 | // | |
| 34 | // `[T..]` declares that `T` stands for the arguments of a call. A | |
| 35 | // formal opts into one of that parameter's behaviours by marking its | |
| 36 | // own type: `f: T.. -> U` takes the arguments as an N-ary function, | |
| 37 | // which the call-site adaptation then fits to it. A plain `T` is the | |
| 38 | // tuple. | |
| 39 | // | |
| 40 | // The marker can sit on the parameter of any function type along the | |
| 41 | // formal's return spine, not only the outermost - `f: X -> T.. -> U` | |
| 42 | // asks for the N-ary function the caller's own lambda returns. The | |
| 43 | // depth is how many returns in the marked function type sits, and is | |
| 44 | // what tells the adaptation how far into the actual to reach. | |
| 45 | class ARGUMENT_PACK_SITES(_logger: Logger) is | |
| 46 | super() | |
| 47 | ||
| 48 | // The formal's declared type opts into N-ary adaptation, and | |
| 49 | // reports every marker written anywhere else in that type. | |
| 50 | is_spread_formal(type_expression: Trees.TypeExpressions.TypeExpression) -> bool => | |
| 51 | classify(type_expression).site =~ PackSite.CALLABLE | |
| 52 | ||
| 53 | // A declared return type opts into the same adaptation, with | |
| 54 | // the marker read at its own spine's positions. The value | |
| 55 | // reading has no return counterpart - nothing spreads a call's | |
| 56 | // arguments into a result - so a marker on the type itself is | |
| 57 | // reported rather than read. | |
| 58 | classify_return(type_expression: Trees.TypeExpressions.TypeExpression) -> (site: PackSite, depth: int) => | |
| 59 | _classify( | |
| 60 | type_expression, | |
| 61 | "a return type can only spread an argument pack into the last parameter of a function type it returns", | |
| 62 | false) | |
| 63 | ||
| 64 | classify(type_expression: Trees.TypeExpressions.TypeExpression) -> (site: PackSite, depth: int) => | |
| 65 | _classify( | |
| 66 | type_expression, | |
| 67 | "an argument pack can only be spread as a formal argument's own type or into the last parameter of a function type it takes or returns", | |
| 68 | true) | |
| 69 | ||
| 70 | _classify( | |
| 71 | type_expression: Trees.TypeExpressions.TypeExpression, | |
| 72 | misplaced: string, | |
| 73 | permit_value_marker: bool | |
| 74 | ) -> (site: PackSite, depth: int) is | |
| 75 | let permitted_callable = Collections.LIST[Trees.TypeExpressions.NAMED?]() | |
| 76 | ||
| 77 | // Parameters of a function type the marker could have been | |
| 78 | // written on, had they been its last. | |
| 79 | let not_last = Collections.LIST[Trees.TypeExpressions.TypeExpression]() | |
| 80 | ||
| 81 | let spine mut = cast Trees.TypeExpressions.FUNCTION?(type_expression) | |
| 82 | ||
| 83 | while spine? do | |
| 84 | // The pack is the last parameter, whatever comes before it: | |
| 85 | // `(A, T..) -> A` takes its first argument as it is and the | |
| 86 | // rest as the pack. | |
| 87 | let count = spine.arguments.elements.count | |
| 88 | ||
| 89 | let parameter = | |
| 90 | if count >= 1 then | |
| 91 | cast Trees.TypeExpressions.NAMED?(spine.arguments.elements[count - 1]) | |
| 92 | else | |
| 93 | null | |
| 94 | fi | |
| 95 | ||
| 96 | // A depth is a position in the spine whether or not its | |
| 97 | // parameter can carry a marker, so an unmarkable one | |
| 98 | // still occupies its place in the list. | |
| 99 | permitted_callable.add(parameter) | |
| 100 | ||
| 101 | for i in 0..count - 1 do | |
| 102 | not_last.add(spine.arguments.elements[i]) | |
| 103 | od | |
| 104 | ||
| 105 | spine = cast Trees.TypeExpressions.FUNCTION?(spine.result) | |
| 106 | od | |
| 107 | ||
| 108 | let permitted_value = cast Trees.TypeExpressions.NAMED?(type_expression) | |
| 109 | ||
| 110 | let finder = ARGUMENT_PACK_MARKER_FINDER() | |
| 111 | ||
| 112 | type_expression.walk(finder) | |
| 113 | ||
| 114 | let accepted mut = PackSite.NONE | |
| 115 | let accepted_depth mut = 0 | |
| 116 | ||
| 117 | for marked in finder.marked do | |
| 118 | let depth = _depth_of(permitted_callable, marked) | |
| 119 | ||
| 120 | if depth >= 0 then | |
| 121 | if _names_a_pack(marked) then | |
| 122 | accepted = PackSite.CALLABLE | |
| 123 | accepted_depth = depth | |
| 124 | fi | |
| 125 | ||
| 126 | continue | |
| 127 | fi | |
| 128 | ||
| 129 | if permitted_value? /\ marked == permitted_value then | |
| 130 | if permit_value_marker /\ _names_a_pack(marked) then | |
| 131 | accepted = PackSite.SPREAD | |
| 132 | accepted_depth = 0 | |
| 133 | elif !permit_value_marker then | |
| 134 | _logger.error(marked.location, misplaced) | |
| 135 | fi | |
| 136 | ||
| 137 | continue | |
| 138 | fi | |
| 139 | ||
| 140 | if not_last |> any(parameter => parameter == marked) then | |
| 141 | _logger.error(marked.location, "an argument pack has to be the last parameter of the function type it is spread into") | |
| 142 | else | |
| 143 | _logger.error(marked.location, misplaced) | |
| 144 | fi | |
| 145 | od | |
| 146 | ||
| 147 | // A function type that takes an argument pack as its last | |
| 148 | // parameter, with no marker written anywhere on it, takes the | |
| 149 | // pack as one tuple. That is a legal thing to ask for, and | |
| 150 | // almost never what was meant: every call written with the | |
| 151 | // pack spread out then fails, far from this declaration. | |
| 152 | if finder.marked.count == 0 then | |
| 153 | for parameter in permitted_callable do | |
| 154 | if let unmarked = parameter /\ unmarked.type? /\ unmarked.type.symbol.is_argument_pack then | |
| 155 | _logger.warn( | |
| 156 | unmarked.location, | |
| 157 | "unmarked-pack-slot", | |
| 158 | "{unmarked.type} is an argument pack but this function type takes it as one tuple", | |
| 159 | unmarked.location, | |
| 160 | "help: write {unmarked.type}.. to take it spread out" | |
| 161 | ) | |
| 162 | fi | |
| 163 | od | |
| 164 | fi | |
| 165 | ||
| 166 | return (site = accepted, depth = accepted_depth) | |
| 167 | si | |
| 168 | ||
| 169 | // How many returns into the formal's own type the marked | |
| 170 | // parameter sits, and negative when it is not one of them. | |
| 171 | _depth_of( | |
| 172 | permitted: Collections.List[Trees.TypeExpressions.NAMED?], | |
| 173 | marked: Trees.TypeExpressions.NAMED | |
| 174 | ) -> int is | |
| 175 | for i in 0..permitted.count do | |
| 176 | if let candidate = permitted[i] /\ candidate == marked then | |
| 177 | return i | |
| 178 | fi | |
| 179 | od | |
| 180 | ||
| 181 | return -1 | |
| 182 | si | |
| 183 | ||
| 184 | // The marked name has to be a type parameter declared `[T..]`. | |
| 185 | // Without that declaration the marker says nothing about what the | |
| 186 | // parameter stands for, so the formal has nothing to spread. | |
| 187 | _names_a_pack(marked: Trees.TypeExpressions.NAMED) -> bool is | |
| 188 | if let type = marked.type then | |
| 189 | if type.symbol.is_argument_pack then | |
| 190 | return true | |
| 191 | fi | |
| 192 | ||
| 193 | _logger.error( | |
| 194 | marked.location, | |
| 195 | "{type} is not an argument pack: declare it [{type}..] to spread it here" | |
| 196 | ) | |
| 197 | fi | |
| 198 | ||
| 199 | return false | |
| 200 | si | |
| 201 | si | |
| 202 | si |