diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c6795ed25a0e..0d40f818fd86 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1122,17 +1122,33 @@ trait Implicits: adapt(generated, pt.widenExpr, locked) else { def untpdGenerated = untpd.TypedSplice(generated) - def producesConversion(info: Type): Boolean = info match - case info: PolyType => producesConversion(info.resType) - case info: MethodType if info.isImplicitMethod => producesConversion(info.resType) - case _ => info.derivesFrom(defn.ConversionClass) + def conversionResultType(info: Type): Type = info match + case info: PolyType => conversionResultType(info.resType) + case info: MethodType if info.isImplicitMethod => conversionResultType(info.resType) + case _ => + if info.derivesFrom(defn.ConversionClass) then + pt match + case selProto: SelectionProto => + // we want to avoid embedding a SelectionProto in a Conversion, as the result type + // as it might end up within a GADT cast type, e.g. tests/pos/i15867.scala + // so, if we can find the target result type - as in, + // if it matches the selection prototype, then let's adapt to that instead + // otherwise just skip adapting with a prototype (by returning NoType) + info.baseType(defn.ConversionClass) match + case AppliedType(_, List(_, restpe)) if selProto.isMatchedBy(restpe) => + restpe + case _ => NoType // can't find conversion result type, avoid adapting with SelectionProto + case _: ProtoType => NoType // avoid adapting with ProtoType + case _ => pt // not a ProtoType, so use it for adapting + else NoType // not a Conversion, don't adapt def tryConversion(using Context) = { + val restpeConv = if ref.symbol.is(Given) then conversionResultType(ref.widenTermRefExpr) else NoType val untpdConv = - if ref.symbol.is(Given) && producesConversion(ref.symbol.info) then + if restpeConv.exists then untpd.Select( untpd.TypedSplice( adapt(generated, - defn.ConversionClass.typeRef.appliedTo(argument.tpe, pt), + defn.ConversionClass.typeRef.appliedTo(argument.tpe, restpeConv), locked)), nme.apply) else untpdGenerated diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 81c0d3e35d3a..2ea2b045448f 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -94,6 +94,7 @@ i4176-gadt.scala # GADT difference i13974a.scala +i15867.scala java-inherited-type1 diff --git a/tests/pos/i15867.scala b/tests/pos/i15867.scala new file mode 100644 index 000000000000..2e62177ba590 --- /dev/null +++ b/tests/pos/i15867.scala @@ -0,0 +1,19 @@ +enum SUB[-A, +B]: + case Refl[S]() extends SUB[S, S] + +class Pow(self: Int): + def **(other: Int): Int = math.pow(self, other).toInt + +given fromList[T]: Conversion[List[T], Pow] = ??? + +given fromInt: Conversion[Int, Pow] = Pow(_) + +def foo[T](t1: T, ev: T SUB List[Int]) = + ev match { case SUB.Refl() => + t1 ** 2 // error + } + +def baz[T](t2: T, ev: T SUB Int) = + ev match { case SUB.Refl() => + t2 ** 2 // works + } diff --git a/tests/pos/i15867.specs2.scala b/tests/pos/i15867.specs2.scala new file mode 100644 index 000000000000..da89b2cba9f0 --- /dev/null +++ b/tests/pos/i15867.specs2.scala @@ -0,0 +1,9 @@ +class Foo: + given Conversion[String, Data] with + def apply(str: String): Data = new Data(str) + + class Data(str: String): + def |(str: String) = new Data(this.str + str) + +class Bar extends Foo: + "str" | "ing"