8074803: Name clash
Summary: Javac incorrectly reports a name clash.
Reviewed-by: mcimadamore
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Thu Oct 22 09:05:54 2015 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java Thu Oct 22 16:18:28 2015 +0530
@@ -220,11 +220,7 @@
*/
public static final long UNION = 1L<<39;
- /**
- * Flag that marks a special kind of bridge method (the ones that
- * come from restricted supertype bounds).
- */
- public static final long OVERRIDE_BRIDGE = 1L<<40;
+ // Flag bit (1L << 40) is available.
/**
* Flag that marks an 'effectively final' local variable.
@@ -383,7 +379,6 @@
HYPOTHETICAL(Flags.HYPOTHETICAL),
PROPRIETARY(Flags.PROPRIETARY),
UNION(Flags.UNION),
- OVERRIDE_BRIDGE(Flags.OVERRIDE_BRIDGE),
EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL),
CLASH(Flags.CLASH),
AUXILIARY(Flags.AUXILIARY),
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Thu Oct 22 09:05:54 2015 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java Thu Oct 22 16:18:28 2015 +0530
@@ -1528,9 +1528,33 @@
* It is assumed that both symbols have the same name. The static
* modifier is ignored for this test.
*
+ * A quirk in the works is that if the receiver is a method symbol for
+ * an inherited abstract method we answer false summarily all else being
+ * immaterial. Abstract "own" methods (i.e `this' is a direct member of
+ * origin) don't get rejected as summarily and are put to test against the
+ * suitable criteria.
+ *
* See JLS 8.4.6.1 (without transitivity) and 8.4.6.4
*/
public boolean overrides(Symbol _other, TypeSymbol origin, Types types, boolean checkResult) {
+ return overrides(_other, origin, types, checkResult, true);
+ }
+
+ /** Does this symbol override `other' symbol, when both are seen as
+ * members of class `origin'? It is assumed that _other is a member
+ * of origin.
+ *
+ * Caveat: If `this' is an abstract inherited member of origin, it is
+ * deemed to override `other' only when `requireConcreteIfInherited'
+ * is false.
+ *
+ * It is assumed that both symbols have the same name. The static
+ * modifier is ignored for this test.
+ *
+ * See JLS 8.4.6.1 (without transitivity) and 8.4.6.4
+ */
+ public boolean overrides(Symbol _other, TypeSymbol origin, Types types, boolean checkResult,
+ boolean requireConcreteIfInherited) {
if (isConstructor() || _other.kind != MTH) return false;
if (this == _other) return true;
@@ -1550,7 +1574,7 @@
}
// check for an inherited implementation
- if ((flags() & ABSTRACT) != 0 ||
+ if (((flags() & ABSTRACT) != 0 && requireConcreteIfInherited) ||
((other.flags() & ABSTRACT) == 0 && (other.flags() & DEFAULT) == 0) ||
!other.isOverridableIn(origin) ||
!this.isMemberOf(origin, types))
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Thu Oct 22 09:05:54 2015 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java Thu Oct 22 16:18:28 2015 +0530
@@ -86,7 +86,7 @@
log = Log.instance(context);
syms = Symtab.instance(context);
enter = Enter.instance(context);
- overridden = new HashMap<>();
+ bridgeSpans = new HashMap<>();
types = Types.instance(context);
make = TreeMaker.instance(context);
resolve = Resolve.instance(context);
@@ -96,10 +96,12 @@
annotate = Annotate.instance(context);
}
- /** A hashtable mapping bridge methods to the methods they override after
- * type erasure.
+ /** A hashtable mapping bridge methods to the pair of methods they bridge.
+ * The bridge overrides the first of the pair after type erasure and deflects
+ * to the second of the pair (which differs in type erasure from the one
+ * it overrides thereby necessitating the bridge)
*/
- Map<MethodSymbol,MethodSymbol> overridden;
+ Map<MethodSymbol, Pair<MethodSymbol, MethodSymbol>> bridgeSpans;
/** Construct an attributed tree for a cast of expression to target type,
* unless it already has precisely that type.
@@ -294,9 +296,9 @@
bridges.append(md);
}
- // Add bridge to scope of enclosing class and `overridden' table.
+ // Add bridge to scope of enclosing class and keep track of the bridge span.
origin.members().enter(bridge);
- overridden.put(bridge, meth);
+ bridgeSpans.put(bridge, new Pair<>(meth, impl));
}
private List<VarSymbol> createBridgeParams(MethodSymbol impl, MethodSymbol bridge,
@@ -344,12 +346,12 @@
if (sym.kind == MTH &&
sym.name != names.init &&
(sym.flags() & (PRIVATE | STATIC)) == 0 &&
- (sym.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC &&
+ (sym.flags() & SYNTHETIC) != SYNTHETIC &&
sym.isMemberOf(origin, types))
{
MethodSymbol meth = (MethodSymbol)sym;
MethodSymbol bridge = meth.binaryImplementation(origin, types);
- MethodSymbol impl = meth.implementation(origin, types, true, overrideBridgeFilter);
+ MethodSymbol impl = meth.implementation(origin, types, true);
if (bridge == null ||
bridge == meth ||
(impl != null && !bridge.owner.isSubClass(impl.owner, types))) {
@@ -365,14 +367,19 @@
// reflection design error.
addBridge(pos, meth, impl, origin, false, bridges);
}
- } else if ((bridge.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) == SYNTHETIC) {
- MethodSymbol other = overridden.get(bridge);
+ } else if ((bridge.flags() & SYNTHETIC) == SYNTHETIC) {
+ final Pair<MethodSymbol, MethodSymbol> bridgeSpan = bridgeSpans.get(bridge);
+ MethodSymbol other = bridgeSpan == null ? null : bridgeSpan.fst;
if (other != null && other != meth) {
if (impl == null || !impl.overrides(other, origin, types, true)) {
- // Bridge for other symbol pair was added
- log.error(pos, "name.clash.same.erasure.no.override",
- other, other.location(origin.type, types),
- meth, meth.location(origin.type, types));
+ // Is bridge effectively also the bridge for `meth', if so no clash.
+ MethodSymbol target = bridgeSpan == null ? null : bridgeSpan.snd;
+ if (target == null || !target.overrides(meth, origin, types, true, false)) {
+ // Bridge for other symbol pair was added
+ log.error(pos, "name.clash.same.erasure.no.override",
+ other, other.location(origin.type, types),
+ meth, meth.location(origin.type, types));
+ }
}
}
} else if (!bridge.overrides(meth, origin, types, true)) {
@@ -388,11 +395,6 @@
}
}
// where
- private Filter<Symbol> overrideBridgeFilter = new Filter<Symbol>() {
- public boolean accepts(Symbol s) {
- return (s.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC;
- }
- };
/**
* @param method The symbol for which a bridge might have to be added
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/NameClash/NameClashTest.java Thu Oct 22 16:18:28 2015 +0530
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015 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 8074803
+ * @summary Incorrect name clash error
+ *
+ * @compile NameClashTest.java
+ */
+
+public class NameClashTest {
+
+ String log = "";
+
+ interface A1 {
+ A1 m(String s);
+ }
+
+ abstract class A2 implements A1 {
+ public abstract A2 m(String s);
+ }
+
+ interface B1 {
+ A1 m(String s);
+ }
+
+ interface B2 extends B1 {
+ A2 m(String s);
+ }
+
+ abstract class C extends A2 implements B2 {}
+
+ class D extends C {
+
+ public A2 m(String s) {
+ log += s;
+ return null;
+ }
+ }
+
+ public static void main(String[] args) {
+ NameClashTest nct = new NameClashTest();
+ A1 a1 = nct.new D();
+ a1.m("A1.m ");
+ A2 a2 = nct.new D();
+ a2.m("A2.m ");
+ B1 b1 = nct.new D();
+ b1.m("B1.m ");
+ B2 b2 = nct.new D();
+ b2.m("B2.m ");
+ C c = nct.new D();
+ c.m("C.m ");
+ D d = nct.new D();
+ d.m("D.m ");
+ if (!nct.log.equals("A1.m A2.m B1.m B2.m C.m D.m "))
+ throw new AssertionError("unexpected output: " + nct.log);
+ }
+}