aboutsummaryrefslogtreecommitdiff
path: root/compiler/src
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-03-14 16:47:55 +0100
committerMartin Odersky <odersky@gmail.com>2017-04-06 13:15:27 +0200
commit6545606cefaf8ec7090f8a601123bfae61b441a6 (patch)
tree8d8f261fb6c9807923ea60f4611b8c2b6e4f3ddf /compiler/src
parent62c2a1e2d6265cf7f096e4c4e51e4e883bce1514 (diff)
downloaddotty-6545606cefaf8ec7090f8a601123bfae61b441a6.tar.gz
dotty-6545606cefaf8ec7090f8a601123bfae61b441a6.tar.bz2
dotty-6545606cefaf8ec7090f8a601123bfae61b441a6.zip
Encode variances in parameter names
This leads to a slight overall simplification, harmonizes pickle format with internal representation, and makes MethodTypes and PolyTypes more similar to each other. I believe the change is useful as it is, but in particular it is a useful step for an eventual unification of MethodTypes and PolyTypes.
Diffstat (limited to 'compiler/src')
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Desugar.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/core/NameOps.scala26
-rw-r--r--compiler/src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--compiler/src/dotty/tools/dotc/core/TypeApplications.scala9
-rw-r--r--compiler/src/dotty/tools/dotc/core/TypeComparer.scala8
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala35
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala6
-rw-r--r--compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala8
-rw-r--r--compiler/src/dotty/tools/dotc/transform/FullParameterization.scala4
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala2
13 files changed, 76 insertions, 41 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
index 8499330fb..5f9d48798 100644
--- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
@@ -242,6 +242,12 @@ object desugar {
* class C { type v C$T; type v T = C$T }
*/
def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = {
+ val name =
+ if (tdef.name.hasVariance && tdef.mods.is(Param)) {
+ ctx.error(em"type parameter name may not start with `+' or `-'", tdef.pos)
+ ("$" + tdef.name).toTypeName
+ }
+ else tdef.name
if (tdef.mods is PrivateLocalParam) {
val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner))
.withMods(tdef.mods &~ PrivateLocal | ExpandedName)
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala
index 39b46cbfe..c48016b67 100644
--- a/compiler/src/dotty/tools/dotc/core/Definitions.scala
+++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala
@@ -152,7 +152,7 @@ class Definitions {
resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = {
val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount)
val tparamBounds = tparamNames map (_ => TypeBounds.empty)
- val ptype = PolyType(tparamNames, tparamNames.map(alwaysZero))(_ => tparamBounds, resultTypeFn)
+ val ptype = PolyType(tparamNames)(_ => tparamBounds, resultTypeFn)
enterMethod(cls, name, ptype, flags)
}
diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala
index ea905c19f..240ad359b 100644
--- a/compiler/src/dotty/tools/dotc/core/NameOps.scala
+++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala
@@ -196,6 +196,31 @@ object NameOps {
if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName
else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING)))
+ /** Name with variance prefix: `+` for covariant, `-` for contravariant */
+ def withVariance(v: Int): N =
+ if (hasVariance) dropVariance.withVariance(v)
+ else v match {
+ case -1 => likeTyped('-' +: name)
+ case 1 => likeTyped('+' +: name)
+ case 0 => name
+ }
+
+ /** Does name have a `+`/`-` variance prefix? */
+ def hasVariance: Boolean =
+ name.nonEmpty && name.head == '+' || name.head == '-'
+
+ /** Drop variance prefix if name has one */
+ def dropVariance: N = if (hasVariance) likeTyped(name.tail) else name
+
+ /** The variance as implied by the variance prefix, or 0 if there is
+ * no variance prefix.
+ */
+ def variance = name.head match {
+ case '-' => -1
+ case '+' => 1
+ case _ => 0
+ }
+
/** Translate a name into a list of simple TypeNames and TermNames.
* In all segments before the last, type/term is determined by whether
* the following separator char is '.' or '#'. The last segment
@@ -271,7 +296,6 @@ object NameOps {
else -1
}
-
/** The number of hops specified in an outer-select name */
def outerSelectHops: Int = {
require(isOuterSelect)
diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala
index bc3f96d91..30fa03537 100644
--- a/compiler/src/dotty/tools/dotc/core/StdNames.scala
+++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala
@@ -850,5 +850,4 @@ object StdNames {
val tpnme = new ScalaTypeNames
val jnme = new JavaTermNames
val jtpnme = new JavaTypeNames
-
}
diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala
index ba3e6a461..a7ce1ee85 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -278,9 +278,10 @@ class TypeApplications(val self: Type) extends AnyVal {
* TODO: Handle parameterized lower bounds
*/
def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = {
+ def nameWithVariance(tparam: TypeParamInfo) =
+ tparam.paramName.withVariance(tparam.paramVariance)
def expand(tp: Type) =
- PolyType(
- tparams.map(_.paramName), tparams.map(_.paramVariance))(
+ PolyType(tparams.map(nameWithVariance))(
tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
tl => tl.lifted(tparams, tp))
if (tparams.isEmpty) self
@@ -365,7 +366,9 @@ class TypeApplications(val self: Type) extends AnyVal {
case arg @ PolyType(tparams, body) if
!tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) &&
tparams.corresponds(hkParams)(varianceConforms) =>
- PolyType(tparams.map(_.paramName), hkParams.map(_.paramVariance))(
+ PolyType(
+ (tparams, hkParams).zipped.map((tparam, hkparam) =>
+ tparam.paramName.withVariance(hkparam.paramVariance)))(
tl => arg.paramBounds.map(_.subst(arg, tl).bounds),
tl => arg.resultType.subst(arg, tl)
)
diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
index 57dde3288..a8f92522c 100644
--- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -669,7 +669,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
val tparams1 = tparams1a.drop(lengthDiff)
variancesConform(tparams1, tparams) && {
if (lengthDiff > 0)
- tycon1b = PolyType(tparams1.map(_.paramName), tparams1.map(_.paramVariance))(
+ tycon1b = PolyType(tparams1.map(_.paramName))(
tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds),
tl => tycon1a.appliedTo(args1.take(lengthDiff) ++
tparams1.indices.toList.map(PolyParam(tl, _))))
@@ -1281,9 +1281,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2)
else
PolyType(
- paramNames = tpnme.syntheticTypeParamNames(tparams1.length),
- variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) =>
- (tparam1.paramVariance + tparam2.paramVariance) / 2))(
+ paramNames = (tpnme.syntheticTypeParamNames(tparams1.length), tparams1, tparams2)
+ .zipped.map((pname, tparam1, tparam2) =>
+ pname.withVariance((tparam1.paramVariance + tparam2.paramVariance) / 2)))(
paramBoundsExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) =>
tl.lifted(tparams1, tparam1.paramBoundsAsSeenFrom(tp1)).bounds &
tl.lifted(tparams2, tparam2.paramBoundsAsSeenFrom(tp2)).bounds),
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
index c80107f93..6b5be5489 100644
--- a/compiler/src/dotty/tools/dotc/core/Types.scala
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -2572,8 +2572,20 @@ object Types {
}
}
- /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */
- class PolyType(val paramNames: List[TypeName], val variances: List[Int])(
+ /** A type lambda of the form `[X_0 B_0, ..., X_n B_n] => T`
+ * This is used both as a type of a polymorphic method and as a type of
+ * a higher-kidned type parameter. Variances are encoded in parameter
+ * names. A name starting with `+` designates a covariant parameter,
+ * a name starting with `-` designates a contravariant parameter,
+ * and every other name designates a non-variant parameter.
+ *
+ * @param paramNames The names `X_0`, ..., `X_n`
+ * @param paramBoundsExp A function that, given the polytype itself, returns the
+ * parameter bounds `B_1`, ..., `B_n`
+ * @param resultTypeExp A function that, given the polytype itself, returns the
+ * result type `T`.
+ */
+ class PolyType(val paramNames: List[TypeName])(
paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)
extends CachedProxyType with BindingType with MethodOrPoly {
@@ -2611,7 +2623,7 @@ object Types {
paramBounds.mapConserve(_.substParams(this, argTypes).bounds)
def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType =
- PolyType.apply(paramNames, variances)(
+ PolyType.apply(paramNames)(
x => paramBounds mapConserve (_.subst(this, x).bounds),
x => resType.subst(this, x))
@@ -2644,7 +2656,7 @@ object Types {
case t => mapOver(t)
}
}
- PolyType(paramNames ++ that.paramNames, variances ++ that.variances)(
+ PolyType(paramNames ++ that.paramNames)(
x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++
that.paramBounds.mapConserve(shift(_).subst(that, x).bounds),
x => shift(that.resultType).subst(that, x).subst(this, x))
@@ -2664,28 +2676,27 @@ object Types {
case other: PolyType =>
other.paramNames == this.paramNames &&
other.paramBounds == this.paramBounds &&
- other.resType == this.resType &&
- other.variances == this.variances
+ other.resType == this.resType
case _ => false
}
- override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)"
+ override def toString = s"PolyType($paramNames, $paramBounds, $resType)"
- override def computeHash = doHash(variances ::: paramNames, resType, paramBounds)
+ override def computeHash = doHash(paramNames, resType, paramBounds)
}
object PolyType {
- def apply(paramNames: List[TypeName], variances: List[Int])(
+ def apply(paramNames: List[TypeName])(
paramBoundsExp: PolyType => List[TypeBounds],
resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = {
- unique(new PolyType(paramNames, variances)(paramBoundsExp, resultTypeExp))
+ unique(new PolyType(paramNames)(paramBoundsExp, resultTypeExp))
}
def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] =
Some((tl.typeParams, tl.resType))
def any(n: Int)(implicit ctx: Context) =
- apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))(
+ apply(tpnme.syntheticTypeParamNames(n))(
pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType)
}
@@ -2698,7 +2709,7 @@ object Types {
def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n)
def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds = paramBounds
def paramBoundsOrCompleter(implicit ctx: Context): Type = paramBounds
- def paramVariance(implicit ctx: Context): Int = tl.variances(n)
+ def paramVariance(implicit ctx: Context): Int = tl.paramNames(n).variance
def toArg: Type = PolyParam(tl, n)
def paramRef(implicit ctx: Context): Type = PolyParam(tl, n)
}
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
index cb1b56c3c..98577f3c5 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala
@@ -151,7 +151,7 @@ Standard-Section: "ASTs" TopLevelStat*
BIND Length boundName_NameRef bounds_Type
// for type-variables defined in a type pattern
BYNAMEtype underlying_Type
- POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/=
+ POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing)
METHODtype Length result_Type NamesTypes // needed for refinements
PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements
SHARED type_ASTRef
@@ -546,8 +546,4 @@ object TastyFormat {
case POLYtype | METHODtype => -1
case _ => 0
}
-
- /** Map between variances and name prefixes */
- val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+')
- val prefixToVariance = Map('-' -> -1, '=' -> 0, '+' -> 1)
}
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
index 80270aa25..871f39838 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
@@ -255,9 +255,7 @@ class TreePickler(pickler: TastyPickler) {
pickleType(tpe.underlying)
case tpe: PolyType =>
writeByte(POLYtype)
- val paramNames = tpe.typeParams.map(tparam =>
- varianceToPrefix(tparam.paramVariance) +: tparam.paramName)
- pickleMethodic(tpe.resultType, paramNames, tpe.paramBounds)
+ pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramBounds)
case tpe: MethodType if richTypes =>
writeByte(METHODtype)
pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes)
diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
index 88b6eef7a..6c6ebc0a3 100644
--- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
+++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
@@ -268,10 +268,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle
registerSym(start, sym)
TypeRef.withFixedSym(NoPrefix, sym.name, sym)
case POLYtype =>
- val (rawNames, paramReader) = readNamesSkipParams
- val (variances, paramNames) = rawNames
- .map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip
- val result = PolyType(paramNames, variances)(
+ val (paramNames, paramReader) = readNamesSkipParams
+ val result = PolyType(paramNames.map(_.toTypeName))(
pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)),
pt => readType())
goto(end)
diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
index 67d44daa1..ba5592bd6 100644
--- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -176,10 +176,9 @@ class PlainPrinter(_ctx: Context) extends Printer {
case tp: ExprType =>
changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) }
case tp: PolyType =>
- def paramText(variance: Int, name: Name, bounds: TypeBounds): Text =
- varianceString(variance) ~ name.toString ~ toText(bounds)
+ def paramText(name: Name, bounds: TypeBounds): Text = name.toString ~ toText(bounds)
changePrec(GlobalPrec) {
- "[" ~ Text((tp.variances, tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~
+ "[" ~ Text((tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~
"]" ~ (" => " provided !tp.resultType.isInstanceOf[MethodType]) ~
toTextGlobal(tp.resultType)
}
@@ -209,7 +208,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
protected def polyParamNameString(name: TypeName): String = name.toString
- protected def polyParamNameString(param: PolyParam): String = polyParamNameString(param.binder.paramNames(param.paramNum))
+ protected def polyParamNameString(param: PolyParam): String =
+ polyParamNameString(param.binder.paramNames(param.paramNum))
/** The name of the symbol without a unique id. Under refined printing,
* the decoded original name.
diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala
index 0cb453b4c..cdc28b23f 100644
--- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala
+++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala
@@ -122,14 +122,14 @@ trait FullParameterization {
info match {
case info: PolyType =>
- PolyType(info.paramNames ++ ctnames, info.variances ++ ctvariances)(
+ PolyType(info.paramNames ++ ctnames)(
pt =>
(info.paramBounds.map(mapClassParams(_, pt).bounds) ++
mappedClassBounds(pt)).mapConserve(_.subst(info, pt).bounds),
pt => resultType(mapClassParams(_, pt)).subst(info, pt))
case _ =>
if (ctparams.isEmpty) resultType(identity)
- else PolyType(ctnames, ctvariances)(mappedClassBounds, pt => resultType(mapClassParams(_, pt)))
+ else PolyType(ctnames)(mappedClassBounds, pt => resultType(mapClassParams(_, pt)))
}
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
index f96b8ae1d..d61e24261 100644
--- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -401,7 +401,7 @@ object ProtoTypes {
/** Create a new polyparam that represents a dependent method parameter singleton */
def newDepPolyParam(tp: Type)(implicit ctx: Context): PolyParam = {
- val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil, 0 :: Nil)(
+ val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil)(
pt => TypeBounds.upper(AndType(tp, defn.SingletonType)) :: Nil,
pt => defn.AnyType)
ctx.typeComparer.addToConstraint(poly, Nil)