# HG changeset patch # User mcimadamore # Date 1281967090 -3600 # Node ID dc550520ed6fc1af0b19b482020807194bfd1335 # Parent 27bae58329d5d492c8c9ae42f3897a02f39b88d5 6369605: Unconstrained type variables fails to include bounds Summary: unconstrained type-variables with recursive bounds are not inferred properly Reviewed-by: jjg diff -r 27bae58329d5 -r dc550520ed6f src/share/classes/com/sun/tools/javac/code/Type.java --- a/src/share/classes/com/sun/tools/javac/code/Type.java Mon Aug 16 14:56:23 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Type.java Mon Aug 16 14:58:10 2010 +0100 @@ -347,11 +347,17 @@ return false; } - /** Does this type contain an occurrence of some type in `elems'? + /** Does this type contain an occurrence of some type in 'ts'? */ - public boolean containsSome(List ts) { - for (List l = ts; l.nonEmpty(); l = l.tail) - if (this.contains(ts.head)) return true; + public boolean containsAny(List ts) { + for (Type t : ts) + if (this.contains(t)) return true; + return false; + } + + public static boolean containsAny(List ts1, List ts2) { + for (Type t : ts1) + if (t.containsAny(ts2)) return true; return false; } @@ -431,6 +437,10 @@ this.bound = bound; } + public boolean contains(Type t) { + return kind != UNBOUND && type.contains(t); + } + public boolean isSuperBound() { return kind == SUPER || kind == UNBOUND; @@ -681,7 +691,9 @@ return elem == this || (isParameterized() - && (getEnclosingType().contains(elem) || contains(getTypeArguments(), elem))); + && (getEnclosingType().contains(elem) || contains(getTypeArguments(), elem))) + || (isCompound() + && (supertype_field.contains(elem) || contains(interfaces_field, elem))); } public void complete() { diff -r 27bae58329d5 -r dc550520ed6f src/share/classes/com/sun/tools/javac/comp/Infer.java --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Mon Aug 16 14:56:23 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Mon Aug 16 14:58:10 2010 +0100 @@ -138,24 +138,73 @@ /** A mapping that returns its type argument with every UndetVar replaced * by its `inst' field. Throws a NoInstanceException * if this not possible because an `inst' field is null. + * Note: mutually referring undertvars will be left uninstantiated + * (that is, they will be replaced by the underlying type-variable). */ + Mapping getInstFun = new Mapping("getInstFun") { public Type apply(Type t) { switch (t.tag) { - case UNKNOWN: - throw ambiguousNoInstanceException - .setMessage("undetermined.type"); - case UNDETVAR: - UndetVar that = (UndetVar) t; - if (that.inst == null) + case UNKNOWN: throw ambiguousNoInstanceException - .setMessage("type.variable.has.undetermined.type", - that.qtype); - return apply(that.inst); - default: - return t.map(this); + .setMessage("undetermined.type"); + case UNDETVAR: + UndetVar that = (UndetVar) t; + if (that.inst == null) + throw ambiguousNoInstanceException + .setMessage("type.variable.has.undetermined.type", + that.qtype); + return isConstraintCyclic(that) ? + that.qtype : + apply(that.inst); + default: + return t.map(this); } } + + private boolean isConstraintCyclic(UndetVar uv) { + Types.UnaryVisitor constraintScanner = + new Types.UnaryVisitor() { + + List seen = List.nil(); + + Boolean visit(List ts) { + for (Type t : ts) { + if (visit(t)) return true; + } + return false; + } + + public Boolean visitType(Type t, Void ignored) { + return false; + } + + @Override + public Boolean visitClassType(ClassType t, Void ignored) { + if (t.isCompound()) { + return visit(types.supertype(t)) || + visit(types.interfaces(t)); + } else { + return visit(t.getTypeArguments()); + } + } + @Override + public Boolean visitWildcardType(WildcardType t, Void ignored) { + return visit(t.type); + } + + @Override + public Boolean visitUndetVar(UndetVar t, Void ignored) { + if (seen.contains(t)) { + return true; + } else { + seen = seen.prepend(t); + return visit(t.inst); + } + } + }; + return constraintScanner.visit(uv); + } }; /*************************************************************************** @@ -257,10 +306,9 @@ TypeVar tv = (TypeVar)uv.qtype; ListBuffer hibounds = new ListBuffer(); for (Type t : that.getConstraints(tv, ConstraintKind.EXTENDS)) { - if (!t.containsSome(that.tvars) && t.tag != BOT) { - hibounds.append(t); - } + hibounds.append(types.subst(t, that.tvars, undetvars)); } + List inst = that.getConstraints(tv, ConstraintKind.EQUAL); if (inst.nonEmpty() && inst.head.tag != BOT) { uv.inst = inst.head; @@ -279,9 +327,32 @@ // check bounds List targs = Type.map(undetvars, getInstFun); - targs = types.subst(targs, that.tvars, targs); + if (Type.containsAny(targs, that.tvars)) { + //replace uninferred type-vars + targs = types.subst(targs, + that.tvars, + instaniateAsUninferredVars(undetvars, that.tvars)); + } return chk.checkType(warn.pos(), that.inst(targs, types), to); } + //where + private List instaniateAsUninferredVars(List undetvars, List tvars) { + ListBuffer new_targs = ListBuffer.lb(); + //step 1 - create syntethic captured vars + for (Type t : undetvars) { + UndetVar uv = (UndetVar)t; + Type newArg = new CapturedType(t.tsym.name, t.tsym, uv.inst, syms.botType, null); + new_targs = new_targs.append(newArg); + } + //step 2 - replace synthetic vars in their bounds + for (Type t : new_targs.toList()) { + CapturedType ct = (CapturedType)t; + ct.bound = types.subst(ct.bound, tvars, new_targs.toList()); + WildcardType wt = new WildcardType(ct.bound, BoundKind.EXTENDS, syms.boundClass); + ct.wildcard = wt; + } + return new_targs.toList(); + } /** Instantiate method type `mt' by finding instantiations of * `tvars' so that method can be applied to `argtypes'. diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/Diagnostics/6862608/T6862608a.out --- a/test/tools/javac/Diagnostics/6862608/T6862608a.out Mon Aug 16 14:56:23 2010 +0100 +++ b/test/tools/javac/Diagnostics/6862608/T6862608a.out Mon Aug 16 14:58:10 2010 +0100 @@ -1,3 +1,3 @@ -T6862608a.java:19:41: compiler.err.invalid.inferred.types: T, (compiler.misc.inferred.do.not.conform.to.params: java.lang.Iterable>, java.util.List>) +T6862608a.java:19:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator, java.util.Comparator)), java.util.Comparator, java.util.Comparator - compiler.misc.where.description.typevar: T,{(compiler.misc.where.typevar: T, java.lang.Object, kindname.method, compound(java.lang.Iterable>))} 1 error diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/diags/examples.not-yet.txt --- a/test/tools/javac/diags/examples.not-yet.txt Mon Aug 16 14:56:23 2010 +0100 +++ b/test/tools/javac/diags/examples.not-yet.txt Mon Aug 16 14:58:10 2010 +0100 @@ -64,6 +64,7 @@ compiler.misc.fatal.err.cant.locate.meth # Resolve, from Lower compiler.misc.file.does.not.contain.package compiler.misc.illegal.start.of.class.file +compiler.misc.inferred.do.not.conform.to.params # UNUSED (hard to see if very complex inference scenario might require this though, so leaving it in, as per JLS3) compiler.misc.kindname.annotation compiler.misc.kindname.enum compiler.misc.kindname.package diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/diags/examples/InvalidInferredTypes.java --- a/test/tools/javac/diags/examples/InvalidInferredTypes.java Mon Aug 16 14:56:23 2010 +0100 +++ b/test/tools/javac/diags/examples/InvalidInferredTypes.java Mon Aug 16 14:58:10 2010 +0100 @@ -22,17 +22,17 @@ */ // key: compiler.err.invalid.inferred.types -// key: compiler.misc.inferred.do.not.conform.to.params +// key: compiler.misc.inferred.do.not.conform.to.bounds import java.util.*; class InvalidInferredTypes { - Comparator compound(Iterable> it) { + > T makeList() { return null; } - public void test(List> x) { - Comparator c3 = compound(x); + public void test() { + List l = makeList(); } } diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/generics/inference/6369605/T6369605a.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/inference/6369605/T6369605a.java Mon Aug 16 14:58:10 2010 +0100 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 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. + * + * 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 6369605 + * @summary Unconstrained type variables fails to include bounds + * @author mcimadamore + * @compile T6369605a.java + */ +import java.util.List; + +class T6369605a { + static > T m1() { + return null; + } + + static , U extends List> T m2() { + return null; + } + + static , U extends List, V extends List> T m3() { + return null; + } + + List l1 = m1(); + List l2 = m2(); + List l3 = m3(); +} + diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/generics/inference/6369605/T6369605b.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/inference/6369605/T6369605b.java Mon Aug 16 14:58:10 2010 +0100 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, 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. + * + * 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 6369605 + * @summary Unconstrained type variables fails to include bounds + * @author mcimadamore + * @compile T6369605b.java + */ +import java.util.List; + +class T6369605b { + static , X> List m1() { + return null; + } + + static , U extends List, X> List m2() { + return null; + } + + static , U extends List, V extends List, X> List m3() { + return null; + } + + List l1 = m1(); + List l2 = m2(); + List l3 = m3(); +} diff -r 27bae58329d5 -r dc550520ed6f test/tools/javac/generics/inference/6638712/T6638712a.out --- a/test/tools/javac/generics/inference/6638712/T6638712a.out Mon Aug 16 14:56:23 2010 +0100 +++ b/test/tools/javac/generics/inference/6638712/T6638712a.out Mon Aug 16 14:58:10 2010 +0100 @@ -1,2 +1,2 @@ -T6638712a.java:16:41: compiler.err.invalid.inferred.types: T, (compiler.misc.inferred.do.not.conform.to.params: java.lang.Iterable>, java.util.List>) +T6638712a.java:16:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator, java.util.Comparator)), java.util.Comparator, java.util.Comparator 1 error