Skip to content

Change/higher kinded #137

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 23 commits into from
Jul 9, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
51563ae
Change definition of isLegalPrefix so that it allows projecting on an…
odersky Jun 12, 2014
96196c9
Names and definitions for Lambdas
odersky Jun 12, 2014
4e287d5
Add isLambda... tests
odersky Jun 12, 2014
c4f9683
Fixing hashing for RefinedThis types.
odersky Jun 12, 2014
a77a4f6
Fix RefinementType#member
odersky Jun 12, 2014
c2bcf2e
Optionally check variance of Lambda classes.
odersky Jun 12, 2014
c1b884b
Make Lambda#Apply a type field instead of a type parameter.
odersky Jun 12, 2014
8db6b3e
Harden allOverriddenSymbols
odersky Jun 12, 2014
9a6a4e8
More careful with lookupRefined
odersky Jun 13, 2014
cfd13f7
Fix to printing type parameters.
odersky Jun 13, 2014
099e5a6
More explanations for -Ycheck failures.
odersky Jun 13, 2014
388d9a8
Fixing subtyping of refined types
odersky Jun 15, 2014
7f72143
Handling higher-kinded types with lambdas
odersky Jun 18, 2014
f9625f9
Delete old, disabled code in TypeComparer
odersky Jun 18, 2014
7623856
Prevent a source of CyclicReference in refined printer
odersky Jun 19, 2014
91e44df
Fixes to lambda abstraction
odersky Jun 19, 2014
ad0600f
Add missing case where isHKSubType is needed
odersky Jun 20, 2014
7cf6202
Make refines work for aliases
odersky Jun 20, 2014
b3364db
Avoid caching values that depend on typevar state.
odersky Jun 20, 2014
c172148
Avoid crashing on name tests when name is empty.
odersky Jun 26, 2014
f823e42
Bring LambdaClass inline with its doc comment.
odersky Jun 26, 2014
f600df4
Improved documentation
odersky Jun 26, 2014
e710af6
Added test case for LMS inspired HK code.
odersky Jun 26, 2014
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
7 changes: 6 additions & 1 deletion src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object Config {
/** Show subtype traces for all deep subtype recursions */
final val traceDeepSubTypeRecursions = false

final val verboseExplainSubtype = true
final val verboseExplainSubtype = false

/** When set, use new signature-based matching.
* Advantantage of doing so: It's supposed to be faster
Expand All @@ -43,4 +43,9 @@ object Config {
* for large constraints.
*/
final val trackConstrDeps = true

/** Check that variances of lambda arguments match the
* variance of the underlying lambda class.
*/
final val checkLambdaVariance = false
}
47 changes: 30 additions & 17 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ class Definitions {
private def newTopClassSymbol(name: TypeName, flags: FlagSet, parents: List[TypeRef]) =
completeClass(newCompleteClassSymbol(ScalaPackageClass, name, flags, parents))

private def newTypeField(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) =
scope.enter(newSymbol(cls, name, flags, TypeBounds.empty))

private def newTypeParam(cls: ClassSymbol, name: TypeName, flags: FlagSet, scope: MutableScope) =
scope.enter(newSymbol(cls, name, flags | TypeParamCreationFlags, TypeBounds.empty))
newTypeField(cls, name, flags | TypeParamCreationFlags, scope)

private def newSyntheticTypeParam(cls: ClassSymbol, scope: MutableScope, paramFlags: FlagSet, suffix: String = "T0") =
newTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope)
Expand Down Expand Up @@ -421,59 +424,69 @@ class Definitions {

def functionArity(tp: Type) = tp.dealias.argInfos.length - 1

// ----- Higher kinds machinery ------------------------------------------
// ----- LambdaXYZ traits ------------------------------------------

private var _hkTraits: Set[Symbol] = Set()
private var myLambdaTraits: Set[Symbol] = Set()

/** The set of HigherKindedXYZ traits encountered so far */
def hkTraits: Set[Symbol] = _hkTraits
def lambdaTraits: Set[Symbol] = myLambdaTraits

private var hkTraitOfArity = mutable.Map[List[Int], ClassSymbol]()
private var lambdaTraitForVariances = mutable.Map[List[Int], ClassSymbol]()

/** The HigherKinded trait corresponding to symbols `boundSyms` (which are assumed
* to be the type parameters of a higher-kided type). This is a class symbol that
* would be generated by the following schema.
*
* class HigherKindedXYZ { type v_n _$hk$0; ...; type v_n _$Hk$n }
* class LambdaXYZ extends Object with P1 with ... with Pn {
* type v_1 $hk$Arg0; ...; type v_N $hk$ArgN;
* type Apply
* }
*
* Here:
*
* - XYZ is a string with one letter for each variant of a bound symbols,
* using `P` (positive variance), `N` (negative variance), `I` (invariant).
* - v_i are the variances of the bound symbols (i.e. +, -, or empty).
* - _$hk$i are higher-kinded parameter names, which are specially treated in type application.
* - XYZ is a string of length N with one letter for each variant of a bound symbol,
* using `P` (positive variance), `N` (negative variance), `I` (invariant).
* - for each positive or negative variance v_i there is a parent trait Pj which
* is the same as LambdaXYZ except that it has `I` in i-th position.
*/
def hkTrait(vcs: List[Int]) = {
def lambdaTrait(vcs: List[Int]): ClassSymbol = {
assert(vcs.nonEmpty)

def varianceFlags(v: Int) = v match {
case -1 => Contravariant
case 0 => Covariant
case 1 => EmptyFlags
case 0 => EmptyFlags
case 1 => Covariant
}

val completer = new LazyType {
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
val cls = denot.asClass.classSymbol
val paramDecls = newScope
for (i <- 0 until vcs.length)
newTypeParam(cls, tpnme.higherKindedParamName(i), EmptyFlags, paramDecls)
denot.info = ClassInfo(ScalaPackageClass.thisType, cls, List(ObjectClass.typeRef), paramDecls)
newTypeParam(cls, tpnme.lambdaArgName(i), varianceFlags(vcs(i)), paramDecls)
newTypeField(cls, tpnme.Apply, Covariant, paramDecls)
val parentTraitRefs =
for (i <- 0 until vcs.length if vcs(i) != 0)
yield lambdaTrait(vcs.updated(i, 0)).typeRef
denot.info = ClassInfo(
ScalaPackageClass.thisType, cls, ObjectClass.typeRef :: parentTraitRefs.toList, paramDecls)
}
}

val traitName = tpnme.higherKindedTraitName(vcs)
val traitName = tpnme.lambdaTraitName(vcs)

def createTrait = {
val cls = newClassSymbol(
ScalaPackageClass,
traitName,
Trait | Interface | Synthetic,
completer)
_hkTraits += cls
myLambdaTraits += cls
cls
}

hkTraitOfArity.getOrElseUpdate(vcs, createTrait)
lambdaTraitForVariances.getOrElseUpdate(vcs, createTrait)
}

// ----- primitive value class machinery ------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ object Denotations {
case info: SignedType =>
try info.signature
catch { // !!! DEBUG
case ex: MatchError =>
case ex: Throwable =>
println(s"cannot take signature of ${info.show}")
throw ex
}
Expand Down
7 changes: 4 additions & 3 deletions src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ object Flags {
final val Final = commonFlag(6, "final")

/** A method symbol. */
final val MethodCommon = commonFlag(7, "<method>")
final val Method = MethodCommon.toTermFlags
final val MethodOrHKCommon = commonFlag(7, "<method>")
final val Method = MethodOrHKCommon.toTermFlags
final val HigherKinded = MethodOrHKCommon.toTypeFlags

/** A (term or type) parameter to a class or method */
final val Param = commonFlag(8, "<param>")
Expand Down Expand Up @@ -411,7 +412,7 @@ object Flags {

/** Flags guaranteed to be set upon symbol creation */
final val FromStartFlags =
AccessFlags | Module | Package | Deferred | MethodCommon | Param | Scala2ExistentialCommon | Touched |
AccessFlags | Module | Package | Deferred | MethodOrHKCommon | Param | Scala2ExistentialCommon | Touched |
Static | CovariantCommon | ContravariantCommon | ExpandedName | AccessorOrSealed |
CaseAccessorOrTypeArgument | Frozen | Erroneous | ImplicitCommon | Permanent | SelfNameOrImplClass

Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/Hashable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ trait Hashable {
private def finishHash(hashCode: Int, arity: Int): Int =
avoidNotCached(hashing.finalizeHash(hashCode, arity))

protected final def identityHash = avoidNotCached(System.identityHashCode(this))
final def identityHash = avoidNotCached(System.identityHashCode(this))

private def finishHash(seed: Int, arity: Int, tp: Type): Int = {
val elemHash = tp.hash
Expand Down
28 changes: 8 additions & 20 deletions src/dotty/tools/dotc/core/NameOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ object NameOps {
def isSingletonName = name endsWith SINGLETON_SUFFIX
def isModuleClassName = name endsWith MODULE_SUFFIX
def isImportName = name startsWith IMPORT
def isInheritedName = name.head == '(' && name.startsWith(nme.INHERITED)
def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED)

def isModuleVarName(name: Name): Boolean =
name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX

/** Is name a variable name? */
def isVariableName: Boolean = {
def isVariableName: Boolean = name.length > 0 && {
val first = name.head
(((first.isLower && first.isLetter) || first == '_')
&& (name != false_)
Expand All @@ -84,16 +84,17 @@ object NameOps {
case raw.NE | raw.LE | raw.GE | EMPTY =>
false
case _ =>
name.last == '=' && name.head != '=' && isOperatorPart(name.head)
name.length > 0 && name.last == '=' && name.head != '=' && isOperatorPart(name.head)
}

/** Is this the name of a higher-kinded type parameter? */
def isHkParamName: Boolean = name(0) == '_' && name.startsWith(HK_PARAM_PREFIX)
/** 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)

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

/** If the name ends with $nn where nn are
* all digits, strip the $ and the digits.
Expand Down Expand Up @@ -177,19 +178,6 @@ object NameOps {
}
}

/** The variances of the higherKinded parameters of the trait named
* by this name.
* @pre The name is a higher-kinded trait name, i.e. it starts with HK_TRAIT_PREFIX
*/
def hkVariances: List[Int] = {
def varianceOfSuffix(suffix: Char): Int = {
val idx = tpnme.varianceSuffixes.indexOf(suffix)
assert(idx >= 0)
idx - 1
}
name.drop(tpnme.HK_TRAIT_PREFIX.length).toList.map(varianceOfSuffix)
}

/** The name of the generic runtime operation corresponding to an array operation */
def genericArrayOp: TermName = name match {
case nme.apply => nme.array_apply
Expand Down
9 changes: 5 additions & 4 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ 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$"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a semi-related note, could we switch to ☃ as our magic char, and reserve $ for class nesting and other java-mimicry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to do this yes. We have a to-do list item to rehaul the whole name mangling scheme.

final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head

final val Any: N = "Any"
final val AnyVal: N = "AnyVal"
Expand Down Expand Up @@ -249,8 +251,6 @@ object StdNames {
val SKOLEM: N = "<skolem>"
val SPECIALIZED_INSTANCE: N = "specInstance$"
val THIS: N = "_$this"
val HK_PARAM_PREFIX: N = "_$hk$"
val HK_TRAIT_PREFIX: N = "$HigherKinded$"

final val Nil: N = "Nil"
final val Predef: N = "Predef"
Expand Down Expand Up @@ -286,6 +286,7 @@ object StdNames {
val Flag : N = "Flag"
val Ident: N = "Ident"
val Import: N = "Import"
val LambdaPrefix: N = "Lambda$"
val Literal: N = "Literal"
val LiteralAnnotArg: N = "LiteralAnnotArg"
val Modifiers: N = "Modifiers"
Expand Down Expand Up @@ -645,8 +646,8 @@ object StdNames {
def syntheticTypeParamNames(num: Int): List[TypeName] =
(0 until num).map(syntheticTypeParamName)(breakOut)

def higherKindedTraitName(vcs: List[Int]): TypeName = HK_TRAIT_PREFIX ++ vcs.map(varianceSuffix).mkString
def higherKindedParamName(n: Int) = HK_PARAM_PREFIX ++ n.toString
def lambdaTraitName(vcs: List[Int]): TypeName = LambdaPrefix ++ vcs.map(varianceSuffix).mkString
def lambdaArgName(n: Int) = LAMBDA_ARG_PREFIX ++ n.toString

final val Conforms = encode("<:<")

Expand Down
10 changes: 9 additions & 1 deletion src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -321,9 +321,17 @@ object SymDenotations {
final def isAnonymousClass(implicit ctx: Context): Boolean =
initial.asSymDenotation.name startsWith tpnme.ANON_CLASS

/** Is this symbol a class representing a refinement? These classes
* are used only temporarily in Typer and Unpickler as an intermediate
* step for creating Refinement types.
*/
final def isRefinementClass(implicit ctx: Context): Boolean =
name.decode == tpnme.REFINE_CLASS

/** is this symbol a trait representing a type lambda? */
final def isLambdaTrait(implicit ctx: Context): Boolean =
isClass && name.startsWith(tpnme.LambdaPrefix)

/** Is this symbol a package object or its module class? */
def isPackageObject(implicit ctx: Context): Boolean = {
val poName = if (isType) nme.PACKAGE_CLS else nme.PACKAGE
Expand Down Expand Up @@ -699,7 +707,7 @@ object SymDenotations {

/** All symbols overriden by this denotation. */
final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
if (exists)
if (exists && owner.isClass)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

&& !isFinal for further optimization?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't understand: we are looking for the symbols which this symbol overrides. Finality of the overriding symbol does not change anything.

owner.info.baseClasses.tail.iterator map overriddenSymbol filter (_.exists)
else
Iterator.empty
Expand Down
Loading