Skip to content

Commit f3787eb

Browse files
authored
Refix avoid GADT casting with ProtoTypes (#18085)
Revert #17755, refix #15867, fixes #18062
2 parents 08de2ba + 9000ebf commit f3787eb

File tree

3 files changed

+31
-24
lines changed

3 files changed

+31
-24
lines changed

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

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,33 +1122,17 @@ trait Implicits:
11221122
adapt(generated, pt.widenExpr, locked)
11231123
else {
11241124
def untpdGenerated = untpd.TypedSplice(generated)
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
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)
11441129
def tryConversion(using Context) = {
1145-
val restpeConv = if ref.symbol.is(Given) then conversionResultType(ref.widenTermRefExpr) else NoType
11461130
val untpdConv =
1147-
if restpeConv.exists then
1131+
if ref.symbol.is(Given) && producesConversion(ref.symbol.info) then
11481132
untpd.Select(
11491133
untpd.TypedSplice(
11501134
adapt(generated,
1151-
defn.ConversionClass.typeRef.appliedTo(argument.tpe, restpeConv),
1135+
defn.ConversionClass.typeRef.appliedTo(argument.tpe, pt),
11521136
locked)),
11531137
nme.apply)
11541138
else untpdGenerated

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4561,7 +4561,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
45614561
AnnotatedType(conj, Annotation(defn.UncheckedStableAnnot, tree.symbol.span))
45624562
else conj
45634563
else pt
4564-
gadts.println(i"insert GADT cast from $tree to $target")
4565-
tree.cast(target)
4564+
if target.existsPart(_.isInstanceOf[ProtoType]) then
4565+
// we want to avoid embedding a SelectionProto in a Conversion, as the result type
4566+
// as it might end up within a GADT cast type, e.g. tests/pos/i15867.scala
4567+
// so we just bail - in that example, a GADT cast will be insert on application, so it compiles.
4568+
// but tests/pos/i18062.scala is an example with a polymorphic method, which requires type variables to
4569+
// be applied to the tree and then constrained before they match the prototype.
4570+
// so rather than try to handle all that before calling adapt, let's just bail on this side.
4571+
tree
4572+
else
4573+
gadts.println(i"insert GADT cast from $tree to $target")
4574+
tree.cast(target)
45664575
end insertGadtCast
45674576
}

tests/pos/i18062.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
trait CB[X] { def get: X }
2+
3+
trait WrapperConvert[F[_], G[_]]:
4+
def conv[X](fx: F[X]): G[X]
5+
6+
object WrapperConvert:
7+
implicit def id[F[_]]: WrapperConvert[F, F] = new WrapperConvert[F, F]:
8+
def conv[X](fx: F[X]): F[X] = fx
9+
10+
transparent inline given convertX[F[_], X](using wc: WrapperConvert[F, CB]): Conversion[F[X], X] =
11+
new Conversion[F[X], X]:
12+
def apply(fx: F[X]) = wc.conv(fx).get
13+
14+
def test(cb: CB[Int], x: Int): Int = cb + x

0 commit comments

Comments
 (0)