Skip to content

Commit

Permalink
Refine constraining scheme for result types
Browse files Browse the repository at this point in the history
 - Take MatchTypes into account
 - Add test cases for tests that failed originally and would otherwise fail again
   after this PR. i19415.scala is fixed by the MatchType extension. i19749.scala was
   fixed by adding a `transparent`.
  • Loading branch information
odersky committed Jun 11, 2024
1 parent 07e9c40 commit 6e8bea7
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 2 deletions.
11 changes: 9 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,16 @@ object ProtoTypes {

constFoldException(pt) || {
if Inlines.isInlineable(meth) then
// Stricter behaviour in 3.4+: do not apply `wildApprox` to non-transparent inlines
// Stricter behavisour in 3.4+: do not apply `wildApprox` to non-transparent inlines
// unless their return type is a MatchType. In this case there's no reason
// not to constrain type variables in the expected type. For transparent inlines
// we do not want to constrain type variables in the expected type since the
// actual return type might be smaller after instantiation. For inlines returning
// MatchTypes we do not want to constrain because the MatchType might be more
// specific after instantiation. TODO: Should we also use Wildcards for non-inline
// methods returning MatchTypes?
if Feature.sourceVersion.isAtLeast(SourceVersion.`3.4`) then
if meth.is(Transparent) then
if meth.is(Transparent) || mt.resultType.isMatchAlias then
constrainResult(mt, wildApprox(pt))
// do not constrain the result type of transparent inline methods
true
Expand Down
24 changes: 24 additions & 0 deletions tests/pos/i19415.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def Test = {
val left: Parser[String] = ???
val right: Parser[Int] = ???
val both = left && right

val works = both.map(Ior.Both.apply)
val fails = (left && right).map(Ior.Both.apply)
}

trait Parser[T]:
final def &&[T2](other: Parser[T2])(implicit zip: Zip[T, T2]): Parser[zip.Out] = ???
final def map[T2](f: T => T2): Parser[T2] = ???

infix trait Ior[+A, +B]
object Ior:
final case class Both[+A, +B](a: A, b: B) extends (A Ior B)

trait Zip[In1, In2]:
type Out

object Zip {
type Out[In1, In2, O] = Zip[In1, In2] { type Out = O }
implicit def zip2[_1, _2]: Zip.Out[_1, _2, (_1, _2)] = ???
}
54 changes: 54 additions & 0 deletions tests/pos/i19479.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
case class Person(id: Int)

class GeodeContinuousSourceSpec {
summon[PdxEncoder[Person]]
}

trait PdxEncoder[A] {
def encode(a: A): Boolean
}

object PdxEncoder extends ObjectEncoder {
implicit def intEncoder: PdxEncoder[Int] = ???
}

trait ObjectEncoder {
given emptyTupleEncoder: PdxEncoder[EmptyTuple] = ???

given tupleEncoder[K <: String, H, T <: Tuple](using
m: ValueOf[K],
hEncoder: PdxEncoder[H],
tEncoder: PdxEncoder[T]
): PdxEncoder[FieldType[K, H] *: T] = ???

given objectEncoder[A, Repr <: Tuple](using
gen: LabelledGeneric.Aux[A, Repr],
tupleEncoder: PdxEncoder[Repr]
): PdxEncoder[A] = ???
}

import scala.deriving.Mirror

private type FieldType[K, +V] = V & KeyTag[K, V]
private type KeyTag[K, +V]
private type ZipWith[T1 <: Tuple, T2 <: Tuple, F[_, _]] <: Tuple = (T1, T2) match {
case (h1 *: t1, h2 *: t2) => F[h1, h2] *: ZipWith[t1, t2, F]
case (EmptyTuple, ?) => EmptyTuple
case (?, EmptyTuple) => EmptyTuple
case _ => Tuple
}

private trait LabelledGeneric[A] {
type Repr
}

private object LabelledGeneric {
type Aux[A, R] = LabelledGeneric[A] { type Repr = R }

transparent inline given productInst[A <: Product](using
m: Mirror.ProductOf[A]
): LabelledGeneric.Aux[A, ZipWith[m.MirroredElemLabels, m.MirroredElemTypes, FieldType]] =
new LabelledGeneric[A] {
type Repr = Tuple & ZipWith[m.MirroredElemLabels, m.MirroredElemTypes, FieldType]
}
}

0 comments on commit 6e8bea7

Please sign in to comment.