diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fff4b517b6a4..cae8c5782bbf 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -6,7 +6,7 @@ import Contexts._ import Periods._ import Symbols._ import Scopes._ -import typer.{FrontEnd, Typer, Mode, ImportInfo} +import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform._ @@ -19,22 +19,20 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new Companions), + List(new FirstTransform, new SyntheticMethods), List(new SuperAccessors), - // pickling and refchecks goes here - List(new ElimRepeated, new ElimLocals), + // pickling goes here + List(new RefChecks, new ElimRepeated, new ElimLocals), List(new ExtensionMethods), List(new TailRec), List(new PatternMatcher, - new LazyValTranformContext().transformer, + // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new Nullarify, new TypeTestsCasts, new InterceptedMethods, new Literalize), - List(new Erasure), - List(new UncurryTreeTransform - /* , new Constructors */) + List(new Erasure) ) var runId = 1 diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala index cc971f05c5fe..98da95f61b96 100644 --- a/src/dotty/tools/dotc/ElimLocals.scala +++ b/src/dotty/tools/dotc/ElimLocals.scala @@ -6,11 +6,11 @@ import DenotTransformers.SymTransformer import Phases.Phase import Contexts.Context import SymDenotations.SymDenotation -import TreeTransforms.TreeTransform +import TreeTransforms.MiniPhaseTransform import Flags.Local /** Widens all private[this] and protected[this] qualifiers to just private/protected */ -class ElimLocals extends TreeTransform with SymTransformer { thisTransformer => +class ElimLocals extends MiniPhaseTransform with SymTransformer { thisTransformer => override def name = "elimlocals" def transformSym(ref: SymDenotation)(implicit ctx: Context) = diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index c3d0f9c3a8a3..cfb9c338fd86 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -72,13 +72,13 @@ object desugar { /** A type definition copied from `tdef` with a rhs typetree derived from it */ def derivedTypeParam(tdef: TypeDef) = - cpy.TypeDef(tdef, tdef.mods, tdef.name, - new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef, tdef.tparams) // todo: copy type params + cpy.TypeDef(tdef)( + rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef) /** A value definition copied from `vdef` with a tpt typetree derived from it */ def derivedTermParam(vdef: ValDef) = - cpy.ValDef(vdef, vdef.mods, vdef.name, - new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef, vdef.rhs) + cpy.ValDef(vdef)( + tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef) // ----- Desugar methods ------------------------------------------------- @@ -98,7 +98,7 @@ object desugar { // I don't see a problem with that but if there is one we can avoid it by making a copy here. val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef)) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral - val setter = cpy.DefDef(vdef, + val setter = cpy.DefDef(vdef)( mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, TypeTree(defn.UnitType), setterRhs) // rhs gets filled in later, when field is generated and getter has parameters Thicket(vdef, setter) @@ -124,14 +124,14 @@ object desugar { val DefDef(mods, name, tparams, vparamss, tpt, rhs) = meth val epbuf = new ListBuffer[ValDef] val tparams1 = tparams mapConserve { - case tparam @ TypeDef(mods, name, ContextBounds(tbounds, cxbounds)) => + case tparam @ TypeDef(_, _, ContextBounds(tbounds, cxbounds)) => for (cxbound <- cxbounds) { val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param val epname = (nme.EVIDENCE_PARAM_PREFIX.toString + epbuf.length).toTermName epbuf += ValDef(Modifiers(paramFlags | Implicit), epname, cxbound, EmptyTree) } - cpy.TypeDef(tparam, mods, name, tbounds, tparam.tparams) + cpy.TypeDef(tparam)(rhs = tbounds) case tparam => tparam } @@ -146,7 +146,7 @@ object desugar { case _ => vparamss :+ evidenceParams } - cpy.DefDef(meth, mods, name, tparams1, vparamss1, tpt, rhs) + cpy.DefDef(meth)(tparams = tparams1, vparamss = vparamss1) } /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */ @@ -159,7 +159,7 @@ object desugar { } def normalizedVparamss = vparamss map (_ map (vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree))) + cpy.ValDef(vparam)(rhs = EmptyTree))) def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { case (vparam :: vparams) :: vparamss1 => @@ -182,9 +182,9 @@ object desugar { val defGetters = defaultGetters(vparamss, 0) if (defGetters.isEmpty) meth1 else { - val mods1 = meth1.mods | DefaultParameterized - val meth2 = cpy.DefDef(meth1, meth1.mods | DefaultParameterized, - meth1.name, meth1.tparams, normalizedVparamss, meth1.tpt, meth1.rhs) + val meth2 = cpy.DefDef(meth1)( + mods = meth1.mods | DefaultParameterized, + vparamss = normalizedVparamss) Thicket(meth2 :: defGetters) } } @@ -196,25 +196,26 @@ object desugar { * class C { type v C$T; type v T = C$T } */ def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { - val TypeDef(mods, name, rhs) = tdef - if (mods is PrivateLocalParam) { - val tparam = cpy.TypeDef(tdef, - mods &~ PrivateLocal | ExpandedName, name.expandedName(ctx.owner), rhs, tdef.tparams) - val alias = cpy.TypeDef(tdef, - Modifiers(PrivateLocalParamAccessor | Synthetic | mods.flags & VarianceFlags), - name, refOfDef(tparam)) + if (tdef.mods is PrivateLocalParam) { + val tparam = cpy.TypeDef(tdef)( + mods = tdef.mods &~ PrivateLocal | ExpandedName, + name = tdef.name.expandedName(ctx.owner)) + val alias = cpy.TypeDef(tdef)( + mods = Modifiers(PrivateLocalParamAccessor | Synthetic | tdef.mods.flags & VarianceFlags), + rhs = refOfDef(tparam), + tparams = Nil) Thicket(tparam, alias) } - else cpy.TypeDef(tdef, mods, name, rhs, tdef.tparams) + else tdef } private val synthetic = Modifiers(Synthetic) private def toDefParam(tparam: TypeDef) = - cpy.TypeDef(tparam, Modifiers(Param), tparam.name, tparam.rhs, tparam.tparams) + cpy.TypeDef(tparam)(mods = Modifiers(Param)) private def toDefParam(vparam: ValDef) = - cpy.ValDef(vparam, Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs) + cpy.ValDef(vparam)(mods = Modifiers(Param | vparam.mods.flags & Implicit)) /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { @@ -239,13 +240,12 @@ object desugar { ListOfNil } else constr1.vparamss.nestedMap(toDefParam) - val constr = cpy.DefDef(constr1, - constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs) + val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) // Add constructor type parameters to auxiliary constructors val normalizedBody = body map { case ddef: DefDef if ddef.name.isConstructorName => - cpy.DefDef(ddef, ddef.mods, ddef.name, constrTparams, ddef.vparamss, ddef.tpt, ddef.rhs) + cpy.DefDef(ddef)(tparams = constrTparams) case stat => stat } @@ -288,20 +288,18 @@ object desugar { DefDef(synthetic, name, Nil, Nil, TypeTree(), rhs) val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true))) val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(arity))) - def selectorName(n: Int) = - if (arity == 1) nme.get else nme.selectorName(n) val caseParams = constrVparamss.head.toArray val productElemMeths = for (i <- 0 until arity) yield - syntheticProperty(selectorName(i), Select(This(EmptyTypeName), caseParams(i).name)) + syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeName), caseParams(i).name)) val copyMeths = if (mods is Abstract) Nil else { def copyDefault(vparam: ValDef) = makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam)) val copyFirstParams = derivedVparamss.head.map(vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, copyDefault(vparam))) + cpy.ValDef(vparam)(rhs = copyDefault(vparam))) val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree)) + cpy.ValDef(vparam)(rhs = EmptyTree)) DefDef(synthetic, nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil } copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList @@ -312,12 +310,12 @@ object desugar { def productConstr(n: Int) = { val tycon = ref(defn.ProductNClass(n).typeRef) val targs = constrVparamss.head map (_.tpt) - AppliedTypeTree(tycon, targs) + if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) } // Case classes get a ProductN parent var parents1 = parents - if ((mods is Case) && 2 <= arity && arity <= Definitions.MaxTupleArity) + if ((mods is Case) && arity <= Definitions.MaxTupleArity) parents1 = parents1 :+ productConstr(arity) // The thicket which is the desugared version of the companion object @@ -326,7 +324,8 @@ object desugar { moduleDef( ModuleDef( Modifiers(Synthetic), name.toTermName, - Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))).toList + Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))) + .withPos(cdef.pos).toList // The companion object defifinitions, if a companion is needed, Nil otherwise. // companion definitions include: @@ -340,7 +339,8 @@ object desugar { val companions = if (mods is Case) { val parent = - if (constrTparams.nonEmpty) anyRef // todo: also use anyRef if constructor has a dependent method type (or rule that out)! + if (constrTparams.nonEmpty || constrVparamss.length > 1) anyRef + // todo: also use anyRef if constructor has a dependent method type (or rule that out)! else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) val applyMeths = if (mods is Abstract) Nil @@ -379,21 +379,23 @@ object desugar { val self1 = { val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt if (self.isEmpty) self - else cpy.ValDef(self, self.mods | SelfName, self.name, selfType, self.rhs) + else cpy.ValDef(self)(mods = self.mods | SelfName, tpt = selfType) } val cdef1 = { val originalTparams = constr1.tparams.toIterator val originalVparams = constr1.vparamss.toIterator.flatten val tparamAccessors = derivedTparams map { tdef => - cpy.TypeDef(tdef, originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams) + cpy.TypeDef(tdef)(mods = originalTparams.next.mods) } + val caseAccessor = if (mods is Case) CaseAccessor else EmptyFlags val vparamAccessors = derivedVparamss.flatten map { vdef => - cpy.ValDef(vdef, originalVparams.next.mods, vdef.name, vdef.tpt, vdef.rhs) + cpy.ValDef(vdef)(mods = originalVparams.next.mods | caseAccessor) } - cpy.TypeDef(cdef, mods, name, - cpy.Template(impl, constr, parents1, self1, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)) + cpy.TypeDef(cdef)( + rhs = cpy.Template(impl)(constr, parents1, self1, + tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), + tparams = Nil) } // install the watch on classTycon @@ -414,18 +416,18 @@ object desugar { * final class name$ extends parents { self: name.type => body } */ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { - val ModuleDef(mods, name, tmpl @ Template(constr, parents, self, body)) = mdef + val ModuleDef(mods, name, tmpl) = mdef if (mods is Package) - PackageDef(Ident(name), cpy.ModuleDef(mdef, mods &~ Package, nme.PACKAGE, tmpl) :: Nil) + PackageDef(Ident(name), cpy.ModuleDef(mdef)(mods &~ Package, nme.PACKAGE, tmpl) :: Nil) else { val clsName = name.moduleClassName val clsRef = Ident(clsName) val modul = ValDef(mods | ModuleCreationFlags, name, clsRef, New(clsRef, Nil)) withPos mdef.pos - val ValDef(selfMods, selfName, selfTpt, selfRhs) = self - if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", self.pos) + val ValDef(selfMods, selfName, selfTpt, selfRhs) = tmpl.self + if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", tmpl.self.pos) val clsSelf = ValDef(selfMods, selfName, SingletonTypeTree(Ident(name)), selfRhs) - .withPos(self.pos orElse tmpl.pos.startPos) - val clsTmpl = cpy.Template(tmpl, constr, parents, clsSelf, body) + .withPos(tmpl.self.pos orElse tmpl.pos.startPos) + val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body) val cls = TypeDef(mods.toTypeFlags & AccessFlags | ModuleClassCreationFlags, clsName, clsTmpl) Thicket(modul, classDef(cls)) } @@ -502,7 +504,7 @@ object desugar { */ def block(tree: Block)(implicit ctx: Context): Block = tree.expr match { case EmptyTree => - cpy.Block(tree, tree.stats, + cpy.Block(tree)(tree.stats, unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) case _ => tree @@ -515,7 +517,7 @@ object desugar { val TypeBoundsTree(lo, hi) = tree val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi - cpy.TypeBoundsTree(tree, lo1, hi1) + cpy.TypeBoundsTree(tree)(lo1, hi1) } /** Make closure corresponding to function. @@ -562,7 +564,7 @@ object desugar { */ def makeBinop(left: Tree, op: Name, right: Tree): Tree = { def assignToNamedArg(arg: Tree) = arg match { - case Assign(Ident(name), rhs) => cpy.NamedArg(arg, name, rhs) + case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs) case _ => arg } if (isLeftAssoc(op)) { diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index cc3e53abcd8a..ca225993046c 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -103,11 +103,19 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** The number of arguments in an application */ def numArgs(tree: Tree): Int = unsplice(tree) match { case Apply(fn, args) => numArgs(fn) + args.length - case TypeApply(fn, args) => numArgs(fn) - case Block(stats, expr) => numArgs(expr) + case TypeApply(fn, _) => numArgs(fn) + case Block(_, expr) => numArgs(expr) case _ => 0 } + /** The (last) list of arguments of an application */ + def arguments(tree: Tree): List[Tree] = unsplice(tree) match { + case Apply(_, args) => args + case TypeApply(fn, _) => arguments(fn) + case Block(_, expr) => arguments(expr) + case _ => Nil + } + /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ @@ -253,7 +261,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => * is an abstract typoe declaration */ def lacksDefinition(mdef: MemberDef) = mdef match { - case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName + case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor) case mdef: TypeDef => mdef.rhs.isEmpty || mdef.rhs.isInstanceOf[TypeBoundsTree] case _ => false } diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index e19221841dd0..1c915ca43abb 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -422,7 +422,7 @@ object Trees { case class Ident[-T >: Untyped] private[ast] (name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Ident[T] - def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this, name) + def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this)(name) def qualifier: Tree[T] = genericEmptyTree } @@ -433,7 +433,7 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] - def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this, qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -620,7 +620,7 @@ object Trees { case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = SelectFromTypeTree[T] - def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this, qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name) } /** left & right */ @@ -666,7 +666,7 @@ object Trees { extends NameTree[T] with DefTree[T] with PatternTree[T] { type ThisTree[-T >: Untyped] = Bind[T] override def envelope: Position = pos union initialPos - def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this, name, body) + def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body) } /** tree_1 | ... | tree_n */ @@ -697,7 +697,7 @@ object Trees { case class ValDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = ValDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this, mods, name.toTermName, tpt, rhs) + def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(name = name.toTermName) assert(isEmpty || tpt != genericEmptyTree) } @@ -705,7 +705,7 @@ object Trees { case class DefDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = DefDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this, mods, name.toTermName, tparams, vparamss, tpt, rhs) + def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this)(name = name.toTermName) assert(tpt != genericEmptyTree) } @@ -717,7 +717,7 @@ object Trees { case class TypeDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TypeName, rhs: Tree[T]) extends MemberDef[T] { type ThisTree[-T >: Untyped] = TypeDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this, mods, name.toTypeName, rhs, tparams) + def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(name = name.toTypeName) /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] @@ -912,6 +912,13 @@ object Trees { val cpy: TreeCopier + /** A class for copying trees. The copy methods avid creating a new tree + * If all arguments stay the same. + * + * Note: Some of the copy methods take a context. + * These are exactly those methods that are overridden in TypedTreeCopier + * so that they selectively retype themselves. Retyping needs a context. + */ abstract class TreeCopier { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] @@ -919,179 +926,200 @@ object Trees { def finalize(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] = postProcess(tree, copied withPos tree.pos) - def Ident(tree: Tree, name: Name): Ident = tree match { + def Ident(tree: Tree)(name: Name): Ident = tree match { case tree: BackquotedIdent => if (name == tree.name) tree else finalize(tree, new BackquotedIdent(name)) case tree: Ident if (name == tree.name) => tree case _ => finalize(tree, untpd.Ident(name)) } - def Select(tree: Tree, qualifier: Tree, name: Name): Select = tree match { + def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = tree match { case tree: SelectWithSig => if ((qualifier eq tree.qualifier) && (name == tree.name)) tree else finalize(tree, new SelectWithSig(qualifier, name, tree.sig)) 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 { + def This(tree: Tree)(qual: TypeName): This = tree match { case tree: This if (qual == tree.qual) => tree case _ => finalize(tree, untpd.This(qual)) } - def Super(tree: Tree, qual: Tree, mix: TypeName): Super = tree match { + def Super(tree: Tree)(qual: Tree, mix: TypeName): Super = tree match { case tree: Super if (qual eq tree.qual) && (mix == tree.mix) => tree case _ => finalize(tree, untpd.Super(qual, mix)) } - def Apply(tree: Tree, fun: Tree, args: List[Tree]): Apply = tree match { + def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = tree match { case tree: Apply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.Apply(fun, args)) } - def TypeApply(tree: Tree, fun: Tree, args: List[Tree]): TypeApply = tree match { + def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = tree match { case tree: TypeApply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.TypeApply(fun, args)) } - def Literal(tree: Tree, const: Constant): Literal = tree match { + def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = tree match { case tree: Literal if (const == tree.const) => tree case _ => finalize(tree, untpd.Literal(const)) } - def New(tree: Tree, tpt: Tree): New = tree match { + def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = tree match { case tree: New if (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.New(tpt)) } - def Pair(tree: Tree, left: Tree, right: Tree): Pair = tree match { + def Pair(tree: Tree)(left: Tree, right: Tree)(implicit ctx: Context): Pair = tree match { case tree: Pair if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.Pair(left, right)) } - def Typed(tree: Tree, expr: Tree, tpt: Tree): Typed = tree match { + def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = tree match { case tree: Typed if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Typed(expr, tpt)) } - def NamedArg(tree: Tree, name: Name, arg: Tree): NamedArg = tree match { + def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = tree match { case tree: NamedArg if (name == tree.name) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.NamedArg(name, arg)) } - def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign = tree match { + def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = tree match { case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.Assign(lhs, rhs)) } - def Block(tree: Tree, stats: List[Tree], expr: Tree): Block = tree match { + def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = tree match { case tree: Block if (stats eq tree.stats) && (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Block(stats, expr)) } - def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If = tree match { + def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = tree match { case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree case _ => finalize(tree, untpd.If(cond, thenp, elsep)) } - def Closure(tree: Tree, env: List[Tree], meth: Tree, tpt: Tree): Closure = tree match { + def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = tree match { case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Closure(env, meth, tpt)) } - def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match = tree match { + def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = tree match { case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree case _ => finalize(tree, untpd.Match(selector, cases)) } - def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef = tree match { + def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = tree match { case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree case _ => finalize(tree, untpd.CaseDef(pat, guard, body)) } - def Return(tree: Tree, expr: Tree, from: Tree): Return = tree match { + def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = tree match { case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree case _ => finalize(tree, untpd.Return(expr, from)) } - def Try(tree: Tree, expr: Tree, handler: Tree, finalizer: Tree): Try = tree match { + def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = tree match { case tree: Try if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => finalize(tree, untpd.Try(expr, handler, finalizer)) } - def Throw(tree: Tree, expr: Tree): Throw = tree match { + def Throw(tree: Tree)(expr: Tree)(implicit ctx: Context): Throw = tree match { case tree: Throw if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Throw(expr)) } - def SeqLiteral(tree: Tree, elems: List[Tree]): SeqLiteral = tree match { + def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = tree match { case tree: JavaSeqLiteral => if (elems eq tree.elems) tree else finalize(tree, new JavaSeqLiteral(elems)) case tree: SeqLiteral if (elems eq tree.elems) => tree case _ => finalize(tree, untpd.SeqLiteral(elems)) } - def TypeTree(tree: Tree, original: Tree): TypeTree = tree match { + def TypeTree(tree: Tree)(original: Tree): TypeTree = tree match { case tree: TypeTree if original eq tree.original => tree case _ => finalize(tree, untpd.TypeTree(original)) } - def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree = tree match { + def SingletonTypeTree(tree: Tree)(ref: Tree): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)) } - def SelectFromTypeTree(tree: Tree, qualifier: Tree, name: Name): SelectFromTypeTree = tree match { + def SelectFromTypeTree(tree: Tree)(qualifier: Tree, name: Name): SelectFromTypeTree = tree match { case tree: SelectFromTypeTree if (qualifier eq tree.qualifier) && (name == tree.name) => tree case _ => finalize(tree, untpd.SelectFromTypeTree(qualifier, name)) } - def AndTypeTree(tree: Tree, left: Tree, right: Tree): AndTypeTree = tree match { + def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match { case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.AndTypeTree(left, right)) } - def OrTypeTree(tree: Tree, left: Tree, right: Tree): OrTypeTree = tree match { + def OrTypeTree(tree: Tree)(left: Tree, right: Tree): OrTypeTree = tree match { case tree: OrTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.OrTypeTree(left, right)) } - def RefinedTypeTree(tree: Tree, tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match { + def RefinedTypeTree(tree: Tree)(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match { case tree: RefinedTypeTree if (tpt eq tree.tpt) && (refinements eq tree.refinements) => tree case _ => finalize(tree, untpd.RefinedTypeTree(tpt, refinements)) } - def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { + def AppliedTypeTree(tree: Tree)(tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) } - def ByNameTypeTree(tree: Tree, result: Tree): ByNameTypeTree = tree match { + def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match { case tree: ByNameTypeTree if (result eq tree.result) => tree case _ => finalize(tree, untpd.ByNameTypeTree(result)) } - def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree): TypeBoundsTree = tree match { + def TypeBoundsTree(tree: Tree)(lo: Tree, hi: Tree): TypeBoundsTree = tree match { case tree: TypeBoundsTree if (lo eq tree.lo) && (hi eq tree.hi) => tree case _ => finalize(tree, untpd.TypeBoundsTree(lo, hi)) } - def Bind(tree: Tree, name: Name, body: Tree): Bind = tree match { + def Bind(tree: Tree)(name: Name, body: Tree): Bind = tree match { case tree: Bind if (name eq tree.name) && (body eq tree.body) => tree case _ => finalize(tree, untpd.Bind(name, body)) } - def Alternative(tree: Tree, trees: List[Tree]): Alternative = tree match { + def Alternative(tree: Tree)(trees: List[Tree]): Alternative = tree match { case tree: Alternative if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Alternative(trees)) } - def UnApply(tree: Tree, fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match { + def UnApply(tree: Tree)(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match { case tree: UnApply if (fun eq tree.fun) && (implicits eq tree.implicits) && (patterns eq tree.patterns) => tree case _ => finalize(tree, untpd.UnApply(fun, implicits, patterns)) } - def ValDef(tree: Tree, mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = tree match { + def ValDef(tree: Tree)(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = tree match { case tree: ValDef if (mods == tree.mods) && (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.ValDef(mods, name, tpt, rhs)) } - def DefDef(tree: Tree, mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef = tree match { + def DefDef(tree: Tree)(mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef = tree match { case tree: DefDef if (mods == tree.mods) && (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.DefDef(mods, name, tparams, vparamss, tpt, rhs)) } - def TypeDef(tree: Tree, mods: Modifiers, name: TypeName, rhs: Tree, tparams: List[untpd.TypeDef] = Nil): TypeDef = tree match { + def TypeDef(tree: Tree)(mods: Modifiers, name: TypeName, rhs: Tree, tparams: List[untpd.TypeDef]): TypeDef = tree match { case tree: TypeDef if (mods == tree.mods) && (name == tree.name) && (rhs eq tree.rhs) && (tparams eq tree.tparams) => tree case _ => finalize(tree, untpd.TypeDef(mods, name, tparams, rhs)) } - def Template(tree: Tree, constr: DefDef, parents: List[Tree], self: ValDef, body: List[Tree]): Template = tree match { + def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: List[Tree]): Template = tree match { case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.body) => tree case _ => finalize(tree, untpd.Template(constr, parents, self, body)) } - def Import(tree: Tree, expr: Tree, selectors: List[untpd.Tree]): Import = tree match { + def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree]): Import = tree match { case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree case _ => finalize(tree, untpd.Import(expr, selectors)) } - def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]): PackageDef = tree match { + def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree]): PackageDef = tree match { case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree case _ => finalize(tree, untpd.PackageDef(pid, stats)) } - def Annotated(tree: Tree, annot: Tree, arg: Tree): Annotated = tree match { + def Annotated(tree: Tree)(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated = tree match { case tree: Annotated if (annot eq tree.annot) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.Annotated(annot, arg)) } - def Thicket(tree: Tree, trees: List[Tree]): Thicket = tree match { + def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match { case tree: Thicket if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Thicket(trees)) } + + // Copier methods with default arguments; these demand that the original tree + // is of the same class as the copy. We only include trees with more than 2 elements here. + def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = + If(tree: Tree)(cond, thenp, elsep) + def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = + Closure(tree: Tree)(env, meth, tpt) + def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = + CaseDef(tree: Tree)(pat, guard, body) + def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = + Try(tree: Tree)(expr, handler, finalizer) + def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = + UnApply(tree: Tree)(fun, implicits, patterns) + def ValDef(tree: ValDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): ValDef = + ValDef(tree: Tree)(mods, name, tpt, rhs) + def DefDef(tree: DefDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): DefDef = + DefDef(tree: Tree)(mods, name, tparams, vparamss, tpt, rhs) + def TypeDef(tree: TypeDef)(mods: Modifiers = tree.mods, name: TypeName = tree.name, rhs: Tree = tree.rhs, tparams: List[untpd.TypeDef] = tree.tparams): TypeDef = + TypeDef(tree: Tree)(mods, name, rhs, tparams) + def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: List[Tree] = tree.body): Template = + Template(tree: Tree)(constr, parents, self, body) } abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { @@ -1100,88 +1128,88 @@ object Trees { case Ident(name) => tree case Select(qualifier, name) => - cpy.Select(tree, transform(qualifier), name) + cpy.Select(tree)(transform(qualifier), name) case This(qual) => tree case Super(qual, mix) => - cpy.Super(tree, transform(qual), mix) + cpy.Super(tree)(transform(qual), mix) case Apply(fun, args) => - cpy.Apply(tree, transform(fun), transform(args)) + cpy.Apply(tree)(transform(fun), transform(args)) case TypeApply(fun, args) => - cpy.TypeApply(tree, transform(fun), transform(args)) + cpy.TypeApply(tree)(transform(fun), transform(args)) + case Literal(const) => + tree case New(tpt) => - cpy.New(tree, transform(tpt)) + cpy.New(tree)(transform(tpt)) + case Pair(left, right) => + cpy.Pair(tree)(transform(left), transform(right)) case Typed(expr, tpt) => - cpy.Typed(tree, transform(expr), transform(tpt)) + cpy.Typed(tree)(transform(expr), transform(tpt)) case NamedArg(name, arg) => - cpy.NamedArg(tree, name, transform(arg)) + cpy.NamedArg(tree)(name, transform(arg)) case Assign(lhs, rhs) => - cpy.Assign(tree, transform(lhs), transform(rhs)) + cpy.Assign(tree)(transform(lhs), transform(rhs)) + case Block(stats, expr) => + cpy.Block(tree)(transformStats(stats), transform(expr)) + case If(cond, thenp, elsep) => + cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) case Closure(env, meth, tpt) => - cpy.Closure(tree, transform(env), transform(meth), transform(tpt)) + cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) + case Match(selector, cases) => + cpy.Match(tree)(transform(selector), transformSub(cases)) + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) case Return(expr, from) => - cpy.Return(tree, transform(expr), transformSub(from)) + cpy.Return(tree)(transform(expr), transformSub(from)) + case Try(block, handler, finalizer) => + cpy.Try(tree)(transform(block), transform(handler), transform(finalizer)) case Throw(expr) => - cpy.Throw(tree, transform(expr)) + cpy.Throw(tree)(transform(expr)) + case SeqLiteral(elems) => + cpy.SeqLiteral(tree)(transform(elems)) case TypeTree(original) => tree case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree, transform(ref)) + cpy.SingletonTypeTree(tree)(transform(ref)) case SelectFromTypeTree(qualifier, name) => - cpy.SelectFromTypeTree(tree, transform(qualifier), name) + cpy.SelectFromTypeTree(tree)(transform(qualifier), name) case AndTypeTree(left, right) => - cpy.AndTypeTree(tree, transform(left), transform(right)) + cpy.AndTypeTree(tree)(transform(left), transform(right)) case OrTypeTree(left, right) => - cpy.OrTypeTree(tree, transform(left), transform(right)) + cpy.OrTypeTree(tree)(transform(left), transform(right)) case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree, transform(tpt), transformSub(refinements)) + cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree, transform(tpt), transform(args)) + cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree, transform(result)) + cpy.ByNameTypeTree(tree)(transform(result)) case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree, transform(lo), transform(hi)) + cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) case Bind(name, body) => - cpy.Bind(tree, name, transform(body)) + cpy.Bind(tree)(name, transform(body)) case Alternative(trees) => - cpy.Alternative(tree, transform(trees)) + cpy.Alternative(tree)(transform(trees)) case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree, transform(fun), transform(implicits), transform(patterns)) + cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) case EmptyValDef => tree case ValDef(mods, name, tpt, rhs) => - cpy.ValDef(tree, mods, name, transform(tpt), transform(rhs)) + cpy.ValDef(tree)(mods, name, transform(tpt), transform(rhs)) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - cpy.DefDef(tree, mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(rhs)) + cpy.DefDef(tree)(mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(rhs)) case tree @ TypeDef(mods, name, rhs) => - cpy.TypeDef(tree, mods, name, transform(rhs), tree.tparams) + cpy.TypeDef(tree)(mods, name, transform(rhs), tree.tparams) case Template(constr, parents, self, body) => - cpy.Template(tree, transformSub(constr), transform(parents), transformSub(self), transformStats(body)) + cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(body)) case Import(expr, selectors) => - cpy.Import(tree, transform(expr), selectors) + cpy.Import(tree)(transform(expr), selectors) case PackageDef(pid, stats) => - cpy.PackageDef(tree, transformSub(pid), transformStats(stats)) + cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)) + case Annotated(annot, arg) => + cpy.Annotated(tree)(transform(annot), transform(arg)) case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case Literal(const) => - tree - case Pair(left, right) => - cpy.Pair(tree, transform(left), transform(right)) - case Block(stats, expr) => - cpy.Block(tree, transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree, transform(cond), transform(thenp), transform(elsep)) - case Match(selector, cases) => - cpy.Match(tree, transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree, transform(pat), transform(guard), transform(body)) - case Try(block, handler, finalizer) => - cpy.Try(tree, transform(block), transform(handler), transform(finalizer)) - case SeqLiteral(elems) => - cpy.SeqLiteral(tree, transform(elems)) - case Annotated(annot, arg) => - cpy.Annotated(tree, transform(annot), transform(arg)) } def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index d38d4695eedf..b1e4a0cd39fb 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -336,6 +336,135 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { class TypedTreeCopier extends TreeCopier { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] = copied.withTypeUnchecked(tree.tpe) + + override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = { + val tree1 = untpd.cpy.Select(tree)(qualifier, name) + tree match { + case tree: Select if (qualifier.tpe eq tree.qualifier.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => tree.tpe match { + case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe)) + case _ => tree1.withTypeUnchecked(tree.tpe) + } + } + } + + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = { + val tree1 = untpd.cpy.Apply(tree)(fun, args) + tree match { + case tree: Apply if (fun.tpe.widen eq tree.fun.tpe.widen) && sameTypes(args, tree.args) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, fun, args) + } + } + + override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = { + val tree1 = untpd.cpy.TypeApply(tree)(fun, args) + tree match { + case tree: TypeApply if (fun.tpe.widen eq tree.fun.tpe.widen) && sameTypes(args, tree.args) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, fun, args) + } + } + + override def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = + ta.assignType(untpd.cpy.Literal(tree)(const)) + + override def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = + ta.assignType(untpd.cpy.New(tree)(tpt), tpt) + + override def Pair(tree: Tree)(left: Tree, right: Tree)(implicit ctx: Context): Pair = { + val tree1 = untpd.cpy.Pair(tree)(left, right) + tree match { + case tree: Pair if (left.tpe eq tree.left.tpe) && (right.tpe eq tree.right.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, left, right) + } + } + + override def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = + ta.assignType(untpd.cpy.Typed(tree)(expr, tpt), tpt) + + override def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = + ta.assignType(untpd.cpy.NamedArg(tree)(name, arg), arg) + + override def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = + ta.assignType(untpd.cpy.Assign(tree)(lhs, rhs)) + + override def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = { + val tree1 = untpd.cpy.Block(tree)(stats, expr) + tree match { + case tree: Block if (expr.tpe eq tree.expr.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, stats, expr) + } + } + + override def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = { + val tree1 = untpd.cpy.If(tree)(cond, thenp, elsep) + tree match { + case tree: If if (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, thenp, elsep) + } + } + + override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = { + val tree1 = untpd.cpy.Closure(tree)(env, meth, tpt) + tree match { + case tree: Closure if (meth.tpe.widen eq tree.meth.tpe.widen) && (tpt eq tree.tpt) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, meth, tpt) + } + } + + override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = { + val tree1 = untpd.cpy.Match(tree)(selector, cases) + tree match { + case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, cases) + } + } + + override def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = { + val tree1 = untpd.cpy.CaseDef(tree)(pat, guard, body) + tree match { + case tree: CaseDef if (body.tpe eq tree.body.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, body) + } + } + + override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = + ta.assignType(untpd.cpy.Return(tree)(expr, from)) + + override def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = { + val tree1 = untpd.cpy.Try(tree)(expr, handler, finalizer) + tree match { + case tree: Try if (expr.tpe eq tree.expr.tpe) && (handler.tpe eq tree.handler.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, expr, handler) + } + } + + override def Throw(tree: Tree)(expr: Tree)(implicit ctx: Context): Throw = + ta.assignType(untpd.cpy.Throw(tree)(expr)) + + override def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = { + val tree1 = untpd.cpy.SeqLiteral(tree)(elems) + tree match { + case tree: SeqLiteral if sameTypes(elems, tree.elems) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, elems) + } + } + + override def Annotated(tree: Tree)(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated = { + val tree1 = untpd.cpy.Annotated(tree)(annot, arg) + tree match { + case tree: Annotated if (arg.tpe eq tree.arg.tpe) && (annot eq tree.annot) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, annot, arg) + } + } + + override def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = + If(tree: Tree)(cond, thenp, elsep) + override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = + Closure(tree: Tree)(env, meth, tpt) + override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = + CaseDef(tree: Tree)(pat, guard, body) + override def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = + Try(tree: Tree)(expr, handler, finalizer) } implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { @@ -405,74 +534,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree = if (targs.isEmpty) tree else TypeApply(tree, targs) + + def isInstance(tp: Type)(implicit ctx: Context): Tree = + tree.select(defn.Any_isInstanceOf).appliedToType(tp) + + def asInstance(tp: Type)(implicit ctx: Context): Tree = + tree.select(defn.Any_asInstanceOf).appliedToType(tp) + + def ensureConforms(tp: Type)(implicit ctx: Context): Tree = + if (tree.tpe <:< tp) tree else asInstance(tp) + + def and(that: Tree)(implicit ctx: Context): Tree = + tree.select(defn.Boolean_&&).appliedTo(that) + + def or(that: Tree)(implicit ctx: Context): Tree = + tree.select(defn.Boolean_||).appliedTo(that) } implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { def tpes: List[Type] = xs map (_.tpe) } - /** RetypingTreeMap is a TreeMap that is able to propagate type changes. - * - * This is required when types can change during transformation, - * for example if `Block(stats, expr)` is being transformed - * and type of `expr` changes from `TypeRef(prefix, name)` to `TypeRef(newPrefix, name)` with different prefix, t - * type of enclosing Block should also change, otherwise the whole tree would not be type-correct anymore. - * see `propagateType` methods for propagation rulles. - * - * TreeMap does not include such logic as it assumes that types of threes do not change during transformation. - */ - class RetypingTreeMap extends TreeMap { - - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case tree@Select(qualifier, name) => - val tree1 = cpy.Select(tree, transform(qualifier), name) - propagateType(tree, tree1) - case tree@Pair(left, right) => - val left1 = transform(left) - val right1 = transform(right) - val tree1 = cpy.Pair(tree, left1, right1) - propagateType(tree, tree1) - case tree@Block(stats, expr) => - val stats1 = transform(stats) - val expr1 = transform(expr) - val tree1 = cpy.Block(tree, stats1, expr1) - propagateType(tree, tree1) - case tree@If(cond, thenp, elsep) => - val cond1 = transform(cond) - val thenp1 = transform(thenp) - val elsep1 = transform(elsep) - val tree1 = cpy.If(tree, cond1, thenp1, elsep1) - propagateType(tree, tree1) - case tree@Match(selector, cases) => - val selector1 = transform(selector) - val cases1 = transformSub(cases) - val tree1 = cpy.Match(tree, selector1, cases1) - propagateType(tree, tree1) - case tree@CaseDef(pat, guard, body) => - val pat1 = transform(pat) - val guard1 = transform(guard) - val body1 = transform(body) - val tree1 = cpy.CaseDef(tree, pat1, guard1, body1) - propagateType(tree, tree1) - case tree@Try(block, handler, finalizer) => - val expr1 = transform(block) - val handler1 = transform(handler) - val finalizer1 = transform(finalizer) - val tree1 = cpy.Try(tree, expr1, handler1, finalizer1) - propagateType(tree, tree1) - case tree@SeqLiteral(elems) => - val elems1 = transform(elems) - val tree1 = cpy.SeqLiteral(tree, elems1) - propagateType(tree, tree1) - case tree@Annotated(annot, arg) => - val annot1 = transform(annot) - val arg1 = transform(arg) - val tree1 = cpy.Annotated(tree, annot1, arg1) - propagateType(tree, tree1) - case _ => super.transform(tree) - } - } - /** A map that applies three functions together to a tree and makes sure * they are coordinated so that the result is well-typed. The functions are * @param typeMap A function from Type to type that gets applied to the @@ -485,7 +567,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { final class TreeTypeMap( val typeMap: Type => Type = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity _, - val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends RetypingTreeMap { + val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends TreeMap { override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { val tree1 = treeMap(tree) @@ -493,19 +575,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => val (tmap1, tparams1) = transformDefs(ddef.tparams) val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) - cpy.DefDef(ddef, mods, name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(rhs)) + cpy.DefDef(ddef)(mods, name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(rhs)) case blk @ Block(stats, expr) => val (tmap1, stats1) = transformDefs(stats) val expr1 = tmap1.transform(expr) - val tree1 = cpy.Block(blk, stats1, expr1) - propagateType(blk, tree1) + cpy.Block(blk)(stats1, expr1) case cdef @ CaseDef(pat, guard, rhs) => val tmap = withMappedSyms(patVars(pat)) val pat1 = tmap.transform(pat) val guard1 = tmap.transform(guard) val rhs1 = tmap.transform(rhs) - val tree1 = cpy.CaseDef(tree, pat1, guard1, rhs1) - propagateType(cdef, tree1) + cpy.CaseDef(tree)(pat1, guard1, rhs1) case tree1 => super.transform(tree1) } @@ -567,56 +647,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { acc(Nil, tree) } - def propagateType(origTree: Pair, newTree: Pair)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.left.tpe eq origTree.left.tpe) && (newTree.right.tpe eq origTree.right.tpe))) newTree - else ta.assignType(newTree, newTree.left, newTree.right) - } - - def propagateType(origTree: Block, newTree: Block)(implicit ctx: Context) = { - if ((newTree eq origTree) || (newTree.expr.tpe eq origTree.expr.tpe)) newTree - else ta.assignType(newTree, newTree.stats, newTree.expr) - } - - def propagateType(origTree: If, newTree: If)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.thenp.tpe eq origTree.thenp.tpe) && (newTree.elsep.tpe eq origTree.elsep.tpe))) newTree - else ta.assignType(newTree, newTree.thenp, newTree.elsep) - } - - def propagateType(origTree: Match, newTree: Match)(implicit ctx: Context) = { - if ((newTree eq origTree) || sameTypes(newTree.cases, origTree.cases)) newTree - else ta.assignType(newTree, newTree.cases) - } - - def propagateType(origTree: CaseDef, newTree: CaseDef)(implicit ctx: Context) = { - if ((newTree eq newTree) || (newTree.body.tpe eq origTree.body.tpe)) newTree - else ta.assignType(newTree, newTree.body) - } - - def propagateType(origTree: Try, newTree: Try)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.expr.tpe eq origTree.expr.tpe) && (newTree.handler.tpe eq origTree.handler.tpe))) newTree - else ta.assignType(newTree, newTree.expr, newTree.handler) - } - - def propagateType(origTree: SeqLiteral, newTree: SeqLiteral)(implicit ctx: Context) = { - if ((newTree eq origTree) || sameTypes(newTree.elems, origTree.elems)) newTree - else ta.assignType(newTree, newTree.elems) - } - - def propagateType(origTree: Annotated, newTree: Annotated)(implicit ctx: Context) = { - if ((newTree eq origTree) || ((newTree.arg.tpe eq origTree.arg.tpe) && (newTree.annot eq origTree.annot))) newTree - else ta.assignType(newTree, newTree.annot, newTree.arg) - } - - def propagateType(origTree: Select, newTree: Select)(implicit ctx: Context) = { - if ((origTree eq newTree) || (origTree.qualifier.tpe eq newTree.qualifier.tpe)) newTree - else newTree.tpe match { - case tpe: NamedType => newTree.withType(tpe.derivedSelect(newTree.qualifier.tpe)) - case _ => newTree - } - } // convert a numeric with a toXXX method def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = { val mname = ("to" + numericCls.name).toTermName @@ -648,15 +678,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = ??? - def mkAnd(tree1: Tree, tree2: Tree)(implicit ctx: Context) = - tree1.select(defn.Boolean_and).appliedTo(tree2) - - def mkAsInstanceOf(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - tree.select(defn.Any_asInstanceOf).appliedToType(pt) - - def ensureConforms(tree: Tree, pt: Type)(implicit ctx: Context): Tree = - if (tree.tpe <:< pt) tree else mkAsInstanceOf(tree, pt) - // ensure that constructors are fully applied? // ensure that normal methods are fully applied? diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 44f34093235d..edade142001f 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -29,7 +29,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) extends MemberDef { type ThisTree[-T >: Untyped] <: Trees.NameTree[T] with Trees.MemberDef[T] with ModuleDef - def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this, mods, name.toTermName, impl) + def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(mods, name.toTermName, impl) } case class SymbolLit(str: String) extends TermTree @@ -59,7 +59,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { class PolyTypeDef(mods: Modifiers, name: TypeName, override val tparams: List[TypeDef], rhs: Tree) extends TypeDef(mods, name, rhs) { - override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this, mods, name.toTypeName, tparams, rhs) + override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this)(mods, name.toTypeName, tparams, rhs) } // ----- TypeTrees that refer to other tree's symbols ------------------- @@ -253,75 +253,75 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def postProcess(tree: Tree, copied: Tree): copied.ThisTree[Untyped] = copied.asInstanceOf[copied.ThisTree[Untyped]] - def ModuleDef(tree: Tree, mods: Modifiers, name: TermName, impl: Template) = tree match { + def ModuleDef(tree: Tree)(mods: Modifiers, name: TermName, impl: Template) = tree match { case tree: ModuleDef if (mods eq tree.mods) && (name eq tree.name) && (impl eq tree.impl) => tree case _ => untpd.ModuleDef(mods, name, impl).withPos(tree.pos) } - def PolyTypeDef(tree: Tree, mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) = tree match { + def PolyTypeDef(tree: Tree)(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) = tree match { case tree: PolyTypeDef if (mods eq tree.mods) && (name eq tree.name) && (tparams eq tree.tparams) && (rhs eq tree.rhs) => tree case _ => new PolyTypeDef(mods, name, tparams, rhs).withPos(tree.pos) } - def SymbolLit(tree: Tree, str: String) = tree match { + def SymbolLit(tree: Tree)(str: String) = tree match { case tree: SymbolLit if (str == tree.str) => tree case _ => untpd.SymbolLit(str).withPos(tree.pos) } - def InterpolatedString(tree: Tree, id: TermName, strings: List[Literal], elems: List[Tree]) = tree match { + def InterpolatedString(tree: Tree)(id: TermName, strings: List[Literal], elems: List[Tree]) = tree match { case tree: InterpolatedString if (id eq tree.id) && (strings eq tree.strings) && (elems eq tree.elems) => tree case _ => untpd.InterpolatedString(id, strings, elems).withPos(tree.pos) } - def Function(tree: Tree, args: List[Tree], body: Tree) = tree match { + def Function(tree: Tree)(args: List[Tree], body: Tree) = tree match { case tree: Function if (args eq tree.args) && (body eq tree.body) => tree case _ => untpd.Function(args, body).withPos(tree.pos) } - def InfixOp(tree: Tree, left: Tree, op: Name, right: Tree) = tree match { + def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match { case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree case _ => untpd.InfixOp(left, op, right).withPos(tree.pos) } - def PostfixOp(tree: Tree, od: Tree, op: Name) = tree match { + def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match { case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree case _ => untpd.PostfixOp(od, op).withPos(tree.pos) } - def PrefixOp(tree: Tree, op: Name, od: Tree) = tree match { + def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match { case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree case _ => untpd.PrefixOp(op, od).withPos(tree.pos) } - def Parens(tree: Tree, t: Tree) = tree match { + def Parens(tree: Tree)(t: Tree) = tree match { case tree: Parens if (t eq tree.t) => tree case _ => untpd.Parens(t).withPos(tree.pos) } - def Tuple(tree: Tree, trees: List[Tree]) = tree match { + def Tuple(tree: Tree)(trees: List[Tree]) = tree match { case tree: Tuple if (trees eq tree.trees) => tree case _ => untpd.Tuple(trees).withPos(tree.pos) } - def WhileDo(tree: Tree, cond: Tree, body: Tree) = tree match { + def WhileDo(tree: Tree)(cond: Tree, body: Tree) = tree match { case tree: WhileDo if (cond eq tree.cond) && (body eq tree.body) => tree case _ => untpd.WhileDo(cond, body).withPos(tree.pos) } - def DoWhile(tree: Tree, body: Tree, cond: Tree) = tree match { + def DoWhile(tree: Tree)(body: Tree, cond: Tree) = tree match { case tree: DoWhile if (body eq tree.body) && (cond eq tree.cond) => tree case _ => untpd.DoWhile(body, cond).withPos(tree.pos) } - def ForYield(tree: Tree, enums: List[Tree], expr: Tree) = tree match { + def ForYield(tree: Tree)(enums: List[Tree], expr: Tree) = tree match { case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree case _ => untpd.ForYield(enums, expr).withPos(tree.pos) } - def ForDo(tree: Tree, enums: List[Tree], body: Tree) = tree match { + def ForDo(tree: Tree)(enums: List[Tree], body: Tree) = tree match { case tree: ForDo if (enums eq tree.enums) && (body eq tree.body) => tree case _ => untpd.ForDo(enums, body).withPos(tree.pos) } - def GenFrom(tree: Tree, pat: Tree, expr: Tree) = tree match { + def GenFrom(tree: Tree)(pat: Tree, expr: Tree) = tree match { case tree: GenFrom if (pat eq tree.pat) && (expr eq tree.expr) => tree case _ => untpd.GenFrom(pat, expr).withPos(tree.pos) } - def GenAlias(tree: Tree, pat: Tree, expr: Tree) = tree match { + def GenAlias(tree: Tree)(pat: Tree, expr: Tree) = tree match { case tree: GenAlias if (pat eq tree.pat) && (expr eq tree.expr) => tree case _ => untpd.GenAlias(pat, expr).withPos(tree.pos) } - def ContextBounds(tree: Tree, bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match { + def ContextBounds(tree: Tree)(bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match { case tree: ContextBounds if (bounds eq tree.bounds) && (cxBounds eq tree.cxBounds) => tree case _ => untpd.ContextBounds(bounds, cxBounds).withPos(tree.pos) } - def PatDef(tree: Tree, mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match { + def PatDef(tree: Tree)(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => untpd.PatDef(mods, pats, tpt, rhs).withPos(tree.pos) } @@ -330,41 +330,41 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(mods, name, impl) => - cpy.ModuleDef(tree, mods, name, transformSub(impl)) + cpy.ModuleDef(tree)(mods, name, transformSub(impl)) case SymbolLit(str) => - cpy.SymbolLit(tree, str) + cpy.SymbolLit(tree)(str) case InterpolatedString(id, strings, elems) => - cpy.InterpolatedString(tree, id, transformSub(strings), transform(elems)) + cpy.InterpolatedString(tree)(id, transformSub(strings), transform(elems)) case Function(args, body) => - cpy.Function(tree, transform(args), transform(body)) + cpy.Function(tree)(transform(args), transform(body)) case InfixOp(left, op, right) => - cpy.InfixOp(tree, transform(left), op, transform(right)) + cpy.InfixOp(tree)(transform(left), op, transform(right)) case PostfixOp(od, op) => - cpy.PostfixOp(tree, transform(od), op) + cpy.PostfixOp(tree)(transform(od), op) case PrefixOp(op, od) => - cpy.PrefixOp(tree, op, transform(od)) + cpy.PrefixOp(tree)(op, transform(od)) case Parens(t) => - cpy.Parens(tree, transform(t)) + cpy.Parens(tree)(transform(t)) case Tuple(trees) => - cpy.Tuple(tree, transform(trees)) + cpy.Tuple(tree)(transform(trees)) case WhileDo(cond, body) => - cpy.WhileDo(tree, transform(cond), transform(body)) + cpy.WhileDo(tree)(transform(cond), transform(body)) case DoWhile(body, cond) => - cpy.DoWhile(tree, transform(body), transform(cond)) + cpy.DoWhile(tree)(transform(body), transform(cond)) case ForYield(enums, expr) => - cpy.ForYield(tree, transform(enums), transform(expr)) + cpy.ForYield(tree)(transform(enums), transform(expr)) case ForDo(enums, body) => - cpy.ForDo(tree, transform(enums), transform(body)) + cpy.ForDo(tree)(transform(enums), transform(body)) case GenFrom(pat, expr) => - cpy.GenFrom(tree, transform(pat), transform(expr)) + cpy.GenFrom(tree)(transform(pat), transform(expr)) case GenAlias(pat, expr) => - cpy.GenAlias(tree, transform(pat), transform(expr)) + cpy.GenAlias(tree)(transform(pat), transform(expr)) case ContextBounds(bounds, cxBounds) => - cpy.ContextBounds(tree, transformSub(bounds), transform(cxBounds)) + cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => - cpy.PatDef(tree, mods, transform(pats), transform(tpt), transform(rhs)) + cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) case tree: PolyTypeDef => - cpy.PolyTypeDef(tree, tree.mods, tree.name, transformSub(tree.tparams), transform(tree.rhs)) + cpy.PolyTypeDef(tree)(tree.mods, tree.name, transformSub(tree.tparams), transform(tree.rhs)) case _ => super.transform(tree) } diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 5bfe1d0b6829..0cbfb6880ca0 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -23,6 +23,7 @@ object Printers { val completions = noPrinter val gadts = noPrinter val hk = noPrinter + val variances = noPrinter val incremental = noPrinter val config = noPrinter val transforms = noPrinter diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index aab2942bdfe4..af2e42b776b4 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -66,7 +66,8 @@ class ScalaSettings extends Settings.SettingGroup { val logFreeTerms = BooleanSetting("-Xlog-free-terms", "Print a message when reification creates a free term.") val logFreeTypes = BooleanSetting("-Xlog-free-types", "Print a message when reification resorts to generating a free type.") val maxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255) - val Xmigration28 = BooleanSetting("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.") + val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.") + val Xsource = VersionSetting("-Xsource", "Treat compiler input as Scala source for the specified version.") val Xnojline = BooleanSetting("-Xnojline", "Do not use JLine for editing.") val Xverify = BooleanSetting("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)") val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.") diff --git a/src/dotty/tools/dotc/config/ScalaVersion.scala b/src/dotty/tools/dotc/config/ScalaVersion.scala new file mode 100644 index 000000000000..7d45854417c6 --- /dev/null +++ b/src/dotty/tools/dotc/config/ScalaVersion.scala @@ -0,0 +1,183 @@ +/* @author James Iry + */ +package dotty.tools.dotc.config + +import scala.util.{Try, Success, Failure} + +/** + * Represents a single Scala version in a manner that + * supports easy comparison and sorting. + */ +sealed abstract class ScalaVersion extends Ordered[ScalaVersion] { + def unparse: String +} + +/** + * A scala version that sorts higher than all actual versions + */ +case object NoScalaVersion extends ScalaVersion { + def unparse = "none" + + def compare(that: ScalaVersion): Int = that match { + case NoScalaVersion => 0 + case _ => 1 + } +} + +/** + * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion + * may or may not be a released version - i.e. this same class is used to represent + * final, release candidate, milestone, and development builds. The build argument is used + * to segregate builds + */ +case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion { + def unparse = s"${major}.${minor}.${rev}.${build.unparse}" + + def compare(that: ScalaVersion): Int = that match { + case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) => + // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these + // comparisons a lot so I'm using brute force direct style code + if (major < thatMajor) -1 + else if (major > thatMajor) 1 + else if (minor < thatMinor) -1 + else if (minor > thatMinor) 1 + else if (rev < thatRev) -1 + else if (rev > thatRev) 1 + else build compare thatBuild + case AnyScalaVersion => 1 + case NoScalaVersion => -1 + } +} + +/** + * A Scala version that sorts lower than all actual versions + */ +case object AnyScalaVersion extends ScalaVersion { + def unparse = "any" + + def compare(that: ScalaVersion): Int = that match { + case AnyScalaVersion => 0 + case _ => -1 + } +} + +/** + * Methods for parsing ScalaVersions + */ +object ScalaVersion { + private val dot = "\\." + private val dash = "\\-" + private def not(s:String) = s"[^${s}]" + private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r + + def parse(versionString : String): Try[ScalaVersion] = { + def failure = Failure(new NumberFormatException( + s"There was a problem parsing ${versionString}. " + + "Versions should be in the form major[.minor[.revision]] " + + "where each part is a positive number, as in 2.10.1. " + + "The minor and revision parts are optional." + )) + + def toInt(s: String) = s match { + case null | "" => 0 + case _ => s.toInt + } + + def isInt(s: String) = Try(toInt(s)).isSuccess + + import ScalaBuild._ + + def toBuild(s: String) = s match { + case null | "FINAL" => Final + case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2))) + case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1))) + case _ => Development(s) + } + + try versionString match { + case "" | "any" => Success(AnyScalaVersion) + case "none" => Success(NoScalaVersion) + case R(_, majorS, _, minorS, _, revS, _, buildS) => + Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS))) + case _ => failure + } catch { + case e: NumberFormatException => failure + } + } + + /** + * The version of the compiler running now + */ + val current = parse(util.Properties.versionNumberString).get +} + +/** + * Represents the data after the dash in major.minor.rev-build + */ +abstract class ScalaBuild extends Ordered[ScalaBuild] { + /** + * Return a version of this build information that can be parsed back into the + * same ScalaBuild + */ + def unparse: String +} + +object ScalaBuild { + + /** A development, test, nightly, snapshot or other "unofficial" build + */ + case class Development(id: String) extends ScalaBuild { + def unparse = s"-${id}" + + def compare(that: ScalaBuild) = that match { + // sorting two development builds based on id is reasonably valid for two versions created with the same schema + // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions + // this is a pragmatic compromise + case Development(thatId) => id compare thatId + // assume a development build is newer than anything else, that's not really true, but good luck + // mapping development build versions to other build types + case _ => 1 + } + } + + /** A final build + */ + case object Final extends ScalaBuild { + def unparse = "" + + def compare(that: ScalaBuild) = that match { + case Final => 0 + // a final is newer than anything other than a development build or another final + case Development(_) => -1 + case _ => 1 + } + } + + /** A candidate for final release + */ + case class RC(n: Int) extends ScalaBuild { + def unparse = s"-RC${n}" + + def compare(that: ScalaBuild) = that match { + // compare two rcs based on their RC numbers + case RC(thatN) => n - thatN + // an rc is older than anything other than a milestone or another rc + case Milestone(_) => 1 + case _ => -1 + } + } + + /** An intermediate release + */ + case class Milestone(n: Int) extends ScalaBuild { + def unparse = s"-M${n}" + + def compare(that: ScalaBuild) = that match { + // compare two milestones based on their milestone numbers + case Milestone(thatN) => n - thatN + // a milestone is older than anything other than another milestone + case _ => -1 + + } + } +} diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala index 17d4d67125e8..531c49bfbc52 100644 --- a/src/dotty/tools/dotc/config/Settings.scala +++ b/src/dotty/tools/dotc/config/Settings.scala @@ -18,6 +18,7 @@ object Settings { val IntTag = ClassTag.Int val StringTag = ClassTag(classOf[String]) val ListTag = ClassTag(classOf[List[_]]) + val VersionTag = ClassTag(classOf[ScalaVersion]) class SettingsState(initialValues: Seq[Any]) { private var values = ArrayBuffer(initialValues: _*) @@ -132,6 +133,11 @@ object Settings { case _: NumberFormatException => fail(s"$arg2 is not an integer argument for $name", args2) } + case (VersionTag, _) => + ScalaVersion.parse(argRest) match { + case Success(v) => update(v, args) + case Failure(ex) => fail(ex.getMessage, args) + } case (_, Nil) => missingArg } @@ -246,5 +252,8 @@ object Settings { def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] = publish(Setting(name, descr, Nil, prefix = pre)) + + def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = + publish(Setting(name, descr, default)) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala index b4b7ebd24e2a..f67381ddcd7b 100644 --- a/src/dotty/tools/dotc/core/Annotations.scala +++ b/src/dotty/tools/dotc/core/Annotations.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc package core import Symbols._, Types._, util.Positions._, Contexts._, Constants._, ast.tpd._ +import config.ScalaVersion object Annotations { @@ -15,6 +16,14 @@ object Annotations { def derivedAnnotation(tree: Tree)(implicit ctx: Context) = if (tree eq this.tree) this else Annotation(tree) + + def arguments(implicit ctx: Context) = ast.tpd.arguments(tree) + def argument(i: Int)(implicit ctx: Context): Option[Tree] = { + val args = arguments + if (i < args.length) Some(args(i)) else None + } + def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] = + for (ConstantType(c) <- argument(i) map (_.tpe)) yield c } case class ConcreteAnnotation(t: Tree) extends Annotation { @@ -69,4 +78,28 @@ object Annotations { val tref = cls.typeRef Annotation(defn.ThrowsAnnot.typeRef.appliedTo(tref), Ident(tref)) } + + /** A decorator that provides queries for specific annotations + * of a symbol. + */ + implicit class AnnotInfo(val sym: Symbol) extends AnyVal { + + def isDeprecated(implicit ctx: Context) = + sym.hasAnnotation(defn.DeprecatedAnnot) + + def deprecationMessage(implicit ctx: Context) = + for (annot <- sym.getAnnotation(defn.DeprecatedAnnot); + arg <- annot.argumentConstant(0)) + yield arg.stringValue + + def migrationVersion(implicit ctx: Context) = + for (annot <- sym.getAnnotation(defn.MigrationAnnot); + arg <- annot.argumentConstant(1)) + yield ScalaVersion.parse(arg.stringValue) + + def migrationMessage(implicit ctx: Context) = + for (annot <- sym.getAnnotation(defn.MigrationAnnot); + arg <- annot.argumentConstant(0)) + yield ScalaVersion.parse(arg.stringValue) + } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index cd7b4689689f..c96f1ba31108 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -130,7 +130,7 @@ object Decorators { */ implicit class PhaseListDecorator(val names: List[String]) extends AnyVal { def containsPhase(phase: Phase): Boolean = phase match { - case phase: TreeTransformer => phase.transformations.exists(containsPhase) + case phase: TreeTransformer => phase.transformations.exists(trans => containsPhase(trans.phase)) case _ => names exists (n => n == "all" || phase.name.startsWith(n)) } } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 286d1437f553..82ed3ce1d6cd 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -107,6 +107,10 @@ class Definitions { lazy val JavaPackageVal = ctx.requiredPackage("java") lazy val JavaLangPackageVal = ctx.requiredPackage("java.lang") + // fundamental modules + lazy val SysPackage = ctx.requiredModule("scala.sys.package") + def Sys_error = ctx.requiredMethod(SysPackage.moduleClass.asClass, nme.error) + /** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter) * because after erasure the Any and AnyVal references get remapped to the Object methods * which would result in a double binding assertion failure. @@ -177,8 +181,17 @@ class Definitions { lazy val ScalaPredefModule = ctx.requiredModule("scala.Predef") lazy val ScalaRuntimeModule = ctx.requiredModule("scala.runtime.ScalaRunTime") - lazy val BoxesRunTimeModule = ctx.requiredModule("scala.runtime.BoxesRunTime") - lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass + lazy val ScalaRuntimeClass = ScalaRuntimeModule.moduleClass.asClass + + def runtimeMethod(name: PreName) = ctx.requiredMethod(ScalaRuntimeClass, name) + + lazy val BoxesRunTimeModule = ctx.requiredModule("scala.runtime.BoxesRunTime") + lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass.asClass + lazy val ScalaStaticsModule = ctx.requiredModule("scala.runtime.Statics") + lazy val ScalaStaticsClass = ScalaStaticsModule.moduleClass.asClass + + def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name) + lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef") lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil") lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms) @@ -200,8 +213,8 @@ class Definitions { lazy val UnitClass = valueClassSymbol("scala.Unit", BoxedUnitClass, java.lang.Void.TYPE, UnitEnc) lazy val BooleanClass = valueClassSymbol("scala.Boolean", BoxedBooleanClass, java.lang.Boolean.TYPE, BooleanEnc) lazy val Boolean_! = BooleanClass.requiredMethod(nme.UNARY_!) - lazy val Boolean_and = BooleanClass.requiredMethod(nme.ZAND) - lazy val Boolean_or = BooleanClass.requiredMethod(nme.ZOR) + lazy val Boolean_&& = BooleanClass.requiredMethod(nme.ZAND) + lazy val Boolean_|| = BooleanClass.requiredMethod(nme.ZOR) lazy val ByteClass = valueClassSymbol("scala.Byte", BoxedByteClass, java.lang.Byte.TYPE, ByteEnc) lazy val ShortClass = valueClassSymbol("scala.Short", BoxedShortClass, java.lang.Short.TYPE, ShortEnc) @@ -269,6 +282,9 @@ class Definitions { lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable") lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable") lazy val ProductClass = ctx.requiredClass("scala.Product") + + lazy val Product_canEqual = ProductClass.requiredMethod(nme.canEqual_) + lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass // Annotation base classes @@ -292,6 +308,7 @@ class Definitions { lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature") lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated") + lazy val MigrationAnnot = ctx.requiredClass("scala.annotation.migration") lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault") lazy val ThrowsAnnot = ctx.requiredClass("scala.throws") lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked") @@ -389,7 +406,7 @@ class Definitions { lazy val Function0_apply = FunctionClass(0).requiredMethod(nme.apply) lazy val TupleClass = mkArityArray("scala.Tuple", MaxTupleArity, 2) - lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 2) + lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 0) lazy val FunctionClasses: Set[Symbol] = FunctionClass.toSet lazy val TupleClasses: Set[Symbol] = TupleClass.toSet @@ -406,6 +423,8 @@ class Definitions { lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule) + lazy val overriddenBySynthetic = Set[Symbol](Any_equals, Any_hashCode, Any_toString, Product_canEqual) + def isTupleType(tp: Type)(implicit ctx: Context) = { val arity = tp.dealias.argInfos.length arity <= MaxTupleArity && (tp isRef TupleClass(arity)) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 022230ff81a5..fa2292c60b71 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -202,7 +202,7 @@ object Denotations { def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = if (isOverloaded) atSignature(targetType.signature).matchingDenotation(site, targetType) - else if (exists && !(site.memberInfo(symbol) matches targetType)) + else if (exists && !site.memberInfo(symbol).matchesLoosely(targetType)) NoDenotation else asSingleDenotation @@ -231,13 +231,16 @@ object Denotations { else if (denot1.signature matches denot2.signature) { val info1 = denot1.info val info2 = denot2.info + val sym1 = denot1.symbol val sym2 = denot2.symbol val sym2Accessible = sym2.isAccessibleFrom(pre) - if (sym2Accessible && info2 <:< info1) denot2 + def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) = + info1 <:< info2 && + (sym1.isAsConcrete(sym2) || !(info2 <:< info1)) + if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2 else { - val sym1 = denot1.symbol val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && info1 <:< info2) denot1 + if (sym1Accessible && prefer(info1, sym1, info2, sym2)) denot1 else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 else { @@ -550,7 +553,9 @@ object Denotations { startPid = cur.validFor.firstPhaseId else { next match { - case next: ClassDenotation => next.resetFlag(Frozen) + case next: ClassDenotation => + assert(!next.is(Package), s"illegal transfomation of package denotation by transformer ${ctx.withPhase(transformer).phase}") + next.resetFlag(Frozen) case _ => } next.nextInRun = cur.nextInRun diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c527cef62209..20427516df24 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -304,7 +304,7 @@ object Flags { /** Info can be refined during GADT pattern match */ final val GADTFlexType = typeFlag(25, "") - /** A case parameter (or its accessor, or a GADT skolem) */ + /** A case parameter accessor */ final val CaseAccessor = termFlag(26, "") /** An type parameter which is an alias for some other (non-visible) type parameter */ @@ -444,7 +444,7 @@ object Flags { final val RetainedTypeArgFlags = VarianceFlags | ExpandedName | Protected | Local /** Modules always have these flags set */ - final val ModuleCreationFlags = ModuleVal | Final | Stable + final val ModuleCreationFlags = ModuleVal | Lazy | Final | Stable /** Module classes always have these flags set */ final val ModuleClassCreationFlags = ModuleClass | Final diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index aabde4cf9fb0..cecc5d1d7e23 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -8,9 +8,8 @@ import DenotTransformers._ import Denotations._ import config.Printers._ import scala.collection.mutable.{ListBuffer, ArrayBuffer} -import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, TreeTransform} +import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} import dotty.tools.dotc.transform.TreeTransforms -import TreeTransforms.Separator import Periods._ trait Phases { @@ -81,7 +80,7 @@ object Phases { val phasesInBlock: Set[String] = phasess(i).map(_.name).toSet for(phase<-phasess(i)) { phase match { - case p: TreeTransform => + case p: MiniPhase => val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases assert(unmetRequirements.isEmpty, @@ -91,9 +90,9 @@ object Phases { assert(false, s"Only tree transforms can be squashed, ${phase.name} can not be squashed") } } - val transforms = phasess(i).asInstanceOf[List[TreeTransform]] + val transforms = phasess(i).asInstanceOf[List[MiniPhase]].map(_.treeTransform) val block = new TreeTransformer { - override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}") + override def name: String = transformations.map(_.phase.name).mkString("TreeTransform:{", ", ", "}") override def transformations: Array[TreeTransform] = transforms.toArray } squashedPhases += block @@ -181,7 +180,7 @@ object Phases { final val erasureName = "erasure" final val flattenName = "flatten" - abstract class Phase extends DotClass { + trait Phase extends DotClass { def name: String diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 919e35a7e97b..426df83bcddb 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -176,9 +176,9 @@ object Scopes { /** enter a symbol in this scope. */ final def enter[T <: Symbol](sym: T)(implicit ctx: Context): T = { - if (sym.isType) { + if (sym.isType && ctx.phaseId <= ctx.typerPhase.id) { assert(lookup(sym.name) == NoSymbol, - s"duplicate type ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG + s"duplicate ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG } newScopeEntry(sym) sym diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 91a8e4345918..a19f824e54ed 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -147,7 +147,7 @@ object SymDenotations { } private def completeFrom(completer: LazyType)(implicit ctx: Context): Unit = { - if (myFlags is Touched) throw new CyclicReference(this) + if (myFlags is Touched) throw CyclicReference(this) myFlags |= Touched // completions.println(s"completing ${this.debugString}") @@ -200,14 +200,9 @@ object SymDenotations { dropOtherAnnotations(annotations, cls).nonEmpty /** Optionally, the arguments of the first annotation matching the given class symbol */ - final def getAnnotationArgs(cls: Symbol)(implicit ctx: Context): Option[List[tpd.Tree]] = + final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { - case annot :: _ => - Some( - annot.tree match { - case Trees.Apply(_, args) => args - case _ => Nil - }) + case annot :: _ => Some(annot) case nil => None } @@ -719,20 +714,29 @@ object SymDenotations { /** The non-private symbol whose name and type matches the type of this symbol * in the given class. - * @param inClass The class containing the symbol's definition + * @param inClass The class containing the result symbol's definition * @param site The base type from which member types are computed * * inClass <-- find denot.symbol class C { <-- symbol is here * * site: Subtype of both inClass and C */ - final def matchingSymbol(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = { + final def matchingDecl(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = { var denot = inClass.info.nonPrivateDecl(name) if (denot.isTerm) // types of the same name always match denot = denot.matchingDenotation(site, site.memberInfo(symbol)) denot.symbol } + /** The non-private member of `site` whose name and type matches the type of this symbol + */ + final def matchingMember(site: Type)(implicit ctx: Context): Symbol = { + var denot = site.nonPrivateMember(name) + if (denot.isTerm) // types of the same name always match + denot = denot.matchingDenotation(site, site.memberInfo(symbol)) + denot.symbol + } + /** If false, this symbol cannot possibly participate in an override, * either as overrider or overridee. */ @@ -742,7 +746,7 @@ object SymDenotations { /** The symbol, in class `inClass`, that is overridden by this denotation. */ final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = if (!canMatchInheritedSymbols && (owner ne inClass)) NoSymbol - else matchingSymbol(inClass, owner.thisType) + else matchingDecl(inClass, owner.thisType) /** All symbols overriden by this denotation. */ final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = @@ -762,7 +766,7 @@ object SymDenotations { * @param ofclazz is a subclass of this symbol's owner */ final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = - if (canMatchInheritedSymbols) matchingSymbol(inClass, inClass.thisType) + if (canMatchInheritedSymbols) matchingDecl(inClass, inClass.thisType) else NoSymbol /** The symbol accessed by a super in the definition of this symbol when @@ -772,7 +776,7 @@ object SymDenotations { final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = { def loop(bcs: List[ClassSymbol]): Symbol = bcs match { case bc :: bcs1 => - val sym = matchingSymbol(bcs.head, base.thisType) + val sym = matchingDecl(bcs.head, base.thisType) .suchThat(alt => !(alt is Deferred)).symbol if (sym.exists) sym else loop(bcs.tail) case _ => @@ -801,7 +805,7 @@ object SymDenotations { */ final def accessBoundary(base: Symbol)(implicit ctx: Context): Symbol = { val fs = flags - if (fs is PrivateOrLocal) owner + if (fs is Private) owner else if (fs is StaticProtected) defn.RootClass else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin else if (fs is Protected) base @@ -1039,7 +1043,7 @@ object SymDenotations { } private def computeBases(implicit ctx: Context): Unit = { - if (myBaseClasses eq Nil) throw new CyclicReference(this) + if (myBaseClasses eq Nil) throw CyclicReference(this) myBaseClasses = Nil val seen = new mutable.BitSet val locked = new mutable.BitSet @@ -1299,7 +1303,7 @@ object SymDenotations { basetp = computeBaseTypeRefOf(tp) baseTypeRefCache.put(tp, basetp) } else if (basetp == NoPrefix) { - throw new CyclicReference(this) + throw CyclicReference(this) } basetp case _ => @@ -1476,6 +1480,8 @@ object SymDenotations { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete") } + object NoCompleter extends NoCompleter + /** A lazy type for modules that points to the module class. * Needed so that `moduleClass` works before completion. * Completion of modules is always completion of the underlying diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 1767d7c0c5ca..06414818f1a0 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -382,7 +382,11 @@ object Symbols { */ def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type = { val nextCtx = ctx.withPhase(phase.next) - this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx) + if (this.owner.is(Package)) { + denot.validFor |= InitialPeriod + if (this is Module) this.moduleClass.validFor |= InitialPeriod + } + else this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx) entered(nextCtx) } diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index c63788ec9377..2c8e9902b8d5 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -463,16 +463,18 @@ class TypeApplications(val self: Type) extends AnyVal { self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams) } - /** If this type has a base type `B[T1, ..., Tn]` where the type parameters - * of `B` match one-by-one the variances of `tparams`, convert it to + /** Test whether this type has a base type `B[T1, ..., Tn]` where the type parameters + * of `B` match one-by-one the variances of `tparams`, and where the lambda + * abstracted type * * LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] } * { type $hkArg$0 = T1; ...; type $hkArg$n = Tn } * + * satisfies predicate `p`. Try base types in the order of ther occurrence in `baseClasses`. * A type parameter matches a varianve V if it has V as its variance or if V == 0. */ - def EtaLiftTo(tparams: List[Symbol])(implicit ctx: Context): Type = { - def tryLift(bcs: List[ClassSymbol]): Type = bcs match { + def testLifted(tparams: List[Symbol], p: Type => Boolean)(implicit ctx: Context): Boolean = { + def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => val tp = self.baseTypeWithArgs(bc) val targs = tp.argInfos @@ -481,19 +483,20 @@ class TypeApplications(val self: Type) extends AnyVal { param2.variance == param2.variance || param2.variance == 0 if ((tycon.typeParams corresponds tparams)(variancesMatch)) { val expanded = tycon.EtaExpand - val res = (expanded /: targs) { (partialInst, targ) => + val lifted = (expanded /: targs) { (partialInst, targ) => val tparam = partialInst.typeParams.head RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance)) } - hk.println(i"eta lifting $self --> $res") - res + ctx.traceIndented(i"eta lifting $self --> $lifted", hk) { + p(lifted) || tryLift(bcs1) + } } else tryLift(bcs1) case nil => - NoType + false } - if (tparams.isEmpty) NoType - else if (typeParams.nonEmpty) EtaExpand + if (tparams.isEmpty) false + else if (typeParams.nonEmpty) p(EtaExpand) else tryLift(self.baseClasses) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1e1d02be2fd5..48286fe471f7 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -315,7 +315,8 @@ class TypeComparer(initctx: Context) extends DotClass { private def rebase(tp: NamedType): Type = { def rebaseFrom(prefix: Type): Type = { rebaseQual(prefix, tp.name, considerBoth = true) match { - case rt: RefinedType if rt ne prefix => tp.derivedSelect(RefinedThis(rt)) + case rt: RefinedType if rt ne prefix => + tp.derivedSelect(RefinedThis(rt)).dealias // dealias to short-circuit cycles spanning type aliases or LazyRefs case _ => tp } } @@ -453,7 +454,7 @@ class TypeComparer(initctx: Context) extends DotClass { def isHKSubType = tp2.name == tpnme.Apply && { val lambda2 = tp2.prefix.LambdaClass(forcing = true) lambda2.exists && !tp1.isLambda && - isSubType(tp1.EtaLiftTo(lambda2.typeParams), tp2.prefix) + tp1.testLifted(lambda2.typeParams, isSubType(_, tp2.prefix)) } def compareNamed = { implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type @@ -511,6 +512,8 @@ class TypeComparer(initctx: Context) extends DotClass { case NoType => true } compareWild + case tp2: LazyRef => + isSubType(tp1, tp2.ref) case tp2: AnnotatedType => isSubType(tp1, tp2.tpe) // todo: refine? case AndType(tp21, tp22) => @@ -568,6 +571,8 @@ class TypeComparer(initctx: Context) extends DotClass { case _ => true } compareWild + case tp1: LazyRef => + isSubType(tp1.ref, tp2) case tp1: AnnotatedType => isSubType(tp1.tpe, tp2) case ErrorType => @@ -668,7 +673,7 @@ class TypeComparer(initctx: Context) extends DotClass { || hasMatchingMember(name2) || fourthTry(tp1, tp2) ) - || needsEtaLift(tp1, tp2) && isSubType(tp1.EtaLiftTo(tp2.typeParams), tp2) + || needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2)) ) } compareRefined @@ -763,7 +768,7 @@ class TypeComparer(initctx: Context) extends DotClass { isNewSubType(tp1.parent, tp2) } finally pendingRefinedBases = saved - } || needsEtaLift(tp2, tp1) && isSubType(tp1, tp2.EtaLiftTo(tp1.typeParams)) + } || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _)) case AndType(tp11, tp12) => isNewSubType(tp11, tp2) || isNewSubType(tp12, tp2) case _ => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 474958b86575..e5c86ef44c2f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -31,6 +31,8 @@ import language.implicitConversions object Types { + private var recCount = 0 + /** The class of types. * The principal subclasses and sub-objects are as follows: * @@ -208,6 +210,10 @@ object Types { final def forallParts(p: Type => Boolean)(implicit ctx: Context): Boolean = !existsPart(!p(_)) + /** Performs operation on all parts of this type */ + final def foreachPart(p: Type => Unit)(implicit ctx: Context): Unit = + new ForeachAccumulator(p).apply((), this) + /** The parts of this type which are type or term refs */ final def namedParts(implicit ctx: Context): collection.Set[NamedType] = namedPartsWith(alwaysTrue) @@ -218,9 +224,6 @@ object Types { def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] = new NamedPartsAccumulator(p).apply(mutable.LinkedHashSet(), this) - // needed? - //final def foreach(f: Type => Unit): Unit = ??? - /** Map function `f` over elements of an AndType, rebuilding with function `g` */ def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match { case AndType(tp1, tp2) => g(tp1.mapReduceAnd(f)(g), tp2.mapReduceAnd(f)(g)) @@ -378,6 +381,8 @@ object Types { * flags in `excluded` from consideration. */ final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try { + recCount += 1 + assert(recCount < 20) @tailrec def go(tp: Type): Denotation = tp match { case tp: RefinedType => if (name eq tp.refinedName) goRefined(tp) else go(tp.parent) @@ -435,8 +440,10 @@ object Types { case ex: MergeError => throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}") case ex: Throwable => - println(s"error occurred during: $this: ${this.widen} member $name") + println(i"findMember exception for $this: ${this.widen} member $name") throw ex // DEBUG + } finally { + recCount -= 1 } /** The set of names of members of this type that pass the given name filter @@ -537,6 +544,16 @@ object Types { this, that, alwaysMatchSimple = !ctx.phase.erasedTypes) } + /** This is the same as `matches` except that it also matches => T with T and + * vice versa. + */ + def matchesLoosely(that: Type)(implicit ctx: Context): Boolean = + (this matches that) || { + val thisResult = this.widenExpr + val thatResult = that.widenExpr + (this eq thisResult) != (that eq thatResult) && (thisResult matchesLoosely thatResult) + } + /** The basetype TypeRef of this type with given class symbol, * but without including any type arguments */ @@ -598,7 +615,9 @@ object Types { case _ => this } - /** Follow aliases until type is no longer an alias type. */ + /** Follow aliases and derefernces LazyRefs and instantiated TypeVars until type + * is no longer alias type, LazyRef, or instantiated type variable. + */ final def dealias(implicit ctx: Context): Type = this match { case tp: TypeRef => tp.info match { @@ -608,6 +627,8 @@ object Types { case tp: TypeVar => val tp1 = tp.instanceOpt if (tp1.exists) tp1.dealias else tp + case tp: LazyRef => + tp.ref.dealias case tp => tp } @@ -642,6 +663,14 @@ object Types { case _ => NoType } + /** The chain of underlying types as long as type is a TypeProxy. + * Useful for diagnostics + */ + def underlyingChain(implicit ctx: Context): List[Type] = this match { + case tp: TypeProxy => tp :: tp.underlying.underlyingChain + case _ => Nil + } + /** A prefix-less termRef to a new skolem symbol that has the given type as info */ def narrow(implicit ctx: Context): TermRef = TermRef(NoPrefix, ctx.newSkolem(this)) @@ -1130,7 +1159,11 @@ object Types { (lastSymbol eq null) || (lastSymbol.defRunId != sym.defRunId) || (lastSymbol.defRunId == NoRunId) || - (lastSymbol.infoOrCompleter == ErrorType), + (lastSymbol.infoOrCompleter == ErrorType || + defn.overriddenBySynthetic.contains(lastSymbol) + // for overriddenBySynthetic symbols a TermRef such as SomeCaseClass.this.hashCode + // might be rewritten from Object#hashCode to the hashCode generated at SyntheticMethods + ), s"data race? overwriting symbol of $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id}") protected def sig: Signature = Signature.NotAMethod @@ -1217,7 +1250,7 @@ object Types { if (ctx.underlyingRecursions < LogPendingUnderlyingThreshold) op else if (ctx.pendingUnderlying contains this) - throw new CyclicReference(symbol) + throw CyclicReference(symbol) else try { ctx.pendingUnderlying += this @@ -1468,6 +1501,12 @@ object Types { unique(new CachedConstantType(value)) } + case class LazyRef(refFn: () => Type) extends UncachedProxyType with ValueType { + lazy val ref = refFn() + override def underlying(implicit ctx: Context) = ref + override def toString = s"LazyRef($ref)" + } + // --- Refined Type --------------------------------------------------------- /** A refined type parent { refinement } @@ -2407,6 +2446,9 @@ object Types { case tp @ SuperType(thistp, supertp) => tp.derivedSuperType(this(thistp), this(supertp)) + case tp: LazyRef => + LazyRef(() => this(tp.ref)) + case tp: ClassInfo => mapClassInfo(tp) @@ -2571,6 +2613,11 @@ object Types { def apply(x: Boolean, tp: Type) = x || p(tp) || foldOver(x, tp) } + class ForeachAccumulator(p: Type => Unit)(implicit ctx: Context) extends TypeAccumulator[Unit] { + override def stopAtStatic = false + def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp) + } + class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] { override def stopAtStatic = false def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x @@ -2656,10 +2703,17 @@ object Types { extends FatalTypeError( s"""malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}""") - class CyclicReference(val denot: SymDenotation) + class CyclicReference private (val denot: SymDenotation) extends FatalTypeError(s"cyclic reference involving $denot") { def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}" - printStackTrace() + } + + object CyclicReference { + def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = { + val ex = new CyclicReference(denot) + if (!(ctx.mode is typer.Mode.CheckCyclic)) ex.printStackTrace() + ex + } } class MergeError(msg: String) extends FatalTypeError(msg) diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 59658c9c14f0..193c872f1a47 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -11,6 +11,7 @@ import java.lang.Integer.toHexString import scala.collection.{ mutable, immutable } import scala.collection.mutable.{ ListBuffer, ArrayBuffer } import scala.annotation.switch +import typer.Checking.checkNonCyclic import io.AbstractFile class ClassfileParser( @@ -337,7 +338,11 @@ class ClassfileParser( val savedIndex = index try { index = start - denot.info = sig2typeBounds(tparams, skiptvs = false) + denot.info = + checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles + denot.symbol, + sig2typeBounds(tparams, skiptvs = false), + reportErrors = false) } finally { index = savedIndex } diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index de3f626da44d..2e21358e4891 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -15,6 +15,7 @@ import printing.Texts._ import printing.Printer import io.AbstractFile import util.common._ +import typer.Checking.checkNonCyclic import PickleBuffer._ import scala.reflect.internal.pickling.PickleFormat._ import Decorators._ @@ -516,7 +517,11 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: denot setFlag Scala2x case denot => val tp1 = depoly(tp, denot) - denot.info = if (tag == ALIASsym) TypeAlias(tp1) else tp1 + denot.info = + if (tag == ALIASsym) TypeAlias(tp1) + else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false) + // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles + else tp1 if (denot.isConstructor) addConstructorTypeParams(denot) if (atEnd) { assert(!(denot is SuperAccessor), denot) diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 587f0c088304..19662d22af0a 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -107,7 +107,8 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard * - For T1 & T2, erasure(T1) (??) * - For T1 | T2, the first base class in the linearization of T which is also a base class of T2 * - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit. - * - For any other method type (Fs)T, (|Fs|)|T|. + * - For any other uncurried method type (Fs)T, (|Fs|)|T|. + * - For a curried method type (Fs1)(Fs2)T, (|Fs1|,Es2)ET where (Es2)ET = |(Fs2)T|. * - For a polymorphic type, the erasure of its result type. * - For the class info type of java.lang.Object, the same type without any parents. * - For a class info type of a value class, the same type without any parents. @@ -140,17 +141,25 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard this(tp.baseTypeRef(lubClass(tp1, tp2))) case tp: MethodType => val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_) - tp.derivedMethodType( - tp.paramNames, tp.paramTypes.mapConserve(paramErasure), eraseResult(tp.resultType)) + val formals = tp.paramTypes.mapConserve(paramErasure) + eraseResult(tp.resultType) match { + case rt: MethodType => + tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType) + case rt => + tp.derivedMethodType(tp.paramNames, formals, rt) + } case tp: PolyType => this(tp.resultType) case tp @ ClassInfo(pre, cls, classParents, decls, _) => - def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] - val parents: List[TypeRef] = - if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil - else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil - else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) - tp.derivedClassInfo(this(pre), parents, decls, this(tp.selfType)) + if (cls is Package) tp + else { + def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] + val parents: List[TypeRef] = + if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil + else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil + else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) + tp.derivedClassInfo(this(pre), parents, decls, this(tp.selfType)) + } case NoType | NoPrefix | ErrorType => tp case tp: WildcardType if wildcardOK => diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 0ad33a96c341..e6de5bb5d347 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -274,9 +274,9 @@ object Parsers { */ def convertToTypeId(tree: Tree): Tree = tree match { case id @ Ident(name) => - cpy.Ident(id, name.toTypeName) + cpy.Ident(id)(name.toTypeName) case id @ Select(qual, name) => - cpy.Select(id, qual, name.toTypeName) + cpy.Select(id)(qual, name.toTypeName) case _ => syntaxError("identifier expected", tree.pos) tree @@ -963,7 +963,7 @@ object Parsers { val tpt = typeDependingOn(location) if (isWildcard(t) && location != Location.InPattern) { val vd :: rest = placeholderParams - placeholderParams = cpy.ValDef(vd, vd.mods, vd.name, tpt, vd.rhs) :: rest + placeholderParams = cpy.ValDef(vd)(tpt = tpt) :: rest } Typed(t, tpt) } @@ -1096,7 +1096,7 @@ object Parsers { if (in.token == LBRACE) blockExpr() :: Nil else parArgumentExprs() val argumentExpr = () => exprInParens() match { - case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a, id, rhs) + case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a)(id, rhs) case e => e } @@ -1409,8 +1409,8 @@ object Parsers { /** Adjust start of annotation or constructor to position of preceding @ or new */ def adjustStart(start: Offset)(tree: Tree): Tree = { val tree1 = tree match { - case Apply(fn, args) => cpy.Apply(tree, adjustStart(start)(fn), args) - case Select(qual, name) => cpy.Select(tree, adjustStart(start)(qual), name) + case Apply(fn, args) => cpy.Apply(tree)(adjustStart(start)(fn), args) + case Select(qual, name) => cpy.Select(tree)(adjustStart(start)(qual), name) case _ => tree } if (start < tree1.pos.start) tree1.withPos(tree1.pos.withStart(start)) @@ -1605,7 +1605,7 @@ object Parsers { imp case sel @ Select(qual, name) => val selector = atPos(sel.pos.point) { Ident(name) } - cpy.Import(sel, qual, selector :: Nil) + cpy.Import(sel)(qual, selector :: Nil) case t => accept(DOT) Import(t, Ident(nme.WILDCARD) :: Nil) @@ -1687,7 +1687,7 @@ object Parsers { } } else EmptyTree lhs match { - case (id @ Ident(name: TermName)) :: Nil => cpy.ValDef(id, mods, name, tpt, rhs) + case (id @ Ident(name: TermName)) :: Nil => cpy.ValDef(id)(mods, name, tpt, rhs) case _ => PatDef(mods, lhs, tpt, rhs) } } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index acba22afeda5..ab248a4fcd65 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -109,7 +109,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close } case tp: TypeRef => - if ((tp.symbol is TypeParam | TypeArgument) && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { + val hideType = tp.symbol is TypeParam | TypeArgument | ExpandedName + if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { tp.info match { case TypeAlias(hi) => return toText(hi) case _ => if (tp.prefix.isInstanceOf[ThisType]) return nameString(tp.symbol) @@ -486,7 +487,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def toTextFlags(sym: Symbol) = { var flags = sym.flagsUNSAFE if (flags is TypeParam) flags = flags &~ Protected - Text(flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ") + Text((flags & SourceModifierFlags).flagStrings map stringToText, " ") } override def toText(denot: Denotation): Text = denot match { diff --git a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala index 0e9f98e79496..d5ae9084065a 100644 --- a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala @@ -1,6 +1,6 @@ package dotty.tools.dotc.transform -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer, MiniPhaseTransform} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.Context import scala.collection.mutable.ListBuffer @@ -23,7 +23,7 @@ import StdNames._ import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.config.JavaPlatform -class CollectEntryPoints extends TreeTransform { +class CollectEntryPoints extends MiniPhaseTransform { /** perform context-dependant initialization */ override def init(implicit ctx: Context, info: TransformerInfo): Unit = { diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 4bef41d8f3a5..890948715794 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -9,19 +9,18 @@ import dotty.tools.dotc.core.StdNames._ * Right now it's a dummy. * Awaiting for real implemetation */ -class Constructors extends TreeTransform { +class Constructors extends MiniPhaseTransform { override def name: String = "constructors" override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { if(tree.symbol.isClassConstructor) { val claz = tree.symbol.enclosingClass.asClass val zuper = claz.info.parents.head.typeSymbol - cpy.DefDef(tree, tree.mods, tree.name, tree.tparams, tree.vparamss, tree.tpt, rhs = { + cpy.DefDef(tree)(rhs = { val parentCall = Super(This(claz), tpnme.EMPTY, true).select(zuper.primaryConstructor).appliedToNone if(tree.rhs.isEmpty) parentCall else Block(List(parentCall), tree.rhs) - }) } else tree } diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala index 30396eb83635..3635a874138a 100644 --- a/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -4,7 +4,7 @@ package transform import core._ import Names._ import Types._ -import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer} import ast.Trees.flatten import Flags._ import Contexts.Context @@ -20,7 +20,7 @@ import TypeUtils._ /** A transformer that removes repeated parameters (T*) from all types, replacing * them with Seq types. */ -class ElimRepeated extends TreeTransform with InfoTransformer { thisTransformer => +class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransformer => import ast.tpd._ override def name = "elimrepeated" diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index e5fd9d85d328..f02846735b11 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -25,7 +25,7 @@ import dotty.tools.dotc.core.Flags import ValueClasses._ import TypeUtils._ -class Erasure extends Phase with DenotTransformer { +class Erasure extends Phase with DenotTransformer { thisTransformer => override def name: String = "erasure" @@ -35,10 +35,27 @@ class Erasure extends Phase with DenotTransformer { def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}") - val owner = ref.owner - ref.copySymDenotation( - owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner, - info = transformInfo(ref.symbol, ref.info)) + if (ref.symbol eq defn.ObjectClass) { + // Aftre erasure, all former Any members are now Object members + val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info + val extendedScope = decls.cloneScope + defn.AnyClass.classInfo.decls.foreach(extendedScope.enter) + ref.copySymDenotation( + info = transformInfo(ref.symbol, + ClassInfo(pre, defn.ObjectClass, ps, extendedScope, selfInfo)) + ) + } + else { + val oldOwner = ref.owner + val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner + val oldInfo = ref.info + val newInfo = transformInfo(ref.symbol, oldInfo) + if ((oldOwner eq newOwner) && (oldInfo eq newInfo)) ref + else { + assert(!ref.is(Flags.PackageClass), s"trans $ref @ ${ctx.phase} oldOwner = $oldOwner, newOwner = $newOwner, oldInfo = $oldInfo, newInfo = $newInfo ${oldOwner eq newOwner} ${oldInfo eq newInfo}") + ref.copySymDenotation(owner = newOwner, info = newInfo) + } + } case ref => ref.derivedSingleDenotation(ref.symbol, erasure(ref.info)) } @@ -145,7 +162,7 @@ object Erasure { cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt) case _ => ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}") - mkAsInstanceOf(tree, pt) + tree.asInstance(pt) } } @@ -208,7 +225,7 @@ object Erasure { assert(sym.exists, tree.show) def select(qual: Tree, sym: Symbol): Tree = - untpd.cpy.Select(tree, qual, sym.name) withType qual.tpe.select(sym) + untpd.cpy.Select(tree)(qual, sym.name) withType qual.tpe.select(sym) def selectArrayMember(qual: Tree, erasedPre: Type) = if (erasedPre isRef defn.ObjectClass) runtimeCall(tree.name.genericArrayOp, qual :: Nil) @@ -240,20 +257,35 @@ object Erasure { fun1.tpe.widen match { case funTpe: PolyType => val args1 = args.mapconserve(typedType(_)) - untpd.cpy.TypeApply(tree, fun1, args1).withType(funTpe.instantiate(args1.tpes)) + untpd.cpy.TypeApply(tree)(fun1, args1).withType(funTpe.instantiate(args1.tpes)) case _ => fun1 } } override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree - val fun1 = typedExpr(fun, WildcardType) - fun1.tpe.widen match { - case mt: MethodType => - val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr) - untpd.cpy.Apply(tree, fun1, args1) withType mt.resultType + fun match { + case fun: Apply => + typedApply(fun, pt)(ctx.fresh.setTree(tree)) case _ => - throw new MatchError(i"tree $tree has uxpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") + def nextOuter(ctx: Context): Context = + if (ctx.outer.tree eq tree) nextOuter(ctx.outer) else ctx.outer + def contextArgs(tree: untpd.Apply)(implicit ctx: Context): List[untpd.Tree] = + ctx.tree match { + case enclApp @ Apply(enclFun, enclArgs) if enclFun eq tree => + enclArgs ++ contextArgs(enclApp)(nextOuter(ctx)) + case _ => + Nil + } + val allArgs = args ++ contextArgs(tree) + val fun1 = typedExpr(fun, WildcardType) + fun1.tpe.widen match { + case mt: MethodType => + val allArgs1 = allArgs.zipWithConserve(mt.paramTypes)(typedExpr) + untpd.cpy.Apply(tree)(fun1, allArgs1) withType mt.resultType + case _ => + throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") + } } } @@ -267,7 +299,7 @@ object Erasure { val tpt1 = // keep UnitTypes intact in result position if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos else ddef.tpt - val ddef1 = untpd.cpy.DefDef(ddef, ddef.mods, ddef.name, Nil, ddef.vparamss, tpt1, ddef.rhs) + val ddef1 = untpd.cpy.DefDef(ddef)(tparams = Nil, tpt = tpt1) super.typedDefDef(ddef1, sym) } @@ -296,9 +328,9 @@ object Erasure { val newSymbol = member.symbol(ctx) assert(oldSymbol.name(beforeCtx) == newSymbol.name, s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}") - val newOverriden = oldSymbol.denot.allOverriddenSymbols.toSet - val oldOverriden = newSymbol.allOverriddenSymbols(beforeCtx).toSet - val neededBridges = oldOverriden -- newOverriden + val newOverridden = oldSymbol.denot.allOverriddenSymbols.toSet // TODO: clarify new <-> old in a comment; symbols are swapped here + val oldOverridden = newSymbol.allOverriddenSymbols(beforeCtx).toSet // TODO: can we find a more efficient impl? newOverridden does not have to be a set! + val neededBridges = oldOverridden -- newOverridden var minimalSet = Set[Symbol]() // compute minimal set of bridges that are needed: @@ -316,9 +348,9 @@ object Erasure { ) clash match { case Some(cl) => - ctx.error(s"bridge for method ${newSymbol.show(beforeCtx)}\n" + - s"clashes with ${cl.symbol.show(beforeCtx)}\n" + - s"both have same type after erasure: ${bridge.symbol.info.show}") + ctx.error(i"bridge for method ${newSymbol.showLocated(beforeCtx)} of type ${newSymbol.info(beforeCtx)}\n" + + i"clashes with ${cl.symbol.showLocated(beforeCtx)} of type ${cl.symbol.info(beforeCtx)}\n" + + i"both have same type after erasure: ${bridge.symbol.info}") case None => minimalSet += bridge } } @@ -347,7 +379,7 @@ object Erasure { } val bridge = ctx.newSymbol(newDef.symbol.owner, parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDef.symbol.owner.coord).asTerm - bridge.entered // this should be safe, as we're executing in context of next phase + bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase ctx.debuglog(s"generating bridge from ${newDef.symbol} to $bridge") val sel: Tree = This(newDef.symbol.owner.asClass).select(newDef.symbol.termRef) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index ba5b9fab64ee..42e10b126aee 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -162,10 +162,10 @@ class ExtensionMethods extends MacroTransform with DenotTransformer with FullPar paramAccessors.foreach(_.makeNotPrivateAfter(ctx.owner, thisTransformer)) super.transform(tree) } else if (ctx.owner.isStaticOwner) { - val tree1 @ Template(constr, parents, selfType, body) = super.transform(tree) + val tree1 @ Template(_, _, _, body) = super.transform(tree) extensionDefs remove tree1.symbol.owner match { case Some(defns) if defns.nonEmpty => - cpy.Template(tree1, constr, parents, selfType, body ++ defns) + cpy.Template(tree1)(body = body ++ defns) case _ => tree1 } @@ -178,7 +178,7 @@ class ExtensionMethods extends MacroTransform with DenotTransformer with FullPar val extensionMeth = extensionMethod(origMeth) ctx.log(s"Value class $origClass spawns extension method.\n Old: ${origMeth.showDcl}\n New: ${extensionMeth.showDcl}") extensionDefs(staticClass) += fullyParameterizedDef(extensionMeth, ddef) - cpy.DefDef(tree, ddef.mods, ddef.name, ddef.tparams, ddef.vparamss, ddef.tpt, + cpy.DefDef(tree)(ddef.mods, ddef.name, ddef.tparams, ddef.vparamss, ddef.tpt, forwarder(extensionMeth, ddef)) case _ => super.transform(tree) diff --git a/src/dotty/tools/dotc/transform/Companions.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala similarity index 60% rename from src/dotty/tools/dotc/transform/Companions.scala rename to src/dotty/tools/dotc/transform/FirstTransform.scala index f6e3dbfdcba4..39791918bd37 100644 --- a/src/dotty/tools/dotc/transform/Companions.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -3,19 +3,27 @@ package transform import core._ import Names._ -import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import ast.Trees.flatten +import TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer} +import ast.Trees._ import Flags._ +import Types._ +import Constants.Constant import Contexts.Context import Symbols._ import scala.collection.mutable import DenotTransformers._ +import typer.Checking import Names.Name import NameOps._ -/** A transformer that creates companion objects for all classes except module classes. */ -class Companions extends TreeTransform with IdentityDenotTransformer { thisTransformer => +/** The first tree transform + * - ensures there are companion objects for all classes except module classes + * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree + * - checks the bounds of AppliedTypeTrees + * - stubs out native methods + */ +class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer => import ast.tpd._ override def name = "companions" @@ -61,6 +69,27 @@ class Companions extends TreeTransform with IdentityDenotTransformer { thisTrans addMissingCompanions(reorder(stats)) } + override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = + if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) { + ddef.symbol.resetFlag(Deferred) + DefDef(ddef.symbol.asTerm, + _ => ref(defn.Sys_error).withPos(ddef.pos) + .appliedTo(Literal(Constant("native method stub")))) + } else ddef + override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next))) + + override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { + case tree: Import => EmptyTree + case tree: NamedArg => tree.arg + case AppliedTypeTree(tycon, args) => + val tparams = tycon.tpe.typeSymbol.typeParams + Checking.checkBounds( + args, tparams.map(_.info.bounds), (tp, argTypes) => tp.substDealias(tparams, argTypes)) + TypeTree(tree.tpe).withPos(tree.pos) + case tree => + if (tree.isType) TypeTree(tree.tpe, tree).withPos(tree.pos) + else tree + } } diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala index 6dd66ec7592a..a8ca754deb7c 100644 --- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala +++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala @@ -40,7 +40,7 @@ import StdNames._ * using the most precise overload available * - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object. */ -class InterceptedMethods extends TreeTransform { +class InterceptedMethods extends MiniPhaseTransform { import tpd._ diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 02e5ed5a7d3b..75fc7ef2e6fb 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -8,7 +8,7 @@ import Symbols._ import Decorators._ import NameOps._ import StdNames.nme -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform} +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform} import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.{untpd, tpd} import dotty.tools.dotc.core.Constants.Constant @@ -43,7 +43,7 @@ class LazyValTranformContext { } } - class LazyValsTransform extends TreeTransform with DenotTransformer { + class LazyValsTransform extends MiniPhaseTransform with DenotTransformer { override def name: String = "LazyVals" diff --git a/src/dotty/tools/dotc/transform/Literalize.scala b/src/dotty/tools/dotc/transform/Literalize.scala index 14ce8fd05c68..03b2b9978f15 100644 --- a/src/dotty/tools/dotc/transform/Literalize.scala +++ b/src/dotty/tools/dotc/transform/Literalize.scala @@ -15,7 +15,7 @@ import ast.Trees._ * The constant types are eliminated by erasure, so we need to keep * the info about constantness in the trees. */ -class Literalize extends TreeTransform { +class Literalize extends MiniPhaseTransform { import ast.tpd._ override def name: String = "literalize" diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 0ee92bccdce3..734380661f90 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -47,7 +47,7 @@ abstract class MacroTransform extends Phase { val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) - case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat) + case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) case _ => transform(stat)(exprCtx) } flatten(trees.mapconserve(transformStat(_))) @@ -60,7 +60,7 @@ abstract class MacroTransform extends Phase { case _: PackageDef | _: MemberDef => super.transform(tree)(localCtx(tree)) case Template(constr, parents, self, body) => - cpy.Template(tree, + cpy.Template(tree)( transformSub(constr), transform(parents), transformSelf(self), @@ -71,6 +71,6 @@ abstract class MacroTransform extends Phase { } def transformSelf(vd: ValDef)(implicit ctx: Context) = - cpy.ValDef(vd, vd.mods, vd.name, transform(vd.tpt), vd.rhs) + cpy.ValDef(vd)(tpt = transform(vd.tpt)) } } diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala index 8d967cc1a7d0..554a51efbe10 100644 --- a/src/dotty/tools/dotc/transform/Nullarify.scala +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -34,7 +34,7 @@ import ast.Trees._ * expr ==> () => expr if other expr is an argument to a call-by-name parameter * */ -class Nullarify extends TreeTransform with InfoTransformer { +class Nullarify extends MiniPhaseTransform with InfoTransformer { import ast.tpd._ override def name: String = "nullarify" @@ -72,7 +72,7 @@ class Nullarify extends TreeTransform with InfoTransformer { val MethodType(_, formals) = methType(funType, tree.fun) val args1 = tree.args.zipWithConserve(formals)(transformArg) - cpy.Apply(tree, tree.fun, args1) withType nullarify(tree.tpe) + cpy.Apply(tree)(tree.fun, args1) withType nullarify(tree.tpe) } /** Insert () or .apply() if the term refers to something that was converted to a @@ -108,16 +108,15 @@ class Nullarify extends TreeTransform with InfoTransformer { insertParens(tree) override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree val vparamss1 = - if (vparamss.isEmpty) Nil :: Nil - else vparamss nestedMap { vparam => + if (tree.vparamss.isEmpty) Nil :: Nil + else tree.vparamss nestedMap { vparam => val tp = vparam.tpt.tpe val tp1 = nullarifyParam(tp) if (tp eq tp1) vparam - else cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt.withType(tp1), vparam.rhs) + else cpy.ValDef(vparam)(tpt = vparam.tpt.withType(tp1)) } - cpy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs) + cpy.DefDef(tree)(vparamss = vparamss1) } def nullarify(tp: Type)(implicit ctx: Context): Type = tp match { @@ -145,4 +144,4 @@ class Nullarify extends TreeTransform with InfoTransformer { if (defn.typeTestsOrCasts contains sym) tp else if (sym is Param) nullarifyParam(tp) else nullarify(tp) -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/OverridingPairs.scala b/src/dotty/tools/dotc/transform/OverridingPairs.scala index 5c857bc38b03..d0bc90389d38 100644 --- a/src/dotty/tools/dotc/transform/OverridingPairs.scala +++ b/src/dotty/tools/dotc/transform/OverridingPairs.scala @@ -8,7 +8,7 @@ import collection.mutable.HashMap import collection.immutable.BitSet import scala.annotation.tailrec -/** A class that yields a kind of iterator (`Cursor`), +/** A module that can produce a kind of iterator (`Cursor`), * which yields all pairs of overriding/overridden symbols * that are visible in some baseclass, unless there's a parent class * that already contains the same pairs. @@ -16,7 +16,7 @@ import scala.annotation.tailrec * Adapted from the 2.9 version of OverridingPairs. The 2.10 version is IMO * way too unwieldy to be maintained. */ -abstract class OverridingPairs { +object OverridingPairs { /** The cursor class * @param base the base class that contains the overriding pairs @@ -102,7 +102,7 @@ abstract class OverridingPairs { def hasNext: Boolean = curEntry ne null @tailrec - final def next: Unit = { + final def next(): Unit = { if (curEntry ne null) { overriding = curEntry.sym if (nextEntry ne null) { diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 40a1574832e4..fdda670b1c38 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -16,11 +16,11 @@ import ast.Trees._ /** This transform eliminates patterns. Right now it's a dummy. * Awaiting the real pattern matcher. */ -class PatternMatcher extends TreeTransform { +class PatternMatcher extends MiniPhaseTransform { import ast.tpd._ override def name: String = "patternMatcher" override def transformCaseDef(tree: CaseDef)(implicit ctx: Context, info: TransformerInfo): Tree = - cpy.CaseDef(tree, Literal(Constant("")), tree.guard, tree.body) + cpy.CaseDef(tree)(Literal(Constant("")), tree.guard, tree.body) } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala index 921aa1916508..6def41419df2 100644 --- a/src/dotty/tools/dotc/transform/Splitter.scala +++ b/src/dotty/tools/dotc/transform/Splitter.scala @@ -12,7 +12,7 @@ import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotatio * * For now, only self references are treated. */ -class Splitter extends TreeTransform { +class Splitter extends MiniPhaseTransform { import ast.tpd._ override def name: String = "splitter" diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 5720c3bd921b..510e0abf0bc0 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -215,14 +215,14 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this ctx.debuglog("Adding protected accessor for " + tree) transform(makeAccessor(sel, targs)) } - else if (goToSuper) super.transform(tree) + else if (goToSuper) super.transform(tree)(ctx.withPhase(thisTransformer.next)) else tree try tree match { // Don't transform patterns or strange trees will reach the matcher (ticket #4062) - // TODO Drop once this runs after pattern matcher + // TODO Query `ctx.mode is Pattern` instead. case CaseDef(pat, guard, body) => - cpy.CaseDef(tree, pat, transform(guard), transform(body)) + cpy.CaseDef(tree)(pat, transform(guard), transform(body)) case TypeDef(_, _, impl: Template) => val cls = sym.asClass @@ -276,7 +276,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this .installAfter(thisTransformer) val superAcc = Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias) - DefDef(sym, ensureConforms(superAcc, sym.info.widen)) + DefDef(sym, superAcc.ensureConforms(sym.info.widen)) } return forwarder(ctx.withPhase(thisTransformer.next)) } @@ -295,7 +295,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) accDefs -= currentClass ownStats ++= body1 - cpy.Template(tree, impl.constr, impl.parents, impl.self, body1) + cpy.Template(impl)(body = body1) } transformTemplate @@ -368,9 +368,9 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this } transformSelect - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - val rhs1 = if (isMethodWithExtension(sym)) withInvalidOwner(transform(rhs)) else transform(rhs) - cpy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1) + case tree@DefDef(_, _, _, _, _, rhs) => + cpy.DefDef(tree)( + rhs = if (isMethodWithExtension(sym)) withInvalidOwner(transform(rhs)) else transform(rhs)) case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, goToSuper = true) @@ -391,7 +391,9 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this case Apply(fn, args) => val MethodType(_, formals) = fn.tpe.widen - cpy.Apply(tree, transform(fn), transformArgs(formals, args)) + ctx.atPhase(thisTransformer.next) { implicit ctx => + cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) + } case _ => super.transform(tree) diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala new file mode 100644 index 000000000000..383abc186924 --- /dev/null +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -0,0 +1,174 @@ +package dotty.tools.dotc +package transform + +import core._ +import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._ +import scala.collection.{ mutable, immutable } +import Flags._ +import TreeTransforms._ +import DenotTransformers._ +import ast.Trees._ +import ast.untpd +import Decorators._ +import ValueClasses.isDerivedValueClass +import scala.collection.mutable.ListBuffer +import scala.language.postfixOps + +/** Synthetic method implementations for case classes, case objects, + * and value classes. + * Selectively added to case classes/objects, unless a non-default + * implementation already exists: + * def equals(other: Any): Boolean + * def hashCode(): Int + * def canEqual(other: Any): Boolean + * def toString(): String + * Special handling: + * protected def readResolve(): AnyRef + * + * Selectively added to value classes, unless a non-default + * implementation already exists: + * + * def equals(other: Any): Boolean + * def hashCode(): Int + */ +class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer => + import ast.tpd._ + + val name = "synthetics" + + private var valueSymbols: List[Symbol] = _ + private var caseSymbols: List[Symbol] = _ + + override def init(implicit ctx: Context, info: TransformerInfo) = { + valueSymbols = List(defn.Any_hashCode, defn.Any_equals) + caseSymbols = valueSymbols ++ List(defn.Any_toString, defn.Product_canEqual) + } + + /** The synthetic methods of the case or value class `clazz`. + */ + def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = { + val clazzType = clazz.typeRef + def accessors = clazz.decls.filter(_ is CaseAccessor) + + val symbolsToSynthesize: List[Symbol] = + if (clazz.is(Case)) caseSymbols + else if (isDerivedValueClass(clazz)) valueSymbols + else Nil + + def syntheticDefIfMissing(sym: Symbol): List[Tree] = { + val existing = sym.matchingMember(clazz.thisType) + if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil + else Nil + } + + def syntheticDef(sym: Symbol): Tree = { + val synthetic = sym.copy( + owner = clazz, + flags = sym.flags &~ Deferred | Synthetic | Override, + coord = clazz.coord).enteredAfter(thisTransformer).asTerm + + def forwardToRuntime(vrefss: List[List[Tree]]): Tree = + ref(defn.runtimeMethod("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) + + def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match { + case nme.hashCode_ => vrefss => hashCodeBody + case nme.toString_ => forwardToRuntime + case nme.equals_ => vrefss => equalsBody(vrefss.head.head) + case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head) + } + ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") + DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))) + } + + /** The class + * + * case class C(x: T, y: U) + * + * gets the equals method: + * + * def equals(that: Any): Boolean = + * (this eq that) || { + * that match { + * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y + * case _ => false + * } + * + * If C is a value class the initial `eq` test is omitted. + */ + def equalsBody(that: Tree)(implicit ctx: Context): Tree = { + val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0 + def wildcardAscription(tp: Type) = + Typed(untpd.Ident(nme.WILDCARD).withType(tp), TypeTree(tp)) + val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C) + val comparisons = accessors map (accessor => + This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor))) + val rhs = // this.x == this$0.x && this.y == x$0.y + if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _) + val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y + val defaultCase = CaseDef(wildcardAscription(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false + val matchExpr = Match(that, List(matchingCase, defaultCase)) + if (isDerivedValueClass(clazz)) matchExpr + else { + val eqCompare = This(clazz).select(defn.Object_eq).appliedTo(that.asInstance(defn.ObjectType)) + eqCompare or matchExpr + } + } + + /** The class + * + * case class C(x: T, y: T) + * + * get the hashCode method: + * + * def hashCode: Int = { + * var acc: Int = 0xcafebabe; + * acc = Statics.mix(acc, x); + * acc = Statics.mix(acc, Statics.this.anyHash(y)); + * Statics.finalizeHash(acc, 2) + * } + */ + def hashCodeBody(implicit ctx: Context): Tree = { + val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.pos) + val accDef = ValDef(acc, Literal(Constant(0xcafebabe))) + val mixes = for (accessor <- accessors.toList) yield + Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor))) + val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size))) + Block(accDef :: mixes, finish) + } + + /** The hashCode implementation for given symbol `sym`. */ + def hashImpl(sym: Symbol)(implicit ctx: Context): Tree = { + val d = defn + import d._ + sym.info.finalResultType.typeSymbol match { + case UnitClass | NullClass => Literal(Constant(0)) + case BooleanClass => If(ref(sym), Literal(Constant(1231)), Literal(Constant(1237))) + case IntClass => ref(sym) + case ShortClass | ByteClass | CharClass => ref(sym).select(nme.toInt) + case LongClass => ref(staticsMethod("longHash")).appliedTo(ref(sym)) + case DoubleClass => ref(staticsMethod("doubleHash")).appliedTo(ref(sym)) + case FloatClass => ref(staticsMethod("floatHash")).appliedTo(ref(sym)) + case _ => ref(staticsMethod("anyHash")).appliedTo(ref(sym)) + } + } + + /** The class + * + * case class C(...) + * + * gets the canEqual method + * + * def canEqual(that: Any) = that.isInstanceOf[C] + */ + def canEqualBody(that: Tree): Tree = that.isInstance(clazzType) + + symbolsToSynthesize flatMap syntheticDefIfMissing + } + + override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) = + if (ctx.owner.is(Case) || isDerivedValueClass(ctx.owner)) + cpy.Template(impl)( + body = impl.body ++ syntheticMethods(ctx.owner.asClass)(ctx.withPhase(thisTransformer.next))) + else + impl +} diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index d3bec6f902c2..6aabdb20e3f0 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core._ import dotty.tools.dotc.transform.TailRec._ -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform} +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform} /** * A Tail Rec Transformer @@ -62,7 +62,7 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform * self recursive functions, that's why it's renamed to tailrec *

*/ -class TailRec extends TreeTransform with DenotTransformer with FullParameterization { +class TailRec extends MiniPhaseTransform with DenotTransformer with FullParameterization { import dotty.tools.dotc.ast.tpd._ @@ -84,7 +84,7 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat case dd@DefDef(mods, name, tparams, vparamss0, tpt, rhs0) if (dd.symbol.isEffectivelyFinal) && !((dd.symbol is Flags.Accessor) || (rhs0 eq EmptyTree) || (dd.symbol is Flags.Label)) => val mandatory = dd.symbol.hasAnnotation(defn.TailrecAnnotationClass) - cpy.DefDef(tree, mods, name, tparams, vparamss0, tpt, rhs = { + cpy.DefDef(dd)(rhs = { val origMeth = tree.symbol val label = mkLabel(dd.symbol) @@ -105,8 +105,7 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat } if (rewrote) { - val dummyDefDef = cpy.DefDef(tree, dd.mods, dd.name, dd.tparams, dd.vparamss, dd.tpt, - rhsSemiTransformed) + val dummyDefDef = cpy.DefDef(tree)(rhs = rhsSemiTransformed) val res = fullyParameterizedDef(label, dummyDefDef) val call = forwarder(label, dd) Block(List(res), call) @@ -127,7 +126,7 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat } - class TailRecElimination(method: Symbol, enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol) extends tpd.RetypingTreeMap { + class TailRecElimination(method: Symbol, enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol) extends tpd.TreeMap { import dotty.tools.dotc.ast.tpd._ @@ -233,21 +232,21 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat def transformHandlers(t: Tree): Tree = { t match { case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) => - val newDef = cpy.DefDef(d, d.mods, d.name, d.tparams, d.vparamss, d.tpt, transform(d.rhs)) + val newDef = cpy.DefDef(d)(rhs = transform(d.rhs)) Block(List(newDef), cl) case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ??? } } if (tree.finalizer eq EmptyTree) { // SI-1672 Catches are in tail position when there is no finalizer - tpd.cpy.Try(tree, + tpd.cpy.Try(tree)( noTailTransform(tree.expr), transformHandlers(tree.handler), EmptyTree ) } else { - tpd.cpy.Try(tree, + tpd.cpy.Try(tree)( noTailTransform(tree.expr), noTailTransform(tree.handler), noTailTransform(tree.finalizer) @@ -258,37 +257,32 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat val res: Tree = tree match { case tree@Block(stats, expr) => - val tree1 = tpd.cpy.Block(tree, + tpd.cpy.Block(tree)( noTailTransforms(stats), transform(expr) ) - propagateType(tree, tree1) - case tree@CaseDef(pat, guard, body) => - val tree1 = cpy.CaseDef(tree, pat, guard, transform(body)) - propagateType(tree, tree1) + case tree@CaseDef(_, _, body) => + cpy.CaseDef(tree)(body = transform(body)) case tree@If(cond, thenp, elsep) => - val tree1 = tpd.cpy.If(tree, + tpd.cpy.If(tree)( noTailTransform(cond), transform(thenp), transform(elsep) ) - propagateType(tree, tree1) case tree@Match(selector, cases) => - val tree1 = tpd.cpy.Match(tree, + tpd.cpy.Match(tree)( noTailTransform(selector), transformSub(cases) ) - propagateType(tree, tree1) case tree: Try => - val tree1 = rewriteTry(tree) - propagateType(tree, tree1) + rewriteTry(tree) - case Apply(fun, args) if fun.symbol == defn.Boolean_or || fun.symbol == defn.Boolean_and => - tpd.cpy.Apply(tree, fun, transform(args)) + case Apply(fun, args) if fun.symbol == defn.Boolean_|| || fun.symbol == defn.Boolean_&& => + tpd.cpy.Apply(tree)(fun, transform(args)) case Apply(fun, args) => rewriteApply(tree, fun.symbol) @@ -300,7 +294,7 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat case tree: Select => val sym = tree.symbol if (sym == method && ctx.tailPos) rewriteApply(tree, sym) - else propagateType(tree, tpd.cpy.Select(tree, noTailTransform(tree.qualifier), tree.name)) + else tpd.cpy.Select(tree)(noTailTransform(tree.qualifier), tree.name) case ValDef(_, _, _, _) | EmptyTree | Super(_, _) | This(_) | Literal(_) | TypeTree(_) | DefDef(_, _, _, _, _, _) | TypeDef(_, _, _) => diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 8e7c4f6d01f3..22f785163e7e 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -6,8 +6,10 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.Flags.PackageVal +import dotty.tools.dotc.typer.Mode import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.util.DotClass import scala.annotation.tailrec import config.Printers.transforms @@ -15,49 +17,49 @@ object TreeTransforms { import tpd._ /** The base class of tree transforms. For each kind of tree K, there are - * two methods which can be overridden: - * - * prepareForK // return a new TreeTransform which gets applied to the K - * // node and its children - * transformK // transform node of type K - * - * If a transform does not need to visit a node or any of its children, it - * signals this fact by returning a NoTransform from a prepare method. - * - * If all transforms in a group are NoTransforms, the tree is no longer traversed. - * - * - * Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of - * about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes - * over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have - * about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for - * the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with - * 0.2sec, or roughly 600M processor cycles. - * - * Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking. - * Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100 - * micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison, - * scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits - * of a node by a transformation. Each visit has a budget of 20 processor cycles. - * - * A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node. - * This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations - * touch these. By contrast the amount of work for generating new transformations should be negligible. - * - * So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new - * transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and - * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable - * for achieving this goal, but there can be no wasted cycles anywhere. - */ - abstract class TreeTransform extends Phase { + * two methods which can be overridden: + * + * prepareForK // return a new TreeTransform which gets applied to the K + * // node and its children + * transformK // transform node of type K + * + * If a transform does not need to visit a node or any of its children, it + * signals this fact by returning a NoTransform from a prepare method. + * + * If all transforms in a group are NoTransforms, the tree is no longer traversed. + * + * + * Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of + * about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes + * over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have + * about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for + * the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with + * 0.2sec, or roughly 600M processor cycles. + * + * Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking. + * Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100 + * micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison, + * scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits + * of a node by a transformation. Each visit has a budget of 20 processor cycles. + * + * A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node. + * This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations + * touch these. By contrast the amount of work for generating new transformations should be negligible. + * + * So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new + * transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and + * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable + * for achieving this goal, but there can be no wasted cycles anywhere. + */ + abstract class TreeTransform extends DotClass { + + def phase: MiniPhase + + def treeTransformPhase: Phase = phase /** id of this treeTransform in group */ var idx: Int = _ - /** List of names of phases that should have finished their processing of all compilation units - * before this phase starts */ - def runsAfterGroupsOf: Set[String] = Set.empty - def prepareForIdent(tree: Ident)(implicit ctx: Context) = this def prepareForSelect(tree: Select)(implicit ctx: Context) = this def prepareForThis(tree: This)(implicit ctx: Context) = this @@ -121,6 +123,7 @@ object TreeTransforms { def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformPackageDef(tree: PackageDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees + def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree /** Transform tree using all transforms of current group (including this one) */ def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0) @@ -132,11 +135,21 @@ object TreeTransforms { def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, idx + 1) /** perform context-dependant initialization */ - def init(implicit ctx:Context, info: TransformerInfo): Unit = {} + def init(implicit ctx: Context, info: TransformerInfo): Unit = {} + } + + /** A phase that defines a TreeTransform to be used in a group */ + trait MiniPhase extends Phase { thisPhase => + def treeTransform: TreeTransform + + /** List of names of phases that should have finished their processing of all compilation units + * before this phase starts + */ + def runsAfterGroupsOf: Set[String] = Set.empty protected def mkTreeTransformer = new TreeTransformer { - override def name: String = TreeTransform.this.name - override def transformations = Array(TreeTransform.this) + override def name: String = thisPhase.name + override def transformations = Array(treeTransform) } override def run(implicit ctx: Context): Unit = { @@ -144,24 +157,30 @@ object TreeTransforms { } } + /** A mini phase that is its own tree transform */ + abstract class MiniPhaseTransform extends TreeTransform with MiniPhase { + def treeTransform = this + def phase = this + } + val NoTransform = new TreeTransform { - override def name: String = "NoTransform" + def phase = unsupported("phase") idx = -1 } - class Separator extends TreeTransform { - override def name: String = "Separator" +/* disabled; not needed anywhere + class Separator extends TreeTransform(phaseId) { + //override def name: String = "Separator" idx = -1 } - +*/ type Mutator[T] = (TreeTransform, T, Context) => TreeTransform - class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group:TreeTransformer) + class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group: TreeTransformer) - /** - * This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX - * Thanks to Martin for this idea - * @see NXTransformations.index for format of plan + /** This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX + * Thanks to Martin for this idea + * @see NXTransformations.index for format of plan */ class NXTransformations { @@ -170,11 +189,11 @@ object TreeTransforms { else hasRedefinedMethod(cls.getSuperclass, name) /** Create an index array `next` of size one larger than teh size of `transforms` such that - * for each index i, `next(i)` is the smallest index j such that - * - * i <= j - * j == transforms.length || transform(j) defines a non-default method with given `name` - */ + * for each index i, `next(i)` is the smallest index j such that + * + * i <= j + * j == transforms.length || transform(j) defines a non-default method with given `name` + */ private def index(transformations: Array[TreeTransform], name: String): Array[Int] = { val len = transformations.length val next = new Array[Int](len + 1) @@ -281,6 +300,7 @@ object TreeTransforms { nxTransTemplate = index(transformations, "transformTemplate") nxTransPackageDef = index(transformations, "transformPackageDef") nxTransStats = index(transformations, "transformStats") + nxTransOther = index(transformations, "transformOther") } def this(prev: NXTransformations, changedTansformation: TreeTransform, transformationIndex: Int, reuse: Boolean = false) = { @@ -349,12 +369,12 @@ object TreeTransforms { nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTansformation, transformationIndex, "transformTemplate", copy) nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTansformation, transformationIndex, "transformPackageDef", copy) nxTransStats = indexUpdate(prev.nxTransStats, changedTansformation, transformationIndex, "transformStats", copy) + nxTransOther = indexUpdate(prev.nxTransOther, changedTansformation, transformationIndex, "transformOther", copy) } - /** - * Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations - * for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype. - * If no nontrivial transformation are left stored value is greater than transformers.size + /** Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations + * for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype. + * If no nontrivial transformation are left stored value is greater than transformers.size */ var nxPrepIdent: Array[Int] = _ var nxPrepSelect: Array[Int] = _ @@ -419,6 +439,7 @@ object TreeTransforms { var nxTransTemplate: Array[Int] = _ var nxTransPackageDef: Array[Int] = _ var nxTransStats: Array[Int] = _ + var nxTransOther: Array[Int] = _ } /** A group of tree transforms that are applied in sequence during the same phase */ @@ -442,7 +463,7 @@ object TreeTransforms { var allDone = i < l while (i < l) { val oldTransform = result(i) - val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform)) + val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.treeTransformPhase)) allDone = allDone && (newTransform eq NoTransform) if (!(oldTransform eq newTransform)) { if (!transformersCopied) result = result.clone() @@ -490,12 +511,12 @@ object TreeTransforms { val prepForTypeDef: Mutator[TypeDef] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx) val prepForTemplate: Mutator[Template] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx) val prepForPackageDef: Mutator[PackageDef] = (trans, tree, ctx) => trans.prepareForPackageDef(tree)(ctx) - val prepForStats: Mutator[List[Tree]]= (trans, trees, ctx) => trans.prepareForStats(trees)(ctx) + val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx) def transform(t: Tree)(implicit ctx: Context): Tree = { val initialTransformations = transformations val info = new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations), this) - initialTransformations.zipWithIndex.foreach{ + initialTransformations.zipWithIndex.foreach { case (transform, id) => transform.idx = id transform.init(ctx, info) @@ -507,7 +528,7 @@ object TreeTransforms { final private[TreeTransforms] def goIdent(tree: Ident, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformIdent(tree)(ctx.withPhase(trans), info) match { + trans.transformIdent(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -518,7 +539,7 @@ object TreeTransforms { final private[TreeTransforms] def goSelect(tree: Select, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformSelect(tree)(ctx.withPhase(trans), info) match { + trans.transformSelect(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -529,7 +550,7 @@ object TreeTransforms { final private[TreeTransforms] def goThis(tree: This, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformThis(tree)(ctx.withPhase(trans), info) match { + trans.transformThis(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: This => goThis(t, info.nx.nxTransThis(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -540,7 +561,7 @@ object TreeTransforms { final private[TreeTransforms] def goSuper(tree: Super, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformSuper(tree)(ctx.withPhase(trans), info) match { + trans.transformSuper(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -551,7 +572,7 @@ object TreeTransforms { final private[TreeTransforms] def goApply(tree: Apply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformApply(tree)(ctx.withPhase(trans), info) match { + trans.transformApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -562,7 +583,7 @@ object TreeTransforms { final private[TreeTransforms] def goTypeApply(tree: TypeApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTypeApply(tree)(ctx.withPhase(trans), info) match { + trans.transformTypeApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -573,7 +594,7 @@ object TreeTransforms { final private[TreeTransforms] def goNew(tree: New, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformNew(tree)(ctx.withPhase(trans), info) match { + trans.transformNew(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: New => goNew(t, info.nx.nxTransNew(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -584,7 +605,7 @@ object TreeTransforms { final private[TreeTransforms] def goPair(tree: Pair, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformPair(tree)(ctx.withPhase(trans), info) match { + trans.transformPair(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Pair => goPair(t, info.nx.nxTransPair(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -595,7 +616,7 @@ object TreeTransforms { final private[TreeTransforms] def goTyped(tree: Typed, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTyped(tree)(ctx.withPhase(trans), info) match { + trans.transformTyped(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -606,7 +627,7 @@ object TreeTransforms { final private[TreeTransforms] def goAssign(tree: Assign, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformAssign(tree)(ctx.withPhase(trans), info) match { + trans.transformAssign(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -617,7 +638,7 @@ object TreeTransforms { final private[TreeTransforms] def goLiteral(tree: Literal, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformLiteral(tree)(ctx.withPhase(trans), info) match { + trans.transformLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -628,7 +649,7 @@ object TreeTransforms { final private[TreeTransforms] def goBlock(tree: Block, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformBlock(tree)(ctx.withPhase(trans), info) match { + trans.transformBlock(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -639,7 +660,7 @@ object TreeTransforms { final private[TreeTransforms] def goIf(tree: If, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformIf(tree)(ctx.withPhase(trans), info) match { + trans.transformIf(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: If => goIf(t, info.nx.nxTransIf(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -650,7 +671,7 @@ object TreeTransforms { final private[TreeTransforms] def goClosure(tree: Closure, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformClosure(tree)(ctx.withPhase(trans), info) match { + trans.transformClosure(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -661,7 +682,7 @@ object TreeTransforms { final private[TreeTransforms] def goMatch(tree: Match, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformMatch(tree)(ctx.withPhase(trans), info) match { + trans.transformMatch(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -672,7 +693,7 @@ object TreeTransforms { final private[TreeTransforms] def goCaseDef(tree: CaseDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformCaseDef(tree)(ctx.withPhase(trans), info) match { + trans.transformCaseDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -683,7 +704,7 @@ object TreeTransforms { final private[TreeTransforms] def goReturn(tree: Return, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformReturn(tree)(ctx.withPhase(trans), info) match { + trans.transformReturn(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -694,7 +715,7 @@ object TreeTransforms { final private[TreeTransforms] def goTry(tree: Try, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTry(tree)(ctx.withPhase(trans), info) match { + trans.transformTry(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Try => goTry(t, info.nx.nxTransTry(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -705,7 +726,7 @@ object TreeTransforms { final private[TreeTransforms] def goThrow(tree: Throw, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformThrow(tree)(ctx.withPhase(trans), info) match { + trans.transformThrow(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Throw => goThrow(t, info.nx.nxTransThrow(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -716,7 +737,7 @@ object TreeTransforms { final private[TreeTransforms] def goSeqLiteral(tree: SeqLiteral, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformSeqLiteral(tree)(ctx.withPhase(trans), info) match { + trans.transformSeqLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -727,7 +748,7 @@ object TreeTransforms { final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTypeTree(tree)(ctx.withPhase(trans), info) match { + trans.transformTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -738,7 +759,7 @@ object TreeTransforms { final private[TreeTransforms] def goSelectFromTypeTree(tree: SelectFromTypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformSelectFromTypeTree(tree)(ctx.withPhase(trans), info) match { + trans.transformSelectFromTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: SelectFromTypeTree => goSelectFromTypeTree(t, info.nx.nxTransSelectFromTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -749,7 +770,7 @@ object TreeTransforms { final private[TreeTransforms] def goBind(tree: Bind, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformBind(tree)(ctx.withPhase(trans), info) match { + trans.transformBind(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -760,7 +781,7 @@ object TreeTransforms { final private[TreeTransforms] def goAlternative(tree: Alternative, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformAlternative(tree)(ctx.withPhase(trans), info) match { + trans.transformAlternative(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -771,7 +792,7 @@ object TreeTransforms { final private[TreeTransforms] def goValDef(tree: ValDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformValDef(tree)(ctx.withPhase(trans), info) match { + trans.transformValDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -782,7 +803,7 @@ object TreeTransforms { final private[TreeTransforms] def goDefDef(tree: DefDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformDefDef(tree)(ctx.withPhase(trans), info) match { + trans.transformDefDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -793,7 +814,7 @@ object TreeTransforms { final private[TreeTransforms] def goUnApply(tree: UnApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformUnApply(tree)(ctx.withPhase(trans), info) match { + trans.transformUnApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -804,7 +825,7 @@ object TreeTransforms { final private[TreeTransforms] def goTypeDef(tree: TypeDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTypeDef(tree)(ctx.withPhase(trans), info) match { + trans.transformTypeDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -815,7 +836,7 @@ object TreeTransforms { final private[TreeTransforms] def goTemplate(tree: Template, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformTemplate(tree)(ctx.withPhase(trans), info) match { + trans.transformTemplate(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -826,13 +847,21 @@ object TreeTransforms { final private[TreeTransforms] def goPackageDef(tree: PackageDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { if (cur < info.transformers.length) { val trans = info.transformers(cur) - trans.transformPackageDef(tree)(ctx.withPhase(trans), info) match { + trans.transformPackageDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1)) case t => transformSingle(t, cur + 1) } } else tree } + final private[TreeTransforms] def goOther(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (cur < info.transformers.length) { + val trans = info.transformers(cur) + val t = trans.transformOther(tree)(ctx.withPhase(trans.treeTransformPhase), info) + transformSingle(t, cur + 1) + } else tree + } + final private[TreeTransforms] def goNamed(tree: NameTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { case tree: Ident => goIdent(tree, info.nx.nxTransIdent(cur)) @@ -871,8 +900,8 @@ object TreeTransforms { case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur)) case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur)) - case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur)) - case tree => tree + case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) + case tree => goOther(tree, info.nx.nxTransOther(cur)) } final private[TreeTransforms] def transformSingle(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = @@ -903,21 +932,21 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) - goSelect(cpy.Select(tree, qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) + goSelect(cpy.Select(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) } case tree: SelectFromTypeTree => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelectFromTypeTree, info.nx.nxPrepSelectFromTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) - goSelectFromTypeTree(cpy.SelectFromTypeTree(tree, qual, tree.name), mutatedInfo.nx.nxTransSelectFromTypeTree(cur)) + goSelectFromTypeTree(cpy.SelectFromTypeTree(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelectFromTypeTree(cur)) } case tree: Bind => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur) if (mutatedInfo eq null) tree else { val body = transform(tree.body, mutatedInfo, mutatedInfo.nx.nxTransBind(cur)) - goBind(cpy.Bind(tree, tree.name, body), cur) + goBind(cpy.Bind(tree)(tree.name, body), cur) } case tree: ValDef if !tree.isEmpty => // As a result of discussing with Martin: emptyValDefs shouldn't be copied // NAME implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur) @@ -926,7 +955,7 @@ object TreeTransforms { val nestedCtx = if (tree.symbol.exists) localContext(tree.symbol) else ctx val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goValDef(cpy.ValDef(tree, tree.mods, tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) + goValDef(cpy.ValDef(tree)(tree.mods, tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) } case tree: DefDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur) @@ -937,14 +966,14 @@ object TreeTransforms { val vparams = tree.vparamss.mapConserve(x => transformSubTrees(x, mutatedInfo, cur)(nestedCtx)) val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goDefDef(cpy.DefDef(tree, tree.mods, tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) + goDefDef(cpy.DefDef(tree)(tree.mods, tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) } case tree: TypeDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur) if (mutatedInfo eq null) tree else { val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol)) - goTypeDef(cpy.TypeDef(tree, tree.mods, tree.name, rhs, tree.tparams), mutatedInfo.nx.nxTransTypeDef(cur)) + goTypeDef(cpy.TypeDef(tree)(tree.mods, tree.name, rhs, tree.tparams), mutatedInfo.nx.nxTransTypeDef(cur)) } case _ => tree @@ -961,7 +990,7 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val qual = transform(tree.qual, mutatedInfo, cur) - goSuper(cpy.Super(tree, qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) + goSuper(cpy.Super(tree)(qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) } case tree: Apply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur) @@ -969,7 +998,7 @@ object TreeTransforms { else { val fun = transform(tree.fun, mutatedInfo, cur) val args = transformSubTrees(tree.args, mutatedInfo, cur) - goApply(cpy.Apply(tree, fun, args), mutatedInfo.nx.nxTransApply(cur)) + goApply(cpy.Apply(tree)(fun, args), mutatedInfo.nx.nxTransApply(cur)) } case tree: TypeApply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur) @@ -977,7 +1006,7 @@ object TreeTransforms { else { val fun = transform(tree.fun, mutatedInfo, cur) val args = transformTrees(tree.args, mutatedInfo, cur) - goTypeApply(cpy.TypeApply(tree, fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) + goTypeApply(cpy.TypeApply(tree)(fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) } case tree: Literal => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur) @@ -988,7 +1017,7 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val tpt = transform(tree.tpt, mutatedInfo, cur) - goNew(cpy.New(tree, tpt), mutatedInfo.nx.nxTransNew(cur)) + goNew(cpy.New(tree)(tpt), mutatedInfo.nx.nxTransNew(cur)) } case tree: Pair => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPair, info.nx.nxPrepPair, tree, cur) @@ -996,7 +1025,7 @@ object TreeTransforms { else { val left = transform(tree.left, mutatedInfo, cur) val right = transform(tree.right, mutatedInfo, cur) - goPair(cpy.Pair(tree, left, right), mutatedInfo.nx.nxTransPair(cur)) + goPair(cpy.Pair(tree)(left, right), mutatedInfo.nx.nxTransPair(cur)) } case tree: Typed => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur) @@ -1004,7 +1033,7 @@ object TreeTransforms { else { val expr = transform(tree.expr, mutatedInfo, cur) val tpt = transform(tree.tpt, mutatedInfo, cur) - goTyped(cpy.Typed(tree, expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) + goTyped(cpy.Typed(tree)(expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) } case tree: Assign => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur) @@ -1012,7 +1041,7 @@ object TreeTransforms { else { val lhs = transform(tree.lhs, mutatedInfo, cur) val rhs = transform(tree.rhs, mutatedInfo, cur) - goAssign(cpy.Assign(tree, lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) + goAssign(cpy.Assign(tree)(lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) } case tree: Block => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur) @@ -1020,7 +1049,7 @@ object TreeTransforms { else { val stats = transformStats(tree.stats, ctx.owner, mutatedInfo, cur) val expr = transform(tree.expr, mutatedInfo, cur) - goBlock(cpy.Block(tree, stats, expr), mutatedInfo.nx.nxTransBlock(cur)) + goBlock(cpy.Block(tree)(stats, expr), mutatedInfo.nx.nxTransBlock(cur)) } case tree: If => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur) @@ -1029,7 +1058,7 @@ object TreeTransforms { val cond = transform(tree.cond, mutatedInfo, cur) val thenp = transform(tree.thenp, mutatedInfo, cur) val elsep = transform(tree.elsep, mutatedInfo, cur) - goIf(cpy.If(tree, cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) + goIf(cpy.If(tree)(cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) } case tree: Closure => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur) @@ -1038,7 +1067,7 @@ object TreeTransforms { val env = transformTrees(tree.env, mutatedInfo, cur) val meth = transform(tree.meth, mutatedInfo, cur) val tpt = transform(tree.tpt, mutatedInfo, cur) - goClosure(cpy.Closure(tree, env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) + goClosure(cpy.Closure(tree)(env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) } case tree: Match => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur) @@ -1046,16 +1075,16 @@ object TreeTransforms { else { val selector = transform(tree.selector, mutatedInfo, cur) val cases = transformSubTrees(tree.cases, mutatedInfo, cur) - goMatch(cpy.Match(tree, selector, cases), mutatedInfo.nx.nxTransMatch(cur)) + goMatch(cpy.Match(tree)(selector, cases), mutatedInfo.nx.nxTransMatch(cur)) } case tree: CaseDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur) if (mutatedInfo eq null) tree else { - val pat = transform(tree.pat, mutatedInfo, cur) + val pat = transform(tree.pat, mutatedInfo, cur)(ctx.withMode(Mode.Pattern)) val guard = transform(tree.guard, mutatedInfo, cur) val body = transform(tree.body, mutatedInfo, cur) - goCaseDef(cpy.CaseDef(tree, pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur)) + goCaseDef(cpy.CaseDef(tree)(pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur)) } case tree: Return => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur) @@ -1063,7 +1092,7 @@ object TreeTransforms { else { val expr = transform(tree.expr, mutatedInfo, cur) val from = transform(tree.from, mutatedInfo, cur) - goReturn(cpy.Return(tree, expr, from), mutatedInfo.nx.nxTransReturn(cur)) + goReturn(cpy.Return(tree)(expr, from), mutatedInfo.nx.nxTransReturn(cur)) } case tree: Try => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur) @@ -1072,35 +1101,35 @@ object TreeTransforms { val block = transform(tree.expr, mutatedInfo, cur) val handler = transform(tree.handler, mutatedInfo, cur) val finalizer = transform(tree.finalizer, mutatedInfo, cur) - goTry(cpy.Try(tree, block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur)) + goTry(cpy.Try(tree)(block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur)) } case tree: Throw => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThrow, info.nx.nxPrepThrow, tree, cur) if (mutatedInfo eq null) tree else { val expr = transform(tree.expr, mutatedInfo, cur) - goThrow(cpy.Throw(tree, expr), mutatedInfo.nx.nxTransThrow(cur)) + goThrow(cpy.Throw(tree)(expr), mutatedInfo.nx.nxTransThrow(cur)) } case tree: SeqLiteral => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur) if (mutatedInfo eq null) tree else { val elems = transformTrees(tree.elems, mutatedInfo, cur) - goSeqLiteral(cpy.SeqLiteral(tree, elems), mutatedInfo.nx.nxTransLiteral(cur)) + goSeqLiteral(cpy.SeqLiteral(tree)(elems), mutatedInfo.nx.nxTransLiteral(cur)) } case tree: TypeTree => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val original = transform(tree.original, mutatedInfo, cur) - goTypeTree(cpy.TypeTree(tree, original), mutatedInfo.nx.nxTransTypeTree(cur)) + goTypeTree(cpy.TypeTree(tree)(original), mutatedInfo.nx.nxTransTypeTree(cur)) } case tree: Alternative => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) if (mutatedInfo eq null) tree else { val trees = transformTrees(tree.trees, mutatedInfo, cur) - goAlternative(cpy.Alternative(tree, trees), mutatedInfo.nx.nxTransAlternative(cur)) + goAlternative(cpy.Alternative(tree)(trees), mutatedInfo.nx.nxTransAlternative(cur)) } case tree: UnApply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur) @@ -1109,7 +1138,7 @@ object TreeTransforms { val fun = transform(tree.fun, mutatedInfo, cur) val implicits = transformTrees(tree.implicits, mutatedInfo, cur) val patterns = transformTrees(tree.patterns, mutatedInfo, cur) - goUnApply(cpy.UnApply(tree, fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) + goUnApply(cpy.UnApply(tree)(fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) } case tree: Template => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur) @@ -1119,7 +1148,7 @@ object TreeTransforms { val parents = transformTrees(tree.parents, mutatedInfo, cur) val self = transformSub(tree.self, mutatedInfo, cur) val body = transformStats(tree.body, tree.symbol, mutatedInfo, cur) - goTemplate(cpy.Template(tree, constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) + goTemplate(cpy.Template(tree)(constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) } case tree: PackageDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur) @@ -1128,21 +1157,19 @@ object TreeTransforms { val nestedCtx = localContext(tree.symbol) val pid = transformSub(tree.pid, mutatedInfo, cur) val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) - goPackageDef(cpy.PackageDef(tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) + goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } - case tree: Import => EmptyTree - case tree: NamedArg => transform(tree.arg, info, cur) - case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur)) + case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => - if (tree.isType) transform(TypeTree(tree.tpe).withPos(tree.pos), info, cur) - else tree + implicit val originalInfo: TransformerInfo = info + goOther(tree, info.nx.nxTransOther(cur)) } def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) { if (cur < info.transformers.length) { // if cur > 0 then some of the symbols can be created by already performed transformations - // this means that their denotations could not exists in previous periods - val pctx = ctx.withPhase(info.transformers(cur)) + // this means that their denotations could not exists in previous period + val pctx = ctx.withPhase(info.transformers(cur).treeTransformPhase) tree match { //split one big match into 2 smaller ones case tree: NameTree => transformNamed(tree, info, cur)(pctx) @@ -1164,9 +1191,9 @@ object TreeTransforms { val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current) val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { - case _: Import | _: DefTree => transform(stat, info, current) - case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat) - case _ => transform(stat, info, current)(exprCtx) + case _: Import | _: DefTree => transform(stat, newInfo, current) + case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) + case _ => transform(stat, newInfo, current)(exprCtx) } val newTrees = flatten(trees.mapconserve(transformStat)) goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo) diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index b209f7647ecf..93acc8e86fb5 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -21,7 +21,7 @@ import Erasure.Boxing.box * - have a reference type as receiver * - can be translated directly to machine instructions */ -class TypeTestsCasts extends TreeTransform { +class TypeTestsCasts extends MiniPhaseTransform { import ast.tpd._ override def name: String = "typeTestsCasts" @@ -34,7 +34,7 @@ class TypeTestsCasts extends TreeTransform { def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass def derivedTree(qual1: Tree, sym: Symbol, tp: Type) = - cpy.TypeApply(tree, qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) + cpy.TypeApply(tree)(qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) def qualCls = qual.tpe.classSymbol @@ -59,7 +59,7 @@ class TypeTestsCasts extends TreeTransform { case _ => erased2 match { case Literal(Constant(true)) => erased1 - case _ => mkAnd(erased1, erased2) + case _ => erased1 and erased2 } } } @@ -68,7 +68,7 @@ class TypeTestsCasts extends TreeTransform { runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil) if (ndims == 1) isArrayTest(qual) else evalOnce(qual) { qual1 => - mkAnd(derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe), isArrayTest(qual1)) + derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1) } case _ => derivedTree(expr, defn.Any_isInstanceOf, argType) diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala deleted file mode 100644 index ccfaaa0dcd96..000000000000 --- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala +++ /dev/null @@ -1,46 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Denotations._ -import core.SymDenotations._ -import core.Contexts._ -import core.Types._ -import core.Symbols._ -import ast.Trees._ -import ast.tpd.{Apply, Tree, cpy} - -class UncurryTreeTransform extends TreeTransform with InfoTransformer { - - override def name: String = "uncurry" - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show}", show = true) { - tree.fun match { - case Apply(fn, args) => - def showType(implicit ctx: Context) = - ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") - showType - ctx.atNextPhase(showType(_)) - showType - cpy.Apply(tree, fn, args ++ tree.args) - case _ => tree - }} - - def uncurry(tp: Type)(implicit ctx: Context): Type = tp match { - case tp @ MethodType(pnames1, ptypes1) => - tp.resultType match { - case rt @ MethodType(pnames2, ptypes2) => - tp.derivedMethodType(pnames1 ++ pnames2, ptypes1 ++ ptypes2, rt.resultType) - case _ => - tp - } - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, uncurry(tp.resultType)) - case _ => - tp - } - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - uncurry(tp) -} \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 242985b57ffc..86327d2fc942 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -404,7 +404,7 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val app0 = cpy.Apply(app, normalizedFun, typedArgs) + val app0 = cpy.Apply(app)(normalizedFun, typedArgs) val app1 = if (!success) app0.withType(ErrorType) else { @@ -433,8 +433,12 @@ trait Applications extends Compatibility { self: Typer => /** Subclass of Application for type checking an Apply node with typed arguments. */ class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context) - extends TypedApply(app, fun, methRef, args, resultType) { - def typedArg(arg: Tree, formal: Type): TypedArg = arg + extends TypedApply[Type](app, fun, methRef, args, resultType) { + // Dotty deviation: Dotc infers Untyped for the supercall. This seems to be according to the rules + // (of both Scala and Dotty). Untyped is legal, and a subtype of Typed, whereas TypeApply + // is invariant in the type parameter, so the minimal type should be inferred. But then typedArg does + // not match the abstract method in Application and an abstract class error results. + def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg def treeToArg(arg: Tree): Tree = arg } @@ -466,7 +470,7 @@ trait Applications extends Compatibility { self: Typer => failedState.commit() failedVal } else typedApply( - cpy.Apply(tree, untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) + cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) } case _ => fun1.tpe match { @@ -523,7 +527,7 @@ trait Applications extends Compatibility { self: Typer => checkBounds(typedArgs, pt, tree.pos) case _ => } - assignType(cpy.TypeApply(tree, typedFn, typedArgs), typedFn, typedArgs) + assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) } def typedUnApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = track("typedUnApply") { @@ -555,18 +559,32 @@ trait Applications extends Compatibility { self: Typer => /** A typed qual.unappy or qual.unappySeq tree, if this typechecks. * Otherwise fallBack with (maltyped) qual.unapply as argument + * Note: requires special handling for overloaded occurrences of + * unapply or unapplySeq. We first try to find a non-overloaded + * method which matches any type. If that fails, we try to find an + * overloaded variant which matches one of the argument types. + * In fact, overloaded unapply's are problematic because a non- + * overloaded unapply does *not* need to be applicable to its argument + * whereas overloaded variants need to have a conforming variant. */ def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = { - val unappProto = new UnapplyFunProto(this) - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto) - } { - (sel, _) => - tryEither { - implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped - } { - (_, _) => fallBack(sel) - } + val genericProto = new UnapplyFunProto(WildcardType, this) + def specificProto = new UnapplyFunProto(pt, this) + // try first for non-overloaded, then for overloaded ocurrences + def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree = + tryEither { + implicit ctx => typedExpr(untpd.Select(qual, name), genericProto) + } { + (sel, _) => + tryEither { + implicit ctx => typedExpr(untpd.Select(qual, name), specificProto) + } { + (_, _) => fallBack(sel) + } + } + // try first for unapply, then for unapplySeq + tryWithName(nme.unapply) { + sel => tryWithName(nme.unapplySeq)(_ => fallBack(sel)) // for backwards compatibility; will be dropped } } @@ -694,14 +712,14 @@ trait Applications extends Compatibility { self: Typer => List.fill(argTypes.length - args.length)(WildcardType) } val unapplyPatterns = (bunchedArgs, argTypes).zipped map (typed(_, _)) - val result = assignType(cpy.UnApply(tree, unapplyFn, unapplyImplicits, unapplyPatterns), ownType) + val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType) unapp.println(s"unapply patterns = $unapplyPatterns") if ((ownType eq pt) || ownType.isError) result else Typed(result, TypeTree(ownType)) case tp => val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) - cpy.UnApply(tree, unapplyErr, Nil, typedArgsErr) withType ErrorType + cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType } } @@ -863,7 +881,7 @@ trait Applications extends Compatibility { self: Typer => def treeShape(tree: untpd.Tree): Tree = tree match { case NamedArg(name, arg) => val argShape = treeShape(arg) - cpy.NamedArg(tree, name, argShape).withType(argShape.tpe) + cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe) case _ => dummyTreeOfType(typeShape(tree)) } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 7da00e051367..eb202cc159d1 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -4,8 +4,16 @@ package typer import core._ import ast._ -import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._ -import Trees._, ProtoTypes._ +import Contexts._ +import Types._ +import Flags._ +import Denotations._ +import Names._ +import StdNames._ +import NameOps._ +import Symbols._ +import Trees._ +import ProtoTypes._ import Constants._ import Scopes._ import annotation.unchecked @@ -14,14 +22,158 @@ import util.{Stats, SimpleMap} import util.common._ import Decorators._ import Uniques._ -import ErrorReporting.{errorType, DiagnosticString} +import ErrorReporting.{err, errorType, DiagnosticString} import config.Printers._ import collection.mutable +import SymDenotations.NoCompleter + +object Checking { + import tpd._ + + /** A general checkBounds method that can be used for TypeApply nodes as + * well as for AppliedTypeTree nodes. + */ + def checkBounds(args: List[tpd.Tree], bounds: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) = { + val argTypes = args.tpes + for ((arg, bounds) <- args zip bounds) { + def notConforms(which: String, bound: Type) = { + ctx.error( + d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", + arg.pos) + } + def checkOverlapsBounds(lo: Type, hi: Type): Unit = { + //println(i"instantiating ${bounds.hi} with $argTypes") + //println(i" = ${instantiate(bounds.hi, argTypes)}") + val hiBound = instantiate(bounds.hi, argTypes) + if (!(lo <:< hiBound)) notConforms("upper", hiBound) + if (!(bounds.lo <:< hi)) notConforms("lower", bounds.lo) + } + arg.tpe match { + case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi) + case tp => checkOverlapsBounds(tp, tp) + } + } + } + + /** A type map which checks that the only cycles in a type are F-bounds + * and that protects all F-bounded references by LazyRefs. + */ + class CheckNonCyclicMap(sym: Symbol, reportErrors: Boolean)(implicit ctx: Context) extends TypeMap { + + /** Are cycles allowed within nested refinedInfos of currently checked type? */ + private var nestedCycleOK = false + + /** Are cycles allwoed within currently checked type? */ + private var cycleOK = false + + /** A diagnostic output string that indicates the position of the last + * part of a type bounds checked by checkInfo. Possible choices: + * alias, lower bound, upper bound. + */ + var where: String = "" + + /** The last type top-level type checked when a CyclicReference occurs. */ + var lastChecked: Type = NoType + + /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles, + * break direct cycle with a LazyRef for legal, F-bounded cycles. + */ + def checkInfo(tp: Type): Type = tp match { + case tp @ TypeBounds(lo, hi) => + if (lo eq hi) + try tp.derivedTypeAlias(apply(lo)) + finally { + where = "alias" + lastChecked = lo + } + else { + val lo1 = try apply(lo) finally { + where = "lower bound" + lastChecked = lo + } + val saved = nestedCycleOK + nestedCycleOK = true + try tp.derivedTypeBounds(lo1, apply(hi)) + finally { + nestedCycleOK = saved + where = "upper bound" + lastChecked = hi + } + } + case _ => + tp + } + + def apply(tp: Type) = tp match { + case tp @ RefinedType(parent, name) => + val parent1 = this(parent) + val saved = cycleOK + cycleOK = nestedCycleOK + try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo)) + finally cycleOK = saved + case tp @ TypeRef(pre, name) => + try { + // A prefix is interesting if it might contain (transitively) a reference + // to symbol `sym` itself. We only check references with interesting + // prefixes for cycles. This pruning is done in order not to force + // global symbols when doing the cyclicity check. + def isInteresting(prefix: Type): Boolean = prefix.stripTypeVar match { + case NoPrefix => true + case ThisType(cls) => sym.owner.isClass && cls.isContainedIn(sym.owner) + case prefix: NamedType => !prefix.symbol.isStaticOwner && isInteresting(prefix.prefix) + case SuperType(thistp, _) => isInteresting(thistp) + case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2) + case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2) + case _ => false + } + // If prefix is interesting, check info of typeref recursively, marking the referred symbol + // with NoCompleter. This provokes a CyclicReference when the symbol + // is hit again. Without this precaution we could stackoverflow here. + if (isInteresting(pre)) { + val info = tp.info + val symInfo = tp.symbol.info + if (tp.symbol.exists) tp.symbol.info = SymDenotations.NoCompleter + try checkInfo(info) + finally if (tp.symbol.exists) tp.symbol.info = symInfo + } + tp + } catch { + case ex: CyclicReference => + ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK") + if (cycleOK) LazyRef(() => tp) + else if (reportErrors) throw ex + else tp + } + case _ => mapOver(tp) + } + } + + /** Check that `info` of symbol `sym` is not cyclic. + * @pre sym is not yet initialized (i.e. its type is a Completer). + * @return `info` where every legal F-bounded reference is proctected + * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported. + */ + def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = { + val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.withMode(Mode.CheckCyclic)) + try checker.checkInfo(info) + catch { + case ex: CyclicReference => + if (reportErrors) { + ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos) + ErrorType + } + else info + } + } +} trait Checking { import tpd._ + def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = + Checking.checkNonCyclic(sym, info, reportErrors) + /** Check that Java statics and packages can only be used in selections. */ def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = { @@ -32,17 +184,13 @@ trait Checking { tree } - /** Check that type arguments `args` conform to corresponding bounds in `poly` */ - def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = { - val argTypes = args.tpes - def substituted(tp: Type) = tp.substParams(poly, argTypes) - for ((arg, bounds) <- args zip poly.paramBounds) { - def notConforms(which: String, bound: Type) = - ctx.error(d"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos) - if (!(arg.tpe <:< substituted(bounds.hi))) notConforms("upper", bounds.hi) - if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo) - } - } + /** Check that type arguments `args` conform to corresponding bounds in `poly` + * Note: This does not check the bounds of AppliedTypeTrees. These + * are handled by method checkBounds in FirstTransform + * TODO: remove pos parameter + */ + def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = Checking.checkBounds( + args, poly.paramBounds, (tp, argTypes) => tp.substParams(poly, argTypes)) /** Check that type `tp` is stable. */ def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = @@ -136,6 +284,7 @@ trait Checking { trait NoChecking extends Checking { import tpd._ + override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = () override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index f20d25792bb7..1f55df2bcb64 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -97,19 +97,21 @@ object ErrorReporting { errorTree(tree, typeMismatchStr(tree.tpe, pt) + implicitFailure.postscript) } + /** A subtype log explaining why `found` does not conform to `expected` */ + def whyNoMatchStr(found: Type, expected: Type) = + if (ctx.settings.explaintypes.value) + "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_)) + else + "" + def typeMismatchStr(found: Type, expected: Type) = disambiguated { implicit ctx => - val (typerStateStr, explanationStr) = - if (ctx.settings.explaintypes.value) - ("\n" + ctx.typerState.show, "\n" + TypeComparer.explained((found <:< expected)(_))) - else - ("", "") def infoStr = found match { // DEBUG case tp: TypeRef => s"with info ${tp.info} / ${tp.prefix.toString} / ${tp.prefix.dealias.toString}" case _ => "" } d"""type mismatch: | found : $found - | required: $expected""".stripMargin + typerStateStr + explanationStr + | required: $expected""".stripMargin + whyNoMatchStr(found, expected) } } diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 69b512416d0a..790a848a77de 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -35,9 +35,9 @@ object EtaExpansion { */ def liftAssigned(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { case Apply(fn @ Select(pre, name), args) => - cpy.Apply(tree, cpy.Select(fn, lift(defs, pre), name), liftArgs(defs, fn.tpe, args)) + cpy.Apply(tree)(cpy.Select(fn)(lift(defs, pre), name), liftArgs(defs, fn.tpe, args)) case Select(pre, name) => - cpy.Select(tree, lift(defs, pre), name) + cpy.Select(tree)(lift(defs, pre), name) case _ => tree } @@ -80,11 +80,11 @@ object EtaExpansion { */ def liftApp(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { case Apply(fn, args) => - cpy.Apply(tree, liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) + cpy.Apply(tree)(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) case TypeApply(fn, targs) => - cpy.TypeApply(tree, liftApp(defs, fn), targs) + cpy.TypeApply(tree)(liftApp(defs, fn), targs) case Select(pre, name) if isPureRef(tree) => - cpy.Select(tree, liftApp(defs, pre), name) + cpy.Select(tree)(liftApp(defs, pre), name) case Block(stats, expr) => liftApp(defs ++= stats, expr) case New(tpt) => diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index 55baa6bc5ea2..54487722a641 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -33,6 +33,7 @@ object Mode { val TypevarsMissContext = newMode(4, "TypevarsMissContext") val InSuperCall = newMode(5, "InSuperCall") + val CheckCyclic = newMode(6, "CheckCyclic") val PatternOrType = Pattern | Type } \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 14404e220038..20a5a1204c67 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -366,9 +366,8 @@ class Namer { typer: Typer => val Thicket(vdef :: (mcls @ TypeDef(_, _, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree) cdef.attachmentOrElse(ExpandedTree, cdef) match { case Thicket(cls :: mval :: TypeDef(_, _, compimpl: Template) :: crest) => - val mcls1 = cpy.TypeDef(mcls, mcls.mods, mcls.name, - cpy.Template(impl, impl.constr, impl.parents, impl.self, - compimpl.body ++ impl.body)) + val mcls1 = cpy.TypeDef(mcls)( + rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body)) mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil)) cdef.putAttachment(ExpandedTree, Thicket(cls :: crest)) case _ => @@ -674,9 +673,11 @@ class Namer { typer: Typer => if (needsLambda) rhsType.LambdaAbstract(tparamSyms) else if (toParameterize) rhsType.parameterizeWith(tparamSyms) else rhsType - rhsType match { - case _: TypeBounds => abstractedRhsType + val unsafeInfo = rhsType match { + case _: TypeBounds => abstractedRhsType.asInstanceOf[TypeBounds] case _ => TypeAlias(abstractedRhsType, if (sym is Local) sym.variance else 0) } + sym.info = NoCompleter + checkNonCyclic(sym, unsafeInfo, reportErrors = true) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 19d8d68953ae..0aa0aa53814f 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -257,8 +257,8 @@ object ProtoTypes { unique(new CachedViewProto(argType, resultType)) } - class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto( - untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer) + class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto( + untpd.TypedSplice(dummyTreeOfType(argType)) :: Nil, WildcardType, typer) /** A prototype for expressions [] that are type-parameterized: * diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 392b8dca1abe..dbf353f9efcd 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -32,13 +32,13 @@ class ReTyper extends Typer { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.Select(tree, qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.SelectFromTypeTree(tree, qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.SelectFromTypeTree(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = @@ -50,7 +50,7 @@ class ReTyper extends Typer { override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = { assert(tree.hasType) val body1 = typed(tree.body, pt) - untpd.cpy.Bind(tree, tree.name, body1).withType(tree.typeOpt) + untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt) } override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala new file mode 100644 index 000000000000..67cb1745f6cc --- /dev/null +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -0,0 +1,1402 @@ +package dotty.tools.dotc +package typer + +import transform._ +import core._ +import config._ +import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._ +import StdNames._, Denotations._, Scopes._, Constants.Constant +import Annotations._ +import util.Positions._ +import scala.collection.{ mutable, immutable } +import ast._ +import Trees._ +import TreeTransforms._ +import util.DotClass +import scala.util.{Try, Success, Failure} +import config.{ScalaVersion, NoScalaVersion} +import typer.ErrorReporting._ +import DenotTransformers._ +import ValueClasses.isDerivedValueClass + +object RefChecks { + import tpd._ + + private def isDefaultGetter(name: Name): Boolean = + name.isTermName && name.asTermName.defaultGetterIndex >= 0 + + private val defaultMethodFilter = new NameFilter { + def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name) + } + + private val AnyOverride = Override | AbsOverride + private val AnyOverrideOrSynthetic = AnyOverride | Synthetic + + /** Only one overloaded alternative is allowed to define default arguments */ + private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = { + // Using the default getters (such as methodName$default$1) as a cheap way of + // finding methods with default parameters. This way, we can limit the members to + // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods + // directly requires inspecting the parameter list of every one. That modification + // shaved 95% off the time spent in this method. + + for ( + defaultGetterClass <- List(clazz, clazz.companionModule.moduleClass); + if defaultGetterClass.isClass + ) { + val defaultGetterNames = defaultGetterClass.asClass.memberNames(defaultMethodFilter) + val defaultMethodNames = defaultGetterNames map (_.asTermName.defaultGetterToMethod) + + for (name <- defaultMethodNames) { + val methods = clazz.info.member(name).alternatives.map(_.symbol) + val haveDefaults = methods.filter(_.hasDefaultParams) + if (haveDefaults.length > 1) { + val owners = haveDefaults map (_.owner) + // constructors of different classes are allowed to have defaults + if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size) + ctx.error( + "in " + clazz + + ", multiple overloaded alternatives of " + haveDefaults.head + + " define default arguments" + ( + if (owners.forall(_ == clazz)) "." + else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")), + clazz.pos) + } + } + } + + // Check for doomed attempt to overload applyDynamic + if (clazz derivesFrom defn.DynamicClass) { + for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.symbol.typeParams.length)) { + ctx.error("implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)", + m1.symbol.pos) + } + } + } + + // Override checking ------------------------------------------------------------ + + /** 1. Check all members of class `clazz` for overriding conditions. + * That is for overriding member M and overridden member O: + * + * 1.1. M must have the same or stronger access privileges as O. + * 1.2. O must not be final. + * 1.3. O is deferred, or M has `override` modifier. + * 1.4. If O is stable, then so is M. + * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias + * 1.6. If O is a type alias, then M is an alias of O. + * 1.7. If O is an abstract type then + * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds. + * or M is a type alias or class which conforms to O's bounds. + * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M + * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds + * 1.8. If O and M are values, then + * 1.8.1 M's type is a subtype of O's type, or + * 1.8.2 M is of type []S, O is of type ()T and S <: T, or + * 1.8.3 M is of type ()S, O is of type []T and S <: T, or + * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O. + * 1.10. If M is not a macro def, O cannot be a macro def. + * 2. Check that only abstract classes have deferred members + * 3. Check that concrete classes do not have deferred definitions + * that are not implemented in a subclass. + * 4. Check that every member with an `override` modifier + * overrides some other member. + * TODO check that classes are not overridden + * TODO This still needs to be cleaned up; the current version is a staright port of what was there + * before, but it looks too complicated and method bodies are far too large. + */ + private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = { + val self = clazz.thisType + + case class MixinOverrideError(member: Symbol, msg: String) + + val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]() + + def printMixinOverrideErrors(): Unit = { + mixinOverrideErrors.toList match { + case List() => + case List(MixinOverrideError(_, msg)) => + ctx.error(msg, clazz.pos) + case MixinOverrideError(member, msg) :: others => + val others1 = others.map(_.member).filter(_.name != member.name).distinct + def othersMsg = { + val others1 = others.map(_.member) + .filter(_.name != member.name) + .map(_.show).distinct + if (others1.isEmpty) "" + else i";\n other members with override errors are:: $others1%, %" + } + ctx.error(msg + othersMsg, clazz.pos) + } + } + + def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz) + def infoStringWithLocation(sym: Symbol) = infoString0(sym, true) + + def infoString0(sym: Symbol, showLocation: Boolean) = { + val sym1 = sym.underlyingSymbol + if (showLocation) sym1.showLocated + else + sym1.show + + (if (sym1.isAliasType) ", which equals " + self.memberInfo(sym1) + else if (sym1.isAbstractType) " with bounds" + self.memberInfo(sym1) + else if (sym1.is(Module)) "" + else if (sym1.isTerm) " of type " + self.memberInfo(sym1) + else "") + } + + /* Check that all conditions for overriding `other` by `member` + * of class `clazz` are met. + */ + def checkOverride(member: Symbol, other: Symbol): Unit = { + def memberTp = self.memberInfo(member) + def otherTp = self.memberInfo(other) + + ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated)) + + def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous + + def overrideErrorMsg(msg: String): String = { + val isConcreteOverAbstract = + (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred) + val addendum = + if (isConcreteOverAbstract) + ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format( + infoStringWithLocation(other), + infoStringWithLocation(member)) + else if (ctx.settings.debug.value) + err.typeMismatchStr(memberTp, otherTp) + else "" + + "overriding %s;\n %s %s%s".format( + infoStringWithLocation(other), infoString(member), msg, addendum) + } + def emitOverrideError(fullmsg: String) = { + if (member.owner == clazz) ctx.error(fullmsg, member.pos) + else mixinOverrideErrors += new MixinOverrideError(member, fullmsg) + } + + def overrideError(msg: String) = { + if (noErrorType) + emitOverrideError(overrideErrorMsg(msg)) + } + + def overrideTypeError() = { + if (noErrorType) { + emitOverrideError(overrideErrorMsg("has incompatible type")) + } + } + + def overrideAccessError() = { + ctx.log(i"member: ${member.showLocated} ${member.flags}") // DEBUG + ctx.log(i"other: ${other.showLocated} ${other.flags}") // DEBUG + val otherAccess = (other.flags & AccessFlags).toString + overrideError("has weaker access privileges; it should be " + + (if (otherAccess == "") "public" else "at least " + otherAccess)) + } + + //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG + + // return if we already checked this combination elsewhere + if (member.owner != clazz) { + def deferredCheck = member.is(Deferred) || !other.is(Deferred) + def subOther(s: Symbol) = s derivesFrom other.owner + def subMember(s: Symbol) = s derivesFrom member.owner + + if (subOther(member.owner) && deferredCheck) { + //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG + return + } + val parentSymbols = clazz.info.parents.map(_.typeSymbol) + if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) { + //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG + return + } + if (parentSymbols forall (p => subOther(p) == subMember(p))) { + //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG + return + } + } + + /* Is the intersection between given two lists of overridden symbols empty? */ + def intersectionIsEmpty(syms1: Iterator[Symbol], syms2: Iterator[Symbol]) = + !(syms1 exists (syms2 contains _)) + + // o: public | protected | package-protected (aka java's default access) + // ^-may be overridden by member with access privileges-v + // m: public | public/protected | public/protected/package-protected-in-same-package-as-o + + if (member.is(Private)) // (1.1) + overrideError("has weaker access privileges; it should not be private") + + // todo: align accessibility implication checking with isAccessible in Contexts + val ob = other.accessBoundary(member.owner) + val mb = member.accessBoundary(member.owner) + def isOverrideAccessOK = ( + (member.flags & AccessFlags).isEmpty // member is public + || // - or - + (!other.is(Protected) || member.is(Protected)) && // if o is protected, so is m, and + (ob.isContainedIn(mb) || other.is(JavaProtected)) // m relaxes o's access boundary, + // or o is Java defined and protected (see #3946) + ) + if (!isOverrideAccessOK) { + overrideAccessError() + } else if (other.isClass) { + overrideError("cannot be used here - class definitions cannot be overridden") + } else if (!other.is(Deferred) && member.isClass) { + overrideError("cannot be used here - classes can only override abstract types") + } else if (other.isEffectivelyFinal) { // (1.2) + overrideError("cannot override final member") + } else if (!other.is(Deferred) && !isDefaultGetter(other.name) && !member.is(AnyOverrideOrSynthetic)) { + // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to + // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. + if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) + emitOverrideError( + clazz + " inherits conflicting members:\n " + + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member) + + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)") + else + overrideError("needs `override' modifier") + } else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) { + overrideError("needs `abstract override' modifiers") + } else if (member.is(AnyOverride) && other.is(Accessor) && + other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) { + // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here. + // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches. + if (!ctx.settings.overrideVars.value) + overrideError("cannot override a mutable variable") + } else if (member.is(AnyOverride) && + !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) && + !member.is(Deferred) && !other.is(Deferred) && + intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) { + overrideError("cannot override a concrete member without a third member that's overridden by both " + + "(this rule is designed to prevent ``accidental overrides'')") + } else if (other.isStable && !member.isStable) { // (1.4) + overrideError("needs to be a stable, immutable value") + } else if (member.is(Lazy) && !other.isSourceMethod && !other.is(Deferred | Lazy)) { + overrideError("cannot override a concrete non-lazy value") + } else if (other.is(Lazy, butNot = Deferred) && !other.isSourceMethod && !member.is(Lazy)) { + overrideError("must be declared lazy to override a concrete lazy value") + } else if (other.is(Deferred) && member.is(Macro) && member.extendedOverriddenSymbols.forall(_.is(Deferred))) { // (1.9) + overrideError("cannot be used here - term macros cannot override abstract methods") + } else if (other.is(Macro) && !member.is(Macro)) { // (1.10) + overrideError("cannot be used here - only term macros can override term macros") + } else { + checkOverrideDeprecated() + } + } + + /* TODO enable; right now the annotation is scala-private, so cannot be seen + * here. + */ + def checkOverrideDeprecated() = { /* + if (other.hasDeprecatedOverridingAnnotation) { + val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse "" + val msg = s"overriding ${other.fullLocationString} is deprecated$suffix" + unit.deprecationWarning(member.pos, msg) + }*/ + } + + val opc = new OverridingPairs.Cursor(clazz) + while (opc.hasNext) { + checkOverride(opc.overriding, opc.overridden) + opc.next() + } + printMixinOverrideErrors() + + // Verifying a concrete class has nothing unimplemented. + if (!clazz.is(AbstractOrTrait)) { + val abstractErrors = new mutable.ListBuffer[String] + def abstractErrorMessage = + // a little formatting polish + if (abstractErrors.size <= 2) abstractErrors mkString " " + else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "") + + def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = { + def prelude = ( + if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible" + else if (mustBeMixin) clazz + " needs to be a mixin" + else clazz + " needs to be abstract") + ", since" + + if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg) + else abstractErrors += msg + } + + def hasJavaErasedOverriding(sym: Symbol): Boolean = + !ctx.erasurePhase.exists || // can't do the test, assume the best + ctx.atPhase(ctx.erasurePhase.next) { implicit ctx => + clazz.info.nonPrivateMember(sym.name).hasAltWith { alt => + alt.symbol.is(JavaDefined, butNot = Deferred) && + !sym.owner.derivesFrom(alt.symbol.owner) && + alt.signature.matches(sym.signature) + } + } + + def ignoreDeferred(member: SingleDenotation) = + member.isType || + member.symbol.is(JavaDefined) && hasJavaErasedOverriding(member.symbol) + + // 2. Check that only abstract classes have deferred members + def checkNoAbstractMembers(): Unit = { + // Avoid spurious duplicates: first gather any missing members. + val missing = clazz.thisType.abstractTermMembers.filterNot(ignoreDeferred) + // Group missing members by the name of the underlying symbol, + // to consolidate getters and setters. + val grouped: Map[Name, Seq[SingleDenotation]] = missing groupBy (_.symbol.underlyingSymbol.name) + // Dotty deviation: Added type annotation for `grouped`. + // The inferred type is Map[Symbol#ThisName, Seq[SingleDenotation]] + // but then the definition of isMultiple fails with an error: + // RefChecks.scala:379: error: type mismatch: + // found : underlying.ThisName + // required: dotty.tools.dotc.core.Symbols.Symbol#ThisName + // + // val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1 + // ^ + // As far as I can see, the complaint is correct, even under the + // old reading where Symbol#ThisName means x.ThisName forSome { val x } + + val missingMethods = grouped.toList flatMap { + case (name, syms) => + val withoutSetters = syms filterNot (_.symbol.isSetter) + if (withoutSetters.nonEmpty) withoutSetters else syms + } + + def stubImplementations: List[String] = { + // Grouping missing methods by the declaring class + val regrouped = missingMethods.groupBy(_.symbol.owner).toList + def membersStrings(members: List[SingleDenotation]) = + members.sortBy(_.symbol.name.toString).map(_.showDcl + " = ???") + + if (regrouped.tail.isEmpty) + membersStrings(regrouped.head._2) + else (regrouped.sortBy("" + _._1.name) flatMap { + case (owner, members) => + ("// Members declared in " + owner.fullName) +: membersStrings(members) :+ "" + }).init + } + + // If there are numerous missing methods, we presume they are aware of it and + // give them a nicely formatted set of method signatures for implementing. + if (missingMethods.size > 1) { + abstractClassError(false, "it has " + missingMethods.size + " unimplemented members.") + val preface = + """|/** As seen from %s, the missing signatures are as follows. + | * For convenience, these are usable as stub implementations. + | */ + |""".stripMargin.format(clazz) + abstractErrors += stubImplementations.map(" " + _ + "\n").mkString(preface, "", "") + return + } + + for (member <- missing) { + val memberSym = member.symbol + def undefined(msg: String) = abstractClassError(false, member.showDcl + " is not defined" + msg) + val underlying = memberSym.underlyingSymbol + + // Give a specific error message for abstract vars based on why it fails: + // It could be unimplemented, have only one accessor, or be uninitialized. + if (underlying.is(Mutable)) { + val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1 + + // If both getter and setter are missing, squelch the setter error. + if (memberSym.isSetter && isMultiple) () + else undefined( + if (memberSym.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)" + else if (memberSym.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)" + else err.abstractVarMessage(memberSym)) + } else if (underlying.is(Method)) { + // If there is a concrete method whose name matches the unimplemented + // abstract method, and a cursory examination of the difference reveals + // something obvious to us, let's make it more obvious to them. + val abstractParams = underlying.info.firstParamTypes + val matchingName = clazz.info.member(underlying.name).alternatives + val matchingArity = matchingName filter { m => + !m.symbol.is(Deferred) && + m.info.firstParamTypes.length == abstractParams.length + } + + matchingArity match { + // So far so good: only one candidate method + case concrete :: Nil => + val mismatches = + abstractParams.zip(concrete.info.firstParamTypes) + .filterNot { case (x, y) => x =:= y } + mismatches match { + // Only one mismatched parameter: say something useful. + case (pa, pc) :: Nil => + val abstractSym = pa.typeSymbol + val concreteSym = pc.typeSymbol + def subclassMsg(c1: Symbol, c2: Symbol) = ( + ": %s is a subclass of %s, but method parameter types must match exactly.".format( + c1.showLocated, c2.showLocated)) + val addendum = + if (abstractSym == concreteSym) { + val paArgs = pa.argInfos + val pcArgs = pc.argInfos + val paConstr = pa.withoutArgs(paArgs) + val pcConstr = pc.withoutArgs(pcArgs) + (paConstr, pcConstr) match { + case (TypeRef(pre1, _), TypeRef(pre2, _)) => + if (pre1 =:= pre2) ": their type parameters differ" + else ": their prefixes (i.e. enclosing instances) differ" + case _ => + "" + } + } else if (abstractSym isSubClass concreteSym) + subclassMsg(abstractSym, concreteSym) + else if (concreteSym isSubClass abstractSym) + subclassMsg(concreteSym, abstractSym) + else "" + + undefined("\n(Note that %s does not match %s%s)".format(pa, pc, addendum)) + case xs => + undefined("") + } + case _ => + undefined("") + } + } else undefined("") + } + } + + // 3. Check that concrete classes do not have deferred definitions + // that are not implemented in a subclass. + // Note that this is not the same as (2); In a situation like + // + // class C { def m: Int = 0} + // class D extends C { def m: Int } + // + // (3) is violated but not (2). + def checkNoAbstractDecls(bc: Symbol): Unit = { + for (decl <- bc.info.decls) { + if (decl.is(Deferred) && !ignoreDeferred(decl)) { + val impl = decl.matchingMember(clazz.thisType) + if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { + val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG + ctx.log(i"${impl1}: ${impl1.info}") // DEBUG + ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG + abstractClassError(false, "there is a deferred declaration of " + infoString(decl) + + " which is not implemented in a subclass" + err.abstractVarMessage(decl)) + } + } + } + if (bc.asClass.superClass.is(Abstract)) + checkNoAbstractDecls(bc.asClass.superClass) + } + + checkNoAbstractMembers() + if (abstractErrors.isEmpty) + checkNoAbstractDecls(clazz) + + if (abstractErrors.nonEmpty) + ctx.error(abstractErrorMessage, clazz.pos) + } else if (clazz.is(Trait) && !(clazz derivesFrom defn.AnyValClass)) { + // For non-AnyVal classes, prevent abstract methods in interfaces that override + // final members in Object; see #4431 + for (decl <- clazz.info.decls) { + // Have to use matchingSymbol, not a method involving overridden symbols, + // because the scala type system understands that an abstract method here does not + // override a concrete method in Object. The jvm, however, does not. + val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType) + if (overridden.is(Final)) + ctx.error("trait cannot redefine final method from class AnyRef", decl.pos) + } + } + + /* Returns whether there is a symbol declared in class `inclazz` + * (which must be different from `clazz`) whose name and type + * seen as a member of `class.thisType` matches `member`'s. + */ + def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = { + + def isSignatureMatch(sym: Symbol) = !sym.isTerm || + clazz.thisType.memberInfo(sym).matchesLoosely(member.info) + + /* The rules for accessing members which have an access boundary are more + * restrictive in java than scala. Since java has no concept of package nesting, + * a member with "default" (package-level) access can only be accessed by members + * in the exact same package. Example: + * + * package a.b; + * public class JavaClass { void foo() { } } + * + * The member foo() can be accessed only from members of package a.b, and not + * nested packages like a.b.c. In the analogous scala class: + * + * package a.b + * class ScalaClass { private[b] def foo() = () } + * + * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic + * is restricting the set of matching signatures according to the above semantics. + */ + def javaAccessCheck(sym: Symbol) = ( + !inclazz.is(JavaDefined) // not a java defined member + || !sym.privateWithin.exists // no access boundary + || sym.is(Protected) // marked protected in java, thus accessible to subclasses + || sym.privateWithin == member.enclosingPackageClass // exact package match + ) + def classDecls = inclazz.info.nonPrivateDecl(member.name) + + (inclazz != clazz) && + classDecls.hasAltWith(d => isSignatureMatch(d.symbol) && javaAccessCheck(d.symbol)) + } + + // 4. Check that every defined member with an `override` modifier overrides some other member. + for (member <- clazz.info.decls) + if (member.is(AnyOverride) && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { + // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG + + val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final)) + def issueError(suffix: String) = + ctx.error(i"$member overrides nothing$suffix", member.pos) + nonMatching match { + case Nil => + issueError("") + case ms => + val superSigs = ms.map(_.showDcl).mkString("\n") + issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}") + } + member.resetFlag(AnyOverride) + } + } + + // Note: if a symbol has both @deprecated and @migration annotations and both + // warnings are enabled, only the first one checked here will be emitted. + // I assume that's a consequence of some code trying to avoid noise by suppressing + // warnings after the first, but I think it'd be better if we didn't have to + // arbitrarily choose one as more important than the other. + private def checkUndesiredProperties(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = { + // If symbol is deprecated, and the point of reference is not enclosed + // in either a deprecated member or a scala bridge method, issue a warning. + if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated)) { + ctx.deprecationWarning("%s%s is deprecated%s".format( + sym, sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse "", pos)) + } + // Similar to deprecation: check if the symbol is marked with @migration + // indicating it has changed semantics between versions. + if (sym.hasAnnotation(defn.MigrationAnnot) && ctx.settings.Xmigration.value != NoScalaVersion) { + val symVersion: scala.util.Try[ScalaVersion] = sym.migrationVersion.get + val changed = symVersion match { + case scala.util.Success(v) => + ctx.settings.Xmigration.value < v + case Failure(ex) => + ctx.warning(s"${sym.showLocated} has an unparsable version number: ${ex.getMessage()}", pos) + false + } + if (changed) + ctx.warning(s"${sym.showLocated} has changed semantics in version $symVersion:\n${sym.migrationMessage.get}") + } + /* (Not enabled yet) + * See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly. + * + if (sym.isCompileTimeOnly) { + def defaultMsg = + sm"""Reference to ${sym.fullLocationString} should not have survived past type checking, + |it should have been processed and eliminated during expansion of an enclosing macro.""" + // The getOrElse part should never happen, it's just here as a backstop. + ctx.error(sym.compileTimeOnlyMessage getOrElse defaultMsg, pos) + }*/ + } + + /** Check that a deprecated val or def does not override a + * concrete, non-deprecated method. If it does, then + * deprecation is meaningless. + */ + private def checkDeprecatedOvers(tree: Tree)(implicit ctx: Context): Unit = { + val symbol = tree.symbol + if (symbol.isDeprecated) { + val concrOvers = + symbol.allOverriddenSymbols.filter(sym => + !sym.isDeprecated && !sym.is(Deferred)) + if (!concrOvers.isEmpty) + ctx.deprecationWarning( + symbol.toString + " overrides concrete, non-deprecated symbol(s):" + + concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos) + } + } + + /** Verify classes extending AnyVal meet the requirements */ + private def checkAnyValSubclass(clazz: Symbol)(implicit ctx: Context) = + if (isDerivedValueClass(clazz)) { + if (clazz.is(Trait)) + ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos) + else if (clazz.is(Abstract)) + ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos) + } + + type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)] + + class OptLevelInfo extends DotClass { + def levelAndIndex: LevelAndIndex = Map() + def enterReference(sym: Symbol, pos: Position): Unit = () + } + + /** A class to help in forward reference checking */ + class LevelInfo(outerLevelAndIndex: LevelAndIndex, stats: List[Tree])(implicit ctx: Context) + extends OptLevelInfo { + override val levelAndIndex: LevelAndIndex = + ((outerLevelAndIndex, 0) /: stats) {(mi, stat) => + val (m, idx) = mi + val m1 = stat match { + case stat: MemberDef => m.updated(stat.symbol, (this, idx)) + case _ => m + } + (m1, idx + 1) + }._1 + var maxIndex: Int = Int.MinValue + var refPos: Position = _ + var refSym: Symbol = _ + + override def enterReference(sym: Symbol, pos: Position): Unit = + if (sym.exists && sym.owner.isTerm) + levelAndIndex.get(sym) match { + case Some((level, idx)) if (level.maxIndex < idx) => + level.maxIndex = idx + level.refPos = pos + level.refSym = sym + case _ => + } + } + + val NoLevelInfo = new OptLevelInfo() +} +import RefChecks._ + +/** Post-attribution checking and transformation. + * + * This phase performs the following checks. + * + * - only one overloaded alternative defines default arguments + * - applyDynamic methods are not overloaded + * - all overrides conform to rules laid down by `checkAllOverrides`. + * - any value classes conform to rules laid down by `checkAnyValSubClass`. + * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals). + * - no forward reference in a local block jumps over a non-lazy val definition. + * + * It warns about references to symbols labeled deprecated or migration. + * + * It performs the following transformations: + * + * - if (true) A else B --> A + * if (false) A else B --> B + * - macro definitions are eliminated. + */ +class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransformer => + + import tpd._ + + val name: String = "refchecks" + + val treeTransform = new Transform(NoLevelInfo) + + class Transform(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform { + def phase = thisTransformer + override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = { + // println(i"preparing for $trees%; %, owner = ${ctx.owner}") + if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees)) + else this + } + + override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo) = { + checkDeprecatedOvers(tree) + val sym = tree.symbol + if (sym.exists && sym.owner.isTerm && !sym.is(Lazy)) + currentLevel.levelAndIndex.get(sym) match { + case Some((level, symIdx)) if symIdx < level.maxIndex => + ctx.debuglog("refsym = " + level.refSym) + ctx.error(s"forward reference extends over definition of $sym", level.refPos) + case _ => + } + tree + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { + checkDeprecatedOvers(tree) + if (tree.symbol is Macro) EmptyTree else tree + } + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = ctx.owner + checkOverloadedRestrictions(cls) + checkAllOverrides(cls) + checkAnyValSubclass(cls) + if (isDerivedValueClass(cls)) + cls.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! + tree + } + + override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo) = { + if (!tree.original.isEmpty) + tree.tpe.foreachPart { + case tp: NamedType => checkUndesiredProperties(tp.symbol, tree.pos) + case _ => + } + tree + } + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { + checkUndesiredProperties(tree.symbol, tree.pos) + currentLevel.enterReference(tree.symbol, tree.pos) + tree + } + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { + checkUndesiredProperties(tree.symbol, tree.pos) + tree + } + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = { + if (isSelfConstrCall(tree)) { + assert(currentLevel.isInstanceOf[LevelInfo], ctx.owner + "/" + i"$tree") + val level = currentLevel.asInstanceOf[LevelInfo] + if (level.maxIndex > 0) { + // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717 + ctx.debuglog("refsym = " + level.refSym) + ctx.error("forward reference not allowed from self constructor invocation", level.refPos) + } + } + tree + } + + override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) = + tree.cond.tpe match { + case ConstantType(value) => if (value.booleanValue) tree.thenp else tree.elsep + case _ => tree + } + + override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = { + currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos) + tree + } + } +} + +/* todo: rewrite and re-enable + +// Comparison checking ------------------------------------------------------- + + object normalizeAll extends TypeMap { + def apply(tp: Type) = mapOver(tp).normalize + } + + def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match { + case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply => + unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567 + case _ => + } + + private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match { + case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true + case _ => false + } + /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */ + private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = { + def isReferenceOp = sym == Object_eq || sym == Object_ne + def isNew(tree: Tree) = tree match { + case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true + case _ => false + } + def underlyingClass(tp: Type): Symbol = { + val sym = tp.widen.typeSymbol + if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi) + else sym + } + val actual = underlyingClass(other.tpe) + val receiver = underlyingClass(qual.tpe) + def onTrees[T](f: List[Tree] => T) = f(List(qual, other)) + def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual)) + + // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol` + def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(other.tpe.widen) + + /* Symbols which limit the warnings we can issue since they may be value types */ + val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass) + + // Whether def equals(other: Any) has known behavior: it is the default + // inherited from java.lang.Object, or it is a synthetically generated + // case equals. TODO - more cases are warnable if the target is a synthetic + // equals. + def isUsingWarnableEquals = { + val m = receiver.info.member(nme.equals_) + ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m)) + } + def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase + def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_)) + // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere. + def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!= + def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual) + + // Whether the operands+operator represent a warnable combo (assuming anyrefs) + // Looking for comparisons performed with ==/!= in combination with either an + // equals method inherited from Object or a case class synthetic equals (for + // which we know the logic.) + def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals) + def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info) + def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass + def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass + def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass + def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s) + def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass + def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass + // includes java.lang.Number if appropriate [SI-5779] + def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s) + def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s) + // used to short-circuit unrelatedTypes check if both sides are special + def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s) + val nullCount = onSyms(_ filter (_ == NullClass) size) + def isNonsenseValueClassCompare = ( + !haveSubclassRelationship + && isUsingDefaultScalaOp + && isEitherValueClass + && !isCaseEquals + ) + + // Have we already determined that the comparison is non-sensible? I mean, non-sensical? + var isNonSensible = false + + def nonSensibleWarning(what: String, alwaysEqual: Boolean) = { + val msg = alwaysEqual == (name == nme.EQ || name == nme.eq) + unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg") + isNonSensible = true + } + def nonSensible(pre: String, alwaysEqual: Boolean) = + nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual) + def nonSensiblyEq() = nonSensible("", alwaysEqual = true) + def nonSensiblyNeq() = nonSensible("", alwaysEqual = false) + def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false) + + def unrelatedMsg = name match { + case nme.EQ | nme.eq => "never compare equal" + case _ => "always compare unequal" + } + def unrelatedTypes() = if (!isNonSensible) { + val weaselWord = if (isEitherValueClass) "" else " most likely" + unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg") + } + + if (nullCount == 2) // null == null + nonSensiblyEq() + else if (nullCount == 1) { + if (onSyms(_ exists isPrimitiveValueClass)) // null == 5 + nonSensiblyNeq() + else if (onTrees( _ exists isNew)) // null == new AnyRef + nonSensiblyNew() + } + else if (isBoolean(receiver)) { + if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5 + nonSensiblyNeq() + } + else if (isUnit(receiver)) { + if (isUnit(actual)) // () == () + nonSensiblyEq() + else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc" + nonSensiblyNeq() + } + else if (isNumeric(receiver)) { + if (!isNumeric(actual)) + if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc" + nonSensiblyNeq() + } + else if (isWarnable && !isCaseEquals) { + if (isNew(qual)) // new X == y + nonSensiblyNew() + else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y + nonSensiblyNew() + else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y + if (isEitherNullable) + nonSensible("non-null ", false) + else + nonSensiblyNeq() + } + } + + // warn if one but not the other is a derived value class + // this is especially important to enable transitioning from + // regular to value classes without silent failures. + if (isNonsenseValueClassCompare) + unrelatedTypes() + // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean + else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) { + // better to have lubbed and lost + def warnIfLubless(): Unit = { + val common = global.lub(List(actual.tpe, receiver.tpe)) + if (ObjectTpe <:< common) + unrelatedTypes() + } + // warn if actual has a case parent that is not same as receiver's; + // if actual is not a case, then warn if no common supertype, as below + if (isCaseEquals) { + def thisCase = receiver.info.member(nme.equals_).owner + actual.info.baseClasses.find(_.isCase) match { + case Some(p) if p != thisCase => nonSensible("case class ", false) + case None => + // stronger message on (Some(1) == None) + //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq() + //else + // if a class, it must be super to thisCase (and receiver) since not <: thisCase + if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq() + else if (!haveSubclassRelationship) warnIfLubless() + case _ => + } + } + // warn only if they have no common supertype below Object + else if (!haveSubclassRelationship) { + warnIfLubless() + } + } + } + /** Sensibility check examines flavors of equals. */ + def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match { + case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) => + checkSensibleEquals(pos, qual, name, fn.symbol, args.head) + case _ => + } +*/ + +/* --------------- Overflow ------------------------------------------------- + * + + def accessFlagsToString(sym: Symbol) = flagsToString( + sym getFlag (PRIVATE | PROTECTED), + if (sym.hasAccessBoundary) "" + sym.privateWithin.name else "" + ) + + def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match { + case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) => + rtp1 <:< rtp2 + case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) => + rtp1 <:< rtp2 + case (TypeRef(_, sym, _), _) if sym.isModuleClass => + overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix) + case _ => + def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner) + + (tp1 <:< tp2) || ( // object override check + tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && { + val cb1 = classBoundAsSeen(tp1) + val cb2 = classBoundAsSeen(tp2) + (cb1 <:< cb2) && { + log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2)) + true + } + } + ) + } + private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean)(implicit ctx: Context) = tp match { + case TypeRef(pre, sym, args) => + tree match { + case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types + // FIXME: reconcile this check with one in resetAttrs + case _ => checkUndesiredProperties(sym, tree.pos) + } + if(sym.isJavaDefined) + sym.typeParams foreach (_.cookJavaRawInfo()) + if (!tp.isHigherKinded && !skipBounds) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) + case _ => + } + + private def checkTypeRefBounds(tp: Type, tree: Tree) = { + var skipBounds = false + tp match { + case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass => + skipBounds = true + underlying + case TypeRef(pre, sym, args) => + if (!tp.isHigherKinded && !skipBounds) + checkBounds(tree, pre, sym.owner, sym.typeParams, args) + tp + case _ => + tp + } + } + + private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp => + checkTypeRef(tp, tree, skipBounds = false) + checkTypeRefBounds(tp, tree) + } + private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f + + private def applyRefchecksToAnnotations(tree: Tree)(implicit ctx: Context): Unit = { + def applyChecks(annots: List[Annotation]) = { + checkAnnotations(annots map (_.atp), tree) + transformTrees(annots flatMap (_.args)) + } + + tree match { + case m: MemberDef => + val sym = m.symbol + applyChecks(sym.annotations) + // validate implicitNotFoundMessage + analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn => + unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn") + } + + case tpt@TypeTree() => + if(tpt.original != null) { + tpt.original foreach { + case dc@TypeTreeWithDeferredRefCheck() => + applyRefchecksToAnnotations(dc.check()) // #2416 + case _ => + } + } + + doTypeTraversal(tree) { + case tp @ AnnotatedType(annots, _) => + applyChecks(annots) + case tp => + } + case _ => + } + } + + private def transformCaseApply(tree: Tree, ifNot: => Unit) = { + val sym = tree.symbol + + def isClassTypeAccessible(tree: Tree): Boolean = tree match { + case TypeApply(fun, targs) => + isClassTypeAccessible(fun) + case Select(module, apply) => + ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`; + // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`. + treeInfo.isQualifierSafeToElide(module) && + // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case, + // the companion class is actually not a ClassSymbol, but a reference to an abstract type. + module.symbol.companionClass.isClass + ) + } + + val doTransform = + sym.isSourceMethod && + sym.isCase && + sym.name == nme.apply && + isClassTypeAccessible(tree) + + if (doTransform) { + tree foreach { + case i@Ident(_) => + enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()` + case _ => + } + toConstructor(tree.pos, tree.tpe) + } + else { + ifNot + tree + } + } + + private def transformApply(tree: Apply): Tree = tree match { + case Apply( + Select(qual, nme.filter | nme.withFilter), + List(Function( + List(ValDef(_, pname, tpt, _)), + Match(_, CaseDef(pat1, _, _) :: _)))) + if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) && + isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) => + + transform(qual) + + case Apply(fn, args) => + // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability + // analyses in the pattern matcher + if (!inPattern) { + checkImplicitViewOptionApply(tree.pos, fn, args) + checkSensible(tree.pos, fn, args) + } + currentApplication = tree + tree + } + private def transformSelect(tree: Select): Tree = { + val Select(qual, _) = tree + val sym = tree.symbol + + checkUndesiredProperties(sym, tree.pos) + checkDelayedInitSelect(qual, sym, tree.pos) + + if (!sym.exists) + devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe) + else if (sym.isLocalToThis) + varianceValidator.checkForEscape(sym, currentClass) + + def checkSuper(mix: Name) = + // term should have been eliminated by super accessors + assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix)) + + transformCaseApply(tree, + qual match { + case Super(_, mix) => checkSuper(mix) + case _ => + } + ) + } + private def transformIf(tree: If): Tree = { + val If(cond, thenpart, elsepart) = tree + def unitIfEmpty(t: Tree): Tree = + if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t + + cond.tpe match { + case ConstantType(value) => + val res = if (value.booleanValue) thenpart else elsepart + unitIfEmpty(res) + case _ => tree + } + } + + // Warning about nullary methods returning Unit. TODO: move to lint + private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match { + case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass => + // this may be the implementation of e.g. a generic method being parameterized + // on Unit, in which case we had better let it slide. + val isOk = ( + sym.isGetter + || (sym.name containsName nme.DEFAULT_GETTER_STRING) + || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType)) + ) + if (!isOk) + unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead") + case _ => () + } + + /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */ + def toConstructor(pos: Position, tpe: Type)(implicit ctx: Context): Tree = { + val rtpe = tpe.finalResultType + assert(rtpe.typeSymbol.is(Case), tpe) + New(rtpe).withPos(pos).select(rtpe.typeSymbol.primaryConstructor) + } + private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match { + case Apply(_, args) => + val clazz = pat.tpe.typeSymbol + clazz == seltpe.typeSymbol && + clazz.isCaseClass && + (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable) + case Typed(pat, tpt) => + seltpe <:< tpt.tpe + case Ident(tpnme.WILDCARD) => + true + case Bind(_, pat) => + isIrrefutable(pat, seltpe) + case _ => + false + } + private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = { + def isLikelyUninitialized = ( + (sym.owner isSubClass DelayedInitClass) + && !qual.tpe.isInstanceOf[ThisType] + && sym.accessedOrSelf.isVal + ) + if (settings.lint.value && isLikelyUninitialized) + unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value") + } + private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( + (otherSym != NoSymbol) + && !otherSym.isProtected + && !otherSym.isTypeParameterOrSkolem + && !otherSym.isExistentiallyBound + && (otherSym isLessAccessibleThan memberSym) + && (otherSym isLessAccessibleThan memberSym.enclClass) + ) + private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = { + val extras = other match { + case TypeRef(pre, _, args) => + // checking the prefix here gives us spurious errors on e.g. a private[process] + // object which contains a type alias, which normalizes to a visible type. + args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym)) + case _ => + Nil + } + if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras + else extras + } + private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) { + val comparison = accessFlagsToString(memberSym) match { + case "" => "" + case acc => " is " + acc + " but" + } + val cannot = + if (memberSym.isDeferred) "may be unable to provide a concrete implementation of" + else "may be unable to override" + + unit.warning(memberSym.pos, + "%s%s references %s %s.".format( + memberSym.fullLocationString, comparison, + accessFlagsToString(otherSym), otherSym + ) + "\nClasses which cannot access %s %s %s.".format( + otherSym.decodedName, cannot, memberSym.decodedName) + ) + } + + /** Warn about situations where a method signature will include a type which + * has more restrictive access than the method itself. + */ + private def checkAccessibilityOfReferencedTypes(tree: Tree) { + val member = tree.symbol + + def checkAccessibilityOfType(tpe: Type) { + val inaccessible = lessAccessibleSymsInType(tpe, member) + // if the unnormalized type is accessible, that's good enough + if (inaccessible.isEmpty) () + // or if the normalized type is, that's good too + else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) () + // otherwise warn about the inaccessible syms in the unnormalized type + else inaccessible foreach (sym => warnLessAccessible(sym, member)) + } + + // types of the value parameters + mapParamss(member)(p => checkAccessibilityOfType(p.tpe)) + // upper bounds of type parameters + member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType + } + + private def checkByNameRightAssociativeDef(tree: DefDef) { + tree match { + case DefDef(_, name, _, params :: _, _, _) => + if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol))) + unit.warning(tree.pos, + "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.") + case _ => + } + } + override def transform(tree: Tree)(implicit ctx: Context): Tree = { + //val savedLocalTyper = localTyper + try { + val sym = tree.symbol + checkOverloadedRestrictions(ctx.owner) + checkAllOverrides(ctx.owner) + checkAnyValSubclass(ctx.owner) + if (ctx.owner.isDerivedValueClass) + ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! + tree + + + // Apply RefChecks to annotations. Makes sure the annotations conform to + // type bounds (bug #935), issues deprecation warnings for symbols used + // inside annotations. + // applyRefchecksToAnnotations(tree) ??? + var result: Tree = tree match { + case tree: ValOrDefDef => + // move to lint: + // if (settings.warnNullaryUnit) + // checkNullaryMethodReturnType(sym) + // if (settings.warnInaccessible) { + // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) + // checkAccessibilityOfReferencedTypes(tree) + // } + // tree match { + // case dd: DefDef => checkByNameRightAssociativeDef(dd) + // case _ => + // } + tree + + case Template(constr, parents, self, body) => + // localTyper = localTyper.atOwner(tree, currentOwner) + checkOverloadedRestrictions(ctx.owner) + checkAllOverrides(ctx.owner) + checkAnyValSubclass(ctx.owner) + if (ctx.owner.isDerivedValueClass) + ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! + tree + + case tpt: TypeTree => + transform(tpt.original) + tree + + case TypeApply(fn, args) => + checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe)) + transformCaseApply(tree, ()) + + case x @ Apply(_, _) => + transformApply(x) + + case x @ If(_, _, _) => + transformIf(x) + + case New(tpt) => + enterReference(tree.pos, tpt.tpe.typeSymbol) + tree + + case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) => + unit.error(tree.pos, "no `: _*' annotation allowed here\n"+ + "(such annotations are only allowed in arguments to *-parameters)") + tree + + case Ident(name) => + checkUndesiredProperties(sym, tree.pos) + transformCaseApply(tree, + if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) { + assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug + enterReference(tree.pos, sym) + } + ) + + case x @ Select(_, _) => + transformSelect(x) + + case UnApply(fun, args) => + transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun) + // transformTrees(args) // TODO: is this necessary? could there be forward references in the args?? + // probably not, until we allow parameterised extractors + tree + + + case _ => tree + } + + // skip refchecks in patterns.... + result = result match { + case CaseDef(pat, guard, body) => + val pat1 = savingInPattern { + inPattern = true + transform(pat) + } + treeCopy.CaseDef(tree, pat1, transform(guard), transform(body)) + case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) => + savingInPattern { + inPattern = true + deriveLabelDef(result)(transform) + } + case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) => + savingInPattern { + // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals + // that we are in the user-supplied code in the case body. + // + // Relies on the translation of: + // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }' + // to: + // val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]); + // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply. + inPattern = false + super.transform(result) + } + case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) => + deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector. + case _ => + super.transform(result) + } + result match { + case ClassDef(_, _, _, _) + | TypeDef(_, _, _, _) => + if (result.symbol.isLocalToBlock || result.symbol.isTopLevel) + varianceValidator.traverse(result) + case tt @ TypeTree() if tt.original != null => + varianceValidator.traverse(tt.original) // See SI-7872 + case _ => + } + + checkUnexpandedMacro(result) + + result + } catch { + case ex: TypeError => + if (settings.debug) ex.printStackTrace() + unit.error(tree.pos, ex.getMessage()) + tree + } finally { + localTyper = savedLocalTyper + currentApplication = savedCurrentApplication + } + } +*/ + diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index d5153bf1384d..cd9ddb0bd5cc 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -272,13 +272,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) - checkValue(assignType(cpy.Select(tree, qual1, tree.name), qual1), pt) + checkValue(assignType(cpy.Select(tree)(qual1, tree.name), qual1), pt) } def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = track("typedSelectFromTypeTree") { val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) checkLegalPrefix(qual1.tpe, tree.name, qual1.pos) - assignType(cpy.SelectFromTypeTree(tree, qual1, tree.name), qual1) + assignType(cpy.SelectFromTypeTree(tree)(qual1, tree.name), qual1) } def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = track("typedThis") { @@ -291,7 +291,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true case _ => false } - assignType(cpy.Super(tree, qual1, tree.mix), qual1, inConstrCall) + assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall) } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { @@ -304,11 +304,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit import untpd._ val x = tpnme.ANON_CLASS val clsDef = TypeDef(Modifiers(Final), x, templ) - typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) + typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => val tpt1 = typedType(tree.tpt) checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) - assignType(cpy.New(tree, tpt1), tpt1) + assignType(cpy.New(tree)(tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } } @@ -320,7 +320,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val left1 = typed(tree.left, leftProto) val right1 = typed(tree.right, rightProto) - assignType(cpy.Pair(tree, left1, right1), left1, right1) + assignType(cpy.Pair(tree)(left1, right1), left1, right1) } def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") { @@ -329,7 +329,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val expr1 = if (isWildcard) tree.expr withType tpt1.tpe else typed(tree.expr, tpt1.tpe) - assignType(cpy.Typed(tree, expr1, tpt1), tpt1) + assignType(cpy.Typed(tree)(expr1, tpt1), tpt1) } tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => @@ -348,15 +348,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") { val arg1 = typed(tree.arg, pt) - assignType(cpy.NamedArg(tree, tree.name, arg1), arg1) + assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1) } def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = track("typedAssign") { tree.lhs match { case lhs @ Apply(fn, args) => - typed(cpy.Apply(lhs, untpd.Select(fn, nme.update), args :+ tree.rhs), pt) + typed(cpy.Apply(lhs)(untpd.Select(fn, nme.update), args :+ tree.rhs), pt) case untpd.TypedSplice(Apply(Select(fn, app), args)) if app == nme.apply => - typed(cpy.Apply(fn, + typed(cpy.Apply(fn)( untpd.Select(untpd.TypedSplice(fn), nme.update), (args map untpd.TypedSplice) :+ tree.rhs), pt) case lhs => @@ -364,10 +364,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def lhs1 = typed(untpd.TypedSplice(lhsCore)) lhsCore.tpe match { case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) => - assignType(cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info))) + assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) case _ => def reassignmentToVal = - errorTree(cpy.Assign(tree, lhsCore, typed(tree.rhs, lhs1.tpe.widen)), + errorTree(cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)), "reassignment to val") lhsCore.tpe match { case ref: TermRef => // todo: further conditions to impose on getter? @@ -379,7 +379,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val setterTypeRaw = pre select (setterName, setter) val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) val lhs2 = lhsCore.withName(setterName).withType(setterType) - typed(cpy.Apply(tree, untpd.TypedSplice(lhs2), tree.rhs :: Nil)) + typed(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => reassignmentToVal } @@ -394,7 +394,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val exprCtx = index(tree.stats) val stats1 = typedStats(tree.stats, ctx.owner) val expr1 = typedExpr(tree.expr, pt)(exprCtx) - ensureNoLocalRefs(assignType(cpy.Block(tree, stats1, expr1), stats1, expr1), pt) + ensureNoLocalRefs(assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt) } def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { @@ -432,11 +432,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (leaks.isEmpty) block else if (isFullyDefined(pt, ForceDegree.all)) { val expr1 = Typed(expr, TypeTree(pt)) - cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant + cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant } else if (!forcedDefined) { fullyDefinedType(block.tpe, "block", block.pos) val expr1 = Typed(expr, TypeTree(avoid(block.tpe, localSyms(stats)))) - val block1 = cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is already done + val block1 = cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is already done ensureNoLocalRefs(block1, pt, forcedDefined = true) } else errorTree(block, @@ -447,13 +447,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val cond1 = typed(tree.cond, defn.BooleanType) val thenp1 = typed(tree.thenp, pt) val elsep1 = typed(tree.elsep orElse untpd.unitLiteral withPos tree.pos, pt) - assignType(cpy.If(tree, cond1, thenp1, elsep1), thenp1, elsep1) + assignType(cpy.If(tree)(cond1, thenp1, elsep1), thenp1, elsep1) } def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) - typed(cpy.AppliedTypeTree(tree, + typed(cpy.AppliedTypeTree(tree)( untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) else { val params = args.asInstanceOf[List[untpd.ValDef]] @@ -491,7 +491,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val protoArgs = args map (_ withType WildcardType) val callProto = FunProto(protoArgs, WildcardType, this) val expr1 = typedExpr(expr, callProto) - fnBody = cpy.Apply(fnBody, untpd.TypedSplice(expr1), args) + fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) expr1.tpe } case _ => @@ -533,7 +533,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (!param.tpt.isEmpty) param else { val paramTpt = untpd.TypeTree(inferredParamType(param, protoFormal(i))) - cpy.ValDef(param, param.mods, param.name, paramTpt, param.rhs) + cpy.ValDef(param)(param.mods, param.name, paramTpt, param.rhs) } // Define result type of closure as the expected type, thereby pushing @@ -569,7 +569,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tp => throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}") } - assignType(cpy.Closure(tree, env1, meth1, target), meth1, target) + assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target) } def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { @@ -612,7 +612,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val guard1 = typedExpr(tree.guard, defn.BooleanType) val body1 = typedExpr(tree.body, pt) - assignType(cpy.CaseDef(tree, pat, guard1, body1), body1) + assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) } val doCase: () => CaseDef = () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) @@ -620,7 +620,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val cases1 = tree.cases mapconserve typedCase - assignType(cpy.Match(tree, sel1, cases1), cases1) + assignType(cpy.Match(tree)(sel1, cases1), cases1) } } @@ -642,25 +642,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val (from, proto) = enclMethInfo(ctx) val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withPos(tree.pos), proto) - assignType(cpy.Return(tree, expr1, from)) + assignType(cpy.Return(tree)(expr1, from)) } def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") { val expr1 = typed(tree.expr, pt) val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt)) val finalizer1 = typed(tree.finalizer, defn.UnitType) - assignType(cpy.Try(tree, expr1, handler1, finalizer1), expr1, handler1) + assignType(cpy.Try(tree)(expr1, handler1, finalizer1), expr1, handler1) } def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Throw = track("typedThrow") { val expr1 = typed(tree.expr, defn.ThrowableType) - assignType(cpy.Throw(tree, expr1)) + assignType(cpy.Throw(tree)(expr1)) } def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") { val proto1 = pt.elemType orElse WildcardType val elems1 = tree.elems mapconserve (typed(_, proto1)) - assignType(cpy.SeqLiteral(tree, elems1), elems1) + assignType(cpy.SeqLiteral(tree)(elems1), elems1) } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { @@ -685,26 +685,26 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } else { val original1 = typed(tree.original) - cpy.TypeTree(tree, original1).withType(original1.tpe) + cpy.TypeTree(tree)(original1).withType(original1.tpe) } } def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) checkStable(ref1.tpe, tree.pos) - assignType(cpy.SingletonTypeTree(tree, ref1), ref1) + assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - assignType(cpy.AndTypeTree(tree, left1, right1), left1, right1) + assignType(cpy.AndTypeTree(tree)(left1, right1), left1, right1) } def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - assignType(cpy.OrTypeTree(tree, left1, right1), left1, right1) + assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1) } def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") { @@ -726,7 +726,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt))) // todo later: check that refinement is within bounds } - val res = cpy.RefinedTypeTree(tree, tpt1, refinements1) withType + val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1) withType (tpt1.tpe /: refinements1)(addRefinement) typr.println(i"typed refinement: ${res.tpe}") res @@ -735,35 +735,34 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): AppliedTypeTree = track("typedAppliedTypeTree") { val tpt1 = typed(tree.tpt) val args1 = tree.args mapconserve (typed(_)) - // todo in later phase: check arguments conform to parameter bounds - assignType(cpy.AppliedTypeTree(tree, tpt1, args1), tpt1, args1) + // check that arguments conform to bounds is done in phase FirstTransform + assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { val result1 = typed(tree.result) - assignType(cpy.ByNameTypeTree(tree, result1), result1) + assignType(cpy.ByNameTypeTree(tree)(result1), result1) } def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree) val lo1 = typed(lo) val hi1 = typed(hi) - // need to do in later phase, as this might cause a cyclic reference error. See pos/t0039.scala - // if (!(lo1.tpe <:< hi1.tpe)) - // ctx.error(d"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) - assignType(cpy.TypeBoundsTree(tree, lo1, hi1), lo1, hi1) + if (!(lo1.tpe <:< hi1.tpe)) + ctx.error(d"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos) + assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) } def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = track("typedBind") { val body1 = typed(tree.body, pt) typr.println(i"typed bind $tree pt = $pt bodytpe = ${body1.tpe}") val sym = ctx.newSymbol(ctx.owner, tree.name.asTermName, EmptyFlags, body1.tpe, coord = tree.pos) - assignType(cpy.Bind(tree, tree.name, body1), sym) + assignType(cpy.Bind(tree)(tree.name, body1), sym) } def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") { val trees1 = tree.trees mapconserve (typed(_, pt)) - assignType(cpy.Alternative(tree, trees1), trees1) + assignType(cpy.Alternative(tree)(trees1), trees1) } def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = { @@ -790,7 +789,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Ident(nme.WILDCARD) => rhs withType tpt1.tpe case _ => typedExpr(rhs, tpt1.tpe) } - assignType(cpy.ValDef(vdef, mods1, name, tpt1, rhs1), sym) + assignType(cpy.ValDef(vdef)(mods1, name, tpt1, rhs1), sym) } def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { @@ -801,7 +800,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) val tpt1 = typedType(tpt) val rhs1 = typedExpr(rhs, tpt1.tpe) - assignType(cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) + assignType(cpy.DefDef(ddef)(mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) //todo: make sure dependent method types do not depend on implicits or by-name params } @@ -809,7 +808,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeDef(mods, name, rhs) = tdef val mods1 = addTypedModifiersAnnotations(mods, sym) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree - assignType(cpy.TypeDef(tdef, mods1, name, TypeTree(sym.info)), sym) + assignType(cpy.TypeDef(tdef)(mods1, name, TypeTree(sym.info), Nil), sym) } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { @@ -842,10 +841,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val dummy = localDummy(cls, impl) val body1 = typedStats(body, dummy)(inClassContext(self1.symbol)) checkNoDoubleDefs(cls) - val impl1 = cpy.Template(impl, constr1, parents1, self1, body1) + val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.termRef) checkVariance(impl1) - assignType(cpy.TypeDef(cdef, mods1, name, impl1), cls) + assignType(cpy.TypeDef(cdef)(mods1, name, impl1, Nil), cls) // todo later: check that // 1. If class is non-abstract, it is instantiatable: @@ -865,7 +864,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.pos) - assignType(cpy.Import(imp, expr1, imp.selectors), sym) + assignType(cpy.Import(imp)(expr1, imp.selectors), sym) } def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { @@ -878,17 +877,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx } val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageContext) - cpy.PackageDef(tree, pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef + cpy.PackageDef(tree)(pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef } def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef) val arg1 = typed(tree.arg, pt) if (ctx.mode is Mode.Type) - assignType(cpy.Annotated(tree, annot1, arg1), annot1, arg1) + assignType(cpy.Annotated(tree)(annot1, arg1), annot1, arg1) else { val tpt = TypeTree(AnnotatedType(Annotation(annot1), arg1.tpe.widen)) - assignType(cpy.Typed(tree, arg1, tpt), tpt) + assignType(cpy.Typed(tree)(arg1, tpt), tpt) } } @@ -1075,7 +1074,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tryEither { implicit ctx => val qual1 = adaptInterpolated(qual, qualProto, EmptyTree) if ((qual eq qual1) || ctx.reporter.hasErrors) tree - else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt) + else typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt) } { (_, _) => tree } case _ => tree @@ -1254,7 +1253,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // but this prevents case blocks from implementing polymorphic partial functions, // since we do not know the result parameter a priori. Have to wait until the // body is typechecked. - return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt) + return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt) case _ => } case _ => diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala index 9ce3ca0b776b..5865f0133f67 100644 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -6,6 +6,7 @@ import core._ import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._ import Decorators._ import Variances._ +import config.Printers.variances /** Provides `check` method to check that all top-level definitions * in tree are variance correct. Does not recurse inside methods. @@ -77,7 +78,7 @@ class VarianceChecker()(implicit ctx: Context) { * explicitly (their TypeDefs will be passed here.) For MethodTypes, the * same is true of the parameters (ValDefs). */ - def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance") { + def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance", variances) { if (status.isDefined) status else tp match { case tp: TypeRef => diff --git a/src/scala/Product0.scala b/src/scala/Product0.scala new file mode 100644 index 000000000000..23e7e52c0256 --- /dev/null +++ b/src/scala/Product0.scala @@ -0,0 +1,24 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ +package scala + +/** A class for Product0 which was missing from the scala distribution. */ +object Product0 { + def unapply(x: Product0): Option[Product0] = + Some(x) +} + +trait Product0 extends Any with Product { + + override def productArity = 0 + + @throws(classOf[IndexOutOfBoundsException]) + override def productElement(n: Int) = + throw new IndexOutOfBoundsException(n.toString()) +} + diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index fa577573a2af..12a2a8cb8efd 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -14,7 +14,7 @@ class tests extends CompilerTest { "-pagewidth", "160") implicit val defaultOptions = noCheckOptions ++ List( - "-Ycheck:tailrec" + "-Ycheck:refchecks,tailrec" ) val twice = List("#runs", "2", "-YnoDoubleBindings") @@ -84,6 +84,7 @@ class tests extends CompilerTest { @Test def neg_tailcall = compileFile(negDir, "tailcall/tailrec", xerrors = 7) @Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2) @Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2) + @Test def nef_t1279a = compileFile(negDir, "t1279a", xerrors = 1) @Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1) @Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1) @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2) diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index f38706d671e9..c628a9ad15a6 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -42,29 +42,29 @@ class DeSugarTest extends ParserTest { case PostfixOp(od, op) => PostfixOp(transform(od), op) case Select(qual, name) => - cpy.Select(tree1, transform(qual, Expr), name) + cpy.Select(tree1)(transform(qual, Expr), name) case Apply(fn, args) => - cpy.Apply(tree1, transform(fn, Expr), transform(args)) + cpy.Apply(tree1)(transform(fn, Expr), transform(args)) case TypeApply(fn, args) => - cpy.TypeApply(tree1, transform(fn, Expr), transform(args, Type)) + cpy.TypeApply(tree1)(transform(fn, Expr), transform(args, Type)) case New(tpt) => - cpy.New(tree1, transform(tpt, Type)) + cpy.New(tree1)(transform(tpt, Type)) case Typed(expr, tpt) => - cpy.Typed(tree1, transform(expr), transform(tpt, Type)) + cpy.Typed(tree1)(transform(expr), transform(tpt, Type)) case CaseDef(pat, guard, body) => - cpy.CaseDef(tree1, transform(pat, Pattern), transform(guard), transform(body)) + cpy.CaseDef(tree1)(transform(pat, Pattern), transform(guard), transform(body)) case SeqLiteral(elems) => - cpy.SeqLiteral(tree1, transform(elems)) + cpy.SeqLiteral(tree1)(transform(elems)) case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree1, transform(fun, Expr), transform(implicits), transform(patterns)) + cpy.UnApply(tree1)(transform(fun, Expr), transform(implicits), transform(patterns)) case ValDef(mods, name, tpt, rhs) => - cpy.ValDef(tree1, mods, name, transform(tpt, Type), transform(rhs)) + cpy.ValDef(tree1)(mods, name, transform(tpt, Type), transform(rhs)) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - cpy.DefDef(tree1, mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(rhs)) + cpy.DefDef(tree1)(mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(rhs)) case tree1 @ TypeDef(mods, name, rhs) => - cpy.TypeDef(tree1, mods, name, transform(rhs, Type), transformSub(tree1.tparams)) + cpy.TypeDef(tree1)(mods, name, transform(rhs, Type), transformSub(tree1.tparams)) case Template(constr, parents, self, body) => - cpy.Template(tree1, transformSub(constr), transform(parents), transformSub(self), transform(body, Expr)) + cpy.Template(tree1)(transformSub(constr), transform(parents), transformSub(self), transform(body, Expr)) case Thicket(trees) => Thicket(flatten(trees mapConserve super.transform)) case tree1 => diff --git a/test/test/transform/TreeTransformerTest.scala b/test/test/transform/TreeTransformerTest.scala index 06257b48b3a5..bfabc2f2c732 100644 --- a/test/test/transform/TreeTransformerTest.scala +++ b/test/test/transform/TreeTransformerTest.scala @@ -3,7 +3,7 @@ package test.transform import org.junit.{Assert, Test} import test.DottyTest -import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context @@ -15,7 +15,7 @@ class TreeTransformerTest extends DottyTest { def shouldReturnSameTreeIfUnchanged = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class EmptyTransform extends TreeTransform { + class EmptyTransform extends MiniPhaseTransform { override def name: String = "empty" init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) } @@ -35,7 +35,7 @@ class TreeTransformerTest extends DottyTest { def canReplaceConstant = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class ConstantTransform extends TreeTransform { + class ConstantTransform extends MiniPhaseTransform { override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(2)) override def name: String = "canReplaceConstant" @@ -57,7 +57,7 @@ class TreeTransformerTest extends DottyTest { def canOverwrite = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class Transformation extends TreeTransform { + class Transformation extends MiniPhaseTransform { override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(-1)) override def name: String = "canOverwrite" @@ -66,7 +66,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-1))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -88,32 +88,32 @@ class TreeTransformerTest extends DottyTest { def transformationOrder = checkCompile("frontend", "class A{ val d = 1}") { (tree, context) => implicit val ctx = context - class Transformation1 extends TreeTransform { + class Transformation1 extends MiniPhaseTransform { override def name: String = "transformationOrder1" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { Assert.assertTrue("correct constant", tree.const.toString == "Constant(1)" ) - tpd.cpy.Literal(tree, Constant(-1)) + tpd.cpy.Literal(tree)(Constant(-1)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-1))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) } - class Transformation2 extends TreeTransform { + class Transformation2 extends MiniPhaseTransform { override def name: String = "transformationOrder2" override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(2))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -135,14 +135,14 @@ class TreeTransformerTest extends DottyTest { (tree, context) => implicit val ctx = context var transformed1 = 0 - class Transformation1 extends TreeTransform { + class Transformation1 extends MiniPhaseTransform { override def name: String = "invocationCount1" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { transformed1 += 1 Assert.assertTrue("correct constant", tree.const.toString == "Constant(1)" ) - tpd.cpy.Literal(tree, Constant(-1)) + tpd.cpy.Literal(tree)(Constant(-1)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -150,13 +150,13 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-3))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, transformFollowing(tpd.Literal(Constant(2)))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, transformFollowing(tpd.Literal(Constant(2)))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) } var transformed2 = 0 - class Transformation2 extends TreeTransform { + class Transformation2 extends MiniPhaseTransform { var constantsSeen = 0 override def name: String = "invocationCount2" override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { @@ -173,7 +173,7 @@ class TreeTransformerTest extends DottyTest { case _ => Assert.fail("to many constants seen") } constantsSeen += 1 - tpd.cpy.Literal(tree, Constant(-3)) + tpd.cpy.Literal(tree)(Constant(-3)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -181,7 +181,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-3))" ) - transformFollowing(tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3)))) + transformFollowing(tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3)))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala new file mode 100644 index 000000000000..4eaec125f4cf --- /dev/null +++ b/tests/neg/cycles.scala @@ -0,0 +1,4 @@ +class Foo[T <: U, U <: T] + +class Bar[T >: T] + diff --git a/tests/pos/t1279a.scala b/tests/neg/t1279a.scala similarity index 92% rename from tests/pos/t1279a.scala rename to tests/neg/t1279a.scala index 18b1e53f463e..6d768d43544b 100644 --- a/tests/pos/t1279a.scala +++ b/tests/neg/t1279a.scala @@ -1,10 +1,10 @@ // covariant linked list abstract class M { - self => + self: M => type T final type selfType = M {type T <: self.T} - type actualSelfType >: self.type <: selfType + type actualSelfType >: self.type <: selfType // this no longer compiles because self.type is not a subtype of selfType def next: selfType diff --git a/tests/pos/desugar.scala b/tests/pos/desugar.scala index f0d8645b7830..0d3b6d8ca624 100644 --- a/tests/pos/desugar.scala +++ b/tests/pos/desugar.scala @@ -6,11 +6,11 @@ object desugar { val list = List(1, 2, 3) { var z: Int = y } - + def foo0(first: Int, second: Int = 2, third: Int = 3) = first + second def foo1(first: Int, second: Int = 2)(third: Int = 3) = first + second def foo2(first: Int)(second: Int = 2)(third: Int = 3) = first + second - + object caseClasses { self => trait List[+T] { def head: T @@ -23,34 +23,37 @@ object desugar { def apply[T](head: T): Cons[T] = apply(head, Nil) } - case object Nil extends List[Nothing] + case object Nil extends List[Nothing] { + def head = throw new Error() + def tail = throw new Error() + } } - + object patDefs { - + import caseClasses._ val xs: List[Int] = Cons(1, Cons(2, Nil)) - - val Cons(y, ys) = xs + + val Cons(y, ys) = xs val Cons(z, _) = xs val Cons(_, _) = xs - + val (cons: Cons[Int]) = xs - + val x1, y1, z1: Int = 1 } - + object Binops { - + x :: y :: Nil - + val x :: y :: Nil = list - + } - + object fors { - + for (x <- List(1, 2, 3)) yield 2 for (x <- List(1, 2, 3) if x % 2 == 0) yield x * x for (x <- List(1, 2, 3); y <- 0 to x) yield x * y @@ -65,7 +68,7 @@ object desugar { for (x <- List(1, 2, 3); y = x * x; if x + y % 2 == 0) println(x * y) for (x <- List(1, 2, 3); y = x * x; z = x * y; u <- 0 to y) println(x * y * z * u) } - + object misc { 'hello s"this is a $x + ${x + y} string" @@ -82,4 +85,4 @@ object desugar { do x -= 1 while (x > 0) } -} \ No newline at end of file +} diff --git a/tests/pos/hk.scala b/tests/pos/hk.scala index 461c6e386ae1..9fdaf94f6ca8 100644 --- a/tests/pos/hk.scala +++ b/tests/pos/hk.scala @@ -2,7 +2,7 @@ import language.higherKinds object hk0 { - class Base { + abstract class Base { type Rep[T] val strRep: Rep[String] } @@ -13,7 +13,7 @@ object hk0 { val sr: Rep[String] = "" } - class Functor[F[_]] { + abstract class Functor[F[_]] { def map[A, B](f: A => B): F[A] => F[B] } val ml: Functor[List] = ??? @@ -53,4 +53,4 @@ object higherKinded { tree1: Tree[String] } -} \ No newline at end of file +} diff --git a/tests/pos/inferred.scala b/tests/pos/inferred.scala index 525848541280..87bbd94730b8 100644 --- a/tests/pos/inferred.scala +++ b/tests/pos/inferred.scala @@ -1,13 +1,13 @@ -class LIST[+T] { - +abstract class LIST[+T] { + def isEmpty: Boolean def head: T def tail: LIST[T] - + def prepend [U >: T] (x: U): LIST[U] = new CONS(x, this) - + def map[U](f: T => U): LIST[U] = if (isEmpty) NIL else tail.map(f).prepend(f(head)) - + } object NIL extends LIST[Nothing] { @@ -37,22 +37,22 @@ object Inferred { val nn = bar(NIL) val ints: LIST[Int] = NIL prepend 1 - + val ints1 = NIL prepend 1 prepend 2 val a = if (1 == 0) NIL else ints - + val n2 = scala.collection.immutable.Nil - + val ss2: scala.collection.immutable.List[String] = "abc" :: n2 - + val ss3 = "abc" :: n2 - + def cl = ((x: Int) => x + 1) - + val ints2 = ints map (_ + 1) - + val ints3 = new CONS[Int](1, NIL) - + val ints4 = new CONS(1, NIL) } \ No newline at end of file diff --git a/tests/pos/opassign.scala b/tests/pos/opassign.scala index 7b8fec652038..8f6cad903a27 100644 --- a/tests/pos/opassign.scala +++ b/tests/pos/opassign.scala @@ -1,28 +1,28 @@ object opassign { - + var count: Int = 0 def next = { count += 1; count } - + var x: Int = 0 x += 1 - + { var x: Int = 0 x += 1 } - + class Ref { - var x: Int + var x: Int = _ } val r = new Ref r.x += 1 - + val arr = new Array[Int](10) arr(0) += 1 - + def f(x: Int): Ref = new Ref f(next).x += 1 - + val buf = new collection.mutable.ListBuffer[Int] buf += 1 -} \ No newline at end of file +} diff --git a/tests/pos/t1832.scala b/tests/pos/t1832.scala index 9ad9703c2955..c34fe4bfa067 100644 --- a/tests/pos/t1832.scala +++ b/tests/pos/t1832.scala @@ -2,7 +2,7 @@ trait Cloning { trait Foo def fn(g: Any => Unit): Foo - class Star { def *(a: Cloning.this.Foo): Cloning.this.Foo } + abstract class Star { def *(a: Cloning.this.Foo): Cloning.this.Foo } implicit def mkStar(i: Int): Star = new Star { def *(a: Foo): Foo = null } diff --git a/tests/pos/tycons.scala b/tests/pos/tycons.scala index f138c78be50e..ef16a77922ab 100644 --- a/tests/pos/tycons.scala +++ b/tests/pos/tycons.scala @@ -12,11 +12,11 @@ object obj extends List[Number] with Set[Exception] { val e: Exception = x } -class Functor[F <: TypeConstructor] { +abstract class Functor[F <: TypeConstructor] { def map[A, B](f: F { type TypeArg <: A }): F { type TypeArg <: B } } implicit object ListFunctor extends Functor[List] { - def map[A, B](f: List[A]): List[B] = ??? + override def map[A, B](f: List { type TypeArg <: A }): List { type TypeArg <: B } = ??? } diff --git a/tests/pos/typers.scala b/tests/pos/typers.scala index b2d786c1c9b1..fe11ca6021be 100644 --- a/tests/pos/typers.scala +++ b/tests/pos/typers.scala @@ -88,7 +88,7 @@ object typers { } class Refinements { - val y: C { type T; val key: T; def process(x: T): Int } + val y: C { type T; val key: T; def process(x: T): Int } = ??? } object Accessibility {