Skip to content

Commit c85dba8

Browse files
committed
Unified PredicateRefinedType with RefinedType, i.e., made it a subclass and thus shared type traversal / type mapping / pickling logic.
1 parent 5c56f14 commit c85dba8

File tree

10 files changed

+168
-56
lines changed

10 files changed

+168
-56
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
146146
def RefinedTypeTree(parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context): Tree =
147147
ta.assignType(untpd.RefinedTypeTree(parent, refinements), parent, refinements, refineCls)
148148

149+
def PredicateTypeTree(subjectVd: ValDef, pred: Tree)(implicit ctx: Context): PredicateTypeTree =
150+
ta.assignType(untpd.PredicateTypeTree(subjectVd, pred), subjectVd, pred)
151+
149152
def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree =
150153
ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args)
151154

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ object NameKinds {
365365
val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass")
366366
val ImplMethName = new SuffixNameKind(IMPLMETH, "$")
367367
val AdaptedClosureName = new SuffixNameKind(ADAPTEDCLOSURE, "$adapted") { override def definesNewName = true }
368+
val PredicateSubjectName = new SuffixNameKind(FIELD, "$subject") {
369+
override def definesNewName = true
370+
override def mkString(underlying: TermName, info: ThisInfo) = underlying.toString
371+
}
368372

369373
/** A name together with a signature. Used in Tasty trees. */
370374
object SignedName extends NameKind(SIGNED) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ object NameTags extends TastyFormat.NameTags {
3434
final val IMPLMETH = 30 // Used to define methods in implementation classes
3535
// (can probably be removed).
3636

37+
final val PREDICATESUBJECT = 31 // Used by PredicateRefinedTypes to encode their predicate subject's name
38+
3739
def nameTagToString(tag: Int): String = tag match {
3840
case UTF8 => "UTF8"
3941
case QUALIFIED => "QUALIFIED"

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ trait Substituters { this: Context =>
147147
.mapOver(tp)
148148
}
149149

150-
final def substRecThis(tp: Type, from: Type, to: Type, theMap: SubstRecThisMap): Type =
150+
final def substRecThis(tp: Type, from: RecType, to: Type, theMap: SubstRecThisMap): Type =
151151
tp match {
152152
case tp @ RecThis(binder) =>
153153
if (binder eq from) to else tp
@@ -161,6 +161,20 @@ trait Substituters { this: Context =>
161161
.mapOver(tp)
162162
}
163163

164+
final def substPredicateThis(tp: Type, from: PredicateRefinedType, to: Type, theMap: SubstPredicateThisMap): Type =
165+
tp match {
166+
case tp @ PredicateThis(binder) =>
167+
if (binder eq from) to else tp
168+
case tp: NamedType =>
169+
if (tp.currentSymbol.isStatic) tp
170+
else tp.derivedSelect(substPredicateThis(tp.prefix, from, to, theMap))
171+
case _: ThisType | _: BoundType | NoPrefix =>
172+
tp
173+
case _ =>
174+
(if (theMap != null) theMap else new SubstPredicateThisMap(from, to))
175+
.mapOver(tp)
176+
}
177+
164178
final def substParam(tp: Type, from: ParamRef, to: Type, theMap: SubstParamMap): Type =
165179
tp match {
166180
case tp: BoundType =>
@@ -222,10 +236,14 @@ trait Substituters { this: Context =>
222236
def apply(tp: Type): Type = substThis(tp, from, to, this)
223237
}
224238

225-
final class SubstRecThisMap(from: Type, to: Type) extends DeepTypeMap {
239+
final class SubstRecThisMap(from: RecType, to: Type) extends DeepTypeMap {
226240
def apply(tp: Type): Type = substRecThis(tp, from, to, this)
227241
}
228242

243+
final class SubstPredicateThisMap(from: PredicateRefinedType, to: Type) extends DeepTypeMap {
244+
def apply(tp: Type): Type = substPredicateThis(tp, from, to, this)
245+
}
246+
229247
final class SubstParamMap(from: ParamRef, to: Type) extends DeepTypeMap {
230248
def apply(tp: Type) = substParam(tp, from, to, this)
231249
}

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
432432
(name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2))
433433
}
434434
def compareRefined: Boolean = {
435+
// FIXME(gsps): Does handle case where PredicateRefinedType is skipped via skipMatching below correctly
436+
if (tp2.isInstanceOf[PredicateRefinedType])
437+
return isPredicateSubType(tp1, tp2.asInstanceOf[PredicateRefinedType])
435438
val tp1w = tp1.widen
436-
if (tp1w.isInstanceOf[RefinedType] && (tp2.refinedName eq nme.PRED))
437-
return false // Would have succeeded one-level up when the types were wrapped in RecTypes
438439
val skipped2 = skipMatching(tp1w, tp2)
439440
if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype)
440441
tp1 match {
@@ -1097,18 +1098,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
10971098
tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit))
10981099
}
10991100

1100-
// PredicateType related checking
1101-
1102-
protected def isPredicateSubType(tp1: Type, tp2: Type): Boolean = {
1103-
tp1 match {
1104-
case PredicateType.Fixed(parent1, pred1) =>
1105-
tp2 match {
1106-
case PredicateType.Fixed(parent2, pred2) if parent1 <:< parent2 => true
1107-
case _ => false
1108-
}
1109-
case _ => false
1110-
}
1111-
}
1101+
/* Skips refinement checks for PredicateRefinedTypes -- this is overridden in PreciseTyping. */
1102+
protected def isPredicateSubType(tp1: Type, tp2: PredicateRefinedType): Boolean =
1103+
// tp2 match {
1104+
// case PredicateRefinedType(parent2, pred2) => tp1 <:< parent2
1105+
// case _ => false
1106+
// }
1107+
tp1 <:< tp2.parent
11121108

11131109
/** A type has been covered previously in subtype checking if it
11141110
* is some combination of TypeRefs that point to classes, where the
@@ -1547,7 +1543,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
15471543
case tp1: RefinedType =>
15481544
tp2 match {
15491545
case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
1550-
PredicateType.mergePredicates(tp1, tp2, isAnd = true) orElse {
1546+
PredicateRefinedType.mergePredicates(tp1, tp2, isAnd = true) orElse {
15511547
tp1.derivedRefinedType(tp1.parent & tp2.parent, tp1.refinedName,
15521548
tp1.refinedInfo & tp2.refinedInfo)
15531549
}
@@ -1587,7 +1583,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
15871583
case tp1: AnnotatedType =>
15881584
tp1.underlying | tp2
15891585
case _ =>
1590-
PredicateType.mergePredicates(tp1, tp2, isAnd = false)
1586+
PredicateRefinedType.mergePredicates(tp1, tp2, isAnd = false)
15911587
}
15921588

15931589
/** Show type, handling type types better than the default */

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

Lines changed: 96 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,10 @@ object Types {
13361336
final def substRecThis(binder: RecType, tp: Type)(implicit ctx: Context): Type =
13371337
ctx.substRecThis(this, binder, tp, null)
13381338

1339+
/** Substitute all occurrences of `PredicateThis(binder)` by `tp` */
1340+
final def substPredicateThis(binder: PredicateRefinedType, tp: Type)(implicit ctx: Context): Type =
1341+
ctx.substPredicateThis(this, binder, tp, null)
1342+
13391343
/** Substitute a bound type by some other type */
13401344
final def substParam(from: ParamRef, to: Type)(implicit ctx: Context): Type =
13411345
ctx.substParam(this, from, to, null)
@@ -1539,6 +1543,9 @@ object Types {
15391543
def isOverloaded(implicit ctx: Context) = false
15401544
}
15411545

1546+
/** A marker trait for types that may act as stable term-level reference types. */
1547+
trait RefType extends SingletonType
1548+
15421549
/** A trait for types that bind other types that refer to them.
15431550
* Instances are: LambdaType, RecType.
15441551
*/
@@ -2090,7 +2097,7 @@ object Types {
20902097

20912098
abstract case class TermRef(override val prefix: Type,
20922099
private var myDesignator: Designator)
2093-
extends NamedType with SingletonType with ImplicitRef {
2100+
extends NamedType with RefType with ImplicitRef {
20942101

20952102
type ThisType = TermRef
20962103
type ThisName = TermName
@@ -2346,10 +2353,9 @@ object Types {
23462353
* @param infoFn: A function that produces the info of the refinement declaration,
23472354
* given the refined type itself.
23482355
*/
2349-
abstract case class RefinedType(parent: Type, refinedName: Name, refinedInfo: Type) extends RefinedOrRecType {
2356+
abstract class RefinedType(val parent: Type, val refinedName: Name) extends RefinedOrRecType {
2357+
val refinedInfo: Type
23502358

2351-
if (refinedName.isTermName) assert(refinedInfo.isInstanceOf[TermType])
2352-
else assert(refinedInfo.isInstanceOf[TypeType], this)
23532359
assert(!refinedName.is(NameKinds.ExpandedName), this)
23542360

23552361
override def underlying(implicit ctx: Context) = parent
@@ -2387,10 +2393,17 @@ object Types {
23872393
case _ => false
23882394
}
23892395
// equals comes from case class; no matching override is needed
2396+
2397+
// TODO(gsps): Add equals(that: Any) and hashCode() which consider refinedInfo?
23902398
}
23912399

2392-
class CachedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)
2393-
extends RefinedType(parent, refinedName, refinedInfo)
2400+
class CachedRefinedType(parent: Type, refinedName: Name, _refinedInfo: Type)
2401+
extends RefinedType(parent, refinedName) {
2402+
val refinedInfo: Type = _refinedInfo
2403+
2404+
if (refinedName.isTermName) assert(refinedInfo.isInstanceOf[TermType], s"Expected TermType, got $refinedInfo")
2405+
else assert(refinedInfo.isInstanceOf[TypeType], this)
2406+
}
23942407

23952408
object RefinedType {
23962409
@tailrec def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type =
@@ -2401,6 +2414,12 @@ object Types {
24012414
assert(!ctx.erasedTypes)
24022415
unique(new CachedRefinedType(parent, name, info)).checkInst
24032416
}
2417+
2418+
// TODO(gsps): This might be unreasonably slow
2419+
def unapply(tp: RefinedType): Option[(Type, Name, Type)] = tp match {
2420+
case tp: RefinedType => Some((tp.parent, tp.refinedName, tp.refinedInfo))
2421+
case _ => None
2422+
}
24042423
}
24052424

24062425
class RecType(parentExp: RecType => Type) extends RefinedOrRecType with BindingType {
@@ -2499,33 +2518,74 @@ object Types {
24992518
}
25002519
}
25012520

2502-
// --- PredicateType (encoding) -----------------------------------------------------
2521+
// --- PredicateRefinedType ---------------------------------------------------------
2522+
2523+
class PredicateRefinedType(val subjectName: TermName, parent: Type)(predicateBuilder: PredicateRefinedType => Type)
2524+
extends RefinedType(parent, NameKinds.PredicateSubjectName(subjectName))
2525+
{
2526+
val refinedInfo: Type = predicateBuilder(PredicateRefinedType.this)
2527+
def predicate: Type = this.refinedInfo
2528+
2529+
def predicateThis: PredicateThis = new PredicateThis(this) {}
2530+
2531+
override def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): Type = {
2532+
if (refinedName ne this.refinedName)
2533+
throw new UnsupportedOperationException("Cannot change refinement name on derived PredicateRefinedType.")
2534+
else if ((parent eq this.parent) && (refinedInfo eq this.refinedInfo))
2535+
this
2536+
else
2537+
new PredicateRefinedType(subjectName, parent)(prt => refinedInfo.substPredicateThis(this, prt.predicateThis))
2538+
}
2539+
}
2540+
2541+
// /** Encapsulates a predicate P[v: T] where `P` is a (typically precise) Boolean dependent on a stable value of
2542+
// * subject type `T`. Can be thought of as a dependent pair `(x: T, P[x])`. */
2543+
// case class PredicateType private(subjectTp: Type, subjectName: Name)(infoBuilder: PredicateType => Type)
2544+
// extends UncachedProxyType with ValueType with SingletonType with BindingType
2545+
// {
2546+
// val info: Type = infoBuilder(this)
2547+
//
2548+
// override def underlying(implicit ctx: Context): Type = defn.BooleanType
2549+
//
2550+
// override def hashCode: Int = identityHash
2551+
// override def equals(that: Any) = this.eq(that.asInstanceOf[AnyRef])
2552+
//
2553+
// def predicateThis: PredicateThis = new PredicateThis(this) {}
2554+
//
2555+
// override def toString = s"PredicateType($subjectTp, $hashCode)"
2556+
// }
2557+
2558+
abstract case class PredicateThis(binder: PredicateRefinedType) extends BoundType with SingletonType {
2559+
type BT = PredicateRefinedType // PredicateType
2560+
override def underlying(implicit ctx: Context): Type = binder.parent // binder.subjectTp
2561+
def copyBoundType(bt: BT) = bt.predicateThis
2562+
2563+
override def computeHash = addDelta(binder.identityHash, 41)
2564+
2565+
override def equals(that: Any) = that match {
2566+
case that: PredicateThis => binder.eq(that.binder)
2567+
case _ => false
2568+
}
2569+
2570+
override def toString =
2571+
try s"PredicateThis(${binder.hashCode})"
2572+
catch {
2573+
case ex: NullPointerException => s"PredicateThis(<under construction>)"
2574+
}
2575+
}
25032576

2504-
object PredicateType {
2577+
object PredicateRefinedType {
25052578
def apply(subject: ValDef, pred: Type)(implicit ctx: Context): Type = {
25062579
assertUnerased()
25072580
val parentTpe = subject.tpt.tpe
25082581
if (parentTpe.isError || pred.isError) parentTpe
2509-
else RecType(rt => RefinedType(parentTpe, nme.PRED, pred).subst(List(subject.symbol), List(rt.recThis)))
2582+
else new PredicateRefinedType(subject.name, parentTpe)(prt =>
2583+
pred.subst(List(subject.symbol), List(prt.predicateThis)))
25102584
}
25112585

2512-
def unapply(tp: Type)(implicit ctx: Context): Option[(Type, Type)] = tp match {
2513-
case recTp: RecType =>
2514-
recTp.parent match {
2515-
case refTp @ RefinedType(parent, nme.PRED, pred) => Some((parent, pred))
2516-
case _ => None
2517-
}
2518-
case _ => None
2519-
}
25202586

2521-
object Fixed {
2522-
// Matches PredicateTypes whose RecThis has already been fixed
2523-
def unapply(tp: Type)(implicit ctx: Context): Option[(Type, Type)] = tp match {
2524-
case refTp @ RefinedType(parent, nme.PRED, pred) => Some((parent, pred))
2525-
case _ => None
2526-
}
2527-
}
25282587

2588+
/*
25292589
object PseudoDnf {
25302590
def unapply(tp: Type)(implicit ctx: Context): Option[List[List[Type]]] = {
25312591
def isAndFn(fn: TermRef): Boolean = fn.symbol eq defn.Boolean_&&
@@ -2568,6 +2628,10 @@ object Types {
25682628
case _ => NoType
25692629
}
25702630
}
2631+
*/
2632+
2633+
// FIXME(gsps): Reimplement (unsound now)
2634+
def mergePredicates(tp1: Type, tp2: Type, isAnd: Boolean)(implicit ctx: Context): Type = tp1
25712635
}
25722636

25732637
// --- AndType/OrType ---------------------------------------------------------------
@@ -3410,7 +3474,7 @@ object Types {
34103474
/** Only created in `binder.paramRefs`. Use `binder.paramRefs(paramNum)` to
34113475
* refer to `TermParamRef(binder, paramNum)`.
34123476
*/
3413-
abstract case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef with SingletonType {
3477+
abstract case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef with RefType {
34143478
type BT = TermLambda
34153479
def kindString = "Term"
34163480
def copyBoundType(bt: BT) = bt.paramRefs(paramNum)
@@ -3465,7 +3529,7 @@ object Types {
34653529
// ----- Skolem types -----------------------------------------------
34663530

34673531
/** A skolem type reference with underlying type `binder`. */
3468-
case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType {
3532+
case class SkolemType(info: Type) extends UncachedProxyType with ValueType with RefType {
34693533
override def underlying(implicit ctx: Context) = info
34703534
def derivedSkolemType(info: Type)(implicit ctx: Context) =
34713535
if (info eq this.info) this else SkolemType(info)
@@ -4115,6 +4179,9 @@ object Types {
41154179
case tp: RefinedType =>
41164180
derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo))
41174181

4182+
// case tp: PredicateType =>
4183+
// PredicateType(this(tp.subjectTp), tp.subjectName)(predTp => this(tp.info).subst(tp, predTp))
4184+
41184185
case tp: TypeAlias =>
41194186
derivedTypeAlias(tp, atVariance(0)(this(tp.alias)))
41204187

@@ -4574,6 +4641,9 @@ object Types {
45744641
case tp: SkolemType =>
45754642
this(x, tp.info)
45764643

4644+
// case tp: PredicateType =>
4645+
// this(this(x, tp.subjectTp), tp.info)
4646+
45774647
case SuperType(thistp, supertp) =>
45784648
this(this(x, thistp), supertp)
45794649

compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ object TastyFormat {
322322
final val IMPORTED = 65
323323
final val RENAMED = 66
324324
final val SYMBOLconst = 67
325+
final val PREDICATEthis = 68
325326

326327
// Cat. 3: tag AST
327328

@@ -417,7 +418,7 @@ object TastyFormat {
417418
}
418419

419420
final val APPLIEDTERMREF = 180
420-
final val PREDICATEtpt = 177
421+
final val PREDICATEtpt = 177 // TODO(gsps): Rename to PREDICATEREFINEDtpt?
421422
final val HOLE = 255
422423

423424
final val firstNatTreeTag = SHAREDterm
@@ -484,7 +485,8 @@ object TastyFormat {
484485
| ANDtpt
485486
| ORtpt
486487
| BYNAMEtpt
487-
| BIND => true
488+
| BIND
489+
| PREDICATEtpt => true
488490
case _ => false
489491
}
490492

@@ -619,6 +621,8 @@ object TastyFormat {
619621
case PRIVATEqualified => "PRIVATEqualified"
620622
case PROTECTEDqualified => "PROTECTEDqualified"
621623
case HOLE => "HOLE"
624+
case PREDICATEtpt => "PREDICATEtpt"
625+
case PREDICATEthis => "PREDICATEthis"
622626
}
623627

624628
/** @return If non-negative, the number of leading references (represented as nats) of a length/trees entry.

0 commit comments

Comments
 (0)