diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 63f79c8d391f..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,11 +46,12 @@ 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, new Constructors)/*, List(new LambdaLift)*/ diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala index f6c62cf30b8b..50aaafc82279 100644 --- a/src/dotty/tools/dotc/TypeErasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -8,8 +8,8 @@ import util.DotClass /** Erased types are: * - * TypeRefWithFixedSym(denot is ClassDenotation) - * TermRefWithFixedSym(denot is SymDenotation) + * TypeRef(prefix is ignored, denot is ClassDenotation) + * TermRef(prefix is ignored, denot is SymDenotation) * JavaArrayType * AnnotatedType * MethodType @@ -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(this(tp.prefix), tp.symbol.asTerm) + this(tp.widen) case ThisType(_) | SuperType(_, _) => tp case ExprType(rt) => @@ -270,7 +290,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) val erasedDecls = decls.filteredScope(d => !d.isType || d.isClass) - tp.derivedClassInfo(NoPrefix, parents, erasedDecls, this(tp.selfType)) + 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(_) => @@ -279,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)) @@ -328,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) @@ -338,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/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index fb9ad24b3b0d..f1ccfdb75e3e 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -812,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 } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 45e0aff541eb..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) @@ -566,13 +563,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) - def appliedIfMethod(implicit ctx: Context): Tree = { - tree.tpe.widen match { - case fntpe: MethodType => appliedToArgs(Nil) - case _ => tree - } - } - def appliedToType(targ: Type)(implicit ctx: Context): Tree = appliedToTypes(targ :: Nil) @@ -582,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) @@ -599,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. @@ -625,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 { diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 731917c42bea..5a6c9fa89012 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -208,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) 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/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index de27282fbb44..a27dd6614fac 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -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) 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 53c193994457..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 } 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 cea9be604048..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. */ @@ -431,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 @@ -612,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 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 6145bdffb809..d93e4eb09338 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1173,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 ae75bcb2d365..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 diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 9cb348dfff5e..7bde1ba4ffa4 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -16,6 +16,7 @@ import SymDenotations._ import Types._ import Decorators._ import DenotTransformers._ +import util.Positions._ import collection.mutable /** This transform @@ -76,7 +77,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor case _ => } assert(accessors.hasSameLengthAs(vparamsWithOuter), - i"lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter") + i"lengths differ for $cls, param accs = $accessors, params = ($vparamsWithOuter%, %)") } val paramSyms = vparamsWithOuter map (_.symbol) @@ -177,6 +178,10 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor 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 { @@ -186,7 +191,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val sym = stat.symbol if (isRetained(sym)) { if (!rhs.isEmpty && !isWildcardArg(rhs)) - constrStats += Assign(ref(sym), intoConstr(rhs)).withPos(stat.pos) + constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos) clsStats += cpy.ValDef(stat)(rhs = EmptyTree) } else if (!rhs.isEmpty) { @@ -210,7 +215,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // The initializers for the retained accessors */ val copyParams = accessorFields.filter(isRetained).map(acc => - Assign(ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) + assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos)) // Drop accessors that are not retained from class scope val dropped = usage.dropped diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala index e656c8438958..1d0307398fc3 100644 --- a/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/src/dotty/tools/dotc/transform/ElimByName.scala @@ -76,16 +76,29 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform cpy.Apply(tree)(tree.fun, args1) } - private def becomesFunction(symd: SymDenotation)(implicit ctx: Context) = + /** 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)) - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = { - val origDenot = originalDenotation(tree) - if (becomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType])) + /** 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)) @@ -102,6 +115,6 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform } def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (becomesFunction(sym)) transformParamInfo(tp) + if (exprBecomesFunction(sym)) transformParamInfo(tp) else elimByNameParams(tp) } diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/transform/ElimLocals.scala similarity index 100% rename from src/dotty/tools/dotc/ElimLocals.scala rename to src/dotty/tools/dotc/transform/ElimLocals.scala diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index e99a3a91ce35..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) } @@ -366,8 +369,7 @@ object Erasure extends TypeTestsCasts{ tparams = Nil, 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 e07be8603d1b..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])) diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/transform/Flatten.scala similarity index 100% rename from src/dotty/tools/dotc/Flatten.scala rename to src/dotty/tools/dotc/transform/Flatten.scala 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 5ae9aba63217..2875327c4712 100644 --- a/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/src/dotty/tools/dotc/transform/SymUtils.scala @@ -8,6 +8,7 @@ import Symbols._ import Decorators._ import Names._ import StdNames._ +import NameOps._ import Flags._ import language.implicitConversions @@ -46,4 +47,21 @@ class SymUtils(val self: Symbol) extends AnyVal { 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 46886afb6f94..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 @@ -46,6 +47,7 @@ class TreeChecker { case _ => Nil } + def check(phasesToRun: Seq[Phase], ctx: Context) = { println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}") val checkingCtx = ctx.fresh @@ -98,8 +100,7 @@ class TreeChecker { private def checkOwner(tree: untpd.Tree)(implicit ctx: Context): Unit = { def ownerMatches(symOwner: Symbol, ctxOwner: Symbol): Boolean = symOwner == ctxOwner || - ctxOwner.isWeakOwner && (!(ctxOwner is Method | Lazy | Mutable) || (ctxOwner is Label)) && - ownerMatches(symOwner, ctxOwner.owner) + ctxOwner.isWeakOwner && ownerMatches(symOwner, ctxOwner.owner) if(!ownerMatches(tree.symbol.owner, ctx.owner)) { assert(ownerMatches(tree.symbol.owner, ctx.owner), i"bad owner; ${tree.symbol} has owner ${tree.symbol.owner}, expected was ${ctx.owner}\n" + @@ -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) = { def isPrimaryConstructorReturn = ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass) - if (ctx.mode.isExpr && !isPrimaryConstructorReturn) + 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 6f5f29b6034c..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 @@ -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 035f19028d7c..a06fe37e31ec 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -672,7 +672,6 @@ trait Applications extends Compatibility { self: Typer => unapplyFn.tpe.widen match { case mt: MethodType if mt.paramTypes.length == 1 && !mt.isDependent => - val m = mt val unapplyArgType = mt.paramTypes.head unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $selType") def wpt = widenForMatchSelector(selType) // needed? diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 79d86de06789..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) } } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 46fd17d15822..80eb5965c63f 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -394,7 +394,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val setterTypeRaw = pre select (setterName, setter) val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) val lhs2 = untpd.rename(lhsCore, setterName).withType(setterType) - typed(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) + typedUnadapted(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => reassignmentToVal } 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 aa8cc05524d7..9d5e7b1fe0b0 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/" @@ -31,6 +28,7 @@ class tests extends CompilerTest { val dotcDir = "./src/dotty/" + @Test def pos_t2168_pat = compileFile(posDir, "t2168", doErase) @Test def pos_erasure = compileFile(posDir, "erasure", doErase) @Test def pos_Coder() = compileFile(posDir, "Coder", doErase) @Test def pos_blockescapes() = compileFile(posDir, "blockescapes", doErase) @@ -92,7 +90,7 @@ class tests extends CompilerTest { @Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2) @Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2) @Test def neg_t1048 = compileFile(negDir, "t1048", xerrors = 1) - @Test def nef_t1279a = compileFile(negDir, "t1279a", xerrors = 1) + @Test def neg_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) @@ -101,7 +99,6 @@ class tests extends CompilerTest { @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) @@ -114,7 +111,7 @@ class tests extends CompilerTest { @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", 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/t7093-generics-encoding.scala b/tests/neg/t7093-generics-encoding.scala new file mode 100644 index 000000000000..cea106668f08 --- /dev/null +++ b/tests/neg/t7093-generics-encoding.scala @@ -0,0 +1,34 @@ +// Encoding with type fields of the +// variance test in pos. This time, the +// test does not pass because of the encoding +// This points to a difference between type members +// and parameters which we should investigate further. +object Test2 { + + trait A { + type X + protected[this] def f(x: X): X = x + } + + trait B extends A { + type X <: 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 { + type X <: 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/neg/t7093.scala b/tests/neg/t7093.scala new file mode 100644 index 000000000000..0cf0c658d663 --- /dev/null +++ b/tests/neg/t7093.scala @@ -0,0 +1,13 @@ + +object Test2 { + + trait A { + type X + protected[this] def f(x: X): X = x + } + + trait B extends A { + type X <: B + def kaboom = f(new B {}) + } +} 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/test.scala b/tests/pending/pos/test.scala new file mode 100644 index 000000000000..8139ed3d914f --- /dev/null +++ b/tests/pending/pos/test.scala @@ -0,0 +1,12 @@ +object Test { + + object returns { + + def foo(x: Int): Int = { + return 3 + } + } + +} + + 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/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/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/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/test.scala b/tests/pos/test.scala new file mode 100644 index 000000000000..454686c3d7a7 --- /dev/null +++ b/tests/pos/test.scala @@ -0,0 +1,5 @@ +object Test { + + def foo(x: Int): Int = return 3 + +} diff --git a/tests/pos/trees.scala b/tests/pos/trees.scala new file mode 100644 index 000000000000..27785d248029 --- /dev/null +++ b/tests/pos/trees.scala @@ -0,0 +1,5 @@ +class Tree[T] + +class Empty[T] extends Tree[Nothing] +class Node[T](elem: T, l: Tree[T], r: Tree[T]) + 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))) +}