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
| 2353 | * return bare NAMED(Stream) with no template args, and the SAM binder |
| 2354 | * couldn't substitute T0. */ |
| 2355 | static const CBMType *propagate_template(CBMArena *a, const char *recv_qn, const char *method_name, |
| 2356 | const CBMType *const *recv_targs, int recv_targ_count, |
| 2357 | const CBMType *return_t) { |
| 2358 | if (!return_t || return_t->kind != CBM_TYPE_NAMED) |
| 2359 | return return_t; |
| 2360 | if (recv_targ_count <= 0 || !recv_targs) |
| 2361 | return return_t; |
| 2362 | const char *ret_qn = return_t->data.named.qualified_name; |
| 2363 | if (!ret_qn) |
| 2364 | return return_t; |
| 2365 | |
| 2366 | /* Carriers that preserve T0. */ |
| 2367 | static const char *t0_carriers[] = { |
| 2368 | "java.util.stream.Stream", "java.util.Iterator", "java.util.ListIterator", |
| 2369 | "java.util.Spliterator", "java.util.Optional", "java.util.List", |
| 2370 | "java.util.Set", "java.util.Collection", "java.lang.Iterable", |
| 2371 | "java.util.Queue", "java.util.Deque", NULL, |
| 2372 | }; |
| 2373 | bool is_t0 = false; |
| 2374 | for (int i = 0; t0_carriers[i]; i++) { |
| 2375 | if (strcmp(t0_carriers[i], ret_qn) == 0) { |
| 2376 | is_t0 = true; |
| 2377 | break; |
| 2378 | } |
| 2379 | } |
| 2380 | if (is_t0) { |
| 2381 | const CBMType **args = (const CBMType **)cbm_arena_alloc(a, 2 * sizeof(*args)); |
| 2382 | if (!args) |
| 2383 | return return_t; |
| 2384 | args[0] = recv_targs[0]; |
| 2385 | args[1] = NULL; |
| 2386 | return cbm_type_template(a, ret_qn, args, 1); |
| 2387 | } |
| 2388 | |
| 2389 | /* Map.keySet → Set<K>, Map.values → Collection<V>, Map.entrySet → Set<Entry<K,V>>. */ |
| 2390 | if (is_map_like(recv_qn) && recv_targ_count >= 2) { |
| 2391 | const CBMType **args = (const CBMType **)cbm_arena_alloc(a, 2 * sizeof(*args)); |
| 2392 | if (!args) |
| 2393 | return return_t; |
| 2394 | if (strcmp(method_name, "keySet") == 0) { |
| 2395 | args[0] = recv_targs[0]; |
| 2396 | args[1] = NULL; |
| 2397 | return cbm_type_template(a, "java.util.Set", args, 1); |
| 2398 | } |
| 2399 | if (strcmp(method_name, "values") == 0) { |
| 2400 | args[0] = recv_targs[1]; |
| 2401 | args[1] = NULL; |
| 2402 | return cbm_type_template(a, "java.util.Collection", args, 1); |
| 2403 | } |
| 2404 | } |
| 2405 | return return_t; |
| 2406 | } |
| 2407 | |
| 2408 | /* Given a method-invocation node and its resolved CBMRegisteredFunc, walk |
| 2409 | * each lambda argument: bind its formal_parameters to the SAM's parameter |
no test coverage detected