Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix match type reduction with avoided types #18043

Merged
merged 7 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,18 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case tp1: MatchType =>
def compareMatch = tp2 match {
case tp2: MatchType =>
isSameType(tp1.scrutinee, tp2.scrutinee) &&
// we allow a small number of scrutinee types to be widened:
// * skolems, which may appear from type avoidance, but are widened in the inferred result type
// * inline proxies, which is inlining's solution to the same problem
def widenScrutinee(scrutinee1: Type) = scrutinee1 match
case tp: TermRef if tp.symbol.is(InlineProxy) => tp.info
case tp => tp.widenSkolem
def checkScrutinee(scrutinee1: Type): Boolean =
isSameType(scrutinee1, tp2.scrutinee) || {
val widenScrutinee1 = widenScrutinee(scrutinee1)
(widenScrutinee1 ne scrutinee1) && checkScrutinee(widenScrutinee1)
}
checkScrutinee(tp1.scrutinee) &&
tp1.cases.corresponds(tp2.cases)(isSubType)
case _ => false
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1373,7 +1373,7 @@ object Types {
Atoms.Range(set, set)
else Atoms.Unknown

dealias match
dealias.normalized match
case tp: SingletonType =>
tp.underlying.atoms match
case as @ Atoms.Range(lo, hi) =>
Expand Down
23 changes: 8 additions & 15 deletions compiler/src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -744,23 +744,16 @@ object TreeChecker {
override def adapt(tree: Tree, pt: Type, locked: TypeVars)(using Context): Tree = {
def isPrimaryConstructorReturn =
ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass)
def infoStr(tp: Type) = tp match {
case tp: TypeRef =>
val sym = tp.symbol
i"${sym.showLocated} with ${tp.designator}, flags = ${sym.flagsString}, underlying = ${tp.underlyingIterator.toList}%, %"
case _ =>
"??"
}
if (ctx.mode.isExpr &&
!tree.isEmpty &&
!isPrimaryConstructorReturn &&
!pt.isInstanceOf[FunOrPolyProto])
if ctx.mode.isExpr
&& !tree.isEmpty
&& !isPrimaryConstructorReturn
&& !pt.isInstanceOf[FunOrPolyProto]
then
assert(tree.tpe <:< pt, {
val mismatch = TypeMismatch(tree.tpe, pt, Some(tree))
i"""|${mismatch.msg}
|found: ${infoStr(tree.tpe)}
|expected: ${infoStr(pt)}
|tree = $tree""".stripMargin
i"""|Type Mismatch:
|${mismatch.message}
|tree = $tree ${tree.className}""".stripMargin
})
tree
}
Expand Down
24 changes: 24 additions & 0 deletions tests/neg/mt-scrutinee-widen.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// We widen scrutinee's that are inline proxies
// But make sure that term refs in scrutinees are not widened in general

val x: Int = 42
val y: Int = 43
val z: Int = 44

type IsX[T] =
T match
case x.type => true
case _ => false
def test = summon[IsX[y.type] =:= IsX[z.type]] // error

def test2 = summon[
(
y.type match
case x.type => true
case _ => false
) =:= (
z.type match
case x.type => true
case _ => false
)
] // error
13 changes: 13 additions & 0 deletions tests/neg/mt-scrutinee-widen2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// A test case showing how we shouldn't widen
// both IsX scrutinees and make "def test" typecheck
import scala.util.Random
val x = 42

type IsX[T] =
T match
case x.type => true
case _ => false

def bothXOrNot(a: Int, b: Int)(using IsX[a.type] =:= IsX[b.type]) = ???

def test = bothXOrNot(Random.nextInt(), Random.nextInt()) // error
14 changes: 14 additions & 0 deletions tests/neg/mt-subtyping-transitivity.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
final class A
final class B

type MT[X] = X match
case A => String
case B => Int

def test: MT[A | B] = ??? : MT[A] // error
// testing that
// MT[A] !<: MT[A | B]
// otherwise
// String <: MT[A] <: MT[A | B]
// but
// String !<: MT[A | B]
19 changes: 19 additions & 0 deletions tests/pos/16583.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import scala.compiletime.constValueTuple

val ll0: Tuple3["one", "two", "three"] = constValueTuple[("one", "two", "three")]
val ll1 = constValueTuple[("one", "two", "three")].toList
val ll3: List["one" | ("two" | ("three" | Nothing))] = constValueTuple[("one", "two", "three")].toList
val ll4: List["one" | ("two" | "three")] = constValueTuple[("one", "two", "three")].toList

inline def labels[Labels <: Tuple](using ev: Tuple.Union[Labels] <:< String): List[String] =
val tmp = constValueTuple[Labels].toList
ev.substituteCo(tmp)

def test = labels[("one", "two", "three")]

def toList(x: Tuple): List[Tuple.Union[x.type]] = ???
def test2[Labels <: Tuple] = toList((???): Labels)

def i16654 =
def t1: Tuple = EmptyTuple
val t2 = t1.toList
7 changes: 7 additions & 0 deletions tests/pos/16654.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
def toCsvFlat[A <: Product](a: A)(using m: scala.deriving.Mirror.ProductOf[A]) = {
def flatTuple(any: Any): Tuple = any match
case p: Product => p.productIterator.map(flatTuple).foldLeft(EmptyTuple: Tuple)(_ ++ _)
case a => Tuple1(a)

val tuple = flatTuple(Tuple.fromProductTyped(a)).toList
}
12 changes: 12 additions & 0 deletions tests/pos/mt-scrutinee-widen3.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Like widen2, but using a.type only, meaning it should typecheck
import scala.util.Random
val x = 42

type IsX[T] =
T match
case x.type => true
case _ => false

def bothXOrNot(a: Int, b: Int)(using IsX[a.type] =:= IsX[a.type]) = ???

def test = bothXOrNot(Random.nextInt(), Random.nextInt())
Loading