From 4e2b2847ded42a3c90be24675f85d89dee05cea8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 21 Sep 2014 18:19:09 +0200 Subject: [PATCH] Fix problem in unapply typing. GADT bound resetting may only be applied when comparing <: , not when comparing the other way around. The fix revealed an error in a test case (t1048) which looks like a real error. Therefore the test got moved to neg. --- src/dotty/tools/dotc/core/TypeComparer.scala | 8 ++++--- src/dotty/tools/dotc/typer/Applications.scala | 4 ++-- src/dotty/tools/dotc/typer/Mode.scala | 3 +++ test/dotc/tests.scala | 1 + tests/neg/t1048.scala | 21 +++++++++++++++++++ tests/pos/t1048.scala | 14 ------------- 6 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 tests/neg/t1048.scala delete mode 100644 tests/pos/t1048.scala diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1e1d02be2fd5..9b4a892f934c 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -362,7 +362,7 @@ class TypeComparer(initctx: Context) extends DotClass { private def traceInfo(tp1: Type, tp2: Type) = s"${tp1.show} <:< ${tp2.show}" + - (if (ctx.settings.verbose.value) s"${tp1.getClass} ${tp2.getClass}" else "") + (if (ctx.settings.verbose.value) s" ${tp1.getClass} ${tp2.getClass}${if (frozenConstraint) " frozen" else ""}" else "") def isSubType(tp1: Type, tp2: Type): Boolean = /*>|>*/ ctx.traceIndented(s"isSubType ${traceInfo(tp1, tp2)}", subtyping) /*<|<*/ { if (tp2 eq NoType) false @@ -589,7 +589,8 @@ class TypeComparer(initctx: Context) extends DotClass { NamedType(tp1.prefix, tp1.name, sd.derivedSingleDenotation(sd.symbol, tp)) secondTry(OrType.make(derivedRef(tp11), derivedRef(tp12)), tp2) case TypeBounds(lo1, hi1) => - if ((tp1.symbol is GADTFlexType) && !isSubTypeWhenFrozen(hi1, tp2)) + if ((ctx.mode is Mode.GADTflexible) && (tp1.symbol is GADTFlexType) && + !isSubTypeWhenFrozen(hi1, tp2)) trySetType(tp1, TypeBounds(lo1, hi1 & tp2)) else if (lo1 eq hi1) isSubType(hi1, tp2) else tryRebase2nd @@ -607,7 +608,8 @@ class TypeComparer(initctx: Context) extends DotClass { } def compareNamed: Boolean = tp2.info match { case TypeBounds(lo2, hi2) => - if ((tp2.symbol is GADTFlexType) && !isSubTypeWhenFrozen(tp1, lo2)) + if ((ctx.mode is Mode.GADTflexible) && (tp2.symbol is GADTFlexType) && + !isSubTypeWhenFrozen(tp1, lo2)) trySetType(tp2, TypeBounds(lo2 | tp1, hi2)) else ((frozenConstraint || !isCappable(tp1)) && isSubType(tp1, lo2) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 242985b57ffc..0d6c0ab801b6 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -623,7 +623,7 @@ trait Applications extends Compatibility { self: Typer => /** Can `subtp` be made to be a subtype of `tp`, possibly by dropping some * refinements in `tp`? */ - def isSubTypeOfParent(subtp: Type, tp: Type): Boolean = + def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean = if (subtp <:< tp) true else tp match { case RefinedType(parent, _) => isSubTypeOfParent(subtp, parent) @@ -640,7 +640,7 @@ trait Applications extends Compatibility { self: Typer => fullyDefinedType(unapplyArgType, "extractor argument", tree.pos) unapp.println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}") pt - } else if (isSubTypeOfParent(unapplyArgType, wpt)) { + } else if (isSubTypeOfParent(unapplyArgType, wpt)(ctx.addMode(Mode.GADTflexible))) { maximizeType(unapplyArgType) match { case Some(tvar) => def msg = diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index 55baa6bc5ea2..73e94bcbd2f8 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -34,5 +34,8 @@ object Mode { val TypevarsMissContext = newMode(4, "TypevarsMissContext") val InSuperCall = newMode(5, "InSuperCall") + /** Allow GADTFlexType labelled types to have their bounds adjusted */ + val GADTflexible = newMode(8, "GADTflexible") + val PatternOrType = Pattern | Type } \ No newline at end of file diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index fa577573a2af..01c3e6f69a15 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -84,6 +84,7 @@ class tests extends CompilerTest { @Test def neg_tailcall = compileFile(negDir, "tailcall/tailrec", xerrors = 7) @Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2) @Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2) + @Test def neg_t1048 = compileFile(negDir, "t1048", xerrors = 1) @Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1) @Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1) @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2) diff --git a/tests/neg/t1048.scala b/tests/neg/t1048.scala new file mode 100644 index 000000000000..4b8e78b4cd85 --- /dev/null +++ b/tests/neg/t1048.scala @@ -0,0 +1,21 @@ +trait T[U] { + def x: T[_ <: U] +} + +object T { + def unapply[U](t: T[U]): Option[T[_ <: U]] = Some(t.x) +} + +object Test { + def f[W](t: T[W]) = t match { + case T(T(_)) => () +// Gives: +// t1048.scala:11: error: There is no best instantiation of pattern type T[Any'] +// that makes it a subtype of selector type T[_ <: W]. +// Non-variant type variable U cannot be uniquely instantiated. +// case T(T(_)) => () +// ^ +// one error found + } +} + diff --git a/tests/pos/t1048.scala b/tests/pos/t1048.scala deleted file mode 100644 index b8694b38e694..000000000000 --- a/tests/pos/t1048.scala +++ /dev/null @@ -1,14 +0,0 @@ -trait T[U] { - def x: T[_ <: U] -} - -object T { - def unapply[U](t: T[U]): Option[T[_ <: U]] = Some(t.x) -} - -object Test { - def f[W](t: T[W]) = t match { - case T(T(_)) => () - } -} -