-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Closed
Changes from 1 commit
Commits
Show all changes
106 commits
Select commit
Hold shift + click to select a range
fb7ffe2
Introduce AppliedType
odersky a060095
Adapt operations in Types
odersky fb08d5f
Move givenSelfType to ClassSymbol
odersky dabeeca
Handle AppliedTypes
odersky a16f5fc
Introduce TypeArgRef
odersky 14ef226
Various fixes
odersky 0fdfd30
More fixes
odersky e4b9c30
Fix stray brace
odersky d563cdf
Fix typing of _* arguments
odersky 9c3b90d
Fix rebase breakage
odersky 70a6bcc
Fix debug output to make it more stable
odersky 235ef90
Fix Stackoverflow in asSeenFrom
odersky 62f612c
Make newScheme non-final
odersky 69226cd
Partially revert change in TypeApplications#Reducer
odersky e675fee
Use atVariance for new cases in TypeMaps and TypeAccumulators
odersky 684773a
Fix bounds propagation
odersky 5d0fe46
Fix variance in avoidParams
odersky 3f8ee89
Fix isSubArg
odersky b8f8b44
Avoid infinite expansion in normaizeWildcardArgs
odersky 7e4f591
Handle parameters from base classes of package objects
odersky b9b1bcd
More fixes
odersky 6cd44c8
Fix #536 again
odersky c0649b9
Adapt ClassTypeParamCreationFlags to new scheme
odersky 08de872
Fix TypeArgRef and argForParam
odersky a225978
Add capture conversion
odersky 306d36c
Fix ExpandSAMs
odersky 68d6ddb
Fix implicit scope computation
odersky fa2d7dd
Refine typeMismatchMsg
odersky 442047b
Generalize argForParam
odersky c53e432
Check that class type parameters are only referenced via their this-t…
odersky 9b56fec
Check that class type parameters are only referenced via their this-t…
odersky 78786e6
Fix illegal select in VCInlineMethods
odersky 5ef7907
Fix classBound
odersky 5bb3c6e
Fix t8280
odersky d7a871a
Adapt flip in Applications to new scheme
odersky 2c123ed
Fix type of outer accessor
odersky ee66d9c
Fix computation of implicit scope
odersky cd100c1
Fix problem in isSubArg
odersky bbd8f35
Fix instantiatability checking
odersky f71be3a
Adapt tpd.ClassDef and tpd.AnonClass to new scheme
odersky 6629f41
Change capture conversion
odersky e89ada9
Fix variances for wildcard arguments in TypeMaps and TypeAccumulators
odersky 86a94b1
Avoid cyclic reference in normalizeWildcardArgs
odersky 3c0e5b5
Fix SuperAccessors
odersky 8fbd356
Fix possible hole in constraint handling
odersky 17e977e
Fix
odersky a59dcde
Fix printing of TypeBounds
odersky 48fb509
Refine Space#refine to handle AppliedTypes
odersky 36d0938
Fix implicit selection for views
odersky 2a3778f
Fix sigName for AppliedType
odersky 86f05a0
Handle Java raw types in isSubType
odersky 0375832
Don't check variances when comparing type lambdas in Scala2 mode
odersky 484bacc
Don't try to simplify & / | types written as types
odersky 3c6da89
Hash-cons applied types in their own table
odersky f989fe3
Make ParamRefs unique types.
odersky 8453797
Refine statistics
odersky 3cdb005
Make ParamRefs unique types.
odersky 217aeb4
Make bound types be uniquely created by their binders
odersky 947e5f1
More detailed stats
odersky 858e7c0
Fix base type computation
odersky 413c8cb
Fix tests
odersky ea065c1
Temporarily disable pattern-matching exhaustivity tests
odersky ed1d565
Temporarily weaken double definition check
odersky 1eaa0ce
Temporarily existentials test to pending
odersky 50ae64d
Ensure type correctness of repeated arguments
odersky 04570bd
Update "good bounds" checks
odersky db8b355
Exclude mixin forwarders from double definition checks
odersky dd0eadc
Adapt superclass inference to new scheme
odersky 62826be
Avoid inifinite loop when comparing & types with class types
odersky e13a7c7
Fix rebase breakage
odersky a25f6de
Handle TypeArgRefs in UserfacingPrinter
odersky fe0ccdd
Fix imports and add explanations in Space
odersky 35015eb
Eliminate Config switches
odersky 8dd5a69
Get rid of parentRefs and associated operations
odersky d76e72e
Re-normalize parents methods
odersky f3ddbb1
Fix rebase breakage, drop old UserfacingPrinter
odersky 7f0efca
Use adaptHkVariances when comparing type arguments
odersky 2851099
Re-apply change to printing TypeArgRefs in UserfacingPrinter
odersky 738e321
Fix script check file
odersky 33d0dc6
Drop HKApply
odersky ee7c3f9
Make baseTypeOf more robust
odersky bc776ae
Drop uniqueRefinedType and uniqueTypeAlias
odersky ba29e51
Drop variance in TypeAlias
odersky 9552235
Fix equals for TypeAlias
odersky fa7551a
Adapt homogenizeArgs to new scheme
odersky 445d9f4
Drop AnyAppliedType
odersky 1aa05e0
Fix printing of infix types
odersky 3440567
Drop TypeParamAccessor
odersky ba12831
Drop BaseTypeArg flag
odersky bd1ad22
Drop withoutArgs
odersky 634378c
Drop ClassDenotation.appliedRef and ClassInfo.typeRef
odersky e2703d3
Reorder and clean up erasure and sigName
odersky 5fed255
More cleanups and removals of now redundant code
odersky 6491ef2
Better implementation of mapArgs
odersky 933c677
Specialize hash-consing of WithFixedSym types
odersky 656ba92
Avoid creating unnecessary new lists in mapArgs
odersky 9aafa1d
Drop unused RefType and ClassRef
odersky 778f4d1
Remove unused code
odersky c003228
Reverted: Refine Space#refine to handle AppliedTypes
odersky 410d0cf
Disable exhaustivity test
odersky 7b81aee
reenable exhaustivity check
liufengyun 6e50ab3
fix #3015: use type inference to type child classes
liufengyun 7a49c8c
make exhaustivity check work on native apply
liufengyun 2953cab
change var to val
liufengyun 3bd360c
remove uselss expose
liufengyun 33efe0f
enable disabled test
liufengyun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
if (tycon.isRef(defn.ArrayClass)) tp.derivedAppliedType(tycon, args.map(erase)) | ||
else tp.derivedAppliedType(tycon, args.map(t => WildcardType(TypeBounds.empty))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) | ||
|
||
|
@@ -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 | ||
} | ||
|
@@ -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 | ||
} | ||
} | ||
|
||
|
@@ -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) | ||
} | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
} | ||
} | ||
} | ||
|
||
|
@@ -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) | ||
|
@@ -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 | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
10: Pattern Match Exhaustivity: CC(_, B2) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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