Skip to content

Commit 9bfaf2b

Browse files
authored
Merge pull request #4964 from dotty-staging/add-matchtype
Add match type
2 parents a2a1112 + 7b548db commit 9bfaf2b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1633
-375
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
306306
case mdef: TypeDef =>
307307
def isBounds(rhs: Tree): Boolean = rhs match {
308308
case _: TypeBoundsTree => true
309+
case _: MatchTypeTree => true // Typedefs with Match rhs classify as abstract
309310
case LambdaTypeTree(_, body) => isBounds(body)
310311
case _ => false
311312
}
@@ -392,20 +393,21 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
392393
case Ident(_) =>
393394
refPurity(tree)
394395
case Select(qual, _) =>
395-
refPurity(tree).min(exprPurity(qual))
396+
if (tree.symbol.is(Erased)) Pure
397+
else refPurity(tree).min(exprPurity(qual))
396398
case New(_) =>
397399
SimplyPure
398400
case TypeApply(fn, _) =>
399-
exprPurity(fn)
401+
if (fn.symbol.is(Erased)) Pure else exprPurity(fn)
400402
case Apply(fn, args) =>
401403
def isKnownPureOp(sym: Symbol) =
402404
sym.owner.isPrimitiveValueClass || sym.owner == defn.StringClass
403405
if (tree.tpe.isInstanceOf[ConstantType] && isKnownPureOp(tree.symbol)
404406
// A constant expression with pure arguments is pure.
405407
|| fn.symbol.isStable)
406408
minOf(exprPurity(fn), args.map(exprPurity)) `min` Pure
407-
else
408-
Impure
409+
else if (fn.symbol.is(Erased)) Pure
410+
else Impure
409411
case Typed(expr, _) =>
410412
exprPurity(expr)
411413
case Block(stats, expr) =>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,12 @@ object Trees {
661661
type ThisTree[-T >: Untyped] = LambdaTypeTree[T]
662662
}
663663

664+
/** [bound] selector match { cases } */
665+
case class MatchTypeTree[-T >: Untyped] private[ast] (bound: Tree[T], selector: Tree[T], cases: List[CaseDef[T]])
666+
extends TypTree[T] {
667+
type ThisTree[-T >: Untyped] = MatchTypeTree[T]
668+
}
669+
664670
/** => T */
665671
case class ByNameTypeTree[-T >: Untyped] private[ast] (result: Tree[T])
666672
extends TypTree[T] {
@@ -916,6 +922,7 @@ object Trees {
916922
type RefinedTypeTree = Trees.RefinedTypeTree[T]
917923
type AppliedTypeTree = Trees.AppliedTypeTree[T]
918924
type LambdaTypeTree = Trees.LambdaTypeTree[T]
925+
type MatchTypeTree = Trees.MatchTypeTree[T]
919926
type ByNameTypeTree = Trees.ByNameTypeTree[T]
920927
type TypeBoundsTree = Trees.TypeBoundsTree[T]
921928
type Bind = Trees.Bind[T]
@@ -1099,6 +1106,10 @@ object Trees {
10991106
case tree: LambdaTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree
11001107
case _ => finalize(tree, untpd.LambdaTypeTree(tparams, body))
11011108
}
1109+
def MatchTypeTree(tree: Tree)(bound: Tree, selector: Tree, cases: List[CaseDef]): MatchTypeTree = tree match {
1110+
case tree: MatchTypeTree if (bound eq tree.bound) && (selector eq tree.selector) && (cases eq tree.cases) => tree
1111+
case _ => finalize(tree, untpd.MatchTypeTree(bound, selector, cases))
1112+
}
11021113
def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match {
11031114
case tree: ByNameTypeTree if result eq tree.result => tree
11041115
case _ => finalize(tree, untpd.ByNameTypeTree(result))
@@ -1255,6 +1266,8 @@ object Trees {
12551266
case LambdaTypeTree(tparams, body) =>
12561267
implicit val ctx = localCtx
12571268
cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body))
1269+
case MatchTypeTree(bound, selector, cases) =>
1270+
cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases))
12581271
case ByNameTypeTree(result) =>
12591272
cpy.ByNameTypeTree(tree)(transform(result))
12601273
case TypeBoundsTree(lo, hi) =>
@@ -1389,6 +1402,8 @@ object Trees {
13891402
case LambdaTypeTree(tparams, body) =>
13901403
implicit val ctx = localCtx
13911404
this(this(x, tparams), body)
1405+
case MatchTypeTree(bound, selector, cases) =>
1406+
this(this(this(x, bound), selector), cases)
13921407
case ByNameTypeTree(result) =>
13931408
this(x, result)
13941409
case TypeBoundsTree(lo, hi) =>

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
115115
}
116116

117117
def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef =
118-
ta.assignType(untpd.CaseDef(pat, guard, body), body)
118+
ta.assignType(untpd.CaseDef(pat, guard, body), pat, body)
119119

120120
def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
121-
ta.assignType(untpd.Match(selector, cases), cases)
121+
ta.assignType(untpd.Match(selector, cases), selector, cases)
122122

123123
def Labeled(bind: Bind, expr: Tree)(implicit ctx: Context): Labeled =
124124
ta.assignType(untpd.Labeled(bind, expr))
@@ -165,6 +165,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
165165
def LambdaTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): LambdaTypeTree =
166166
ta.assignType(untpd.LambdaTypeTree(tparams, body), tparams, body)
167167

168+
def MatchTypeTree(bound: Tree, selector: Tree, cases: List[CaseDef])(implicit ctx: Context): MatchTypeTree =
169+
ta.assignType(untpd.MatchTypeTree(bound, selector, cases), bound, selector, cases)
170+
168171
def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree =
169172
ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi)
170173

@@ -575,7 +578,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
575578
}
576579
}
577580

578-
579581
override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = {
580582
val tree1 = untpd.cpy.Closure(tree)(env, meth, tpt)
581583
tree match {
@@ -584,19 +586,20 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
584586
case _ => ta.assignType(tree1, meth, tpt)
585587
}
586588
}
589+
587590
override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = {
588591
val tree1 = untpd.cpy.Match(tree)(selector, cases)
589592
tree match {
590593
case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe)
591-
case _ => ta.assignType(tree1, cases)
594+
case _ => ta.assignType(tree1, selector, cases)
592595
}
593596
}
594597

595598
override def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = {
596599
val tree1 = untpd.cpy.CaseDef(tree)(pat, guard, body)
597600
tree match {
598601
case tree: CaseDef if body.tpe eq tree.body.tpe => tree1.withTypeUnchecked(tree.tpe)
599-
case _ => ta.assignType(tree1, body)
602+
case _ => ta.assignType(tree1, pat, body)
600603
}
601604
}
602605

@@ -821,7 +824,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
821824

822825
/** `tree == that` */
823826
def equal(that: Tree)(implicit ctx: Context) =
824-
applyOverloaded(tree, nme.EQ, that :: Nil, Nil, defn.BooleanType)
827+
if (that.tpe.widen.isRef(defn.NothingClass))
828+
Literal(Constant(false))
829+
else
830+
applyOverloaded(tree, nme.EQ, that :: Nil, Nil, defn.BooleanType)
825831

826832
/** `tree.isInstanceOf[tp]`, with special treatment of singleton types */
827833
def isInstance(tp: Type)(implicit ctx: Context): Tree = tp.dealias match {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
293293
def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements)
294294
def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args)
295295
def LambdaTypeTree(tparams: List[TypeDef], body: Tree): LambdaTypeTree = new LambdaTypeTree(tparams, body)
296+
def MatchTypeTree(bound: Tree, selector: Tree, cases: List[CaseDef]): MatchTypeTree = new MatchTypeTree(bound, selector, cases)
296297
def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result)
297298
def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi)
298299
def Bind(name: Name, body: Tree): Bind = new Bind(name, body)

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ object Config {
66
final val cacheAsSeenFrom = true
77
final val cacheMemberNames = true
88
final val cacheImplicitScopes = true
9+
final val cacheMatchReduced = true
910

1011
final val checkCacheMembersNamed = false
1112

compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ trait ConstraintHandling {
3535
/** If the constraint is frozen we cannot add new bounds to the constraint. */
3636
protected var frozenConstraint = false
3737

38+
/** Potentially a type lambda that is still instantiatable, even though the constraint
39+
* is generally frozen.
40+
*/
41+
protected var caseLambda: Type = NoType
42+
3843
/** If set, align arguments `S1`, `S2`when taking the glb
3944
* `T1 { X = S1 } & T2 { X = S2 }` of a constraint upper bound for some type parameter.
4045
* Aligning means computing `S1 =:= S2` which may change the current constraint.
@@ -47,7 +52,7 @@ trait ConstraintHandling {
4752
*/
4853
protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty
4954

50-
private def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean): Boolean =
55+
protected def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean): Boolean =
5156
!constraint.contains(param) || {
5257
def occursIn(bound: Type): Boolean = {
5358
val b = bound.dealias
@@ -167,19 +172,20 @@ trait ConstraintHandling {
167172
isSubType(tp1, tp2)
168173
}
169174

170-
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
171-
val saved = frozenConstraint
175+
@forceInline final def inFrozenConstraint[T](op: => T): T = {
176+
val savedFrozen = frozenConstraint
177+
val savedLambda = caseLambda
172178
frozenConstraint = true
173-
try isSubType(tp1, tp2)
174-
finally frozenConstraint = saved
179+
caseLambda = NoType
180+
try op
181+
finally {
182+
frozenConstraint = savedFrozen
183+
caseLambda = savedLambda
184+
}
175185
}
176186

177-
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
178-
val saved = frozenConstraint
179-
frozenConstraint = true
180-
try isSameType(tp1, tp2)
181-
finally frozenConstraint = saved
182-
}
187+
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = inFrozenConstraint(isSubType(tp1, tp2))
188+
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = inFrozenConstraint(isSameType(tp1, tp2))
183189

184190
/** Test whether the lower bounds of all parameters in this
185191
* constraint are a solution to the constraint.
@@ -319,7 +325,7 @@ trait ConstraintHandling {
319325
}
320326

321327
/** The current bounds of type parameter `param` */
322-
final def bounds(param: TypeParamRef): TypeBounds = {
328+
def bounds(param: TypeParamRef): TypeBounds = {
323329
val e = constraint.entry(param)
324330
if (e.exists) e.bounds
325331
else {
@@ -355,7 +361,7 @@ trait ConstraintHandling {
355361

356362
/** Can `param` be constrained with new bounds? */
357363
final def canConstrain(param: TypeParamRef): Boolean =
358-
!frozenConstraint && (constraint contains param)
364+
(!frozenConstraint || (caseLambda `eq` param.binder)) && constraint.contains(param)
359365

360366
/** Add constraint `param <: bound` if `fromBelow` is false, `param >: bound` otherwise.
361367
* `bound` is assumed to be in normalized form, as specified in `firstTry` and
@@ -492,19 +498,18 @@ trait ConstraintHandling {
492498
/** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */
493499
def checkPropagated(msg: => String)(result: Boolean): Boolean = {
494500
if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) {
495-
val saved = frozenConstraint
496-
frozenConstraint = true
497-
for (p <- constraint.domainParams) {
498-
def check(cond: => Boolean, q: TypeParamRef, ordering: String, explanation: String): Unit =
499-
assert(cond, i"propagation failure for $p $ordering $q: $explanation\n$msg")
500-
for (u <- constraint.upper(p))
501-
check(bounds(p).hi <:< bounds(u).hi, u, "<:", "upper bound not propagated")
502-
for (l <- constraint.lower(p)) {
503-
check(bounds(l).lo <:< bounds(p).hi, l, ">:", "lower bound not propagated")
504-
check(constraint.isLess(l, p), l, ">:", "reverse ordering (<:) missing")
501+
inFrozenConstraint {
502+
for (p <- constraint.domainParams) {
503+
def check(cond: => Boolean, q: TypeParamRef, ordering: String, explanation: String): Unit =
504+
assert(cond, i"propagation failure for $p $ordering $q: $explanation\n$msg")
505+
for (u <- constraint.upper(p))
506+
check(bounds(p).hi <:< bounds(u).hi, u, "<:", "upper bound not propagated")
507+
for (l <- constraint.lower(p)) {
508+
check(bounds(l).lo <:< bounds(p).hi, l, ">:", "lower bound not propagated")
509+
check(constraint.isLess(l, p), l, ">:", "reverse ordering (<:) missing")
510+
}
505511
}
506512
}
507-
frozenConstraint = saved
508513
}
509514
result
510515
}

compiler/src/dotty/tools/dotc/core/ConstraintRunInfo.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package dotty.tools.dotc
22
package core
33

44
import Contexts._
5-
import config.Printers.typr
5+
import config.Printers.{default, typr}
66

77
trait ConstraintRunInfo { self: Run =>
88
private[this] var maxSize = 0
@@ -12,8 +12,9 @@ trait ConstraintRunInfo { self: Run =>
1212
maxSize = size
1313
maxConstraint = c
1414
}
15-
def printMaxConstraint()(implicit ctx: Context) =
16-
if (maxSize > 0) typr.println(s"max constraint = ${maxConstraint.show}")
17-
15+
def printMaxConstraint()(implicit ctx: Context) = {
16+
val printer = if (ctx.settings.YdetailedStats.value) default else typr
17+
if (maxSize > 0) printer.println(s"max constraint = ${maxConstraint.show}")
18+
}
1819
protected def reset() = maxConstraint = null
1920
}

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,15 @@ class Definitions {
218218
lazy val Sys_errorR = SysPackage.moduleClass.requiredMethodRef(nme.error)
219219
def Sys_error(implicit ctx: Context) = Sys_errorR.symbol
220220

221+
lazy val TypelevelPackageObjectRef = ctx.requiredModuleRef("scala.typelevel.package")
222+
lazy val TypelevelPackageObject = TypelevelPackageObjectRef.symbol.moduleClass
223+
lazy val Typelevel_errorR = TypelevelPackageObjectRef.symbol.requiredMethodRef(nme.error)
224+
def Typelevel_error(implicit ctx: Context) = Typelevel_errorR.symbol
225+
lazy val Typelevel_constValueR = TypelevelPackageObjectRef.symbol.requiredMethodRef("constValue")
226+
def Typelevel_constValue(implicit ctx: Context) = Typelevel_constValueR.symbol
227+
lazy val Typelevel_constValueOptR = TypelevelPackageObjectRef.symbol.requiredMethodRef("constValueOpt")
228+
def Typelevel_constValueOpt(implicit ctx: Context) = Typelevel_constValueOptR.symbol
229+
221230
/** The `scalaShadowing` package is used to safely modify classes and
222231
* objects in scala so that they can be used from dotty. They will
223232
* be visible as members of the `scala` package, replacing any objects
@@ -712,6 +721,8 @@ class Definitions {
712721

713722
lazy val TupleTypeRef = ctx.requiredClassRef("scala.Tuple")
714723
def TupleClass(implicit ctx: Context) = TupleTypeRef.symbol.asClass
724+
lazy val NonEmptyTupleTypeRef = ctx.requiredClassRef("scala.NonEmptyTuple")
725+
def NonEmptyTupleClass(implicit ctx: Context) = NonEmptyTupleTypeRef.symbol.asClass
715726

716727
lazy val PairType = ctx.requiredClassRef("scala.*:")
717728
def PairClass(implicit ctx: Context) = PairType.symbol.asClass
@@ -888,6 +899,9 @@ class Definitions {
888899
}
889900
}
890901

902+
final def isTypelevel_S(sym: Symbol)(implicit ctx: Context) =
903+
sym.name == tpnme.S && sym.owner == TypelevelPackageObject
904+
891905
// ----- Symbol sets ---------------------------------------------------
892906

893907
lazy val AbstractFunctionType = mkArityArray("scala.runtime.AbstractFunction", MaxImplementedFunctionArity, 0)
@@ -1217,7 +1231,7 @@ class Definitions {
12171231
def isValueSubClass(sym1: Symbol, sym2: Symbol) =
12181232
valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0
12191233

1220-
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, SingletonClass)
1234+
lazy val erasedToObject = Set[Symbol](AnyClass, AnyValClass, TupleClass, NonEmptyTupleClass, SingletonClass)
12211235

12221236
// ----- Initialization ---------------------------------------------------
12231237

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ object StdNames {
204204
final val Object: N = "Object"
205205
final val PartialFunction: N = "PartialFunction"
206206
final val PrefixType: N = "PrefixType"
207+
final val S: N = "S"
207208
final val Serializable: N = "Serializable"
208209
final val Singleton: N = "Singleton"
209210
final val Throwable: N = "Throwable"

compiler/src/dotty/tools/dotc/core/TypeApplications.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import util.common._
1212
import Names._
1313
import NameOps._
1414
import NameKinds._
15+
import Constants.Constant
1516
import Flags._
1617
import StdNames.tpnme
1718
import util.Positions.Position
@@ -337,8 +338,8 @@ class TypeApplications(val self: Type) extends AnyVal {
337338
tl => arg.paramInfos.map(_.subst(arg, tl).bounds),
338339
tl => arg.resultType.subst(arg, tl)
339340
)
340-
case arg @ TypeAlias(alias) =>
341-
arg.derivedTypeAlias(adaptArg(alias))
341+
case arg: AliasingBounds =>
342+
arg.derivedAlias(adaptArg(arg.alias))
342343
case arg @ TypeBounds(lo, hi) =>
343344
arg.derivedTypeBounds(adaptArg(lo), adaptArg(hi))
344345
case _ =>
@@ -401,8 +402,8 @@ class TypeApplications(val self: Type) extends AnyVal {
401402
dealiased.derivedAndType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args))
402403
case dealiased: OrType =>
403404
dealiased.derivedOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args))
404-
case dealiased: TypeAlias =>
405-
dealiased.derivedTypeAlias(dealiased.alias.appliedTo(args))
405+
case dealiased: AliasingBounds =>
406+
dealiased.derivedAlias(dealiased.alias.appliedTo(args))
406407
case dealiased: TypeBounds =>
407408
dealiased.derivedTypeBounds(dealiased.lo.appliedTo(args), dealiased.hi.appliedTo(args))
408409
case dealiased: LazyRef =>
@@ -434,10 +435,13 @@ class TypeApplications(val self: Type) extends AnyVal {
434435
appliedTo(args)
435436
}
436437

437-
/** Turns non-bounds types to type aliases */
438+
/** Turns non-bounds types to type bounds.
439+
* A (possible lambda abstracted) match type is turned into an abstract type.
440+
* Every other type is turned into a type alias
441+
*/
438442
final def toBounds(implicit ctx: Context): TypeBounds = self match {
439443
case self: TypeBounds => self // this can happen for wildcard args
440-
case _ => TypeAlias(self)
444+
case _ => if (self.isMatch) MatchAlias(self) else TypeAlias(self)
441445
}
442446

443447
/** Translate a type of the form From[T] to To[T], keep other types as they are.

0 commit comments

Comments
 (0)