Skip to content

Commit 233c8ec

Browse files
authored
Avoid embedding SelectionProtos in Conversions (#17755)
2 parents 0749271 + 701254f commit 233c8ec

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,17 +1122,33 @@ trait Implicits:
11221122
adapt(generated, pt.widenExpr, locked)
11231123
else {
11241124
def untpdGenerated = untpd.TypedSplice(generated)
1125-
def producesConversion(info: Type): Boolean = info match
1126-
case info: PolyType => producesConversion(info.resType)
1127-
case info: MethodType if info.isImplicitMethod => producesConversion(info.resType)
1128-
case _ => info.derivesFrom(defn.ConversionClass)
1125+
def conversionResultType(info: Type): Type = info match
1126+
case info: PolyType => conversionResultType(info.resType)
1127+
case info: MethodType if info.isImplicitMethod => conversionResultType(info.resType)
1128+
case _ =>
1129+
if info.derivesFrom(defn.ConversionClass) then
1130+
pt match
1131+
case selProto: SelectionProto =>
1132+
// we want to avoid embedding a SelectionProto in a Conversion, as the result type
1133+
// as it might end up within a GADT cast type, e.g. tests/pos/i15867.scala
1134+
// so, if we can find the target result type - as in,
1135+
// if it matches the selection prototype, then let's adapt to that instead
1136+
// otherwise just skip adapting with a prototype (by returning NoType)
1137+
info.baseType(defn.ConversionClass) match
1138+
case AppliedType(_, List(_, restpe)) if selProto.isMatchedBy(restpe) =>
1139+
restpe
1140+
case _ => NoType // can't find conversion result type, avoid adapting with SelectionProto
1141+
case _: ProtoType => NoType // avoid adapting with ProtoType
1142+
case _ => pt // not a ProtoType, so use it for adapting
1143+
else NoType // not a Conversion, don't adapt
11291144
def tryConversion(using Context) = {
1145+
val restpeConv = if ref.symbol.is(Given) then conversionResultType(ref.widenTermRefExpr) else NoType
11301146
val untpdConv =
1131-
if ref.symbol.is(Given) && producesConversion(ref.symbol.info) then
1147+
if restpeConv.exists then
11321148
untpd.Select(
11331149
untpd.TypedSplice(
11341150
adapt(generated,
1135-
defn.ConversionClass.typeRef.appliedTo(argument.tpe, pt),
1151+
defn.ConversionClass.typeRef.appliedTo(argument.tpe, restpeConv),
11361152
locked)),
11371153
nme.apply)
11381154
else untpdGenerated

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ i4176-gadt.scala
9494

9595
# GADT difference
9696
i13974a.scala
97+
i15867.scala
9798

9899
java-inherited-type1
99100

tests/pos/i15867.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
enum SUB[-A, +B]:
2+
case Refl[S]() extends SUB[S, S]
3+
4+
class Pow(self: Int):
5+
def **(other: Int): Int = math.pow(self, other).toInt
6+
7+
given fromList[T]: Conversion[List[T], Pow] = ???
8+
9+
given fromInt: Conversion[Int, Pow] = Pow(_)
10+
11+
def foo[T](t1: T, ev: T SUB List[Int]) =
12+
ev match { case SUB.Refl() =>
13+
t1 ** 2 // error
14+
}
15+
16+
def baz[T](t2: T, ev: T SUB Int) =
17+
ev match { case SUB.Refl() =>
18+
t2 ** 2 // works
19+
}

tests/pos/i15867.specs2.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Foo:
2+
given Conversion[String, Data] with
3+
def apply(str: String): Data = new Data(str)
4+
5+
class Data(str: String):
6+
def |(str: String) = new Data(this.str + str)
7+
8+
class Bar extends Foo:
9+
"str" | "ing"

0 commit comments

Comments
 (0)