--- old/src/share/classes/com/sun/tools/javac/code/Types.java 2013-06-13 15:14:41.109334524 -0400 +++ new/src/share/classes/com/sun/tools/javac/code/Types.java 2013-06-13 15:14:40.984333149 -0400 @@ -1317,6 +1317,26 @@ } // + /** + * Can t and s be compared for equality? Any primitive == + * primitive or primitive == object comparisons here are an error. + * Unboxing and correct primitive == primitive comparisons are + * already dealt with in Attr.visitBinary. + * + */ + public boolean isEqualityComparable(Type s, Type t, Warner warn) { + if (t.isNumeric() && s.isNumeric()) + return true; + + boolean tPrimitive = t.isPrimitive(); + boolean sPrimitive = s.isPrimitive(); + if (!tPrimitive && !sPrimitive) { + return isCastable(s, t, warn) || isCastable(t, s, warn); + } else { + return false; + } + } + // public boolean isCastable(Type t, Type s) { return isCastable(t, s, noWarnings); --- old/src/share/classes/com/sun/tools/javac/comp/Attr.java 2013-06-13 15:14:41.783341828 -0400 +++ new/src/share/classes/com/sun/tools/javac/comp/Attr.java 2013-06-13 15:14:41.655340419 -0400 @@ -2991,6 +2991,8 @@ !left.isErroneous() && !right.isErroneous()) { owntype = operator.type.getReturnType(); + // This will figure out when unboxing can happen and + // choose the right comparison operator. int opc = chk.checkOperator(tree.lhs.pos(), (OperatorSymbol)operator, tree.getTag(), @@ -3018,9 +3020,11 @@ } // Check that argument types of a reference ==, != are - // castable to each other, (JLS???). + // castable to each other, (JLS 15.21). Note: unboxing + // comparisons will not have an acmp* opc at this point. if ((opc == ByteCodes.if_acmpeq || opc == ByteCodes.if_acmpne)) { - if (!types.isCastable(left, right, new Warner(tree.pos()))) { + if (!types.isEqualityComparable(left, right, + new Warner(tree.pos()))) { log.error(tree.pos(), "incomparable.types", left, right); } } --- old/test/tools/javac/lambda/LambdaConv01.java 2013-06-13 15:14:42.268347168 -0400 +++ new/test/tools/javac/lambda/LambdaConv01.java 2013-06-13 15:14:42.141345770 -0400 @@ -67,7 +67,7 @@ assertTrue(3 == f1.foo()); //Covariant returns: TU f2 = (Integer x) -> x; - assertTrue(3 == f2.foo(3)); + assertTrue(3 == f2.foo(3).intValue()); //Method resolution with boxing: int res = LambdaConv01.exec((Integer x) -> x, 3); assertTrue(3 == res); @@ -86,7 +86,7 @@ assertTrue(3 == f1.foo()); //Covariant returns: TU f2 = (Integer x) -> x; - assertTrue(3 == f2.foo(3)); + assertTrue(3 == f2.foo(3).intValue()); //Method resolution with boxing: int res = LambdaConv01.exec((Integer x) -> x, 3); assertTrue(3 == res); @@ -105,7 +105,7 @@ assertTrue(3 == f1.foo()); //Covariant returns: TU f2 = (Integer x) -> x; - assertTrue(3 == f2.foo(3)); + assertTrue(3 == f2.foo(3).intValue()); //Method resolution with boxing: int res = LambdaConv01.exec((Integer x) -> x, 3); assertTrue(3 == res); @@ -124,7 +124,7 @@ assertTrue(3 == f1.foo()); //Covariant returns: TU f2 = (Integer x) -> x; - assertTrue(3 == f2.foo(3)); + assertTrue(3 == f2.foo(3).intValue()); //Method resolution with boxing: int res = LambdaConv01.exec((Integer x) -> x, 3); assertTrue(3 == res); --- old/test/tools/javac/lambda/LambdaExpr15.java 2013-06-13 15:14:42.703351957 -0400 +++ new/test/tools/javac/lambda/LambdaExpr15.java 2013-06-13 15:14:42.576350559 -0400 @@ -48,7 +48,7 @@ new Object() { String get() { return ""; } }; - assertTrue(t == 1); + assertTrue((Integer)t == 1); }; ba1.apply(1); @@ -58,7 +58,7 @@ String get() { return ""; } }; new A(); - assertTrue(t == 2); + assertTrue((Integer)t == 2); }; ba2.apply(2); assertTrue(assertionCount == 2); --- old/test/tools/javac/lambda/typeInference/InferenceTest2b.java 2013-06-13 15:14:43.141356780 -0400 +++ new/test/tools/javac/lambda/typeInference/InferenceTest2b.java 2013-06-13 15:14:43.013355370 -0400 @@ -64,7 +64,7 @@ void m2(SAM6 s) { System.out.println("m2()"); - assertTrue(s.m6(1, 2) == 1); + assertTrue(s.m6(1, 2).equals(Integer.valueOf(1))); } void m3(SAM6 s) { --- /dev/null 2013-06-11 04:39:25.854507558 -0400 +++ new/test/tools/javac/types/TestComparisons.java 2013-06-13 15:14:43.422359873 -0400 @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2013, 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 8013357 + * @summary javac should correctly enforce binary comparison rules. + */ +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.*; +import com.sun.tools.javac.code.Symbol.*; +import java.io.*; +import java.lang.reflect.Array; +import java.util.EnumSet; + +public class TestComparisons { + + private int errors = 0; + private int testnum = 0; + + static final File testdir = new File("8013357"); + + private enum CompareType { + BYTE_PRIM("byte"), + SHORT_PRIM("short"), + CHAR_PRIM("char"), + INTEGER_PRIM("int"), + LONG_PRIM("long"), + FLOAT_PRIM("float"), + DOUBLE_PRIM("double"), + BOOLEAN_PRIM("boolean"), + + BYTE("Byte"), + SHORT("Short"), + CHAR("Character"), + INTEGER("Integer"), + LONG("Long"), + FLOAT("Float"), + DOUBLE("Double"), + BOOLEAN("Boolean"), + + BYTE_SUPER("List", true), + SHORT_SUPER("List", true), + CHAR_SUPER("List", true), + INTEGER_SUPER("List", true), + LONG_SUPER("List", true), + FLOAT_SUPER("List", true), + DOUBLE_SUPER("List", true), + BOOLEAN_SUPER("List", true), + + OBJECT("Object"), + NUMBER("Number"), + STRING("String"); + + public final boolean isList; + public final String name; + + private CompareType(final String name, final boolean isList) { + this.isList = isList; + this.name = name; + } + + private CompareType(final String name) { + this(name, false); + } + } + + // The integers here refer to which subsection of JLS 15.21 is in + // effect. 0 means no comparison is allowed. + private static final int truthtab[][] = { + // byte, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // short, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // char, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // int, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // long, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // float, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // double, comparable to itself, any numeric type, or any boxed + // numeric type. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 1, 1, 1, 1, 1, 1, 1, 0, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // boolean, comparable only to itself and Boolean. + { 0, 0, 0, 0, 0, 0, 0, 2, // Primitives + 0, 0, 0, 0, 0, 0, 0, 2, // Boxed primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Captures + 0, 0, 0 // Reference types + }, + // Byte, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 3, 0, 0, 0, 0, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Short, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 3, 0, 0, 0, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Character, comparable to itself, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 0, 3, 0, 0, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 0, 0 // Reference types + }, + // Int, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 0, 0, 3, 0, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Long, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 0, 0, 0, 3, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Float, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 0, 0, 0, 0, 3, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Double, comparable to itself, Number, Object, any numeric primitive, + // and any captures. + { 1, 1, 1, 1, 1, 1, 1, 0, // Primitives + 0, 0, 0, 0, 0, 0, 3, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // Boolean, to itself, any capture, Object, and boolean. + { 0, 0, 0, 0, 0, 0, 0, 2, // Primitives + 0, 0, 0, 0, 0, 0, 0, 2, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 0, 0 // Reference types + }, + // Byte supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Short supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Character supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Integer supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Long supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Float supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Double supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Boolean supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Object, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 3 // Reference types + }, + // Number, comparable to Object, any of its subclasses. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 3, 3, 0, 3, 3, 3, 3, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 3, 0 // Reference types + }, + // String supertype wildcard, comparable to any reference type. + // and any captures. + { 0, 0, 0, 0, 0, 0, 0, 0, // Primitives + 0, 0, 0, 0, 0, 0, 0, 0, // Boxed primitives + 3, 3, 3, 3, 3, 3, 3, 3, // Captures + 3, 0, 3 // Reference types + } + }; + + private void assert_compile_fail(final File file, final String body) { + final String filename = file.getPath(); + final String[] args = { filename }; + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + final int rc = com.sun.tools.javac.Main.compile(args, pw); + pw.close(); + if (rc == 0) { + System.err.println("Compilation of " + file.getName() + + " didn't fail as expected.\nFile:\n" + + body + "\nOutput:\n" + sw.toString()); + errors++; + } + } + + private void assert_compile_succeed(final File file, final String body) { + final String filename = file.getPath(); + final String[] args = { filename }; + final StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + final int rc = com.sun.tools.javac.Main.compile(args, pw); + pw.close(); + if (rc != 0) { + System.err.println("Compilation of " + file.getName() + + " didn't succeed as expected.\nFile:\n" + + body + "\nOutput:\n" + + sw.toString()); + errors++; + } + } + + private String makeBody(final int num, + final CompareType left, + final CompareType right) { + return "import java.util.List;\n" + + "public class Test" + num + " {\n" + + " public boolean test(" + left.name + + " left, " + right.name + " right) {\n" + + " return left" + (left.isList ? ".get(0)" : "") + + " == right" + (right.isList ? ".get(0)" : "") + ";\n" + + " }\n" + + "}\n"; + } + + private File writeFile(final String filename, + final String body) + throws IOException { + final File f = new File(testdir, filename); + f.getParentFile().mkdirs(); + final FileWriter out = new FileWriter(f); + out.write(body); + out.close(); + return f; + } + + private void test(final CompareType left, final CompareType right) + throws IOException { + final int num = testnum++; + final String filename = "Test" + num + ".java"; + final String body = makeBody(num, left, right); + final File file = writeFile(filename, body); + if (truthtab[left.ordinal()][right.ordinal()] != 0) + assert_compile_succeed(file, body); + else + assert_compile_fail(file, body); + } + + void run() throws Exception { + testdir.mkdir(); + + for(CompareType left : CompareType.values()) + for(CompareType right : CompareType.values()) + test(left, right); + + if (errors != 0) + throw new Exception("ObjectZeroCompare test failed with " + + errors + " errors."); + } + + public static void main(String... args) throws Exception { + new TestComparisons().run(); + } +}