Skip to content

Commit 8c0f444

Browse files
committed
SI-5022 Retain precise existentials through pattern matching
From the dawn of scalac's existentials, the typer widens existentials pt-s by substituting wildcard types in places of existential quantifiers. In this example: class ForSomeVsUnapply { def test { def makeWrap: Wrap = ??? def useRep[e](rep: (e, X[e])) = () val rep = makeWrap match { case Wrap(r) => r }; useRep(rep) // error } } the type of `r` is the result of typechecking: Apply( fun = TypeTree( tpe = (rep#12037: (e#12038, X#7041[e#12038]) forSome { type e#12038 }) args = Bind(r @ _) :: Nil } This descends to type the `Bind` with: pt = (e#12038, X#7041[e#12038]) forSome { type e#12038 } `dropExistential` clobbers that type to `Tuple2#1540[?, X#7041[?]]`, which doesn't express any relationship between the two instances of the wildcard type. `typedIdent` sort of reverses this with a call to `makeFullyDefined`, but only ends up with: pt = (Any#3330, X#7041[_1#12227]) forSome { type _1#12227; type e#12038 } I suspect that this existential dropping only makes sense outside of typechecking patterns. In pattern mode, type information flows from the expected type onwards to the body of the case; we must not lose precision in the types. For SIP-18 friendly existentials, one `dropExistential` is invertable with `makeFullyDefined`, so this hasn't been such a big problem. The error message improvement conferred by SI-4515 took a hit. That might be a good example to consider when reviewing this change: Does it tell us anything interesting about this `dropExistential` business?
1 parent d70c0e3 commit 8c0f444

File tree

4 files changed

+42
-4
lines changed

4 files changed

+42
-4
lines changed

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5108,7 +5108,7 @@ trait Typers extends Adaptations with Tags {
51085108
if (mode.inPatternMode) {
51095109
val uncheckedTypeExtractor = extractorForUncheckedType(tpt.pos, tptTyped.tpe)
51105110
// make fully defined to avoid bounded wildcard types that may be in pt from calling dropExistential (SI-2038)
5111-
val ptDefined = ensureFullyDefined(pt)
5111+
val ptDefined = ensureFullyDefined(pt) // FIXME this is probably redundant now that we don't dropExistenial in pattern mode.
51125112
val ownType = inferTypedPattern(tptTyped, tptTyped.tpe, ptDefined, canRemedy = uncheckedTypeExtractor.nonEmpty)
51135113
treeTyped setType ownType
51145114

@@ -5342,7 +5342,12 @@ trait Typers extends Adaptations with Tags {
53425342
"context.owner" -> context.owner
53435343
)
53445344
)
5345-
typed1(tree, mode, dropExistential(ptPlugins))
5345+
val ptWild = if (mode.inPatternMode)
5346+
ptPlugins // SI-5022 don't widen pt for patterns as types flow from it to the case body.
5347+
else
5348+
dropExistential(ptPlugins) // FIXME: document why this is done.
5349+
5350+
typed1(tree, mode, ptWild)
53465351
}
53475352
// Can happen during erroneous compilation - error(s) have been
53485353
// reported, but we need to avoid causing an NPE with this tree

test/files/neg/t4515.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
t4515.scala:37: error: type mismatch;
2-
found : _0(in value $anonfun) where type _0(in value $anonfun)
3-
required: (some other)_0(in value $anonfun)
2+
found : _$1 where type _$1
3+
required: _$2
44
handler.onEvent(target, ctx.getEvent, node, ctx)
55
^
66
one error found

test/files/pos/t2613.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import language.existentials
2+
3+
object Test {
4+
class Row
5+
6+
abstract class MyRelation [R <: Row, +Relation <: MyRelation[R, Relation]]
7+
8+
type M = MyRelation[R, Relation] forSome {type R <: Row; type Relation <: MyRelation[R, Relation]}
9+
10+
var (x,y): (String, M) = null
11+
}

test/files/pos/t5022.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class ForSomeVsUnapply {
2+
def test {
3+
def makeWrap: Wrap = ???
4+
def useRep[e](rep: (e, X[e])) = ()
5+
6+
val repUnapply = Wrap.unapply(makeWrap).get
7+
useRep(repUnapply) // okay
8+
9+
val Wrap(rep0) = makeWrap
10+
useRep(rep0) // error
11+
12+
val rep = makeWrap match {
13+
case Wrap(r) => r
14+
};
15+
16+
useRep(rep) // error
17+
}
18+
}
19+
20+
class X[e]
21+
22+
case class Wrap(rep: (e, X[e]) forSome { type e })

0 commit comments

Comments
 (0)