Skip to content

Don't follow BaseType of abstract binders in MT reduction #13780

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

Merged
merged 2 commits into from
Apr 4, 2022
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: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
}

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

isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
!caseLambda.exists && isSubType(hi1, tp2, approx.addLow) || compareGADT || tryLiftedToThis1
case _ =>
// `Mode.RelaxedOverriding` is only enabled when checking Java overriding
// in explicit nulls, and `Null` becomes a bottom type, which allows
Expand Down Expand Up @@ -2535,8 +2540,8 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
def fullyInstantiated(tp: Type): Boolean = new TypeAccumulator[Boolean] {
override def apply(x: Boolean, t: Type) =
x && {
t match {
case tp: TypeRef if tp.symbol.isAbstractOrParamType => false
t.dealias match {
case tp: TypeRef if !tp.symbol.isClass => false
case _: SkolemType | _: TypeVar | _: TypeParamRef => false
case _ => foldOver(x, t)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Implicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ trait Implicits:
/** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */
val wildProto: Type =
if argument.isEmpty then wildApprox(pt)
else ViewProto(wildApprox(argument.tpe.widen), wildApprox(pt))
else ViewProto(wildApprox(argument.tpe.widen.normalized), wildApprox(pt))
// Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.

val isNotGiven: Boolean = wildProto.classSymbol == defn.NotGivenClass
Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotty/tools/dotc/CompilationTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class CompilationTests {
@Test def runAll: Unit = {
implicit val testGroup: TestGroup = TestGroup("runAll")
aggregateTests(
compileFile("tests/run-custom-args/typeclass-derivation1.scala", defaultOptions.without(yCheckOptions: _*)),
compileFile("tests/run-custom-args/tuple-cons.scala", allowDeepSubtypes),
compileFile("tests/run-custom-args/i5256.scala", allowDeepSubtypes),
compileFile("tests/run-custom-args/fors.scala", defaultOptions.and("-source", "future")),
Expand Down
40 changes: 40 additions & 0 deletions tests/neg/11982.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// testCompilation 11982.scala
type Head[X] = X match {
case Tuple2[a, b] => a
}

object Unpair {
def unpair[X <: Tuple2[Any, Any]]: Head[X] = 1 // error
unpair[Tuple2["msg", 42]]: "msg"
}


type Head2[X] = X match {
case Tuple2[Tuple2[a, b], Tuple2[c, d]] => a
}

object Unpair2 {
def unpair[X <: Tuple2[Tuple2[Any, Any], Tuple2[Any, Any]]]: Head2[X] = 1 // error
unpair[Tuple2[Tuple2["msg", 42], Tuple2[41, 40]]]: "msg"
}


type Head3[X] = X match {
case Tuple2[a, b] => a
}

object Unpair3 {
def unpair[X <: Tuple2[Any, Any]]: Head3[Tuple2[X, X]] = (1, 2) // error
unpair[Tuple2["msg", 42]]: ("msg", 42)
}

trait Foo[+A, +B]

type Head4[X] = X match {
case Foo[Foo[a, b], Foo[c, d]] => a
}

object Unpair4 {
def unpair[X <: Foo[Any, Any]]: Head4[Foo[X, X]] = 1 // error
unpair[Foo["msg", 42]]: "msg"
}
24 changes: 23 additions & 1 deletion tests/neg/6570-1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,32 @@ trait Root[A] {

class Asploder extends Root[Cov[Box[Int & String]]] {
def thing = new Trait1 {} // error
// ^
// Found: Object with Trait1 {...}
// Required: N[Box[Int & String]]
//
// Note: a match type could not be fully reduced:
//
// trying to reduce N[Box[Int & String]]
// failed since selector Box[Int & String]
// is uninhabited (there are no values of that type).
}

object Main {
def foo[T <: Cov[Box[Int]]](c: Root[T]): Trait2 = c.thing
def foo[T <: Cov[Box[Int]]](c: Root[T]): Trait2 = c.thing // error
// ^^^^^^^
// Found: M[T]
// Required: Trait2
//
// where: T is a type in method foo with bounds <: Cov[Box[Int]]
//
//
// Note: a match type could not be fully reduced:
//
// trying to reduce M[T]
// failed since selector T
// does not match case Cov[x] => N[x]
// and cannot be shown to be disjoint from it either.

def explode = foo(new Asploder)

Expand Down
6 changes: 3 additions & 3 deletions tests/neg/6570.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object UpperBoundParametricVariant {
trait Child[A <: Cov[Int]] extends Root[A]

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

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

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

def foo(c: Child): Trait2 = c.thing
def foo(c: Child): Trait2 = c.thing // error

class Asploder extends Child {
type B = { type A = String & Int }
Expand Down Expand Up @@ -98,7 +98,7 @@ object UpperBoundVariant {

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

def foo(c: Child): Trait2 = c.thing
def foo(c: Child): Trait2 = c.thing // error

class Asploder extends Child {
type A = Cov[String & Int]
Expand Down
9 changes: 9 additions & 0 deletions tests/pos/11982-a/119_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
object Unpair {
class Inv[T]

type Head[X] = X match {
case Tuple2[a, b] => a
}

def unpair[X <: Tuple2[Any, Any]]: Head[X] = ???
}
5 changes: 5 additions & 0 deletions tests/pos/11982-a/119_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object UnpairApp {
import Unpair._

val x: String = unpair[("msg", 42)]
}
3 changes: 2 additions & 1 deletion tests/pos/13491.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ object Rule {
type RuleN[+L <: HList] = Rule[HNil, L]

def rule[I <: HList, O <: HList](r: Rule[I, O]): Rule[I, O] = ???
implicit def valueMap[T](m: Map[String, T])(implicit h: HListable[T]): RuleN[h.Out] = ???

implicit def valueMap[T, Out0 <: HList](m: Map[String, T])(implicit h: HListable[T] { type Out = Out0 }): RuleN[Out0] = ???
}

object Test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,15 @@ object Test extends App {
assert(!eq2.equals(yss, xss))
assert(eq2.equals(yss, yss))
}

// -Ycheck failure minimized to:
// import scala.compiletime.*
// object Eq {
// inline def deriveForProduct[Elems <: Tuple](xs: Elems): Boolean = inline erasedValue[Elems] match {
// case _: (elem *: elems1) =>
// val xs1 = xs.asInstanceOf[elem *: elems1]
// deriveForProduct(xs1.tail)
// case _: EmptyTuple =>
// true
// }
// }