8170410: inference: javac doesn't implement 18.2.5 correctly
Javac does not generate constraints of the kind 'throws alpha' as described in the spec Reviewed-by: vromero, dlsmith
This commit is contained in:
parent
ae8ace7912
commit
04f289629a
@ -1869,6 +1869,12 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
*/
|
||||
public static class UndetVar extends DelegatedType {
|
||||
|
||||
enum Kind {
|
||||
NORMAL,
|
||||
CAPTURED,
|
||||
THROWS;
|
||||
}
|
||||
|
||||
/** Inference variable change listener. The listener method is called
|
||||
* whenever a change to the inference variable's bounds occurs
|
||||
*/
|
||||
@ -1929,6 +1935,8 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
/** inference variable's change listener */
|
||||
public UndetVarListener listener = null;
|
||||
|
||||
Kind kind;
|
||||
|
||||
@Override
|
||||
public <R,S> R accept(Type.Visitor<R,S> v, S s) {
|
||||
return v.visitUndetVar(this, s);
|
||||
@ -1937,6 +1945,9 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
public UndetVar(TypeVar origin, UndetVarListener listener, Types types) {
|
||||
// This is a synthesized internal type, so we cannot annotate it.
|
||||
super(UNDETVAR, origin);
|
||||
this.kind = origin.isCaptured() ?
|
||||
Kind.CAPTURED :
|
||||
Kind.NORMAL;
|
||||
this.listener = listener;
|
||||
bounds = new EnumMap<>(InferenceBound.class);
|
||||
List<Type> declaredBounds = types.getBounds(origin);
|
||||
@ -1948,6 +1959,10 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
//add bound works in reverse order
|
||||
addBound(InferenceBound.UPPER, t, types, true);
|
||||
}
|
||||
if (origin.isCaptured() && !origin.lower.hasTag(BOT)) {
|
||||
//add lower bound if needed
|
||||
addBound(InferenceBound.LOWER, origin.lower, types, true);
|
||||
}
|
||||
}
|
||||
|
||||
@DefinedBy(Api.LANGUAGE_MODEL)
|
||||
@ -1977,6 +1992,14 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setThrow() {
|
||||
if (this.kind == Kind.CAPTURED) {
|
||||
//invalid state transition
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
this.kind = Kind.THROWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new copy of this undet var.
|
||||
*/
|
||||
@ -2062,17 +2085,29 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
addBound(ib, bound, types, false);
|
||||
}
|
||||
|
||||
protected void addBound(InferenceBound ib, Type bound, Types types, boolean update) {
|
||||
Type bound2 = bound.map(toTypeVarMap).baseType();
|
||||
List<Type> prevBounds = bounds.get(ib);
|
||||
if (bound == qtype) return;
|
||||
for (Type b : prevBounds) {
|
||||
//check for redundancy - use strict version of isSameType on tvars
|
||||
//(as the standard version will lead to false positives w.r.t. clones ivars)
|
||||
if (types.isSameType(b, bound2, true)) return;
|
||||
@SuppressWarnings("fallthrough")
|
||||
private void addBound(InferenceBound ib, Type bound, Types types, boolean update) {
|
||||
if (kind == Kind.CAPTURED && !update) {
|
||||
//Captured inference variables bounds must not be updated during incorporation,
|
||||
//except when some inference variable (beta) has been instantiated in the
|
||||
//right-hand-side of a 'C<alpha> = capture(C<? extends/super beta>) constraint.
|
||||
if (bound.hasTag(UNDETVAR) && !((UndetVar)bound).isCaptured()) {
|
||||
//If the new incoming bound is itself a (regular) inference variable,
|
||||
//then we are allowed to propagate this inference variable bounds to it.
|
||||
((UndetVar)bound).addBound(ib.complement(), this, types, false);
|
||||
}
|
||||
} else {
|
||||
Type bound2 = bound.map(toTypeVarMap).baseType();
|
||||
List<Type> prevBounds = bounds.get(ib);
|
||||
if (bound == qtype) return;
|
||||
for (Type b : prevBounds) {
|
||||
//check for redundancy - use strict version of isSameType on tvars
|
||||
//(as the standard version will lead to false positives w.r.t. clones ivars)
|
||||
if (types.isSameType(b, bound2, true)) return;
|
||||
}
|
||||
bounds.put(ib, prevBounds.prepend(bound2));
|
||||
notifyBoundChange(ib, bound2, false);
|
||||
}
|
||||
bounds.put(ib, prevBounds.prepend(bound2));
|
||||
notifyBoundChange(ib, bound2, false);
|
||||
}
|
||||
//where
|
||||
TypeMapping<Void> toTypeVarMap = new TypeMapping<Void>() {
|
||||
@ -2128,46 +2163,12 @@ public abstract class Type extends AnnoConstruct implements TypeMirror {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCaptured() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to represent synthetic captured inference variables
|
||||
* that can be generated during nested generic method calls. The only difference
|
||||
* between these inference variables and ordinary ones is that captured inference
|
||||
* variables cannot get new bounds through incorporation.
|
||||
*/
|
||||
public static class CapturedUndetVar extends UndetVar {
|
||||
|
||||
public CapturedUndetVar(CapturedType origin, UndetVarListener listener, Types types) {
|
||||
super(origin, listener, types);
|
||||
if (!origin.lower.hasTag(BOT)) {
|
||||
addBound(InferenceBound.LOWER, origin.lower, types, true);
|
||||
}
|
||||
public final boolean isCaptured() {
|
||||
return kind == Kind.CAPTURED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBound(InferenceBound ib, Type bound, Types types, boolean update) {
|
||||
if (update) {
|
||||
//only change bounds if request comes from substBounds
|
||||
super.addBound(ib, bound, types, update);
|
||||
}
|
||||
else if (bound.hasTag(UNDETVAR) && !((UndetVar) bound).isCaptured()) {
|
||||
((UndetVar) bound).addBound(ib.complement(), this, types, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaptured() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public UndetVar dup(Types types) {
|
||||
UndetVar uv2 = new CapturedUndetVar((CapturedType)qtype, listener, types);
|
||||
dupTo(uv2, types);
|
||||
return uv2;
|
||||
public final boolean isThrows() {
|
||||
return kind == Kind.THROWS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2494,6 +2494,11 @@ public class Attr extends JCTree.Visitor {
|
||||
List<Type> thrownTypes = resultInfo.checkContext.inferenceContext().asUndetVars(lambdaType.getThrownTypes());
|
||||
|
||||
chk.unhandled(inferredThrownTypes, thrownTypes);
|
||||
|
||||
//18.2.5: "In addition, for all j (1 <= j <= n), the constraint reduces to the bound throws Ej"
|
||||
thrownTypes.stream()
|
||||
.filter(t -> t.hasTag(UNDETVAR))
|
||||
.forEach(t -> ((UndetVar)t).setThrow());
|
||||
}
|
||||
|
||||
checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
|
||||
@ -3074,6 +3079,10 @@ public class Attr extends JCTree.Visitor {
|
||||
if (chk.unhandled(refType.getThrownTypes(), thrownTypes).nonEmpty()) {
|
||||
log.error(tree, "incompatible.thrown.types.in.mref", refType.getThrownTypes());
|
||||
}
|
||||
//18.2.5: "In addition, for all j (1 <= j <= n), the constraint reduces to the bound throws Ej"
|
||||
thrownTypes.stream()
|
||||
.filter(t -> t.hasTag(UNDETVAR))
|
||||
.forEach(t -> ((UndetVar)t).setThrow());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,12 +627,11 @@ public class Infer {
|
||||
TypeMapping<Void> fromTypeVarFun = new TypeMapping<Void>() {
|
||||
@Override
|
||||
public Type visitTypeVar(TypeVar tv, Void aVoid) {
|
||||
return new UndetVar(tv, incorporationEngine(), types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type visitCapturedType(CapturedType t, Void aVoid) {
|
||||
return new CapturedUndetVar(t, incorporationEngine(), types);
|
||||
UndetVar uv = new UndetVar(tv, incorporationEngine(), types);
|
||||
if ((tv.tsym.flags() & Flags.THROWS) != 0) {
|
||||
uv.setThrow();
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1463,7 +1462,7 @@ public class Infer {
|
||||
THROWS(InferenceBound.UPPER) {
|
||||
@Override
|
||||
public boolean accepts(UndetVar t, InferenceContext inferenceContext) {
|
||||
if ((t.qtype.tsym.flags() & Flags.THROWS) == 0) {
|
||||
if (!t.isThrows()) {
|
||||
//not a throws undet var
|
||||
return false;
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* @test
|
||||
* @bug 8170410
|
||||
* @summary inference: javac doesn't implement 18.2.5 correctly
|
||||
* @compile T8170410.java
|
||||
*/
|
||||
|
||||
class T8170410 {
|
||||
interface CheckedSupplier<T extends Throwable, R> {
|
||||
R get() throws T;
|
||||
}
|
||||
|
||||
static <T extends Throwable, R> CheckedSupplier<T, R> checked(CheckedSupplier<T, R> checkedSupplier) {
|
||||
return checkedSupplier;
|
||||
}
|
||||
|
||||
static void test() {
|
||||
checked(() -> null).get();
|
||||
checked(T8170410::m).get();
|
||||
}
|
||||
|
||||
static String m() { return ""; }
|
||||
}
|
Loading…
Reference in New Issue
Block a user