Skip to content

Commit ad2553d

Browse files
authored
Merge pull request #13780 from dotty-staging/fix-11982-2
Don't follow BaseType of abstract binders in MT reduction
2 parents d5097d2 + c0ca8e9 commit ad2553d

File tree

10 files changed

+105
-10
lines changed

10 files changed

+105
-10
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
744744
}
745745

746746
def tryBaseType(cls2: Symbol) = {
747+
val allowBaseType = !caseLambda.exists || (tp1 match {
748+
case tp: TypeRef if tp.symbol.isClass => true
749+
case AppliedType(tycon: TypeRef, _) if tycon.symbol.isClass => true
750+
case _ => false
751+
})
747752
val base = nonExprBaseType(tp1, cls2)
748-
if (base.exists && (base `ne` tp1))
753+
if (base.exists && base.ne(tp1) && allowBaseType)
749754
isSubType(base, tp2, if (tp1.isRef(cls2)) approx else approx.addLow) ||
750755
base.isInstanceOf[OrType] && fourthTry
751756
// if base is a disjunction, this might have come from a tp1 type that
@@ -764,7 +769,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
764769
|| narrowGADTBounds(tp1, tp2, approx, isUpper = true))
765770
&& (tp2.isAny || GADTusage(tp1.symbol))
766771

767-
isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
772+
!caseLambda.exists && isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
768773
case _ =>
769774
// `Mode.RelaxedOverriding` is only enabled when checking Java overriding
770775
// in explicit nulls, and `Null` becomes a bottom type, which allows
@@ -2535,8 +2540,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
25352540
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
25362541
override def apply(x: Boolean, t: Type) =
25372542
x && {
2538-
t match {
2539-
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
2543+
t.dealias match {
2544+
case tp: TypeRef if !tp.symbol.isClass => false
25402545
case _: SkolemType | _: TypeVar | _: TypeParamRef => false
25412546
case _ => foldOver(x, t)
25422547
}

compiler/src/dotty/tools/dotc/typer/Implicits.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ trait Implicits:
11371137
/** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */
11381138
val wildProto: Type =
11391139
if argument.isEmpty then wildApprox(pt)
1140-
else ViewProto(wildApprox(argument.tpe.widen), wildApprox(pt))
1140+
else ViewProto(wildApprox(argument.tpe.widen.normalized), wildApprox(pt))
11411141
// Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
11421142

11431143
val isNotGiven: Boolean = wildProto.classSymbol == defn.NotGivenClass

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ class CompilationTests {
198198
@Test def runAll: Unit = {
199199
implicit val testGroup: TestGroup = TestGroup("runAll")
200200
aggregateTests(
201+
compileFile("tests/run-custom-args/typeclass-derivation1.scala", defaultOptions.without(yCheckOptions: _*)),
201202
compileFile("tests/run-custom-args/tuple-cons.scala", allowDeepSubtypes),
202203
compileFile("tests/run-custom-args/i5256.scala", allowDeepSubtypes),
203204
compileFile("tests/run-custom-args/fors.scala", defaultOptions.and("-source", "future")),

tests/neg/11982.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// testCompilation 11982.scala
2+
type Head[X] = X match {
3+
case Tuple2[a, b] => a
4+
}
5+
6+
object Unpair {
7+
def unpair[X <: Tuple2[Any, Any]]: Head[X] = 1 // error
8+
unpair[Tuple2["msg", 42]]: "msg"
9+
}
10+
11+
12+
type Head2[X] = X match {
13+
case Tuple2[Tuple2[a, b], Tuple2[c, d]] => a
14+
}
15+
16+
object Unpair2 {
17+
def unpair[X <: Tuple2[Tuple2[Any, Any], Tuple2[Any, Any]]]: Head2[X] = 1 // error
18+
unpair[Tuple2[Tuple2["msg", 42], Tuple2[41, 40]]]: "msg"
19+
}
20+
21+
22+
type Head3[X] = X match {
23+
case Tuple2[a, b] => a
24+
}
25+
26+
object Unpair3 {
27+
def unpair[X <: Tuple2[Any, Any]]: Head3[Tuple2[X, X]] = (1, 2) // error
28+
unpair[Tuple2["msg", 42]]: ("msg", 42)
29+
}
30+
31+
trait Foo[+A, +B]
32+
33+
type Head4[X] = X match {
34+
case Foo[Foo[a, b], Foo[c, d]] => a
35+
}
36+
37+
object Unpair4 {
38+
def unpair[X <: Foo[Any, Any]]: Head4[Foo[X, X]] = 1 // error
39+
unpair[Foo["msg", 42]]: "msg"
40+
}

tests/neg/6570-1.scala

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,32 @@ trait Root[A] {
2121

2222
class Asploder extends Root[Cov[Box[Int & String]]] {
2323
def thing = new Trait1 {} // error
24+
// ^
25+
// Found: Object with Trait1 {...}
26+
// Required: N[Box[Int & String]]
27+
//
28+
// Note: a match type could not be fully reduced:
29+
//
30+
// trying to reduce N[Box[Int & String]]
31+
// failed since selector Box[Int & String]
32+
// is uninhabited (there are no values of that type).
2433
}
2534

2635
object Main {
27-
def foo[T <: Cov[Box[Int]]](c: Root[T]): Trait2 = c.thing
36+
def foo[T <: Cov[Box[Int]]](c: Root[T]): Trait2 = c.thing // error
37+
// ^^^^^^^
38+
// Found: M[T]
39+
// Required: Trait2
40+
//
41+
// where: T is a type in method foo with bounds <: Cov[Box[Int]]
42+
//
43+
//
44+
// Note: a match type could not be fully reduced:
45+
//
46+
// trying to reduce M[T]
47+
// failed since selector T
48+
// does not match case Cov[x] => N[x]
49+
// and cannot be shown to be disjoint from it either.
2850

2951
def explode = foo(new Asploder)
3052

tests/neg/6570.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ object UpperBoundParametricVariant {
2121
trait Child[A <: Cov[Int]] extends Root[A]
2222

2323
// we reduce `M[T]` to `Trait2`, even though we cannot be certain of that
24-
def foo[T <: Cov[Int]](c: Child[T]): Trait2 = c.thing
24+
def foo[T <: Cov[Int]](c: Child[T]): Trait2 = c.thing // error
2525

2626
class Asploder extends Child[Cov[String & Int]] {
2727
def thing = new Trait1 {} // error
@@ -42,7 +42,7 @@ object InheritanceVariant {
4242

4343
trait Child extends Root { type B <: { type A <: Int } }
4444

45-
def foo(c: Child): Trait2 = c.thing
45+
def foo(c: Child): Trait2 = c.thing // error
4646

4747
class Asploder extends Child {
4848
type B = { type A = String & Int }
@@ -98,7 +98,7 @@ object UpperBoundVariant {
9898

9999
trait Child extends Root { type A <: Cov[Int] }
100100

101-
def foo(c: Child): Trait2 = c.thing
101+
def foo(c: Child): Trait2 = c.thing // error
102102

103103
class Asploder extends Child {
104104
type A = Cov[String & Int]

tests/pos/11982-a/119_1.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
object Unpair {
2+
class Inv[T]
3+
4+
type Head[X] = X match {
5+
case Tuple2[a, b] => a
6+
}
7+
8+
def unpair[X <: Tuple2[Any, Any]]: Head[X] = ???
9+
}

tests/pos/11982-a/119_2.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object UnpairApp {
2+
import Unpair._
3+
4+
val x: String = unpair[("msg", 42)]
5+
}

tests/pos/13491.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ object Rule {
8686
type RuleN[+L <: HList] = Rule[HNil, L]
8787

8888
def rule[I <: HList, O <: HList](r: Rule[I, O]): Rule[I, O] = ???
89-
implicit def valueMap[T](m: Map[String, T])(implicit h: HListable[T]): RuleN[h.Out] = ???
89+
90+
implicit def valueMap[T, Out0 <: HList](m: Map[String, T])(implicit h: HListable[T] { type Out = Out0 }): RuleN[Out0] = ???
9091
}
9192

9293
object Test {

tests/run/typeclass-derivation1.scala renamed to tests/run-custom-args/typeclass-derivation1.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,15 @@ object Test extends App {
9696
assert(!eq2.equals(yss, xss))
9797
assert(eq2.equals(yss, yss))
9898
}
99+
100+
// -Ycheck failure minimized to:
101+
// import scala.compiletime.*
102+
// object Eq {
103+
// inline def deriveForProduct[Elems <: Tuple](xs: Elems): Boolean = inline erasedValue[Elems] match {
104+
// case _: (elem *: elems1) =>
105+
// val xs1 = xs.asInstanceOf[elem *: elems1]
106+
// deriveForProduct(xs1.tail)
107+
// case _: EmptyTuple =>
108+
// true
109+
// }
110+
// }

0 commit comments

Comments
 (0)