diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 258cab36889a..1aa1cce1073d 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -24,17 +24,6 @@ class Compiler { * all refs to it would become outdated - they could not be dereferenced in the * new phase. * - * As an example, addGetters would change a field - * - * val x: T - * - * to a method - * - * def x: T - * - * but this would affect the signature of `x` (goes from NotAMethod to a method - * signature). So we can't do this before erasure. - * * After erasure, signature changing denot-transformers are OK because erasure * will make sure that only term refs with fixed SymDenotations survive beyond it. This * is possible because: @@ -57,13 +46,15 @@ class Compiler { new TailRec), List(new PatternMatcher, new ExplicitOuter, - new LazyValsTransform, + // new LazyValTranformContext().transformer, // disabled, awaiting fixes new Splitter), List(new ElimByName, new InterceptedMethods, - new Literalize), + new Literalize, + new GettersSetters), List(new Erasure), - List(new CapturedVars) + List(new CapturedVars, new Constructors)/*, + List(new LambdaLift)*/ ) var runId = 1 diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala index 20afc8eb4dc4..d18ad0288f9c 100644 --- a/src/dotty/tools/dotc/ElimLocals.scala +++ b/src/dotty/tools/dotc/ElimLocals.scala @@ -19,4 +19,4 @@ class ElimLocals extends MiniPhaseTransform with SymTransformer { thisTransforme private def dropLocal(ref: SymDenotation)(implicit ctx: Context) = if (ref.flags is Local) ref.copySymDenotation(initFlags = ref.flags &~ Local) else ref -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala index 71f669e26717..d7ccc1ae4e11 100644 --- a/src/dotty/tools/dotc/Flatten.scala +++ b/src/dotty/tools/dotc/Flatten.scala @@ -12,4 +12,4 @@ class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer = override def phaseName = "flatten" def transformSym(ref: SymDenotation)(implicit ctx: Context) = ??? -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index 1212c9e4dac8..50aaafc82279 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -31,7 +31,7 @@ object TypeErasure { */ def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeRef => - tp.symbol.isClass + tp.symbol.isClass && tp.symbol != defn.AnyClass case _: TermRef => true case JavaArrayType(elem) => @@ -87,15 +87,36 @@ object TypeErasure { private val javaSigFn = erasureFn(isJava = true, isSemi = false, isConstructor = false, wildcardOK = true) private val semiErasureFn = erasureFn(isJava = false, isSemi = true, isConstructor = false, wildcardOK = false) - def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp) - def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp) + /** The current context with a phase no later than erasure */ + private def erasureCtx(implicit ctx: Context) = + if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) else ctx + + def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx) + def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx) def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = { val normTp = if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass) else tp - (if (isJava) javaSigFn else scalaSigFn).sigName(normTp) + (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx) + } + + /** The erasure of a top-level reference. Differs from normal erasure in that + * TermRefs are kept instead of being widened away. + */ + def erasedRef(tp: Type)(implicit ctx: Context): Type = tp match { + case tp: TermRef => + assert(tp.symbol.exists, tp) + TermRef(erasedRef(tp.prefix), tp.symbol.asTerm) + case tp => + erasure(tp) } + /** The erasure of a function result type. Differs from normal erasure in that + * Unit is kept instead of being mapped to BoxedUnit. + */ + def eraseResult(tp: Type)(implicit ctx: Context): Type = + scalaErasureFn.eraseResult(tp)(erasureCtx) + /** The symbol's erased info. This is the type's erasure, except for the following symbols: * * - For $asInstanceOf : [T]T @@ -113,8 +134,8 @@ object TypeErasure { if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) - else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)) - else erase(tp) + else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx)) + else erase(tp)(erasureCtx) } def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( @@ -147,7 +168,7 @@ object TypeErasure { def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match { case bc :: bcs1 => if (cls2.derivesFrom(bc)) - if (!bc.is(Trait)) bc + if (!bc.is(Trait) && bc != defn.AnyClass) bc else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc) else loop(bcs1, bestSoFar) @@ -225,7 +246,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild * - For NoType or NoPrefix, the type itself. * - For any other type, exception. */ - def apply(tp: Type)(implicit ctx: Context): Type = tp match { + private def apply(tp: Type)(implicit ctx: Context): Type = tp match { case tp: TypeRef => val sym = tp.symbol if (!sym.isClass) this(tp.info) @@ -236,8 +257,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild if (parent isRef defn.ArrayClass) eraseArray(tp) else this(parent) case tp: TermRef => - assert(tp.symbol.exists, tp) - TermRef(NoPrefix, tp.symbol.asTerm) + this(tp.widen) case ThisType(_) | SuperType(_, _) => tp case ExprType(rt) => @@ -269,7 +289,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild val parents: List[TypeRef] = if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) - tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType)) + val erasedDecls = decls.filteredScope(d => !d.isType || d.isClass) + tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } case NoType | NoPrefix | ErrorType | JavaArrayType(_) => @@ -278,7 +299,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild tp } - def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { val defn.ArrayType(elemtp) = tp if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType) else if (isUnboundedGeneric(elemtp)) @@ -327,7 +348,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild /** The name of the type as it is used in `Signature`s. * Need to ensure correspondence with erasure! */ - def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match { + private def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match { case tp: TypeRef => val sym = tp.symbol if (!sym.isClass) sigName(tp.info) @@ -337,8 +358,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild sigName(this(tp)) case JavaArrayType(elem) => sigName(elem) ++ "[]" - case tp: TypeBounds => - sigName(tp.hi) + case tp: TermRef => + sigName(tp.widen) case ExprType(rt) => sigName(defn.FunctionType(Nil, rt)) case tp: TypeProxy => diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index d7bbdf2bb386..dba3872cb127 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -90,7 +90,7 @@ object desugar { def valDef(vdef: ValDef)(implicit ctx: Context): Tree = { val ValDef(mods, name, tpt, rhs) = vdef def setterNeeded = - (mods is Mutable) && ctx.owner.isClass && (!(mods is Private) || (ctx.owner is Trait)) + (mods is Mutable) && ctx.owner.isClass && (!(mods is PrivateLocal) || (ctx.owner is Trait)) if (setterNeeded) { // todo: copy of vdef as getter needed? // val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ? diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala index 56602cca91e1..33be848c1ed1 100644 --- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -6,6 +6,7 @@ import core._ import Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._ import Denotations._, Decorators._ +import dotty.tools.dotc.transform.SymUtils._ /** A map that applies three functions and a substitution together to a tree and * makes sure they are coordinated so that the result is well-typed. The functions are @@ -42,13 +43,7 @@ final class TreeTypeMap( import tpd._ /** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */ - def mapOwner(sym: Symbol) = { - def loop(from: List[Symbol], to: List[Symbol]): Symbol = - if (from.isEmpty) sym - else if (sym eq from.head) to.head - else loop(from.tail, to.tail) - loop(oldOwners, newOwners) - } + def mapOwner(sym: Symbol) = sym.subst(oldOwners, newOwners) /** Replace occurrences of `This(oldOwner)` in some prefix of a type * by the corresponding `This(newOwner)`. diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index b63f0ad8cc03..f1ccfdb75e3e 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -408,7 +408,6 @@ object Trees { abstract class NameTree[-T >: Untyped] extends DenotingTree[T] { type ThisTree[-T >: Untyped] <: NameTree[T] def name: Name - def withName(name1: Name)(implicit ctx: Context): untpd.NameTree } /** Tree refers by name to a denotation */ @@ -449,7 +448,6 @@ 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 qualifier: Tree[T] = genericEmptyTree } @@ -460,7 +458,6 @@ 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) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -656,7 +653,6 @@ 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) } /** left & right */ @@ -702,7 +698,6 @@ 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) } /** tree_1 | ... | tree_n */ @@ -734,7 +729,6 @@ 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)(name = name.toTermName) assert(isEmpty || tpt != genericEmptyTree) } @@ -742,7 +736,6 @@ 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)(name = name.toTermName) assert(tpt != genericEmptyTree) } @@ -754,7 +747,6 @@ 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)(name = name.toTypeName) /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] @@ -820,7 +812,7 @@ object Trees { } class EmptyValDef[T >: Untyped] extends ValDef[T]( - Modifiers[T](Private), nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] { + Modifiers[T](PrivateLocal), nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] { override def isEmpty: Boolean = true } @@ -1344,6 +1336,7 @@ object Trees { abstract class TreeTraverser extends TreeAccumulator[Unit] { def traverse(tree: Tree): Unit def apply(x: Unit, tree: Tree) = traverse(tree) + protected def traverseChildren(tree: Tree) = foldOver((), tree) } /** Fold `f` over all tree nodes, in depth-first, prefix order */ @@ -1361,6 +1354,19 @@ object Trees { else foldOver(x1, tree) } } + + def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[T] = { + tree match { + case tree: Ident => cpy.Ident(tree)(newName) + case tree: Select => cpy.Select(tree)(tree.qualifier, newName) + case tree: Bind => cpy.Bind(tree)(newName, tree.body) + case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName) + case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName) + case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(tree.mods, newName.asTypeName, tree.tparams, tree.rhs) + case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName) + case tree: SelectFromTypeTree => cpy.SelectFromTypeTree(tree)(tree.qualifier, newName) + } + }.asInstanceOf[tree.ThisTree[T]] } } // ----- Helper functions and classes --------------------------------------- diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 3fa08b9c6a6f..9b7c9cbae810 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -2,8 +2,8 @@ package dotty.tools package dotc package ast +import transform.SymUtils._ import core._ -import dotty.tools.dotc.transform.TypeUtils import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ import Denotations._, Decorators._ @@ -44,9 +44,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = ta.assignType(untpd.Apply(fn, args), fn, args) - def ensureApplied(fn: Tree)(implicit ctx: Context): Tree = - if (fn.tpe.widen.isParameterless) fn else Apply(fn, Nil) - def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = ta.assignType(untpd.TypeApply(fn, args), fn, args) @@ -358,7 +355,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) } - private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { def apply(sym: Symbol, tree: Tree) = if (sym.exists) sym @@ -565,15 +561,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree = ((tree: Tree) /: argss)(Apply(_, _)) - def appliedToNone(implicit ctx: Context): Tree = appliedToArgs(Nil) - - def appliedIfMethod(implicit ctx: Context): Tree = { - tree.tpe.widen match { - case fntpe: MethodType => appliedToArgs(Nil) - case _ => tree - } - } - + def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) def appliedToType(targ: Type)(implicit ctx: Context): Tree = appliedToTypes(targ :: Nil) @@ -584,6 +572,9 @@ 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 ensureApplied(implicit ctx: Context): Tree = + if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone + def isInstance(tp: Type)(implicit ctx: Context): Tree = tree.select(defn.Any_isInstanceOf).appliedToType(tp) @@ -601,6 +592,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def or(that: Tree)(implicit ctx: Context): Tree = tree.select(defn.Boolean_||).appliedTo(that) + def becomes(rhs: Tree)(implicit ctx: Context): Tree = + if (tree.symbol is Method) { + val setr = tree match { + case Ident(_) => + val setter = tree.symbol.setter + assert(setter.exists, tree.symbol.showLocated) + ref(tree.symbol.setter) + case Select(qual, _) => qual.select(tree.symbol.setter) + } + setr.appliedTo(rhs) + } + else Assign(tree, rhs) + // --- Higher order traversal methods ------------------------------- def foreachSubTree(f: Tree => Unit): Unit = { //TODO should go in tpd. @@ -627,7 +631,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val mname = ("to" + numericCls.name).toTermName val conversion = tree.tpe member mname if (conversion.symbol.exists) - ensureApplied(tree.select(conversion.symbol.termRef)) + tree.select(conversion.symbol.termRef).ensureApplied else if (tree.tpe.widen isRef numericCls) tree else { @@ -655,6 +659,22 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) } + /** A traverser that passes the enlcosing class or method as an argumenr + * to the traverse method. + */ + abstract class EnclosingMethodTraverser(implicit ctx: Context) extends TreeAccumulator[Symbol] { + def traverse(enclMeth: Symbol, tree: Tree): Unit + def apply(enclMeth: Symbol, tree: Tree) = { + tree match { + case _: DefTree if tree.symbol.exists => + traverse(tree.symbol.enclosingMethod, tree) + case _ => + traverse(enclMeth, tree) + } + enclMeth + } + } + // 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 9ac01df9cc93..5a6c9fa89012 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -58,9 +58,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree 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) - } + extends TypeDef(mods, name, rhs) // ----- TypeTrees that refer to other tree's symbols ------------------- @@ -210,7 +208,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { makeConstructor(Modifiers(), Nil, Nil) def makeSelfDef(name: TermName, tpt: Tree)(implicit ctx: Context) = - ValDef(Modifiers(Private), name, tpt, EmptyTree) + ValDef(Modifiers(PrivateLocal), name, tpt, EmptyTree) def makeTupleOrParens(ts: List[Tree])(implicit ctx: Context) = ts match { case t :: Nil => Parens(t) @@ -414,4 +412,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { super.foldOver(x, tree) } } + + override def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[Untyped] = tree match { + case t: PolyTypeDef => + cpy.PolyTypeDef(t)(t.mods, newName.asTypeName, t.tparams, t.rhs).asInstanceOf[tree.ThisTree[Untyped]] + case _ => super.rename(tree, newName) + } } diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 6b9b1dec7a08..3b14872b7a7e 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -282,7 +282,7 @@ object Contexts { * from constructor parameters to class paramater accessors. */ def superCallContext: Context = { - val locals = newScopeWith(owner.decls.filter(_ is ParamAccessor).toList: _*) + val locals = newScopeWith(owner.asClass.paramAccessors: _*) superOrThisCallContext(owner.primaryConstructor, locals) } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index b74506caf45a..b1c2baff6cd7 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -4,7 +4,6 @@ package core import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._ import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._ -import TypeApplications._ import pickling.UnPickler.ensureConstructor import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } @@ -192,6 +191,7 @@ class Definitions { 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) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 9970c5948274..82fd60fa0cef 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -603,26 +603,26 @@ object Denotations { */ protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { val targetId = phase.next.id - assert(ctx.phaseId == targetId, - s"denotation update for $this called in phase ${ctx.phase}, expected was ${phase.next}") - val current = symbol.current - // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") - // printPeriods(current) - this.nextInRun = current.nextInRun - this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) - if (current.validFor.firstPhaseId == targetId) { - // replace current with this denotation - var prev = current - while (prev.nextInRun ne current) prev = prev.nextInRun - prev.nextInRun = this - current.validFor = Nowhere - } + if (ctx.phaseId != targetId) installAfter(phase)(ctx.withPhase(phase.next)) else { - // insert this denotation after current - current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) - current.nextInRun = this - } + val current = symbol.current + // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}") + // printPeriods(current) + this.nextInRun = current.nextInRun + this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId) + if (current.validFor.firstPhaseId == targetId) { + // replace current with this denotation + var prev = current + while (prev.nextInRun ne current) prev = prev.nextInRun + prev.nextInRun = this + current.validFor = Nowhere + } else { + // insert this denotation after current + current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1) + current.nextInRun = this + } // printPeriods(this) + } } def staleSymbolError(implicit ctx: Context) = { @@ -681,6 +681,7 @@ object Denotations { // ------ PreDenotation ops ---------------------------------------------- final def first = this + final def last = this final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym) final def containsSig(sig: Signature)(implicit ctx: Context) = @@ -766,8 +767,9 @@ object Denotations { /** A denotation in the group exists */ def exists: Boolean - /** First denotation in the group */ + /** First/last denotation in the group */ def first: Denotation + def last: Denotation /** Convert to full denotation by &-ing all elements */ def toDenot(pre: Type)(implicit ctx: Context): Denotation @@ -832,6 +834,7 @@ object Denotations { assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)") def exists = true def first = denots1.first + def last = denots2.last def toDenot(pre: Type)(implicit ctx: Context) = (denots1 toDenot pre) & (denots2 toDenot pre, pre) def containsSym(sym: Symbol) = diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index e34483f8f75f..a27dd6614fac 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -421,7 +421,7 @@ object Flags { /** Flags representing source modifiers */ final val SourceModifierFlags = commonFlags(Private, Protected, Abstract, Final, - Sealed, Case, Implicit, Override, AbsOverride, Lazy) + Sealed, Case, Implicit, Override, AbsOverride, Lazy, Static) /** Flags representing modifiers that can appear in trees */ final val ModifierFlags = @@ -459,6 +459,9 @@ object Flags { /** Module classes always have these flags set */ final val ModuleClassCreationFlags = ModuleClass | Final + /** Accessors always have these flags set */ + final val AccessorCreationFlags = Method | Accessor + /** The flags of the self symbol */ final val SelfSymFlags = Private | Local | Deferred @@ -526,7 +529,7 @@ object Flags { final val HasDefaultParams = DefaultParameterized | InheritedDefaultParams /** Is valid forever */ - final val ValidForever = Package | Permanent + final val ValidForever = Package | Permanent | Scala2ExistentialCommon /** Is a default parameter in Scala 2*/ final val DefaultParameter = allOf(Param, DefaultParameterized) @@ -543,12 +546,15 @@ object Flags { /** Labeled private[this] */ final val PrivateLocal = allOf(Private, Local) - /** A private parameter accessor */ + /** A private[this] parameter accessor */ final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor) - /** A private parameter */ + /** A private[this] parameter */ final val PrivateLocalParam = allOf(Private, Local, Param) + /** A private parameter accessor */ + final val PrivateParamAccessor = allOf(Private, ParamAccessor) + /** A local parameter */ final val ParamAndLocal = allOf(Param, Local) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index b18f708edabd..beb3142d3876 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -68,10 +68,11 @@ object NameOps { def isProtectedAccessorName = name startsWith PROTECTED_PREFIX def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER def isSetterName = name endsWith SETTER_SUFFIX - def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_SEPARATOR) + def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_PREFIX) def isSingletonName = name endsWith SINGLETON_SUFFIX def isModuleClassName = name endsWith MODULE_SUFFIX def isImportName = name startsWith IMPORT + def isFieldName = name endsWith LOCAL_SUFFIX def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED) def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 @@ -223,23 +224,36 @@ object NameOps { implicit class TermNameDecorator(val name: TermName) extends AnyVal { import nme._ - /** The expanded setter name of `name` relative to this class `base` - */ - def expandedSetterName(base: Symbol)(implicit ctx: Context): TermName = - name.expandedName(base, separator = TRAIT_SETTER_SEPARATOR) + def traitSetterName: TermName = + nme.TRAIT_SETTER_PREFIX ++ setterName + + def setterName: TermName = + if (name.isFieldName) name.fieldToGetter.setterName + else name ++ SETTER_SUFFIX + + def getterName: TermName = + if (name.isFieldName) fieldToGetter + else setterToGetter - def setterName: TermName = name ++ SETTER_SUFFIX + def fieldName: TermName = + if (name.isSetterName) getterName.fieldName + else name ++ LOCAL_SUFFIX - def setterToGetter: TermName = { - val p = name.indexOfSlice(TRAIT_SETTER_SEPARATOR) + private def setterToGetter: TermName = { + val p = name.indexOfSlice(TRAIT_SETTER_PREFIX) if (p >= 0) - (name drop (p + TRAIT_SETTER_SEPARATOR.length)).asTermName.setterToGetter + (name drop (p + TRAIT_SETTER_PREFIX.length)).asTermName.getterName else { assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") name.take(name.length - SETTER_SUFFIX.length).asTermName } } + def fieldToGetter: TermName = { + assert(name.isFieldName) + name.take(name.length - LOCAL_SUFFIX.length).asTermName + } + /** Nominally, name$default$N, encoded for * @param Post the parameters position. * @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1 diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 5eb8cd920b18..7b589fec1e0e 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -9,7 +9,7 @@ import Denotations._ import config.Printers._ import scala.collection.mutable.{ListBuffer, ArrayBuffer} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform} -import dotty.tools.dotc.transform.{ExplicitOuter, TreeTransforms, Erasure, Flatten} +import dotty.tools.dotc.transform.{TreeTransforms, ExplicitOuter, Erasure, Flatten, GettersSetters} import Periods._ import typer.{FrontEnd, RefChecks} import ast.tpd @@ -169,12 +169,14 @@ object Phases { private val erasureCache = new PhaseCache(classOf[Erasure]) private val flattenCache = new PhaseCache(classOf[Flatten]) private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter]) + private val gettersSettersCache = new PhaseCache(classOf[GettersSetters]) def typerPhase = typerCache.phase def refchecksPhase = refChecksCache.phase def erasurePhase = erasureCache.phase def flattenPhase = flattenCache.phase - def explicitOuter = explicitOuterCache.phase + def explicitOuterPhase = explicitOuterCache.phase + def gettersSettersPhase = gettersSettersCache.phase def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id } @@ -200,6 +202,11 @@ object Phases { */ def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = () + /** If set, allow missing or superfluous arguments in applications + * and type applications. + */ + def relaxedTyping: Boolean = false + def exists: Boolean = true private var myPeriod: Period = Periods.InvalidPeriod diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala index 54317a4962c8..3f7a4cb2ca20 100644 --- a/src/dotty/tools/dotc/core/Scopes.scala +++ b/src/dotty/tools/dotc/core/Scopes.scala @@ -106,7 +106,10 @@ object Scopes { def next(): Symbol = { val r = e.sym; e = lookupNextEntry(e); r } } - /** The denotation set of all the symbols with given name in this scope */ + /** The denotation set of all the symbols with given name in this scope + * Symbols occur in the result in reverse order relative to their occurrence + * in `this.toList`. + */ final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(implicit ctx: Context): PreDenotation = { var syms: PreDenotation = NoDenotation var e = lookupEntry(name) @@ -118,6 +121,20 @@ object Scopes { syms } + /** The scope that keeps only those symbols from this scope that match the + * given predicates. If all symbols match, returns the scope itself, otherwise + * a copy with the matching symbols. + */ + final def filteredScope(p: Symbol => Boolean)(implicit ctx: Context): Scope = { + var result: MutableScope = null + for (sym <- iterator) + if (!p(sym)) { + if (result == null) result = cloneScope + result.unlink(sym) + } + if (result == null) this else result + } + def implicitDecls(implicit ctx: Context): List[TermRef] = Nil final def toText(printer: Printer): Text = printer.toText(this) diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index 4f2fe9dba3b9..f7354a8b423d 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -119,7 +119,7 @@ object StdNames { val SINGLETON_SUFFIX: N = ".type" val SPECIALIZED_SUFFIX: N = "$sp" val SUPER_PREFIX: N = "super$" - val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" + val TRAIT_SETTER_PREFIX: N = "_setter_$" val WHILE_PREFIX: N = "while$" // value types (and AnyRef) are all used as terms as well @@ -226,7 +226,7 @@ object StdNames { val LAZY_LOCAL: N = "$lzy" val LAZY_FIELD_OFFSET: N = "OFFSET$" val LAZY_SLOW_SUFFIX: N = "$lzycompute" - val LOCAL_SUFFIX: N = " " + val LOCAL_SUFFIX: N = "$$local" val UNIVERSE_BUILD_PREFIX: N = "$u.build." val UNIVERSE_BUILD: N = "$u.build" val UNIVERSE_PREFIX: N = "$u." @@ -685,15 +685,6 @@ object StdNames { def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString def selectorName(n: Int): TermName = "_" + (n + 1) - /** Is name a variable name? */ - def isVariableName(name: Name): Boolean = { - val first = name.firstChar - ( ((first.isLower && first.isLetter) || first == '_') - && (name != nme.false_) - && (name != nme.true_) - && (name != nme.null_) - ) - } object primitive { val arrayApply: TermName = "[]apply" diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 073c9112d3d6..ae37ab87cdeb 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -43,14 +43,10 @@ trait SymDenotations { this: Context => if (denot is ValidForever) true else try { val owner = denot.owner.denot - def isSelfSym = owner.infoOrCompleter match { - case ClassInfo(_, _, _, _, selfInfo) => selfInfo == denot.symbol - case _ => false - } stillValid(owner) && ( !owner.isClass || (owner.decls.lookupAll(denot.name) contains denot.symbol) - || isSelfSym + || denot.isSelfSym ) } catch { case ex: StaleSymbol => false @@ -200,7 +196,7 @@ object SymDenotations { final def hasAnnotation(cls: Symbol)(implicit ctx: Context) = dropOtherAnnotations(annotations, cls).nonEmpty - /** Optionally, get annotation matching the given class symbol */ + /** Optionally, the annotation matching the given class symbol */ final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] = dropOtherAnnotations(annotations, cls) match { case annot :: _ => Some(annot) @@ -360,6 +356,23 @@ object SymDenotations { /** Is this symbol an abstract or alias type? */ final def isAbstractOrAliasType = isType & !isClass + /** Is this the denotation of a self symbol of some class? + * This is the case if one of two conditions holds: + * 1. It is the symbol referred to in the selfInfo part of the ClassInfo + * which is the type of this symbol's owner. + * 2. This symbol is owned by a class, it's selfInfo field refers to a type + * (indicating the self definition does not introduce a name), and the + * symbol's name is "_". + * TODO: Find a more robust way to characterize self symbols, maybe by + * spending a Flag on them? + */ + final def isSelfSym(implicit ctx: Context) = owner.infoOrCompleter match { + case ClassInfo(_, _, _, _, selfInfo) => + selfInfo == symbol || + selfInfo.isInstanceOf[Type] && name == nme.WILDCARD + case _ => false + } + /** Is this definition contained in `boundary`? * Same as `ownersIterator contains boundary` but more efficient. */ @@ -372,6 +385,9 @@ object SymDenotations { recur(symbol) } + final def isProperlyContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = + symbol != boundary && isContainedIn(boundary) + /** Is this denotation static (i.e. with no outer instance)? */ final def isStatic(implicit ctx: Context) = (this is Static) || this.exists && owner.isStaticOwner @@ -428,7 +444,7 @@ object SymDenotations { /** Does this symbol denote the primary constructor of its enclosing class? */ final def isPrimaryConstructor(implicit ctx: Context) = - isConstructor && owner.primaryConstructor.denot == this + isConstructor && owner.primaryConstructor == symbol /** Is this a subclass of the given class `base`? */ def isSubClass(base: Symbol)(implicit ctx: Context) = false @@ -609,7 +625,7 @@ object SymDenotations { /** The field accessed by this getter or setter, or if it does not exist, the getter */ def accessedFieldOrGetter(implicit ctx: Context): Symbol = { - val fieldName = if (isSetter) name.asTermName.setterToGetter else name + val fieldName = if (isSetter) name.asTermName.getterName else name val d = owner.info.decl(fieldName) val field = d.suchThat(!_.is(Method)).symbol def getter = d.suchThat(_.info.isParameterless).symbol @@ -916,7 +932,7 @@ object SymDenotations { privateWithin: Symbol = null, annotations: List[Annotation] = null)(implicit ctx: Context) = { // simulate default parameters, while also passing implicit context ctx to the default values - val initFlags1 = if (initFlags != UndefinedFlags) initFlags else this.flags &~ Frozen + val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) &~ Frozen val info1 = if (info != null) info else this.info val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin val annotations1 = if (annotations != null) annotations else this.annotations @@ -1314,6 +1330,18 @@ object SymDenotations { case _ => bt } + def inCache(tp: Type) = baseTypeRefCache.containsKey(tp) + + /** Can't cache types containing type variables which are uninstantiated + * or whose instances can change, depending on typerstate. + */ + def isCachable(tp: Type): Boolean = tp match { + case tp: TypeVar => tp.inst.exists && inCache(tp.inst) + case tp: TypeProxy => inCache(tp.underlying) + case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) + case _ => true + } + def computeBaseTypeRefOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") if (symbol.isStatic && tp.derivesFrom(symbol)) @@ -1330,9 +1358,6 @@ object SymDenotations { case _ => baseTypeRefOf(tp.underlying) } - case tp: TypeVar => - if (tp.inst.exists) computeBaseTypeRefOf(tp.inst) - else Uncachable(computeBaseTypeRefOf(tp.underlying)) case tp: TypeProxy => baseTypeRefOf(tp.underlying) case AndType(tp1, tp2) => @@ -1353,15 +1378,9 @@ object SymDenotations { var basetp = baseTypeRefCache get tp if (basetp == null) { baseTypeRefCache.put(tp, NoPrefix) - val computedBT = computeBaseTypeRefOf(tp) - basetp = computedBT match { - case Uncachable(basetp) => - baseTypeRefCache.remove(tp) - computedBT - case basetp => - baseTypeRefCache.put(tp, basetp) - basetp - } + basetp = computeBaseTypeRefOf(tp) + if (isCachable(tp)) baseTypeRefCache.put(tp, basetp) + else baseTypeRefCache.remove(tp) } else if (basetp == NoPrefix) { throw CyclicReference(this) } @@ -1420,27 +1439,29 @@ object SymDenotations { override def primaryConstructor(implicit ctx: Context): Symbol = { val cname = if (this is ImplClass) nme.IMPLCLASS_CONSTRUCTOR else nme.CONSTRUCTOR - decls.denotsNamed(cname).first.symbol + decls.denotsNamed(cname).last.symbol // denotsNamed returns Symbols in reverse order of occurrence } + /** The parameter accessors of this class. Term and type accessors, + * getters and setters are all returned int his list + */ + def paramAccessors(implicit ctx: Context): List[Symbol] = + decls.filter(_ is ParamAccessor).toList + /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. - * @pre Can only be called in `phase.next`. */ - def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = { - assert(ctx.phaseId == phase.next.id) - val prevCtx = ctx.withPhase(phase) - val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo - if (classInfo(prevCtx).decls eq decls) { - copySymDenotation( - info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo), - initFlags = this.flags &~ Frozen).installAfter(phase) + def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = + if (ctx.phaseId != phase.next.id) ensureFreshScopeAfter(phase)(ctx.withPhase(phase.next)) + else { + val prevCtx = ctx.withPhase(phase) + val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo + if (classInfo(prevCtx).decls eq decls) + copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo)) + .installAfter(phase) } - } } - private case class Uncachable(tp: Type) extends UncachedGroundType - /** The denotation of a package class. * It overrides ClassDenotation to take account of package objects when looking for members */ diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 6d0f81c72612..f36572755eb8 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -587,11 +587,22 @@ class TypeComparer(initctx: Context) extends DotClass { else thirdTry(tp1, tp2) } tp1.info match { + // There was the following code, which was meant to implement this logic: + // If x has type A | B, then x.type <: C if + // x.type <: C assuming x has type A, and + // x.type <: C assuming x has type B. + // But it did not work, because derivedRef would always give back the same + // type and cache the denotation. So it ended up copmparing just one branch. + // The code seems to be unncessary for the tests and does not seems to help performance. + // So it is commented out. If we ever need to come back to this, we would have + // to create unchached TermRefs in order to avoid cross talk between the branches. + /* case OrType(tp11, tp12) => val sd = tp1.denot.asSingleDenotation def derivedRef(tp: Type) = NamedType(tp1.prefix, tp1.name, sd.derivedSingleDenotation(sd.symbol, tp)) secondTry(OrType.make(derivedRef(tp11), derivedRef(tp12)), tp2) + */ case TypeBounds(lo1, hi1) => if ((ctx.mode is Mode.GADTflexible) && (tp1.symbol is GADTFlexType) && !isSubTypeWhenFrozen(hi1, tp2)) @@ -762,6 +773,8 @@ class TypeComparer(initctx: Context) extends DotClass { tp2.info match { case tp2i: TermRef => isSubType(tp1, tp2i) + case ExprType(tp2i: TermRef) if (ctx.phase.id > ctx.gettersSettersPhase.id) => + isSubType(tp1, tp2i) case _ => false } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 529452c733ac..d93e4eb09338 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -906,10 +906,14 @@ object Types { /** Turn type into a function type. * @pre this is a non-dependent method type. + * @param drop The number of trailing parameters that should be dropped + * when forming the function type. */ - def toFunctionType(implicit ctx: Context): Type = this match { + def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { case mt @ MethodType(_, formals) if !mt.isDependent => - defn.FunctionType(formals mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) + val formals1 = if (dropLast == 0) formals else formals dropRight dropLast + defn.FunctionType( + formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType) } /** The signature of this type. This is by default NotAMethod, @@ -1169,7 +1173,12 @@ object Types { else loadDenot } if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot") - else { + else if (d.exists) { + // Avoid storing NoDenotations in the cache - we will not be able to recover from + // them. The situation might arise that a type has NoDenotation in some later + // phase but a defined denotation earlier (e.g. a TypeRef to an abstract type + // is undefined after erasure.) We need to be able to do time travel back and + // forth also in these cases. lastDenotation = d lastSymbol = d.symbol checkedPeriod = ctx.period diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index 193c872f1a47..67f8255025f1 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -226,7 +226,9 @@ class ClassfileParser( while (!isDelimiter(sig(index))) { index += 1 } sig.slice(start, index) } - def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = { + // Warning: sigToType contains nested completers which might be forced in a later run! + // So local methods need their own ctx parameters. + def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { val tag = sig(index); index += 1 (tag: @switch) match { case BYTE_TAG => defn.ByteType @@ -321,7 +323,7 @@ class ClassfileParser( } } // sig2type(tparams, skiptvs) - def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean): Type = { + def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { val ts = new ListBuffer[Type] while (sig(index) == ':') { index += 1 diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d66d93a16f80..6076ca78754f 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -180,7 +180,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def annotText(tree: untpd.Tree): Text = "@" ~ constrText(tree) // DD - def useSymbol = ctx.isAfterTyper(ctx.phase) && tree.symbol != null && tree.symbol.exists + def useSymbol = ctx.isAfterTyper(ctx.phase) && tree.hasType && tree.symbol.exists def modText(mods: untpd.Modifiers, kw: String): Text = { // DD val suppressKw = if (ownerIsClass) mods is ParamAndLocal else mods is Param @@ -326,10 +326,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { typeDefText(optText(rhs)(" = " ~ _)) } } - case Template(DefDef(mods, _, tparams, vparamss, _, _), parents, self, stats) => + case Template(constr @ DefDef(mods, _, tparams, vparamss, _, rhs), parents, self, stats) => val tparamsTxt = tparamsText(tparams) + val primaryConstrs = if (rhs.isEmpty) Nil else constr :: Nil val prefix: Text = - if (vparamss.isEmpty) tparamsTxt + if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt else { var modsText = modText(mods, "") if (mods.hasAnnotations && !mods.hasFlags) modsText = modsText ~~ " this" @@ -340,7 +341,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close } provided !self.isEmpty - val bodyText = "{" ~~ selfText ~~ toTextGlobal(stats, "\n") ~ "}" + val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: stats, "\n") ~ "}" prefix ~~ (" extends" provided ownerIsClass) ~~ parentsText ~~ bodyText case Import(expr, selectors) => def selectorText(sel: Tree): Text = sel match { diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala index d5bd56bc365e..91b81fea5d0a 100644 --- a/src/dotty/tools/dotc/transform/CapturedVars.scala +++ b/src/dotty/tools/dotc/transform/CapturedVars.scala @@ -17,31 +17,26 @@ import SymUtils._ import collection.{ mutable, immutable } import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet } -class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransformer => +class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransform => import ast.tpd._ /** the following two members override abstract members in Transform */ val phaseName: String = "capturedVars" - override def treeTransformPhase = thisTransformer.next + override def treeTransformPhase = thisTransform.next private var captured: mutable.HashSet[Symbol] = _ - private class CollectCaptured(implicit ctx: Context) extends TreeAccumulator[Symbol] { - def apply(enclMeth: Symbol, tree: Tree) = { - tree match { - case id: Ident => - val sym = id.symbol - if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) { - ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") - captured += sym - } - case tree: DefTree if tree.symbol.exists => - foldOver(tree.symbol.enclosingMethod, tree) - case _ => - foldOver(enclMeth, tree) - } - enclMeth + private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser { + def traverse(enclMeth: Symbol, tree: Tree) = tree match { + case id: Ident => + val sym = id.symbol + if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) { + ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth") + captured += sym + } + case _ => + foldOver(enclMeth, tree) } def runOver(tree: Tree) = { captured = mutable.HashSet() @@ -50,7 +45,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor } override def init(implicit ctx: Context, info: TransformerInfo): Unit = - (new CollectCaptured).runOver(ctx.compilationUnit.tpdTree) + (new CollectCaptured)(ctx.withPhase(thisTransform)).runOver(ctx.compilationUnit.tpdTree) override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation = if (captured(sd.symbol)) { @@ -69,7 +64,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor } def capturedType(vble: Symbol)(implicit ctx: Context): Type = { - val oldInfo = vble.denot(ctx.withPhase(thisTransformer)).info + val oldInfo = vble.denot(ctx.withPhase(thisTransform)).info refCls(oldInfo.classSymbol, vble.isVolatile).typeRef } @@ -91,7 +86,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor override def transformIdent(id: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { val vble = id.symbol if (captured(vble)) - (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransformer)).info) + (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransform)).info) else id } diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 34c90fdc2d31..7bde1ba4ffa4 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -1,27 +1,235 @@ -package dotty.tools.dotc.transform +package dotty.tools.dotc +package transform +import core._ import TreeTransforms._ import dotty.tools.dotc.ast.tpd._ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.StdNames._ +import Phases._ +import ast._ +import Trees._ +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import util.Positions._ +import collection.mutable -/** This transform moves initializers from body to constructor. - * Right now it's a dummy. - * Awaiting for real implemetation +/** This transform + * - moves initializers from body to constructor. + * - makes all supercalls explicit + * - also moves private fields that are accessed only from constructor + * into the constructor if possible. */ -class Constructors extends MiniPhaseTransform { +class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform => + import tpd._ override def phaseName: 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)(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 + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure]) + + override def treeTransformPhase = thisTransform.next + + /** Symbols that are owned by either or a class field move into the + * primary constructor. + */ + override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { + def ownerBecomesConstructor(owner: Symbol): Boolean = + (owner.isLocalDummy || owner.isTerm && !owner.is(Method | Lazy)) && + owner.owner.isClass + if (ownerBecomesConstructor(sym.owner)) + sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor) + else sym + } + + /** @return true if after ExplicitOuter, all references from this tree go via an + * outer link, so no parameter accessors need to be rewired to parameters + */ + private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) = + tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall) + + /** Class members that can be eliminated if referenced only from their own + * constructor. + */ + private def mightBeDropped(sym: Symbol)(implicit ctx: Context) = + sym.is(Private, butNot = KeeperFlags) && !sym.is(MutableParamAccessor) + + private final val KeeperFlags = Method | Lazy | NotJavaPrivate + private final val MutableParamAccessor = allOf(Mutable, ParamAccessor) + + override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = { + val cls = ctx.owner.asClass + val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr + + // Produce aligned accessors and constructor parameters. We have to adjust + // for any outer parameters, which are last in the sequence of original + // parameter accessors but should come first in the constructor parameter list. + var accessors = cls.paramAccessors.filterNot(_.isSetter) + var vparamsWithOuter = vparams + if (!accessors.hasSameLengthAs(vparams)) { + accessors.reverse match { + case last :: _ if (last.name == nme.OUTER) => + accessors = last :: accessors.init // align wth calling convention + vparamsWithOuter = ValDef(last.asTerm) :: vparams + case _ => + } + assert(accessors.hasSameLengthAs(vparamsWithOuter), + i"lengths differ for $cls, param accs = $accessors, params = ($vparamsWithOuter%, %)") + } + val paramSyms = vparamsWithOuter map (_.symbol) + + // Adjustments performed when moving code into the constructor: + // (1) Replace references to param accessors by constructor parameters + // except possibly references to mutable variables, if `excluded = Mutable`. + // (Mutable parameters should be replaced only during the super call) + // (2) If the parameter accessor reference was to an alias getter, + // drop the () when replacing by the parameter. + object intoConstr extends TreeMap { + private var excluded: FlagSet = _ + override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { + case Ident(_) | Select(This(_), _) => + var sym = tree.symbol + if (sym is (ParamAccessor, butNot = excluded)) sym = sym.subst(accessors, paramSyms) + if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree + case Apply(fn, Nil) => + val fn1 = transform(fn) + if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor) + fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass + else cpy.Apply(tree)(fn1, Nil) + case _ => + if (noDirectRefsFrom(tree)) tree else super.transform(tree) + } + + def apply(tree: Tree, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = { + this.excluded = if (inSuperCall) EmptyFlags else Mutable + transform(tree) + } + } + + val superCalls = new mutable.ListBuffer[Tree] + + // If parent is a constructor call, pull out the call into a separate + // supercall constructor, which gets appended to `superCalls`, and keep + // only the type. + def normalizeParent(tree: Tree) = tree match { + case superApp @ Apply( + superSel @ Select( + superNew @ New(superType), + nme.CONSTRUCTOR), + superArgs) => + val toClass = !superType.symbol.is(Trait) + val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass)) + val receiver = + if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true) + else This(cls) + superCalls += + cpy.Apply(superApp)( + receiver.withPos(superNew.pos) + .select(superSel.symbol).withPos(superSel.pos), + mappedArgs) + superType + case tree: TypeTree => tree + } + val parentTypeTrees = tree.parents.map(normalizeParent) + + // Collect all private parameter accessors and value definitions that need + // to be retained. There are several reasons why a parameter accessor or + // definition might need to be retained: + // 1. It is accessed after the constructor has finished + // 2. It is accessed before it is defined + // 3. It is accessed on an object other than `this` + // 4. It is a mutable parameter accessor + // 5. It is has a wildcard initializer `_` + object usage extends TreeTraverser { + private var inConstr: Boolean = true + private val seen = mutable.Set[Symbol](accessors: _*) + val retained = mutable.Set[Symbol]() + def dropped: collection.Set[Symbol] = seen -- retained + override def traverse(tree: Tree) = { + val sym = tree.symbol + tree match { + case Ident(_) | Select(This(_), _) if inConstr && seen(tree.symbol) => + // could refer to definition in constructors, so no retention necessary + case tree: RefTree => + if (mightBeDropped(sym)) retained += sym + case _ => + } + if (!noDirectRefsFrom(tree)) traverseChildren(tree) + } + def collect(stats: List[Tree]): Unit = stats foreach { + case stat: ValDef if !stat.symbol.is(Lazy) => + traverse(stat) + if (mightBeDropped(stat.symbol)) + (if (isWildcardStarArg(stat.rhs)) retained else seen) += stat.symbol + case stat: DefTree => + inConstr = false + traverse(stat) + inConstr = true + case stat => + traverse(stat) + } + } + usage.collect(superCalls.toList ++ tree.body) + + def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc) + + val constrStats, clsStats = new mutable.ListBuffer[Tree] + + def assign(vble: Symbol, rhs: Tree): Tree = + if (cls is Trait) ref(vble.setter).appliedTo(rhs) + else Assign(ref(vble), rhs) + + // Split class body into statements that go into constructor and + // definitions that are kept as members of the class. + def splitStats(stats: List[Tree]): Unit = stats match { + case stat :: stats1 => + stat match { + case stat @ ValDef(mods, name, tpt, rhs) if !stat.symbol.is(Lazy) => + val sym = stat.symbol + if (isRetained(sym)) { + if (!rhs.isEmpty && !isWildcardArg(rhs)) + constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos) + clsStats += cpy.ValDef(stat)(rhs = EmptyTree) + } + else if (!rhs.isEmpty) { + sym.copySymDenotation( + initFlags = sym.flags &~ Private, + owner = constr.symbol).installAfter(thisTransform) + constrStats += intoConstr(stat) + } + case _: DefTree => + clsStats += stat + case _ => + constrStats += intoConstr(stat) + } + splitStats(stats1) + case Nil => + (Nil, Nil) + } + splitStats(tree.body) + + val accessorFields = accessors.filterNot(_ is Method) + + // The initializers for the retained accessors */ + val copyParams = accessorFields.filter(isRetained).map(acc => + assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + + // Drop accessors that are not retained from class scope + val dropped = usage.dropped + if (dropped.nonEmpty) { + val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error + cls.copy( + info = clsInfo.derivedClassInfo( + decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) + } + + cpy.Template(tree)( + constr = cpy.DefDef(constr)( + rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)), + parents = parentTypeTrees, + body = clsStats.toList) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala index 5925ffa721ff..1d0307398fc3 100644 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/src/dotty/tools/dotc/transform/ElimByName.scala @@ -4,6 +4,7 @@ package transform import TreeTransforms._ import core.DenotTransformers._ import core.Symbols._ +import core.SymDenotations._ import core.Contexts._ import core.Types._ import core.Flags._ @@ -75,13 +76,29 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform cpy.Apply(tree)(tree.fun, args1) } - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { - val origDenot = originalDenotation(tree) - if ((origDenot is Param) && (origDenot.info.isInstanceOf[ExprType])) + /** If denotation had an ExprType before, it now gets a function type */ + private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) = + (symd is Param) || (symd is (ParamAccessor, butNot = Method)) + + /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */ + private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = { + val origDenot = originalDenotation(ftree) + if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) tree.select(defn.Function0_apply).appliedToNone else tree } + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + applyIfFunction(tree, tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { + case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) => + // tree might be of form e.asInstanceOf[x.type] where x becomes a function. + // See pos/t296.scala + applyIfFunction(tree, arg) + case _ => tree + } + def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match { case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType)) @@ -98,6 +115,6 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform } def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (sym is Param) transformParamInfo(tp) + if (exprBecomesFunction(sym)) transformParamInfo(tp) else elimByNameParams(tp) } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index aae1ff1baf79..0e1b389ff663 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -234,11 +234,14 @@ object Erasure extends TypeTestsCasts{ class Typer extends typer.ReTyper with NoChecking { import Boxing._ - def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = erasure(tree.typeOpt) + def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = tree.typeOpt match { + case tp: TermRef if tree.isTerm => erasedRef(tp) + case tp => erasure(tp) + } override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { assert(tree.hasType) - val erased = erasedType(tree)(ctx.withPhase(ctx.erasurePhase)) + val erased = erasedType(tree) ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}") tree.withType(erased) } @@ -364,10 +367,9 @@ object Erasure extends TypeTestsCasts{ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = { val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, - vparamss = if (ddef.vparamss.isEmpty) Nil :: Nil else ddef.vparamss, + vparamss = ddef.vparamss.flatten :: Nil, tpt = // keep UnitTypes intact in result position - if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos - else ddef.tpt) + untpd.TypedSplice(TypeTree(eraseResult(ddef.tpt.typeOpt)).withPos(ddef.tpt.pos))) super.typedDefDef(ddef1, sym) } diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index aeda6a1d30e3..179f8d7124e6 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -112,7 +112,7 @@ object ExplicitOuter { def ensureOuterAccessors(cls: ClassSymbol)(implicit ctx: Context): Unit = { //todo: implementing #165 would simplify this logic val prevPhase = ctx.phase.prev - assert(prevPhase.id <= ctx.explicitOuter.id, "can add $outer symbols only before ExplicitOuter") + assert(prevPhase.id <= ctx.explicitOuterPhase.id, "can add $outer symbols only before ExplicitOuter") assert(prevPhase.isInstanceOf[DenotTransformer], "adding outerAccessors requires being DenotTransformer") if (!hasOuter(cls)) { newOuterAccessors(cls).foreach(_.enteredAfter(prevPhase.asInstanceOf[DenotTransformer])) @@ -190,7 +190,7 @@ object ExplicitOuter { */ def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = { def isOuter(sym: Symbol) = - sym != cls && !sym.isStaticOwner && cls.isContainedIn(sym) + !sym.isStaticOwner && cls.isProperlyContainedIn(sym) tree match { case thisTree @ This(_) => isOuter(thisTree.symbol) diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala new file mode 100644 index 000000000000..772a63e52a6c --- /dev/null +++ b/src/dotty/tools/dotc/transform/GettersSetters.scala @@ -0,0 +1,118 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Phases.Phase +import Contexts.Context +import SymDenotations.SymDenotation +import Types._ +import Symbols._ +import SymUtils._ +import Constants._ +import ast.Trees._ +import TreeTransforms._ +import NameOps._ +import Flags._ +import Decorators._ + +/** Performs the following rewritings: + * + * val x: T = e + * --> private val x_L: T = e + * def x: T = x_L (if in class) + * --> private notJavaPrivate var x_L: T = e + * def x: T = x_L + * private notJavaPrivate def x_=(x: T): Unit = x_L = x (if in trait) + * var x: T = e + * --> private var x_L: T = e + * def x: T = x_L + * def x_=(x: T): Unit = x_L = x (if in class or trait) + * lazy val x: T = e + * --> lazy def x = e + * + * Furthermore, assignements to mutable vars are replaced by setter calls + * + * p.x = e + * --> p.x_=(e) + */ +class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform => + import ast.tpd._ + + override def phaseName = "gettersSetters" + + override def treeTransformPhase = thisTransform.next + + override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = { + def noGetterNeeded = + d.is(Method | Param | JavaDefined) || + d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) || + d.is(Module) && d.isStatic || + d.isSelfSym + if (d.isTerm && (d.owner.isClass || d.is(Lazy)) && d.info.isValueType && !noGetterNeeded) { + val maybeStable = if (d.isStable) Stable else EmptyFlags + if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}") + d.copySymDenotation( + initFlags = d.flags | maybeStable | AccessorCreationFlags, + info = ExprType(d.info)) + } + else d + } + + override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (tree.symbol is Method) { + val getter = tree.symbol.asTerm + assert(getter is Accessor) + if (getter.is(Lazy | Deferred | ParamAccessor)) DefDef(getter, tree.rhs) + else { + val inTrait = getter.owner.is(Trait) + val maybePrivate = + if (inTrait) Private | NotJavaPrivate + else if (getter.owner.isClass) Private + else EmptyFlags + val maybeMutable = + if (inTrait || getter.is(Mutable)) Mutable + else EmptyFlags + val field = ctx.newSymbol( + owner = ctx.owner, + name = getter.name.fieldName, + flags = maybePrivate | maybeMutable, + info = getter.info.resultType).enteredAfter(thisTransform) + assert(tree.rhs.tpe.exists, tree.show) + val fieldDef = + cpy.ValDef(tree)( + mods = tree.mods & EmptyFlags | field.flags, + name = field.name, + rhs = tree.rhs.changeOwner(getter, field).ensureConforms(field.info.widen) + ).withType(field.valRef) + val rhs = ref(field) + assert(rhs.hasType) + val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen)) + if (!getter.is(Mutable) && inTrait) { // add a setter anyway, will be needed for mixin + val setter = ctx.newSymbol( + owner = ctx.owner, + name = getter.name.traitSetterName, + flags = (getter.flags & AccessFlags) | Accessor | maybePrivate, + info = MethodType(field.info :: Nil, defn.UnitType)).enteredAfter(thisTransform) + val setterDef = DefDef(setter.asTerm, vrefss => Assign(ref(field), vrefss.head.head)) + Thicket(fieldDef, getterDef, setterDef) + } + else Thicket(fieldDef, getterDef) + } + } + else tree + } + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) { + val Literal(Constant(())) = tree.rhs + val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol)) + assert(initializer.hasType) + cpy.DefDef(tree)(rhs = initializer) + } + else tree + + override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree = + if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs) + else tree +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 97525e2891d2..9087ba0c4bc0 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -20,7 +20,7 @@ import typer.ErrorReporting._ import ast.Trees._ import Applications._ import TypeApplications._ -import TypeUtils._ +import SymUtils._, core.NameOps._ import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.core.Decorators._ @@ -112,7 +112,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans def tupleSel(binder: Symbol)(i: Int): Tree = ref(binder).select(nme.productAccessorName(i)) def index(tgt: Tree)(i: Int): Tree = { if (i > 0) tgt.select(defn.Seq_apply).appliedTo(Literal(Constant(i))) - else tgt.select(defn.Seq_head).appliedIfMethod + else tgt.select(defn.Seq_head).ensureApplied } // Right now this blindly calls drop on the result of the unapplySeq @@ -237,7 +237,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val matchFail = newSynthCaseLabel(ctx.freshName("matchFail"), MethodType(Nil, restpe)) val catchAllDefBody = DefDef(matchFail, catchAllDef) - val nextCases = (caseSyms.tail ::: List(matchFail)).map(ref(_).appliedIfMethod) + val nextCases = (caseSyms.tail ::: List(matchFail)).map(ref(_).ensureApplied) val caseDefs = (cases zip caseSyms zip nextCases).foldRight[Tree](catchAllDefBody) { // dotty deviation //case (((mkCase, sym), nextCase), acc) => @@ -248,7 +248,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val caseBody = DefDef(sym, _ => Block(List(acc), body)) - Block(List(caseBody),ref(sym).appliedIfMethod) + Block(List(caseBody),ref(sym).ensureApplied) }} @@ -278,7 +278,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val isDefined = extractorMemberType(prev.tpe, nme.isDefined) if ((isDefined isRef defn.BooleanClass) && getTp.exists) { - val prevValue = ref(prevSym).select("get".toTermName).appliedIfMethod + val prevValue = ref(prevSym).select("get".toTermName).ensureApplied Block( List(ValDef(prevSym, prev)), // must be isEmpty and get as we don't control the target of the call (prev is an extractor call) @@ -945,7 +945,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans def isVarPattern(pat: Tree): Boolean = pat match { case x: BackquotedIdent => false - case x: Ident => nme.isVariableName(x.name) + case x: Ident => x.name.isVariableName case _ => false } @@ -1391,7 +1391,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans val accessors = if (defn.isProductSubType(binder.info)) productSelectors(binder.info) - else binder.info.caseAccessors + else binder.caseAccessors val res = if (accessors.isDefinedAt(i - 1)) ref(binder).select(accessors(i - 1).name) else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN @@ -1480,7 +1480,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans * when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder */ def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position, binderTypeTested: Type): TreeMaker = { - val paramAccessors = binder.info.caseAccessors + val paramAccessors = binder.caseAccessors // binders corresponding to mutable fields should be stored (SI-5158, SI-6070) // make an exception for classes under the scala package as they should be well-behaved, // to optimize matching on List @@ -1800,7 +1800,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && resultOfGet.exists) getUnapplySelectors(resultOfGet, args) else if (defn.isProductSubType(resultType)) productSelectorTypes(resultType) - else if (resultType =:= defn.BooleanType) Nil + else if (resultType isRef defn.BooleanClass) Nil else { ctx.error(i"invalid return type in Unapply node: $resultType") Nil diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala index df0ac59ae930..2875327c4712 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -7,6 +7,9 @@ import Contexts._ import Symbols._ import Decorators._ import Names._ +import StdNames._ +import NameOps._ +import Flags._ import language.implicitConversions object SymUtils { @@ -17,9 +20,48 @@ object SymUtils { * that are needed in the transformer pipeline. */ class SymUtils(val self: Symbol) extends AnyVal { + import SymUtils._ def isTypeTestOrCast(implicit ctx: Context): Boolean = self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf def isVolatile(implicit ctx: Context) = self.hasAnnotation(defn.VolatileAnnot) + + /** If this is a constructor, its owner: otherwise this. */ + final def skipConstructor(implicit ctx: Context): Symbol = + if (self.isConstructor) self.owner else self + + final def isAnonymousFunction(implicit ctx: Context): Boolean = + self.is(Method) && (self.denot.initial.asSymDenotation.name startsWith nme.ANON_FUN) + + /** The logically enclosing method or class for this symbol. + * Instead of constructors one always picks the enclosing class. + */ + final def enclosure(implicit ctx: Context) = self.owner.enclosingMethod.skipConstructor + + /** Apply symbol/symbol substitution to this symbol */ + def subst(from: List[Symbol], to: List[Symbol]): Symbol = { + def loop(from: List[Symbol], to: List[Symbol]): Symbol = + if (from.isEmpty) self + else if (self eq from.head) to.head + else loop(from.tail, to.tail) + loop(from, to) + } + + def accessorNamed(name: TermName)(implicit ctx: Context): Symbol = + self.owner.info.decl(name).suchThat(_ is Accessor).symbol + + def caseAccessors(implicit ctx:Context) = + self.decls.filter(_ is CaseAccessor).toList + + def getter(implicit ctx: Context): Symbol = + if (self.isGetter) self else accessorNamed(self.asTerm.name.getterName) + + def setter(implicit ctx: Context): Symbol = + if (self.isSetter) self + else accessorNamed(self.asTerm.name.setterName) orElse + accessorNamed(self.asTerm.name.traitSetterName) + + def field(implicit ctx: Context): Symbol = + self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol } diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 34cca2872fdc..128449efa73a 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package transform import core._ -import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._ +import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._ import scala.collection.{ mutable, immutable } import Flags._ import TreeTransforms._ @@ -48,7 +48,7 @@ class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer */ def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = { val clazzType = clazz.typeRef - def accessors = clazz.decls.filter(_ is CaseAccessor).toList + lazy val accessors = clazz.caseAccessors val symbolsToSynthesize: List[Symbol] = if (clazz.is(Case)) caseSymbols diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index ef5baca0729f..1b5cc7c07c1a 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -20,6 +20,7 @@ import reporting.ThrowingReporter import ast.Trees._ import ast.{tpd, untpd} import util.SourcePosition +import ProtoTypes._ import java.lang.AssertionError /** Run by -Ycheck option after a given phase, this class retypes all syntax trees @@ -130,10 +131,13 @@ class TreeChecker { super.typedStats(trees, exprOwner) } + override def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = + block override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = { - if (ctx.mode.isExpr) - if(!(tree.tpe <:< pt)) + def isPrimaryConstructorReturn = + ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) + if (ctx.mode.isExpr && !isPrimaryConstructorReturn && !pt.isInstanceOf[FunProto]) assert(tree.tpe <:< pt, s"error at ${sourcePos(tree.pos)}\n" + err.typeMismatchStr(tree.tpe, pt)) diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 7aaf7d86d73e..c25e81af9c22 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -13,6 +13,7 @@ import core.TypeErasure.isUnboundedGeneric import typer.ErrorReporting._ import ast.Trees._ import Erasure.Boxing._ +import core.TypeErasure._ /** This transform normalizes type tests and type casts, * also replacing type tests with singleton argument type with reference equality check @@ -69,7 +70,7 @@ trait TypeTestsCasts { } case defn.MultiArrayType(elem, ndims) if isUnboundedGeneric(elem) => def isArrayTest(arg: Tree) = - runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil) + ref(defn.runtimeMethod(nme.isArray)).appliedTo(arg, Literal(Constant(ndims))) if (ndims == 1) isArrayTest(qual) else evalOnce(qual) { qual1 => derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1) @@ -92,11 +93,11 @@ trait TypeTestsCasts { else derivedTree(qual, defn.Any_asInstanceOf, argType) } - + def erasedArg = erasure(tree.args.head.tpe) if (sym eq defn.Any_isInstanceOf) - transformIsInstanceOf(qual, tree.args.head.tpe) + transformIsInstanceOf(qual, erasedArg) else if (sym eq defn.Any_asInstanceOf) - transformAsInstanceOf(tree.args.head.tpe) + transformAsInstanceOf(erasedArg) else tree case _ => diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala index ceb4048a7060..e510fcc88aa4 100644 --- a/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -26,6 +26,4 @@ class TypeUtils(val self: Type) extends AnyVal { def isPrimitiveValueType(implicit ctx: Context): Boolean = self.classSymbol.isPrimitiveValueClass - - def caseAccessors(implicit ctx:Context) = self.decls.filter(x => x.is(Flags.CaseAccessor) && x.is(Flags.Method)).toList -} \ 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 2ff116f461b5..035f19028d7c 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -132,8 +132,7 @@ trait Applications extends Compatibility { self: Typer => protected def appPos: Position - /** If constructing trees, the current function part, which might be - * affected by lifting. EmptyTree otherwise. + /** The current function part, which might be affected by lifting. */ protected def normalizedFun: Tree @@ -251,40 +250,52 @@ trait Applications extends Compatibility { self: Typer => /** Find reference to default parameter getter for parameter #n in current * parameter list, or NoType if none was found */ - def findDefaultGetter(n: Int)(implicit ctx: Context): Type = { - val meth = methRef.symbol - val prefix = - if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else methRef.name - def getterName = prefix.defaultGetterName(n) - def ref(pre: Type, sym: Symbol): Type = - if (pre.exists && sym.isTerm) pre select sym else NoType - if (meth.hasDefaultParams) - methRef.prefix match { - case NoPrefix => - def findDefault(cx: Context): Type = { - if (cx eq NoContext) NoType - else if (cx.scope != cx.outer.scope && - cx.denotNamed(methRef.name).hasAltWith(_.symbol == meth)) { - val denot = cx.denotNamed(getterName) - assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") - cx.owner.thisType.select(getterName, denot) - } else findDefault(cx.outer) - } - findDefault(ctx) - case mpre => - val cls = meth.owner - val pre = - if (meth.isClassConstructor) { - // default getters for class constructors are found in the companion object - mpre.baseTypeRef(cls) match { - case tp: TypeRef => ref(tp.prefix, cls.companionModule) - case _ => NoType - } - } else mpre - val getter = pre.member(getterName) - ref(pre, getter.symbol) + def findDefaultGetter(n: Int)(implicit ctx: Context): Tree = { + val meth = methRef.symbol.asTerm + val receiver: Tree = methPart(normalizedFun) match { + case Select(receiver, _) => receiver + case mr => mr.tpe.normalizedPrefix match { + case mr: TermRef => ref(mr) + case _ => EmptyTree + } + } + val getterPrefix = + if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else meth.name + def getterName = getterPrefix.defaultGetterName(n) + if (!meth.hasDefaultParams) + EmptyTree + else if (receiver.isEmpty) { + def findGetter(cx: Context): Tree = { + if (cx eq NoContext) EmptyTree + else if (cx.scope != cx.outer.scope && + cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) { + val denot = cx.denotNamed(getterName) + assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)") + ref(TermRef(cx.owner.thisType, getterName, denot)) + } else findGetter(cx.outer) + } + findGetter(ctx) + } + else { + def selectGetter(qual: Tree): Tree = { + val getterDenot = qual.tpe.member(getterName) + if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot)) + else EmptyTree + } + if (!meth.isClassConstructor) + selectGetter(receiver) + else { + // default getters for class constructors are found in the companion object + val cls = meth.owner + val companion = cls.companionModule + receiver.tpe.baseTypeRef(cls) match { + case tp: TypeRef if companion.isTerm => + selectGetter(ref(TermRef(tp.prefix, companion.asTerm))) + case _ => + EmptyTree + } } - else NoType + } } /** Match re-ordered arguments against formal parameters @@ -305,13 +316,12 @@ trait Applications extends Compatibility { self: Typer => } def tryDefault(n: Int, args1: List[Arg]): Unit = { - findDefaultGetter(n + numArgs(normalizedFun)) match { - case dref: NamedType => - liftFun() - addTyped(treeToArg(spliceMeth(ref(dref) withPos appPos, normalizedFun)), formal) - matchArgs(args1, formals1, n + 1) - case _ => - missingArg(n) + liftFun() + val getter = findDefaultGetter(n + numArgs(normalizedFun)) + if (getter.isEmpty) missingArg(n) + else { + addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal) + matchArgs(args1, formals1, n + 1) } } @@ -364,7 +374,7 @@ trait Applications extends Compatibility { self: Typer => def fail(msg: => String) = ok = false def appPos = NoPosition - def normalizedFun = EmptyTree + lazy val normalizedFun = ref(methRef) init() } @@ -594,7 +604,7 @@ trait Applications extends Compatibility { self: Typer => def followTypeAlias(tree: untpd.Tree): untpd.Tree = { tree match { case tree: untpd.RefTree => - val ttree = typedType(tree.withName(tree.name.toTypeName)) + val ttree = typedType(untpd.rename(tree, tree.name.toTypeName)) ttree.tpe match { case alias: TypeRef if alias.info.isAlias => companionRef(alias) match { diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala index 1f55df2bcb64..e96e04b1a449 100644 --- a/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -56,10 +56,12 @@ object ErrorReporting { else "" def expectedTypeStr(tp: Type): String = tp match { + case tp: PolyProto => + d"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}" case tp: FunProto => val result = tp.resultType match { - case tp: WildcardType => "" - case tp => d"and expected result type $tp" + case _: WildcardType | _: IgnoredProto => "" + case tp => d" and expected result type $tp" } d"arguments (${tp.typedArgs.tpes}%, %)$result" case _ => diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 7c1130b83378..394accd03ae3 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -84,7 +84,7 @@ object EtaExpansion { case TypeApply(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)(liftPrefix(defs, pre), name) case Block(stats, expr) => liftApp(defs ++= stats, expr) case New(tpt) => @@ -93,6 +93,18 @@ object EtaExpansion { lift(defs, tree) } + /** Lift prefix `pre` of an application `pre.f(...)` to + * + * val x0 = pre + * x0.f(...) + * + * unless `pre` is a `New` or `pre` is idempotent. + */ + def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { + case New(_) => tree + case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree) + } + /** Eta-expanding a tree means converting a method reference to a function value. * @param tree The tree to expand * @param mt The type of the method reference diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 5ceab475ebb5..3a1f0a98b461 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -632,10 +632,7 @@ class Namer { typer: Typer => completeParams(tparams) vparamss foreach completeParams val isConstructor = name == nme.CONSTRUCTOR - val isSecondaryConstructor = isConstructor && sym != sym.owner.primaryConstructor - def typeParams = - if (isSecondaryConstructor) sym.owner.primaryConstructor.typeParams - else tparams map symbolOfTree + def typeParams = tparams map symbolOfTree def wrapMethType(restpe: Type): Type = { var paramSymss = vparamss.nestedMap(symbolOfTree) // Make sure constructor has one non-implicit parameter list diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 31a776fc98f8..6895f997874a 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -71,6 +71,9 @@ class ReTyper extends Typer { override def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = typedModifiers(mods, sym) + override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = + parents + override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index c23b820e4a57..ccf67b55b9ae 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -209,10 +209,11 @@ trait TypeAssigner { def assignType(tree: untpd.Literal)(implicit ctx: Context) = tree.withType { - tree.const.tag match { + val value = tree.const + value.tag match { case UnitTag => defn.UnitType case NullTag => defn.NullType - case _ => ConstantType(tree.const) + case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value) } } @@ -235,7 +236,7 @@ trait TypeAssigner { } val owntype = if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall) cls.info.firstParent + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent else { val ps = cls.info.parents if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) @@ -246,7 +247,7 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case fntpe @ MethodType(_, ptypes) => - if (sameLength(ptypes, args)) fntpe.instantiate(args.tpes) + if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) else errorType(i"wrong number of parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos) case t => errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) @@ -258,7 +259,7 @@ trait TypeAssigner { val ownType = fn.tpe.widen match { case pt: PolyType => val argTypes = args.tpes - if (sameLength(argTypes, pt.paramNames)) pt.instantiate(argTypes) + if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos) case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) @@ -285,7 +286,9 @@ trait TypeAssigner { tree.withType(thenp.tpe | elsep.tpe) def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = - tree.withType(if (target.isEmpty) meth.tpe.widen.toFunctionType else target.tpe) + tree.withType( + if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) + else target.tpe) def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = tree.withType(body.tpe) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 9066012f035a..80eb5965c63f 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -374,8 +374,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case lhs => val lhsCore = typedUnadapted(lhs) def lhs1 = typed(untpd.TypedSplice(lhsCore)) + def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields + sym.is(Mutable, butNot = Accessor) || + ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner lhsCore.tpe match { - case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) => + case ref: TermRef if canAssign(ref.symbol) => assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) case _ => def reassignmentToVal = @@ -390,8 +393,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case lhsCore: RefTree if setter.exists => 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)) + val lhs2 = untpd.rename(lhsCore, setterName).withType(setterType) + typedUnadapted(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => reassignmentToVal } @@ -843,20 +846,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit result } - /** If this is a real class, make sure its first parent is a - * constructor call. Cannot simply use a type. - */ - def ensureConstrCall(parents: List[Tree]): List[Tree] = { - val firstParent :: otherParents = parents - if (firstParent.isType && !(cls is Trait)) - typed(untpd.New(untpd.TypedSplice(firstParent), Nil))(superCtx) :: otherParents - else parents - } - val mods1 = addTypedModifiersAnnotations(mods, cls) val constr1 = typed(constr).asInstanceOf[DefDef] - val parents1 = ensureConstrCall(ensureFirstIsClass( - parents mapconserve typedParent, cdef.pos.toSynthetic)) + val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic) + val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible val dummy = localDummy(cls, impl) val body1 = typedStats(body, dummy)(inClassContext(self1.symbol)) @@ -875,6 +868,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // 4. Polymorphic type defs override nothing. } + /** If this is a real class, make sure its first parent is a + * constructor call. Cannot simply use a type. Overridden in ReTyper. + */ + def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = { + val firstParent :: otherParents = parents + if (firstParent.isType && !(cls is Trait)) + typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents + else parents + } + /** Overridden in retyper */ def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree) @@ -1009,7 +1012,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } - protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree withName tree.name.encode + protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = + untpd.rename(tree, tree.name.encode) def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { assertPositioned(tree) @@ -1272,7 +1276,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) => pt match { case SAMType(meth) - if wtp <:< meth.info.toFunctionType => + if wtp <:< meth.info.toFunctionType() => // was ... && isFullyDefined(pt, ForceDegree.noBottom) // 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 diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala index 5865f0133f67..e1f50ded9d74 100644 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -125,7 +125,7 @@ class VarianceChecker()(implicit ctx: Context) { ctx.debuglog(s"Skipping variance check of ${sym.showDcl}") case tree: TypeDef => checkVariance(sym) - foldOver((), tree) + traverseChildren(tree) case tree: ValDef => checkVariance(sym) case DefDef(_, _, tparams, vparamss, _, _) => @@ -133,7 +133,7 @@ class VarianceChecker()(implicit ctx: Context) { tparams foreach traverse vparamss foreach (_ foreach traverse) case Template(_, _, _, body) => - foldOver((), tree) + traverseChildren(tree) case _ => } } diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala index 44b64553b966..0fec1e5a772c 100644 --- a/src/dotty/tools/dotc/typer/Variances.scala +++ b/src/dotty/tools/dotc/typer/Variances.scala @@ -68,11 +68,10 @@ object Variances { /** Compute variance of type parameter tparam in type tp. */ def varianceInType(tp: Type)(tparam: Symbol)(implicit ctx: Context): Variance = tp match { - case TermRef(pre, sym) => + case TermRef(pre, _) => varianceInType(pre)(tparam) - case TypeRef(pre, sym) => - /* @odersky sym is a typeName here, comparison is always false */ - if (sym == tparam) Covariant else varianceInType(pre)(tparam) + case tp @ TypeRef(pre, _) => + if (tp.symbol == tparam) Covariant else varianceInType(pre)(tparam) case tp @ TypeBounds(lo, hi) => if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance) else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 02184599729a..dccd96eed215 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -15,15 +15,12 @@ class tests extends CompilerTest { implicit val defaultOptions = noCheckOptions ++ List( "-YnoDeepSubtypes", - "-Ycheck:patternMatcher,literalize,capturedVars", - "-Ystop-before:erasure" - /*,"-uniqid", "-explaintypes", "-verbose", "-Ylog:splitter", "-Xprompt", "-YnoDoubleBindings"*/ + "-Ycheck:patternMatcher,gettersSetters,constructors" ) - val allowDeepSubtypes = defaultOptions diff List("-YnoDeepSubtypes") - - val twice = List("#runs", "2", "-YnoDoubleBindings", "-Ystop-before:terminal") + val twice = List("#runs", "2", "-YnoDoubleBindings") val doErase = List("-Ystop-before:terminal") + val allowDeepSubtypes = defaultOptions diff List("-YnoDeepSubtypes") val posDir = "./tests/pos/" val negDir = "./tests/neg/" @@ -49,7 +46,6 @@ class tests extends CompilerTest { @Test def pos_desugar() = compileFile(posDir, "desugar", doErase) @Test def pos_sigs() = compileFile(posDir, "sigs", doErase) @Test def pos_typers() = compileFile(posDir, "typers", doErase) - @Test def pos_typedidents() = compileFile(posDir, "typedIdents", doErase) @Test def pos_assignments() = compileFile(posDir, "assignments", doErase) @Test def pos_packageobject() = compileFile(posDir, "packageobject", doErase) @@ -97,29 +93,25 @@ class tests extends CompilerTest { @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_t2660_ambi = compileFile(negDir, "t2660", xerrors = 2) @Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2) @Test def neg_subtyping = compileFile(negDir, "subtyping", xerrors = 1) @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2) @Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1) - @Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes) @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) @Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes) @Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes) - @Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice) - @Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice) @Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice) - @Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice) @Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", twice) - @Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice) @Test def tools_io = compileDir(dotcDir + "tools/io", twice) - @Test def tools = compileDir(dotcDir + "tools", twice)(allowDeepSubtypes) + //@Test def tools = compileDir(dotcDir + "tools", "-deep" :: Nil)(allowDeepSubtypes) @Test def testNonCyclic = compileArgs(Array( dotcDir + "tools/dotc/CompilationUnit.scala", @@ -129,7 +121,6 @@ class tests extends CompilerTest { "-Xprompt", "#runs", "2")) - @Test def testIssue_34 = compileArgs(Array( dotcDir + "tools/dotc/config/Properties.scala", dotcDir + "tools/dotc/config/PathResolver.scala", diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala index f5b13845434c..c9c7c602bdea 100644 --- a/test/test/CompilerTest.scala +++ b/test/test/CompilerTest.scala @@ -24,8 +24,12 @@ class CompilerTest extends DottyTest { compileDir(Directory(path), args, xerrors) def compileDir(dir: Directory, args: List[String], xerrors: Int)(implicit defaultOptions: List[String]): Unit = { - val fileNames = dir.deepFiles.toArray.map(_.toString).filter(_ endsWith ".scala") - compileArgs(fileNames ++ args, xerrors) + val (files, normArgs) = args match { + case "-deep" :: args1 => (dir.deepFiles, args1) + case _ => (dir.files, args) + } + val fileNames = files.toArray.map(_.toString).filter(_ endsWith ".scala") + compileArgs(fileNames ++ normArgs, xerrors) } def compileFiles(path: String, args: List[String] = Nil)(implicit defaultOptions: List[String]): Unit = { diff --git a/tests/neg/t2660.scala b/tests/neg/t2660.scala new file mode 100644 index 000000000000..85e318915db5 --- /dev/null +++ b/tests/neg/t2660.scala @@ -0,0 +1,47 @@ +// Dotty deviation. The calls here now are classified as ambiguous. + +package hoho + +class G + +class H extends G + +class A[T](x: T) { + + def this(y: G, z: T) = { + this(z) + print(1) + } + + def this(z: H, h: T) = { + this(h) + print(2) + } +} + +object T { + def main(args: Array[String]): Unit = { + implicit def g2h(g: G): H = new H + new A[Int](new H, 23) + // in the context here, either secondary constructor is applicable + // to the other, due to the implicit in scope. So the call is ambiguous. + } +} + + +// A version of t2660 which does not use constructors + +object X { + def f[T](x: T) = ??? + def f[T](y: G, z: T) = ??? + def f[T](z: H, h: T) = ??? +} + +object T2 { + def main(args: Array[String]): Unit = { + implicit def g2h(g: G): H = new H + X.f(new H, 23) + } +} + + diff --git a/tests/pending/pos/annot.scala b/tests/pending/pos/annot.scala new file mode 100644 index 000000000000..8e21803b431b --- /dev/null +++ b/tests/pending/pos/annot.scala @@ -0,0 +1,5 @@ +class Test { + + @SuppressWarnings("hi") def foo() = ??? + +} diff --git a/tests/pending/pos/t1672.scala b/tests/pending/pos/t1672.scala new file mode 100644 index 000000000000..77a86db2244e --- /dev/null +++ b/tests/pending/pos/t1672.scala @@ -0,0 +1,36 @@ +// moved to pending. +/* Tail calls translates this program to: + +final lazy object Test1672: Test1672$ = new Test1672$() + final class Test1672$() extends Object() { this: Test1672$.type => + @tailrec def bar: (x: Int)(y: Int)Nothing = { + def tailLabel2: ($this: Test1672$.type)(x: Int)(y: Int)Nothing = { + try { + throw new scala.package.RuntimeException() + } catch { + def $anonfun: (x$1: Throwable)Nothing = + x$1 match { + case _: scala.package.Throwable => + tailLabel2($this)(x)(y) + } + closure($anonfun) + } + } + tailLabel2(Test1672$.this)(x)(y) + } + } + +Note the tail call to taillabel2 from the local method $anonfun. +LambdaLift doe snot know how to deal wioth this. +*/ + +object Test1672 { + @annotation.tailrec + def bar(x: Int)(y: Int) : Nothing = { + try { + throw new RuntimeException + } catch { + case _: Throwable => bar(x)(y) + } + } +} diff --git a/tests/pending/pos/vararg-pattern.scala b/tests/pending/pos/vararg-pattern.scala new file mode 100644 index 000000000000..314d6460f840 --- /dev/null +++ b/tests/pending/pos/vararg-pattern.scala @@ -0,0 +1,12 @@ +object Test { + + List(1, 2, 3, 4) match { + case List(1, 2, xs: _*) => + val ys: Seq[Int] = xs + println(ys) + } + val List(1, 2, x: _*) = List(1, 2, 3, 4) + +} + + diff --git a/tests/pos/Fileish.scala b/tests/pos/Fileish.scala new file mode 100644 index 000000000000..d226bb0bdeee --- /dev/null +++ b/tests/pos/Fileish.scala @@ -0,0 +1,53 @@ +// Inspired by the original Fileish, +// testing combinations of lazy and non-lazy vals for their treatment in constructors +package dotty.tools +package io + +import java.io.{ InputStream } +import java.util.jar.JarEntry +import language.postfixOps + +/** A common interface for File-based things and Stream-based things. + * (In particular, io.File and JarEntry.) + */ +class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} +class Fileish2(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} + +class Fileish3(val path: Path, val input: () => InputStream) extends Streamable.Chars { + def inputStream() = input() + + def parent = path.parent + def name = path.name + def isSourceFile = path.hasExtension("java", "scala") + + private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim } + private val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".") + private val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "." + + override def toString = path.path +} + diff --git a/tests/pos/array-clone.scala b/tests/pos/array-clone.scala new file mode 100644 index 000000000000..ef5ac5c85911 --- /dev/null +++ b/tests/pos/array-clone.scala @@ -0,0 +1,7 @@ +object test { + + val xs = Array(1, 2, 3) + + val ys: Array[Int] = xs.clone() + +} diff --git a/tests/pos/constrs.scala b/tests/pos/constrs.scala new file mode 100644 index 000000000000..dc0e1a369305 --- /dev/null +++ b/tests/pos/constrs.scala @@ -0,0 +1,33 @@ +class Foo(x: Int, var y: Int) { + + val z: Int = 0 + + var u: Int = _ + + def f = x + +} + +class Baz(val base: Int) { + +} + + +class Bar(base: Int, byName: => String, local: Int) extends Baz(base + local) { + + def f() = println(base.toString + byName) + +} + +class Rational(n: Int, d: Int) { + def gcd(x: Int, y: Int): Int = ??? + private val x = gcd(n, d) + def numer = n / x + def denom = d / x +} +class Rational2(n: Int, d: Int) { + def gcd(x: Int, y: Int): Int = ??? + private val x = gcd(n, d) + val numer = n / x + val denom = d / x +} diff --git a/tests/pos/getset.scala b/tests/pos/getset.scala new file mode 100644 index 000000000000..7b6207e94bcd --- /dev/null +++ b/tests/pos/getset.scala @@ -0,0 +1,23 @@ +package test + +trait T { + + val x = 2 + + var y = 2 + + private[this] var z = 3 + + private var a = 3 + +} +class C { + + val x = 2 + + var y = 2 + + private[this] var z = 3 + + private var a = 3 +} diff --git a/tests/pos/lambdalift.scala b/tests/pos/lambdalift.scala new file mode 100644 index 000000000000..febae6828bc6 --- /dev/null +++ b/tests/pos/lambdalift.scala @@ -0,0 +1,46 @@ +object test { + + def foo(x: Int) = { + + def bar(y: Int) = x + y + def baz(z: Int) = bar(z) + + baz(1) + + } + + def foo2(x: Int) = { + + class C { + def bam(y: Int): String => Int = { + def baz = x + y + z => baz * z.length + } + } + + val fun = new C().bam(1) + fun("abc") + + } +} + +class Super(x: Int) + +class Sub extends Super({ + def foo3(x: Int) = { + + class C { + def this(name: String) = this() + + def bam(y: Int): String => Int = { + def baz = x + y + z => baz * z.length + } + } + + val fun = new C("dummy").bam(1) + fun("abc") + + } + foo3(22) +}) diff --git a/tests/pos/points.scala b/tests/pos/points.scala new file mode 100644 index 000000000000..db6104c883e5 --- /dev/null +++ b/tests/pos/points.scala @@ -0,0 +1,8 @@ +class Point extends Comparable[Point] { + override def compareTo(other: Point): Int = ??? +} + +class ColoredPoint extends Point with Comparable[ColoredPoint] { + override def compareTo(other: ColoredPoint): Int = ??? +} + diff --git a/tests/pos/synthetics.scala b/tests/pos/synthetics.scala new file mode 100644 index 000000000000..c7d49df70de9 --- /dev/null +++ b/tests/pos/synthetics.scala @@ -0,0 +1,4 @@ +case class C(x: Int, var y: String) { + +} + diff --git a/tests/pos/t2660.scala b/tests/pos/t2660.scala deleted file mode 100644 index 94a40f7403e8..000000000000 --- a/tests/pos/t2660.scala +++ /dev/null @@ -1,25 +0,0 @@ -package hoho - -class G - -class H extends G - -class A[T](x: T) { - - def this(y: G, z: T) = { - this(z) - print(1) - } - - def this(z: H, h: T) = { - this(h) - print(2) - } -} - -object T { - def main(args: Array[String]): Unit = { - implicit def g2h(g: G): H = new H - new A[Int](new H, 23) - } -} diff --git a/tests/pos/t7093.scala b/tests/pos/t7093.scala new file mode 100644 index 000000000000..287b7a78c726 --- /dev/null +++ b/tests/pos/t7093.scala @@ -0,0 +1,27 @@ +object Test { + + trait A[+X] { + protected[this] def f(x: X): X = x + } + + trait B extends A[B] { + def kaboom = f(new B {}) + } + +// protected[this] disables variance checking +// of the signature of `f`. +// +// C's parent list unifies A[B] with A[C] +// +// The protected[this] loophole is widely used +// in the collections, every newBuilder method +// would fail variance checking otherwise. + class C extends B with A[C] { + override protected[this] def f(c: C) = c + } + +// java.lang.ClassCastException: B$$anon$1 cannot be cast to C +// at C.f(:15) + new C().kaboom +} + diff --git a/tests/pos/unapply.scala b/tests/pos/unapply.scala new file mode 100644 index 000000000000..ba885be7375a --- /dev/null +++ b/tests/pos/unapply.scala @@ -0,0 +1,11 @@ +object test { + class Foo[T](val arg : T) + + object Foo { + def unapply [a](m : Foo[a]) = Some (m.arg) + } + def matchAndGetArgFromFoo[b]( e:Foo[b]):b = {e match { case Foo(x) => x }} +// Unapply node here will have type argument [a] instantiated to scala.Nothing: +// UnApply(TypeApply(Select(Ident(Foo),unapply),List(TypeTree[TypeVar(PolyParam(a) -> TypeRef(ThisType(TypeRef(NoPrefix,scala)),Nothing))])),List(),List(Bind(x,Ident(_)))) +// but the type of the UnApply node itself is correct: RefinedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,)),test$)),Foo), test$$Foo$$a, TypeAlias(TypeRef(NoPrefix,a))) +}