8191802: Upward projection result is A<? extends Number> instead of A<? super Integer>

Code in Types.TypeProjection doesn't match the latest spec text

Reviewed-by: vromero
This commit is contained in:
Maurizio Cimadamore 2017-11-29 17:31:23 +00:00
parent bc5a87988c
commit 3534ed0379
7 changed files with 317 additions and 51 deletions

View File

@ -237,7 +237,7 @@ public class Types {
* {@code upwards(List<#CAP1>, [#CAP2]) = List<#CAP1> }
* {@code downwards(List<#CAP1>, [#CAP1]) = not defined }
*/
class TypeProjection extends StructuralTypeMapping<ProjectionKind> {
class TypeProjection extends TypeMapping<ProjectionKind> {
List<Type> vars;
Set<Type> seen = new HashSet<>();
@ -257,13 +257,21 @@ public class Types {
Type outer = t.getEnclosingType();
Type outer1 = visit(outer, pkind);
List<Type> typarams = t.getTypeArguments();
List<Type> typarams1 = typarams.map(ta -> mapTypeArgument(ta, pkind));
if (typarams1.stream().anyMatch(ta -> ta.hasTag(BOT))) {
//not defined
return syms.botType;
List<Type> formals = t.tsym.type.getTypeArguments();
ListBuffer<Type> typarams1 = new ListBuffer<>();
boolean changed = false;
for (Type actual : typarams) {
Type t2 = mapTypeArgument(t, formals.head.getUpperBound(), actual, pkind);
if (t2.hasTag(BOT)) {
//not defined
return syms.botType;
}
typarams1.add(t2);
changed |= actual != t2;
formals = formals.tail;
}
if (outer1 == outer && typarams1 == typarams) return t;
else return new ClassType(outer1, typarams1, t.tsym, t.getMetadata()) {
if (outer1 == outer && !changed) return t;
else return new ClassType(outer1, typarams1.toList(), t.tsym, t.getMetadata()) {
@Override
protected boolean needsStripping() {
return true;
@ -272,21 +280,23 @@ public class Types {
}
}
protected Type makeWildcard(Type upper, Type lower) {
BoundKind bk;
Type bound;
if (upper.hasTag(BOT)) {
upper = syms.objectType;
}
boolean isUpperObject = isSameType(upper, syms.objectType);
if (!lower.hasTag(BOT) && isUpperObject) {
bound = lower;
bk = SUPER;
@Override
public Type visitArrayType(ArrayType t, ProjectionKind s) {
Type elemtype = t.elemtype;
Type elemtype1 = visit(elemtype, s);
if (elemtype1 == elemtype) {
return t;
} else if (elemtype1.hasTag(BOT)) {
//undefined
return syms.botType;
} else {
bound = upper;
bk = isUpperObject ? UNBOUND : EXTENDS;
return new ArrayType(elemtype1, t.tsym, t.metadata) {
@Override
protected boolean needsStripping() {
return true;
}
};
}
return new WildcardType(bound, bk, syms.boundClass);
}
@Override
@ -322,33 +332,79 @@ public class Types {
}
}
@Override
public Type visitWildcardType(WildcardType wt, ProjectionKind pkind) {
switch (pkind) {
case UPWARDS:
return wt.isExtendsBound() ?
wt.type.map(this, pkind) :
syms.objectType;
case DOWNWARDS:
return wt.isSuperBound() ?
wt.type.map(this, pkind) :
syms.botType;
default:
Assert.error();
return null;
}
private Type mapTypeArgument(Type site, Type declaredBound, Type t, ProjectionKind pkind) {
return t.containsAny(vars) ?
t.map(new TypeArgumentProjection(site, declaredBound), pkind) :
t;
}
private Type mapTypeArgument(Type t, ProjectionKind pkind) {
if (!t.containsAny(vars)) {
return t;
} else if (!t.hasTag(WILDCARD) && pkind == ProjectionKind.DOWNWARDS) {
//not defined
return syms.botType;
} else {
Type upper = t.map(this, pkind);
Type lower = t.map(this, pkind.complement());
return makeWildcard(upper, lower);
class TypeArgumentProjection extends TypeMapping<ProjectionKind> {
Type site;
Type declaredBound;
TypeArgumentProjection(Type site, Type declaredBound) {
this.site = site;
this.declaredBound = declaredBound;
}
@Override
public Type visitType(Type t, ProjectionKind pkind) {
//type argument is some type containing restricted vars
if (pkind == ProjectionKind.DOWNWARDS) {
//not defined
return syms.botType;
}
Type upper = t.map(TypeProjection.this, ProjectionKind.UPWARDS);
Type lower = t.map(TypeProjection.this, ProjectionKind.DOWNWARDS);
List<Type> formals = site.tsym.type.getTypeArguments();
BoundKind bk;
Type bound;
if (!isSameType(upper, syms.objectType) &&
(declaredBound.containsAny(formals) ||
!isSubtype(declaredBound, upper))) {
bound = upper;
bk = EXTENDS;
} else if (!lower.hasTag(BOT)) {
bound = lower;
bk = SUPER;
} else {
bound = syms.objectType;
bk = UNBOUND;
}
return makeWildcard(bound, bk);
}
@Override
public Type visitWildcardType(WildcardType wt, ProjectionKind pkind) {
//type argument is some wildcard whose bound contains restricted vars
Type bound = syms.botType;
BoundKind bk = wt.kind;
switch (wt.kind) {
case EXTENDS:
bound = wt.type.map(TypeProjection.this, pkind);
if (bound.hasTag(BOT)) {
return syms.botType;
}
break;
case SUPER:
bound = wt.type.map(TypeProjection.this, pkind.complement());
if (bound.hasTag(BOT)) {
bound = syms.objectType;
bk = UNBOUND;
}
break;
}
return makeWildcard(bound, bk);
}
private Type makeWildcard(Type bound, BoundKind bk) {
return new WildcardType(bound, bk, syms.boundClass) {
@Override
protected boolean needsStripping() {
return true;
}
};
}
}
}

View File

@ -946,8 +946,6 @@ public class Check {
}
Type checkLocalVarType(DiagnosticPosition pos, Type t, Name name) {
//upward project the initializer type
t = types.upward(t, types.captures(t));
//check that resulting type is not the null type
if (t.hasTag(BOT)) {
log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferNull));
@ -956,7 +954,9 @@ public class Check {
log.error(pos, Errors.CantInferLocalVarType(name, Fragments.LocalCantInferVoid));
return types.createErrorType(t);
}
return t;
//upward project the initializer type
return types.upward(t, types.captures(t));
}
Type checkMethod(final Type mtype,

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 8144903 8171981
* @bug 8144903 8171981 8191802
* @summary Tests for determining the type from the expression
* @build KullaTesting TestingInputStream
* @run testng TypeNameTest
@ -55,7 +55,7 @@ public class TypeNameTest extends KullaTesting {
assertEval("class D<T extends CharSequence> { D<? super T> getS() { return null; } }");
assertEval("D<?> d = new D<String>();");
assertType("d.getS()", "D<? extends CharSequence>");
assertType("d.getS()", "D<?>");
assertType("null", "Object");
assertType("Class.forName( \"java.util.ArrayList\" )", "Class<?>");
assertType("new ArrayList<Boolean>() {}", "ArrayList<Boolean>");
@ -68,7 +68,7 @@ public class TypeNameTest extends KullaTesting {
assertEval("interface J extends A, I {}");
assertEval("interface K extends A, I {}");
assertEval("class P<T extends A & I> {}");
assertType("(P<?>) null", "P<? extends Object>");
assertType("(P<?>) null", "P<?>");
}
public void testConditionals() {

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191802 8191893
* @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
* @compile T8191893.java
*/
class T8191893 {
static class A<E> { }
<T> A<? super T> m(T t) {
return null;
}
<U> A<? super A<? super U>> m2(A<? super U> u) {
return null;
}
void test() {
var varValue = m2(m(10));
A<? super A<Object>> expectedTypeValue = varValue;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191802 8191959
* @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
* @compile T8191959.java
*/
public class T8191959 {
static class A<E> { }
<T> A<? super T> m(T t) {
return null;
}
<U> A<? super A<? extends U>> m2(A<? super U> u) {
return null;
}
void test() {
var varValue = m2(m(10));
A<? super A<? extends Integer>> expectedTypeValue = varValue;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191802
* @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
* @compile TestBadArray.java
*/
import java.util.List;
class TestArr {
<Z> List<? super Z[]> m(List<Z> z) { return null; }
void test(List<? extends Number> l) {
var v = m(l);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8191802
* @summary Upward projection result is A<? extends Number> instead of A<? super Integer>
* @modules jdk.compiler/com.sun.source.tree
* jdk.compiler/com.sun.source.util
* jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.util
* @build LocalVariableInferenceTester
* @run main LocalVariableInferenceTester UpperBounds.java
*/
class UpperBounds {
static class A<T extends Number> { }
static class C<T extends Comparable<T>> { }
void test1(A<? super Integer> s) {
//U is not Object, Bi is not fbound and U is not more specific than Bi, use "? super L"
@InferredType("UpperBounds.A<? super java.lang.Integer>")
var x = s;
}
void test2(C<? super Integer> s) {
//U is not Object, Bi is fbound, use "? extends L"
@InferredType("UpperBounds.C<? extends java.lang.Comparable<?>>")
var x = s;
}
void test3(A<? extends Object> s) {
//U is not Object, Bi is not fbound and U is not more specific than Bi, L is undefined, use "?"
@InferredType("UpperBounds.A<?>")
var x = s;
}
void test4(A<? extends Integer> s) {
//U is not Object, Bi is not fbound and U is more specific than Bi, use "? extends U"
@InferredType("UpperBounds.A<? extends java.lang.Integer>")
var x = s;
}
void test5(A<? extends Object> s) {
//U is not Object, Bi is not fbound and U is not more specific than Bi, L is undefined, use "?"
@InferredType("UpperBounds.A<?>")
var x = s;
}
}