Skip to content

Commit b2634a8

Browse files
committed
Merge pull request #585 from dotty-staging/fix/collection-related
Improvements to higher-kinded types
2 parents afe910d + 78640eb commit b2634a8

24 files changed

+423
-118
lines changed

docs/SyntaxSummary.txt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ grammar.
9999
FunArgTypes ::= InfixType
100100
| `(' [ FunArgType {`,' FunArgType } ] `)'
101101
InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2)
102-
RefinedType ::= WithType {Annotation | [nl] Refinement} Annotated(t, annot), RefinedTypeTree(t, ds)
103-
WithType ::= SimpleType {`with' SimpleType} (deprecated)
102+
RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds)
103+
WithType ::= AnnotType {`with' AnnotType} (deprecated)
104+
AnnotType ::= SimpleType {Annotation} Annotated(t, annot)
104105
SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args)
105106
| SimpleType `#' id SelectFromTypeTree(t, name)
106107
| StableId
@@ -121,9 +122,9 @@ grammar.
121122

122123
Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
123124
| Expr1
124-
FunParams ::= Bindings
125-
| [`implicit'] id
126-
| `_'
125+
FunParams ::= Bindings
126+
| [`implicit'] id
127+
| `_'
127128
ExprInParens ::= PostfixExpr `:' Type
128129
| Expr
129130
BlockResult ::= (FunParams | [`implicit'] id `:' InfixType) => Block
@@ -248,7 +249,7 @@ grammar.
248249
AccessModifier ::= (`private' | `protected') [AccessQualifier]
249250
AccessQualifier ::= `[' (id | `this') `]'
250251

251-
Annotation ::= `@' SimpleType {ArgumentExprs} Apply(tpe, args)
252+
Annotation ::= `@' SimpleType {ParArgumentExprs} Apply(tpe, args)
252253

253254
TemplateBody ::= [nl] `{' [SelfType] TemplateStat {semi TemplateStat} `} (self, stats)
254255
TemplateStat ::= Import
@@ -301,7 +302,7 @@ grammar.
301302
TemplateOpt ::= [`extends' Template | [nl] TemplateBody]
302303
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
303304
ConstrApps ::= ConstrApp {`with' ConstrApp}
304-
ConstrApp ::= SimpleType {ArgumentExprs} Apply(tp, args)
305+
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
305306

306307
ConstrExpr ::= SelfInvocation
307308
| ConstrBlock

src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class GenBCode extends Phase {
3838

3939

4040
def run(implicit ctx: Context): Unit = {
41-
new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
41+
new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
4242
entryPoints.clear()
4343
}
4444
}

src/dotty/tools/dotc/config/CompilerCommand.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,19 @@ object CompilerCommand extends DotClass {
110110

111111
if (summary.errors.nonEmpty) {
112112
summary.errors foreach (ctx.error(_))
113-
ctx.echo(" dotc -help gives more information")
113+
ctx.println(" dotc -help gives more information")
114114
Nil
115115
}
116116
else if (settings.version.value) {
117-
ctx.echo(versionMsg)
117+
ctx.println(versionMsg)
118118
Nil
119119
}
120120
else if (shouldStopWithInfo) {
121-
ctx.echo(infoMessage)
121+
ctx.println(infoMessage)
122122
Nil
123123
} else {
124124
if (summary.arguments.isEmpty && !settings.resident.value)
125-
ctx.echo(usageMessage)
125+
ctx.println(usageMessage)
126126
summary.arguments
127127
}
128128
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,31 @@ object Config {
7171

7272
/** Check that certain types cannot be created in erasedTypes phases */
7373
final val checkUnerased = true
74+
75+
76+
/** Initial size of superId table */
77+
final val InitialSuperIdsSize = 4096
78+
79+
/** Initial capacity of uniques HashMap */
80+
final val initialUniquesCapacity = 40000
81+
82+
/** How many recursive calls to NamedType#underlying are performed before logging starts. */
83+
final val LogPendingUnderlyingThreshold = 50
84+
85+
/** How many recursive calls to isSubType are performed before logging starts. */
86+
final val LogPendingSubTypesThreshold = 50
87+
88+
/** How many recursive calls to findMember are performed before logging names starts
89+
* Note: this threshold has to be chosen carefully. Too large, and programs
90+
* like tests/pos/IterableSelfRec go into polynomial (or even exponential?)
91+
* compile time slowdown. Too small and normal programs will cause the compiler to
92+
* do inefficient operations on findMember. The current value is determined
93+
* so that (1) IterableSelfRec still compiles in reasonable time (< 10sec) (2) Compiling
94+
* dotty itself only causes small pending names lists to be generated (we measured
95+
* at max 6 elements) and these lists are never searched with contains.
96+
*/
97+
final val LogPendingFindMemberThreshold = 10
98+
99+
/** Maximal number of outstanding recursive calls to findMember */
100+
final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
74101
}

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
2020
import typer._
2121
import Implicits.ContextualImplicits
2222
import config.Settings._
23+
import config.Config
2324
import reporting._
2425
import collection.mutable
2526
import collection.immutable.BitSet
@@ -164,6 +165,14 @@ object Contexts {
164165
_typeComparer
165166
}
166167

168+
/** Number of findMember calls on stack */
169+
private[core] var findMemberCount: Int = 0
170+
171+
/** List of names which have a findMemberCall on stack,
172+
* after Config.LogPendingFindMemberThreshold is reached.
173+
*/
174+
private[core] var pendingMemberSearches: List[Name] = Nil
175+
167176
/** The new implicit references that are introduced by this scope */
168177
private var implicitsCache: ContextualImplicits = null
169178
def implicits: ContextualImplicits = {
@@ -508,7 +517,7 @@ object Contexts {
508517
def nextId = { _nextId += 1; _nextId }
509518

510519
/** A map from a superclass id to the typeref of the class that has it */
511-
private[core] var classOfId = new Array[ClassSymbol](InitialSuperIdsSize)
520+
private[core] var classOfId = new Array[ClassSymbol](Config.InitialSuperIdsSize)
512521

513522
/** A map from a the typeref of a class to its superclass id */
514523
private[core] val superIdOfClass = new mutable.AnyRefMap[ClassSymbol, Int]
@@ -529,7 +538,7 @@ object Contexts {
529538

530539
// Types state
531540
/** A table for hash consing unique types */
532-
private[core] val uniques = new util.HashSet[Type](initialUniquesCapacity) {
541+
private[core] val uniques = new util.HashSet[Type](Config.initialUniquesCapacity) {
533542
override def hash(x: Type): Int = x.hash
534543
}
535544

@@ -614,20 +623,4 @@ object Contexts {
614623
myBounds = myBounds.updated(sym, b)
615624
def bounds = myBounds
616625
}
617-
618-
/** Initial size of superId table */
619-
private final val InitialSuperIdsSize = 4096
620-
621-
/** Initial capacity of uniques HashMap */
622-
private[core] final val initialUniquesCapacity = 40000
623-
624-
/** How many recursive calls to NamedType#underlying are performed before
625-
* logging starts.
626-
*/
627-
private[core] final val LogPendingUnderlyingThreshold = 50
628-
629-
/** How many recursive calls to isSubType are performed before
630-
* logging starts.
631-
*/
632-
private[core] final val LogPendingSubTypesThreshold = 50
633626
}

src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ object Decorators {
173173
def treatSingleArg(arg: Any) : Any =
174174
try
175175
arg match {
176-
case arg: Showable => arg.show(ctx.fresh.addMode(Mode.FutureDefsOK))
176+
case arg: Showable => arg.show(ctx.addMode(Mode.FutureDefsOK))
177177
case _ => arg
178178
}
179179
catch {

src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,18 @@ object NameOps {
9999

100100
/** Is this the name of a higher-kinded type parameter of a Lambda? */
101101
def isLambdaArgName =
102-
name.length > 0 && name.head == tpnme.LAMBDA_ARG_PREFIXhead && name.startsWith(tpnme.LAMBDA_ARG_PREFIX)
102+
name.length > 0 &&
103+
name.head == tpnme.LAMBDA_ARG_PREFIXhead &&
104+
name.startsWith(tpnme.LAMBDA_ARG_PREFIX) && {
105+
val digits = name.drop(tpnme.LAMBDA_ARG_PREFIX.length)
106+
digits.length <= 4 && digits.forall(_.isDigit)
107+
}
103108

104109
/** The index of the higher-kinded type parameter with this name.
105110
* Pre: isLambdaArgName.
106111
*/
107-
def lambdaArgIndex: Int = name.drop(name.lastIndexOf('$') + 1).toString.toInt
112+
def lambdaArgIndex: Int =
113+
name.drop(tpnme.LAMBDA_ARG_PREFIX.length).toString.toInt
108114

109115
/** If the name ends with $nn where nn are
110116
* all digits, strip the $ and the digits.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ object StdNames {
172172
final val WILDCARD_STAR: N = "_*"
173173
final val REIFY_TREECREATOR_PREFIX: N = "$treecreator"
174174
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
175-
final val LAMBDA_ARG_PREFIX: N = "$hkArg$"
175+
final val LAMBDA_ARG_PREFIX: N = "HK$"
176176
final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head
177177

178178
final val Any: N = "Any"

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -515,25 +515,28 @@ class TypeApplications(val self: Type) extends AnyVal {
515515
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams)
516516
}
517517

518-
/** Test whether this type has a base type `B[T1, ..., Tn]` where the type parameters
519-
* of `B` match one-by-one the variances of `tparams`, and where the lambda
520-
* abstracted type
518+
/** Test whether this type has a base type of the form `B[T1, ..., Bn]` where
519+
* the type parameters of `B` match one-by-one the variances of `tparams`,
520+
* and where the lambda abstracted type
521521
*
522522
* LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] }
523523
* { type $hkArg$0 = T1; ...; type $hkArg$n = Tn }
524524
*
525525
* satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`.
526526
* A type parameter matches a variance V if it has V as its variance or if V == 0.
527+
* @param classBounds A hint to bound the search. Only types that derive from one of the
528+
* classes in classBounds are considered.
527529
*/
528-
def testLifted(tparams: List[Symbol], p: Type => Boolean)(implicit ctx: Context): Boolean = {
530+
def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = {
529531
def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match {
530532
case bc :: bcs1 =>
531533
val tp = self.baseTypeWithArgs(bc)
532534
val targs = tp.argInfos
533535
val tycon = tp.withoutArgs(targs)
534536
def variancesMatch(param1: Symbol, param2: Symbol) =
535537
param2.variance == param2.variance || param2.variance == 0
536-
if ((tycon.typeParams corresponds tparams)(variancesMatch)) {
538+
if (classBounds.exists(tycon.derivesFrom(_)) &&
539+
tycon.typeParams.corresponds(tparams)(variancesMatch)) {
537540
val expanded = tycon.EtaExpand
538541
val lifted = (expanded /: targs) { (partialInst, targ) =>
539542
val tparam = partialInst.typeParams.head
@@ -548,7 +551,7 @@ class TypeApplications(val self: Type) extends AnyVal {
548551
false
549552
}
550553
if (tparams.isEmpty) false
551-
else if (typeParams.nonEmpty) p(EtaExpand) || tryLift(self.baseClasses)
552-
else tryLift(self.baseClasses)
554+
else if (typeParams.nonEmpty) p(EtaExpand) || classBounds.nonEmpty && tryLift(self.baseClasses)
555+
else classBounds.nonEmpty && tryLift(self.baseClasses)
553556
}
554557
}

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
9797
try {
9898
recCount = recCount + 1
9999
val result =
100-
if (recCount < LogPendingSubTypesThreshold) firstTry(tp1, tp2)
100+
if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
101101
else monitoredIsSubType(tp1, tp2)
102102
recCount = recCount - 1
103103
if (!result) constraint = saved
@@ -345,7 +345,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
345345
|| fourthTry(tp1, tp2)
346346
)
347347
normalPath ||
348-
needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2))
348+
needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2))
349349
}
350350
else // fast path, in particular for refinements resulting from parameterization.
351351
isSubType(tp1, skipped2) &&
@@ -453,7 +453,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
453453
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
454454
case tp1: RefinedType =>
455455
isNewSubType(tp1.parent, tp2) ||
456-
needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _))
456+
needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil)
457457
case AndType(tp11, tp12) =>
458458
eitherIsSubType(tp11, tp2, tp12, tp2)
459459
case JavaArrayType(elem1) =>
@@ -475,9 +475,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
475475
val lambda = projection.prefix.LambdaClass(forcing = true)
476476
lambda.exists && !other.isLambda &&
477477
other.testLifted(lambda.typeParams,
478-
if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix))
478+
if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix),
479+
if (inOrder) Nil else classBounds(projection.prefix))
479480
}
480481

482+
/** The class symbols bounding the type of the `Apply` member of `tp` */
483+
private def classBounds(tp: Type) = tp.member(tpnme.Apply).info.classSymbols
484+
481485
/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
482486
* to keep the constraint as wide as possible. Specifically, if
483487
*
@@ -1141,13 +1145,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
11411145
}
11421146

11431147
/** Show subtype goal that led to an assertion failure */
1144-
def showGoal(tp1: Type, tp2: Type) = {
1145-
println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
1148+
def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = {
1149+
ctx.println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
11461150
def explainPoly(tp: Type) = tp match {
1147-
case tp: PolyParam => println(s"polyparam ${tp.show} found in ${tp.binder.show}")
1148-
case tp: TypeRef if tp.symbol.exists => println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
1149-
case tp: TypeVar => println(s"typevar ${tp.show}, origin = ${tp.origin}")
1150-
case _ => println(s"${tp.show} is a ${tp.getClass}")
1151+
case tp: PolyParam => ctx.println(s"polyparam ${tp.show} found in ${tp.binder.show}")
1152+
case tp: TypeRef if tp.symbol.exists => ctx.println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
1153+
case tp: TypeVar => ctx.println(s"typevar ${tp.show}, origin = ${tp.origin}")
1154+
case _ => ctx.println(s"${tp.show} is a ${tp.getClass}")
11511155
}
11521156
explainPoly(tp1)
11531157
explainPoly(tp2)

src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,18 +302,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
302302
// println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG
303303
var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty
304304
var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty
305-
def normalizeToRef(tp: Type): TypeRef = tp match {
305+
def normalizeToRef(tp: Type): TypeRef = tp.dealias match {
306+
case tp: TypeRef =>
307+
tp
306308
case tp @ RefinedType(tp1, name: TypeName) =>
307309
val prevInfo = refinements(name)
308310
refinements = refinements.updated(name,
309311
if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo)
310312
formals = formals.updated(name, tp1.typeParamNamed(name))
311313
normalizeToRef(tp1)
312-
case tp: TypeRef =>
313-
if (tp.symbol.info.isAlias) normalizeToRef(tp.info.bounds.hi)
314-
else tp
315314
case ErrorType =>
316315
defn.AnyClass.typeRef
316+
case AnnotatedType(_, tpe) =>
317+
normalizeToRef(tpe)
317318
case _ =>
318319
throw new TypeError(s"unexpected parent type: $tp")
319320
}

0 commit comments

Comments
 (0)