test/tools/javac/varargs/6199075/T6199075.java

Fri, 10 Dec 2010 15:24:17 +0000

author
mcimadamore
date
Fri, 10 Dec 2010 15:24:17 +0000
changeset 787
b1c98bfd4709
child 892
3e30c95da3c6
permissions
-rw-r--r--

6199075: Unambiguous varargs method calls flagged as ambiguous
Summary: javac does not implement overload resolution w.r.t. varargs methods as described in the JLS
Reviewed-by: jjg

mcimadamore@787 1 /*
mcimadamore@787 2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
mcimadamore@787 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
mcimadamore@787 4 *
mcimadamore@787 5 * This code is free software; you can redistribute it and/or modify it
mcimadamore@787 6 * under the terms of the GNU General Public License version 2 only, as
mcimadamore@787 7 * published by the Free Software Foundation.
mcimadamore@787 8 *
mcimadamore@787 9 * This code is distributed in the hope that it will be useful, but WITHOUT
mcimadamore@787 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
mcimadamore@787 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
mcimadamore@787 12 * version 2 for more details (a copy is included in the LICENSE file that
mcimadamore@787 13 * accompanied this code).
mcimadamore@787 14 *
mcimadamore@787 15 * You should have received a copy of the GNU General Public License version
mcimadamore@787 16 * 2 along with this work; if not, write to the Free Software Foundation,
mcimadamore@787 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
mcimadamore@787 18 *
mcimadamore@787 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
mcimadamore@787 20 * or visit www.oracle.com if you need additional information or have any
mcimadamore@787 21 * questions.
mcimadamore@787 22 */
mcimadamore@787 23
mcimadamore@787 24 /*
mcimadamore@787 25 * @test
mcimadamore@787 26 * @bug 6199075
mcimadamore@787 27 *
mcimadamore@787 28 * @summary Unambiguous varargs method calls flagged as ambiguous
mcimadamore@787 29 * @author mcimadamore
mcimadamore@787 30 *
mcimadamore@787 31 */
mcimadamore@787 32
mcimadamore@787 33 import com.sun.source.util.JavacTask;
mcimadamore@787 34 import com.sun.tools.classfile.Instruction;
mcimadamore@787 35 import com.sun.tools.classfile.Attribute;
mcimadamore@787 36 import com.sun.tools.classfile.ClassFile;
mcimadamore@787 37 import com.sun.tools.classfile.Code_attribute;
mcimadamore@787 38 import com.sun.tools.classfile.ConstantPool.*;
mcimadamore@787 39 import com.sun.tools.classfile.Method;
mcimadamore@787 40 import com.sun.tools.javac.util.List;
mcimadamore@787 41
mcimadamore@787 42 import java.io.File;
mcimadamore@787 43 import java.net.URI;
mcimadamore@787 44 import java.util.Arrays;
mcimadamore@787 45 import java.util.Locale;
mcimadamore@787 46 import javax.tools.Diagnostic;
mcimadamore@787 47 import javax.tools.JavaCompiler;
mcimadamore@787 48 import javax.tools.JavaFileObject;
mcimadamore@787 49 import javax.tools.SimpleJavaFileObject;
mcimadamore@787 50 import javax.tools.ToolProvider;
mcimadamore@787 51
mcimadamore@787 52 public class T6199075 {
mcimadamore@787 53
mcimadamore@787 54 int checkCount = 0;
mcimadamore@787 55 int bytecodeCheckCount = 0;
mcimadamore@787 56
mcimadamore@787 57 enum TypeKind {
mcimadamore@787 58 BYTE("byte", "(byte)1", "[B", 0),
mcimadamore@787 59 CHAR("char", "'c'", "[C", 1),
mcimadamore@787 60 SHORT("short", "(short)1", "[S", 2),
mcimadamore@787 61 INT("int", "1", "[I", 3),
mcimadamore@787 62 LONG("long", "1L", "[J", 4),
mcimadamore@787 63 FLOAT("float", "1.0F", "[F", 5),
mcimadamore@787 64 DOUBLE("double", "1.0D", "[D", 6),
mcimadamore@787 65 BOOLEAN("boolean", "true", "[Z", -1);
mcimadamore@787 66
mcimadamore@787 67 String typeString;
mcimadamore@787 68 String valueString;
mcimadamore@787 69 String bytecodeString;
mcimadamore@787 70 private int subtypeTag;
mcimadamore@787 71
mcimadamore@787 72 TypeKind(String typeString, String valueString, String bytecodeString, int subtypeTag) {
mcimadamore@787 73 this.typeString = typeString;
mcimadamore@787 74 this.valueString = valueString;
mcimadamore@787 75 this.bytecodeString = bytecodeString;
mcimadamore@787 76 this.subtypeTag = subtypeTag;
mcimadamore@787 77 }
mcimadamore@787 78
mcimadamore@787 79 boolean isSubtypeOf(TypeKind that) {
mcimadamore@787 80 switch (this) {
mcimadamore@787 81 case BOOLEAN:
mcimadamore@787 82 return that == BOOLEAN;
mcimadamore@787 83 case BYTE:
mcimadamore@787 84 case CHAR:
mcimadamore@787 85 return this.subtypeTag == that.subtypeTag ||
mcimadamore@787 86 this.subtypeTag + 2 <= that.subtypeTag;
mcimadamore@787 87 default:
mcimadamore@787 88 return this.subtypeTag <= that.subtypeTag;
mcimadamore@787 89 }
mcimadamore@787 90 }
mcimadamore@787 91 }
mcimadamore@787 92
mcimadamore@787 93 enum ArgumentsArity {
mcimadamore@787 94 ZERO(0),
mcimadamore@787 95 ONE(1),
mcimadamore@787 96 TWO(2),
mcimadamore@787 97 THREE(3);
mcimadamore@787 98
mcimadamore@787 99 int arity;
mcimadamore@787 100
mcimadamore@787 101 ArgumentsArity(int arity) {
mcimadamore@787 102 this.arity = arity;
mcimadamore@787 103 }
mcimadamore@787 104
mcimadamore@787 105 String asExpressionList(TypeKind type) {
mcimadamore@787 106 StringBuilder buf = new StringBuilder();
mcimadamore@787 107 String sep = "";
mcimadamore@787 108 for (int i = 0; i < arity; i++) {
mcimadamore@787 109 buf.append(sep);
mcimadamore@787 110 buf.append(type.valueString);
mcimadamore@787 111 sep = ",";
mcimadamore@787 112 }
mcimadamore@787 113 return buf.toString();
mcimadamore@787 114 }
mcimadamore@787 115 }
mcimadamore@787 116
mcimadamore@787 117 static class VarargsMethod {
mcimadamore@787 118 TypeKind varargsElement;
mcimadamore@787 119
mcimadamore@787 120 VarargsMethod(TypeKind varargsElement) {
mcimadamore@787 121 this.varargsElement = varargsElement;
mcimadamore@787 122 }
mcimadamore@787 123
mcimadamore@787 124 @Override
mcimadamore@787 125 public String toString() {
mcimadamore@787 126 return "void m("+ varargsElement.typeString+ "... args) {}";
mcimadamore@787 127 }
mcimadamore@787 128
mcimadamore@787 129 boolean isApplicable(TypeKind actual, ArgumentsArity argsArity) {
mcimadamore@787 130 return argsArity == ArgumentsArity.ZERO ||
mcimadamore@787 131 actual.isSubtypeOf(varargsElement);
mcimadamore@787 132 }
mcimadamore@787 133
mcimadamore@787 134 boolean isMoreSpecificThan(VarargsMethod that) {
mcimadamore@787 135 return varargsElement.isSubtypeOf(that.varargsElement);
mcimadamore@787 136 }
mcimadamore@787 137 }
mcimadamore@787 138
mcimadamore@787 139 public static void main(String... args) throws Exception {
mcimadamore@787 140 new T6199075().test();
mcimadamore@787 141 }
mcimadamore@787 142
mcimadamore@787 143 void test() throws Exception {
mcimadamore@787 144 for (TypeKind formal1 : TypeKind.values()) {
mcimadamore@787 145 VarargsMethod m1 = new VarargsMethod(formal1);
mcimadamore@787 146 for (TypeKind formal2 : TypeKind.values()) {
mcimadamore@787 147 VarargsMethod m2 = new VarargsMethod(formal2);
mcimadamore@787 148 for (TypeKind actual : TypeKind.values()) {
mcimadamore@787 149 for (ArgumentsArity argsArity : ArgumentsArity.values()) {
mcimadamore@787 150 compileAndCheck(m1, m2, actual, argsArity);
mcimadamore@787 151 }
mcimadamore@787 152 }
mcimadamore@787 153 }
mcimadamore@787 154 }
mcimadamore@787 155
mcimadamore@787 156 System.out.println("Total checks made: " + checkCount);
mcimadamore@787 157 System.out.println("Bytecode checks made: " + bytecodeCheckCount);
mcimadamore@787 158 }
mcimadamore@787 159
mcimadamore@787 160 void compileAndCheck(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) throws Exception {
mcimadamore@787 161 final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
mcimadamore@787 162 JavaSource source = new JavaSource(m1, m2, actual, argsArity);
mcimadamore@787 163 ErrorChecker ec = new ErrorChecker();
mcimadamore@787 164 JavacTask ct = (JavacTask)tool.getTask(null, null, ec,
mcimadamore@787 165 null, null, Arrays.asList(source));
mcimadamore@787 166 ct.generate();
mcimadamore@787 167 check(source, ec, m1, m2, actual, argsArity);
mcimadamore@787 168 }
mcimadamore@787 169
mcimadamore@787 170 void check(JavaSource source, ErrorChecker ec, VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) {
mcimadamore@787 171 checkCount++;
mcimadamore@787 172 boolean resolutionError = false;
mcimadamore@787 173 VarargsMethod selectedMethod = null;
mcimadamore@787 174
mcimadamore@787 175 boolean m1_applicable = m1.isApplicable(actual, argsArity);
mcimadamore@787 176 boolean m2_applicable = m2.isApplicable(actual, argsArity);
mcimadamore@787 177
mcimadamore@787 178 if (!m1_applicable && !m2_applicable) {
mcimadamore@787 179 resolutionError = true;
mcimadamore@787 180 } else if (m1_applicable && m2_applicable) {
mcimadamore@787 181 //most specific
mcimadamore@787 182 boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
mcimadamore@787 183 boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
mcimadamore@787 184 resolutionError = m1_moreSpecific == m2_moreSpecific;
mcimadamore@787 185 selectedMethod = m1_moreSpecific ? m1 : m2;
mcimadamore@787 186 } else {
mcimadamore@787 187 selectedMethod = m1_applicable ?
mcimadamore@787 188 m1 : m2;
mcimadamore@787 189 }
mcimadamore@787 190
mcimadamore@787 191 if (ec.errorFound != resolutionError) {
mcimadamore@787 192 throw new Error("invalid diagnostics for source:\n" +
mcimadamore@787 193 source.getCharContent(true) +
mcimadamore@787 194 "\nExpected resolution error: " + resolutionError +
mcimadamore@787 195 "\nFound error: " + ec.errorFound +
mcimadamore@787 196 "\nCompiler diagnostics:\n" + ec.printDiags());
mcimadamore@787 197 } else if (!resolutionError) {
mcimadamore@787 198 verifyBytecode(selectedMethod);
mcimadamore@787 199 }
mcimadamore@787 200 }
mcimadamore@787 201
mcimadamore@787 202 void verifyBytecode(VarargsMethod selected) {
mcimadamore@787 203 bytecodeCheckCount++;
mcimadamore@787 204 File compiledTest = new File("Test.class");
mcimadamore@787 205 try {
mcimadamore@787 206 ClassFile cf = ClassFile.read(compiledTest);
mcimadamore@787 207 Method testMethod = null;
mcimadamore@787 208 for (Method m : cf.methods) {
mcimadamore@787 209 if (m.getName(cf.constant_pool).equals("test")) {
mcimadamore@787 210 testMethod = m;
mcimadamore@787 211 break;
mcimadamore@787 212 }
mcimadamore@787 213 }
mcimadamore@787 214 if (testMethod == null) {
mcimadamore@787 215 throw new Error("Test method not found");
mcimadamore@787 216 }
mcimadamore@787 217 Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
mcimadamore@787 218 if (testMethod == null) {
mcimadamore@787 219 throw new Error("Code attribute for test() method not found");
mcimadamore@787 220 }
mcimadamore@787 221
mcimadamore@787 222 for (Instruction i : ea.getInstructions()) {
mcimadamore@787 223 if (i.getMnemonic().equals("invokevirtual")) {
mcimadamore@787 224 int cp_entry = i.getUnsignedShort(1);
mcimadamore@787 225 CONSTANT_Methodref_info methRef =
mcimadamore@787 226 (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
mcimadamore@787 227 String type = methRef.getNameAndTypeInfo().getType();
mcimadamore@787 228 if (!type.contains(selected.varargsElement.bytecodeString)) {
mcimadamore@787 229 throw new Error("Unexpected type method call: " + type);
mcimadamore@787 230 }
mcimadamore@787 231 break;
mcimadamore@787 232 }
mcimadamore@787 233 }
mcimadamore@787 234 } catch (Exception e) {
mcimadamore@787 235 e.printStackTrace();
mcimadamore@787 236 throw new Error("error reading " + compiledTest +": " + e);
mcimadamore@787 237 }
mcimadamore@787 238 }
mcimadamore@787 239
mcimadamore@787 240 static class JavaSource extends SimpleJavaFileObject {
mcimadamore@787 241
mcimadamore@787 242 static final String source_template = "class Test {\n" +
mcimadamore@787 243 " #V1\n" +
mcimadamore@787 244 " #V2\n" +
mcimadamore@787 245 " void test() { m(#E); }\n" +
mcimadamore@787 246 "}";
mcimadamore@787 247
mcimadamore@787 248 String source;
mcimadamore@787 249
mcimadamore@787 250 public JavaSource(VarargsMethod m1, VarargsMethod m2, TypeKind actual, ArgumentsArity argsArity) {
mcimadamore@787 251 super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
mcimadamore@787 252 source = source_template.replaceAll("#V1", m1.toString()).
mcimadamore@787 253 replaceAll("#V2", m2.toString()).
mcimadamore@787 254 replaceAll("#E", argsArity.asExpressionList(actual));
mcimadamore@787 255 }
mcimadamore@787 256
mcimadamore@787 257 @Override
mcimadamore@787 258 public CharSequence getCharContent(boolean ignoreEncodingErrors) {
mcimadamore@787 259 return source;
mcimadamore@787 260 }
mcimadamore@787 261 }
mcimadamore@787 262
mcimadamore@787 263 static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
mcimadamore@787 264
mcimadamore@787 265 boolean errorFound;
mcimadamore@787 266 List<String> errDiags = List.nil();
mcimadamore@787 267
mcimadamore@787 268 public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
mcimadamore@787 269 if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
mcimadamore@787 270 errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault()));
mcimadamore@787 271 errorFound = true;
mcimadamore@787 272 }
mcimadamore@787 273 }
mcimadamore@787 274
mcimadamore@787 275 String printDiags() {
mcimadamore@787 276 StringBuilder buf = new StringBuilder();
mcimadamore@787 277 for (String s : errDiags) {
mcimadamore@787 278 buf.append(s);
mcimadamore@787 279 buf.append("\n");
mcimadamore@787 280 }
mcimadamore@787 281 return buf.toString();
mcimadamore@787 282 }
mcimadamore@787 283 }
mcimadamore@787 284 }

mercurial