Skip to content

[CI only] Singleton constraint test #5704

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

Closed
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
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,13 @@ object Contexts {
}, gadts)
}

bound match {
case _: SkolemType =>
val TypeBounds(lo, hi) = bounds(sym)
return if (isUpper) lo <:< (hi & bound) else (lo | bound) <:< hi
case _ => ;
}

val symTvar: TypeVar = stripInternalTypeVar(tvar(sym)) match {
case tv: TypeVar => tv
case inst =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
* - If a type proxy P is not a reference to a class, P's supertype is in G
*/
def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean =
if (constrainPatternType(subtp, tp)) true
if (constrainPatternType(SkolemType(subtp), tp)) true
else tp match {
case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParent)
case tp: TypeProxy => isSubTypeOfParent(subtp, tp.superType)
Expand Down
10 changes: 7 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ class Typer extends Namer
def handlePattern: Tree = {
val tpt1 = typedTpt
if (!ctx.isAfterTyper && pt != defn.ImplicitScrutineeTypeRef)
constrainPatternType(tpt1.tpe, pt)(ctx.addMode(Mode.GADTflexible))
constrainPatternType(SkolemType(tpt1.tpe), pt)(ctx.addMode(Mode.GADTflexible))
// special case for an abstract type that comes with a class tag
tryWithClassTag(ascription(tpt1, isWildcard = true), pt)
}
Expand Down Expand Up @@ -1026,7 +1026,7 @@ class Typer extends Namer
val accu = new TypeAccumulator[Set[Symbol]] {
def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = {
val tsyms1 = t match {
case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 =>
case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm =>
tsyms + tr.symbol
case _ =>
tsyms
Expand All @@ -1041,7 +1041,11 @@ class Typer extends Namer
def gadtContext(gadtSyms: Set[Symbol])(implicit ctx: Context): Context = {
val gadtCtx = ctx.fresh.setFreshGADTBounds
for (sym <- gadtSyms)
if (!gadtCtx.gadt.contains(sym)) gadtCtx.gadt.addEmptyBounds(sym)
if (!gadtCtx.gadt.contains(sym)) {
val TypeBounds(lo, hi) = sym.info.bounds
gadtCtx.gadt.addBound(sym, lo, isUpper = false)
gadtCtx.gadt.addBound(sym, hi, isUpper = true)
}
gadtCtx
}

Expand Down
31 changes: 31 additions & 0 deletions tests/neg/int-extractor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
object Test {
object EssaInt {
def unapply(i: Int): Some[Int] = Some(i)
}

def foo1[T](t: T) = t match {
case EssaInt(_) =>
0 // error
}

def foo2[T](t: T) = t match {
case EssaInt(_) => t match {
case EssaInt(_) =>
0 // error
}
}

case class Inv[T](t: T)

def bar1[T](t: T) = Inv(t) match {
case Inv(EssaInt(_)) =>
0 // error
}

def bar2[T](t: T) = t match {
case Inv(EssaInt(_)) => t match {
case Inv(EssaInt(_)) =>
0 // error
}
}
}
27 changes: 27 additions & 0 deletions tests/neg/invariant-gadt.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
object `invariant-gadt` {
case class Invariant[T](value: T)

def unsound0[T](t: T): T = Invariant(t) match {
case Invariant(_: Int) =>
(0: Any) // error
}

def unsound1[T](t: T): T = Invariant(t) match {
case Invariant(_: Int) =>
0 // error
}

def unsound2[T](t: T): T = Invariant(t) match {
case Invariant(value) => value match {
case _: Int =>
0 // error
}
}

def unsoundTwice[T](t: T): T = Invariant(t) match {
case Invariant(_: Int) => Invariant(t) match {
case Invariant(_: Int) =>
0 // error
}
}
}
16 changes: 16 additions & 0 deletions tests/pos/precise-pattern-type.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
object `precise-pattern-type` {
class Type {
def isType: Boolean = true
}

class Tree[-T >: Null] {
def tpe: T @annotation.unchecked.uncheckedVariance = ???
}

case class Select[-T >: Null](qual: Tree[T]) extends Tree[T]

def test[T <: Tree[Type]](tree: T) = tree match {
case Select(q) =>
q.tpe.isType
}
}
58 changes: 42 additions & 16 deletions tests/run/typeclass-derivation2.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import scala.collection.mutable
import scala.annotation.tailrec

object _typelevel {
final abstract class Type[-A, +B]
type Subtype[t] = Type[_, t]
type Supertype[t] = Type[t, _]
type Exactly[t] = Type[t, t]
erased def typeOf[T]: Type[T, T] = ???
}

trait Deriving {
import Deriving._

Expand Down Expand Up @@ -177,6 +185,7 @@ trait Eq[T] {
object Eq {
import scala.typelevel._
import Deriving._
import _typelevel._

inline def tryEql[T](x: T, y: T) = implicit match {
case eq: Eq[T] => eq.eql(x, y)
Expand All @@ -197,14 +206,19 @@ object Eq {
inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean =
inline erasedValue[Alts] match {
case _: (Shape.Case[alt, elems] *: alts1) =>
x match {
case x: `alt` =>
y match {
case y: `alt` => eqlCase[T, elems](r, x, y)
case _ => false
inline typeOf[alt] match {
case _: Subtype[T] =>
x match {
case x: `alt` =>
y match {
case y: `alt` => eqlCase[T, elems](r, x, y)
case _ => false
}
case _ => eqlCases[T, alts1](r, x, y)
}
case _ => eqlCases[T, alts1](r, x, y)
}
case _ =>
error("invalid call to eqlCases: one of Alts is not a subtype of T")
}
case _: Unit =>
false
}
Expand Down Expand Up @@ -232,6 +246,7 @@ trait Pickler[T] {
object Pickler {
import scala.typelevel._
import Deriving._
import _typelevel._

def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1)

Expand All @@ -253,12 +268,17 @@ object Pickler {
inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit =
inline erasedValue[Alts] match {
case _: (Shape.Case[alt, elems] *: alts1) =>
x match {
case x: `alt` =>
buf += n
pickleCase[T, elems](r, buf, x)
inline typeOf[alt] match {
case _: Subtype[T] =>
x match {
case x: `alt` =>
buf += n
pickleCase[T, elems](r, buf, x)
case _ =>
pickleCases[T, alts1](r, buf, x, n + 1)
}
case _ =>
pickleCases[T, alts1](r, buf, x, n + 1)
error("invalid pickleCases call: one of Alts is not a subtype of T")
}
case _: Unit =>
}
Expand Down Expand Up @@ -323,6 +343,7 @@ trait Show[T] {
object Show {
import scala.typelevel._
import Deriving._
import _typelevel._

inline def tryShow[T](x: T): String = implicit match {
case s: Show[T] => s.show(x)
Expand All @@ -347,9 +368,14 @@ object Show {
inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String =
inline erasedValue[Alts] match {
case _: (Shape.Case[alt, elems] *: alts1) =>
x match {
case x: `alt` => showCase[T, elems](r, x)
case _ => showCases[T, alts1](r, x)
inline typeOf[alt] match {
case _: Subtype[T] =>
x match {
case x: `alt` => showCase[T, elems](r, x)
case _ => showCases[T, alts1](r, x)
}
case _ =>
error("invalid call to showCases: one of Alts is not a subtype of T")
}
case _: Unit =>
throw new MatchError(x)
Expand Down Expand Up @@ -424,4 +450,4 @@ object Test extends App {
println(implicitly[Show[T]].show(x))
showPrintln(xs)
showPrintln(xss)
}
}