diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index b5be894402fa..feaca721ea58 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -244,7 +244,7 @@ object desugar { if (tdef.mods is PrivateLocalParam) { val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner)) .withMods(tdef.mods &~ PrivateLocal | ExpandedName) - val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam)) + val alias = cpy.TypeDef(tdef)(name = tdef.name, rhs = refOfDef(tparam)) .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic) Thicket(tparam, alias) } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4d4350f98651..3f99fa71bbe5 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 ea905c19f50a..240ad359bba1 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 5b7dc3d1d5b2..2bb26f327988 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -846,5 +846,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 c713cd54274f..a9487c30dd64 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -277,9 +277,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 @@ -364,7 +365,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 21a12dbb744b..a934b3ac6f82 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, _)))) @@ -1279,9 +1279,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 54251cbb1fd8..1249ba33511b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2567,8 +2567,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-kinded 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 { @@ -2606,7 +2618,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)) @@ -2639,7 +2651,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)) @@ -2659,28 +2671,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) } @@ -2693,7 +2704,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 cb1b56c3c3d5..98577f3c5070 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 80270aa25e0e..871f398388d5 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 a9ea49ad1fde..8ed5bd3c3ad4 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 67d44daa1ee7..ba5592bd6a89 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 0cb453b4c197..cdc28b23f2d5 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 f7dd725c8672..236088c3ff6e 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) diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala index cfb66fa568d8..ad8981ea21df 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala @@ -94,10 +94,7 @@ class DocASTPhase extends Phase { NonEntity else { val tparams = t.rhs.tpe match { - case tp: PolyType => tp.paramRefs.zip(tp.variances).map { case (tp, variance) => - val varianceSym = if (variance == 1) "+" else if (variance == -1) "-" else "" - varianceSym + tp.paramName.show - } + case tp: PolyType => tp.paramNames.map(_.show) case _ => Nil } TypeAliasImpl(sym, annotations(sym), flags(t), t.name.show.split("\\$\\$").last, path(sym), alias(t.rhs.tpe), tparams)