Skip to content

Fix #3015: exhaustivity check on top of native apply #3074

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
wants to merge 106 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
fb7ffe2
Introduce AppliedType
odersky Jul 21, 2017
a060095
Adapt operations in Types
odersky Jul 21, 2017
fb08d5f
Move givenSelfType to ClassSymbol
odersky Jul 22, 2017
dabeeca
Handle AppliedTypes
odersky Jul 23, 2017
a16f5fc
Introduce TypeArgRef
odersky Jul 26, 2017
14ef226
Various fixes
odersky Jul 28, 2017
0fdfd30
More fixes
odersky Jul 28, 2017
e4b9c30
Fix stray brace
odersky Jul 29, 2017
d563cdf
Fix typing of _* arguments
odersky Jul 30, 2017
9c3b90d
Fix rebase breakage
odersky Aug 16, 2017
70a6bcc
Fix debug output to make it more stable
odersky Aug 16, 2017
235ef90
Fix Stackoverflow in asSeenFrom
odersky Aug 16, 2017
62f612c
Make newScheme non-final
odersky Aug 16, 2017
69226cd
Partially revert change in TypeApplications#Reducer
odersky Aug 16, 2017
e675fee
Use atVariance for new cases in TypeMaps and TypeAccumulators
odersky Aug 16, 2017
684773a
Fix bounds propagation
odersky Aug 17, 2017
5d0fe46
Fix variance in avoidParams
odersky Aug 17, 2017
3f8ee89
Fix isSubArg
odersky Aug 17, 2017
b8f8b44
Avoid infinite expansion in normaizeWildcardArgs
odersky Aug 17, 2017
7e4f591
Handle parameters from base classes of package objects
odersky Aug 17, 2017
b9b1bcd
More fixes
odersky Aug 18, 2017
6cd44c8
Fix #536 again
odersky Aug 18, 2017
c0649b9
Adapt ClassTypeParamCreationFlags to new scheme
odersky Aug 18, 2017
08de872
Fix TypeArgRef and argForParam
odersky Aug 18, 2017
a225978
Add capture conversion
odersky Aug 18, 2017
306d36c
Fix ExpandSAMs
odersky Aug 18, 2017
68d6ddb
Fix implicit scope computation
odersky Aug 18, 2017
fa2d7dd
Refine typeMismatchMsg
odersky Aug 19, 2017
442047b
Generalize argForParam
odersky Aug 19, 2017
c53e432
Check that class type parameters are only referenced via their this-t…
odersky Aug 20, 2017
9b56fec
Check that class type parameters are only referenced via their this-t…
odersky Aug 20, 2017
78786e6
Fix illegal select in VCInlineMethods
odersky Aug 20, 2017
5ef7907
Fix classBound
odersky Aug 21, 2017
5bb3c6e
Fix t8280
odersky Aug 21, 2017
d7a871a
Adapt flip in Applications to new scheme
odersky Aug 21, 2017
2c123ed
Fix type of outer accessor
odersky Aug 21, 2017
ee66d9c
Fix computation of implicit scope
odersky Aug 22, 2017
cd100c1
Fix problem in isSubArg
odersky Aug 22, 2017
bbd8f35
Fix instantiatability checking
odersky Aug 23, 2017
f71be3a
Adapt tpd.ClassDef and tpd.AnonClass to new scheme
odersky Aug 23, 2017
6629f41
Change capture conversion
odersky Aug 23, 2017
e89ada9
Fix variances for wildcard arguments in TypeMaps and TypeAccumulators
odersky Aug 23, 2017
86a94b1
Avoid cyclic reference in normalizeWildcardArgs
odersky Aug 23, 2017
3c0e5b5
Fix SuperAccessors
odersky Aug 23, 2017
8fbd356
Fix possible hole in constraint handling
odersky Aug 24, 2017
17e977e
Fix
odersky Aug 24, 2017
a59dcde
Fix printing of TypeBounds
odersky Aug 24, 2017
48fb509
Refine Space#refine to handle AppliedTypes
odersky Aug 24, 2017
36d0938
Fix implicit selection for views
odersky Aug 25, 2017
2a3778f
Fix sigName for AppliedType
odersky Aug 26, 2017
86f05a0
Handle Java raw types in isSubType
odersky Aug 27, 2017
0375832
Don't check variances when comparing type lambdas in Scala2 mode
odersky Aug 27, 2017
484bacc
Don't try to simplify & / | types written as types
odersky Aug 27, 2017
3c6da89
Hash-cons applied types in their own table
odersky Aug 27, 2017
f989fe3
Make ParamRefs unique types.
odersky Aug 27, 2017
8453797
Refine statistics
odersky Aug 27, 2017
3cdb005
Make ParamRefs unique types.
odersky Aug 28, 2017
217aeb4
Make bound types be uniquely created by their binders
odersky Aug 28, 2017
947e5f1
More detailed stats
odersky Aug 29, 2017
858e7c0
Fix base type computation
odersky Aug 29, 2017
413c8cb
Fix tests
odersky Aug 29, 2017
ea065c1
Temporarily disable pattern-matching exhaustivity tests
odersky Aug 29, 2017
ed1d565
Temporarily weaken double definition check
odersky Aug 29, 2017
1eaa0ce
Temporarily existentials test to pending
odersky Aug 29, 2017
50ae64d
Ensure type correctness of repeated arguments
odersky Aug 30, 2017
04570bd
Update "good bounds" checks
odersky Aug 30, 2017
db8b355
Exclude mixin forwarders from double definition checks
odersky Aug 31, 2017
dd0eadc
Adapt superclass inference to new scheme
odersky Aug 31, 2017
62826be
Avoid inifinite loop when comparing & types with class types
odersky Aug 31, 2017
e13a7c7
Fix rebase breakage
odersky Aug 31, 2017
a25f6de
Handle TypeArgRefs in UserfacingPrinter
odersky Aug 31, 2017
fe0ccdd
Fix imports and add explanations in Space
odersky Aug 31, 2017
35015eb
Eliminate Config switches
odersky Aug 31, 2017
8dd5a69
Get rid of parentRefs and associated operations
odersky Aug 31, 2017
d76e72e
Re-normalize parents methods
odersky Aug 31, 2017
f3ddbb1
Fix rebase breakage, drop old UserfacingPrinter
odersky Aug 31, 2017
7f0efca
Use adaptHkVariances when comparing type arguments
odersky Aug 31, 2017
2851099
Re-apply change to printing TypeArgRefs in UserfacingPrinter
odersky Aug 31, 2017
738e321
Fix script check file
odersky Aug 31, 2017
33d0dc6
Drop HKApply
odersky Aug 31, 2017
ee7c3f9
Make baseTypeOf more robust
odersky Aug 31, 2017
bc776ae
Drop uniqueRefinedType and uniqueTypeAlias
odersky Aug 31, 2017
ba29e51
Drop variance in TypeAlias
odersky Aug 31, 2017
9552235
Fix equals for TypeAlias
odersky Aug 31, 2017
fa7551a
Adapt homogenizeArgs to new scheme
odersky Sep 1, 2017
445d9f4
Drop AnyAppliedType
odersky Sep 1, 2017
1aa05e0
Fix printing of infix types
odersky Sep 1, 2017
3440567
Drop TypeParamAccessor
odersky Sep 1, 2017
ba12831
Drop BaseTypeArg flag
odersky Sep 1, 2017
bd1ad22
Drop withoutArgs
odersky Sep 1, 2017
634378c
Drop ClassDenotation.appliedRef and ClassInfo.typeRef
odersky Sep 1, 2017
e2703d3
Reorder and clean up erasure and sigName
odersky Sep 1, 2017
5fed255
More cleanups and removals of now redundant code
odersky Sep 2, 2017
6491ef2
Better implementation of mapArgs
odersky Sep 2, 2017
933c677
Specialize hash-consing of WithFixedSym types
odersky Sep 3, 2017
656ba92
Avoid creating unnecessary new lists in mapArgs
odersky Sep 4, 2017
9aafa1d
Drop unused RefType and ClassRef
odersky Sep 6, 2017
778f4d1
Remove unused code
odersky Sep 6, 2017
c003228
Reverted: Refine Space#refine to handle AppliedTypes
odersky Sep 4, 2017
410d0cf
Disable exhaustivity test
odersky Sep 6, 2017
7b81aee
reenable exhaustivity check
liufengyun Sep 7, 2017
6e50ab3
fix #3015: use type inference to type child classes
liufengyun Sep 7, 2017
7a49c8c
make exhaustivity check work on native apply
liufengyun Sep 7, 2017
2953cab
change var to val
liufengyun Sep 7, 2017
3bd360c
remove uselss expose
liufengyun Sep 7, 2017
33efe0f
enable disabled test
liufengyun Sep 7, 2017
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
180 changes: 86 additions & 94 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -403,20 +403,31 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
else
Prod(pat.tpe.stripAnnots, fun.tpe.widen, fun.symbol, pats.map(project), irrefutable(fun))
case Typed(pat @ UnApply(_, _, _), _) => project(pat)
case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true)
case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true)
case _ =>
debug.println(s"unknown pattern: $pat")
Empty
}

/* Erase a type binding according to erasure semantics in pattern matching */
def erase(tp: Type): Type = tp match {
case tp@AppliedType(tycon, args) => erase(tp.superType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useless erase, this is thrown away

if (tycon.isRef(defn.ArrayClass)) tp.derivedAppliedType(tycon, args.map(erase))
else tp.derivedAppliedType(tycon, args.map(t => WildcardType(TypeBounds.empty)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just use the WildcardType object here.

case OrType(tp1, tp2) =>
OrType(erase(tp1), erase(tp2))
case AndType(tp1, tp2) =>
AndType(erase(tp1), erase(tp2))
case _ => tp
}

/** Space of the pattern: unapplySeq(a, b, c: _*)
*/
def projectSeq(pats: List[Tree]): Space = {
if (pats.isEmpty) return Typ(scalaNilType, false)

val (items, zero) = if (pats.last.tpe.isRepeatedParam)
(pats.init, Typ(scalaListType.appliedTo(pats.head.tpe.widen), false))
(pats.init, Typ(scalaListType.appliedTo(pats.last.tpe.argTypes.head), false))
else
(pats, Typ(scalaNilType, false))

Expand All @@ -428,41 +439,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}
}


/* Erase a type binding according to erasure semantics in pattern matching */
def erase(tp: Type): Type = {
def doErase(tp: Type): Type = tp match {
case tp: AppliedType => erase(tp.superType)
case tp: RefinedType => erase(tp.parent)
case _ => tp
}

tp match {
case OrType(tp1, tp2) =>
OrType(erase(tp1), erase(tp2))
case AndType(tp1, tp2) =>
AndType(erase(tp1), erase(tp2))
case _ =>
val origin = doErase(tp)
if (origin =:= defn.ArrayType) tp else origin
}
}

/** Is `tp1` a subtype of `tp2`? */
def isSubType(tp1: Type, tp2: Type): Boolean = {
// `erase` is a workaround to make the following code pass the check:
//
// def f(e: Either[Int, String]) = e match {
// case Left(i) => i
// case Right(s) => 0
// }
//
// The problem is that when decompose `Either[Int, String]`, `Type.wrapIfMember`
// only refines the type member inherited from `Either` -- it's complex to refine
// the type members in `Left` and `Right`.
//
// FIXME: remove this hack
val res = tp1 <:< erase(tp2)
val res = tp1 <:< tp2
debug.println(s"${tp1.show} <:< ${tp2.show} = $res")
res
}
Expand Down Expand Up @@ -563,15 +542,15 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {

val childTp = if (child.isTerm) child.termRef else child.typeRef

val resTp = instantiate(childTp, expose(parent))(ctx.fresh.setNewTyperState)
var resTp = instantiate(childTp, expose(parent))(ctx.fresh.setNewTyperState)

if (!resTp.exists) {
debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}")
NoType
}
else {
debug.println(s"$child instantiated ------> $resTp")
resTp
resTp.dealias
}
}

Expand All @@ -588,8 +567,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
// precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant
val thisTypeMap = new TypeMap {
def apply(t: Type): Type = t match {
case t @ ThisType(tref) if !tref.symbol.isStaticOwner && !tref.symbol.is(Module) =>
newTypeVar(TypeBounds.upper(mapOver(tref & tref.givenSelfType)))
case tp @ ThisType(tref) if !tref.symbol.isStaticOwner && !tref.symbol.is(Module) =>
// TODO: stackoverflow here
// newTypeVar(TypeBounds.upper(mapOver(tp.underlying)))
newTypeVar(TypeBounds.upper(mapOver(tref & tref.classSymbol.asClass.givenSelfType)))
case _ =>
mapOver(t)
}
Expand All @@ -598,10 +579,34 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
val protoTp1 = thisTypeMap(tp1.appliedTo(tvars))

if (protoTp1 <:< tp2 && isFullyDefined(protoTp1, ForceDegree.all)) protoTp1
// replace type parameter references with fresh type vars or bounds
val typeParamMap = new TypeMap {
def apply(t: Type): Type = t match {

case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] =>
// See tests/patmat/gadt.scala tests/patmat/exhausting.scala
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add: if tp.symbol.is(TypeParam) &&

val bound =
if (variance == 0) tp.underlying.bounds // non-variant case is not well-founded
else if (variance == 1) TypeBounds.upper(tp)
else TypeBounds.lower(tp)
newTypeVar(bound)
case tp: RefinedType if tp.refinedInfo.isInstanceOf[TypeBounds] =>
// Ideally, we would expect type inference to do the job
// Check tests/patmat/t9657.scala
expose(tp)
case _ =>
mapOver(t)
}
}

if (protoTp1 <:< tp2 && isFullyDefined(protoTp1, ForceDegree.noBottom)) protoTp1
else {
debug.println(s"$protoTp1 <:< $tp2 = false")
NoType
val protoTp2 = typeParamMap(tp2)
if (protoTp1 <:< protoTp2 && isFullyDefined(protoTp1 & protoTp2, ForceDegree.noBottom)) protoTp1
else {
debug.println(s"$protoTp1 <:< $protoTp2 = false")
NoType
}
}
}

Expand Down Expand Up @@ -652,6 +657,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {

def refine(tp: Type): String = tp match {
case tp: RefinedType => refine(tp.parent)
case tp: AppliedType => refine(tp.typeConstructor)
case tp: ThisType => refine(tp.tref)
case tp: NamedType =>
val pre = refinePrefix(tp.prefix)
Expand Down Expand Up @@ -740,64 +746,50 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic {
}


/** Expose refined type to eliminate reference to type variables
*
* A = B M { type T = A } ~~> M { type T = B }
*
* A <: X :> Y M { type T = A } ~~> M { type T <: X :> Y }
/** Eliminate reference to type parameters in refinements
*
* A <: X :> Y B <: U :> V M { type T <: A :> B } ~~> M { type T <: X :> V }
*
* A = X B = Y M { type T <: A :> B } ~~> M { type T <: X :> Y }
*/
def expose(tp: Type): Type = {
def follow(tp: Type, up: Boolean): Type = tp match {
case tp: TypeProxy =>
tp.underlying match {
case TypeBounds(lo, hi) =>
follow(if (up) hi else lo, up)
case _ =>
tp
}
case OrType(tp1, tp2) =>
OrType(follow(tp1, up), follow(tp2, up))
case AndType(tp1, tp2) =>
AndType(follow(tp1, up), follow(tp2, up))
}
def expose(tp: Type, refineCtx: Boolean = false, up: Boolean = true): Type = tp match {
case tp: AppliedType =>
tp.derivedAppliedType(expose(tp.tycon, refineCtx, up), tp.args.map(expose(_, refineCtx, up)))

tp match {
case tp: RefinedType =>
tp.refinedInfo match {
case tpa : TypeAlias =>
val hi = follow(tpa.alias, true)
val lo = follow(tpa.alias, false)
val refined = if (hi =:= lo)
tpa.derivedTypeAlias(hi)
else
tpa.derivedTypeBounds(lo, hi)

tp.derivedRefinedType(
expose(tp.parent),
tp.refinedName,
refined
)
case tpb @ TypeBounds(lo, hi) =>
tp.derivedRefinedType(
expose(tp.parent),
tp.refinedName,
tpb.derivedTypeBounds(follow(lo, false), follow(hi, true))
)
case _ =>
tp.derivedRefinedType(
expose(tp.parent),
tp.refinedName,
tp.refinedInfo
)
}
case _ => tp
}
case tp: TypeAlias =>
val hi = expose(tp.alias, refineCtx, up)
val lo = expose(tp.alias, refineCtx, up)

if (hi =:= lo)
tp.derivedTypeAlias(hi)
else
tp.derivedTypeBounds(lo, hi)

case tp @ TypeBounds(lo, hi) =>
tp.derivedTypeBounds(expose(lo, refineCtx, false), expose(hi, refineCtx, true))

case tp: RefinedType =>
tp.derivedRefinedType(
expose(tp.parent),
tp.refinedName,
expose(tp.refinedInfo, true, up)
)
case tp: TypeProxy if refineCtx =>
tp.underlying match {
case TypeBounds(lo, hi) =>
expose(if (up) hi else lo, refineCtx, up)
case _ =>
tp
}

case OrType(tp1, tp2) =>
OrType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up))

case AndType(tp1, tp2) =>
AndType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up))

case _ => tp
}


def checkExhaustivity(_match: Match): Unit = {
val Match(sel, cases) = _match
val selTyp = sel.tpe.widen.dealias
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class PatmatExhaustivityTest {
(file, checkContent, actual)
}

// @Test // TODO: reenable when exchaustivity is fixed
@Test
def patmatExhaustivity: Unit = {
val res = Directory(testsDir).list.toList
.filter(f => f.extension == "scala" || f.isDirectory)
Expand Down
2 changes: 1 addition & 1 deletion tests/patmat/andtype-opentype-interaction.check
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
31: Pattern Match Exhaustivity: _: Trait & OpenClass
35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass
43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass
47: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClassSubclass
47: Pattern Match Exhaustivity: _: Trait & OpenClass & OpenTrait & OpenClassSubclass
10 changes: 4 additions & 6 deletions tests/patmat/andtype-refinedtype-interaction.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
48: Pattern Match Exhaustivity: _: Clazz & (C1 | C2 | T1){x: Int} & (C3 | C4 | T2){x: Int}, _: Trait & (C1 | C2 | T1){x: Int} & (C3 | C4 | T2){x: Int}
54: Pattern Match Exhaustivity: _: Trait & (C1 | C2 | T1){x: Int} & C3{x: Int}
65: Pattern Match Exhaustivity: _: Trait & (C1 | C2){x: Int} & (C3 | SubC1){x: Int}
72: Pattern Match Exhaustivity: _: Trait & (T1 & C1 | T1 & SubC2){x: Int} &
(T2 & C2 | T2 & C3 | T2 & SubC1){x: Int}
& SubSubC1{x: Int}
79: Pattern Match Exhaustivity: _: Trait & (T1 & C1 | T1 & SubC2){x: Int} &
(T2 & C2 | T2 & C3 | T2 & SubC1){x: Int}
& SubSubC2{x: Int}
72: Pattern Match Exhaustivity: _: Trait & (T1 & (C1 | SubC2)){x: Int} & (T2 & (C2 | C3 | SubC1)){x: Int} &
SubSubC1{x: Int}
79: Pattern Match Exhaustivity: _: Trait & (T1 & (C1 | SubC2)){x: Int} & (T2 & (C2 | C3 | SubC1)){x: Int} &
SubSubC2{x: Int}
2 changes: 1 addition & 1 deletion tests/patmat/exhausting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ object Test {
sealed abstract class Foo[T]
case object Bar1 extends Foo[Int]
case object Bar2 extends Foo[String]
case object Bar3 extends Foo[Any]
case object Bar3 extends Foo[AnyRef]

def ex1[T](xs: List[T]) = xs match {
case ys: List[_] => "ok"
Expand Down
1 change: 1 addition & 0 deletions tests/patmat/partial-function.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
10: Pattern Match Exhaustivity: CC(_, B2)