8074803: Name clash

Javac incorrectly reports a name clash.

Reviewed-by: mcimadamore
This commit is contained in:
Srikanth Adayapalam 2015-10-22 16:18:28 +05:30
parent 200d75bd08
commit b84f19e870
4 changed files with 126 additions and 26 deletions

View file

@ -220,11 +220,7 @@ public class Flags {
*/ */
public static final long UNION = 1L<<39; public static final long UNION = 1L<<39;
/** // Flag bit (1L << 40) is available.
* 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 that marks an 'effectively final' local variable. * Flag that marks an 'effectively final' local variable.
@ -383,7 +379,6 @@ public class Flags {
HYPOTHETICAL(Flags.HYPOTHETICAL), HYPOTHETICAL(Flags.HYPOTHETICAL),
PROPRIETARY(Flags.PROPRIETARY), PROPRIETARY(Flags.PROPRIETARY),
UNION(Flags.UNION), UNION(Flags.UNION),
OVERRIDE_BRIDGE(Flags.OVERRIDE_BRIDGE),
EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL), EFFECTIVELY_FINAL(Flags.EFFECTIVELY_FINAL),
CLASH(Flags.CLASH), CLASH(Flags.CLASH),
AUXILIARY(Flags.AUXILIARY), AUXILIARY(Flags.AUXILIARY),

View file

@ -1528,9 +1528,33 @@ public abstract class Symbol extends AnnoConstruct implements Element {
* It is assumed that both symbols have the same name. The static * It is assumed that both symbols have the same name. The static
* modifier is ignored for this test. * 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 * See JLS 8.4.6.1 (without transitivity) and 8.4.6.4
*/ */
public boolean overrides(Symbol _other, TypeSymbol origin, Types types, boolean checkResult) { 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 (isConstructor() || _other.kind != MTH) return false;
if (this == _other) return true; if (this == _other) return true;
@ -1550,7 +1574,7 @@ public abstract class Symbol extends AnnoConstruct implements Element {
} }
// check for an inherited implementation // check for an inherited implementation
if ((flags() & ABSTRACT) != 0 || if (((flags() & ABSTRACT) != 0 && requireConcreteIfInherited) ||
((other.flags() & ABSTRACT) == 0 && (other.flags() & DEFAULT) == 0) || ((other.flags() & ABSTRACT) == 0 && (other.flags() & DEFAULT) == 0) ||
!other.isOverridableIn(origin) || !other.isOverridableIn(origin) ||
!this.isMemberOf(origin, types)) !this.isMemberOf(origin, types))

View file

@ -86,7 +86,7 @@ public class TransTypes extends TreeTranslator {
log = Log.instance(context); log = Log.instance(context);
syms = Symtab.instance(context); syms = Symtab.instance(context);
enter = Enter.instance(context); enter = Enter.instance(context);
overridden = new HashMap<>(); bridgeSpans = new HashMap<>();
types = Types.instance(context); types = Types.instance(context);
make = TreeMaker.instance(context); make = TreeMaker.instance(context);
resolve = Resolve.instance(context); resolve = Resolve.instance(context);
@ -96,10 +96,12 @@ public class TransTypes extends TreeTranslator {
annotate = Annotate.instance(context); annotate = Annotate.instance(context);
} }
/** A hashtable mapping bridge methods to the methods they override after /** A hashtable mapping bridge methods to the pair of methods they bridge.
* type erasure. * 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, /** Construct an attributed tree for a cast of expression to target type,
* unless it already has precisely that type. * unless it already has precisely that type.
@ -294,9 +296,9 @@ public class TransTypes extends TreeTranslator {
bridges.append(md); 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); origin.members().enter(bridge);
overridden.put(bridge, meth); bridgeSpans.put(bridge, new Pair<>(meth, impl));
} }
private List<VarSymbol> createBridgeParams(MethodSymbol impl, MethodSymbol bridge, private List<VarSymbol> createBridgeParams(MethodSymbol impl, MethodSymbol bridge,
@ -344,12 +346,12 @@ public class TransTypes extends TreeTranslator {
if (sym.kind == MTH && if (sym.kind == MTH &&
sym.name != names.init && sym.name != names.init &&
(sym.flags() & (PRIVATE | STATIC)) == 0 && (sym.flags() & (PRIVATE | STATIC)) == 0 &&
(sym.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) != SYNTHETIC && (sym.flags() & SYNTHETIC) != SYNTHETIC &&
sym.isMemberOf(origin, types)) sym.isMemberOf(origin, types))
{ {
MethodSymbol meth = (MethodSymbol)sym; MethodSymbol meth = (MethodSymbol)sym;
MethodSymbol bridge = meth.binaryImplementation(origin, types); MethodSymbol bridge = meth.binaryImplementation(origin, types);
MethodSymbol impl = meth.implementation(origin, types, true, overrideBridgeFilter); MethodSymbol impl = meth.implementation(origin, types, true);
if (bridge == null || if (bridge == null ||
bridge == meth || bridge == meth ||
(impl != null && !bridge.owner.isSubClass(impl.owner, types))) { (impl != null && !bridge.owner.isSubClass(impl.owner, types))) {
@ -365,16 +367,21 @@ public class TransTypes extends TreeTranslator {
// reflection design error. // reflection design error.
addBridge(pos, meth, impl, origin, false, bridges); addBridge(pos, meth, impl, origin, false, bridges);
} }
} else if ((bridge.flags() & (SYNTHETIC | OVERRIDE_BRIDGE)) == SYNTHETIC) { } else if ((bridge.flags() & SYNTHETIC) == SYNTHETIC) {
MethodSymbol other = overridden.get(bridge); final Pair<MethodSymbol, MethodSymbol> bridgeSpan = bridgeSpans.get(bridge);
MethodSymbol other = bridgeSpan == null ? null : bridgeSpan.fst;
if (other != null && other != meth) { if (other != null && other != meth) {
if (impl == null || !impl.overrides(other, origin, types, true)) { if (impl == null || !impl.overrides(other, origin, types, true)) {
// 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 // Bridge for other symbol pair was added
log.error(pos, "name.clash.same.erasure.no.override", log.error(pos, "name.clash.same.erasure.no.override",
other, other.location(origin.type, types), other, other.location(origin.type, types),
meth, meth.location(origin.type, types)); meth, meth.location(origin.type, types));
} }
} }
}
} else if (!bridge.overrides(meth, origin, types, true)) { } else if (!bridge.overrides(meth, origin, types, true)) {
// Accidental binary override without source override. // Accidental binary override without source override.
if (bridge.owner == origin || if (bridge.owner == origin ||
@ -388,11 +395,6 @@ public class TransTypes extends TreeTranslator {
} }
} }
// where // 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 * @param method The symbol for which a bridge might have to be added

View file

@ -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);
}
}