-
Bug
-
Resolution: Unresolved
-
P4
-
8, 9
If a lambda expression's result type mentions a capture variable that was generated in the body, it is unsound for that capture variable to escape into inference bounds. This is because the soundness of capture depends on the corresponding expression being evaluated only once; lambda bodies can be evaluated multiple times.
An example which exploits this bug to achieve heap pollution:
import java.util.*;
import java.util.stream.Collectors;
import java.util.function.Function;
public class CaptureLambda {
<S,T> List<T> map(List<S> orig, Function<S,T> f) {
return orig.stream().map(f).collect(Collectors.toList());
}
<T> List<List<T>> swap(List<List<T>> lists) {
List<T> l1 = lists.get(0);
List<T> l2 = lists.get(1);
T t1 = l1.remove(0);
T t2 = l2.remove(0);
l1.add(t2);
l2.add(t1);
return lists;
}
List<String> strings = new ArrayList<>();
{ strings.add("x"); }
List<Integer> ints = new ArrayList<>();
{ ints.add(1); }
List<?> pickList(boolean b) { return b ? strings : ints; }
void test() {
List<Boolean> l1 = Arrays.asList(true, false);
List<? extends List<?>> l2 = swap(map(l1, b -> pickList(b)));
System.out.printf("l2=%s, strings=%s, ints=%s%n", l2, strings, ints);
}
public static void main(String... args) {
new CaptureLambda().test();
}
}
Proposed solution is for upward projection (see JDK-8016196) to be applied to the result type before comparing to the target function type's return type. This will take some spec work, since the constraints produced by 18.2.1 are expression compatibility constraints, not type compatibility constraints.
Method references also need to be handled. In this case, a shortcut is to just avoid capturing the referenced method's return type in the first place.
An example which exploits this bug to achieve heap pollution:
import java.util.*;
import java.util.stream.Collectors;
import java.util.function.Function;
public class CaptureLambda {
<S,T> List<T> map(List<S> orig, Function<S,T> f) {
return orig.stream().map(f).collect(Collectors.toList());
}
<T> List<List<T>> swap(List<List<T>> lists) {
List<T> l1 = lists.get(0);
List<T> l2 = lists.get(1);
T t1 = l1.remove(0);
T t2 = l2.remove(0);
l1.add(t2);
l2.add(t1);
return lists;
}
List<String> strings = new ArrayList<>();
{ strings.add("x"); }
List<Integer> ints = new ArrayList<>();
{ ints.add(1); }
List<?> pickList(boolean b) { return b ? strings : ints; }
void test() {
List<Boolean> l1 = Arrays.asList(true, false);
List<? extends List<?>> l2 = swap(map(l1, b -> pickList(b)));
System.out.printf("l2=%s, strings=%s, ints=%s%n", l2, strings, ints);
}
public static void main(String... args) {
new CaptureLambda().test();
}
}
Proposed solution is for upward projection (see JDK-8016196) to be applied to the result type before comparing to the target function type's return type. This will take some spec work, since the constraints produced by 18.2.1 are expression compatibility constraints, not type compatibility constraints.
Method references also need to be handled. In this case, a shortcut is to just avoid capturing the referenced method's return type in the first place.
- blocks
-
JDK-8160244 skip capture conversion before subtyping if types are parameterizations of the same class or interface
- Open
- relates to
-
JDK-8016196 Inference: define supertype parameterization for wildcard-parameterized types
- Open