From 987c5905b1024aa9e62ac72aee2d1d6f63cb2110 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Nov 2016 14:53:57 +0100 Subject: [PATCH 01/10] Add assignType for RefinedTypeTrees If we want to pickle type trees, we need a type assigner for RefinedTypeTree. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 13 ++++++++++++- src/dotty/tools/dotc/typer/Typer.scala | 15 ++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index b1fed308cfd0..259b3b83ec09 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -431,7 +431,18 @@ trait TypeAssigner { def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = tree.withType(left.tpe | right.tpe) - // RefinedTypeTree is missing, handled specially in Typer and Unpickler. + /** Assign type of RefinedType. + * Refinements are typed as if they were members of refinement class `refineCls`. + */ + def assignType(tree: untpd.RefinedTypeTree, parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context) = { + def addRefinement(parent: Type, refinement: Tree): Type = { + val rsym = refinement.symbol + val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info + RefinedType(parent, rsym.name, rinfo) + } + val refined = (parent.tpe /: refinements)(addRefinement) + tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) + } def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { val tparams = tycon.tpe.typeParams diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index a93262314d22..6b69a859e482 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1008,23 +1008,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val refineCls = createSymbol(refineClsDef).asClass val TypeDef(_, impl: Template) = typed(refineClsDef) val refinements1 = impl.body - val seen = mutable.Set[Symbol]() assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1") - def addRefinement(parent: Type, refinement: Tree): Type = { + val seen = mutable.Set[Symbol]() + for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions typr.println(s"adding refinement $refinement") checkRefinementNonCyclic(refinement, refineCls, seen) val rsym = refinement.symbol if (rsym.is(Method) && rsym.allOverriddenSymbols.isEmpty) - ctx.error(i"refinement $rsym without matching type in parent $parent", refinement.pos) - val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info - RefinedType(parent, rsym.name, rinfo) - // todo later: check that refinement is within bounds + ctx.error(i"refinement $rsym without matching type in parent $tpt1", refinement.pos) } - val refined = (tpt1.tpe /: refinements1)(addRefinement) - val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1).withType( - RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) - typr.println(i"typed refinement: ${res.tpe}") - res + assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls) } def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") { From 49022717bc0e8c0de8834f4cde5021e1da8c0ea0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Nov 2016 14:54:54 +0100 Subject: [PATCH 02/10] Make test more robust Pick a less common name for the missing identifier. Depending on my classpath I sometimes got `x is not a package` as an additional error for this one. --- tests/neg/i1605.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg/i1605.scala b/tests/neg/i1605.scala index 776f2df47010..f5fd9f29bde5 100644 --- a/tests/neg/i1605.scala +++ b/tests/neg/i1605.scala @@ -1,5 +1,5 @@ object Test { def foo = inlineMe - inline def inlineMe = 1 + x // error + inline def inlineMe = 1 + x2233 // error } From 49cef124a7be97647db3ccdbb0b775f53e5265d0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Nov 2016 17:01:30 +0100 Subject: [PATCH 03/10] Replace PolyType.fromSymbols with LambdaAbstract As a side effect, avoid creating synthetic parameters in lambda abstract. --- .../tools/dotc/core/TypeApplications.scala | 5 ++-- src/dotty/tools/dotc/core/Types.scala | 8 +------ .../dotc/core/classfile/ClassfileParser.scala | 2 +- .../core/unpickleScala2/Scala2Unpickler.scala | 24 ++++--------------- src/dotty/tools/dotc/typer/Inliner.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 ++-- 7 files changed, 14 insertions(+), 33 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 8aaf77032153..70819e590e89 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -340,10 +340,11 @@ class TypeApplications(val self: Type) extends AnyVal { def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = { def expand(tp: Type) = PolyType( - tpnme.syntheticLambdaParamNames(tparams.length), tparams.map(_.paramVariance))( + tparams.map(_.paramName), tparams.map(_.paramVariance))( tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), tl => tl.lifted(tparams, tp)) - self match { + if (tparams.isEmpty) self + else self match { case self: TypeAlias => self.derivedTypeAlias(expand(self.alias)) case self @ TypeBounds(lo, hi) => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index ce308412bf79..a90fec2b4639 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2524,7 +2524,7 @@ object Types { case _: MethodType => true case _ => false } - + /** Is this polytype a higher-kinded type lambda as opposed to a polymorphic? * method type? Only type lambdas get created with variances, that's how we can tell. */ @@ -2619,12 +2619,6 @@ object Types { unique(new PolyType(paramNames, vs)(paramBoundsExp, resultTypeExp)) } - def fromSymbols(tparams: List[Symbol], resultType: Type)(implicit ctx: Context): Type = - if (tparams.isEmpty) resultType - else apply(tparams map (_.name.asTypeName), tparams.map(_.variance))( - pt => tparams.map(tparam => pt.lifted(tparams, tparam.info).bounds), - pt => pt.lifted(tparams, resultType)) - def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = Some((tl.typeParams, tl.resType)) diff --git a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 1570dbca09f6..97a82e80deea 100644 --- a/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -217,7 +217,7 @@ class ClassfileParser( if (isEnum) denot.info = ConstantType(Constant(sym)) if (isConstructor) stripOuterParamFromConstructor() setPrivateWithin(denot, jflags) - denot.info = depoly(parseAttributes(sym, denot.info), denot) + denot.info = translateTempPoly(parseAttributes(sym, denot.info)) if (isConstructor) normalizeConstructorInfo() if ((denot is Flags.Method) && (jflags & JAVA_ACC_VARARGS) != 0) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 70148b3e2a61..b01f6cc6ad45 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -40,29 +40,15 @@ object Scala2Unpickler { /** Temporary type for classinfos, will be decomposed on completion of the class */ case class TempClassInfoType(parentTypes: List[Type], decls: Scope, clazz: Symbol) extends UncachedGroundType - /** Convert temp poly type to some native Dotty idiom. - * @param denot The denotation that gets the converted type as info. - * If `denot` is not an abstract type, this simply returns an equivalent `PolyType`. - * If `denot` is an abstract type, it converts a - * - * TempPolyType(List(v_1 T_1, ..., v_n T_n), lo .. hi) - * - * to a type lambda using `parameterizeWith/LambdaAbstract`. - */ - def depoly(tp: Type, denot: SymDenotation)(implicit ctx: Context): Type = tp match { - case TempPolyType(tparams, restpe) => - if (denot.isType) { - assert(!denot.isClass) - restpe.LambdaAbstract(tparams) - } - else - PolyType.fromSymbols(tparams, restpe) + /** Convert temp poly type to poly type and leave other types alone. */ + def translateTempPoly(tp: Type)(implicit ctx: Context): Type = tp match { + case TempPolyType(tparams, restpe) => restpe.LambdaAbstract(tparams) case tp => tp } def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { assert(denot.isConstructor) - denot.info = PolyType.fromSymbols(denot.owner.typeParams, denot.info) + denot.info = denot.info.LambdaAbstract(denot.owner.typeParams) } /** Convert array parameters denoting a repeated parameter of a Java method @@ -549,7 +535,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val selfInfo = if (atEnd) NoType else readTypeRef() setClassInfo(denot, tp, selfInfo) case denot => - val tp1 = depoly(tp, denot) + val tp1 = translateTempPoly(tp) denot.info = if (tag == ALIASsym) TypeAlias(tp1) else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false) diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala index 6499167ad5fc..f6616d329324 100644 --- a/src/dotty/tools/dotc/typer/Inliner.scala +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -120,7 +120,7 @@ object Inliner { // Abstract accessed type over local refs def abstractQualType(mtpe: Type): Type = if (localRefs.isEmpty) mtpe - else PolyType.fromSymbols(localRefs.map(_.symbol), mtpe).asInstanceOf[PolyType].flatten + else mtpe.LambdaAbstract(localRefs.map(_.symbol)).asInstanceOf[PolyType].flatten val accessorType = abstractQualType(addQualType(dealiasMap(accessedType))) val accessor = accessorSymbol(tree, accessorType).asTerm diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index dd7326fae49a..77f8ff78696a 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -113,7 +113,7 @@ trait NamerContextOps { this: Context => if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType make.fromSymbols(params, resultType) } - if (typeParams.nonEmpty) PolyType.fromSymbols(typeParams, monotpe) + if (typeParams.nonEmpty) monotpe.LambdaAbstract(typeParams) else if (valueParamss.isEmpty) ExprType(monotpe) else monotpe } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 259b3b83ec09..1599d95e64ba 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -431,7 +431,7 @@ trait TypeAssigner { def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = tree.withType(left.tpe | right.tpe) - /** Assign type of RefinedType. + /** Assign type of RefinedType. * Refinements are typed as if they were members of refinement class `refineCls`. */ def assignType(tree: untpd.RefinedTypeTree, parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context) = { @@ -467,7 +467,7 @@ trait TypeAssigner { } def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = - tree.withType(PolyType.fromSymbols(tparamDefs.map(_.symbol), body.tpe)) + tree.withType(body.tpe.LambdaAbstract(tparamDefs.map(_.symbol))) def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = tree.withType(ExprType(result.tpe)) From f3c8fdddcc7a5aef1c206afb0942d4a961ef1186 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Nov 2016 17:07:57 +0100 Subject: [PATCH 04/10] Merge syntheticTypeParamNames and syntheticLambdaParamNames It seems like overengineering to use different names for poly methods in definitions and synthetic lambdas. --- src/dotty/tools/dotc/core/StdNames.scala | 6 +----- src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 920c9635e62d..c2a14b36f7a4 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -735,15 +735,11 @@ object StdNames { class ScalaTypeNames extends ScalaNames[TypeName] { protected implicit def fromString(s: String): TypeName = typeName(s) - def syntheticTypeParamName(i: Int): TypeName = "T" + i - def syntheticLambdaParamName(i: Int): TypeName = "X" + i + def syntheticTypeParamName(i: Int): TypeName = "X" + i def syntheticTypeParamNames(num: Int): List[TypeName] = (0 until num).map(syntheticTypeParamName)(breakOut) - def syntheticLambdaParamNames(num: Int): List[TypeName] = - (0 until num).map(syntheticLambdaParamName)(breakOut) - final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1980fe50df91..a34ebe9eac1c 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1267,7 +1267,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2) else PolyType( - paramNames = tpnme.syntheticLambdaParamNames(tparams1.length), + paramNames = tpnme.syntheticTypeParamNames(tparams1.length), variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) => (tparam1.paramVariance + tparam2.paramVariance) / 2))( paramBoundsExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a90fec2b4639..5ae17affbf5a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2623,7 +2623,7 @@ object Types { Some((tl.typeParams, tl.resType)) def any(n: Int)(implicit ctx: Context) = - apply(tpnme.syntheticLambdaParamNames(n), List.fill(n)(0))( + apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))( pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) } From 97b6985c34915b58e0c81fbab464f4bd532c27d0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 11 Nov 2016 22:20:24 +0100 Subject: [PATCH 05/10] Clean up parameterized typedefs Express them in terms PolyTypeTrees rather than having an irregular, untyped only tparams field. This is necessary if we want to pickle type trees instead of types, because now the rhs of a typedef tells the whole story, so we are not required any longer to use the info of the symbol. --- src/dotty/tools/dotc/ast/Desugar.scala | 5 ++-- src/dotty/tools/dotc/ast/TreeInfo.scala | 17 +++++++----- src/dotty/tools/dotc/ast/Trees.scala | 27 +++++++------------ src/dotty/tools/dotc/ast/untpd.scala | 23 +++------------- .../tools/dotc/core/tasty/TreePickler.scala | 8 ++---- .../tools/dotc/core/tasty/TreeUnpickler.scala | 5 +++- .../tools/dotc/parsing/JavaParsers.scala | 9 +++---- src/dotty/tools/dotc/parsing/Parsers.scala | 6 ++--- .../tools/dotc/printing/RefinedPrinter.scala | 25 +++++++++-------- .../tools/dotc/transform/TreeTransform.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 16 ++++++++--- src/dotty/tools/dotc/typer/Typer.scala | 12 +++++++-- test/test/DeSugarTest.scala | 2 +- 13 files changed, 77 insertions(+), 80 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 349fbfb2c1a9..3c510c7b9e99 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -234,7 +234,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), tparams = Nil) + val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam)) .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic) Thicket(tparam, alias) } @@ -461,8 +461,7 @@ object desugar { val vparamAccessors = derivedVparamss.flatten.map(_.withMods(originalVparams.next.mods | caseAccessor)) cpy.TypeDef(cdef)( rhs = cpy.Template(impl)(constr, parents1, self1, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), - tparams = Nil) + tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)) } // install the watch on classTycon diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 8b291517447f..d1e6bd38a83a 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -272,7 +272,12 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case mdef: ValOrDefDef => mdef.unforcedRhs == EmptyTree && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor) case mdef: TypeDef => - mdef.rhs.isEmpty || mdef.rhs.isInstanceOf[TypeBoundsTree] + def isBounds(rhs: Tree): Boolean = rhs match { + case _: TypeBoundsTree => true + case PolyTypeTree(_, body) => isBounds(body) + case _ => false + } + mdef.rhs.isEmpty || isBounds(mdef.rhs) case _ => false } @@ -382,9 +387,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => def isIdempotentRef(tree: Tree)(implicit ctx: Context) = refPurity(tree) >= Idempotent - /** If `tree` is a constant expression, its value as a Literal, + /** If `tree` is a constant expression, its value as a Literal, * or `tree` itself otherwise. - * + * * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. * Example * @@ -410,11 +415,11 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => * * Revisit this issue once we have implemented `inline`. Then we can demand * purity of the prefix unless the selection goes to an inline val. - * + * * Note: This method should be applied to all term tree nodes that are not literals, * that can be idempotent, and that can have constant types. So far, only nodes - * of the following classes qualify: - * + * of the following classes qualify: + * * Ident * Select * TypeApply diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 9108a4d227b4..78ac66812596 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -60,18 +60,18 @@ object Trees { with Cloneable { if (Stats.enabled) ntrees += 1 - + private def nxId = { nextId += 1 //assert(nextId != 199, this) - nextId + nextId } /** A unique identifier for this tree. Used for debugging, and potentially * tracking presentation compiler interactions */ private var myUniqueId: Int = nxId - + def uniqueId = myUniqueId /** The type constructor at the root of the tree */ @@ -192,7 +192,7 @@ object Trees { override def hashCode(): Int = uniqueId // for debugging; was: System.identityHashCode(this) override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - + override def clone: Tree[T] = { val tree = super.clone.asInstanceOf[Tree[T]] tree.myUniqueId = nxId @@ -653,12 +653,6 @@ object Trees { /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] - - /** If this a non-class type definition, its type parameters. - * Can be different from Nil only for PolyTypeDefs, which are always - * untyped and get eliminated during desugaring. - */ - def tparams: List[untpd.TypeDef] = Nil } /** extends parents { self => body } */ @@ -1023,9 +1017,9 @@ object Trees { case tree: DefDef if (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.unforcedRhs) => tree case _ => finalize(tree, untpd.DefDef(name, tparams, vparamss, tpt, rhs)) } - def TypeDef(tree: Tree)(name: TypeName, rhs: Tree, tparams: List[untpd.TypeDef]): TypeDef = tree match { - case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) && (tparams eq tree.tparams) => tree - case _ => finalize(tree, untpd.TypeDef(name, tparams, rhs)) + def TypeDef(tree: Tree)(name: TypeName, rhs: Tree): TypeDef = tree match { + case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree + case _ => finalize(tree, untpd.TypeDef(name, rhs)) } def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList): Template = tree match { case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.unforcedBody) => tree @@ -1064,8 +1058,8 @@ object Trees { ValDef(tree: Tree)(name, tpt, rhs) def DefDef(tree: DefDef)(name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: LazyTree = tree.unforcedRhs): DefDef = DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs) - def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs, tparams: List[untpd.TypeDef] = tree.tparams): TypeDef = - TypeDef(tree: Tree)(name, rhs, tparams) + def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs): TypeDef = + TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, self, body) } @@ -1146,7 +1140,7 @@ object Trees { case tree @ DefDef(name, tparams, vparamss, tpt, _) => cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) case tree @ TypeDef(name, rhs) => - cpy.TypeDef(tree)(name, transform(rhs), tree.tparams) + cpy.TypeDef(tree)(name, transform(rhs)) case tree @ Template(constr, parents, self, _) => cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body)) case Import(expr, selectors) => @@ -1294,7 +1288,6 @@ object Trees { case tree: Bind => cpy.Bind(tree)(newName, tree.body) case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName) case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName) - case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(newName.asTypeName, tree.tparams, tree.rhs).withMods(tree.rawMods) case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName) } }.asInstanceOf[tree.ThisTree[T]] diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index f259bdc579fc..ac35ad09c208 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -80,9 +80,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree]) extends TypTree case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree - class PolyTypeDef(name: TypeName, override val tparams: List[TypeDef], rhs: Tree) - extends TypeDef(name, rhs) - /** A block arising from a right-associative infix operation, where, e.g. * * a +: b @@ -310,9 +307,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def TypeTree(tpe: Type)(implicit ctx: Context): TypedSplice = TypedSplice(TypeTree().withTypeUnchecked(tpe)) - def TypeDef(name: TypeName, tparams: List[TypeDef], rhs: Tree): TypeDef = - if (tparams.isEmpty) TypeDef(name, rhs) else new PolyTypeDef(name, tparams, rhs) - def unitLiteral = Literal(Constant(())) def ref(tp: NamedType)(implicit ctx: Context): Tree = @@ -348,6 +342,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def makeSyntheticParameter(n: Int = 1, tpt: Tree = TypeTree())(implicit ctx: Context): ValDef = ValDef(nme.syntheticParamName(n), tpt, EmptyTree).withFlags(SyntheticTermParam) + def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context) = + if (tparams.isEmpty) tpt else PolyTypeTree(tparams, tpt) + /** A reference to given definition. If definition is a repeated * parameter, the reference will be a repeated argument. */ @@ -392,10 +389,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => untpd.ParsedTry(expr, handler, finalizer).withPos(tree.pos) } - def PolyTypeDef(tree: Tree)(name: TypeName, tparams: List[TypeDef], rhs: Tree) = tree match { - case tree: PolyTypeDef if (name eq tree.name) && (tparams eq tree.tparams) && (rhs eq tree.rhs) => tree - case _ => new PolyTypeDef(name, tparams, rhs).withPos(tree.pos) - } def SymbolLit(tree: Tree)(str: String) = tree match { case tree: SymbolLit if str == tree.str => tree case _ => untpd.SymbolLit(str).withPos(tree.pos) @@ -506,8 +499,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) - case tree: PolyTypeDef => - cpy.PolyTypeDef(tree)(tree.name, transformSub(tree.tparams), transform(tree.rhs)) case _ => super.transform(tree) } @@ -553,8 +544,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(x, bounds), cxBounds) case PatDef(mods, pats, tpt, rhs) => this(this(this(x, pats), tpt), rhs) - case tree: PolyTypeDef => - this(this(x, tree.tparams), tree.rhs) case TypedSplice(tree) => this(x, tree) case _ => @@ -566,10 +555,4 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { class UntypedDeepFolder[X](f: (X, Tree) => X) extends UntypedTreeAccumulator[X] { def apply(x: X, tree: Tree)(implicit ctx: Context): X = foldOver(f(x, tree), tree) } - - override def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[Untyped] = tree match { - case t: PolyTypeDef => - cpy.PolyTypeDef(t)(newName.asTypeName, t.tparams, t.rhs).asInstanceOf[tree.ThisTree[Untyped]] - case _ => super.rename(tree, newName) - } } diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 9dfb78798427..98a369f25608 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -49,10 +49,6 @@ class TreePickler(pickler: TastyPickler) { case None => } } - - def rhs(tdef: TypeDef)(implicit ctx: Context) = - if (tdef.symbol.isClass) tdef.rhs - else TypeTree(tdef.symbol.info).withPos(tdef.rhs.pos) private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) @@ -336,7 +332,7 @@ class TreePickler(pickler: TastyPickler) { tree match { case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt) case tree: DefDef => pickleDef(PARAM, tree.symbol, tree.tpt, tree.rhs) - case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, rhs(tree)) + case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs) } } @@ -478,7 +474,7 @@ class TreePickler(pickler: TastyPickler) { } pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) case tree: TypeDef => - pickleDef(TYPEDEF, tree.symbol, rhs(tree)) + pickleDef(TYPEDEF, tree.symbol, tree.rhs) case tree: Template => registerDef(tree.symbol) writeByte(TEMPLATE) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d2605afea91b..90f718402fc0 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -696,7 +696,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle TypeDef(readTemplate(localCtx)) } else { val rhs = readTpt() - sym.info = rhs.tpe + sym.info = rhs.tpe match { + case _: TypeBounds | _: ClassInfo => rhs.tpe + case _ => TypeAlias(rhs.tpe, sym.variance) + } TypeDef(rhs) } case PARAM => diff --git a/src/dotty/tools/dotc/parsing/JavaParsers.scala b/src/dotty/tools/dotc/parsing/JavaParsers.scala index b6a423dc7c85..0f63b25bb2fd 100644 --- a/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -417,7 +417,7 @@ object JavaParsers { atPos(in.offset) { val name = identForType() val hi = if (in.token == EXTENDS) { in.nextToken() ; bound() } else EmptyTree - TypeDef(name, Nil, TypeBoundsTree(EmptyTree, hi)).withMods(Modifiers(flags)) + TypeDef(name, TypeBoundsTree(EmptyTree, hi)).withMods(Modifiers(flags)) } def bound(): Tree = @@ -625,8 +625,7 @@ object JavaParsers { val template = cdef.rhs.asInstanceOf[Template] cpy.TypeDef(cdef)(cdef.name, cpy.Template(template)(template.constr, template.parents, template.self, - importCompanionObject(cdef) :: template.body), - cdef.tparams).withMods(cdef.mods) + importCompanionObject(cdef) :: template.body)).withMods(cdef.mods) } List(makeCompanionObject(cdefNew, statics), cdefNew) @@ -715,7 +714,7 @@ object JavaParsers { val (statics, body) = typeBody(INTERFACE, name, tparams) val iface = atPos(start, nameOffset) { TypeDef( - name, tparams, + name, makeTemplate(parents, body, tparams, false)).withMods(mods | Flags.Trait | Flags.JavaInterface | Flags.Abstract) } addCompanionObject(statics, iface) @@ -830,7 +829,7 @@ object JavaParsers { Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), List(Literal(Constant(null)),Literal(Constant(0)))) val enum = atPos(start, nameOffset) { - TypeDef(name, List(), + TypeDef(name, makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum) } addCompanionObject(consts ::: statics ::: predefs, enum) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index f442c13b3713..e0c6be8c8931 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1646,7 +1646,7 @@ object Parsers { val bounds = if (isConcreteOwner) typeParamBounds(name) else typeBounds() - TypeDef(name, hkparams, bounds).withMods(mods) + TypeDef(name, lambdaAbstract(hkparams, bounds)).withMods(mods) } } commaSeparated(typeParam) @@ -1956,9 +1956,9 @@ object Parsers { in.token match { case EQUALS => in.nextToken() - TypeDef(name, tparams, typ()).withMods(mods).setComment(docstring) + TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(docstring) case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF => - TypeDef(name, tparams, typeBounds()).withMods(mods).setComment(docstring) + TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(docstring) case _ => syntaxErrorOrIncomplete("`=', `>:', or `<:' expected") EmptyTree diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7aaf2e19005e..39a21b17be66 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -351,7 +351,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case SeqLiteral(elems, elemtpt) => "[" ~ toTextGlobal(elems, ",") ~ " : " ~ toText(elemtpt) ~ "]" case tree @ Inlined(call, bindings, body) => - (("/* inlined from " ~ toText(call) ~ "*/ ") provided !homogenizedView) ~ + (("/* inlined from " ~ toText(call) ~ "*/ ") provided !homogenizedView) ~ blockText(bindings :+ body) case tpt: untpd.DerivedTypeTree => "" @@ -402,24 +402,27 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } } case tree @ TypeDef(name, rhs) => - def typeDefText(rhsText: Text) = + def typeDefText(tparamsText: => Text, rhsText: => Text) = dclTextOr { modText(tree.mods, "type") ~~ (varianceText(tree.mods) ~ nameIdText(tree)) ~ withEnclosingDef(tree) { - val rhsText1 = if (tree.hasType) toText(tree.symbol.info) else rhsText - tparamsText(tree.tparams) ~ rhsText1 + if (tree.hasType) toText(tree.symbol.info) // TODO: always print RHS, once we pickle/unpickle type trees + else tparamsText ~ rhsText } } - rhs match { + def recur(rhs: Tree, tparamsTxt: => Text): Text = rhs match { case impl: Template => modText(tree.mods, if ((tree).mods is Trait) "trait" else "class") ~~ nameIdText(tree) ~ withEnclosingDef(tree) { toTextTemplate(impl) } ~ (if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "") case rhs: TypeBoundsTree => - typeDefText(toText(rhs)) - case _ => - typeDefText(optText(rhs)(" = " ~ _)) + typeDefText(tparamsTxt, toText(rhs)) + case PolyTypeTree(tparams, body) => + recur(body, tparamsText(tparams)) + case rhs => + typeDefText(tparamsTxt, optText(rhs)(" = " ~ _)) } + recur(rhs, "") case Import(expr, selectors) => def selectorText(sel: Tree): Text = sel match { case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r) @@ -525,11 +528,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (tree.isType) txt = toText(tp) else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close } - else if (homogenizedView && tree.isType) + else if (homogenizedView && tree.isType) txt = toText(tree.typeOpt) if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { - val pos = - if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic + val pos = + if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic else tree.pos val clsStr = "" // DEBUG: if (tree.isType) tree.getClass.toString else "" txt = (txt ~ "@" ~ pos.toString ~ clsStr).close diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index a1ccf0e63cb4..5385ca720ce3 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -977,7 +977,7 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol)) - goTypeDef(cpy.TypeDef(tree)(tree.name, rhs, tree.tparams), mutatedInfo.nx.nxTransTypeDef(cur)) + goTypeDef(cpy.TypeDef(tree)(tree.name, rhs), mutatedInfo.nx.nxTransTypeDef(cur)) } case _ => tree diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 77f8ff78696a..3876fdf15d0d 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -293,7 +293,7 @@ class Namer { typer: Typer => val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall // suppress inSuperCall for constructor parameters val higherKinded = tree match { - case tree: TypeDef if tree.tparams.nonEmpty && isDeferred => HigherKinded + case TypeDef(_, PolyTypeTree(_, _)) if isDeferred => HigherKinded case _ => EmptyFlags } @@ -605,8 +605,12 @@ class Namer { typer: Typer => nestedCtx = localContext(sym).setNewScope myTypeParams = { implicit val ctx: Context = nestedCtx - completeParams(original.tparams) - original.tparams.map(symbolOfTree(_).asType) + val tparams = original.rhs match { + case PolyTypeTree(tparams, _) => tparams + case _ => Nil + } + completeParams(tparams) + tparams.map(symbolOfTree(_).asType) } } myTypeParams @@ -1005,7 +1009,11 @@ class Namer { typer: Typer => // inspects a TypeRef's info, instead of simply dealiasing alias types. val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree] - val rhsBodyType = typedAheadType(tdef.rhs).tpe + val rhs = tdef.rhs match { + case PolyTypeTree(_, body) => body + case rhs => rhs + } + val rhsBodyType = typedAheadType(rhs).tpe val rhsType = if (isDerived) rhsBodyType else abstracted(rhsBodyType) val unsafeInfo = rhsType match { case bounds: TypeBounds => bounds diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 6b69a859e482..a72c1d5f2b85 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1185,7 +1185,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") { val TypeDef(name, rhs) = tdef completeAnnotations(tdef, sym) - assignType(cpy.TypeDef(tdef)(name, typedType(rhs), Nil), sym) + val rhs1 = tdef.rhs match { + case rhs @ PolyTypeTree(tparams, body) => + val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]] + val body1 = typedType(body) + assignType(cpy.PolyTypeTree(rhs)(tparams1, body1), tparams1, body1) + case rhs => + typedType(rhs) + } + assignType(cpy.TypeDef(tdef)(name, rhs1), sym) } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { @@ -1250,7 +1258,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit .withType(dummy.nonMemberTermRef) checkVariance(impl1) if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) - val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls) + val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true, diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index 1365f3222f38..d7a056e425fb 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -62,7 +62,7 @@ class DeSugarTest extends ParserTest { case tree1 @ DefDef(name, tparams, vparamss, tpt, _) => cpy.DefDef(tree1)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(tree1.rhs)) case tree1 @ TypeDef(name, rhs) => - cpy.TypeDef(tree1)(name, transform(rhs, Type), transformSub(tree1.tparams)) + cpy.TypeDef(tree1)(name, transform(rhs, Type)) case impl @ Template(constr, parents, self, _) => cpy.Template(tree1)(transformSub(constr), transform(parents), transformSub(self), transform(impl.body, Expr)) case Thicket(trees) => From fe20b9064fca765a38345a09aa484bfb537aa3c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Nov 2016 11:58:59 +0100 Subject: [PATCH 06/10] Pickle and unpickle type trees Lots of other changes to make positions work out everywhere. One important change is that now trees can be shared, just as types can. This change improves memory requirements (a bit) and also makes positions in shared trees more robust. --- docs/syntax-summary.txt | 4 +- src/dotty/tools/dotc/ast/tpd.scala | 10 +- src/dotty/tools/dotc/core/Definitions.scala | 8 +- .../dotc/core/tasty/PositionPickler.scala | 26 +- .../tools/dotc/core/tasty/TastyFormat.scala | 142 ++++--- .../tools/dotc/core/tasty/TastyPickler.scala | 2 +- .../tools/dotc/core/tasty/TreeBuffer.scala | 24 +- .../tools/dotc/core/tasty/TreePickler.scala | 387 ++++++++++-------- .../tools/dotc/core/tasty/TreeUnpickler.scala | 114 ++++-- .../tools/dotc/printing/PlainPrinter.scala | 7 +- .../tools/dotc/printing/RefinedPrinter.scala | 55 ++- src/dotty/tools/dotc/transform/Pickler.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 2 +- tests/pickling/annot.scala | 12 + tests/pickling/pickleTypes.scala | 13 + tests/pickling/simple.scala | 6 + 16 files changed, 502 insertions(+), 314 deletions(-) create mode 100644 tests/pickling/annot.scala create mode 100644 tests/pickling/pickleTypes.scala create mode 100644 tests/pickling/simple.scala diff --git a/docs/syntax-summary.txt b/docs/syntax-summary.txt index 0a52ec802ad8..04e149de6f3b 100644 --- a/docs/syntax-summary.txt +++ b/docs/syntax-summary.txt @@ -160,7 +160,7 @@ grammar. SimpleExpr1 ::= Literal | Path | `_' - | `(' ExprsInParens `)' Parens(exprs) + | `(' ExprsInParens2 `)' Parens(exprs) | SimpleExpr `.' id Select(expr, id) | SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args) | SimpleExpr1 ArgumentExprs Apply(expr, args) @@ -210,7 +210,7 @@ grammar. | SimplePattern1 `.' id PatVar ::= varid | `_' - Patterns ::= Pattern [`,' Pattern] + Patterns ::= Pattern {`,' Pattern} ArgumentPatterns ::= `(' [Patterns] `)' Apply(fn, pats) | `(' [Patterns `,'] Pattern2 `:' `_' `*' ') diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index db2ce56496f9..09f2099d2309 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -133,7 +133,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def OrTypeTree(left: Tree, right: Tree)(implicit ctx: Context): OrTypeTree = ta.assignType(untpd.OrTypeTree(left, right), left, right) - // RefinedTypeTree is missing, handled specially in Typer and Unpickler. + def RefinedTypeTree(parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context): Tree = + ta.assignType(untpd.RefinedTypeTree(parent, refinements), parent, refinements, refineCls) def AppliedTypeTree(tycon: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree = ta.assignType(untpd.AppliedTypeTree(tycon, args), tycon, args) @@ -141,6 +142,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree = ta.assignType(untpd.ByNameTypeTree(result), result) + def PolyTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): PolyTypeTree = + ta.assignType(untpd.PolyTypeTree(tparams, body), tparams, body) + def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree = ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi) @@ -306,8 +310,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case _ => false } - typeIsElidable || - tp.symbol.is(JavaStatic) || + typeIsElidable || + tp.symbol.is(JavaStatic) || tp.symbol.hasAnnotation(defn.ScalaStaticAnnot) } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 62fa2d07de88..7b51a58d13f4 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -633,9 +633,9 @@ class Definitions { name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) } - def isBottomClass(cls: Symbol) = + def isBottomClass(cls: Symbol) = cls == NothingClass || cls == NullClass - def isBottomType(tp: Type) = + def isBottomType(tp: Type) = tp.derivesFrom(NothingClass) || tp.derivesFrom(NullClass) def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) @@ -785,8 +785,8 @@ class Definitions { if (!_isInitialized) { // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() - - // Enter all symbols from the scalaShadowing package in the scala package + + // Enter all symbols from the scalaShadowing package in the scala package for (m <- ScalaShadowingPackageClass.info.decls) ScalaPackageClass.enter(m) diff --git a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala index 4b67bc18899d..546894a9efca 100644 --- a/src/dotty/tools/dotc/core/tasty/PositionPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/PositionPickler.scala @@ -13,7 +13,7 @@ import collection.mutable import TastyBuffer._ import util.Positions._ -class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr]) { +class PositionPickler(pickler: TastyPickler, addrOfTree: tpd.Tree => Option[Addr]) { val buf = new TastyBuffer(5000) pickler.newSection("Positions", buf) import buf._ @@ -21,21 +21,6 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] private val remainingAddrs = new java.util.IdentityHashMap[Tree, Iterator[Addr]] - def nextTreeAddr(tree: Tree): Option[Addr] = remainingAddrs.get(tree) match { - case null => - addrsOfTree(tree) match { - case Nil => - None - case addr :: Nil => - Some(addr) - case addrs => - remainingAddrs.put(tree, addrs.iterator) - nextTreeAddr(tree) - } - case it: Iterator[_] => - if (it.hasNext) Some(it.next) else None - } - def header(addrDelta: Int, hasStartDelta: Boolean, hasEndDelta: Boolean, hasPoint: Boolean) = { def toInt(b: Boolean) = if (b) 1 else 0 (addrDelta << 3) | (toInt(hasStartDelta) << 2) | (toInt(hasEndDelta) << 1) | toInt(hasPoint) @@ -60,8 +45,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] */ def alwaysNeedsPos(x: Positioned) = x match { case _: WithLazyField[_] // initialPos is inaccurate for trees with lazy field - | _: Trees.PackageDef[_] => true // package defs might be split into several Tasty files - case x: Trees.Tree[_] => x.isType // types are unpickled as TypeTrees, so child positions are not available + | _: Trees.PackageDef[_] => true // package defs might be split into several Tasty files case _ => false } @@ -69,7 +53,7 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] case x: Tree @unchecked => val pos = if (x.isInstanceOf[MemberDef]) x.pos else x.pos.toSynthetic if (pos.exists && (pos != x.initialPos.toSynthetic || alwaysNeedsPos(x))) { - nextTreeAddr(x) match { + addrOfTree(x) match { case Some(addr) => //println(i"pickling $x with $pos at $addr") pickleDeltas(addr.index, pos) @@ -79,13 +63,15 @@ class PositionPickler(pickler: TastyPickler, addrsOfTree: tpd.Tree => List[Addr] } //else if (x.pos.exists) println(i"skipping $x") x match { - case x: MemberDef @unchecked => + case x: MemberDef @unchecked => for (ann <- x.symbol.annotations) traverse(ann.tree) case _ => } traverse(x.productIterator) case xs: TraversableOnce[_] => xs.foreach(traverse) + case x: Annotation => + traverse(x.tree) case _ => } traverse(roots) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index f9743d9d2b72..80bd39841467 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -71,7 +71,7 @@ Standard-Section: "ASTs" TopLevelStat* Term = Path Application - IDENT NameRef Type // used when ident’s type is not a TermRef + IDENT NameRef Type // used when term ident’s type is not a TermRef SELECT possiblySigned_NameRef qual_Term NEW cls_Type SUPER Length this_Term mixinTrait_Type? @@ -89,6 +89,17 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef patType_Type pat_Term ALTERNATIVE Length alt_Term* UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* + IDENTtpt NameRef Type // used when type ident's type is not a TypeRef + SELECTtpt NameRef qual_Term + SINGLETONtpt Path + REFINDtpt Length underlying_Term refinement_Stat* + APPLIEDtpt Length tycon_Term arg_Term* + POLYtpt Length TypeParam* body_Term + TYPEBOUNDStpt Length low_Term high_Term + ANNOTATEDtpt Length underlying_Term fullAnnotation_Term + ANDtpt Length left_Term right_Term + ORtpt Length left_Term right_Term + BYNAMEtpt underlying_Term EMPTYTREE SHARED term_ASTRef Application = APPLY Length fn_Term arg_Term* @@ -133,7 +144,7 @@ Standard-Section: "ASTs" TopLevelStat* APPLIEDtype Length tycon_Type arg_Type* TYPEBOUNDS Length low_Type high_Type TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? - ANNOTATED Length underlying_Type fullAnnotation_Term + ANNOTATEDtype Length underlying_Type fullAnnotation_Term ANDtype Length left_Type right_Type ORtype Length left_Type right_Type BIND Length boundName_NameRef bounds_Type @@ -271,19 +282,23 @@ object TastyFormat { final val CLASSconst = 97 final val ENUMconst = 98 final val BYNAMEtype = 99 - final val NEW = 100 - final val IMPLICITarg = 101 - final val PRIVATEqualified = 102 - final val PROTECTEDqualified = 103 - final val RECtype = 104 + final val BYNAMEtpt = 100 + final val NEW = 101 + final val IMPLICITarg = 102 + final val PRIVATEqualified = 103 + final val PROTECTEDqualified = 104 + final val RECtype = 105 + final val SINGLETONtpt = 106 final val IDENT = 112 - final val SELECT = 113 - final val TERMREFsymbol = 114 - final val TERMREF = 115 - final val TYPEREFsymbol = 116 - final val TYPEREF = 117 - final val SELFDEF = 118 + final val IDENTtpt = 113 + final val SELECT = 114 + final val SELECTtpt = 115 + final val TERMREFsymbol = 116 + final val TERMREF = 117 + final val TYPEREFsymbol = 118 + final val TYPEREF = 119 + final val SELFDEF = 120 final val PACKAGE = 128 final val VALDEF = 129 @@ -293,39 +308,44 @@ object TastyFormat { final val TYPEPARAM = 133 final val PARAMS = 134 final val PARAM = 136 - - final val APPLY = 139 - final val TYPEAPPLY = 140 - - final val TYPED = 143 - final val NAMEDARG = 144 - final val ASSIGN = 145 - final val BLOCK = 146 - final val IF = 147 - final val LAMBDA = 148 - final val MATCH = 149 - final val RETURN = 150 - final val TRY = 151 - final val INLINED = 152 - final val REPEATED = 153 - final val BIND = 154 - final val ALTERNATIVE = 155 - final val UNAPPLY = 156 - final val ANNOTATED = 157 - final val CASEDEF = 158 - final val TEMPLATE = 160 - final val SUPER = 163 - final val SUPERtype = 166 - final val REFINEDtype = 167 - final val APPLIEDtype = 168 - final val TYPEBOUNDS = 169 - final val TYPEALIAS = 170 - final val ANDtype = 171 - final val ORtype = 172 - final val METHODtype = 174 - final val POLYtype = 175 - final val PARAMtype = 176 - final val ANNOTATION = 177 + final val APPLY = 137 + final val TYPEAPPLY = 138 + final val TYPED = 139 + final val NAMEDARG = 140 + final val ASSIGN = 141 + final val BLOCK = 142 + final val IF = 143 + final val LAMBDA = 144 + final val MATCH = 145 + final val RETURN = 146 + final val TRY = 147 + final val INLINED = 148 + final val REPEATED = 149 + final val BIND = 150 + final val ALTERNATIVE = 151 + final val UNAPPLY = 152 + final val ANNOTATEDtype = 153 + final val ANNOTATEDtpt = 154 + final val CASEDEF = 155 + final val TEMPLATE = 156 + final val SUPER = 157 + final val SUPERtype = 158 + final val REFINEDtype = 159 + final val REFINEDtpt = 160 + final val APPLIEDtype = 161 + final val APPLIEDtpt = 162 + final val TYPEBOUNDS = 163 + final val TYPEBOUNDStpt = 164 + final val TYPEALIAS = 165 + final val ANDtype = 166 + final val ANDtpt = 167 + final val ORtype = 168 + final val ORtpt = 169 + final val METHODtype = 170 + final val POLYtype = 171 + final val POLYtpt = 172 + final val PARAMtype = 173 + final val ANNOTATION = 174 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -367,7 +387,22 @@ object TastyFormat { | PRIVATEqualified | PROTECTEDqualified => true case _ => false - } + } + + def isTypeTreeTag(tag: Int) = tag match { + case IDENTtpt + | SELECTtpt + | SINGLETONtpt + | REFINEDtpt + | APPLIEDtpt + | POLYtpt + | TYPEBOUNDStpt + | ANNOTATEDtpt + | ANDtpt + | ORtpt + | BYNAMEtpt => true + case _ => false + } def nameTagToString(tag: Int): String = tag match { case UTF8 => "UTF8" @@ -429,7 +464,9 @@ object TastyFormat { case RECtype => "RECtype" case IDENT => "IDENT" + case IDENTtpt => "IDENTtpt" case SELECT => "SELECT" + case SELECTtpt => "SELECTtpt" case TERMREFsymbol => "TERMREFsymbol" case TERMREF => "TERMREF" case TYPEREFsymbol => "TYPEREFsymbol" @@ -462,7 +499,8 @@ object TastyFormat { case BIND => "BIND" case ALTERNATIVE => "ALTERNATIVE" case UNAPPLY => "UNAPPLY" - case ANNOTATED => "ANNOTATED" + case ANNOTATEDtype => "ANNOTATEDtype" + case ANNOTATEDtpt => "ANNOTATEDtpt" case CASEDEF => "CASEDEF" case IMPLICITarg => "IMPLICITarg" case TEMPLATE => "TEMPLATE" @@ -471,15 +509,23 @@ object TastyFormat { case SUPER => "SUPER" case CLASSconst => "CLASSconst" case ENUMconst => "ENUMconst" + case SINGLETONtpt => "SINGLETONtpt" case SUPERtype => "SUPERtype" case REFINEDtype => "REFINEDtype" + case REFINEDtpt => "REFINEDtpt" case APPLIEDtype => "APPLIEDtype" + case APPLIEDtpt => "APPLIEDtpt" case TYPEBOUNDS => "TYPEBOUNDS" + case TYPEBOUNDStpt => "TYPEBOUNDStpt" case TYPEALIAS => "TYPEALIAS" case ANDtype => "ANDtype" + case ANDtpt => "ANDtpt" case ORtype => "ORtype" + case ORtpt => "ORtpt" case BYNAMEtype => "BYNAMEtype" + case BYNAMEtpt => "BYNAMEtpt" case POLYtype => "POLYtype" + case POLYtpt => "POLYtpt" case METHODtype => "METHODtype" case PARAMtype => "PARAMtype" case ANNOTATION => "ANNOTATION" diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index f847dda686f0..55fe232c077f 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -61,7 +61,7 @@ class TastyPickler { * Note that a tree can have several addresses, if it is shared, * i.e. accessible from different paths. Any such sharing is undone by pickling. */ - var addrsOfTree: tpd.Tree => List[Addr] = (_ => Nil) + var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) /** * Addresses in TASTY file of symbols, stored by pickling. diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index f2681ecde31c..67dc6076f045 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -17,26 +17,20 @@ class TreeBuffer extends TastyBuffer(50000) { private var delta: Array[Int] = _ private var numOffsets = 0 - private type TreeAddrs = Any // really: Addr | List[Addr] - /** A map from trees to the address(es) at which a tree is pickled. There may be several * such addresses if the tree is shared. To keep the map compact, the value type is a * disjunction of a single address (which is the common case) and a list of addresses. */ - private val treeAddrs = new java.util.IdentityHashMap[Tree, TreeAddrs] + private val treeAddrs = new java.util.IdentityHashMap[Tree, Any] // really: Addr | Null - def registerTreeAddr(tree: Tree) = - treeAddrs.put(tree, - treeAddrs.get(tree) match { - case null => currentAddr - case x: Addr => x :: currentAddr :: Nil - case xs: List[_] => xs :+ currentAddr - }) - - def addrsOfTree(tree: Tree): List[Addr] = treeAddrs.get(tree) match { - case null => Nil - case addr: Addr => addr :: Nil - case addrs: List[Addr] => addrs + def registerTreeAddr(tree: Tree): Addr = treeAddrs.get(tree) match { + case null => treeAddrs.put(tree, currentAddr); currentAddr + case addr: Addr => addr + } + + def addrOfTree(tree: Tree): Option[Addr] = treeAddrs.get(tree) match { + case null => None + case addr: Addr => Some(addr) } private def offset(i: Int): Addr = Addr(offsets(i)) diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 98a369f25608..1838bc5bb000 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -10,6 +10,7 @@ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annot import collection.mutable import typer.Inliner import NameOps._ +import StdNames.nme import TastyBuffer._ import TypeApplications._ @@ -153,11 +154,6 @@ class TreePickler(pickler: TastyPickler) { throw ex } - def pickleTypeWithPos(tpe: Type, tree: Tree)(implicit ctx: Context): Unit = { - registerTreeAddr(tree) - pickleType(tpe) - } - private def pickleNewType(tpe: Type, richTypes: Boolean)(implicit ctx: Context): Unit = try { tpe match { case AppliedType(tycon, args) => writeByte(APPLIEDtype) @@ -249,7 +245,7 @@ class TreePickler(pickler: TastyPickler) { writeByte(TYPEBOUNDS) withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } case tpe: AnnotatedType => - writeByte(ANNOTATED) + writeByte(ANNOTATEDtype) withLength { pickleType(tpe.tpe, richTypes); pickleTree(tpe.annot.tree) } case tpe: AndOrType => writeByte(if (tpe.isAnd) ANDtype else ORtype) @@ -306,7 +302,7 @@ class TreePickler(pickler: TastyPickler) { } def pickleTpt(tpt: Tree)(implicit ctx: Context): Unit = - pickleTypeWithPos(tpt.tpe, tpt) // TODO correlate with original when generating positions + pickleTree(tpt) def pickleTreeUnlessEmpty(tree: Tree)(implicit ctx: Context): Unit = if (!tree.isEmpty) pickleTree(tree) @@ -346,181 +342,226 @@ class TreePickler(pickler: TastyPickler) { stats.foreach(stat => if (!stat.isEmpty) pickleTree(stat)) } - def pickleTree(tree: Tree)(implicit ctx: Context): Unit = try { - registerTreeAddr(tree) - tree match { - case tree if tree.isType => - pickleTpt(tree) - case Ident(name) => - tree.tpe match { - case tp: TermRef => pickleType(tp) - case _ => - writeByte(IDENT) - pickleName(name) - pickleType(tree.tpe) - } - case This(_) => - pickleType(tree.tpe) - case Select(qual, name) => - writeByte(SELECT) - val realName = tree.tpe match { - case tp: NamedType if tp.name.isShadowedName => tp.name - case _ => name - } - val sig = tree.tpe.signature - if (sig == Signature.NotAMethod) pickleName(realName) - else pickleNameAndSig(realName, sig) - pickleTree(qual) - case Apply(fun, args) => - writeByte(APPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTree) - } - case TypeApply(fun, args) => - writeByte(TYPEAPPLY) - withLength { - pickleTree(fun) - args.foreach(pickleTpt) - } - case Literal(const1) => - pickleConstant { + def pickleTree(tree: Tree)(implicit ctx: Context): Unit = { + val addr = registerTreeAddr(tree) + if (addr != currentAddr) { + writeByte(SHARED) + writeRef(addr) + } + else + try tree match { + case Ident(name) => tree.tpe match { - case ConstantType(const2) => const2 - case _ => const1 + case tp: TermRef if name != nme.WILDCARD => + // wildcards are pattern bound, need to be preserved as ids. + pickleType(tp) + case _ => + writeByte(if (tree.isType) IDENTtpt else IDENT) + pickleName(name) + pickleType(tree.tpe) } - } - case Super(qual, mix) => - writeByte(SUPER) - withLength { - pickleTree(qual); - if (!mix.isEmpty) { - val SuperType(_, mixinType) = tree.tpe - pickleType(mixinType) + case This(_) => + pickleType(tree.tpe) + case Select(qual, name) => + writeByte(if (name.isTypeName) SELECTtpt else SELECT) + val realName = tree.tpe match { + case tp: NamedType if tp.name.isShadowedName => tp.name + case _ => name } - } - case New(tpt) => - writeByte(NEW) - pickleTpt(tpt) - case Typed(expr, tpt) => - writeByte(TYPED) - withLength { pickleTree(expr); pickleTpt(tpt) } - case NamedArg(name, arg) => - writeByte(NAMEDARG) - withLength { pickleName(name); pickleTree(arg) } - case Assign(lhs, rhs) => - writeByte(ASSIGN) - withLength { pickleTree(lhs); pickleTree(rhs) } - case Block(stats, expr) => - writeByte(BLOCK) - stats.foreach(preRegister) - withLength { pickleTree(expr); stats.foreach(pickleTree) } - case If(cond, thenp, elsep) => - writeByte(IF) - withLength{ pickleTree(cond); pickleTree(thenp); pickleTree(elsep) } - case Closure(env, meth, tpt) => - writeByte(LAMBDA) - assert(env.isEmpty) - withLength{ - pickleTree(meth) - if (tpt.tpe.exists) pickleTpt(tpt) - } - case Match(selector, cases) => - writeByte(MATCH) - withLength { pickleTree(selector); cases.foreach(pickleTree) } - case CaseDef(pat, guard, rhs) => - writeByte(CASEDEF) - withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) } - case Return(expr, from) => - writeByte(RETURN) - withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) } - case Try(block, cases, finalizer) => - writeByte(TRY) - withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) } - case SeqLiteral(elems, elemtpt) => - writeByte(REPEATED) - withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } - case Inlined(call, bindings, expansion) => - writeByte(INLINED) - bindings.foreach(preRegister) - withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } - case Bind(name, body) => - registerDef(tree.symbol) - writeByte(BIND) - withLength { pickleName(name); pickleType(tree.symbol.info); pickleTree(body) } - case Alternative(alts) => - writeByte(ALTERNATIVE) - withLength { alts.foreach(pickleTree) } - case UnApply(fun, implicits, patterns) => - writeByte(UNAPPLY) - withLength { - pickleTree(fun) - for (implicitArg <- implicits) { - writeByte(IMPLICITarg) - pickleTree(implicitArg) + val sig = tree.tpe.signature + if (sig == Signature.NotAMethod) pickleName(realName) + else pickleNameAndSig(realName, sig) + pickleTree(qual) + case Apply(fun, args) => + writeByte(APPLY) + withLength { + pickleTree(fun) + args.foreach(pickleTree) } - pickleType(tree.tpe) - patterns.foreach(pickleTree) - } - case tree: ValDef => - pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs) - case tree: DefDef => - def pickleAllParams = { - pickleParams(tree.tparams) - for (vparams <- tree.vparamss) { - writeByte(PARAMS) - withLength { pickleParams(vparams) } + case TypeApply(fun, args) => + writeByte(TYPEAPPLY) + withLength { + pickleTree(fun) + args.foreach(pickleTpt) } - } - pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) - case tree: TypeDef => - pickleDef(TYPEDEF, tree.symbol, tree.rhs) - case tree: Template => - registerDef(tree.symbol) - writeByte(TEMPLATE) - val (params, rest) = tree.body partition { - case stat: TypeDef => stat.symbol is Flags.Param - case stat: ValOrDefDef => - stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter - case _ => false - } - withLength { - pickleParams(params) - tree.parents.foreach(pickleTree) - val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info - if ((selfInfo ne NoType) || !tree.self.isEmpty) { - writeByte(SELFDEF) - pickleName(tree.self.name) - if (!tree.self.isEmpty) registerTreeAddr(tree.self.tpt) - pickleType { - cinfo.selfInfo match { - case sym: Symbol => sym.info - case tp: Type => tp + case Literal(const1) => + pickleConstant { + tree.tpe match { + case ConstantType(const2) => const2 + case _ => const1 + } + } + case Super(qual, mix) => + writeByte(SUPER) + withLength { + pickleTree(qual); + if (!mix.isEmpty) { + val SuperType(_, mixinType) = tree.tpe + pickleType(mixinType) + } + } + case New(tpt) => + writeByte(NEW) + pickleTpt(tpt) + case Typed(expr, tpt) => + writeByte(TYPED) + withLength { pickleTree(expr); pickleTpt(tpt) } + case NamedArg(name, arg) => + writeByte(NAMEDARG) + withLength { pickleName(name); pickleTree(arg) } + case Assign(lhs, rhs) => + writeByte(ASSIGN) + withLength { pickleTree(lhs); pickleTree(rhs) } + case Block(stats, expr) => + writeByte(BLOCK) + stats.foreach(preRegister) + withLength { pickleTree(expr); stats.foreach(pickleTree) } + case If(cond, thenp, elsep) => + writeByte(IF) + withLength { pickleTree(cond); pickleTree(thenp); pickleTree(elsep) } + case Closure(env, meth, tpt) => + writeByte(LAMBDA) + assert(env.isEmpty) + withLength { + pickleTree(meth) + if (tpt.tpe.exists) pickleTpt(tpt) + } + case Match(selector, cases) => + writeByte(MATCH) + withLength { pickleTree(selector); cases.foreach(pickleTree) } + case CaseDef(pat, guard, rhs) => + writeByte(CASEDEF) + withLength { pickleTree(pat); pickleTree(rhs); pickleTreeUnlessEmpty(guard) } + case Return(expr, from) => + writeByte(RETURN) + withLength { pickleSymRef(from.symbol); pickleTreeUnlessEmpty(expr) } + case Try(block, cases, finalizer) => + writeByte(TRY) + withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeUnlessEmpty(finalizer) } + case SeqLiteral(elems, elemtpt) => + writeByte(REPEATED) + withLength { pickleTree(elemtpt); elems.foreach(pickleTree) } + case Inlined(call, bindings, expansion) => + writeByte(INLINED) + bindings.foreach(preRegister) + withLength { pickleTree(call); pickleTree(expansion); bindings.foreach(pickleTree) } + case Bind(name, body) => + registerDef(tree.symbol) + writeByte(BIND) + withLength { pickleName(name); pickleType(tree.symbol.info); pickleTree(body) } + case Alternative(alts) => + writeByte(ALTERNATIVE) + withLength { alts.foreach(pickleTree) } + case UnApply(fun, implicits, patterns) => + writeByte(UNAPPLY) + withLength { + pickleTree(fun) + for (implicitArg <- implicits) { + writeByte(IMPLICITarg) + pickleTree(implicitArg) + } + pickleType(tree.tpe) + patterns.foreach(pickleTree) + } + case tree: ValDef => + pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs) + case tree: DefDef => + def pickleAllParams = { + pickleParams(tree.tparams) + for (vparams <- tree.vparamss) { + writeByte(PARAMS) + withLength { pickleParams(vparams) } + } + } + pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams) + case tree: TypeDef => + pickleDef(TYPEDEF, tree.symbol, tree.rhs) + case tree: Template => + registerDef(tree.symbol) + writeByte(TEMPLATE) + val (params, rest) = tree.body partition { + case stat: TypeDef => stat.symbol is Flags.Param + case stat: ValOrDefDef => + stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter + case _ => false + } + withLength { + pickleParams(params) + tree.parents.foreach(pickleTree) + val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info + if ((selfInfo ne NoType) || !tree.self.isEmpty) { + writeByte(SELFDEF) + pickleName(tree.self.name) + + if (!tree.self.tpt.isEmpty) pickleTree(tree.self.tpt) + else { + if (!tree.self.isEmpty) registerTreeAddr(tree.self) + pickleType { + cinfo.selfInfo match { + case sym: Symbol => sym.info + case tp: Type => tp + } + } } } + pickleStats(tree.constr :: rest) } - pickleStats(tree.constr :: rest) - } - case Import(expr, selectors) => - writeByte(IMPORT) - withLength { - pickleTree(expr) - selectors foreach { - case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => - pickleSelector(IMPORTED, from) - pickleSelector(RENAMED, to) - case id @ Ident(_) => - pickleSelector(IMPORTED, id) + case Import(expr, selectors) => + writeByte(IMPORT) + withLength { + pickleTree(expr) + selectors foreach { + case Thicket((from @ Ident(_)) :: (to @ Ident(_)) :: Nil) => + pickleSelector(IMPORTED, from) + pickleSelector(RENAMED, to) + case id @ Ident(_) => + pickleSelector(IMPORTED, id) + } } - } - case PackageDef(pid, stats) => - writeByte(PACKAGE) - withLength { pickleType(pid.tpe); pickleStats(stats) } - }} - catch { - case ex: AssertionError => - println(i"error when pickling tree $tree") - throw ex + case PackageDef(pid, stats) => + writeByte(PACKAGE) + withLength { pickleType(pid.tpe); pickleStats(stats) } + case tree: TypeTree => + pickleType(tree.tpe) + case SingletonTypeTree(ref) => + writeByte(SINGLETONtpt) + pickleTree(ref) + case RefinedTypeTree(parent, refinements) => + if (refinements.isEmpty) pickleTree(parent) + else { + val refineCls = refinements.head.symbol.owner.asClass + pickledTypes.put(refineCls.typeRef, currentAddr) + writeByte(REFINEDtpt) + refinements.foreach(preRegister) + withLength { pickleTree(parent); refinements.foreach(pickleTree) } + } + case AppliedTypeTree(tycon, args) => + writeByte(APPLIEDtpt) + withLength { pickleTree(tycon); args.foreach(pickleTree(_)) } + case AndTypeTree(tp1, tp2) => + writeByte(ANDtpt) + withLength { pickleTree(tp1); pickleTree(tp2) } + case OrTypeTree(tp1, tp2) => + writeByte(ORtpt) + withLength { pickleTree(tp1); pickleTree(tp2) } + case ByNameTypeTree(tp) => + writeByte(BYNAMEtpt) + pickleTree(tp) + case Annotated(tree, annot) => + writeByte(ANNOTATEDtpt) + withLength { pickleTree(tree); pickleTree(annot.tree) } + case PolyTypeTree(tparams, body) => + writeByte(POLYtpt) + withLength { pickleParams(tparams); pickleTree(body) } + case TypeBoundsTree(lo, hi) => + writeByte(TYPEBOUNDStpt) + withLength { pickleTree(lo); pickleTree(hi) } + } + catch { + case ex: AssertionError => + println(i"error when pickling tree $tree") + throw ex + } } def pickleSelector(tag: Int, id: untpd.Ident)(implicit ctx: Context): Unit = { diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 90f718402fc0..1f0bbca19864 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -256,7 +256,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle else if (nextByte == CONTRAVARIANT) { readByte(); -1 } else 0 TypeAlias(alias, variance) - case ANNOTATED => + case ANNOTATEDtype => AnnotatedType(readType(), Annotation(readTerm())) case ANDtype => AndType(readType(), readType()) @@ -368,7 +368,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle private def readPackageRef()(implicit ctx: Context): TermSymbol = { val name = readName() - if (name == nme.ROOT) defn.RootPackage + if (name == nme.ROOT || name == nme.ROOTPKG) defn.RootPackage else if (name == nme.EMPTY_PACKAGE) defn.EmptyPackageVal else ctx.requiredPackage(name) } @@ -389,11 +389,11 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle if (owner.isClass) lctx.setScope(owner.unforcedDecls) else lctx.setNewScope } - private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbstractType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { + private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbsType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { val lacksDefinition = rhsIsEmpty && name.isTermName && !name.isConstructorName && !givenFlags.is(ParamOrAccessor) || - isAbstractType + isAbsType var flags = givenFlags if (lacksDefinition && tag != PARAM) flags |= Deferred if (tag == DEFDEF) flags |= Method @@ -407,6 +407,17 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle flags } + def isAbstractType(ttag: Int)(implicit ctx: Context): Boolean = nextUnsharedTag match { + case POLYtpt => + val rdr = fork + rdr.reader.readByte() // tag + rdr.reader.readNat() // length + rdr.skipParams() // tparams + rdr.isAbstractType(rdr.nextUnsharedTag) + case TYPEBOUNDS | TYPEBOUNDStpt => true + case _ => false + } + /** Create symbol of definition node and enter in symAtAddr map * @return the created symbol */ @@ -433,7 +444,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName skipParams() val ttag = nextUnsharedTag - val isAbstractType = ttag == TYPEBOUNDS + val isAbsType = isAbstractType(ttag) val isClass = ttag == TEMPLATE val templateStart = currentAddr skipTree() // tpt @@ -447,7 +458,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case _ => EmptyFlags } pickling.println(i"creating symbol $name at $start with flags $givenFlags") - val flags = normalizeFlags(tag, givenFlags | nameFlags(rawName), name, isAbstractType, rhsIsEmpty) + val flags = normalizeFlags(tag, givenFlags | nameFlags(rawName), name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer val sym = @@ -623,11 +634,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val tag = readByte() val end = readEnd() - def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = { - fork.indexParams(tag)(localContext(sym)) - readIndexedParams(tag) - } - def readParamss(implicit ctx: Context): List[List[ValDef]] = { collectWhile(nextByte == PARAMS) { readByte() @@ -849,9 +855,14 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readIndexedParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = collectWhile(nextByte == tag) { readIndexedDef().asInstanceOf[T] } -// ------ Reading terms ----------------------------------------------------- + def readParams[T <: MemberDef](tag: Int)(implicit ctx: Context): List[T] = { + fork.indexParams(tag) + readIndexedParams(tag) + } - def readTerm()(implicit ctx: Context): Tree = { +// ------ Reading trees ----------------------------------------------------- + + def readTerm()(implicit ctx: Context): Tree = { // TODO: rename to readTree val start = currentAddr val tag = readByte() pickling.println(s"reading term ${astTagToString(tag)} at $start") @@ -859,34 +870,44 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readPathTerm(): Tree = { goto(start) readType() match { + case path: TypeRef => TypeTree(path) case path: TermRef => ref(path) case path: ThisType => This(path.cls) case path: ConstantType => Literal(path.value) } } + def completeSelect(name: Name, tpf: Type => Type): Select = { + val localCtx = + if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx + val qual = readTerm()(localCtx) + val unshadowed = if (name.isShadowedName) name.revertShadowed else name + untpd.Select(qual, unshadowed).withType(tpf(qual.tpe.widenIfUnstable)) + } + def readSimpleTerm(): Tree = tag match { + case SHARED => + forkAt(readAddr()).readTerm() case IDENT => untpd.Ident(readName()).withType(readType()) + case IDENTtpt => + untpd.Ident(readName().toTypeName).withType(readType()) case SELECT => - def readQual(name: Name) = { - val localCtx = - if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx - readTerm()(localCtx) - } - def readRest(name: Name, sig: Signature) = { - val unshadowed = if (name.isShadowedName) name.revertShadowed else name - val qual = readQual(name) - untpd.Select(qual, unshadowed) - .withType(TermRef.withSig(qual.tpe.widenIfUnstable, name.asTermName, sig)) - } + def readRest(name: Name, sig: Signature) = + completeSelect(name, TermRef.withSig(_, name.asTermName, sig)) readNameSplitSig match { case name: Name => readRest(name, Signature.NotAMethod) case (name: Name, sig: Signature) => readRest(name, sig) } - + case SELECTtpt => + val name = readName().toTypeName + completeSelect(name, TypeRef(_, name)) case NEW => New(readTpt()) + case SINGLETONtpt => + SingletonTypeTree(readTerm()) + case BYNAMEtpt => + ByNameTypeTree(readTpt()) case _ => readPathTerm() } @@ -894,10 +915,15 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readLengthTerm(): Tree = { val end = readEnd() + def localNonClassCtx = { + val ctx1 = ctx.fresh.setNewScope + if (ctx.owner.isClass) ctx1.setOwner(ctx1.newLocalDummy(ctx.owner)) else ctx1 + } + def readBlock(mkTree: (List[Tree], Tree) => Tree): Tree = { val exprReader = fork skipTree() - val localCtx = ctx.fresh.setNewScope + val localCtx = localNonClassCtx val stats = readStats(ctx.owner, end)(localCtx) val expr = exprReader.readTerm()(localCtx) mkTree(stats, expr) @@ -937,7 +963,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case BLOCK => readBlock(Block) case INLINED => - val call = setPos(currentAddr, TypeTree(readType())) + val call = readTerm() readBlock((defs, expr) => Inlined(call, defs.asInstanceOf[List[MemberDef]], expr)) case IF => If(readTerm(), readTerm(), readTerm()) @@ -974,6 +1000,28 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val patType = readType() val argPats = until(end)(readTerm()) UnApply(fn, implicitArgs, argPats, patType) + case REFINEDtpt => + val refineCls = ctx.newCompleteClassSymbol( + ctx.owner, tpnme.REFINE_CLASS, Fresh, parents = Nil) + typeAtAddr(start) = refineCls.typeRef + val parent = readTpt() + val refinements = readStats(refineCls, end)(localContext(refineCls)) + RefinedTypeTree(parent, refinements, refineCls) + case APPLIEDtpt => + AppliedTypeTree(readTpt(), until(end)(readTpt())) + case ANDtpt => + AndTypeTree(readTpt(), readTpt()) + case ORtpt => + OrTypeTree(readTpt(), readTpt()) + case ANNOTATEDtpt => + Annotated(readTpt(), readTerm()) + case POLYtpt => + val localCtx = localNonClassCtx + val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) + val body = readTpt()(localCtx) + PolyTypeTree(tparams, body) + case TYPEBOUNDStpt => + TypeBoundsTree(readTpt(), readTpt()) case _ => readPathTerm() } @@ -986,11 +1034,13 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle setPos(start, tree) } - def readTpt()(implicit ctx: Context) = { - val start = currentAddr - val tp = readType() - if (tp.exists) setPos(start, TypeTree(tp)) else EmptyTree - } + def readTpt()(implicit ctx: Context) = + if (isTypeTreeTag(nextUnsharedTag)) readTerm() + else { + val start = currentAddr + val tp = readType() + if (tp.exists) setPos(start, TypeTree(tp)) else EmptyTree + } def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 785f57897e54..06d44e3010fc 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -231,9 +231,10 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def trimPrefix(text: Text) = text.stripPrefix(objectPrefix).stripPrefix(packagePrefix) - protected def selectionString(tp: NamedType) = - if (tp.currentSymbol.exists) nameString(tp.symbol) - else nameString(tp.name) + protected def selectionString(tp: NamedType) = { + val sym = if (homogenizedView) tp.symbol else tp.currentSymbol + if (sym.exists) nameString(sym) else nameString(tp.name) + } /** The string representation of this type used as a prefix */ protected def toTextRef(tp: SingletonType): Text = controlled { diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 39a21b17be66..4818c6eeed41 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -21,6 +21,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ private var enclosingDef: untpd.Tree = untpd.EmptyTree private var myCtx: Context = _ctx + private var printPos = ctx.settings.Yprintpos.value override protected[this] implicit def ctx: Context = myCtx def withEnclosingDef(enclDef: Tree[_ >: Untyped])(op: => Text): Text = { @@ -35,6 +36,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } } + def inPattern(op: => Text): Text = { + val savedCtx = myCtx + myCtx = ctx.addMode(Mode.Pattern) + try op finally myCtx = savedCtx + } + + def withoutPos(op: => Text): Text = { + val savedPrintPos = printPos + printPos = false + try op finally printPos = savedPrintPos + } + private def enclDefIsClass = enclosingDef match { case owner: TypeDef[_] => owner.isClassDef case owner: untpd.ModuleDef => true @@ -134,6 +147,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return toTextParents(tp.parentsWithArgs) ~ "{...}" case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" + case tp: AnnotatedType if homogenizedView => + withoutPos(super.toText(tp)) case tp: SelectionProto => return "?{ " ~ toText(tp.name) ~ (" " provided !tp.name.decode.last.isLetterOrDigit) ~ ": " ~ toText(tp.memberProto) ~ " }" @@ -249,7 +264,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } def nameIdText(tree: untpd.NameTree): Text = - toText(tree.name) ~ idText(tree) + if (tree.hasType && tree.symbol.exists) nameString(tree.symbol) + else toText(tree.name) ~ idText(tree) def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { val Template(constr @ DefDef(_, tparams, vparamss, _, _), parents, self, _) = impl @@ -284,7 +300,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp: NamedType if name != nme.WILDCARD => val pre = if (tp.symbol is JavaStatic) tp.prefix.widen else tp.prefix toTextPrefix(pre) ~ selectionString(tp) - case _ => toText(name) + case _ => + toText(name) } case tree @ Select(qual, name) => if (qual.isType) toTextLocal(qual) ~ "#" ~ toText(name) @@ -337,7 +354,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (sel.isEmpty) blockText(cases) else changePrec(GlobalPrec) { toText(sel) ~ " match " ~ blockText(cases) } case CaseDef(pat, guard, body) => - "case " ~ toText(pat) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body) + "case " ~ inPattern(toText(pat)) ~ optText(guard)(" if " ~ _) ~ " => " ~ caseBlockText(body) case Return(expr, from) => changePrec(GlobalPrec) { "return" ~ optText(expr)(" " ~ _) } case Try(expr, cases, finalizer) => @@ -519,22 +536,40 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case _ => tree.fallbackToText(this) } + var txt = toTextCore(tree) + + def suppressTypes = + tree.isType || tree.isDef || // don't print types of types or defs + homogenizedView && ctx.mode.is(Mode.Pattern) + // When comparing pickled info, disregard types of patterns. + // The reason is that GADT matching can rewrite types of pattern trees + // without changing the trees themselves. (see Typer.typedCase.indexPatterns.transform). + // But then pickling and unpickling the original trees will yield trees + // with the original types before they are rewritten, which causes a discrepancy. + + def suppressPositions = tree match { + case _: WithoutTypeOrPos[_] | _: TypeTree => true // TypeTrees never have an interesting position + case _ => false + } + if (ctx.settings.printtypes.value && tree.hasType) { - val tp = tree.typeOpt match { + // add type to term nodes; replace type nodes with their types unless -Yprintpos is also set. + def tp = tree.typeOpt match { case tp: TermRef if tree.isInstanceOf[RefTree] && !tp.denot.isOverloaded => tp.underlying case tp => tp } - if (tree.isType) txt = toText(tp) - else if (!tree.isDef) txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close + if (!suppressTypes) + txt = ("<" ~ txt ~ ":" ~ toText(tp) ~ ">").close + else if (tree.isType && !homogenizedView) + txt = toText(tp) } - else if (homogenizedView && tree.isType) - txt = toText(tree.typeOpt) - if (ctx.settings.Yprintpos.value && !tree.isInstanceOf[WithoutTypeOrPos[_]]) { + if (printPos && !suppressPositions) { + // add positions val pos = if (homogenizedView && !tree.isInstanceOf[MemberDef]) tree.pos.toSynthetic else tree.pos - val clsStr = "" // DEBUG: if (tree.isType) tree.getClass.toString else "" + val clsStr = ""//if (tree.isType) tree.getClass.toString else "" txt = (txt ~ "@" ~ pos.toString ~ clsStr).close } tree match { diff --git a/src/dotty/tools/dotc/transform/Pickler.scala b/src/dotty/tools/dotc/transform/Pickler.scala index fc70ac4f247c..2fb85b6c03dd 100644 --- a/src/dotty/tools/dotc/transform/Pickler.scala +++ b/src/dotty/tools/dotc/transform/Pickler.scala @@ -46,10 +46,10 @@ class Pickler extends Phase { val treePkl = pickler.treePkl treePkl.pickle(tree :: Nil) treePkl.compactify() - pickler.addrsOfTree = treePkl.buf.addrsOfTree + pickler.addrOfTree = treePkl.buf.addrOfTree pickler.addrOfSym = treePkl.addrOfSym if (tree.pos.exists) - new PositionPickler(pickler, treePkl.buf.addrsOfTree).picklePositions(tree :: Nil) + new PositionPickler(pickler, treePkl.buf.addrOfTree).picklePositions(tree :: Nil) def rawBytes = // not needed right now, but useful to print raw format. pickler.assembleParts().iterator.grouped(10).toList.zipWithIndex.map { diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 7e51635e5dca..1ed47d92e349 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -175,7 +175,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran Checking.checkRealizable(qual.tpe, qual.pos.focus) super.transform(tree) } - else + else transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil) case tree: Super => if (ctx.owner.enclosingMethod.isInlineMethod) diff --git a/tests/pickling/annot.scala b/tests/pickling/annot.scala new file mode 100644 index 000000000000..d20a6cbf4532 --- /dev/null +++ b/tests/pickling/annot.scala @@ -0,0 +1,12 @@ +trait Type +class RefinedType extends Type + + +object TestAnnot { + def toText(tp: Type) = tp match { + case tp: RefinedType => + val parent :: (refined: List[RefinedType @unchecked]) = ??? + ??? + } + val xs: List[RefinedType @unchecked] = ??? +} diff --git a/tests/pickling/pickleTypes.scala b/tests/pickling/pickleTypes.scala new file mode 100644 index 000000000000..ef322816a4f5 --- /dev/null +++ b/tests/pickling/pickleTypes.scala @@ -0,0 +1,13 @@ +object pickleTypes { + + abstract class C { type T; val x: T; def f: T; def g(y: T): T; def h[U](z: U, y: T): U } + + val x1: Int = ??? + val x2: List[List[Int]] = ??? + val x3: C { type T <: C } = ??? + val x4: C { type T = Int; val x: Int } = ??? + val x5: C { type T = String; def f: String; def g(y: String): String } = ??? + val x6: C { type T = String; def f: String; def g(y: String): String; def h[U](z: U, y: T): U } = ??? + + +} diff --git a/tests/pickling/simple.scala b/tests/pickling/simple.scala new file mode 100644 index 000000000000..243b411176bb --- /dev/null +++ b/tests/pickling/simple.scala @@ -0,0 +1,6 @@ +object Simple {} +/*class Simple { + val x = 3 + val y: String = "" +}*/ + From 5637720ea8769faa86b2cfedbfe9fe705e3f5b28 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 17 Nov 2016 18:56:16 +0100 Subject: [PATCH 07/10] Address reviewer's comments. --- src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 4 ++-- src/dotty/tools/dotc/core/tasty/TastyPickler.scala | 9 +++------ src/dotty/tools/dotc/core/tasty/TreeBuffer.scala | 5 +---- src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 ++++ 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 80bd39841467..5e84c7428139 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -89,10 +89,10 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef patType_Type pat_Term ALTERNATIVE Length alt_Term* UNAPPLY Length fun_Term ImplicitArg* pat_Type pat_Term* - IDENTtpt NameRef Type // used when type ident's type is not a TypeRef + IDENTtpt NameRef Type // used for all type idents SELECTtpt NameRef qual_Term SINGLETONtpt Path - REFINDtpt Length underlying_Term refinement_Stat* + REFINEDtpt Length underlying_Term refinement_Stat* APPLIEDtpt Length tycon_Term arg_Term* POLYtpt Length TypeParam* body_Term TYPEBOUNDStpt Length low_Term high_Term diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index 55fe232c077f..c750a7bb385e 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -54,12 +54,9 @@ class TastyPickler { all.bytes } - /** - * Addresses in TASTY file of trees, stored by pickling. - * Note that trees are checked for reference equality, - * so one can reliably use this function only directly after `pickler`. - * Note that a tree can have several addresses, if it is shared, - * i.e. accessible from different paths. Any such sharing is undone by pickling. + /** The address in the TASTY file of a given tree, or None if unknown. + * Note that trees are looked up by for reference equality, + * so one can reliably use this function only directly after `pickler`. */ var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) diff --git a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala index 67dc6076f045..6c7982d780ab 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeBuffer.scala @@ -17,10 +17,7 @@ class TreeBuffer extends TastyBuffer(50000) { private var delta: Array[Int] = _ private var numOffsets = 0 - /** A map from trees to the address(es) at which a tree is pickled. There may be several - * such addresses if the tree is shared. To keep the map compact, the value type is a - * disjunction of a single address (which is the common case) and a list of addresses. - */ + /** A map from trees to the address at which a tree is pickled. */ private val treeAddrs = new java.util.IdentityHashMap[Tree, Any] // really: Addr | Null def registerTreeAddr(tree: Tree): Addr = treeAddrs.get(tree) match { diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 1838bc5bb000..279ab50263d7 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -537,7 +537,7 @@ class TreePickler(pickler: TastyPickler) { } case AppliedTypeTree(tycon, args) => writeByte(APPLIEDtpt) - withLength { pickleTree(tycon); args.foreach(pickleTree(_)) } + withLength { pickleTree(tycon); args.foreach(pickleTree) } case AndTypeTree(tp1, tp2) => writeByte(ANDtpt) withLength { pickleTree(tp1); pickleTree(tp2) } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 4818c6eeed41..279dda9a140c 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -148,6 +148,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" case tp: AnnotatedType if homogenizedView => + // Positions of annotations in types are not serialized + // (they don;t need to because we keep the original type tree with + // the original annotation anyway. Therefore, there will always be + // one version of the annotation tree that has the correct positions). withoutPos(super.toText(tp)) case tp: SelectionProto => return "?{ " ~ toText(tp.name) ~ (" " provided !tp.name.decode.last.isLetterOrDigit) ~ From 5c7617b006fe4446a105b3db4916956a92826304 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 20 Nov 2016 15:50:26 +0100 Subject: [PATCH 08/10] Fix typos --- src/dotty/tools/dotc/core/tasty/TastyPickler.scala | 2 +- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index c750a7bb385e..c844d522eaa9 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -55,7 +55,7 @@ class TastyPickler { } /** The address in the TASTY file of a given tree, or None if unknown. - * Note that trees are looked up by for reference equality, + * Note that trees are looked up by reference equality, * so one can reliably use this function only directly after `pickler`. */ var addrOfTree: tpd.Tree => Option[Addr] = (_ => None) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 279dda9a140c..29e1d4869757 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -149,7 +149,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return toText(elemtp) ~ "[]" case tp: AnnotatedType if homogenizedView => // Positions of annotations in types are not serialized - // (they don;t need to because we keep the original type tree with + // (they don't need to because we keep the original type tree with // the original annotation anyway. Therefore, there will always be // one version of the annotation tree that has the correct positions). withoutPos(super.toText(tp)) From 7f0637ce073131d8603c567329885e4443cd48d5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Nov 2016 10:22:07 +0100 Subject: [PATCH 09/10] Make This and Super take idents as qualifier/mixin The qualifier of a This and the mixin of a Super were names, which meant that their positions were lost. Now they are untyped idents. --- .../backend/jvm/DottyBackendInterface.scala | 4 +-- src/dotty/tools/dotc/ast/Desugar.scala | 2 +- src/dotty/tools/dotc/ast/Trees.scala | 14 +++++----- src/dotty/tools/dotc/ast/tpd.scala | 7 +++-- src/dotty/tools/dotc/ast/untpd.scala | 8 ++++-- .../tools/dotc/core/tasty/TastyFormat.scala | 25 +++++++++-------- .../tools/dotc/core/tasty/TreePickler.scala | 13 ++++++--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 13 ++++++--- src/dotty/tools/dotc/parsing/Parsers.scala | 27 ++++++++++--------- .../tools/dotc/printing/PlainPrinter.scala | 5 +++- src/dotty/tools/dotc/transform/Erasure.scala | 4 +-- .../tools/dotc/transform/SuperAccessors.scala | 6 ++--- src/dotty/tools/dotc/typer/TypeAssigner.scala | 4 +-- 13 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 03c4315fe956..a7c44994702b 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -973,7 +973,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } object This extends ThisDeconstructor { - def get = field.qual + def get = field.qual.name def apply(s: Symbol): This = tpd.This(s.asClass) } @@ -1020,7 +1020,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } object Super extends SuperDeconstructor { def _1: Tree = field.qual - def _2: Name = field.mix + def _2: Name = field.mix.name } object ArrayValue extends ArrayValueDeconstructor { def _1: Type = field.tpe match { diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 3c510c7b9e99..8f9c42e219dd 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -340,7 +340,7 @@ object desugar { val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) val caseParams = constrVparamss.head.toArray val productElemMeths = for (i <- 0 until arity) yield - syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeName), caseParams(i).name)) + syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeIdent), caseParams(i).name)) def isRepeated(tree: Tree): Boolean = tree match { case PostfixOp(_, nme.raw.STAR) => true case ByNameTypeTree(tree1) => isRepeated(tree1) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 78ac66812596..2801bcae2228 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -159,7 +159,7 @@ object Trees { /** Does this tree define a new symbol that is not defined elsewhere? */ def isDef: Boolean = false - /** Is this tree either the empty tree or the empty ValDef? */ + /** Is this tree either the empty tree or the empty ValDef or an empty type ident? */ def isEmpty: Boolean = false /** Convert tree to a list. Gives a singleton list, except @@ -353,7 +353,7 @@ object Trees { } /** qual.this */ - case class This[-T >: Untyped] private[ast] (qual: TypeName) + case class This[-T >: Untyped] private[ast] (qual: untpd.Ident) extends DenotingTree[T] with TermTree[T] { type ThisTree[-T >: Untyped] = This[T] // Denotation of a This tree is always the underlying class; needs correction for modules. @@ -368,7 +368,7 @@ object Trees { } /** C.super[mix], where qual = C.this */ - case class Super[-T >: Untyped] private[ast] (qual: Tree[T], mix: TypeName) + case class Super[-T >: Untyped] private[ast] (qual: Tree[T], mix: untpd.Ident) extends ProxyTree[T] with TermTree[T] { type ThisTree[-T >: Untyped] = Super[T] def forwardTo = qual @@ -890,12 +890,12 @@ object Trees { case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree case _ => finalize(tree, untpd.Select(qualifier, name)) } - def This(tree: Tree)(qual: TypeName): This = tree match { - case tree: This if qual == tree.qual => tree + def This(tree: Tree)(qual: untpd.Ident): This = tree match { + case tree: This if qual eq tree.qual => tree case _ => finalize(tree, untpd.This(qual)) } - def Super(tree: Tree)(qual: Tree, mix: TypeName): Super = tree match { - case tree: Super if (qual eq tree.qual) && (mix == tree.mix) => tree + def Super(tree: Tree)(qual: Tree, mix: untpd.Ident): Super = tree match { + case tree: Super if (qual eq tree.qual) && (mix eq tree.mix) => tree case _ => finalize(tree, untpd.Super(qual, mix)) } def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = tree match { diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 09f2099d2309..219d6d4ec27d 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -31,11 +31,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { untpd.Select(qualifier, tp.name).withType(tp) def This(cls: ClassSymbol)(implicit ctx: Context): This = - untpd.This(cls.name).withType(cls.thisType) + untpd.This(untpd.Ident(cls.name)).withType(cls.thisType) - def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = + def Super(qual: Tree, mix: untpd.Ident, inConstrCall: Boolean, mixinClass: Symbol)(implicit ctx: Context): Super = ta.assignType(untpd.Super(qual, mix), qual, inConstrCall, mixinClass) + def Super(qual: Tree, mixName: TypeName, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context): Super = + Super(qual, if (mixName.isEmpty) untpd.EmptyTypeIdent else untpd.Ident(mixName), inConstrCall, mixinClass) + def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = ta.assignType(untpd.Apply(fn, args), fn, args) diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index ac35ad09c208..6c5210287134 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -80,6 +80,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree]) extends TypTree case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree + @sharable object EmptyTypeIdent extends Ident(tpnme.EMPTY) with WithoutTypeOrPos[Untyped] { + override def isEmpty = true + } + /** A block arising from a right-associative infix operation, where, e.g. * * a +: b @@ -225,8 +229,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def BackquotedIdent(name: Name): BackquotedIdent = new BackquotedIdent(name) def Select(qualifier: Tree, name: Name): Select = new Select(qualifier, name) def SelectWithSig(qualifier: Tree, name: Name, sig: Signature): Select = new SelectWithSig(qualifier, name, sig) - def This(qual: TypeName): This = new This(qual) - def Super(qual: Tree, mix: TypeName): Super = new Super(qual, mix) + def This(qual: Ident): This = new This(qual) + def Super(qual: Tree, mix: Ident): Super = new Super(qual, mix) def Apply(fun: Tree, args: List[Tree]): Apply = new Apply(fun, args) def TypeApply(fun: Tree, args: List[Tree]): TypeApply = new TypeApply(fun, args) def Literal(const: Constant): Literal = new Literal(const) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 5e84c7428139..cb1b56c3c3d5 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -73,8 +73,9 @@ Standard-Section: "ASTs" TopLevelStat* Application IDENT NameRef Type // used when term ident’s type is not a TermRef SELECT possiblySigned_NameRef qual_Term + QUALTHIS typeIdent_Tree NEW cls_Type - SUPER Length this_Term mixinTrait_Type? + SUPER Length this_Term mixinTypeIdent_Tree? TYPED Length expr_Term ascription_Type NAMEDARG Length paramName_NameRef arg_Term ASSIGN Length lhs_Term rhs_Term @@ -279,16 +280,17 @@ object TastyFormat { final val RENAMED = 79 final val THIS = 96 - final val CLASSconst = 97 - final val ENUMconst = 98 - final val BYNAMEtype = 99 - final val BYNAMEtpt = 100 - final val NEW = 101 - final val IMPLICITarg = 102 - final val PRIVATEqualified = 103 - final val PROTECTEDqualified = 104 - final val RECtype = 105 - final val SINGLETONtpt = 106 + final val QUALTHIS = 97 + final val CLASSconst = 98 + final val ENUMconst = 99 + final val BYNAMEtype = 100 + final val BYNAMEtpt = 101 + final val NEW = 102 + final val IMPLICITarg = 103 + final val PRIVATEqualified = 104 + final val PROTECTEDqualified = 105 + final val RECtype = 106 + final val SINGLETONtpt = 107 final val IDENT = 112 final val IDENTtpt = 113 @@ -506,6 +508,7 @@ object TastyFormat { case TEMPLATE => "TEMPLATE" case SELFDEF => "SELFDEF" case THIS => "THIS" + case QUALTHIS => "QUALTHIS" case SUPER => "SUPER" case CLASSconst => "CLASSconst" case ENUMconst => "ENUMconst" diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 279ab50263d7..80270aa25e0e 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -360,8 +360,13 @@ class TreePickler(pickler: TastyPickler) { pickleName(name) pickleType(tree.tpe) } - case This(_) => - pickleType(tree.tpe) + case This(qual) => + if (qual.isEmpty) pickleType(tree.tpe) + else { + writeByte(QUALTHIS) + val ThisType(tref) = tree.tpe + pickleTree(qual.withType(tref)) + } case Select(qual, name) => writeByte(if (name.isTypeName) SELECTtpt else SELECT) val realName = tree.tpe match { @@ -396,8 +401,8 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleTree(qual); if (!mix.isEmpty) { - val SuperType(_, mixinType) = tree.tpe - pickleType(mixinType) + val SuperType(_, mixinType: TypeRef) = tree.tpe + pickleTree(mix.withType(mixinType)) } } case New(tpt) => diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 1f0bbca19864..eba9ab5331d6 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -885,6 +885,11 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle untpd.Select(qual, unshadowed).withType(tpf(qual.tpe.widenIfUnstable)) } + def readQualId(): (untpd.Ident, TypeRef) = { + val qual = readTerm().asInstanceOf[untpd.Ident] + (untpd.Ident(qual.name).withPos(qual.pos), qual.tpe.asInstanceOf[TypeRef]) + } + def readSimpleTerm(): Tree = tag match { case SHARED => forkAt(readAddr()).readTerm() @@ -902,6 +907,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case SELECTtpt => val name = readName().toTypeName completeSelect(name, TypeRef(_, name)) + case QUALTHIS => + val (qual, tref) = readQualId() + untpd.This(qual).withType(ThisType.raw(tref)) case NEW => New(readTpt()) case SINGLETONtpt => @@ -933,9 +941,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle (tag: @switch) match { case SUPER => val qual = readTerm() - val mixClass = ifBefore(end)(readType().typeSymbol, NoSymbol) - val mixName = if (mixClass.exists) mixClass.name.asTypeName else tpnme.EMPTY - tpd.Super(qual, mixName, ctx.mode.is(Mode.InSuperCall), mixClass) + val (mixId, mixTpe) = ifBefore(end)(readQualId(), (untpd.EmptyTypeIdent, NoType)) + tpd.Super(qual, mixId, ctx.mode.is(Mode.InSuperCall), mixTpe.typeSymbol) case APPLY => val fn = readTerm() val isJava = fn.symbol.is(JavaDefined) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index e0c6be8c8931..68d7e1a53ecf 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -527,27 +527,28 @@ object Parsers { */ def path(thisOK: Boolean, finish: Tree => Tree = id): Tree = { val start = in.offset - def handleThis(name: TypeName) = { + def handleThis(qual: Ident) = { in.nextToken() - val t = atPos(start) { This(name) } + val t = atPos(start) { This(qual) } if (!thisOK && in.token != DOT) syntaxError("`.' expected") dotSelectors(t, finish) } - def handleSuper(name: TypeName) = { + def handleSuper(qual: Ident) = { in.nextToken() val mix = mixinQualifierOpt() - val t = atPos(start) { Super(This(name), mix) } + val t = atPos(start) { Super(This(qual), mix) } accept(DOT) dotSelectors(selector(t), finish) } - if (in.token == THIS) handleThis(tpnme.EMPTY) - else if (in.token == SUPER) handleSuper(tpnme.EMPTY) + if (in.token == THIS) handleThis(EmptyTypeIdent) + else if (in.token == SUPER) handleSuper(EmptyTypeIdent) else { val t = termIdent() if (in.token == DOT) { + def qual = cpy.Ident(t)(t.name.toTypeName) in.nextToken() - if (in.token == THIS) handleThis(t.name.toTypeName) - else if (in.token == SUPER) handleSuper(t.name.toTypeName) + if (in.token == THIS) handleThis(qual) + else if (in.token == SUPER) handleSuper(qual) else selectors(t, finish) } else t @@ -556,9 +557,9 @@ object Parsers { /** MixinQualifier ::= `[' Id `]' */ - def mixinQualifierOpt(): TypeName = - if (in.token == LBRACKET) inBrackets(ident().toTypeName) - else tpnme.EMPTY + def mixinQualifierOpt(): Ident = + if (in.token == LBRACKET) inBrackets(atPos(in.offset) { typeIdent() }) + else EmptyTypeIdent /** StableId ::= Id * | Path `.' Id @@ -617,7 +618,7 @@ object Parsers { termIdent() else if (in.token == THIS) { in.nextToken() - This(tpnme.EMPTY) + This(EmptyTypeIdent) } else if (in.token == LBRACE) if (inPattern) Block(Nil, inBraces(pattern())) @@ -2145,7 +2146,7 @@ object Parsers { val first = expr1() if (in.token == ARROW) { first match { - case Typed(tree @ This(tpnme.EMPTY), tpt) => + case Typed(tree @ This(EmptyTypeIdent), tpt) => self = makeSelfDef(nme.WILDCARD, tpt).withPos(first.pos) case _ => val ValDef(name, tpt, _) = convertToParam(first, expected = "self type clause") diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 06d44e3010fc..4894fa019563 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -127,7 +127,10 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp) match { case tp: TypeType => toTextRHS(tp) - case tp: TermRef if !tp.denotationIsCurrent || tp.symbol.is(Module) || tp.symbol.name.isImportName => + case tp: TermRef + if !tp.denotationIsCurrent && !homogenizedView || // always print underyling when testing picklers + tp.symbol.is(Module) || + tp.symbol.name.isImportName => toTextRef(tp) ~ ".type" case tp: TermRef if tp.denot.isOverloaded => "" diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index a503d55e5af7..069176111ad9 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -352,10 +352,10 @@ object Erasure extends TypeTestsCasts{ assignType(untpd.cpy.Select(tree)(qual, tree.name.primitiveArrayOp), qual) def adaptIfSuper(qual: Tree): Tree = qual match { - case Super(thisQual, tpnme.EMPTY) => + case Super(thisQual, untpd.EmptyTypeIdent) => val SuperType(thisType, supType) = qual.tpe if (sym.owner is Flags.Trait) - cpy.Super(qual)(thisQual, sym.owner.asClass.name) + cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) .withType(SuperType(thisType, sym.owner.typeRef)) else qual.withType(SuperType(thisType, thisType.firstParent)) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 10be6db6507e..fea478c9ba1b 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -106,7 +106,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) else if (sym is Deferred) { val member = sym.overridingSymbol(clazz) - if (mix != tpnme.EMPTY || + if (!mix.name.isEmpty || !member.exists || !((member is AbsOverride) && member.isIncompleteIn(clazz))) ctx.error( @@ -114,7 +114,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { sel.pos) else ctx.log(i"ok super $sel ${sym.showLocated} $member $clazz ${member.isIncompleteIn(clazz)}") } - else if (mix == tpnme.EMPTY && !(sym.owner is Trait)) + else if (mix.name.isEmpty && !(sym.owner is Trait)) // SI-4989 Check if an intermediate class between `clazz` and `sym.owner` redeclares the method as abstract. for (intermediateClass <- clazz.info.baseClasses.tail.takeWhile(_ != sym.owner)) { val overriding = sym.overridingSymbol(intermediateClass) @@ -124,7 +124,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { sel.pos) } - if (name.isTermName && mix == tpnme.EMPTY && + if (name.isTermName && mix.name.isEmpty && ((clazz is Trait) || clazz != ctx.owner.enclosingClass || !validCurrentClass)) superAccessorCall(sel)(ctx.withPhase(thisTransformer.next)) else sel diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1599d95e64ba..ee2d682785ba 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -282,7 +282,7 @@ trait TypeAssigner { } def assignType(tree: untpd.This)(implicit ctx: Context) = { - val cls = qualifyingClass(tree, tree.qual, packageOK = false) + val cls = qualifyingClass(tree, tree.qual.name, packageOK = false) tree.withType(cls.thisType) } @@ -291,7 +291,7 @@ trait TypeAssigner { val qtype @ ThisType(_) = qual.tpe val cls = qtype.cls - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix) match { + def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { case p :: Nil => p case Nil => From a2b1e601142e66255c252bf0584d2fc5ceb46b07 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 21 Nov 2016 14:46:34 +0100 Subject: [PATCH 10/10] Make inliner not rely on ambiguous map key --- src/dotty/tools/dotc/typer/Inliner.scala | 34 +++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala index f6616d329324..3931fcaf40a6 100644 --- a/src/dotty/tools/dotc/typer/Inliner.scala +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -290,8 +290,20 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { */ private val paramProxy = new mutable.HashMap[Type, Type] - /** A map from (direct and outer) this references in `rhs` to references of their proxies */ - private val thisProxy = new mutable.HashMap[Type, TermRef] + /** A map from the classes of (direct and outer) this references in `rhs` + * to references of their proxies. + * Note that we can't index by the ThisType itself since there are several + * possible forms to express what is logicaly the same ThisType. E.g. + * + * ThisType(TypeRef(ThisType(p), cls)) + * + * vs + * + * ThisType(TypeRef(TermRef(ThisType(), p), cls)) + * + * These are different (wrt ==) types but represent logically the same key + */ + private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef] /** A buffer for bindings that define proxies for actual arguments */ val bindingsBuf = new mutable.ListBuffer[ValOrDefDef] @@ -349,13 +361,13 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { private def registerType(tpe: Type): Unit = tpe match { case tpe: ThisType if !ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package) && - !thisProxy.contains(tpe) => + !thisProxy.contains(tpe.cls) => if (tpe.cls.isStaticOwner) - thisProxy(tpe) = tpe.cls.sourceModule.termRef + thisProxy(tpe.cls) = tpe.cls.sourceModule.termRef else { val proxyName = s"${tpe.cls.name}_this".toTermName val proxyType = tpe.asSeenFrom(prefix.tpe, meth.owner) - thisProxy(tpe) = newSym(proxyName, EmptyFlags, proxyType).termRef + thisProxy(tpe.cls) = newSym(proxyName, EmptyFlags, proxyType).termRef registerType(meth.owner.thisType) // make sure we have a base from which to outer-select } case tpe: NamedType @@ -408,7 +420,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { // and parameters to type references of their arguments or proxies. val typeMap = new TypeMap { def apply(t: Type) = t match { - case t: ThisType => thisProxy.getOrElse(t, t) + case t: ThisType => thisProxy.getOrElse(t.cls, t) case t: TypeRef => paramProxy.getOrElse(t, mapOver(t)) case t: SingletonType => paramProxy.getOrElse(t, mapOver(t)) case t => mapOver(t) @@ -420,9 +432,13 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { def treeMap(tree: Tree) = { tree match { case _: This => - thisProxy.get(tree.tpe) match { - case Some(t) => ref(t).withPos(tree.pos) - case None => tree + tree.tpe match { + case thistpe: ThisType => + thisProxy.get(thistpe.cls) match { + case Some(t) => ref(t).withPos(tree.pos) + case None => tree + } + case _ => tree } case _: Ident => paramProxy.get(tree.tpe) match {