diff options
Diffstat (limited to 'src')
53 files changed, 893 insertions, 433 deletions
diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 7c8098bd1..0a5d4d7f3 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -4,22 +4,34 @@ import scala.reflect.ClassTag import scala.reflect.runtime.universe.TypeTag import scala.Predef.??? // this is currently ineffective, because of #530 -object DottyPredef { - /** implicits for ClassTag and TypeTag. Should be implemented with macros */ +abstract class I1 { implicit def classTag[T]: ClassTag[T] = scala.Predef.??? implicit def typeTag[T]: TypeTag[T] = scala.Predef.??? - - - /** ClassTags for final classes */ + implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double +} +abstract class I2 extends I1 { + implicit val FloatClassTag: ClassTag[Double] = ClassTag.Double +} +abstract class I3 extends I2 { + implicit val LongClassTag: ClassTag[Long] = ClassTag.Long +} +abstract class I4 extends I3 { implicit val IntClassTag: ClassTag[Int] = ClassTag.Int - implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte +} +abstract class I5 extends I4 { implicit val ShortClassTag: ClassTag[Short] = ClassTag.Short +} +abstract class I6 extends I5 { + implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte implicit val CharClassTag: ClassTag[Char] = ClassTag.Char - implicit val LongClassTag: ClassTag[Long] = ClassTag.Long - implicit val FloatClassTag: ClassTag[Float] = ClassTag.Float - implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double implicit val BooleanClassTag: ClassTag[Boolean] = ClassTag.Boolean implicit val UnitClassTag: ClassTag[Unit] = ClassTag.Unit implicit val NullClassTag: ClassTag[Null] = ClassTag.Null +} + +/** implicits for ClassTag and TypeTag. Should be implemented with macros */ +object DottyPredef extends I6 { + + /** ClassTags for final classes */ implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 94be0342b..e9b0a9676 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -46,7 +46,8 @@ class Compiler { new NormalizeFlags, new ExtensionMethods, new ExpandSAMs, - new TailRec), + new TailRec, + new ClassOf), List(new PatternMatcher, new ExplicitOuter, new Splitter), diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 6401c01c1..82c8c9d60 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -8,6 +8,7 @@ import Names._, StdNames._, NameOps._, Decorators._, Symbols._ import util.HashSet trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => + import TreeInfo._ // Note: the <: Type constraint looks necessary (and is needed to make the file compile in dotc). // But Scalac accepts the program happily without it. Need to find out why. @@ -24,12 +25,16 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => false } - /** Does tree contain an initialization part when seen as a member of a class or trait? + /** The largest subset of {NoInits, PureInterface} that a + * trait enclosing this statement can have as flags. + * Does tree contain an initialization part when seen as a member of a class or trait? */ - def isNoInitMember(tree: Tree): Boolean = unsplice(tree) match { - case EmptyTree | Import(_, _) | TypeDef(_, _) | DefDef(_, _, _, _, _) => true - case tree: ValDef => tree.unforcedRhs == EmptyTree - case _ => false + def defKind(tree: Tree): FlagSet = unsplice(tree) match { + case EmptyTree | _: Import => NoInitsInterface + case tree: TypeDef => if (tree.isClassDef) NoInits else NoInitsInterface + case tree: DefDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else NoInits + case tree: ValDef => if (tree.unforcedRhs == EmptyTree) NoInitsInterface else EmptyFlags + case _ => EmptyFlags } def isOpAssign(tree: Tree) = unsplice(tree) match { @@ -184,7 +189,8 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** Is this argument node of the form <expr> : _* ? */ - def isWildcardStarArg(tree: untpd.Tree)(implicit ctx: Context): Boolean = unsplice(tree) match { + def isWildcardStarArg(tree: Tree)(implicit ctx: Context): Boolean = unbind(tree) match { + case Typed(Ident(nme.WILDCARD_STAR), _) => true case Typed(_, Ident(tpnme.WILDCARD_STAR)) => true case Typed(_, tpt: TypeTree) => tpt.hasType && tpt.tpe.isRepeatedParam case _ => false @@ -272,6 +278,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] } trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => + import TreeInfo._ /** The purity level of this statement. * @return pure if statement has no side effects @@ -510,15 +517,17 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case nil => Nil } +} - private class PurityLevel(val x: Int) { +object TreeInfo { + class PurityLevel(val x: Int) extends AnyVal { def >= (that: PurityLevel) = x >= that.x def min(that: PurityLevel) = new PurityLevel(x min that.x) } - private val Pure = new PurityLevel(2) - private val Idempotent = new PurityLevel(1) - private val Impure = new PurityLevel(0) + val Pure = new PurityLevel(2) + val Idempotent = new PurityLevel(1) + val Impure = new PurityLevel(0) } /** a Match(Typed(_, tpt), _) must be translated into a switch if isSwitchAnnotation(tpt.tpe) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index defcf4838..a35e0e523 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -76,8 +76,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = ta.assignType(untpd.Block(stats, expr), stats, expr) - def maybeBlock(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree = - if (stats.isEmpty) expr else Block(stats, expr) + /** Join `stats` in front of `expr` creating a new block if necessary */ + def seq(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree = + if (stats.isEmpty) expr + else expr match { + case Block(estats, eexpr) => cpy.Block(expr)(stats ::: estats, eexpr) + case _ => Block(stats, expr) + } def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = ta.assignType(untpd.If(cond, thenp, elsep), thenp, elsep) @@ -302,7 +307,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { true case pre: ThisType => pre.cls.isStaticOwner || - tp.symbol.is(ParamOrAccessor) && ctx.owner.enclosingClass == pre.cls + tp.symbol.is(ParamOrAccessor) && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough // and was spuriously triggered in case inner class would inherit from outer one // eg anonymous TypeMap inside TypeMap.andThen diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index 27d5effa5..9e9974bdc 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -32,6 +32,14 @@ object Config { */ final val checkConstraintsPropagated = false + /** Check that constraints of globally committable typer states are closed */ + final val checkConstraintsClosed = true + + /** Check that no type appearing as the info of a SymDenotation contains + * skolem types. + */ + final val checkNoSkolemsInInfo = false + /** Type comparer will fail with an assert if the upper bound * of a constrained parameter becomes Nothing. This should be turned * on only for specific debugging as normally instantiation to Nothing diff --git a/src/dotty/tools/dotc/core/Constraint.scala b/src/dotty/tools/dotc/core/Constraint.scala index 5a758f144..19f93ce47 100644 --- a/src/dotty/tools/dotc/core/Constraint.scala +++ b/src/dotty/tools/dotc/core/Constraint.scala @@ -146,4 +146,7 @@ abstract class Constraint extends Showable { /** Check that no constrained parameter contains itself as a bound */ def checkNonCyclic()(implicit ctx: Context): Unit + + /** Check that constraint only refers to PolyParams bound by itself */ + def checkClosed()(implicit ctx: Context): Unit } diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index b00896117..c9deaab10 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -386,13 +386,6 @@ object Contexts { final def withOwner(owner: Symbol): Context = if (owner ne this.owner) fresh.setOwner(owner) else this - final def withMode(mode: Mode): Context = - if (mode != this.mode) fresh.setMode(mode) else this - - final def addMode(mode: Mode): Context = withMode(this.mode | mode) - final def maskMode(mode: Mode): Context = withMode(this.mode & mode) - final def retractMode(mode: Mode): Context = withMode(this.mode &~ mode) - override def toString = "Context(\n" + (outersIterator map ( ctx => s" owner = ${ctx.owner}, scope = ${ctx.scope}") mkString "\n") @@ -444,6 +437,21 @@ object Contexts { def setDebug = setSetting(base.settings.debug, true) } + implicit class ModeChanges(val c: Context) extends AnyVal { + final def withMode(mode: Mode): Context = + if (mode != c.mode) c.fresh.setMode(mode) else c + + final def addMode(mode: Mode): Context = withMode(c.mode | mode) + final def maskMode(mode: Mode): Context = withMode(c.mode & mode) + final def retractMode(mode: Mode): Context = withMode(c.mode &~ mode) + } + + implicit class FreshModeChanges(val c: FreshContext) extends AnyVal { + final def addMode(mode: Mode): c.type = c.setMode(c.mode | mode) + final def maskMode(mode: Mode): c.type = c.setMode(c.mode & mode) + final def retractMode(mode: Mode): c.type = c.setMode(c.mode &~ mode) + } + /** A class defining the initial context with given context base * and set of possible settings. */ diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index fdeee82de..e3348d4f3 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -271,6 +271,16 @@ class Definitions { lazy val BoxedFloatClass = ctx.requiredClass("java.lang.Float") lazy val BoxedDoubleClass = ctx.requiredClass("java.lang.Double") + lazy val BoxedBooleanModule = ctx.requiredModule("java.lang.Boolean") + lazy val BoxedByteModule = ctx.requiredModule("java.lang.Byte") + lazy val BoxedShortModule = ctx.requiredModule("java.lang.Short") + lazy val BoxedCharModule = ctx.requiredModule("java.lang.Character") + lazy val BoxedIntModule = ctx.requiredModule("java.lang.Integer") + lazy val BoxedLongModule = ctx.requiredModule("java.lang.Long") + lazy val BoxedFloatModule = ctx.requiredModule("java.lang.Float") + lazy val BoxedDoubleModule = ctx.requiredModule("java.lang.Double") + lazy val BoxedVoidModule = ctx.requiredModule("java.lang.Void") + lazy val ByNameParamClass2x = specialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, AnyType) lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, AnyType) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 9cbac9115..70ca88702 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -274,21 +274,31 @@ object Denotations { val sym1 = denot1.symbol val sym2 = denot2.symbol val sym2Accessible = sym2.isAccessibleFrom(pre) - def unshadowed(sym: Symbol, from: Symbol) = { - val symOwner = sym.owner - val fromOwner = from.owner - !fromOwner.derivesFrom(symOwner) || fromOwner.eq(symOwner) + + /** Does `sym1` come before `sym2` in the linearization of `pre`? */ + def precedes(sym1: Symbol, sym2: Symbol) = { + def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match { + case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1) + case Nil => true + } + sym1.derivesFrom(sym2) || + !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses) } - /** Preference according to partial pre-order (isConcrete, unshadowed) */ + + /** Preference according to partial pre-order (isConcrete, precedes) */ def preferSym(sym1: Symbol, sym2: Symbol) = - sym1.isAsConcrete(sym2) && (!sym2.isAsConcrete(sym1) || unshadowed(sym1, sym2)) + sym1.eq(sym2) || + sym1.isAsConcrete(sym2) && + (!sym2.isAsConcrete(sym1) || precedes(sym1.owner, sym2.owner)) + /** Sym preference provided types also override */ - def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) = + def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = preferSym(sym1, sym2) && info1.overrides(info2) - if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2 + + if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2 else { val sym1Accessible = sym1.isAccessibleFrom(pre) - if (sym1Accessible && prefer(info1, sym1, info2, sym2)) denot1 + if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1 else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1 else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2 else { diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 3efadcb00..759dff0d4 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -467,6 +467,8 @@ object Flags { /** Pure interfaces always have these flags */ final val PureInterfaceCreationFlags = Trait | NoInits | PureInterface + final val NoInitsInterface = NoInits | PureInterface + /** The flags of the self symbol */ final val SelfSymFlags = Private | Local | Deferred diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala index 21d003451..7e27ee628 100644 --- a/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -399,7 +399,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def removeParam(ps: List[PolyParam]) = ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx) - def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int) = tp match { + def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match { case bounds @ TypeBounds(lo, hi) => def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { @@ -424,7 +424,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } bounds.derivedTypeBounds(replaceIn(lo, isUpper = false), replaceIn(hi, isUpper = true)) - case _ => tp + case _ => + tp.substParam(param, replacement) } var current = @@ -438,8 +439,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } - def remove(pt: PolyType)(implicit ctx: Context): This = - newConstraint(boundsMap.remove(pt), lowerMap.remove(pt), upperMap.remove(pt)) + def remove(pt: PolyType)(implicit ctx: Context): This = { + def removeFromOrdering(po: ParamOrdering) = { + def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = { + val bndss1 = bndss.map(_.filterConserve(_.binder ne pt)) + if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1 + } + po.remove(pt).mapValuesNow(removeFromBoundss) + } + newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap)) + } def isRemovable(pt: PolyType, removedParam: Int = -1): Boolean = { val entries = boundsMap(pt) @@ -491,6 +500,19 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } + override def checkClosed()(implicit ctx: Context): Unit = { + def isFreePolyParam(tp: Type) = tp match { + case PolyParam(binder, _) => !contains(binder) + case _ => false + } + def checkClosedType(tp: Type, where: String) = + if (tp != null) + assert(!tp.existsPart(isFreePolyParam), i"unclosed constraint: $this refers to $tp in $where") + boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds"))) + lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower")))) + upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper")))) + } + private var myUninstVars: mutable.ArrayBuffer[TypeVar] = _ /** The uninstantiated typevars of this constraint */ diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 0ec3320bb..b086308a2 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -285,6 +285,9 @@ object Phases { */ def relaxedTyping: Boolean = false + /** Overridden by FrontEnd */ + def isTyper = false + def exists: Boolean = true private var myPeriod: Period = Periods.InvalidPeriod diff --git a/src/dotty/tools/dotc/core/Skolemization.scala b/src/dotty/tools/dotc/core/Skolemization.scala deleted file mode 100644 index fb47cb62a..000000000 --- a/src/dotty/tools/dotc/core/Skolemization.scala +++ /dev/null @@ -1,126 +0,0 @@ -package dotty.tools.dotc -package core - -import Symbols._, Types._, Contexts._ -import collection.mutable - -/** Methods to add and remove skolemtypes. - * - * Skolem types are generated when comparing refinements. - * A skolem type is simply a fresh singleton type that has a given type - * as underlying type. - * Two skolem types are equal if they refer to the same underlying type. - * To avoid unsoundness, skolem types have to be kept strictly local to the - * comparison, they are not allowed to escape the lifetime of a comparison - * by surviving in a context or in GADT bounds. - */ -trait Skolemization { - - implicit val ctx: Context - - protected var skolemsOutstanding = false - - def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match { - case tp: SingletonType if tp.isStable => - tp - case tp: ValueType => - skolemsOutstanding = true - SkolemType(tp) - case tp: TypeProxy => - ensureStableSingleton(tp.underlying) - } - - /** Approximate a type `tp` with a type that does not contain skolem types. - * @param toSuper if true, return the smallest supertype of `tp` with this property - * else return the largest subtype. - */ - final def deSkolemize(tp: Type, toSuper: Boolean): Type = - if (skolemsOutstanding) deSkolemize(tp, if (toSuper) 1 else -1, Set()) - else tp - - private def deSkolemize(tp: Type, variance: Int, seen: Set[SkolemType]): Type = - ctx.traceIndented(s"deskolemize $tp, variance = $variance, seen = $seen = ") { - def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType, newSeen: Set[SkolemType] = seen) = - if (variance == 0) NoType - else deSkolemize(if (variance < 0) lo else hi, variance, newSeen) - tp match { - case tp: SkolemType => - if (seen contains tp) NoType - else approx(hi = tp.binder, newSeen = seen + tp) - case tp: NamedType => - val sym = tp.symbol - if (sym.isStatic) tp - else { - val pre1 = deSkolemize(tp.prefix, variance, seen) - if (pre1.exists && !pre1.isRef(defn.NothingClass)) tp.derivedSelect(pre1) - else { - ctx.log(s"deskolem: $tp: ${tp.info}") - tp.info match { - case TypeBounds(lo, hi) => approx(lo, hi) - case info => approx(defn.NothingType, info) - } - } - } - case _: ThisType | _: BoundType | _: SuperType | NoType | NoPrefix => - tp - case tp: RefinedType => - val parent1 = deSkolemize(tp.parent, variance, seen) - if (parent1.exists) { - val refinedInfo1 = deSkolemize(tp.refinedInfo, variance, seen) - if (refinedInfo1.exists) - tp.derivedRefinedType(parent1, tp.refinedName, refinedInfo1) - else - approx(hi = parent1) - } - else approx() - case tp: TypeAlias => - val alias1 = deSkolemize(tp.alias, variance * tp.variance, seen) - if (alias1.exists) tp.derivedTypeAlias(alias1) - else approx(hi = TypeBounds.empty) - case tp: TypeBounds => - val lo1 = deSkolemize(tp.lo, -variance, seen) - val hi1 = deSkolemize(tp.hi, variance, seen) - if (lo1.exists && hi1.exists) tp.derivedTypeBounds(lo1, hi1) - else approx(hi = - if (lo1.exists) TypeBounds.lower(lo1) - else if (hi1.exists) TypeBounds.upper(hi1) - else TypeBounds.empty) - case tp: ClassInfo => - val pre1 = deSkolemize(tp.prefix, variance, seen) - if (pre1.exists) tp.derivedClassInfo(pre1) - else NoType - case tp: AndOrType => - val tp1d = deSkolemize(tp.tp1, variance, seen) - val tp2d = deSkolemize(tp.tp2, variance, seen) - if (tp1d.exists && tp2d.exists) - tp.derivedAndOrType(tp1d, tp2d) - else if (tp.isAnd) - approx(hi = tp1d & tp2d) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d - else - approx(lo = tp1d & tp2d) - case tp: WildcardType => - val bounds1 = deSkolemize(tp.optBounds, variance, seen) - if (bounds1.exists) tp.derivedWildcardType(bounds1) - else WildcardType - case _ => - if (tp.isInstanceOf[MethodicType]) assert(variance != 0, tp) - deSkolemizeMap.mapOver(tp, variance, seen) - } - } - - object deSkolemizeMap extends TypeMap { - private var seen: Set[SkolemType] = _ - def apply(tp: Type) = deSkolemize(tp, variance, seen) - def mapOver(tp: Type, variance: Int, seen: Set[SkolemType]) = { - val savedVariance = this.variance - val savedSeen = this.seen - this.variance = variance - this.seen = seen - try super.mapOver(tp) - finally { - this.variance = savedVariance - this.seen = savedSeen - } - } - } -} diff --git a/src/dotty/tools/dotc/core/Substituters.scala b/src/dotty/tools/dotc/core/Substituters.scala index 77ecf7fba..e4bbf2305 100644 --- a/src/dotty/tools/dotc/core/Substituters.scala +++ b/src/dotty/tools/dotc/core/Substituters.scala @@ -179,21 +179,21 @@ trait Substituters { this: Context => .mapOver(tp) } - final def substSkolem(tp: Type, from: Type, to: Type, theMap: SubstSkolemMap): Type = + final def substRefinedThis(tp: Type, from: Type, to: Type, theMap: SubstRefinedThisMap): Type = tp match { - case tp @ SkolemType(binder) => + case tp @ RefinedThis(binder) => if (binder eq from) to else tp case tp: NamedType => if (tp.currentSymbol.isStatic) tp - else tp.derivedSelect(substSkolem(tp.prefix, from, to, theMap)) + else tp.derivedSelect(substRefinedThis(tp.prefix, from, to, theMap)) case _: ThisType | _: BoundType | NoPrefix => tp case tp: RefinedType => - tp.derivedRefinedType(substSkolem(tp.parent, from, to, theMap), tp.refinedName, substSkolem(tp.refinedInfo, from, to, theMap)) + tp.derivedRefinedType(substRefinedThis(tp.parent, from, to, theMap), tp.refinedName, substRefinedThis(tp.refinedInfo, from, to, theMap)) case tp: TypeAlias => - tp.derivedTypeAlias(substSkolem(tp.alias, from, to, theMap)) + tp.derivedTypeAlias(substRefinedThis(tp.alias, from, to, theMap)) case _ => - (if (theMap != null) theMap else new SubstSkolemMap(from, to)) + (if (theMap != null) theMap else new SubstRefinedThisMap(from, to)) .mapOver(tp) } @@ -222,7 +222,7 @@ trait Substituters { this: Context => case tp: NamedType => if (tp.currentSymbol.isStatic) tp else tp.derivedSelect(substParams(tp.prefix, from, to, theMap)) - case _: ThisType | NoPrefix | _: SkolemType => + case _: ThisType | NoPrefix => tp case tp: RefinedType => tp.derivedRefinedType(substParams(tp.parent, from, to, theMap), tp.refinedName, substParams(tp.refinedInfo, from, to, theMap)) @@ -266,8 +266,8 @@ trait Substituters { this: Context => def apply(tp: Type): Type = substThis(tp, from, to, this) } - final class SubstSkolemMap(from: Type, to: Type) extends DeepTypeMap { - def apply(tp: Type): Type = substSkolem(tp, from, to, this) + final class SubstRefinedThisMap(from: Type, to: Type) extends DeepTypeMap { + def apply(tp: Type): Type = substRefinedThis(tp, from, to, this) } final class SubstParamMap(from: ParamType, to: Type) extends DeepTypeMap { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index d2b0d5030..d8dddb082 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -40,7 +40,7 @@ trait SymDenotations { this: Context => } def stillValid(denot: SymDenotation): Boolean = - if (denot is ValidForever) true + if (denot.is(ValidForever) || denot.isRefinementClass) true else { val initial = denot.initial if (initial ne denot) @@ -49,6 +49,7 @@ trait SymDenotations { this: Context => val owner = denot.owner.denot stillValid(owner) && ( !owner.isClass + || owner.isRefinementClass || (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol) || denot.isSelfSym) } catch { @@ -79,6 +80,7 @@ object SymDenotations { super.validFor_=(p) } */ + if (Config.checkNoSkolemsInInfo) assertNoSkolems(initInfo) // ------ Getting and setting fields ----------------------------- @@ -114,6 +116,12 @@ object SymDenotations { /** Unset given flags(s) of this denotation */ final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags } + /** Set applicable flags from `flags` which is a subset of {NoInits, PureInterface} */ + final def setApplicableFlags(flags: FlagSet): Unit = { + val mask = if (myFlags.is(Trait)) NoInitsInterface else NoInits + setFlag(flags & mask) + } + /** Has this denotation one of the flags in `fs` set? */ final def is(fs: FlagSet)(implicit ctx: Context) = { (if (fs <= FromStartFlags) myFlags else flags) is fs @@ -168,8 +176,8 @@ object SymDenotations { } protected[dotc] final def info_=(tp: Type) = { - /* - def illegal: String = s"illegal type for $this: $tp" + /* // DEBUG + def illegal: String = s"illegal type for $this: $tp" if (this is Module) // make sure module invariants that allow moduleClass and sourceModule to work are kept. tp match { case tp: ClassInfo => assert(tp.selfInfo.isInstanceOf[TermRefBySym], illegal) @@ -178,6 +186,7 @@ object SymDenotations { case _ => } */ + if (Config.checkNoSkolemsInInfo) assertNoSkolems(initInfo) myInfo = tp } @@ -507,7 +516,7 @@ object SymDenotations { !isAnonymousFunction && !isCompanionMethod - /** Is this a setter? */ + /** Is this a getter? */ final def isGetter(implicit ctx: Context) = (this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix @@ -1049,8 +1058,28 @@ object SymDenotations { s"$kindString $name" } + // ----- Sanity checks and debugging */ + def debugString = toString + "#" + symbol.id // !!! DEBUG + def hasSkolems(tp: Type): Boolean = tp match { + case tp: SkolemType => true + case tp: NamedType => hasSkolems(tp.prefix) + case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) + case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType) + case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType) + case tp: ExprType => hasSkolems(tp.resType) + case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) + case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) + case tp: AnnotatedType => hasSkolems(tp.tpe) + case tp: TypeVar => hasSkolems(tp.inst) + case _ => false + } + + def assertNoSkolems(tp: Type) = + if (!this.isSkolem) + assert(!hasSkolems(tp), s"assigning type $tp containing skolems to $this") + // ----- copies and transforms ---------------------------------------- protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor) diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index 0478b1b7b..602bdba80 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -408,7 +408,7 @@ object Symbols { /** Subclass tests and casts */ final def isTerm(implicit ctx: Context): Boolean = (if(isDefinedInCurrentRun) lastDenot else denot).isTerm - + final def isType(implicit ctx: Context): Boolean = (if(isDefinedInCurrentRun) lastDenot else denot).isType diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 7f3f8a446..f466cee77 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -387,9 +387,9 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => firstBaseArgInfo(defn.SeqClass) } - def containsSkolemType(target: Type)(implicit ctx: Context): Boolean = { + def containsRefinedThis(target: Type)(implicit ctx: Context): Boolean = { def recur(tp: Type): Boolean = tp.stripTypeVar match { - case SkolemType(tp) => + case RefinedThis(tp) => tp eq target case tp: NamedType => tp.info match { @@ -446,7 +446,7 @@ class TypeApplications(val self: Type) extends AnyVal { def replacements(rt: RefinedType): List[Type] = for (sym <- boundSyms) - yield TypeRef(SkolemType(rt), correspondingParamName(sym)) + yield TypeRef(RefinedThis(rt), correspondingParamName(sym)) def rewrite(tp: Type): Type = tp match { case tp @ RefinedType(parent, name: TypeName) => @@ -489,7 +489,7 @@ class TypeApplications(val self: Type) extends AnyVal { val lambda = defn.lambdaTrait(boundSyms.map(_.variance)) val substitutedRHS = (rt: RefinedType) => { val argRefs = boundSyms.indices.toList.map(i => - SkolemType(rt).select(tpnme.lambdaArgName(i))) + RefinedThis(rt).select(tpnme.lambdaArgName(i))) tp.subst(boundSyms, argRefs).bounds.withVariance(1) } val res = RefinedType(lambda.typeRef, tpnme.Apply, substitutedRHS) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 069525db0..ea815f6c0 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -16,7 +16,7 @@ import scala.util.control.NonFatal /** Provides methods to compare types. */ -class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling with Skolemization { +class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { implicit val ctx: Context = initctx val state = ctx.typerState @@ -276,7 +276,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi } case tp1: SkolemType => tp2 match { - case tp2: SkolemType if tp1 == tp2 => true + case tp2: SkolemType if !ctx.phase.isTyper && tp1.info <:< tp2.info => true case _ => thirdTry(tp1, tp2) } case tp1: TypeVar => @@ -536,18 +536,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi * rebase both itself and the member info of `tp` on a freshly created skolem type. */ protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = { - val saved = skolemsOutstanding - try { - val rebindNeeded = tp2.refinementRefersToThis - val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 - val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substSkolem(tp2, base) else tp2.refinedInfo - def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) - def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance - case mbr: SingleDenotation => qualifies(mbr) - case _ => mbr hasAltWith qualifies - } - /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${base.member(name).info.show} $rinfo2", subtyping) /*<|<*/ { - memberMatches(base member name) || + val rebindNeeded = tp2.refinementRefersToThis + val base = if (rebindNeeded) ensureStableSingleton(tp1) else tp1 + val rinfo2 = if (rebindNeeded) tp2.refinedInfo.substRefinedThis(tp2, base) else tp2.refinedInfo + def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2) + def memberMatches(mbr: Denotation): Boolean = mbr match { // inlined hasAltWith for performance + case mbr: SingleDenotation => qualifies(mbr) + case _ => mbr hasAltWith qualifies + } + /*>|>*/ ctx.traceIndented(i"hasMatchingMember($base . $name :? ${tp2.refinedInfo}) ${base.member(name).info.show} $rinfo2", subtyping) /*<|<*/ { + memberMatches(base member name) || tp1.isInstanceOf[SingletonType] && { // special case for situations like: // class C { type T } @@ -558,9 +556,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi case _ => false } } - } } - finally skolemsOutstanding = saved + } + + final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match { + case tp: SingletonType if tp.isStable => tp + case tp: ValueType => SkolemType(tp) + case tp: TypeProxy => ensureStableSingleton(tp.underlying) } /** Skip refinements in `tp2` which match corresponding refinements in `tp1`. @@ -645,13 +647,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi private def narrowGADTBounds(tr: NamedType, bound: Type, isUpper: Boolean): Boolean = ctx.mode.is(Mode.GADTflexible) && { val tparam = tr.symbol - val bound1 = deSkolemize(bound, toSuper = !isUpper) - typr.println(s"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound1 ${bound1.isRef(tparam)}") - !bound1.isRef(tparam) && { + typr.println(s"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.isRef(tparam)}") + !bound.isRef(tparam) && { val oldBounds = ctx.gadt.bounds(tparam) val newBounds = - if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound1) - else TypeBounds(oldBounds.lo | bound1, oldBounds.hi) + if (isUpper) TypeBounds(oldBounds.lo, oldBounds.hi & bound) + else TypeBounds(oldBounds.lo | bound, oldBounds.hi) isSubType(newBounds.lo, newBounds.hi) && { ctx.gadt.setBounds(tparam, newBounds); true } } diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala index 92e32d4b1..abe5418d4 100644 --- a/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/src/dotty/tools/dotc/core/TypeErasure.scala @@ -110,7 +110,14 @@ object TypeErasure { private def erasureCtx(implicit ctx: Context) = if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode.FutureDefsOK) else ctx - def erasure(tp: Type, semiEraseVCs: Boolean = true)(implicit ctx: Context): Type = + /** The standard erasure of a Scala type. + * + * @param tp The type to erase. + * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType + * (they will be fully erased in [[ElimErasedValueType]]). + * If false, they are erased like normal classes. + */ + def erasure(tp: Type, semiEraseVCs: Boolean = false)(implicit ctx: Context): Type = erasureFn(isJava = false, semiEraseVCs, isConstructor = false, wildcardOK = false)(tp)(erasureCtx) def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = { @@ -134,7 +141,7 @@ object TypeErasure { case tp: ThisType => tp case tp => - erasure(tp) + erasure(tp, semiEraseVCs = true) } /** The symbol's erased info. This is the type's erasure, except for the following symbols: @@ -389,7 +396,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = { val cls = tref.symbol.asClass val underlying = underlyingOfValueClass(cls) - ErasedValueType(cls, erasure(underlying)) + ErasedValueType(cls, erasure(underlying, semiEraseVCs = true)) } diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index acbd5b6f0..2b2ef83a2 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -12,16 +12,61 @@ import ast.tpd._ trait TypeOps { this: Context => // TODO: Make standalone object. - final def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = { + /** The type `tp` as seen from prefix `pre` and owner `cls`. See the spec + * for what this means. Called very often, so the code is optimized heavily. + * + * A tricky aspect is what to do with unstable prefixes. E.g. say we have a class + * + * class C { type T; def f(x: T): T } + * + * and an expression `e` of type `C`. Then computing the type of `e.f` leads + * to the query asSeenFrom(`C`, `(x: T)T`). What should it's result be? The + * naive answer `(x: C.T)C.T` is incorrect given that we treat `C.T` as the existential + * `exists(c: C)c.T`. What we need to do instead is to skolemize the existential. So + * the answer would be `(x: c.T)c.T` for some (unknown) value `c` of type `C`. + * `c.T` is expressed in the compiler as a skolem type `Skolem(C)`. + * + * Now, skolemization is messy and expensive, so we want to do it only if we absolutely + * must. We must skolemize if an unstable prefix is used in nonvariant or + * contravariant position of the return type of asSeenFrom. + * + * In the implementation of asSeenFrom, we first try to run asSeenFrom without + * skolemizing. If that would be incorrect we will be told by the fact that + * `unstable` is set in the passed AsSeenFromMap. In that case we run asSeenFrom + * again with a skolemized prefix. + * + * In the interest of speed we want to avoid creating an AsSeenFromMap every time + * asSeenFrom is called. So we do this here only if the prefix is unstable + * (because then we need the map as a container for the unstable field). For + * stable prefixes the map is `null`; it might however be instantiated later + * for more complicated types. + */ + final def asSeenFrom(tp: Type, pre: Type, cls: Symbol): Type = { + val m = if (pre.isStable || !ctx.phase.isTyper) null else new AsSeenFromMap(pre, cls) + var res = asSeenFrom(tp, pre, cls, m) + if (m != null && m.unstable) asSeenFrom(tp, SkolemType(pre), cls) else res + } + /** Helper method, taking a map argument which is instantiated only for more + * complicated cases of asSeenFrom. + */ + private def asSeenFrom(tp: Type, pre: Type, cls: Symbol, theMap: AsSeenFromMap): Type = { + + /** Map a `C.this` type to the right prefix. If the prefix is unstable and + * the `C.this` occurs in nonvariant or contravariant position, mark the map + * to be unstable. + */ def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ { if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) tp - else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) + else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { + if (theMap != null && theMap.currentVariance <= 0 && !pre.isStable) + theMap.unstable = true pre match { case SuperType(thispre, _) => thispre case _ => pre } + } else if ((pre.termSymbol is Package) && !(thiscls is Package)) toPrefix(pre.select(nme.PACKAGE), cls, thiscls) else @@ -33,7 +78,20 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case tp: NamedType => val sym = tp.symbol if (sym.isStatic) tp - else tp.derivedSelect(asSeenFrom(tp.prefix, pre, cls, theMap)) + else { + val prevStable = theMap == null || !theMap.unstable + val pre1 = asSeenFrom(tp.prefix, pre, cls, theMap) + if (theMap != null && theMap.unstable && prevStable) { + pre1.member(tp.name).info match { + case TypeAlias(alias) => + // try to follow aliases of this will avoid skolemization. + theMap.unstable = false + return alias + case _ => + } + } + tp.derivedSelect(pre1) + } case tp: ThisType => toPrefix(pre, cls, tp.cls) case _: BoundType | NoPrefix => @@ -43,7 +101,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. asSeenFrom(tp.parent, pre, cls, theMap), tp.refinedName, asSeenFrom(tp.refinedInfo, pre, cls, theMap)) - case tp: TypeAlias => + case tp: TypeAlias if theMap == null => // if theMap exists, need to do the variance calculation tp.derivedTypeAlias(asSeenFrom(tp.alias, pre, cls, theMap)) case _ => (if (theMap != null) theMap else new AsSeenFromMap(pre, cls)) @@ -52,8 +110,114 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } + /** The TypeMap handling the asSeenFrom in more complicated cases */ class AsSeenFromMap(pre: Type, cls: Symbol) extends TypeMap { def apply(tp: Type) = asSeenFrom(tp, pre, cls, this) + + /** A method to export the current variance of the map */ + def currentVariance = variance + + /** A field which indicates whether an unstable argument in nonvariant + * or contravariant position was encountered. + */ + var unstable = false + } + + /** Approximate a type `tp` with a type that does not contain skolem types. + */ + final def deskolemize(tp: Type): Type = deskolemize(tp, 1, Set()) + + private def deskolemize(tp: Type, variance: Int, seen: Set[SkolemType]): Type = { + def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType, newSeen: Set[SkolemType] = seen) = + if (variance == 0) NoType + else deskolemize(if (variance < 0) lo else hi, variance, newSeen) + tp match { + case tp: SkolemType => + if (seen contains tp) NoType + else approx(hi = tp.info, newSeen = seen + tp) + case tp: NamedType => + val sym = tp.symbol + if (sym.isStatic) tp + else { + val pre1 = deskolemize(tp.prefix, variance, seen) + if (pre1 eq tp.prefix) tp + else { + val d = tp.prefix.member(tp.name) + d.info match { + case TypeAlias(alias) => deskolemize(alias, variance, seen) + case _ => + if (pre1.exists && !pre1.isRef(defn.NothingClass)) tp.derivedSelect(pre1) + else { + ctx.log(s"deskolem: $tp: ${tp.info}") + tp.info match { + case TypeBounds(lo, hi) => approx(lo, hi) + case info => approx(defn.NothingType, info) + } + } + } + } + } + case _: ThisType | _: BoundType | _: SuperType | NoType | NoPrefix => + tp + case tp: RefinedType => + val parent1 = deskolemize(tp.parent, variance, seen) + if (parent1.exists) { + val refinedInfo1 = deskolemize(tp.refinedInfo, variance, seen) + if (refinedInfo1.exists) + tp.derivedRefinedType(parent1, tp.refinedName, refinedInfo1) + else + approx(hi = parent1) + } + else approx() + case tp: TypeAlias => + val alias1 = deskolemize(tp.alias, variance * tp.variance, seen) + if (alias1.exists) tp.derivedTypeAlias(alias1) + else approx(hi = TypeBounds.empty) + case tp: TypeBounds => + val lo1 = deskolemize(tp.lo, -variance, seen) + val hi1 = deskolemize(tp.hi, variance, seen) + if (lo1.exists && hi1.exists) tp.derivedTypeBounds(lo1, hi1) + else approx(hi = + if (lo1.exists) TypeBounds.lower(lo1) + else if (hi1.exists) TypeBounds.upper(hi1) + else TypeBounds.empty) + case tp: ClassInfo => + val pre1 = deskolemize(tp.prefix, variance, seen) + if (pre1.exists) tp.derivedClassInfo(pre1) + else NoType + case tp: AndOrType => + val tp1d = deskolemize(tp.tp1, variance, seen) + val tp2d = deskolemize(tp.tp2, variance, seen) + if (tp1d.exists && tp2d.exists) + tp.derivedAndOrType(tp1d, tp2d) + else if (tp.isAnd) + approx(hi = tp1d & tp2d) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d + else + approx(lo = tp1d & tp2d) + case tp: WildcardType => + val bounds1 = deskolemize(tp.optBounds, variance, seen) + if (bounds1.exists) tp.derivedWildcardType(bounds1) + else WildcardType + case _ => + if (tp.isInstanceOf[MethodicType]) assert(variance != 0, tp) + deskolemizeMap.mapOver(tp, variance, seen) + } + } + + object deskolemizeMap extends TypeMap { + private var seen: Set[SkolemType] = _ + def apply(tp: Type) = deskolemize(tp, variance, seen) + def mapOver(tp: Type, variance: Int, seen: Set[SkolemType]) = { + val savedVariance = this.variance + val savedSeen = this.seen + this.variance = variance + this.seen = seen + try super.mapOver(tp) + finally { + this.variance = savedVariance + this.seen = savedSeen + } + } } /** Implementation of Types#simplified */ diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala index 91cda1dd8..ba7a6a806 100644 --- a/src/dotty/tools/dotc/core/TyperState.scala +++ b/src/dotty/tools/dotc/core/TyperState.scala @@ -9,6 +9,7 @@ import util.{SimpleMap, DotClass} import reporting._ import printing.{Showable, Printer} import printing.Texts._ +import config.Config import collection.mutable class TyperState(r: Reporter) extends DotClass with Showable { @@ -19,7 +20,7 @@ class TyperState(r: Reporter) extends DotClass with Showable { /** The current constraint set */ def constraint: Constraint = new OrderingConstraint(SimpleMap.Empty, SimpleMap.Empty, SimpleMap.Empty) - def constraint_=(c: Constraint): Unit = {} + def constraint_=(c: Constraint)(implicit ctx: Context): Unit = {} /** The uninstantiated variables */ def uninstVars = constraint.uninstVars @@ -85,7 +86,10 @@ extends TyperState(r) { private var myConstraint: Constraint = previous.constraint override def constraint = myConstraint - override def constraint_=(c: Constraint) = myConstraint = c + override def constraint_=(c: Constraint)(implicit ctx: Context) = { + if (Config.checkConstraintsClosed && isGlobalCommittable) c.checkClosed() + myConstraint = c + } private var myEphemeral: Boolean = previous.ephemeral diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index ae9088f00..1270466e9 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -48,6 +48,7 @@ object Types { * | | +--- SuperType * | | +--- ConstantType * | | +--- MethodParam + * | | +----RefinedThis * | | +--- SkolemType * | +- PolyParam * | +- RefinedType @@ -89,9 +90,10 @@ object Types { final def isValueType: Boolean = this.isInstanceOf[ValueType] /** Does this type denote a stable reference (i.e. singleton type)? */ - final def isStable(implicit ctx: Context): Boolean = this match { - case tp: TermRef => tp.termSymbol.isStable + final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { + case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable case _: SingletonType => true + case tp: RefinedType => tp.parent.isStable case NoPrefix => true case _ => false } @@ -154,18 +156,6 @@ object Types { false } - /** A type T is a legal prefix in a type selection T#A if - * T is stable or T contains no abstract types except possibly A. - * !!! Todo: What about non-final vals that contain abstract types? - */ - final def isLegalPrefixFor(selector: Name)(implicit ctx: Context): Boolean = - isStable || { - val absTypeNames = memberNames(abstractTypeNameFilter) - if (absTypeNames.nonEmpty) typr.println(s"abstract type members of ${this.showWithUnderlying()}: $absTypeNames") - absTypeNames.isEmpty || - absTypeNames.head == selector && absTypeNames.tail.isEmpty - } - /** Is this type guaranteed not to have `null` as a value? * For the moment this is only true for modules, but it could * be refined later. @@ -449,7 +439,7 @@ object Types { def goRefined(tp: RefinedType) = { val pdenot = go(tp.parent) val rinfo = - if (tp.refinementRefersToThis) tp.refinedInfo.substSkolem(tp, pre) + if (tp.refinementRefersToThis) tp.refinedInfo.substRefinedThis(tp, pre) else tp.refinedInfo if (name.isTypeName) { // simplified case that runs more efficiently val jointInfo = @@ -594,7 +584,7 @@ object Types { */ final def asSeenFrom(pre: Type, cls: Symbol)(implicit ctx: Context): Type = track("asSeenFrom") { if (!cls.membersNeedAsSeenFrom(pre)) this - else ctx.asSeenFrom(this, pre, cls, null) + else ctx.asSeenFrom(this, pre, cls) } // ----- Subtype-related -------------------------------------------- @@ -611,6 +601,19 @@ object Types { ctx.typeComparer.isSameType(this, that) } + /** Is this type a primitive value type which can be widened to the primitive value type `that`? */ + def isValueSubType(that: Type)(implicit ctx: Context) = widenExpr match { + case self: TypeRef if defn.ScalaValueClasses contains self.symbol => + that.widenExpr match { + case that: TypeRef if defn.ScalaValueClasses contains that.symbol => + defn.isValueSubClass(self.symbol, that.symbol) + case _ => + false + } + case _ => + false + } + /** Is this type a legal type for a member that overrides another * member of type `that`? This is the same as `<:<`, except that * the types ()T and => T are identified, and T is seen as overriding @@ -833,7 +836,7 @@ object Types { object instantiate extends TypeMap { var isSafe = true def apply(tp: Type): Type = tp match { - case TypeRef(SkolemType(`pre`), name) if name.isLambdaArgName => + case TypeRef(RefinedThis(`pre`), name) if name.isLambdaArgName => val TypeAlias(alias) = member(name).info alias case tp: TypeVar if !tp.inst.exists => @@ -856,13 +859,15 @@ object Types { if (pre.refinedName ne name) loop(pre.parent, pre.refinedName :: resolved) else if (!pre.refinementRefersToThis) alias else alias match { - case TypeRef(SkolemType(`pre`), aliasName) => lookupRefined(aliasName) // (1) + case TypeRef(RefinedThis(`pre`), aliasName) => lookupRefined(aliasName) // (1) case _ => if (name == tpnme.Apply) betaReduce(alias) else NoType // (2) } case _ => loop(pre.parent, resolved) } - case SkolemType(binder) => + case RefinedThis(binder) => binder.lookupRefined(name) + case SkolemType(tp) => + tp.lookupRefined(name) case pre: WildcardType => WildcardType case pre: TypeRef => @@ -1035,8 +1040,8 @@ object Types { if (cls.isStaticOwner) this else ctx.substThis(this, cls, tp, null) /** Substitute all occurrences of `SkolemType(binder)` by `tp` */ - final def substSkolem(binder: Type, tp: Type)(implicit ctx: Context): Type = - ctx.substSkolem(this, binder, tp, null) + final def substRefinedThis(binder: Type, tp: Type)(implicit ctx: Context): Type = + ctx.substRefinedThis(this, binder, tp, null) /** Substitute a bound type by some other type */ final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = @@ -1413,7 +1418,7 @@ object Types { * to an (unbounded) wildcard type. * * (2) Reduce a type-ref `T { X = U; ... } # X` to `U` - * provided `U` does not refer with a SkolemType to the + * provided `U` does not refer with a RefinedThis to the * refinement type `T { X = U; ... }` */ def reduceProjection(implicit ctx: Context): Type = { @@ -1827,7 +1832,7 @@ object Types { def refinementRefersToThis(implicit ctx: Context): Boolean = { if (!refinementRefersToThisKnown) { - refinementRefersToThisCache = refinedInfo.containsSkolemType(this) + refinementRefersToThisCache = refinedInfo.containsRefinedThis(this) refinementRefersToThisKnown = true } refinementRefersToThisCache @@ -1863,7 +1868,7 @@ object Types { derivedRefinedType(parent.EtaExpand, refinedName, refinedInfo) else if (false) RefinedType(parent, refinedName, refinedInfo) - else RefinedType(parent, refinedName, rt => refinedInfo.substSkolem(this, SkolemType(rt))) + else RefinedType(parent, refinedName, rt => refinedInfo.substRefinedThis(this, RefinedThis(rt))) } /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */ @@ -2019,7 +2024,7 @@ object Types { def isJava = false def isImplicit = false - private val resType = resultTypeExp(this) + private[core] val resType = resultTypeExp(this) assert(resType.exists) override def resultType(implicit ctx: Context): Type = @@ -2247,7 +2252,7 @@ object Types { } } - // ----- Bound types: MethodParam, PolyParam, SkolemType -------------------------- + // ----- Bound types: MethodParam, PolyParam, RefinedThis -------------------------- abstract class BoundType extends CachedProxyType with ValueType { type BT <: Type @@ -2320,20 +2325,39 @@ object Types { } } - /** A skolem type reference with underlying type `binder`. */ - case class SkolemType(binder: Type) extends BoundType with SingletonType { - type BT = Type + /** a this-reference to an enclosing refined type `binder`. */ + case class RefinedThis(binder: RefinedType) extends BoundType with SingletonType { + type BT = RefinedType override def underlying(implicit ctx: Context) = binder - def copyBoundType(bt: BT) = SkolemType(bt) + def copyBoundType(bt: BT) = RefinedThis(bt) // need to customize hashCode and equals to prevent infinite recursion for // refinements that refer to the refinement type via this override def computeHash = addDelta(binder.identityHash, 41) override def equals(that: Any) = that match { - case that: SkolemType => this.binder eq that.binder + case that: RefinedThis => this.binder eq that.binder case _ => false } - override def toString = s"SkolemType(${binder.hashCode})" + override def toString = s"RefinedThis(${binder.hashCode})" + } + + // ----- Skolem types ----------------------------------------------- + + /** A skolem type reference with underlying type `binder`. */ + abstract case class SkolemType(info: Type) extends CachedProxyType with ValueType with SingletonType { + override def underlying(implicit ctx: Context) = info + def derivedSkolemType(info: Type)(implicit ctx: Context) = + if (info eq this.info) this else SkolemType(info) + override def computeHash: Int = identityHash + override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + override def toString = s"Skolem($info)" + } + + final class CachedSkolemType(info: Type) extends SkolemType(info) + + object SkolemType { + def apply(info: Type)(implicit ctx: Context) = + unique(new CachedSkolemType(info)) } // ------------ Type variables ---------------------------------------- @@ -2434,6 +2458,9 @@ object Types { if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound)) inst = inst.approximateUnion + if (ctx.typerState.isGlobalCommittable) + assert(!inst.isInstanceOf[PolyParam], i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}") + instantiateWith(inst) } @@ -2894,6 +2921,9 @@ object Types { case tp: AndOrType => tp.derivedAndOrType(this(tp.tp1), this(tp.tp2)) + case tp: SkolemType => + tp.derivedSkolemType(this(tp.info)) + case tp @ AnnotatedType(annot, underlying) => val underlying1 = this(underlying) if (underlying1 eq underlying) tp else tp.derivedAnnotatedType(mapOver(annot), underlying1) @@ -3033,6 +3063,9 @@ object Types { case tp: AndOrType => this(this(x, tp.tp1), tp.tp2) + case tp: SkolemType => + this(x, tp.info) + case AnnotatedType(annot, underlying) => this(applyToAnnot(x, annot), underlying) diff --git a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 106a6510d..94ed94052 100644 --- a/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -103,10 +103,9 @@ Standard-Section: "ASTs" TopLevelStat* TERMREFpkg fullyQualified_NameRef TERMREF possiblySigned_NameRef qual_Type THIS clsRef_Type - SKOLEMtype refinedType_ASTRef + REFINEDthis refinedType_ASTRef SHARED path_ASTRef - Constant = UNITconst FALSEconst TRUEconst @@ -158,7 +157,6 @@ Standard-Section: "ASTs" TopLevelStat* LAZY OVERRIDE INLINE // macro - ABSOVERRIDE // abstract override STATIC // mapped to static Java member OBJECT // an object or its class TRAIT // a trait @@ -239,29 +237,28 @@ object TastyFormat { final val LAZY = 14 final val OVERRIDE = 15 final val INLINE = 16 - final val ABSOVERRIDE = 17 - final val STATIC = 18 - final val OBJECT = 19 - final val TRAIT = 20 - final val LOCAL = 21 - final val SYNTHETIC = 22 - final val ARTIFACT = 23 - final val MUTABLE = 24 - final val LABEL = 25 - final val FIELDaccessor = 26 - final val CASEaccessor = 27 - final val COVARIANT = 28 - final val CONTRAVARIANT = 29 - final val SCALA2X = 30 - final val DEFAULTparameterized = 31 - final val INSUPERCALL = 32 + final val STATIC = 17 + final val OBJECT = 18 + final val TRAIT = 19 + final val LOCAL = 20 + final val SYNTHETIC = 21 + final val ARTIFACT = 22 + final val MUTABLE = 23 + final val LABEL = 24 + final val FIELDaccessor = 25 + final val CASEaccessor = 26 + final val COVARIANT = 27 + final val CONTRAVARIANT = 28 + final val SCALA2X = 29 + final val DEFAULTparameterized = 30 + final val INSUPERCALL = 31 final val SHARED = 64 final val TERMREFdirect = 65 final val TYPEREFdirect = 66 final val TERMREFpkg = 67 final val TYPEREFpkg = 68 - final val SKOLEMtype = 69 + final val REFINEDthis = 69 final val BYTEconst = 70 final val SHORTconst = 71 final val CHARconst = 72 @@ -350,7 +347,6 @@ object TastyFormat { | LAZY | OVERRIDE | INLINE - | ABSOVERRIDE | STATIC | OBJECT | TRAIT @@ -398,7 +394,6 @@ object TastyFormat { case LAZY => "LAZY" case OVERRIDE => "OVERRIDE" case INLINE => "INLINE" - case ABSOVERRIDE => "ABSOVERRIDE" case STATIC => "STATIC" case OBJECT => "OBJECT" case TRAIT => "TRAIT" @@ -420,7 +415,7 @@ object TastyFormat { case TYPEREFdirect => "TYPEREFdirect" case TERMREFpkg => "TERMREFpkg" case TYPEREFpkg => "TYPEREFpkg" - case SKOLEMtype => "SKOLEMtype" + case REFINEDthis => "REFINEDthis" case BYTEconst => "BYTEconst" case SHORTconst => "SHORTconst" case CHARconst => "CHARconst" diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 365b5d268..8847d2de3 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -198,9 +198,11 @@ class TreePickler(pickler: TastyPickler) { case tpe: SuperType => writeByte(SUPERtype) withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} - case tpe: SkolemType => - writeByte(SKOLEMtype) + case tpe: RefinedThis => + writeByte(REFINEDthis) writeRef(pickledTypes.get(tpe.binder).asInstanceOf[Addr]) + case tpe: SkolemType => + pickleType(tpe.info) case tpe: RefinedType => val args = tpe.argInfos(interpolate = false) if (args.isEmpty) { @@ -520,7 +522,7 @@ class TreePickler(pickler: TastyPickler) { if (sym.isTerm) { if (flags is Implicit) writeByte(IMPLICIT) if ((flags is Lazy) && !(sym is Module)) writeByte(LAZY) - if (flags is AbsOverride) writeByte(ABSOVERRIDE) + if (flags is AbsOverride) { writeByte(ABSTRACT); writeByte(OVERRIDE) } if (flags is Mutable) writeByte(MUTABLE) if (flags is Accessor) writeByte(FIELDaccessor) if (flags is CaseAccessor) writeByte(CASEaccessor) diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e753bdcab..e76b2e764 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -6,7 +6,7 @@ package tasty import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ import StdNames._, Denotations._, Flags._, Constants._, Annotations._ import util.Positions._ -import dotty.tools.dotc.ast.{tpd, Trees, untpd} +import ast.{tpd, Trees, untpd} import Trees._ import Decorators._ import TastyUnpickler._, TastyBuffer._, PositionPickler._ @@ -254,8 +254,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } case THIS => ThisType.raw(readType().asInstanceOf[TypeRef]) - case SKOLEMtype => - SkolemType(readTypeRef()) + case REFINEDthis => + RefinedThis(readTypeRef().asInstanceOf[RefinedType]) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) @@ -350,9 +350,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } /** Create symbol of definition node and enter in symAtAddr map - * @return true iff the definition does not contain initialization code + * @return the largest subset of {NoInits, PureInterface} that a + * trait owning this symbol can have as flags. */ - def createSymbol()(implicit ctx: Context): Boolean = { + def createSymbol()(implicit ctx: Context): FlagSet = { val start = currentAddr val tag = readByte() val end = readEnd() @@ -408,7 +409,10 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { sym.completer.withDecls(newScope) forkAt(templateStart).indexTemplateParams()(localContext(sym)) } - tag != VALDEF || rhsIsEmpty + if (isClass) NoInits + else if (sym.isType || sym.isConstructor || flags.is(Deferred)) NoInitsInterface + else if (tag == VALDEF) EmptyFlags + else NoInits } /** Read modifier list into triplet of flags, annotations and a privateWithin @@ -427,7 +431,12 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { case PRIVATE => addFlag(Private) case INTERNAL => ??? // addFlag(Internal) case PROTECTED => addFlag(Protected) - case ABSTRACT => addFlag(Abstract) + case ABSTRACT => + readByte() + nextByte match { + case OVERRIDE => addFlag(AbsOverride) + case _ => flags |= Abstract + } case FINAL => addFlag(Final) case SEALED => addFlag(Sealed) case CASE => addFlag(Case) @@ -435,7 +444,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { case LAZY => addFlag(Lazy) case OVERRIDE => addFlag(Override) case INLINE => addFlag(Inline) - case ABSOVERRIDE => addFlag(AbsOverride) case STATIC => addFlag(JavaStatic) case OBJECT => addFlag(Module) case TRAIT => addFlag(Trait) @@ -472,25 +480,26 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { /** Create symbols for a definitions in statement sequence between * current address and `end`. - * @return true iff none of the statements contains initialization code + * @return the largest subset of {NoInits, PureInterface} that a + * trait owning the indexed statements can have as flags. */ - def indexStats(end: Addr)(implicit ctx: Context): Boolean = { - val noInitss = + def indexStats(end: Addr)(implicit ctx: Context): FlagSet = { + val flagss = until(end) { nextByte match { case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM => createSymbol() case IMPORT => skipTree() - true + NoInitsInterface case PACKAGE => processPackage { (pid, end) => implicit ctx => indexStats(end) } case _ => skipTree() - false + EmptyFlags } } - noInitss.forall(_ == true) + (NoInitsInterface /: flagss)(_ & _) } /** Process package with given operation `op`. The operation takes as arguments @@ -631,8 +640,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) { } else EmptyValDef setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe) - val noInits = fork.indexStats(end) - if (noInits) cls.setFlag(NoInits) + cls.setApplicableFlags(fork.indexStats(end)) val constr = readIndexedDef().asInstanceOf[DefDef] def mergeTypeParamsAndAliases(tparams: List[TypeDef], stats: List[Tree]): (List[Tree], List[Tree]) = diff --git a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala index aa1fd9a90..b080a97b6 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala @@ -220,7 +220,7 @@ object PickleBuffer { DEFERRED_PKL -> Deferred, FINAL_PKL -> Final, METHOD_PKL -> Method, - INTERFACE_PKL -> PureInterface, + INTERFACE_PKL -> NoInitsInterface, MODULE_PKL -> Module, IMPLICIT_PKL -> Implicit, SEALED_PKL -> Sealed, diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index b4549a8d8..9498cf43c 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -689,7 +689,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else { def addRefinement(tp: Type, sym: Symbol) = { def subst(info: Type, rt: RefinedType) = - if (clazz.isClass) info.substThis(clazz.asClass, SkolemType(rt)) + if (clazz.isClass) info.substThis(clazz.asClass, RefinedThis(rt)) else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. RefinedType(tp, sym.name, subst(sym.info, _)) } diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 12c94677f..5d2309e95 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -12,7 +12,7 @@ import typer.Mode import scala.annotation.switch class PlainPrinter(_ctx: Context) extends Printer { - protected[this] implicit def ctx: Context = _ctx.fresh.addMode(Mode.Printing) + protected[this] implicit def ctx: Context = _ctx.addMode(Mode.Printing) protected def maxToTextRecursions = 100 @@ -223,16 +223,16 @@ class PlainPrinter(_ctx: Context) extends Printer { case SuperType(thistpe: SingletonType, _) => toTextRef(thistpe).map(_.replaceAll("""\bthis$""", "super")) case SuperType(thistpe, _) => - "Super(" ~ toTextLocal(thistpe) ~ ")" + "Super(" ~ toTextGlobal(thistpe) ~ ")" case tp @ ConstantType(value) => toText(value) case MethodParam(mt, idx) => nameString(mt.paramNames(idx)) - case sk: SkolemType => - sk.binder match { - case rt: RefinedType => s"${nameString(rt.typeSymbol)}{...}.this" - case _ => "<skolem>" - } + case tp: RefinedThis => + s"${nameString(tp.binder.typeSymbol)}{...}.this" + case tp: SkolemType => + if (homogenizedView) toText(tp.info) + else "<unknown instance of type " ~ toTextGlobal(tp.info) ~ ">" } } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index fa238f32c..70fab7e0f 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -148,7 +148,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" case tp: SelectionProto => - return "?{ " ~ toText(tp.name) ~ ": " ~ toText(tp.memberProto) ~ " }" + return "?{ " ~ toText(tp.name) ~ (" " provided !tp.name.decode.last.isLetterOrDigit) ~ + ": " ~ toText(tp.memberProto) ~ " }" case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) case tp @ FunProto(args, resultType, _) => @@ -179,7 +180,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { * * The body is simplified as follows * - if it is a TypeAlias, follow it - * - replace all references to of the form <skolem>.HK$i by references + * - replace all references to of the form <refined-this>.HK$i by references * without a prefix, because the latter print nicer. * */ @@ -190,7 +191,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { // LambdaI{...}.HK$i val simplifyArgs = new TypeMap { override def apply(tp: Type) = tp match { - case tp @ TypeRef(SkolemType(_), name) if name.isLambdaArgName => + case tp @ TypeRef(RefinedThis(_), name) if name.isLambdaArgName => TypeRef(NoPrefix, tp.symbol.asType) case _ => mapOver(tp) diff --git a/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala b/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala index 6c2d63d10..9f9d2dd67 100644 --- a/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala +++ b/src/dotty/tools/dotc/transform/AugmentScala2Traits.scala @@ -11,6 +11,7 @@ import SymDenotations._ import Types._ import Decorators._ import DenotTransformers._ +import Annotations._ import StdNames._ import NameOps._ import ast.Trees._ @@ -80,9 +81,14 @@ class AugmentScala2Traits extends MiniPhaseTransform with IdentityDenotTransform for (sym <- mixin.info.decls) { if (needsForwarder(sym) || sym.isConstructor || sym.isGetter && sym.is(Lazy)) implClass.enter(implMethod(sym.asTerm)) - if (sym.isGetter && !sym.is(LazyOrDeferred) && - !sym.setter.exists && !sym.info.resultType.isInstanceOf[ConstantType]) - traitSetter(sym.asTerm).enteredAfter(thisTransform) + if (sym.isGetter) + if (sym.is(Lazy)) { + if (!sym.hasAnnotation(defn.VolatileAnnot)) + sym.addAnnotation(Annotation(defn.VolatileAnnot, Nil)) + } + else if (!sym.is(Deferred) && !sym.setter.exists && + !sym.info.resultType.isInstanceOf[ConstantType]) + traitSetter(sym.asTerm).enteredAfter(thisTransform) if (sym.is(PrivateAccessor, butNot = ExpandedName) && (sym.isGetter || sym.isSetter)) // strangely, Scala 2 fields are also methods that have Accessor set. sym.ensureNotPrivate.installAfter(thisTransform) diff --git a/src/dotty/tools/dotc/transform/ClassOf.scala b/src/dotty/tools/dotc/transform/ClassOf.scala new file mode 100644 index 000000000..4d6bf2dc9 --- /dev/null +++ b/src/dotty/tools/dotc/transform/ClassOf.scala @@ -0,0 +1,53 @@ +package dotty.tools.dotc +package transform + +import ast.tpd +import core.Constants.Constant +import core.Contexts.Context +import core.StdNames.nme +import core.Symbols.TermSymbol +import core.TypeErasure +import TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform} + +/** Rewrite `classOf` calls as follow: + * + * For every primitive class C whose boxed class is called B: + * classOf[C] -> B.TYPE + * For every non-primitive class D: + * classOf[D] -> Literal(Constant(erasure(D))) + */ +class ClassOf extends MiniPhaseTransform { + import tpd._ + + override def phaseName: String = "classOf" + + private var classOfMethod: TermSymbol = _ + + override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { + val predefModule = ctx.definitions.ScalaPredefModule + classOfMethod = ctx.requiredMethod(predefModule.moduleClass.asClass, nme.classOf) + this + } + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (tree.symbol eq classOfMethod) { + val tp = tree.args.head.tpe + val defn = ctx.definitions + val claz = tp.classSymbol + + def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_).ensureConforms(tree.tpe) + claz match { + case defn.BooleanClass => TYPE(defn.BoxedBooleanModule) + case defn.ByteClass => TYPE(defn.BoxedByteModule) + case defn.ShortClass => TYPE(defn.BoxedShortModule) + case defn.CharClass => TYPE(defn.BoxedCharModule) + case defn.IntClass => TYPE(defn.BoxedIntModule) + case defn.LongClass => TYPE(defn.BoxedLongModule) + case defn.FloatClass => TYPE(defn.BoxedFloatModule) + case defn.DoubleClass => TYPE(defn.BoxedDoubleModule) + case defn.UnitClass => TYPE(defn.BoxedVoidModule) + case _ => Literal(Constant(TypeErasure.erasure(tp))) + } + } else tree + } +} diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 100e9ff21..1d94b3552 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -156,6 +156,20 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor val constrStats, clsStats = new mutable.ListBuffer[Tree] + /** Map outer getters $outer and outer accessors $A$B$$$outer to the given outer parameter. */ + def mapOuter(outerParam: Symbol) = new TreeMap { + override def transform(tree: Tree)(implicit ctx: Context) = tree match { + case Apply(fn, Nil) + if (fn.symbol.is(OuterAccessor) + || fn.symbol.isGetter && fn.symbol.name == nme.OUTER + ) && + fn.symbol.info.resultType.classSymbol == outerParam.info.classSymbol => + ref(outerParam) + case _ => + super.transform(tree) + } + } + // 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 { @@ -174,6 +188,8 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor owner = constr.symbol).installAfter(thisTransform) constrStats += intoConstr(stat, sym) } + case DefDef(nme.CONSTRUCTOR, _, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => + clsStats += mapOuter(outerParam.symbol).transform(stat) case _: DefTree => clsStats += stat case _ => @@ -221,9 +237,15 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor case stats => (Nil, stats) } + val mappedSuperCalls = vparams match { + case (outerParam @ ValDef(nme.OUTER, _, _)) :: _ => + superCalls.map(mapOuter(outerParam.symbol).transform) + case _ => superCalls + } + cpy.Template(tree)( constr = cpy.DefDef(constr)( - rhs = Block(superCalls ::: copyParams ::: followConstrStats, unitLiteral)), + rhs = Block(mappedSuperCalls ::: copyParams ::: followConstrStats, unitLiteral)), body = clsStats.toList) } } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 1664db456..ad261c16b 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -268,7 +268,7 @@ object Erasure extends TypeTestsCasts{ class Typer extends typer.ReTyper with NoChecking { import Boxing._ - def erasedType(tree: untpd.Tree, semiEraseVCs: Boolean = true)(implicit ctx: Context): Type = + def erasedType(tree: untpd.Tree, semiEraseVCs: Boolean)(implicit ctx: Context): Type = tree.typeOpt match { case tp: TermRef if tree.isTerm => erasedRef(tp) case tp => erasure(tp, semiEraseVCs) @@ -296,7 +296,7 @@ object Erasure extends TypeTestsCasts{ /** This override is only needed to semi-erase type ascriptions */ override def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = { val Typed(expr, tpt) = tree - val tpt1 = promote(tpt) + val tpt1 = promote(tpt, semiEraseVCs = true) val expr1 = typed(expr, tpt1.tpe) assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1) } @@ -460,7 +460,7 @@ object Erasure extends TypeTestsCasts{ if (pt.isValueType) pt else { if (tree.typeOpt.derivesFrom(ctx.definitions.UnitClass)) tree.typeOpt - else erasure(tree.typeOpt) + else erasure(tree.typeOpt, semiEraseVCs = true) } } @@ -479,7 +479,9 @@ object Erasure extends TypeTestsCasts{ val MethodType(pnames, ptypes) = sym.info.resultType effectiveSym = sym.copy(info = MethodType(pnames, ptypes, defn.ObjectType)) } - val restpe = effectiveSym.info.resultType + val restpe = + if (effectiveSym.isConstructor) defn.UnitType + else effectiveSym.info.resultType val ddef1 = untpd.cpy.DefDef(ddef)( tparams = Nil, vparamss = (outer.paramDefs(effectiveSym) ::: ddef.vparamss.flatten) :: Nil, diff --git a/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 2416e4624..1650a244d 100644 --- a/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -25,20 +25,19 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => import ast.tpd._ - def noJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = - !cls.is(Trait) || - cls.superClass != defn.ObjectClass || - !cls.is(NoInits) || - !cls.directlyInheritedTraits.forall(_.is(NoInits)) || - ExplicitOuter.needsOuterIfReferenced(cls) || - cls.typeRef.fields.nonEmpty // Superaccessors already show up as abstract methods here, so no test necessary - + /** Is SAMType `cls` also a SAM under the rules of the JVM? */ + def isJvmSam(cls: ClassSymbol)(implicit ctx: Context): Boolean = + cls.is(NoInitsTrait) && + cls.superClass == defn.ObjectClass && + cls.directlyInheritedTraits.forall(_.is(NoInits)) && + !ExplicitOuter.needsOuterIfReferenced(cls) && + cls.typeRef.fields.isEmpty // Superaccessors already show up as abstract methods here, so no test necessary override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol => tpt.tpe match { case NoType => tree // it's a plain function - case tpe @ SAMType(_) if !noJvmSam(tpe.classSymbol.asClass) => + case tpe @ SAMType(_) if isJvmSam(tpe.classSymbol.asClass) => if (tpe isRef defn.PartialFunctionClass) toPartialFunction(tree) else tree case tpe => diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 912bc9b7f..eb231bfe7 100644 --- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -136,7 +136,9 @@ object ExplicitOuter { /** A new outer accessor or param accessor */ private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = { - ctx.newSymbol(owner, name, Synthetic | flags, cls.owner.enclosingClass.typeRef, coord = cls.coord) + val target = cls.owner.enclosingClass.typeRef + val info = if (flags.is(Method)) ExprType(target) else target + ctx.newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) } /** A new param accessor for the outer field in class `cls` */ @@ -302,7 +304,7 @@ object ExplicitOuter { val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls") if (treeCls == toCls) tree - else loop(tree select outerAccessor(treeCls.asClass)(outerAccessorCtx)) + else loop(tree.select(outerAccessor(treeCls.asClass)(outerAccessorCtx)).ensureApplied) } ctx.log(i"computing outerpath to $toCls from ${ctx.outersIterator.map(_.owner).toList}") loop(This(ctx.owner.enclosingClass.asClass)) diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 36a1b9b30..503016d8b 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -65,7 +65,7 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful } } - val underlying = erasure(underlyingOfValueClass(valueClass)) + val underlying = erasure(underlyingOfValueClass(valueClass), semiEraseVCs = true) val evt = ErasedValueType(valueClass, underlying) val u2evtSym = ctx.newSymbol(moduleSym, nme.U2EVT, Synthetic | Method, MethodType(List(nme.x_0), List(underlying), evt)) diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala index bffc7458e..043c92737 100644 --- a/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -384,14 +384,36 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform private def addFreeParams(tree: Tree, proxies: List[Symbol])(implicit ctx: Context, info: TransformerInfo): Tree = proxies match { case Nil => tree case proxies => + val sym = tree.symbol val ownProxies = - if (!tree.symbol.isConstructor) proxies - else proxies.map(_.copy(owner = tree.symbol, flags = Synthetic | Param)) + if (!sym.isConstructor) proxies + else proxies.map(_.copy(owner = sym, flags = Synthetic | Param)) val freeParamDefs = ownProxies.map(proxy => transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef]) + def proxyInit(field: Symbol, param: Symbol) = + transformFollowingDeep(memberRef(field).becomes(ref(param))) + + /** Map references to proxy fields `this.proxy` to proxy parameters */ + def mapProxies = new TreeMap { + override def transform(tree: Tree)(implicit ctx: Context) = tree match { + case Select(This(_), _) if proxies contains tree.symbol => + ref(tree.symbol.subst(proxies, ownProxies)) + case _ => + super.transform(tree) + } + } + + /** Initialize proxy fields from proxy parameters and map `rhs` from fields to parameters */ + def copyParams(rhs: Tree) = { + ctx.log(i"copy params ${proxies.map(_.showLocated)}%, %, own = ${ownProxies.map(_.showLocated)}%, %") + seq((proxies, ownProxies).zipped.map(proxyInit), mapProxies.transform(rhs)) + } + tree match { case tree: DefDef => - cpy.DefDef(tree)(vparamss = tree.vparamss.map(freeParamDefs ++ _)) + cpy.DefDef(tree)( + vparamss = tree.vparamss.map(freeParamDefs ++ _), + rhs = if (sym.isPrimaryConstructor) copyParams(tree.rhs) else tree.rhs) case tree: Template => cpy.Template(tree)(body = freeParamDefs ++ tree.body) } diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index e52e2537c..b57e4c592 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -68,11 +68,16 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { appendOffsetDefs.get(cls) match { case None => template case Some(data) => - cpy.Template(template)(body = data.defs ::: template.body) + cpy.Template(template)(body = addInFront(data.defs, template.body)) } } + private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { + case first :: rest if isSuperConstrCall(first) => first :: prefix ::: rest + case _ => prefix ::: stats + } + /** Replace a local lazy val inside a method, * with a LazyHolder from * dotty.runtime(eg dotty.runtime.LazyInt) diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala index 63e680414..de6cde8f2 100644 --- a/src/dotty/tools/dotc/transform/Mixin.scala +++ b/src/dotty/tools/dotc/transform/Mixin.scala @@ -20,7 +20,7 @@ import collection.mutable /** This phase performs the following transformations: * - * 1. (done in `traitDefs`) Map every concrete trait getter + * 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter * * <mods> def x(): T = expr * @@ -46,32 +46,45 @@ import collection.mutable * For every trait M directly implemented by the class (see SymUtils.mixin), in * reverse linearization order, add the following definitions to C: * - * 3.1 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M, - * in order of textual occurrence, produce the following: + * 3.1 (done in `traitInits`) For every parameter accessor `<mods> def x(): T` in M, + * in order of textual occurrence, add * - * 3.1.1 If `x` is also a member of `C`, and M is a Dotty trait: + * <mods> def x() = e + * + * where `e` is the constructor argument in C that corresponds to `x`. Issue + * an error if no such argument exists. + * + * 3.2 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M + * which is not a parameter accessor, in order of textual occurrence, produce the following: + * + * 3.2.1 If `x` is also a member of `C`, and M is a Dotty trait: * * <mods> def x(): T = super[M].initial$x() * - * 3.1.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: + * 3.2.2 If `x` is also a member of `C`, and M is a Scala 2.x trait: * * <mods> def x(): T = _ * - * 3.1.3 If `x` is not a member of `C`, and M is a Dotty trait: + * 3.2.3 If `x` is not a member of `C`, and M is a Dotty trait: * * super[M].initial$x() * - * 3.1.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. + * 3.2.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added. * * - * 3.2 (done in `superCallOpt`) The call: + * 3.3 (done in `superCallOpt`) The call: * * super[M].<init> * - * 3.3 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: + * 3.4 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M: * * <mods> def x_=(y: T) = () * + * 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait + * constructors. + * + * 5. (done in `transformSym`) Drop ParamAccessor flag from all parameter accessors in traits. + * * Conceptually, this is the second half of the previous mixin phase. It needs to run * after erasure because it copies references to possibly private inner classes and objects * into enclosing classes where they are not visible. This can only be done if all references @@ -86,7 +99,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait)) - sym.copySymDenotation(initFlags = sym.flags | Deferred).ensureNotPrivate + sym.copySymDenotation(initFlags = sym.flags &~ ParamAccessor | Deferred).ensureNotPrivate + else if (sym.isConstructor && sym.owner.is(Trait) && sym.info.firstParamTypes.nonEmpty) + sym.copySymDenotation(info = MethodType(Nil, sym.info.resultType)) else sym @@ -111,7 +126,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def traitDefs(stats: List[Tree]): List[Tree] = { val initBuf = new mutable.ListBuffer[Tree] stats.flatMap({ - case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => + case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) => // make initializer that has all effects of previous getter, // replace getter rhs with empty tree. val vsym = stat.symbol @@ -131,15 +146,22 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => }) ++ initBuf } - def transformSuper(tree: Tree): Tree = { + /** Map constructor call to a pair of a supercall and a list of arguments + * to be used as initializers of trait parameters if the target of the call + * is a trait. + */ + def transformConstructor(tree: Tree): (Tree, List[Tree]) = { val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree - superRef(tree.symbol, tree.pos).appliedToArgs(args) + val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil) + (superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs) } - val superCalls = ( + val superCallsAndArgs = ( for (p <- impl.parents if p.symbol.isConstructor) - yield p.symbol.owner -> transformSuper(p) + yield p.symbol.owner -> transformConstructor(p) ).toMap + val superCalls = superCallsAndArgs.mapValues(_._1) + val initArgs = superCallsAndArgs.mapValues(_._2) def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match { case Some(call) => @@ -155,24 +177,63 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform => def wasDeferred(sym: Symbol) = ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred } - def traitInits(mixin: ClassSymbol): List[Tree] = + def traitInits(mixin: ClassSymbol): List[Tree] = { + var argNum = 0 + def nextArgument() = initArgs.get(mixin) match { + case Some(arguments) => + try arguments(argNum) finally argNum += 1 + case None => + val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match { + case Some(parent) => ("lacks argument list", parent.pos) + case None => + ("""is indirectly implemented, + |needs to be implemented directly so that arguments can be passed""".stripMargin, + cls.pos) + } + ctx.error(i"parameterized $mixin $msg", pos) + EmptyTree + } + for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield { val isScala2x = mixin.is(Scala2x) def default = Underscore(getter.info.resultType) def initial = transformFollowing(superRef(initializer(getter)).appliedToNone) - if (isCurrent(getter) || getter.is(ExpandedName)) + + /** A call to the implementation of `getter` in `mixin`'s implementation class */ + def lazyGetterCall = { + def canbeImplClassGetter(sym: Symbol) = sym.info.firstParamTypes match { + case t :: Nil => t.isDirectRef(mixin) + case _ => false + } + val implClassGetter = mixin.implClass.info.nonPrivateDecl(getter.name) + .suchThat(canbeImplClassGetter).symbol + ref(mixin.implClass).select(implClassGetter).appliedTo(This(cls)) + } + + if (isCurrent(getter) || getter.is(ExpandedName)) { + val rhs = + if (ctx.atPhase(thisTransform)(implicit ctx => getter.is(ParamAccessor))) nextArgument() + else if (isScala2x) + if (getter.is(Lazy)) lazyGetterCall + else Underscore(getter.info.resultType) + else transformFollowing(superRef(initializer(getter)).appliedToNone) // transformFollowing call is needed to make memoize & lazy vals run - transformFollowing( - DefDef(implementation(getter.asTerm), if (isScala2x) default else initial)) + transformFollowing(DefDef(implementation(getter.asTerm), rhs)) + } else if (isScala2x) EmptyTree else initial } + } def setters(mixin: ClassSymbol): List[Tree] = for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList) yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos)) cpy.Template(impl)( + constr = + if (cls.is(Trait) && impl.constr.vparamss.flatten.nonEmpty) + cpy.DefDef(impl.constr)(vparamss = Nil :: Nil) + else impl.constr, parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)), body = if (cls is Trait) traitDefs(impl.body) diff --git a/src/dotty/tools/dotc/transform/NormalizeFlags.scala b/src/dotty/tools/dotc/transform/NormalizeFlags.scala index 2f5907b75..755846904 100644 --- a/src/dotty/tools/dotc/transform/NormalizeFlags.scala +++ b/src/dotty/tools/dotc/transform/NormalizeFlags.scala @@ -19,12 +19,7 @@ class NormalizeFlags extends MiniPhaseTransform with SymTransformer { thisTransf def transformSym(ref: SymDenotation)(implicit ctx: Context) = { var newFlags = ref.flags &~ Local - if (ref.is(NoInitsTrait) && ref.info.decls.forall(isPureInterfaceMember)) - newFlags |= PureInterface if (newFlags != ref.flags) ref.copySymDenotation(initFlags = newFlags) else ref } - - private def isPureInterfaceMember(sym: Symbol)(implicit ctx: Context) = - if (sym.isTerm) sym.is(Deferred) else !sym.isClass } diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 5fa17921f..af8da01ff 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -136,7 +136,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans def callDirect = tgt.select(nme.drop).appliedTo(Literal(Constant(n))) def callRuntime = ref(defn.traversableDropMethod).appliedTo(tgt, Literal(Constant(n))) - def needsRuntime = tgt.tpe derivesFrom defn.SeqClass /*typeOfMemberNamedDrop(tgt.tpe) == NoType*/ + def needsRuntime = !(tgt.tpe derivesFrom defn.SeqClass) /*typeOfMemberNamedDrop(tgt.tpe) == NoType*/ if (needsRuntime) callRuntime else callDirect } diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 1be0ef39b..4ce64da33 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -96,7 +96,7 @@ trait TypeTestsCasts { } else derivedTree(qual, defn.Any_asInstanceOf, argType) } - def erasedArg = erasure(tree.args.head.tpe, semiEraseVCs = false) + def erasedArg = erasure(tree.args.head.tpe) if (sym eq defn.Any_isInstanceOf) transformIsInstanceOf(qual, erasedArg) else if (sym eq defn.Any_asInstanceOf) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 9e9acf97a..c45db4ccc 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -915,7 +915,9 @@ trait Applications extends Compatibility { self: Typer => }} def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") { - (alts: @unchecked) match { + alts match { + case Nil => alts + case _ :: Nil => alts case alt :: alts1 => def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match { case alt :: alts1 => @@ -966,6 +968,51 @@ trait Applications extends Compatibility { self: Typer => def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] = alts filter (isApplicable(_, argTypes, resultType)) + /** Is `alt` a method or polytype whose result type after the first value parameter + * section conforms to the expected type `resultType`? If `resultType` + * is a `IgnoredProto`, pick the underlying type instead. + */ + def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match { + case IgnoredProto(ignored) => resultConforms(alt, ignored) + case _: ValueType => + alt.widen match { + case tp: PolyType => resultConforms(constrained(tp).resultType, resultType) + case tp: MethodType => constrainResult(tp.resultType, resultType) + case _ => true + } + case _ => true + } + + /** If the `chosen` alternative has a result type incompatible with the expected result + * type `pt`, run overloading resolution again on all alternatives that do match `pt`. + * If the latter succeeds with a single alternative, return it, otherwise + * fallback to `chosen`. + * + * Note this order of events is done for speed. One might be tempted to + * preselect alternatives by result type. But is slower, because it discriminates + * less. The idea is when searching for a best solution, as is the case in overloading + * resolution, we should first try criteria which are cheap and which have a high + * probability of pruning the search. result type comparisons are neither cheap nor + * do they prune much, on average. + */ + def adaptByResult(alts: List[TermRef], chosen: TermRef) = { + def nestedCtx = ctx.fresh.setExploreTyperState + pt match { + case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) => + alts.filter(alt => + (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match { + case Nil => chosen + case alt2 :: Nil => alt2 + case alts2 => + resolveOverloaded(alts2, pt) match { + case alt2 :: Nil => alt2 + case _ => chosen + } + } + case _ => chosen + } + } + val candidates = pt match { case pt @ FunProto(args, resultType, _) => val numArgs = args.length @@ -1027,15 +1074,20 @@ trait Applications extends Compatibility { self: Typer => case pt => alts filter (normalizedCompatible(_, pt)) } - if (isDetermined(candidates)) candidates - else narrowMostSpecific(candidates) match { - case result @ (alt1 :: alt2 :: _) => -// overload.println(i"ambiguous $alt1 $alt2") + narrowMostSpecific(candidates) match { + case Nil => Nil + case alt :: Nil => + adaptByResult(alts, alt) :: Nil + // why `alts` and not `candidates`? pos/array-overload.scala gives a test case. + // Here, only the Int-apply is a candidate, but it is not compatible with the result + // type. Picking the Byte-apply as the only result-compatible solution then forces + // the arguments (which are constants) to be adapted to Byte. If we had picked + // `candidates` instead, no solution would have been found. + case alts => +// overload.println(i"ambiguous $alts%, %") val deepPt = pt.deepenProto if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) - else result - case result => - result + else alts } } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b2d368e8c..cd06618e6 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -20,6 +20,7 @@ import annotation.unchecked import util.Positions._ import util.{Stats, SimpleMap} import util.common._ +import transform.SymUtils._ import Decorators._ import Uniques._ import ErrorReporting.{err, errorType, DiagnosticString} @@ -49,8 +50,11 @@ object Checking { if (cls.is(AbstractOrTrait)) ctx.error(d"$cls is abstract; cannot be instantiated", pos) if (!cls.is(Module)) { - val selfType = tp.givenSelfType.asSeenFrom(tref.prefix, cls.owner) - if (selfType.exists && !(tp <:< selfType)) + // Create a synthetic singleton type instance, and check whether + // it conforms to the self type of the class as seen from that instance. + val stp = SkolemType(tp) + val selfType = tref.givenSelfType.asSeenFrom(stp, cls) + if (selfType.exists && !(stp <:< selfType)) ctx.error(d"$tp does not conform to its self type $selfType; cannot be instantiated") } case _ => @@ -252,12 +256,6 @@ trait Checking { if (!tp.isStable && !tp.isErroneous) ctx.error(d"$tp is not stable", pos) - /** Check that type `tp` is a legal prefix for '#'. - * @return The type itself - */ - def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = - if (!tp.isLegalPrefixFor(selector)) ctx.error(d"$tp is not a valid prefix for '# $selector'", pos) - /** Check that `tp` is a class type with a stable prefix. Also, if `traitReq` is * true check that `tp` is a trait. * Stability checking is disabled in phases after RefChecks. @@ -334,9 +332,15 @@ trait Checking { } } - def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = { - ??? // to be done in later phase: check that class `cls` is legal in a new. - } + def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = + if (!ctx.isAfterTyper) { + val called = call.tpe.classSymbol + if (caller is Trait) + ctx.error(i"$caller may not call constructor of $called", call.pos) + else if (called.is(Trait) && !caller.mixins.contains(called)) + ctx.error(i"""$called is already implemented by super${caller.superClass}, + |its constructor cannot be called again""".stripMargin, call.pos) + } } trait NoChecking extends Checking { @@ -345,9 +349,9 @@ trait NoChecking extends Checking { override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree override def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = () override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () - override def checkLegalPrefix(tp: Type, selector: Name, pos: Position)(implicit ctx: Context): Unit = () override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () + override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () } diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 1c0e6a11f..4fa3d78eb 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -57,8 +57,8 @@ object EtaExpansion { /** Lift arguments that are not-idempotent into ValDefs in buffer `defs` * and replace by the idents of so created ValDefs. */ - def liftArgs(defs: mutable.ListBuffer[Tree], methType: Type, args: List[Tree])(implicit ctx: Context) = - methType match { + def liftArgs(defs: mutable.ListBuffer[Tree], methRef: Type, args: List[Tree])(implicit ctx: Context) = + methRef.widen match { case MethodType(paramNames, paramTypes) => (args, paramNames, paramTypes).zipped map { (arg, name, tp) => if (tp.isInstanceOf[ExprType]) arg diff --git a/src/dotty/tools/dotc/typer/FrontEnd.scala b/src/dotty/tools/dotc/typer/FrontEnd.scala index bd597f008..f417448bd 100644 --- a/src/dotty/tools/dotc/typer/FrontEnd.scala +++ b/src/dotty/tools/dotc/typer/FrontEnd.scala @@ -13,6 +13,7 @@ import scala.util.control.NonFatal class FrontEnd extends Phase { override def phaseName = "frontend" + override def isTyper = true def monitor(doing: String)(body: => Unit)(implicit ctx: Context) = try body diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index b03bcfcf9..a3ddca5d9 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -90,7 +90,7 @@ object Implicits { } if (refs.isEmpty) refs - else refs filter (refMatches(_)(ctx.fresh.setExploreTyperState.addMode(Mode.TypevarsMissContext))) // create a defensive copy of ctx to avoid constraint pollution + else refs filter (refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution } } @@ -382,21 +382,12 @@ trait Implicits { self: Typer => && !to.isError && !ctx.isAfterTyper && (ctx.mode is Mode.ImplicitsEnabled) - && { from.widenExpr match { - case from: TypeRef if defn.ScalaValueClasses contains from.symbol => - to.widenExpr match { - case to: TypeRef if defn.ScalaValueClasses contains to.symbol => - util.Stats.record("isValueSubClass") - return defn.isValueSubClass(from.symbol, to.symbol) - case _ => - } - case from: ValueType => - ; - case _ => - return false - } - inferView(dummyTreeOfType(from), to)(ctx.fresh.setExploreTyperState).isInstanceOf[SearchSuccess] - } + && from.isInstanceOf[ValueType] + && ( from.isValueSubType(to) + || inferView(dummyTreeOfType(from), to) + (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState) + .isInstanceOf[SearchSuccess] + ) ) /** Find an implicit conversion to apply to given tree `from` so that the @@ -491,7 +482,8 @@ trait Implicits { self: Typer => pt) val generated1 = adapt(generated, pt) lazy val shadowing = - typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(nestedContext.setNewTyperState) + typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto) + (nestedContext.addMode(Mode.ImplicitShadowing).setNewTyperState) def refMatches(shadowing: Tree): Boolean = ref.symbol == closureBody(shadowing).symbol || { shadowing match { @@ -501,7 +493,8 @@ trait Implicits { self: Typer => } if (ctx.typerState.reporter.hasErrors) nonMatchingImplicit(ref) - else if (contextual && !shadowing.tpe.isError && !refMatches(shadowing)) { + else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) && + !shadowing.tpe.isError && !refMatches(shadowing)) { implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}") shadowedImplicit(ref, methPart(shadowing).tpe) } @@ -524,8 +517,11 @@ trait Implicits { self: Typer => case fail: SearchFailure => rankImplicits(pending1, acc) case best: SearchSuccess => - val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) - rankImplicits(newPending, best :: acc) + if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil + else { + val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState)) + rankImplicits(newPending, best :: acc) + } } case nil => acc } diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala index 8889cf604..b903049d2 100644 --- a/src/dotty/tools/dotc/typer/Mode.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -68,5 +68,16 @@ object Mode { */ val Printing = newMode(10, "Printing") + /** We are currently typechecking an ident to determine whether some implicit + * is shadowed - don't do any other shadowing tests. + */ + val ImplicitShadowing = newMode(11, "ImplicitShadowing") + + /** We are currently in a `viewExists` check. In that case, ambiguous + * implicits checks are disabled and we succeed with the first implicit + * found. + */ + val ImplicitExploration = newMode(12, "ImplicitExploration") + val PatternOrType = Pattern | Type } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 10667f884..d5a6ec8f4 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -15,6 +15,7 @@ import annotation.tailrec import ErrorReporting._ import tpd.ListOfTreeDecorator import config.Printers._ +import Annotations._ import language.implicitConversions trait NamerContextOps { this: Context => @@ -495,8 +496,23 @@ class Namer { typer: Typer => completeInCreationContext(denot) } - def completeInCreationContext(denot: SymDenotation): Unit = + protected def addAnnotations(denot: SymDenotation): Unit = original match { + case original: untpd.MemberDef => + for (annotTree <- untpd.modsDeco(original).mods.annotations) { + val cls = typedAheadAnnotation(annotTree) + val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree)) + denot.addAnnotation(ann) + } + case _ => + } + + /** Intentionally left without `implicit ctx` parameter. We need + * to pick up the context at the point where the completer was created. + */ + def completeInCreationContext(denot: SymDenotation): Unit = { denot.info = typeSig(denot.symbol) + addAnnotations(denot) + } } class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) { @@ -563,7 +579,9 @@ class Namer { typer: Typer => index(rest)(inClassContext(selfInfo)) denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo) - if (impl.body forall isNoInitMember) cls.setFlag(NoInits) + addAnnotations(denot) + cls.setApplicableFlags( + (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat))) } } @@ -585,6 +603,13 @@ class Namer { typer: Typer => def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType) + def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match { + case Apply(fn, _) => typedAheadAnnotation(fn) + case TypeApply(fn, _) => typedAheadAnnotation(fn) + case Select(qual, nme.CONSTRUCTOR) => typedAheadAnnotation(qual) + case New(tpt) => typedAheadType(tpt).tpe.classSymbol + } + /** Enter and typecheck parameter list */ def completeParams(params: List[MemberDef])(implicit ctx: Context) = { index(params) @@ -676,8 +701,9 @@ class Namer { typer: Typer => // println(s"final inherited for $sym: ${inherited.toString}") !!! // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}") - val rhsCtx = ctx.fresh addMode Mode.InferringReturnType - def rhsType = typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion + val rhsCtx = ctx.addMode(Mode.InferringReturnType) + def rhsType = ctx.deskolemize( + typedAheadExpr(mdef.rhs, rhsProto)(rhsCtx).tpe.widen.approximateUnion) def lhsType = fullyDefinedType(rhsType, "right-hand side", mdef.pos) if (inherited.exists) inherited else { diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index c7efe45b7..9a012c30e 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -46,7 +46,7 @@ object ProtoTypes { * fits the given expected result type. */ def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match { - case _: FunProto => + case pt: FunProto => mt match { case mt: MethodType => mt.isDependent || constrainResult(mt.resultType, pt.resultType) diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index d3a5fd05f..f4e4f52b9 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -72,9 +72,7 @@ class ReTyper extends Typer { override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree = fallBack(tree, ctx.typerState) - override def addTypedModifiersAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = - () // was: typedModifiers(Modifiers(sym), sym) - // but annotations are not transformed after typer, so no use to check them. + override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = () override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = parents diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index acf4f3845..97bd0f514 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -312,13 +312,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (ctx.compilationUnit.isJava && tree.name.isTypeName) { // SI-3120 Java uses the same syntax, A.B, to express selection from the // value A and from the type A. We have to try both. - tryEither(tryCtx => asSelect(tryCtx))((_,_) => asJavaSelectFromTypeTree(ctx)) + tryEither(tryCtx => asSelect(tryCtx))((_, _) => asJavaSelectFromTypeTree(ctx)) } else asSelect(ctx) } def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): Tree = track("typedSelectFromTypeTree") { val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) - checkLegalPrefix(qual1.tpe, tree.name, qual1.pos) assignType(cpy.SelectFromTypeTree(tree)(qual1, tree.name), qual1) } @@ -347,8 +346,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val clsDef = TypeDef(x, templ).withFlags(Final) typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => - val tpt1 = typedType(tree.tpt) - checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + val tpt1 = typedType(tree.tpt) + checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) assignType(cpy.New(tree)(tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } @@ -378,10 +377,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => - if (id.name == nme.WILDCARD) regularTyped(isWildcard = true) + if (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) regularTyped(isWildcard = true) else { import untpd._ - typed(Bind(id.name, Typed(Ident(nme.WILDCARD), tree.tpt)).withPos(id.pos), pt) + val name = if (untpd.isWildcardStarArg(tree)) nme.WILDCARD_STAR else nme.WILDCARD + typed(Bind(id.name, Typed(Ident(name), tree.tpt)).withPos(id.pos), pt) } case _ => if (untpd.isWildcardStarArg(tree)) @@ -524,7 +524,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ var fnBody = tree.body - /** If function is of the form * (x1, ..., xN) => f(x1, ..., XN) * the type of `f`, otherwise NoType. (updates `fnBody` as a side effect). @@ -814,7 +813,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if ((rsym.is(Method) || rsym.isType) && rsym.allOverriddenSymbols.isEmpty) ctx.error(i"refinement $rsym without matching type in parent $parent", refinement.pos) val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info - RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, SkolemType(rt))) + RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt))) // todo later: check that refinement is within bounds } val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1) withType @@ -861,15 +860,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit assignType(cpy.Alternative(tree)(trees1), trees1) } - def addTypedModifiersAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = { - val mods1 = typedModifiers(untpd.modsDeco(mdef).mods, sym) - for (tree <- mods1.annotations) sym.addAnnotation(Annotation(tree)) - } - - def typedModifiers(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = track("typedModifiers") { - val annotations1 = mods.annotations mapconserve typedAnnotation - if (annotations1 eq mods.annotations) mods.asInstanceOf[Modifiers] - else Modifiers(mods.flags, mods.privateWithin, annotations1) + def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = { + // necessary to force annotation trees to be computed. + sym.annotations.foreach(_.tree) + // necessary in order to mark the typed ahead annotations as definitiely typed: + untpd.modsDeco(mdef).mods.annotations.mapconserve(typedAnnotation) } def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") { @@ -878,7 +873,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") { val ValDef(name, tpt, _) = vdef - addTypedModifiersAnnotations(vdef, sym) + completeAnnotations(vdef, sym) val tpt1 = typedType(tpt) val rhs1 = vdef.rhs match { case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe @@ -889,7 +884,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { val DefDef(name, tparams, vparamss, tpt, _) = ddef - addTypedModifiersAnnotations(ddef, sym) + completeAnnotations(ddef, sym) val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef]) val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef]) if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) @@ -901,7 +896,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") { val TypeDef(name, rhs) = tdef - addTypedModifiersAnnotations(tdef, sym) + completeAnnotations(tdef, sym) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree assignType(cpy.TypeDef(tdef)(name, TypeTree(sym.info), Nil), sym) } @@ -913,12 +908,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (tree.isType) typedType(tree)(superCtx) else { val result = typedExpr(tree)(superCtx) - if ((cls is Trait) && result.tpe.classSymbol.isRealClass && !ctx.isAfterTyper) - ctx.error(s"trait may not call constructor of ${result.tpe.classSymbol}", tree.pos) + checkParentCall(result, cls) result } - addTypedModifiersAnnotations(cdef, cls) + completeAnnotations(cdef, cls) val constr1 = typed(constr).asInstanceOf[DefDef] val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic) val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) diff --git a/src/dotty/tools/dotc/util/SimpleMap.scala b/src/dotty/tools/dotc/util/SimpleMap.scala index 7bd263f0f..b8668d7e4 100644 --- a/src/dotty/tools/dotc/util/SimpleMap.scala +++ b/src/dotty/tools/dotc/util/SimpleMap.scala @@ -8,7 +8,7 @@ abstract class SimpleMap[K <: AnyRef, +V >: Null <: AnyRef] extends (K => V) { def remove(k: K): SimpleMap[K, V] def updated[V1 >: V <: AnyRef](k: K, v: V1): SimpleMap[K, V1] def contains(k: K): Boolean = apply(k) != null - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleMap[K, V1] + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1): SimpleMap[K, V1] def foreachBinding(f: (K, V) => Unit): Unit def map2[T](f: (K, V) => T): List[T] = { val buf = new ListBuffer[T] @@ -32,7 +32,7 @@ object SimpleMap { def apply(k: AnyRef) = null def remove(k: AnyRef) = this def updated[V1 >: Null <: AnyRef](k: AnyRef, v: V1) = new Map1(k, v) - def mapValues[V1 >: Null <: AnyRef](f: (AnyRef, V1) => V1) = this + def mapValuesNow[V1 >: Null <: AnyRef](f: (AnyRef, V1) => V1) = this def foreachBinding(f: (AnyRef, Null) => Unit) = () } @@ -49,7 +49,7 @@ object SimpleMap { def updated[V1 >: V <: AnyRef](k: K, v: V1) = if (k == k1) new Map1(k, v) else new Map2(k1, v1, k, v) - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1) = { + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { val w1 = f(k1, v1) if (v1 eq w1) this else new Map1(k1, w1) } @@ -70,7 +70,7 @@ object SimpleMap { if (k == k1) new Map2(k, v, k2, v2) else if (k == k2) new Map2(k1, v1, k, v) else new Map3(k1, v1, k2, v2, k, v) - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1) = { + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { val w1 = f(k1, v1); val w2 = f(k2, v2) if ((v1 eq w1) && (v2 eq w2)) this else new Map2(k1, w1, k2, w2) @@ -95,7 +95,7 @@ object SimpleMap { else if (k == k2) new Map3(k1, v1, k, v, k3, v3) else if (k == k3) new Map3(k1, v1, k2, v2, k, v) else new Map4(k1, v1, k2, v2, k3, v3, k, v) - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1) = { + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3) if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3)) this else new Map3(k1, w1, k2, w2, k3, w3) @@ -123,7 +123,7 @@ object SimpleMap { else if (k == k3) new Map4(k1, v1, k2, v2, k, v, k4, v4) else if (k == k4) new Map4(k1, v1, k2, v2, k3, v3, k, v) else new MapMore(Array[AnyRef](k1, v1, k2, v2, k3, v3, k4, v4, k, v)) - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1) = { + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { val w1 = f(k1, v1); val w2 = f(k2, v2); val w3 = f(k3, v3); val w4 = f(k4, v4) if ((v1 eq w1) && (v2 eq w2) && (v3 eq w3) && (v4 eq w4)) this else new Map4(k1, w1, k2, w2, k3, w3, k4, w4) @@ -197,7 +197,7 @@ object SimpleMap { false } - def mapValues[V1 >: V <: AnyRef](f: (K, V1) => V1) = { + def mapValuesNow[V1 >: V <: AnyRef](f: (K, V1) => V1) = { var bindings1: Array[AnyRef] = bindings var i = 0 while (i < bindings.length) { |