@@ -24,6 +24,7 @@ import Inferencing._
24
24
import transform .ValueClasses ._
25
25
import transform .TypeUtils ._
26
26
import transform .SymUtils ._
27
+ import TypeErasure .erasure
27
28
import reporting ._
28
29
import config .Feature .sourceVersion
29
30
import config .SourceVersion ._
@@ -1237,8 +1238,64 @@ class Namer { typer: Typer =>
1237
1238
addForwarders(sels1, sel.name :: seen)
1238
1239
case _ =>
1239
1240
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
+
1240
1297
addForwarders(selectors, Nil )
1241
- val forwarders = buf.toList
1298
+ val forwarders = avoidClashes( buf.toList)
1242
1299
exp.pushAttachment(ExportForwarders , forwarders)
1243
1300
forwarders
1244
1301
end exportForwarders
0 commit comments