When a method on a TEMPLATE receiver returns a NAMED type that is itself * a parametric "carrier" (Stream, Iterator, Optional, …), preserve the * receiver's element type so chained lambda inference keeps working. * * This is the fix for `xs.stream().filter(x -> x.foo())` losing track of * String inside the filter lambda — without propagation, stream() would * return bare NAMED(Stream) with n
| 2288 | * return bare NAMED(Stream) with no template args, and the SAM binder |
| 2289 | * couldn't substitute T0. */ |
| 2290 | static const CBMType *propagate_template(CBMArena *a, const char *recv_qn, const char *method_name, |
| 2291 | const CBMType *const *recv_targs, int recv_targ_count, |
| 2292 | const CBMType *return_t) { |
| 2293 | if (!return_t || return_t->kind != CBM_TYPE_NAMED) |
| 2294 | return return_t; |
| 2295 | if (recv_targ_count <= 0 || !recv_targs) |
| 2296 | return return_t; |
| 2297 | const char *ret_qn = return_t->data.named.qualified_name; |
| 2298 | if (!ret_qn) |
| 2299 | return return_t; |
| 2300 | |
| 2301 | /* Carriers that preserve T0. */ |
| 2302 | static const char *t0_carriers[] = { |
| 2303 | "java.util.stream.Stream", "java.util.Iterator", "java.util.ListIterator", |
| 2304 | "java.util.Spliterator", "java.util.Optional", "java.util.List", |
| 2305 | "java.util.Set", "java.util.Collection", "java.lang.Iterable", |
| 2306 | "java.util.Queue", "java.util.Deque", NULL, |
| 2307 | }; |
| 2308 | bool is_t0 = false; |
| 2309 | for (int i = 0; t0_carriers[i]; i++) { |
| 2310 | if (strcmp(t0_carriers[i], ret_qn) == 0) { |
| 2311 | is_t0 = true; |
| 2312 | break; |
| 2313 | } |
| 2314 | } |
| 2315 | if (is_t0) { |
| 2316 | const CBMType **args = (const CBMType **)cbm_arena_alloc(a, 2 * sizeof(*args)); |
| 2317 | if (!args) |
| 2318 | return return_t; |
| 2319 | args[0] = recv_targs[0]; |
| 2320 | args[1] = NULL; |
| 2321 | return cbm_type_template(a, ret_qn, args, 1); |
| 2322 | } |
| 2323 | |
| 2324 | /* Map.keySet → Set<K>, Map.values → Collection<V>, Map.entrySet → Set<Entry<K,V>>. */ |
| 2325 | if (is_map_like(recv_qn) && recv_targ_count >= 2) { |
| 2326 | const CBMType **args = (const CBMType **)cbm_arena_alloc(a, 2 * sizeof(*args)); |
| 2327 | if (!args) |
| 2328 | return return_t; |
| 2329 | if (strcmp(method_name, "keySet") == 0) { |
| 2330 | args[0] = recv_targs[0]; |
| 2331 | args[1] = NULL; |
| 2332 | return cbm_type_template(a, "java.util.Set", args, 1); |
| 2333 | } |
| 2334 | if (strcmp(method_name, "values") == 0) { |
| 2335 | args[0] = recv_targs[1]; |
| 2336 | args[1] = NULL; |
| 2337 | return cbm_type_template(a, "java.util.Collection", args, 1); |
| 2338 | } |
| 2339 | } |
| 2340 | return return_t; |
| 2341 | } |
| 2342 | |
| 2343 | /* Given a method-invocation node and its resolved CBMRegisteredFunc, walk |
| 2344 | * each lambda argument: bind its formal_parameters to the SAM's parameter |
no test coverage detected