Tue, 24 Jan 2012 17:52:02 +0000
7129801: Merge the two method applicability routines
Summary: Resolve.java and Infer.java should reuse the same method applicability check routine
Reviewed-by: dlsmith, jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Wed Jan 18 18:26:36 2012 -0800 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Tue Jan 24 17:52:02 2012 +0000 1.3 @@ -34,6 +34,7 @@ 1.4 import com.sun.tools.javac.code.Type.*; 1.5 import com.sun.tools.javac.code.Type.ForAll.ConstraintKind; 1.6 import com.sun.tools.javac.code.Symbol.*; 1.7 +import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; 1.8 import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; 1.9 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 1.10 1.11 @@ -84,7 +85,7 @@ 1.12 1.13 } 1.14 1.15 - public static class InferenceException extends Resolve.InapplicableMethodException { 1.16 + public static class InferenceException extends InapplicableMethodException { 1.17 private static final long serialVersionUID = 0; 1.18 1.19 InferenceException(JCDiagnostic.Factory diags) { 1.20 @@ -287,6 +288,18 @@ 1.21 } 1.22 } 1.23 1.24 + Type asUndetType(Type t, List<Type> undetvars) { 1.25 + return types.subst(t, inferenceVars(undetvars), undetvars); 1.26 + } 1.27 + 1.28 + List<Type> inferenceVars(List<Type> undetvars) { 1.29 + ListBuffer<Type> tvars = ListBuffer.lb(); 1.30 + for (Type uv : undetvars) { 1.31 + tvars.append(((UndetVar)uv).qtype); 1.32 + } 1.33 + return tvars.toList(); 1.34 + } 1.35 + 1.36 /*************************************************************************** 1.37 * Exported Methods 1.38 ***************************************************************************/ 1.39 @@ -372,62 +385,11 @@ 1.40 final Warner warn) throws InferenceException { 1.41 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG 1.42 List<Type> undetvars = Type.map(tvars, fromTypeVarFun); 1.43 - List<Type> formals = mt.argtypes; 1.44 - //need to capture exactly once - otherwise subsequent 1.45 - //applicability checks might fail 1.46 - final List<Type> capturedArgs = types.capture(argtypes); 1.47 - List<Type> actuals = capturedArgs; 1.48 - List<Type> actualsNoCapture = argtypes; 1.49 - // instantiate all polymorphic argument types and 1.50 - // set up lower bounds constraints for undetvars 1.51 - Type varargsFormal = useVarargs ? formals.last() : null; 1.52 - if (varargsFormal == null && 1.53 - actuals.size() != formals.size()) { 1.54 - throw unambiguousNoInstanceException 1.55 - .setMessage("infer.arg.length.mismatch"); 1.56 - } 1.57 - while (actuals.nonEmpty() && formals.head != varargsFormal) { 1.58 - Type formal = formals.head; 1.59 - Type actual = actuals.head.baseType(); 1.60 - Type actualNoCapture = actualsNoCapture.head.baseType(); 1.61 - if (actual.tag == FORALL) 1.62 - actual = instantiateArg((ForAll)actual, formal, tvars, warn); 1.63 - Type undetFormal = types.subst(formal, tvars, undetvars); 1.64 - boolean works = allowBoxing 1.65 - ? types.isConvertible(actual, undetFormal, warn) 1.66 - : types.isSubtypeUnchecked(actual, undetFormal, warn); 1.67 - if (!works) { 1.68 - throw unambiguousNoInstanceException 1.69 - .setMessage("infer.no.conforming.assignment.exists", 1.70 - tvars, actualNoCapture, formal); 1.71 - } 1.72 - formals = formals.tail; 1.73 - actuals = actuals.tail; 1.74 - actualsNoCapture = actualsNoCapture.tail; 1.75 - } 1.76 + //final List<Type> capturedArgs = types.capture(argtypes); 1.77 1.78 - if (formals.head != varargsFormal) // not enough args 1.79 - throw unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch"); 1.80 - 1.81 - // for varargs arguments as well 1.82 - if (useVarargs) { 1.83 - Type elemType = types.elemtype(varargsFormal); 1.84 - Type elemUndet = types.subst(elemType, tvars, undetvars); 1.85 - while (actuals.nonEmpty()) { 1.86 - Type actual = actuals.head.baseType(); 1.87 - Type actualNoCapture = actualsNoCapture.head.baseType(); 1.88 - if (actual.tag == FORALL) 1.89 - actual = instantiateArg((ForAll)actual, elemType, tvars, warn); 1.90 - boolean works = types.isConvertible(actual, elemUndet, warn); 1.91 - if (!works) { 1.92 - throw unambiguousNoInstanceException 1.93 - .setMessage("infer.no.conforming.assignment.exists", 1.94 - tvars, actualNoCapture, elemType); 1.95 - } 1.96 - actuals = actuals.tail; 1.97 - actualsNoCapture = actualsNoCapture.tail; 1.98 - } 1.99 - } 1.100 + final List<Type> capturedArgs = 1.101 + rs.checkRawArgumentsAcceptable(env, undetvars, argtypes, mt.getParameterTypes(), 1.102 + allowBoxing, useVarargs, warn, new InferenceCheckHandler(undetvars)); 1.103 1.104 // minimize as yet undetermined type variables 1.105 for (Type t : undetvars) 1.106 @@ -503,6 +465,31 @@ 1.107 } 1.108 //where 1.109 1.110 + /** inference check handler **/ 1.111 + class InferenceCheckHandler implements Resolve.MethodCheckHandler { 1.112 + 1.113 + List<Type> undetvars; 1.114 + 1.115 + public InferenceCheckHandler(List<Type> undetvars) { 1.116 + this.undetvars = undetvars; 1.117 + } 1.118 + 1.119 + public InapplicableMethodException arityMismatch() { 1.120 + return unambiguousNoInstanceException.setMessage("infer.arg.length.mismatch"); 1.121 + } 1.122 + public InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected) { 1.123 + String key = varargs ? 1.124 + "infer.varargs.argument.mismatch" : 1.125 + "infer.no.conforming.assignment.exists"; 1.126 + return unambiguousNoInstanceException.setMessage(key, 1.127 + inferenceVars(undetvars), found, expected); 1.128 + } 1.129 + public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) { 1.130 + return unambiguousNoInstanceException.setMessage("inaccessible.varargs.type", 1.131 + expected, Kinds.kindName(location), location); 1.132 + } 1.133 + } 1.134 + 1.135 /** 1.136 * A delegated type representing a partially uninferred method type. 1.137 * The return type of a partially uninferred method type is a ForAll 1.138 @@ -572,7 +559,7 @@ 1.139 rs.checkRawArgumentsAcceptable(env, actuals, formals, 1.140 allowBoxing, useVarargs, warn); 1.141 } 1.142 - catch (Resolve.InapplicableMethodException ex) { 1.143 + catch (InapplicableMethodException ex) { 1.144 // inferred method is not applicable 1.145 throw invalidInstanceException.setMessage(ex.getDiagnostic()); 1.146 }
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Wed Jan 18 18:26:36 2012 -0800 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Jan 24 17:52:02 2012 +0000 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -474,52 +474,126 @@ 2.11 return false; 2.12 } 2.13 } 2.14 + /** 2.15 + * A check handler is used by the main method applicability routine in order 2.16 + * to handle specific method applicability failures. It is assumed that a class 2.17 + * implementing this interface should throw exceptions that are a subtype of 2.18 + * InapplicableMethodException (see below). Such exception will terminate the 2.19 + * method applicability check and propagate important info outwards (for the 2.20 + * purpose of generating better diagnostics). 2.21 + */ 2.22 + interface MethodCheckHandler { 2.23 + /* The number of actuals and formals differ */ 2.24 + InapplicableMethodException arityMismatch(); 2.25 + /* An actual argument type does not conform to the corresponding formal type */ 2.26 + InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected); 2.27 + /* The element type of a varargs is not accessible in the current context */ 2.28 + InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected); 2.29 + } 2.30 + 2.31 + /** 2.32 + * Basic method check handler used within Resolve - all methods end up 2.33 + * throwing InapplicableMethodException; a diagnostic fragment that describes 2.34 + * the cause as to why the method is not applicable is set on the exception 2.35 + * before it is thrown. 2.36 + */ 2.37 + MethodCheckHandler resolveHandler = new MethodCheckHandler() { 2.38 + public InapplicableMethodException arityMismatch() { 2.39 + return inapplicableMethodException.setMessage("arg.length.mismatch"); 2.40 + } 2.41 + public InapplicableMethodException argumentMismatch(boolean varargs, Type found, Type expected) { 2.42 + String key = varargs ? 2.43 + "varargs.argument.mismatch" : 2.44 + "no.conforming.assignment.exists"; 2.45 + return inapplicableMethodException.setMessage(key, 2.46 + found, expected); 2.47 + } 2.48 + public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) { 2.49 + return inapplicableMethodException.setMessage("inaccessible.varargs.type", 2.50 + expected, Kinds.kindName(location), location); 2.51 + } 2.52 + }; 2.53 + 2.54 void checkRawArgumentsAcceptable(Env<AttrContext> env, 2.55 List<Type> argtypes, 2.56 List<Type> formals, 2.57 boolean allowBoxing, 2.58 boolean useVarargs, 2.59 Warner warn) { 2.60 + checkRawArgumentsAcceptable(env, List.<Type>nil(), argtypes, formals, 2.61 + allowBoxing, useVarargs, warn, resolveHandler); 2.62 + } 2.63 + 2.64 + /** 2.65 + * Main method applicability routine. Given a list of actual types A, 2.66 + * a list of formal types F, determines whether the types in A are 2.67 + * compatible (by method invocation conversion) with the types in F. 2.68 + * 2.69 + * Since this routine is shared between overload resolution and method 2.70 + * type-inference, it is crucial that actual types are converted to the 2.71 + * corresponding 'undet' form (i.e. where inference variables are replaced 2.72 + * with undetvars) so that constraints can be propagated and collected. 2.73 + * 2.74 + * Moreover, if one or more types in A is a poly type, this routine calls 2.75 + * Infer.instantiateArg in order to complete the poly type (this might involve 2.76 + * deferred attribution). 2.77 + * 2.78 + * A method check handler (see above) is used in order to report errors. 2.79 + */ 2.80 + List<Type> checkRawArgumentsAcceptable(Env<AttrContext> env, 2.81 + List<Type> undetvars, 2.82 + List<Type> argtypes, 2.83 + List<Type> formals, 2.84 + boolean allowBoxing, 2.85 + boolean useVarargs, 2.86 + Warner warn, 2.87 + MethodCheckHandler handler) { 2.88 Type varargsFormal = useVarargs ? formals.last() : null; 2.89 + ListBuffer<Type> checkedArgs = ListBuffer.lb(); 2.90 + 2.91 if (varargsFormal == null && 2.92 argtypes.size() != formals.size()) { 2.93 - throw inapplicableMethodException.setMessage("arg.length.mismatch"); // not enough args 2.94 + throw handler.arityMismatch(); // not enough args 2.95 } 2.96 2.97 while (argtypes.nonEmpty() && formals.head != varargsFormal) { 2.98 - boolean works = allowBoxing 2.99 - ? types.isConvertible(argtypes.head, formals.head, warn) 2.100 - : types.isSubtypeUnchecked(argtypes.head, formals.head, warn); 2.101 - if (!works) 2.102 - throw inapplicableMethodException.setMessage("no.conforming.assignment.exists", 2.103 - argtypes.head, 2.104 - formals.head); 2.105 + Type undetFormal = infer.asUndetType(formals.head, undetvars); 2.106 + Type capturedActual = types.capture(argtypes.head); 2.107 + boolean works = allowBoxing ? 2.108 + types.isConvertible(capturedActual, undetFormal, warn) : 2.109 + types.isSubtypeUnchecked(capturedActual, undetFormal, warn); 2.110 + if (!works) { 2.111 + throw handler.argumentMismatch(false, argtypes.head, formals.head); 2.112 + } 2.113 + checkedArgs.append(capturedActual); 2.114 argtypes = argtypes.tail; 2.115 formals = formals.tail; 2.116 } 2.117 2.118 - if (formals.head != varargsFormal) 2.119 - throw inapplicableMethodException.setMessage("arg.length.mismatch"); // not enough args 2.120 + if (formals.head != varargsFormal) { 2.121 + throw handler.arityMismatch(); // not enough args 2.122 + } 2.123 2.124 if (useVarargs) { 2.125 + //note: if applicability check is triggered by most specific test, 2.126 + //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5) 2.127 Type elt = types.elemtype(varargsFormal); 2.128 + Type eltUndet = infer.asUndetType(elt, undetvars); 2.129 while (argtypes.nonEmpty()) { 2.130 - if (!types.isConvertible(argtypes.head, elt, warn)) 2.131 - throw inapplicableMethodException.setMessage("varargs.argument.mismatch", 2.132 - argtypes.head, 2.133 - elt); 2.134 + Type capturedActual = types.capture(argtypes.head); 2.135 + if (!types.isConvertible(capturedActual, eltUndet, warn)) { 2.136 + throw handler.argumentMismatch(true, argtypes.head, elt); 2.137 + } 2.138 + checkedArgs.append(capturedActual); 2.139 argtypes = argtypes.tail; 2.140 } 2.141 //check varargs element type accessibility 2.142 - if (!isAccessible(env, elt)) { 2.143 + if (undetvars.isEmpty() && !isAccessible(env, elt)) { 2.144 Symbol location = env.enclClass.sym; 2.145 - throw inapplicableMethodException.setMessage("inaccessible.varargs.type", 2.146 - elt, 2.147 - Kinds.kindName(location), 2.148 - location); 2.149 + throw handler.inaccessibleVarargs(location, elt); 2.150 } 2.151 } 2.152 - return; 2.153 + return checkedArgs.toList(); 2.154 } 2.155 // where 2.156 public static class InapplicableMethodException extends RuntimeException {
3.1 --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties Wed Jan 18 18:26:36 2012 -0800 3.2 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties Tue Jan 24 17:52:02 2012 +0000 3.3 @@ -1,5 +1,5 @@ 3.4 # 3.5 -# Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. 3.6 +# Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. 3.7 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 # 3.9 # This code is free software; you can redistribute it and/or modify it 3.10 @@ -1620,6 +1620,10 @@ 3.11 compiler.misc.infer.arg.length.mismatch=\ 3.12 cannot instantiate from arguments because actual and formal argument lists differ in length 3.13 3.14 +# 0: list of type, 1: type, 2: type 3.15 +compiler.misc.infer.varargs.argument.mismatch=\ 3.16 + no instance(s) of type variable(s) {0} exist so that argument type {1} conforms to vararg element type {2} 3.17 + 3.18 # 0: type, 1: list of type 3.19 compiler.misc.inferred.do.not.conform.to.bounds=\ 3.20 inferred type does not conform to declared bound(s)\n\
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/test/tools/javac/diags/examples/InferVarargsArgumentMismatch.java Tue Jan 24 17:52:02 2012 +0000 4.3 @@ -0,0 +1,30 @@ 4.4 +/* 4.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + */ 4.26 + 4.27 +// key: compiler.err.cant.apply.symbol.1 4.28 +// key: compiler.misc.infer.varargs.argument.mismatch 4.29 + 4.30 +class InferVarargsArgumentMismatch { 4.31 + <X> void m(X x1, String... xs) {} 4.32 + { this.m("", 1); } 4.33 +}