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