aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dotty/DottyPredef.scala30
-rw-r--r--src/dotty/tools/dotc/Compiler.scala3
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala29
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala11
-rw-r--r--src/dotty/tools/dotc/config/Config.scala8
-rw-r--r--src/dotty/tools/dotc/core/Constraint.scala3
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala22
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala10
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala28
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala2
-rw-r--r--src/dotty/tools/dotc/core/OrderingConstraint.scala30
-rw-r--r--src/dotty/tools/dotc/core/Phases.scala3
-rw-r--r--src/dotty/tools/dotc/core/Skolemization.scala126
-rw-r--r--src/dotty/tools/dotc/core/Substituters.scala18
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala37
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala2
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala8
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala43
-rw-r--r--src/dotty/tools/dotc/core/TypeErasure.scala13
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala172
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala8
-rw-r--r--src/dotty/tools/dotc/core/Types.scala97
-rw-r--r--src/dotty/tools/dotc/core/tasty/TastyFormat.scala41
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreePickler.scala8
-rw-r--r--src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala40
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala2
-rw-r--r--src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala2
-rw-r--r--src/dotty/tools/dotc/printing/PlainPrinter.scala14
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala7
-rw-r--r--src/dotty/tools/dotc/transform/AugmentScala2Traits.scala12
-rw-r--r--src/dotty/tools/dotc/transform/ClassOf.scala53
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala24
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala10
-rw-r--r--src/dotty/tools/dotc/transform/ExpandSAMs.scala17
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala6
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala2
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala28
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala7
-rw-r--r--src/dotty/tools/dotc/transform/Mixin.scala99
-rw-r--r--src/dotty/tools/dotc/transform/NormalizeFlags.scala5
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala2
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala68
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala28
-rw-r--r--src/dotty/tools/dotc/typer/EtaExpansion.scala4
-rw-r--r--src/dotty/tools/dotc/typer/FrontEnd.scala1
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala36
-rw-r--r--src/dotty/tools/dotc/typer/Mode.scala11
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala34
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ReTyper.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala40
-rw-r--r--src/dotty/tools/dotc/util/SimpleMap.scala14
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) {