# HG changeset patch # User mcimadamore # Date 1301413251 -3600 # Node ID f5b5112ee1cc5bb181aadc09d5265c91527ec9c8 # Parent ddec8c712e85eac13806afb35a7a19423ad08046 7030150: Type inference for generic instance creation failed for formal type parameter Summary: Problem when explicit generic constructor type-arguments are used in conjunction with diamond Reviewed-by: jjg diff -r ddec8c712e85 -r f5b5112ee1cc src/share/classes/com/sun/tools/javac/code/Types.java --- a/src/share/classes/com/sun/tools/javac/code/Types.java Tue Mar 29 16:40:31 2011 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Types.java Tue Mar 29 16:40:51 2011 +0100 @@ -2461,6 +2461,22 @@ } }; + public Type createMethodTypeWithReturn(Type original, Type newReturn) { + return original.accept(methodWithReturn, newReturn); + } + // where + private final MapVisitor methodWithReturn = new MapVisitor() { + public Type visitType(Type t, Type newReturn) { + throw new IllegalArgumentException("Not a method type: " + t); + } + public Type visitMethodType(MethodType t, Type newReturn) { + return new MethodType(t.argtypes, newReturn, t.thrown, t.tsym); + } + public Type visitForAll(ForAll t, Type newReturn) { + return new ForAll(t.tvars, t.qtype.accept(this, newReturn)); + } + }; + // public Type createErrorType(Type originalType) { return new ErrorType(originalType, syms.errSymbol); diff -r ddec8c712e85 -r f5b5112ee1cc src/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue Mar 29 16:40:31 2011 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java Tue Mar 29 16:40:51 2011 +0100 @@ -1580,7 +1580,7 @@ // Attribute clazz expression and store // symbol + type back into the attributed tree. Type clazztype = attribType(clazz, env); - Pair mapping = getSyntheticScopeMapping(clazztype, cdef != null); + Pair mapping = getSyntheticScopeMapping(clazztype); clazztype = chk.checkDiamond(tree, clazztype); chk.validate(clazz, localEnv); if (tree.encl != null) { @@ -1778,62 +1778,48 @@ Pair mapping, List argtypes, List typeargtypes) { - if (clazztype.isErroneous() || mapping == erroneousMapping) { + if (clazztype.isErroneous() || + clazztype.isInterface() || + mapping == erroneousMapping) { //if the type of the instance creation expression is erroneous, - //or something prevented us to form a valid mapping, return the - //(possibly erroneous) type unchanged + //or if it's an interface, or if something prevented us to form a valid + //mapping, return the (possibly erroneous) type unchanged return clazztype; } - else if (clazztype.isInterface()) { - //if the type of the instance creation expression is an interface - //skip the method resolution step (JLS 15.12.2.7). The type to be - //inferred is of the kind C - clazztype = new ForAll(clazztype.tsym.type.allparams(), clazztype.tsym.type) { - @Override - public List getConstraints(TypeVar tv, ConstraintKind ck) { - switch (ck) { - case EXTENDS: return types.getBounds(tv); - default: return List.nil(); - } - } - @Override - public Type inst(List inferred, Types types) throws Infer.NoInstanceException { - // check that inferred bounds conform to their bounds - infer.checkWithinBounds(tvars, - types.subst(tvars, tvars, inferred), Warner.noWarnings); - return super.inst(inferred, types); - } - }; + + //dup attribution environment and augment the set of inference variables + Env localEnv = env.dup(tree); + localEnv.info.tvars = clazztype.tsym.type.getTypeArguments(); + + //if the type of the instance creation expression is a class type + //apply method resolution inference (JLS 15.12.2.7). The return type + //of the resolved constructor will be a partially instantiated type + ((ClassSymbol) clazztype.tsym).members_field = mapping.snd; + Symbol constructor; + try { + constructor = rs.resolveDiamond(tree.pos(), + localEnv, + clazztype.tsym.type, + argtypes, + typeargtypes); + } finally { + ((ClassSymbol) clazztype.tsym).members_field = mapping.fst; + } + if (constructor.kind == MTH) { + ClassType ct = new ClassType(clazztype.getEnclosingType(), + clazztype.tsym.type.getTypeArguments(), + clazztype.tsym); + clazztype = checkMethod(ct, + constructor, + localEnv, + tree.args, + argtypes, + typeargtypes, + localEnv.info.varArgs).getReturnType(); } else { - //if the type of the instance creation expression is a class type - //apply method resolution inference (JLS 15.12.2.7). The return type - //of the resolved constructor will be a partially instantiated type - ((ClassSymbol) clazztype.tsym).members_field = mapping.snd; - Symbol constructor; - try { - constructor = rs.resolveDiamond(tree.pos(), - env, - clazztype.tsym.type, - argtypes, - typeargtypes); - } finally { - ((ClassSymbol) clazztype.tsym).members_field = mapping.fst; - } - if (constructor.kind == MTH) { - ClassType ct = new ClassType(clazztype.getEnclosingType(), - clazztype.tsym.type.getTypeArguments(), - clazztype.tsym); - clazztype = checkMethod(ct, - constructor, - env, - tree.args, - argtypes, - typeargtypes, - env.info.varArgs).getReturnType(); - } else { - clazztype = syms.errType; - } + clazztype = syms.errType; } + if (clazztype.tag == FORALL && !pt.isErroneous()) { //if the resolved constructor's return type has some uninferred //type-variables, infer them using the expected type and declared @@ -1863,34 +1849,28 @@ * inference. The inferred return type of the synthetic constructor IS * the inferred type for the diamond operator. */ - private Pair getSyntheticScopeMapping(Type ctype, boolean overrideProtectedAccess) { + private Pair getSyntheticScopeMapping(Type ctype) { if (ctype.tag != CLASS) { return erroneousMapping; } + Pair mapping = new Pair(ctype.tsym.members(), new Scope(ctype.tsym)); - List typevars = ctype.tsym.type.getTypeArguments(); + + //for each constructor in the original scope, create a synthetic constructor + //whose return type is the type of the class in which the constructor is + //declared, and insert it into the new scope. for (Scope.Entry e = mapping.fst.lookup(names.init); e.scope != null; e = e.next()) { - MethodSymbol newConstr = (MethodSymbol) e.sym.clone(ctype.tsym); - if (overrideProtectedAccess && (newConstr.flags() & PROTECTED) != 0) { - //make protected constructor public (this is required for - //anonymous inner class creation expressions using diamond) - newConstr.flags_field |= PUBLIC; - newConstr.flags_field &= ~PROTECTED; - } - newConstr.name = names.init; - List oldTypeargs = List.nil(); - if (newConstr.type.tag == FORALL) { - oldTypeargs = ((ForAll) newConstr.type).tvars; - } - newConstr.type = new MethodType(newConstr.type.getParameterTypes(), - new ClassType(ctype.getEnclosingType(), ctype.tsym.type.getTypeArguments(), ctype.tsym), - newConstr.type.getThrownTypes(), - syms.methodClass); - newConstr.type = new ForAll(typevars.prependList(oldTypeargs), newConstr.type); - mapping.snd.enter(newConstr); + Type synthRestype = new ClassType(ctype.getEnclosingType(), + ctype.tsym.type.getTypeArguments(), + ctype.tsym); + MethodSymbol synhConstr = new MethodSymbol(e.sym.flags(), + names.init, + types.createMethodTypeWithReturn(e.sym.type, synthRestype), + e.sym.owner); + mapping.snd.enter(synhConstr); } return mapping; } diff -r ddec8c712e85 -r f5b5112ee1cc src/share/classes/com/sun/tools/javac/comp/Resolve.java --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Mar 29 16:40:31 2011 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Mar 29 16:40:51 2011 +0100 @@ -338,7 +338,11 @@ // tvars is the list of formal type variables for which type arguments // need to inferred. - List tvars = env.info.tvars; + List tvars = null; + if (env.info.tvars != null) { + tvars = types.newInstances(env.info.tvars); + mt = types.subst(mt, env.info.tvars, tvars); + } if (typeargtypes == null) typeargtypes = List.nil(); if (mt.tag != FORALL && typeargtypes.nonEmpty()) { // This is not a polymorphic method, but typeargs are supplied diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/GenericConstructorAndDiamondTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/GenericConstructorAndDiamondTest.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2011, 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 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + */ + +import com.sun.source.util.JavacTask; +import java.net.URI; +import java.util.Arrays; +import javax.tools.Diagnostic; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +public class GenericConstructorAndDiamondTest { + + enum BoundKind { + NO_BOUND(""), + STRING_BOUND("extends String"), + INTEGER_BOUND("extends Integer"); + + String boundStr; + + private BoundKind(String boundStr) { + this.boundStr = boundStr; + } + + boolean matches(TypeArgumentKind tak) { + switch (tak) { + case NONE: return true; + case STRING: return this != INTEGER_BOUND; + case INTEGER: return this != STRING_BOUND; + default: return false; + } + } + } + + enum ConstructorKind { + NON_GENERIC("Foo(Object o) {}"), + GENERIC_NO_BOUND(" Foo(T t) {}"), + GENERIC_STRING_BOUND(" Foo(T t) {}"), + GENERIC_INTEGER_BOUND(" Foo(T t) {}"); + + String constrStr; + + private ConstructorKind(String constrStr) { + this.constrStr = constrStr; + } + + boolean matches(ArgumentKind ak) { + switch (ak) { + case STRING: return this != GENERIC_INTEGER_BOUND; + case INTEGER: return this != GENERIC_STRING_BOUND; + default: return false; + } + } + } + + enum TypeArgArity { + ONE(1), + TWO(2), + THREE(3); + + int n; + + private TypeArgArity(int n) { + this.n = n; + } + } + + enum TypeArgumentKind { + NONE(""), + STRING("String"), + INTEGER("Integer"); + + String typeargStr; + + private TypeArgumentKind(String typeargStr) { + this.typeargStr = typeargStr; + } + + String getArgs(TypeArgArity arity) { + if (this == NONE) return ""; + else { + StringBuilder buf = new StringBuilder(); + String sep = ""; + for (int i = 0 ; i < arity.n ; i++) { + buf.append(sep); + buf.append(typeargStr); + sep = ","; + } + return "<" + buf.toString() + ">"; + } + } + + boolean matches(ArgumentKind ak) { + switch (ak) { + case STRING: return this != INTEGER; + case INTEGER: return this != STRING; + default: return false; + } + } + } + + enum ArgumentKind { + STRING("\"\""), + INTEGER("1"); + + String argStr; + + private ArgumentKind(String argStr) { + this.argStr = argStr; + } + } + + public static void main(String... args) throws Exception { + + //create default shared JavaCompiler - reused across multiple compilations + JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null); + + for (BoundKind boundKind : BoundKind.values()) { + for (ConstructorKind constructorKind : ConstructorKind.values()) { + for (TypeArgumentKind declArgKind : TypeArgumentKind.values()) { + for (TypeArgArity arity : TypeArgArity.values()) { + for (TypeArgumentKind useArgKind : TypeArgumentKind.values()) { + for (ArgumentKind argKind : ArgumentKind.values()) { + new GenericConstructorAndDiamondTest(boundKind, constructorKind, + declArgKind, arity, useArgKind, argKind).run(comp, fm); + } + } + } + } + } + } + } + + BoundKind boundKind; + ConstructorKind constructorKind; + TypeArgumentKind declTypeArgumentKind; + TypeArgArity useTypeArgArity; + TypeArgumentKind useTypeArgumentKind; + ArgumentKind argumentKind; + JavaSource source; + DiagnosticChecker diagChecker; + + GenericConstructorAndDiamondTest(BoundKind boundKind, ConstructorKind constructorKind, + TypeArgumentKind declTypeArgumentKind, TypeArgArity useTypeArgArity, + TypeArgumentKind useTypeArgumentKind, ArgumentKind argumentKind) { + this.boundKind = boundKind; + this.constructorKind = constructorKind; + this.declTypeArgumentKind = declTypeArgumentKind; + this.useTypeArgArity = useTypeArgArity; + this.useTypeArgumentKind = useTypeArgumentKind; + this.argumentKind = argumentKind; + this.source = new JavaSource(); + this.diagChecker = new DiagnosticChecker(); + } + + class JavaSource extends SimpleJavaFileObject { + + String template = "class Foo {\n" + + "#CK\n" + + "}\n" + + "class Test {\n" + + "void test() {\n" + + "Foo#TA1 f = new #TA2 Foo<>(#A);\n" + + "}\n" + + "}\n"; + + String source; + + public JavaSource() { + super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE); + source = template.replace("#BK", boundKind.boundStr). + replace("#CK", constructorKind.constrStr) + .replace("#TA1", declTypeArgumentKind.getArgs(TypeArgArity.ONE)) + .replace("#TA2", useTypeArgumentKind.getArgs(useTypeArgArity)) + .replace("#A", argumentKind.argStr); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return source; + } + } + + void run(JavaCompiler tool, StandardJavaFileManager fm) throws Exception { + JavacTask ct = (JavacTask)tool.getTask(null, fm, diagChecker, + null, null, Arrays.asList(source)); + ct.analyze(); + check(); + } + + void check() { + boolean badActual = !constructorKind.matches(argumentKind); + + boolean badArity = constructorKind != ConstructorKind.NON_GENERIC && + useTypeArgumentKind != TypeArgumentKind.NONE && + useTypeArgArity != TypeArgArity.ONE; + + boolean badMethodTypeArg = constructorKind != ConstructorKind.NON_GENERIC && + !useTypeArgumentKind.matches(argumentKind); + + boolean badGenericType = !boundKind.matches(declTypeArgumentKind); + + boolean shouldFail = badActual || badArity || badMethodTypeArg || badGenericType; + + if (shouldFail != diagChecker.errorFound) { + throw new Error("invalid diagnostics for source:\n" + + source.getCharContent(true) + + "\nFound error: " + diagChecker.errorFound + + "\nExpected error: " + shouldFail); + } + } + + static class DiagnosticChecker implements javax.tools.DiagnosticListener { + + boolean errorFound; + + public void report(Diagnostic diagnostic) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + errorFound = true; + } + } + } +} diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg01.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg01.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,17 @@ +/* + * @test /nodynamiccopyright/ + * @bug 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + * check that explicit type-argument that causes resolution failure is rejected + * @compile/fail/ref=Neg01.out -XDrawDiagnostics Neg01.java + */ + +class Neg01 { + + static class Foo { + Foo(T t) {} + } + + Foo fi1 = new Foo<>(1); + Foo fi2 = new Foo(1); +} diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg01.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg01.out Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,3 @@ +Neg01.java:15:24: compiler.err.cant.apply.diamond.1: (compiler.misc.diamond: Neg01.Foo), (compiler.misc.infer.no.conforming.assignment.exists: X, int, java.lang.String) +Neg01.java:16:24: compiler.err.cant.apply.symbol.1: kindname.constructor, Foo, T, int, kindname.class, Neg01.Foo, (compiler.misc.no.conforming.assignment.exists: int, java.lang.String) +2 errors diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg02.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg02.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,17 @@ +/* + * @test /nodynamiccopyright/ + * @bug 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + * check that compiler rejects bad number of explicit type-arguments + * @compile/fail/ref=Neg02.out -XDrawDiagnostics Neg02.java + */ + +class Neg02 { + + static class Foo { + Foo(T t) {} + } + + Foo fi1 = new Foo<>(""); + Foo fi2 = new Foo(""); +} diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg02.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg02.out Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,3 @@ +Neg02.java:15:24: compiler.err.cant.apply.diamond.1: (compiler.misc.diamond: Neg02.Foo), (compiler.misc.arg.length.mismatch) +Neg02.java:16:24: compiler.err.cant.apply.symbol.1: kindname.constructor, Foo, T, java.lang.String, kindname.class, Neg02.Foo, (compiler.misc.arg.length.mismatch) +2 errors diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg03.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg03.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,17 @@ +/* + * @test /nodynamiccopyright/ + * @bug 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + * check that explicit type-argument that does not conform to bound is rejected + * @compile/fail/ref=Neg03.out -XDrawDiagnostics Neg03.java + */ + +class Neg03 { + + static class Foo { + Foo(T t) {} + } + + Foo fi1 = new Foo<>(1); + Foo fi2 = new Foo(1); +} diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Neg03.out --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Neg03.out Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,3 @@ +Neg03.java:15:24: compiler.err.cant.apply.diamond.1: (compiler.misc.diamond: Neg03.Foo), (compiler.misc.explicit.param.do.not.conform.to.bounds: java.lang.String, java.lang.Integer) +Neg03.java:16:24: compiler.err.cant.apply.symbol.1: kindname.constructor, Foo, T, int, kindname.class, Neg03.Foo, (compiler.misc.explicit.param.do.not.conform.to.bounds: java.lang.String, java.lang.Integer) +2 errors diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Pos01.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Pos01.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011, 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 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + * check that redundant type-arguments on non-generic constructor are accepted + * @compile Pos01.java + */ + +class Pos01 { + + static class Foo { + Foo(X t) {} + } + + Foo fi1 = new Foo<>(1); + Foo fi2 = new Foo(1); + Foo fi3 = new Foo<>(1); + Foo fi4 = new Foo(1); + Foo fi5 = new Foo<>(1); + Foo fi6 = new Foo(1); +} diff -r ddec8c712e85 -r f5b5112ee1cc test/tools/javac/generics/diamond/7030150/Pos02.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/generics/diamond/7030150/Pos02.java Tue Mar 29 16:40:51 2011 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011, 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 7030150 + * @summary Type inference for generic instance creation failed for formal type parameter + * check that diamond in return context works w/o problems + * @compile Pos02.java + */ + +class Pos02 { + + Pos02(X x) {} + + + Pos02 test(X x) { + return new Pos02<>(x); + } +}