@@ -24,6 +24,7 @@ import Inferencing._
2424import transform .ValueClasses ._
2525import transform .TypeUtils ._
2626import transform .SymUtils ._
27+ import TypeErasure .erasure
2728import reporting ._
2829import config .Feature .sourceVersion
2930import config .SourceVersion ._
@@ -1238,8 +1239,64 @@ class Namer { typer: Typer =>
12381239 addForwarders(sels1, sel.name :: seen)
12391240 case _ =>
12401241
1242+ /** Avoid a clash of export forwarder `forwarder` with other forwarders in `forwarders`.
1243+ * @return If `forwarder` clashes, a new leading forwarder and trailing forwarders list
1244+ * that avoids the clash according to the scheme described in `avoidClashes`.
1245+ * If there's no clash, the inputs as they are in a pair.
1246+ */
1247+ def avoidClashWith (forwarder : tpd.DefDef , forwarders : List [tpd.MemberDef ]): (tpd.DefDef , List [tpd.MemberDef ]) =
1248+ def clashes (fwd1 : Symbol , fwd2 : Symbol ) =
1249+ fwd1.targetName == fwd2.targetName
1250+ && erasure(fwd1.info).signature == erasure(fwd2.info).signature
1251+
1252+ forwarders match
1253+ case forwarders @ ((forwarder1 : tpd.DefDef ) :: forwarders1)
1254+ if forwarder.name == forwarder1.name =>
1255+ if clashes(forwarder.symbol, forwarder1.symbol) then
1256+ val alt1 = tpd.methPart(forwarder.rhs).tpe
1257+ val alt2 = tpd.methPart(forwarder1.rhs).tpe
1258+ val cmp = alt1 match
1259+ case alt1 : TermRef => alt2 match
1260+ case alt2 : TermRef => compare(alt1, alt2)
1261+ case _ => 0
1262+ case _ => 0
1263+ if cmp == 0 then
1264+ report.error(
1265+ ex """ Clashing exports: The exported
1266+ | ${forwarder.rhs.symbol}: ${alt1.widen}
1267+ |and ${forwarder1.rhs.symbol}: ${alt2.widen}
1268+ |have the same signature after erasure and overloading resolution could not disambiguate. """ ,
1269+ exp.srcPos)
1270+ avoidClashWith(if cmp < 0 then forwarder1 else forwarder, forwarders1)
1271+ else
1272+ val (forwarder2, forwarders2) = avoidClashWith(forwarder, forwarders1)
1273+ (forwarder2, forwarders.derivedCons(forwarder1, forwarders2))
1274+ case _ =>
1275+ (forwarder, forwarders)
1276+ end avoidClashWith
1277+
1278+ /** Avoid clashes of any two export forwarders in `forwarders`.
1279+ * A clash is if two forwarders f1 and f2 have the same name and signatures after erasure.
1280+ * We try to avoid a clash by dropping one of f1 and f2, keeping the one whose right hand
1281+ * side reference would be preferred by overloading resolution.
1282+ * If neither of f1 or f2 is preferred over the other, report an error.
1283+ *
1284+ * The idea is that this simulates the hypothetical case where export forwarders
1285+ * are not generated and we treat an export instead more like an import where we
1286+ * expand the use site reference. Test cases in {neg,pos}/i14699.scala.
1287+ *
1288+ * @pre Forwarders with the same name are consecutive in `forwarders`.
1289+ */
1290+ def avoidClashes (forwarders : List [tpd.MemberDef ]): List [tpd.MemberDef ] = forwarders match
1291+ case forwarders @ (forwarder :: forwarders1) =>
1292+ val (forwarder2, forwarders2) = forwarder match
1293+ case forwarder : tpd.DefDef => avoidClashWith(forwarder, forwarders1)
1294+ case _ => (forwarder, forwarders1)
1295+ forwarders.derivedCons(forwarder2, avoidClashes(forwarders2))
1296+ case Nil => forwarders
1297+
12411298 addForwarders(selectors, Nil )
1242- val forwarders = buf.toList
1299+ val forwarders = avoidClashes( buf.toList)
12431300 exp.pushAttachment(ExportForwarders , forwarders)
12441301 forwarders
12451302 end exportForwarders
0 commit comments