Merge "Avoid some duplicate bridge methods" into main
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java
index 36d3a2b..116a17c 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TransTypes.java
@@ -76,6 +76,8 @@
     private final Resolve resolve;
     private final CompileStates compileStates;
 
+    private final boolean skipDuplicateBridges;
+
     @SuppressWarnings("this-escape")
     protected TransTypes(Context context) {
         context.put(transTypesKey, this);
@@ -87,6 +89,8 @@
         types = Types.instance(context);
         make = TreeMaker.instance(context);
         resolve = Resolve.instance(context);
+        Source source = Source.instance(context);
+        skipDuplicateBridges = Options.instance(context).getBoolean("skipDuplicateBridges", false);
         annotate = Annotate.instance(context);
         attr = Attr.instance(context);
     }
@@ -358,6 +362,9 @@
                                        MethodSymbol impl,
                                        Type dest) {
             if (impl != method) {
+                if (skipBridge(method, impl, dest)) {
+                    return false;
+                }
                 // If either method or impl have different erasures as
                 // members of dest, a bridge is needed.
                 Type method_erasure = method.erasure(types);
@@ -398,6 +405,41 @@
             return types.isSameType(erasure(types.memberType(type, method)),
                                     erasure);
         }
+        /**
+         * Returns true if a bridge should be skipped because we expect it to be declared in
+         * a super-type.
+         *
+         * @param method The symbol for which a bridge might have to be added
+         * @param impl The implementation of method
+         * @param dest The type in which the bridge would go
+         */
+        private boolean skipBridge(MethodSymbol method,
+                                   MethodSymbol impl,
+                                   Type dest) {
+            if (!skipDuplicateBridges) {
+                return false;
+            }
+            if (dest.tsym == impl.owner) {
+                // the method is implemented in the current class; we need to bridge it here
+                return false;
+            }
+            if (!types.isSubtype(
+                    types.erasure(impl.owner.type), types.erasure(method.owner.type))) {
+                // the method is implemented in some supertype that is not a subtype of
+                // the declaring type, so the bridge will not have been created in the
+                // implementing class
+                return false;
+            }
+            if (!impl.overrides(method, (TypeSymbol) impl.owner, types, true)) {
+                // the method is implementing in a supertype that is also a subtype
+                // of the declaring type, but the method's signature in the implementing
+                // class does not override its signature in the declaring class
+                return false;
+            }
+            // we don't need to consider visibility for accessibility bridges, because
+            // that happens on a separate code path
+            return true;
+        }
 
     void addBridges(DiagnosticPosition pos,
                     TypeSymbol i,