Skip to content

Commit a42beba

Browse files
committed
Dependent pattern match prototype
1 parent ecc332f commit a42beba

File tree

6 files changed

+113
-14
lines changed

6 files changed

+113
-14
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -810,8 +810,12 @@ object desugar {
810810
for i <- List.range(0, arity)
811811
selName = nme.selectorName(i)
812812
if (selName ne caseParams(i).name) && !selectorNamesInBody.contains(selName)
813-
yield syntheticProperty(selName, caseParams(i).tpt,
814-
Select(This(EmptyTypeIdent), caseParams(i).name))
813+
yield
814+
val ptp =
815+
if caseParams(i).mods.mods.exists(_.isInstanceOf[Mod.Var]) then caseParams(i).tpt
816+
else SingletonTypeTree(Select(This(EmptyTypeIdent), caseParams(i).name))
817+
syntheticProperty(selName, ptp,
818+
Select(This(EmptyTypeIdent), caseParams(i).name))
815819

816820
def enumCaseMeths =
817821
if isEnumCase then
@@ -918,7 +922,10 @@ object desugar {
918922
if (arity == 0) Literal(Constant(true))
919923
else if caseClassInScala2Library then scala2LibCompatUnapplyRhs(unapplyParam.name)
920924
else Ident(unapplyParam.name)
921-
val unapplyResTp = if (arity == 0) Literal(Constant(true)) else TypeTree()
925+
val unapplyResTp =
926+
if (arity == 0) Literal(Constant(true))
927+
else if caseClassInScala2Library then TypeTree()
928+
else SingletonTypeTree(Ident(unapplyParam.name))
922929

923930
DefDef(
924931
methName,

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,8 @@ trait Applications extends Compatibility {
16251625
val patternBound = maximizeType(unapplyArgType, unapplyFn.span.endPos)
16261626
if (patternBound.nonEmpty) unapplyFn = addBinders(unapplyFn, patternBound)
16271627
unapp.println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}")
1628-
unapplyArgType
1628+
selType & unapplyArgType
1629+
// unapplyArgType
16291630

16301631
val dummyArg = dummyTreeOfType(ownType)
16311632
val (newUnapplyFn, unapplyApp) =
@@ -1637,7 +1638,7 @@ trait Applications extends Compatibility {
16371638
.typedPatterns(qual, this)
16381639
val result = assignType(cpy.UnApply(tree)(newUnapplyFn, unapplyImplicits(dummyArg, unapplyApp), unapplyPatterns), ownType)
16391640
if (ownType.stripped eq selType.stripped) || ownType.isError then result
1640-
else tryWithTypeTest(Typed(result, TypeTree(ownType)), selType)
1641+
else tryWithTypeTest(Typed(result, TypeTree(unapplyArgType)), selType)
16411642
case tp =>
16421643
val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn)
16431644
val typedArgsErr = unadaptedArgs.mapconserve(typed(_, defn.AnyType))

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
593593
typr.println(s"typed ident $kind$name in ${ctx.owner}")
594594
if ctx.mode.is(Mode.Pattern) then
595595
if name == nme.WILDCARD then
596-
return tree.withType(pt)
596+
val wpt = pt match
597+
case pt: TermRef => pt.widen
598+
case pt => pt
599+
return tree.withType(wpt)
597600
if name == tpnme.WILDCARD then
598601
return tree.withType(defn.AnyType)
599602
if untpd.isVarPattern(tree) && name.isTermName then
@@ -2031,7 +2034,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20312034
val rawSelectorTpe = fullyDefinedType(sel1.tpe, "pattern selector", tree.srcPos)
20322035
val selType = rawSelectorTpe match
20332036
case c: ConstantType if tree.isInline => c
2037+
case ref: TermRef => ref
20342038
case otherTpe => otherTpe.widen
2039+
val selTypeW = selType match
2040+
case tr: TermRef => tr.widen
2041+
case _ => selType
2042+
20352043

20362044
/** Does `tree` has the same shape as the given match type?
20372045
* We only support typed patterns with empty guards, but
@@ -2049,7 +2057,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20492057
// To check that pattern types correspond we need to type
20502058
// check `pat` here and throw away the result.
20512059
val gadtCtx: Context = ctx.fresh.setFreshGADTBounds
2052-
val pat1 = typedPattern(pat, selType)(using gadtCtx)
2060+
val pat1 = typedPattern(pat, selTypeW)(using gadtCtx)
20532061
val tpt = tpd.unbind(tpd.unsplice(pat1)) match
20542062
case Typed(_, tpt) => tpt
20552063
case UnApply(fun, _, p1 :: _) if fun.symbol == defn.TypeTest_unapply => p1
@@ -2067,7 +2075,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20672075

20682076
val result = pt.underlyingNormalizable match {
20692077
case mt: MatchType if isMatchTypeShaped(mt) =>
2070-
typedDependentMatchFinish(tree, sel1, selType, tree.cases, mt)
2078+
typedDependentMatchFinish(tree, sel1, selTypeW, tree.cases, mt)
20712079
case _ =>
20722080
typedMatchFinish(tree, sel1, selType, tree.cases, pt)
20732081
}
@@ -2677,6 +2685,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
26772685
def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = {
26782686
if !isFullyDefined(pt, ForceDegree.all) then
26792687
return errorTree(tree, em"expected type of $tree is not fully defined")
2688+
val wpt = pt match
2689+
case pt: TermRef => pt.widen
2690+
case _ => pt
26802691
val body1 = typed(tree.body, pt)
26812692
body1 match {
26822693
case UnApply(fn, Nil, arg :: Nil)
@@ -2705,9 +2716,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27052716
val symTp =
27062717
if isStableIdentifierOrLiteral || pt.isNamedTupleType then pt
27072718
// need to combine tuple element types with expected named type
2708-
else if isWildcardStarArg(body1)
2709-
|| pt == defn.ImplicitScrutineeTypeRef
2710-
|| body1.tpe <:< pt // There is some strange interaction with gadt matching.
2719+
else if isWildcardStarArg(body1) || pt == defn.ImplicitScrutineeTypeRef
2720+
then body1.tpe
2721+
else if body1.tpe <:< wpt // There is some strange interaction with gadt matching.
27112722
// and implicit scopes.
27122723
// run/t2755.scala fails to compile if this subtype test is omitted
27132724
// and the else clause is changed to `body1.tpe & pt`. What
@@ -2717,14 +2728,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
27172728
// it is Array[T] we get an implicit not found. To avoid fragility
27182729
// wrt to operand order for `&`, we include the explicit subtype test here.
27192730
// See also #5649.
2720-
then body1.tpe
2731+
then
2732+
if ctx.mode.is(Mode.Pattern) && (pt ne wpt) // && !(body1.tpe <:< pt)
2733+
then AndType(pt, body1.tpe)
2734+
// then pt & body1.tpe
2735+
else body1.tpe
27212736
else body1.tpe match
27222737
case btpe: TypeRef
27232738
if btpe.symbol == defn.TupleXXLClass && pt.tupleElementTypes.isDefined =>
27242739
// leave the original tuple type; don't mix with & TupleXXL which would only obscure things
27252740
pt
27262741
case _ =>
2727-
pt & body1.tpe
2742+
wpt & body1.tpe
27282743
val sym = newPatternBoundSymbol(name, symTp, tree.span)
27292744
if (pt == defn.ImplicitScrutineeTypeRef || tree.mods.is(Given)) sym.setFlag(Given)
27302745
if (ctx.mode.is(Mode.InPatternAlternative))

library/src/scala/Tuple.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ object Tuple {
265265

266266
/** Convert an array into a tuple of unknown arity and types */
267267
def fromArray[T](xs: Array[T]): Tuple = {
268-
val xs2 = xs match {
268+
val xs2: Array[Object] = xs match {
269269
case xs: Array[Object] => xs
270270
case xs => xs.map(_.asInstanceOf[Object])
271271
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//> using options -Xfatal-warnings
2+
3+
import scala.reflect.TypeTest
4+
5+
trait M:
6+
type Tree <: AnyRef
7+
type Apply <: Tree
8+
given TypeTest[Tree, Apply] = ???
9+
val Apply: ApplyModule
10+
trait ApplyModule:
11+
this: Apply.type =>
12+
def unapply(x: Apply): (Tree, Tree) = ???
13+
14+
def quote(x: Tree) = x match
15+
case Apply(f, args) =>
16+
println(args)
17+
case _ =>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
trait A:
2+
val x: Int
3+
type T
4+
case class B(x: Int, y: Int) extends A:
5+
type T = String
6+
val z: T = "B"
7+
case class C(x: Int) extends A:
8+
type T = Int
9+
val z: T = 1
10+
11+
object BParts:
12+
def unapply(b: B): Option[(b.x.type, b.y.type)] = Some((b.x, b.y))
13+
14+
def test1(a: A) = a match
15+
case b @ B(x, y) =>
16+
// b: a.type & B
17+
// x: (a.type & B).x.type
18+
// y: (a.type & B).y.type
19+
val e1: a.type = b
20+
val e2: a.x.type = b.x
21+
val e3: a.x.type = x
22+
val e4: b.x.type = x
23+
x + y
24+
case C(x) =>
25+
val e1: a.x.type = x
26+
x
27+
case BParts(x, y) =>
28+
val e1: a.x.type = x
29+
x + y
30+
case _ => 0
31+
32+
def test2(a: A): a.T =
33+
a match
34+
case b: B =>
35+
// b: a.type & B
36+
// b.z: b.T = (a & B)#T = a.T & String
37+
b.z
38+
case c: C => c.z
39+
40+
def test3(a: A): a.T =
41+
a match
42+
case b: B =>
43+
// b: a.type & B; hence b.T <:< a.T & String
44+
// We don't have a: b.type in the body,
45+
// so we can't prove String <:< a.T
46+
val x: b.T = b.z + ""
47+
x
48+
case c: C =>
49+
val x: c.T = c.z + 0
50+
x
51+
52+
def test4(x: A, y: A) =
53+
x match
54+
case z: y.type =>
55+
// if x.eq(y) then z = x = y,
56+
// z: x.type & y.type
57+
val a: x.type = z
58+
val b: y.type = z
59+
case _ =>

0 commit comments

Comments
 (0)