From 212ff60ba7a7a641449f6b6b5d8e5ccfd6efdcb8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 16 Feb 2021 19:09:17 +0100 Subject: [PATCH] More robust comparison of type constructors in provablyDisjoint A type comparison with `==` should be used only for exploring opportunities to optimize, never where it affects the logic. I.e. we need to always have a fallback that checks via `=:=`. Fixes #11393 --- compiler/src/dotty/tools/dotc/config/Printers.scala | 1 + compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 9 +++++---- compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++-- tests/pos/i11393/Format_1.scala | 7 +++++++ tests/pos/i11393/Test_2.scala | 5 +++++ 5 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i11393/Format_1.scala create mode 100644 tests/pos/i11393/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 8f9e480e0330..27391154591f 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -31,6 +31,7 @@ object Printers { val init = noPrinter val inlining = noPrinter val interactiv = noPrinter + val matchTypes = noPrinter val nullables = noPrinter val overload = noPrinter val patmatch = noPrinter diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 91f5c2258ea7..8bf13b9c6f2b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -11,7 +11,7 @@ import collection.mutable import util.Stats import config.Config import config.Feature.migrateTo3 -import config.Printers.{constr, subtyping, gadts, noPrinter} +import config.Printers.{constr, subtyping, gadts, matchTypes, noPrinter} import TypeErasure.{erasedLub, erasedGlb} import TypeApplications._ import Variances.{Variance, variancesConform} @@ -2408,7 +2408,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling * property that in all possible contexts, the same match type expression * is either stuck or reduces to the same case. */ - def provablyDisjoint(tp1: Type, tp2: Type)(using Context): Boolean = { + def provablyDisjoint(tp1: Type, tp2: Type)(using Context): Boolean = trace(i"provable disjoint $tp1, $tp2", matchTypes) { // println(s"provablyDisjoint(${tp1.show}, ${tp2.show})") def isEnumValueOrModule(ref: TermRef): Boolean = @@ -2452,7 +2452,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling decompose(cls2, tp2).forall(x => provablyDisjoint(x, tp1)) else false - case (AppliedType(tycon1, args1), AppliedType(tycon2, args2)) if tycon1 == tycon2 => + case (AppliedType(tycon1, args1), AppliedType(tycon2, args2)) + if tycon1.typeSymbol == tycon2.typeSymbol && tycon1 =:= tycon2 => // It is possible to conclude that two types applies are disjoint by // looking at covariant type parameters if the said type parameters // are disjoin and correspond to fields. @@ -2768,7 +2769,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { * None if the match fails and we should consider the following cases * because scrutinee and pattern do not overlap */ - def matchCase(cas: Type): Option[Type] = { + def matchCase(cas: Type): Option[Type] = trace(i"match case $cas vs $scrut", matchTypes) { val cas1 = cas match { case cas: HKTypeLambda => caseLambda = constrained(cas) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 971267112723..068330b52a31 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -33,7 +33,7 @@ import config.Config import annotation.{tailrec, constructorOnly} import language.implicitConversions import scala.util.hashing.{ MurmurHash3 => hashing } -import config.Printers.{core, typr} +import config.Printers.{core, typr, matchTypes} import reporting.{trace, Message} import java.lang.ref.WeakReference @@ -4489,7 +4489,7 @@ object Types { record("MatchType.reduce computed") if (myReduced != null) record("MatchType.reduce cache miss") myReduced = - trace(i"reduce match type $this $hashCode", typr, show = true) { + trace(i"reduce match type $this $hashCode", matchTypes, show = true) { def matchCases(cmp: TrackingTypeComparer): Type = try cmp.matchCases(scrutinee.normalized, cases) catch case ex: Throwable => diff --git a/tests/pos/i11393/Format_1.scala b/tests/pos/i11393/Format_1.scala new file mode 100644 index 000000000000..22abde4ccdd1 --- /dev/null +++ b/tests/pos/i11393/Format_1.scala @@ -0,0 +1,7 @@ +object Formatt: + type ToFormat[X <: Tuple] = X match + case EmptyTuple => String + case '%' *: 's' *: ts => (String => ToFormat[ts]) + case Char *: ts => ToFormat[ts] + + diff --git a/tests/pos/i11393/Test_2.scala b/tests/pos/i11393/Test_2.scala new file mode 100644 index 000000000000..9b4fed33a1af --- /dev/null +++ b/tests/pos/i11393/Test_2.scala @@ -0,0 +1,5 @@ +@main def hello: Unit = { + val x: Formatt.ToFormat['a' *: EmptyTuple] = "" + + +}