7042566: Regression: new ambiguity between varargs method

Wed, 11 May 2011 13:10:57 +0200

author
mcimadamore
date
Wed, 11 May 2011 13:10:57 +0200
changeset 1006
a2d422d480cb
parent 1005
68fde7f5863b
child 1007
95fc7fd39be2

7042566: Regression: new ambiguity between varargs method
Summary: Erroneous ambiguity error when choosing most specific varargs method
Reviewed-by: jjg

src/share/classes/com/sun/tools/javac/comp/Infer.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Resolve.java file | annotate | diff | comparison | revisions
test/tools/javac/varargs/7042566/T7042566.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java	Tue May 10 19:53:49 2011 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java	Wed May 11 13:10:57 2011 +0200
     1.3 @@ -407,9 +407,7 @@
     1.4  
     1.5          // for varargs arguments as well
     1.6          if (useVarargs) {
     1.7 -            //note: if applicability check is triggered by most specific test,
     1.8 -            //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
     1.9 -            Type elemType = types.elemtypeOrType(varargsFormal);
    1.10 +            Type elemType = types.elemtype(varargsFormal);
    1.11              Type elemUndet = types.subst(elemType, tvars, undetvars);
    1.12              while (actuals.nonEmpty()) {
    1.13                  Type actual = actuals.head.baseType();
     2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Tue May 10 19:53:49 2011 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Wed May 11 13:10:57 2011 +0200
     2.3 @@ -458,9 +458,7 @@
     2.4              throw inapplicableMethodException.setMessage("arg.length.mismatch"); // not enough args
     2.5  
     2.6          if (useVarargs) {
     2.7 -            //note: if applicability check is triggered by most specific test,
     2.8 -            //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
     2.9 -            Type elt = types.elemtypeOrType(varargsFormal);
    2.10 +            Type elt = types.elemtype(varargsFormal);
    2.11              while (argtypes.nonEmpty()) {
    2.12                  if (!types.isConvertible(argtypes.head, elt, warn))
    2.13                      throw inapplicableMethodException.setMessage("varargs.argument.mismatch",
    2.14 @@ -819,10 +817,10 @@
    2.15      private boolean signatureMoreSpecific(Env<AttrContext> env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
    2.16          noteWarner.clear();
    2.17          Type mtype1 = types.memberType(site, adjustVarargs(m1, m2, useVarargs));
    2.18 -        return (instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
    2.19 -                             allowBoxing, false, noteWarner) != null ||
    2.20 -                 useVarargs && instantiate(env, site, adjustVarargs(m2, m1, useVarargs), types.lowerBoundArgtypes(mtype1), null,
    2.21 -                                           allowBoxing, true, noteWarner) != null) &&
    2.22 +        Type mtype2 = instantiate(env, site, adjustVarargs(m2, m1, useVarargs),
    2.23 +                types.lowerBoundArgtypes(mtype1), null,
    2.24 +                allowBoxing, false, noteWarner);
    2.25 +        return mtype2 != null &&
    2.26                  !noteWarner.hasLint(Lint.LintCategory.UNCHECKED);
    2.27      }
    2.28      //where
    2.29 @@ -855,7 +853,7 @@
    2.30              //append varargs element type as last synthetic formal
    2.31              args.append(types.elemtype(varargsTypeTo));
    2.32              Type mtype = types.createMethodTypeWithParameters(to.type, args.toList());
    2.33 -            return new MethodSymbol(to.flags_field, to.name, mtype, to.owner);
    2.34 +            return new MethodSymbol(to.flags_field & ~VARARGS, to.name, mtype, to.owner);
    2.35          } else {
    2.36              return to;
    2.37          }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/tools/javac/varargs/7042566/T7042566.java	Wed May 11 13:10:57 2011 +0200
     3.3 @@ -0,0 +1,350 @@
     3.4 +/*
     3.5 + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.7 + *
     3.8 + * This code is free software; you can redistribute it and/or modify it
     3.9 + * under the terms of the GNU General Public License version 2 only, as
    3.10 + * published by the Free Software Foundation.
    3.11 + *
    3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    3.15 + * version 2 for more details (a copy is included in the LICENSE file that
    3.16 + * accompanied this code).
    3.17 + *
    3.18 + * You should have received a copy of the GNU General Public License version
    3.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    3.21 + *
    3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    3.23 + * or visit www.oracle.com if you need additional information or have any
    3.24 + * questions.
    3.25 + */
    3.26 +
    3.27 +/*
    3.28 + * @test
    3.29 + * @bug 7042566
    3.30 + * @summary Unambiguous varargs method calls flagged as ambiguous
    3.31 + */
    3.32 +
    3.33 +import com.sun.source.util.JavacTask;
    3.34 +import com.sun.tools.classfile.Instruction;
    3.35 +import com.sun.tools.classfile.Attribute;
    3.36 +import com.sun.tools.classfile.ClassFile;
    3.37 +import com.sun.tools.classfile.Code_attribute;
    3.38 +import com.sun.tools.classfile.ConstantPool.*;
    3.39 +import com.sun.tools.classfile.Method;
    3.40 +import com.sun.tools.javac.api.JavacTool;
    3.41 +import com.sun.tools.javac.util.List;
    3.42 +
    3.43 +import java.io.File;
    3.44 +import java.net.URI;
    3.45 +import java.util.Arrays;
    3.46 +import java.util.Locale;
    3.47 +import javax.tools.Diagnostic;
    3.48 +import javax.tools.JavaCompiler;
    3.49 +import javax.tools.JavaFileObject;
    3.50 +import javax.tools.SimpleJavaFileObject;
    3.51 +import javax.tools.StandardJavaFileManager;
    3.52 +import javax.tools.ToolProvider;
    3.53 +
    3.54 +public class T7042566 {
    3.55 +
    3.56 +    VarargsMethod m1;
    3.57 +    VarargsMethod m2;
    3.58 +    TypeConfiguration actuals;
    3.59 +
    3.60 +    T7042566(TypeConfiguration m1_conf, TypeConfiguration m2_conf, TypeConfiguration actuals) {
    3.61 +        this.m1 = new VarargsMethod(m1_conf);
    3.62 +        this.m2 = new VarargsMethod(m2_conf);
    3.63 +        this.actuals = actuals;
    3.64 +    }
    3.65 +
    3.66 +    void compileAndCheck() throws Exception {
    3.67 +        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
    3.68 +        JavaSource source = new JavaSource();
    3.69 +        ErrorChecker ec = new ErrorChecker();
    3.70 +        JavacTask ct = (JavacTask)tool.getTask(null, fm, ec,
    3.71 +                null, null, Arrays.asList(source));
    3.72 +        ct.call();
    3.73 +        check(source, ec);
    3.74 +    }
    3.75 +
    3.76 +    void check(JavaSource source, ErrorChecker ec) {
    3.77 +        checkCount++;
    3.78 +        boolean resolutionError = false;
    3.79 +        VarargsMethod selectedMethod = null;
    3.80 +
    3.81 +        boolean m1_applicable = m1.isApplicable(actuals);
    3.82 +        boolean m2_applicable = m2.isApplicable(actuals);
    3.83 +
    3.84 +        if (!m1_applicable && !m2_applicable) {
    3.85 +            resolutionError = true;
    3.86 +        } else if (m1_applicable && m2_applicable) {
    3.87 +            //most specific
    3.88 +            boolean m1_moreSpecific = m1.isMoreSpecificThan(m2);
    3.89 +            boolean m2_moreSpecific = m2.isMoreSpecificThan(m1);
    3.90 +
    3.91 +            resolutionError = m1_moreSpecific == m2_moreSpecific;
    3.92 +            selectedMethod = m1_moreSpecific ? m1 : m2;
    3.93 +        } else {
    3.94 +            selectedMethod = m1_applicable ?
    3.95 +                m1 : m2;
    3.96 +        }
    3.97 +
    3.98 +        if (ec.errorFound != resolutionError) {
    3.99 +            throw new Error("invalid diagnostics for source:\n" +
   3.100 +                    source.getCharContent(true) +
   3.101 +                    "\nExpected resolution error: " + resolutionError +
   3.102 +                    "\nFound error: " + ec.errorFound +
   3.103 +                    "\nCompiler diagnostics:\n" + ec.printDiags());
   3.104 +        } else if (!resolutionError) {
   3.105 +            verifyBytecode(selectedMethod, source);
   3.106 +        }
   3.107 +    }
   3.108 +
   3.109 +    void verifyBytecode(VarargsMethod selected, JavaSource source) {
   3.110 +        bytecodeCheckCount++;
   3.111 +        File compiledTest = new File("Test.class");
   3.112 +        try {
   3.113 +            ClassFile cf = ClassFile.read(compiledTest);
   3.114 +            Method testMethod = null;
   3.115 +            for (Method m : cf.methods) {
   3.116 +                if (m.getName(cf.constant_pool).equals("test")) {
   3.117 +                    testMethod = m;
   3.118 +                    break;
   3.119 +                }
   3.120 +            }
   3.121 +            if (testMethod == null) {
   3.122 +                throw new Error("Test method not found");
   3.123 +            }
   3.124 +            Code_attribute ea = (Code_attribute)testMethod.attributes.get(Attribute.Code);
   3.125 +            if (testMethod == null) {
   3.126 +                throw new Error("Code attribute for test() method not found");
   3.127 +            }
   3.128 +
   3.129 +            for (Instruction i : ea.getInstructions()) {
   3.130 +                if (i.getMnemonic().equals("invokevirtual")) {
   3.131 +                    int cp_entry = i.getUnsignedShort(1);
   3.132 +                    CONSTANT_Methodref_info methRef =
   3.133 +                            (CONSTANT_Methodref_info)cf.constant_pool.get(cp_entry);
   3.134 +                    String type = methRef.getNameAndTypeInfo().getType();
   3.135 +                    String sig = selected.parameterTypes.bytecodeSigStr;
   3.136 +                    if (!type.contains(sig)) {
   3.137 +                        throw new Error("Unexpected type method call: " + type + "" +
   3.138 +                                        "\nfound: " + sig +
   3.139 +                                        "\n" + source.getCharContent(true));
   3.140 +                    }
   3.141 +                    break;
   3.142 +                }
   3.143 +            }
   3.144 +        } catch (Exception e) {
   3.145 +            e.printStackTrace();
   3.146 +            throw new Error("error reading " + compiledTest +": " + e);
   3.147 +        }
   3.148 +    }
   3.149 +
   3.150 +    class JavaSource extends SimpleJavaFileObject {
   3.151 +
   3.152 +        static final String source_template = "class Test {\n" +
   3.153 +                "   #V1\n" +
   3.154 +                "   #V2\n" +
   3.155 +                "   void test() { m(#E); }\n" +
   3.156 +                "}";
   3.157 +
   3.158 +        String source;
   3.159 +
   3.160 +        public JavaSource() {
   3.161 +            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   3.162 +            source = source_template.replaceAll("#V1", m1.toString()).
   3.163 +                    replaceAll("#V2", m2.toString()).
   3.164 +                    replaceAll("#E", actuals.expressionListStr);
   3.165 +        }
   3.166 +
   3.167 +        @Override
   3.168 +        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   3.169 +            return source;
   3.170 +        }
   3.171 +    }
   3.172 +
   3.173 +    /** global decls ***/
   3.174 +
   3.175 +    // Create a single file manager and reuse it for each compile to save time.
   3.176 +    static StandardJavaFileManager fm = JavacTool.create().getStandardFileManager(null, null, null);
   3.177 +
   3.178 +    //statistics
   3.179 +    static int checkCount = 0;
   3.180 +    static int bytecodeCheckCount = 0;
   3.181 +
   3.182 +    public static void main(String... args) throws Exception {
   3.183 +        for (TypeConfiguration tconf1 : TypeConfiguration.values()) {
   3.184 +            for (TypeConfiguration tconf2 : TypeConfiguration.values()) {
   3.185 +                for (TypeConfiguration tconf3 : TypeConfiguration.values()) {
   3.186 +                    new T7042566(tconf1, tconf2, tconf3).compileAndCheck();
   3.187 +                }
   3.188 +            }
   3.189 +        }
   3.190 +
   3.191 +        System.out.println("Total checks made: " + checkCount);
   3.192 +        System.out.println("Bytecode checks made: " + bytecodeCheckCount);
   3.193 +    }
   3.194 +
   3.195 +    enum TypeKind {
   3.196 +        OBJECT("Object", "(Object)null", "Ljava/lang/Object;"),
   3.197 +        STRING("String", "(String)null", "Ljava/lang/String;");
   3.198 +
   3.199 +        String typeString;
   3.200 +        String valueString;
   3.201 +        String bytecodeString;
   3.202 +
   3.203 +        TypeKind(String typeString, String valueString, String bytecodeString) {
   3.204 +            this.typeString = typeString;
   3.205 +            this.valueString = valueString;
   3.206 +            this.bytecodeString = bytecodeString;
   3.207 +        }
   3.208 +
   3.209 +        boolean isSubtypeOf(TypeKind that) {
   3.210 +            return that == OBJECT ||
   3.211 +                    (that == STRING && this == STRING);
   3.212 +        }
   3.213 +    }
   3.214 +
   3.215 +    enum TypeConfiguration {
   3.216 +        A(TypeKind.OBJECT),
   3.217 +        B(TypeKind.STRING),
   3.218 +        AA(TypeKind.OBJECT, TypeKind.OBJECT),
   3.219 +        AB(TypeKind.OBJECT, TypeKind.STRING),
   3.220 +        BA(TypeKind.STRING, TypeKind.OBJECT),
   3.221 +        BB(TypeKind.STRING, TypeKind.STRING),
   3.222 +        AAA(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.OBJECT),
   3.223 +        AAB(TypeKind.OBJECT, TypeKind.OBJECT, TypeKind.STRING),
   3.224 +        ABA(TypeKind.OBJECT, TypeKind.STRING, TypeKind.OBJECT),
   3.225 +        ABB(TypeKind.OBJECT, TypeKind.STRING, TypeKind.STRING),
   3.226 +        BAA(TypeKind.STRING, TypeKind.OBJECT, TypeKind.OBJECT),
   3.227 +        BAB(TypeKind.STRING, TypeKind.OBJECT, TypeKind.STRING),
   3.228 +        BBA(TypeKind.STRING, TypeKind.STRING, TypeKind.OBJECT),
   3.229 +        BBB(TypeKind.STRING, TypeKind.STRING, TypeKind.STRING);
   3.230 +
   3.231 +        List<TypeKind> typeKindList;
   3.232 +        String expressionListStr;
   3.233 +        String parameterListStr;
   3.234 +        String bytecodeSigStr;
   3.235 +
   3.236 +        private TypeConfiguration(TypeKind... typeKindList) {
   3.237 +            this.typeKindList = List.from(typeKindList);
   3.238 +            expressionListStr = asExpressionList();
   3.239 +            parameterListStr = asParameterList();
   3.240 +            bytecodeSigStr = asBytecodeString();
   3.241 +        }
   3.242 +
   3.243 +        private String asExpressionList() {
   3.244 +            StringBuilder buf = new StringBuilder();
   3.245 +            String sep = "";
   3.246 +            for (TypeKind tk : typeKindList) {
   3.247 +                buf.append(sep);
   3.248 +                buf.append(tk.valueString);
   3.249 +                sep = ",";
   3.250 +            }
   3.251 +            return buf.toString();
   3.252 +        }
   3.253 +
   3.254 +        private String asParameterList() {
   3.255 +            StringBuilder buf = new StringBuilder();
   3.256 +            String sep = "";
   3.257 +            int count = 0;
   3.258 +            for (TypeKind arg : typeKindList) {
   3.259 +                buf.append(sep);
   3.260 +                buf.append(arg.typeString);
   3.261 +                if (count == (typeKindList.size() - 1)) {
   3.262 +                    buf.append("...");
   3.263 +                }
   3.264 +                buf.append(" ");
   3.265 +                buf.append("arg" + count++);
   3.266 +                sep = ",";
   3.267 +            }
   3.268 +            return buf.toString();
   3.269 +        }
   3.270 +
   3.271 +        private String asBytecodeString() {
   3.272 +            StringBuilder buf = new StringBuilder();
   3.273 +            int count = 0;
   3.274 +            for (TypeKind arg : typeKindList) {
   3.275 +                if (count == (typeKindList.size() - 1)) {
   3.276 +                    buf.append("[");
   3.277 +                }
   3.278 +                buf.append(arg.bytecodeString);
   3.279 +                count++;
   3.280 +            }
   3.281 +            return buf.toString();
   3.282 +        }
   3.283 +    }
   3.284 +
   3.285 +    static class VarargsMethod {
   3.286 +        TypeConfiguration parameterTypes;
   3.287 +
   3.288 +        public VarargsMethod(TypeConfiguration parameterTypes) {
   3.289 +            this.parameterTypes = parameterTypes;
   3.290 +        }
   3.291 +
   3.292 +        @Override
   3.293 +        public String toString() {
   3.294 +            return "void m( " + parameterTypes.parameterListStr + ") {}";
   3.295 +        }
   3.296 +
   3.297 +        boolean isApplicable(TypeConfiguration that) {
   3.298 +            List<TypeKind> actuals = that.typeKindList;
   3.299 +            List<TypeKind> formals = parameterTypes.typeKindList;
   3.300 +            if ((actuals.size() - formals.size()) < -1)
   3.301 +                return false; //not enough args
   3.302 +            for (TypeKind actual : actuals) {
   3.303 +                if (!actual.isSubtypeOf(formals.head))
   3.304 +                    return false; //type mismatch
   3.305 +                formals = formals.tail.isEmpty() ?
   3.306 +                    formals :
   3.307 +                    formals.tail;
   3.308 +            }
   3.309 +            return true;
   3.310 +        }
   3.311 +
   3.312 +        boolean isMoreSpecificThan(VarargsMethod that) {
   3.313 +            List<TypeKind> actuals = parameterTypes.typeKindList;
   3.314 +            List<TypeKind> formals = that.parameterTypes.typeKindList;
   3.315 +            int checks = 0;
   3.316 +            int expectedCheck = Math.max(actuals.size(), formals.size());
   3.317 +            while (checks < expectedCheck) {
   3.318 +                if (!actuals.head.isSubtypeOf(formals.head))
   3.319 +                    return false; //type mismatch
   3.320 +                formals = formals.tail.isEmpty() ?
   3.321 +                    formals :
   3.322 +                    formals.tail;
   3.323 +                actuals = actuals.tail.isEmpty() ?
   3.324 +                    actuals :
   3.325 +                    actuals.tail;
   3.326 +                checks++;
   3.327 +            }
   3.328 +            return true;
   3.329 +        }
   3.330 +    }
   3.331 +
   3.332 +    static class ErrorChecker implements javax.tools.DiagnosticListener<JavaFileObject> {
   3.333 +
   3.334 +        boolean errorFound;
   3.335 +        List<String> errDiags = List.nil();
   3.336 +
   3.337 +        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
   3.338 +            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
   3.339 +                errDiags = errDiags.append(diagnostic.getMessage(Locale.getDefault()));
   3.340 +                errorFound = true;
   3.341 +            }
   3.342 +        }
   3.343 +
   3.344 +        String printDiags() {
   3.345 +            StringBuilder buf = new StringBuilder();
   3.346 +            for (String s : errDiags) {
   3.347 +                buf.append(s);
   3.348 +                buf.append("\n");
   3.349 +            }
   3.350 +            return buf.toString();
   3.351 +        }
   3.352 +    }
   3.353 +}

mercurial