Skip to content

Improvements to higher-kinded types #585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions docs/SyntaxSummary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ grammar.
FunArgTypes ::= InfixType
| `(' [ FunArgType {`,' FunArgType } ] `)'
InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2)
RefinedType ::= WithType {Annotation | [nl] Refinement} Annotated(t, annot), RefinedTypeTree(t, ds)
WithType ::= SimpleType {`with' SimpleType} (deprecated)
RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds)
WithType ::= AnnotType {`with' AnnotType} (deprecated)
AnnotType ::= SimpleType {Annotation} Annotated(t, annot)
SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args)
| SimpleType `#' id SelectFromTypeTree(t, name)
| StableId
Expand All @@ -121,9 +122,9 @@ grammar.

Expr ::= FunParams `=>' Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
| Expr1
FunParams ::= Bindings
| [`implicit'] id
| `_'
FunParams ::= Bindings
| [`implicit'] id
| `_'
ExprInParens ::= PostfixExpr `:' Type
| Expr
BlockResult ::= (FunParams | [`implicit'] id `:' InfixType) => Block
Expand Down Expand Up @@ -248,7 +249,7 @@ grammar.
AccessModifier ::= (`private' | `protected') [AccessQualifier]
AccessQualifier ::= `[' (id | `this') `]'

Annotation ::= `@' SimpleType {ArgumentExprs} Apply(tpe, args)
Annotation ::= `@' SimpleType {ParArgumentExprs} Apply(tpe, args)

TemplateBody ::= [nl] `{' [SelfType] TemplateStat {semi TemplateStat} `} (self, stats)
TemplateStat ::= Import
Expand Down Expand Up @@ -301,7 +302,7 @@ grammar.
TemplateOpt ::= [`extends' Template | [nl] TemplateBody]
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
ConstrApps ::= ConstrApp {`with' ConstrApp}
ConstrApp ::= SimpleType {ArgumentExprs} Apply(tp, args)
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)

ConstrExpr ::= SelfInvocation
| ConstrBlock
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class GenBCode extends Phase {


def run(implicit ctx: Context): Unit = {
new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
new GenBCodePipeline(entryPoints.toList, new DottyBackendInterface()(ctx))(ctx).run(ctx.compilationUnit.tpdTree)
entryPoints.clear()
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/dotty/tools/dotc/config/CompilerCommand.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,19 @@ object CompilerCommand extends DotClass {

if (summary.errors.nonEmpty) {
summary.errors foreach (ctx.error(_))
ctx.echo(" dotc -help gives more information")
ctx.println(" dotc -help gives more information")
Nil
}
else if (settings.version.value) {
ctx.echo(versionMsg)
ctx.println(versionMsg)
Nil
}
else if (shouldStopWithInfo) {
ctx.echo(infoMessage)
ctx.println(infoMessage)
Nil
} else {
if (summary.arguments.isEmpty && !settings.resident.value)
ctx.echo(usageMessage)
ctx.println(usageMessage)
summary.arguments
}
}
Expand Down
27 changes: 27 additions & 0 deletions src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,31 @@ object Config {

/** Check that certain types cannot be created in erasedTypes phases */
final val checkUnerased = true


/** Initial size of superId table */
final val InitialSuperIdsSize = 4096

/** Initial capacity of uniques HashMap */
final val initialUniquesCapacity = 40000

/** How many recursive calls to NamedType#underlying are performed before logging starts. */
final val LogPendingUnderlyingThreshold = 50

/** How many recursive calls to isSubType are performed before logging starts. */
final val LogPendingSubTypesThreshold = 50

/** How many recursive calls to findMember are performed before logging names starts
* Note: this threshold has to be chosen carefully. Too large, and programs
* like tests/pos/IterableSelfRec go into polynomial (or even exponential?)
* compile time slowdown. Too small and normal programs will cause the compiler to
* do inefficient operations on findMember. The current value is determined
* so that (1) IterableSelfRec still compiles in reasonable time (< 10sec) (2) Compiling
* dotty itself only causes small pending names lists to be generated (we measured
* at max 6 elements) and these lists are never searched with contains.
*/
final val LogPendingFindMemberThreshold = 10

/** Maximal number of outstanding recursive calls to findMember */
final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4
}
29 changes: 11 additions & 18 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import util.{FreshNameCreator, SimpleMap, SourceFile, NoSource}
import typer._
import Implicits.ContextualImplicits
import config.Settings._
import config.Config
import reporting._
import collection.mutable
import collection.immutable.BitSet
Expand Down Expand Up @@ -164,6 +165,14 @@ object Contexts {
_typeComparer
}

/** Number of findMember calls on stack */
private[core] var findMemberCount: Int = 0

/** List of names which have a findMemberCall on stack,
* after Config.LogPendingFindMemberThreshold is reached.
*/
private[core] var pendingMemberSearches: List[Name] = Nil

/** The new implicit references that are introduced by this scope */
private var implicitsCache: ContextualImplicits = null
def implicits: ContextualImplicits = {
Expand Down Expand Up @@ -508,7 +517,7 @@ object Contexts {
def nextId = { _nextId += 1; _nextId }

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

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

// Types state
/** A table for hash consing unique types */
private[core] val uniques = new util.HashSet[Type](initialUniquesCapacity) {
private[core] val uniques = new util.HashSet[Type](Config.initialUniquesCapacity) {
override def hash(x: Type): Int = x.hash
}

Expand Down Expand Up @@ -614,20 +623,4 @@ object Contexts {
myBounds = myBounds.updated(sym, b)
def bounds = myBounds
}

/** Initial size of superId table */
private final val InitialSuperIdsSize = 4096

/** Initial capacity of uniques HashMap */
private[core] final val initialUniquesCapacity = 40000

/** How many recursive calls to NamedType#underlying are performed before
* logging starts.
*/
private[core] final val LogPendingUnderlyingThreshold = 50

/** How many recursive calls to isSubType are performed before
* logging starts.
*/
private[core] final val LogPendingSubTypesThreshold = 50
}
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ object Decorators {
def treatSingleArg(arg: Any) : Any =
try
arg match {
case arg: Showable => arg.show(ctx.fresh.addMode(Mode.FutureDefsOK))
case arg: Showable => arg.show(ctx.addMode(Mode.FutureDefsOK))
case _ => arg
}
catch {
Expand Down
10 changes: 8 additions & 2 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,18 @@ object NameOps {

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

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

/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ object StdNames {
final val WILDCARD_STAR: N = "_*"
final val REIFY_TREECREATOR_PREFIX: N = "$treecreator"
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
final val LAMBDA_ARG_PREFIX: N = "$hkArg$"
final val LAMBDA_ARG_PREFIX: N = "HK$"
final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head

final val Any: N = "Any"
Expand Down
17 changes: 10 additions & 7 deletions src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -515,25 +515,28 @@ class TypeApplications(val self: Type) extends AnyVal {
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams)
}

/** Test whether this type has a base type `B[T1, ..., Tn]` where the type parameters
* of `B` match one-by-one the variances of `tparams`, and where the lambda
* abstracted type
/** Test whether this type has a base type of the form `B[T1, ..., Bn]` where
* the type parameters of `B` match one-by-one the variances of `tparams`,
* and where the lambda abstracted type
*
* LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] }
* { type $hkArg$0 = T1; ...; type $hkArg$n = Tn }
*
* satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`.
* A type parameter matches a variance V if it has V as its variance or if V == 0.
* @param classBounds A hint to bound the search. Only types that derive from one of the
* classes in classBounds are considered.
*/
def testLifted(tparams: List[Symbol], p: Type => Boolean)(implicit ctx: Context): Boolean = {
def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = {
def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match {
case bc :: bcs1 =>
val tp = self.baseTypeWithArgs(bc)
val targs = tp.argInfos
val tycon = tp.withoutArgs(targs)
def variancesMatch(param1: Symbol, param2: Symbol) =
param2.variance == param2.variance || param2.variance == 0
if ((tycon.typeParams corresponds tparams)(variancesMatch)) {
if (classBounds.exists(tycon.derivesFrom(_)) &&
tycon.typeParams.corresponds(tparams)(variancesMatch)) {
val expanded = tycon.EtaExpand
val lifted = (expanded /: targs) { (partialInst, targ) =>
val tparam = partialInst.typeParams.head
Expand All @@ -548,7 +551,7 @@ class TypeApplications(val self: Type) extends AnyVal {
false
}
if (tparams.isEmpty) false
else if (typeParams.nonEmpty) p(EtaExpand) || tryLift(self.baseClasses)
else tryLift(self.baseClasses)
else if (typeParams.nonEmpty) p(EtaExpand) || classBounds.nonEmpty && tryLift(self.baseClasses)
else classBounds.nonEmpty && tryLift(self.baseClasses)
}
}
24 changes: 14 additions & 10 deletions src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
try {
recCount = recCount + 1
val result =
if (recCount < LogPendingSubTypesThreshold) firstTry(tp1, tp2)
if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
else monitoredIsSubType(tp1, tp2)
recCount = recCount - 1
if (!result) constraint = saved
Expand Down Expand Up @@ -345,7 +345,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
|| fourthTry(tp1, tp2)
)
normalPath ||
needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2))
needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2))
}
else // fast path, in particular for refinements resulting from parameterization.
isSubType(tp1, skipped2) &&
Expand Down Expand Up @@ -453,7 +453,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths
case tp1: RefinedType =>
isNewSubType(tp1.parent, tp2) ||
needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _))
needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil)
case AndType(tp11, tp12) =>
eitherIsSubType(tp11, tp2, tp12, tp2)
case JavaArrayType(elem1) =>
Expand All @@ -475,9 +475,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
val lambda = projection.prefix.LambdaClass(forcing = true)
lambda.exists && !other.isLambda &&
other.testLifted(lambda.typeParams,
if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix))
if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix),
if (inOrder) Nil else classBounds(projection.prefix))
}

/** The class symbols bounding the type of the `Apply` member of `tp` */
private def classBounds(tp: Type) = tp.member(tpnme.Apply).info.classSymbols

/** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time
* to keep the constraint as wide as possible. Specifically, if
*
Expand Down Expand Up @@ -1141,13 +1145,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
}

/** Show subtype goal that led to an assertion failure */
def showGoal(tp1: Type, tp2: Type) = {
println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = {
ctx.println(disambiguated(implicit ctx => s"assertion failure for ${tp1.show} <:< ${tp2.show}, frozen = $frozenConstraint"))
def explainPoly(tp: Type) = tp match {
case tp: PolyParam => println(s"polyparam ${tp.show} found in ${tp.binder.show}")
case tp: TypeRef if tp.symbol.exists => println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
case tp: TypeVar => println(s"typevar ${tp.show}, origin = ${tp.origin}")
case _ => println(s"${tp.show} is a ${tp.getClass}")
case tp: PolyParam => ctx.println(s"polyparam ${tp.show} found in ${tp.binder.show}")
case tp: TypeRef if tp.symbol.exists => ctx.println(s"typeref ${tp.show} found in ${tp.symbol.owner.show}")
case tp: TypeVar => ctx.println(s"typevar ${tp.show}, origin = ${tp.origin}")
case _ => ctx.println(s"${tp.show} is a ${tp.getClass}")
}
explainPoly(tp1)
explainPoly(tp2)
Expand Down
9 changes: 5 additions & 4 deletions src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -302,18 +302,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
// println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG
var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty
var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty
def normalizeToRef(tp: Type): TypeRef = tp match {
def normalizeToRef(tp: Type): TypeRef = tp.dealias match {
case tp: TypeRef =>
tp
case tp @ RefinedType(tp1, name: TypeName) =>
val prevInfo = refinements(name)
refinements = refinements.updated(name,
if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo)
formals = formals.updated(name, tp1.typeParamNamed(name))
normalizeToRef(tp1)
case tp: TypeRef =>
if (tp.symbol.info.isAlias) normalizeToRef(tp.info.bounds.hi)
else tp
case ErrorType =>
defn.AnyClass.typeRef
case AnnotatedType(_, tpe) =>
normalizeToRef(tpe)
case _ =>
throw new TypeError(s"unexpected parent type: $tp")
}
Expand Down
Loading