aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2016-10-11 22:28:42 +0200
committerGitHub <noreply@github.com>2016-10-11 22:28:42 +0200
commitf738201973f6965b861fe4b0b580c2dfed61f158 (patch)
treee8918383ef651fb3e5df0da0e09b2093964954f6 /src/dotty
parentd96bba0bfd5ab4d80c269bd04ff9ac4d863713db (diff)
parentba18173c4ac655eb07eca036a81a7a8b9e76caa7 (diff)
downloaddotty-f738201973f6965b861fe4b0b580c2dfed61f158.tar.gz
dotty-f738201973f6965b861fe4b0b580c2dfed61f158.tar.bz2
dotty-f738201973f6965b861fe4b0b580c2dfed61f158.zip
Merge pull request #1550 from dotty-staging/union-types
True union types
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/language.scala9
-rw-r--r--src/dotty/tools/dotc/config/Config.scala8
-rw-r--r--src/dotty/tools/dotc/core/ConstraintHandling.scala15
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala4
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala3
-rw-r--r--src/dotty/tools/dotc/core/OrderingConstraint.scala5
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala1
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala21
-rw-r--r--src/dotty/tools/dotc/core/TypeOps.scala134
-rw-r--r--src/dotty/tools/dotc/core/Types.scala46
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitSelf.scala9
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala5
-rw-r--r--src/dotty/tools/dotc/transform/Splitter.scala51
-rw-r--r--src/dotty/tools/dotc/transform/TailRec.scala5
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala27
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala8
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ProtoTypes.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala5
19 files changed, 222 insertions, 143 deletions
diff --git a/src/dotty/language.scala b/src/dotty/language.scala
deleted file mode 100644
index 416a4281b..000000000
--- a/src/dotty/language.scala
+++ /dev/null
@@ -1,9 +0,0 @@
-package dotty
-
-object language {
-
- class Feature
-
- /** Keep union types */
- val keepUnions = new Feature
-}
diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala
index c188bfab4..7744a5479 100644
--- a/src/dotty/tools/dotc/config/Config.scala
+++ b/src/dotty/tools/dotc/config/Config.scala
@@ -87,8 +87,12 @@ object Config {
*/
final val checkLambdaVariance = false
- /** Check that certain types cannot be created in erasedTypes phases */
- final val checkUnerased = true
+ /** Check that certain types cannot be created in erasedTypes phases.
+ * Note: Turning this option on will get some false negatives, since it is
+ * possible that And/Or types are still created during erasure as the result
+ * of some operation on an existing type.
+ */
+ final val checkUnerased = false
/** In `derivedSelect`, rewrite
*
diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala
index 5911af72c..84f531385 100644
--- a/src/dotty/tools/dotc/core/ConstraintHandling.scala
+++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala
@@ -35,6 +35,15 @@ trait ConstraintHandling {
/** If the constraint is frozen we cannot add new bounds to the constraint. */
protected var frozenConstraint = false
+ protected var alwaysFluid = false
+
+ /** Perform `op` in a mode where all attempts to set `frozen` to true are ignored */
+ def fluidly[T](op: => T): T = {
+ val saved = alwaysFluid
+ alwaysFluid = true
+ try op finally alwaysFluid = saved
+ }
+
/** We are currently comparing lambdas. Used as a flag for
* optimization: when `false`, no need to do an expensive `pruneLambdaParams`
*/
@@ -126,14 +135,14 @@ trait ConstraintHandling {
final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
val saved = frozenConstraint
- frozenConstraint = true
+ frozenConstraint = !alwaysFluid
try isSubType(tp1, tp2)
finally frozenConstraint = saved
}
final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = {
val saved = frozenConstraint
- frozenConstraint = true
+ frozenConstraint = !alwaysFluid
try isSameType(tp1, tp2)
finally frozenConstraint = saved
}
@@ -219,7 +228,7 @@ trait ConstraintHandling {
// is not a union type, approximate the union type from above by an intersection
// of all common base types.
if (fromBelow && isOrType(inst) && isFullyDefined(inst) && !isOrType(upperBound))
- inst = inst.approximateUnion
+ inst = ctx.harmonizeUnion(inst)
// 3. If instance is from below, and upper bound has open named parameters
// make sure the instance has all named parameters of the bound.
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 75b75d3d5..b1c2bc535 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -430,10 +430,8 @@ class Definitions {
def Product_productArity(implicit ctx: Context) = Product_productArityR.symbol
lazy val Product_productPrefixR = ProductClass.requiredMethodRef(nme.productPrefix)
def Product_productPrefix(implicit ctx: Context) = Product_productPrefixR.symbol
- lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
+ lazy val LanguageModuleRef = ctx.requiredModule("scala.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
- lazy val Scala2LanguageModuleRef = ctx.requiredModule("scala.language")
- def Scala2LanguageModuleClass(implicit ctx: Context) = Scala2LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 4f01c43cf..0f95fc591 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -336,7 +336,8 @@ object Denotations {
(!sym2.isAsConcrete(sym1) ||
precedes(sym1.owner, sym2.owner) ||
accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
- sym1.is(Method) && !sym2.is(Method))
+ sym1.is(Method) && !sym2.is(Method)) ||
+ sym1.info.isErroneous
/** Sym preference provided types also override */
def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
diff --git a/src/dotty/tools/dotc/core/OrderingConstraint.scala b/src/dotty/tools/dotc/core/OrderingConstraint.scala
index 458f8b82f..68c7655ef 100644
--- a/src/dotty/tools/dotc/core/OrderingConstraint.scala
+++ b/src/dotty/tools/dotc/core/OrderingConstraint.scala
@@ -521,6 +521,11 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
case _ if e1 contains e2 => e2
case _ => mergeError
}
+ case tv1: TypeVar =>
+ e2 match {
+ case tv2: TypeVar if tv1.instanceOpt eq tv2.instanceOpt => e1
+ case _ => mergeError
+ }
case _ if e1 eq e2 => e1
case _ => mergeError
}
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index c52264637..920c9635e 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -429,7 +429,6 @@ object StdNames {
val isEmpty: N = "isEmpty"
val isInstanceOf_ : N = "isInstanceOf"
val java: N = "java"
- val keepUnions: N = "keepUnions"
val key: N = "key"
val lang: N = "lang"
val length: N = "length"
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 991dd2664..52b248abb 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -8,7 +8,7 @@ import StdNames.{nme, tpnme}
import collection.mutable
import util.{Stats, DotClass, SimpleMap}
import config.Config
-import config.Printers.{typr, constr, subtyping}
+import config.Printers.{typr, constr, subtyping, noPrinter}
import TypeErasure.{erasedLub, erasedGlb}
import TypeApplications._
import scala.util.control.NonFatal
@@ -324,8 +324,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case AndType(tp11, tp12) =>
if (tp11.stripTypeVar eq tp12.stripTypeVar) isSubType(tp11, tp2)
else thirdTry(tp1, tp2)
- case OrType(tp11, tp12) =>
- isSubType(tp11, tp2) && isSubType(tp12, tp2)
+ case tp1 @ OrType(tp11, tp12) =>
+ def joinOK = tp2.dealias match {
+ case tp12: HKApply =>
+ // If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a
+ // type parameter, we will instantiate `C` to `A` and then fail when comparing
+ // with `B[Y]`. To do the right thing, we need to instantiate `C` to the
+ // common superclass of `A` and `B`.
+ isSubType(tp1.join, tp2)
+ case _ =>
+ false
+ }
+ joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2)
case ErrorType =>
true
case _ =>
@@ -827,8 +837,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
op1 && {
val leftConstraint = constraint
constraint = preConstraint
- if (!(op2 && subsumes(leftConstraint, constraint, preConstraint)))
+ if (!(op2 && subsumes(leftConstraint, constraint, preConstraint))) {
+ if (constr != noPrinter && !subsumes(constraint, leftConstraint, preConstraint))
+ constr.println(i"CUT - prefer $leftConstraint over $constraint")
constraint = leftConstraint
+ }
true
} || op2
}
diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala
index bee69ae69..d480a792b 100644
--- a/src/dotty/tools/dotc/core/TypeOps.scala
+++ b/src/dotty/tools/dotc/core/TypeOps.scala
@@ -173,21 +173,53 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
}
/** Approximate union type by intersection of its dominators.
- * See Type#approximateUnion for an explanation.
+ * That is, replace a union type Tn | ... | Tn
+ * by the smallest intersection type of base-class instances of T1,...,Tn.
+ * Example: Given
+ *
+ * trait C[+T]
+ * trait D
+ * class A extends C[A] with D
+ * class B extends C[B] with D with E
+ *
+ * we approximate `A | B` by `C[A | B] with D`
*/
- def approximateUnion(tp: Type): Type = {
+ def orDominator(tp: Type): Type = {
+
/** a faster version of cs1 intersect cs2 */
def intersect(cs1: List[ClassSymbol], cs2: List[ClassSymbol]): List[ClassSymbol] = {
val cs2AsSet = new util.HashSet[ClassSymbol](100)
cs2.foreach(cs2AsSet.addEntry)
cs1.filter(cs2AsSet.contains)
}
+
/** The minimal set of classes in `cs` which derive all other classes in `cs` */
def dominators(cs: List[ClassSymbol], accu: List[ClassSymbol]): List[ClassSymbol] = (cs: @unchecked) match {
case c :: rest =>
val accu1 = if (accu exists (_ derivesFrom c)) accu else c :: accu
if (cs == c.baseClasses) accu1 else dominators(rest, accu1)
}
+
+ def mergeRefined(tp1: Type, tp2: Type): Type = {
+ def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2")
+ tp1 match {
+ case tp1 @ RefinedType(parent1, name1, rinfo1) =>
+ tp2 match {
+ case RefinedType(parent2, `name1`, rinfo2) =>
+ tp1.derivedRefinedType(
+ mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2)
+ case _ => fail
+ }
+ case tp1 @ TypeRef(pre1, name1) =>
+ tp2 match {
+ case tp2 @ TypeRef(pre2, `name1`) =>
+ tp1.derivedSelect(pre1 | pre2)
+ case _ => fail
+ }
+ case _ => fail
+ }
+ }
+
def approximateOr(tp1: Type, tp2: Type): Type = {
def isClassRef(tp: Type): Boolean = tp match {
case tp: TypeRef => tp.symbol.isClass
@@ -195,78 +227,70 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
case _ => false
}
- /** If `tp1` and `tp2` are typebounds, try to make one fit into the other
- * or to make them equal, by instantiating uninstantiated type variables.
- */
- def homogenizedUnion(tp1: Type, tp2: Type): Type = {
- tp1 match {
- case tp1: TypeBounds =>
- tp2 match {
- case tp2: TypeBounds =>
- def fitInto(tp1: TypeBounds, tp2: TypeBounds): Unit = {
- val nestedCtx = ctx.fresh.setNewTyperState
- if (tp2.boundsInterval.contains(tp1.boundsInterval)(nestedCtx))
- nestedCtx.typerState.commit()
- }
- fitInto(tp1, tp2)
- fitInto(tp2, tp1)
- case _ =>
- }
- case _ =>
- }
- tp1 | tp2
- }
-
- tp1 match {
- case tp1: RefinedType =>
- tp2 match {
- case tp2: RefinedType if tp1.refinedName == tp2.refinedName =>
- return tp1.derivedRefinedType(
- approximateUnion(OrType(tp1.parent, tp2.parent)),
- tp1.refinedName,
- homogenizedUnion(tp1.refinedInfo, tp2.refinedInfo))
- //.ensuring { x => println(i"approx or $tp1 | $tp2 = $x\n constr = ${ctx.typerState.constraint}"); true } // DEBUG
- case _ =>
- }
- case _ =>
- }
-
tp1 match {
case tp1: RecType =>
tp1.rebind(approximateOr(tp1.parent, tp2))
case tp1: TypeProxy if !isClassRef(tp1) =>
- approximateUnion(tp1.superType | tp2)
+ orDominator(tp1.superType | tp2)
case _ =>
tp2 match {
case tp2: RecType =>
tp2.rebind(approximateOr(tp1, tp2.parent))
case tp2: TypeProxy if !isClassRef(tp2) =>
- approximateUnion(tp1 | tp2.superType)
+ orDominator(tp1 | tp2.superType)
case _ =>
val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect)
val doms = dominators(commonBaseClasses, Nil)
- def baseTp(cls: ClassSymbol): Type =
- if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
- else tp.baseTypeWithArgs(cls)
+ def baseTp(cls: ClassSymbol): Type = {
+ val base =
+ if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls)
+ else tp.baseTypeWithArgs(cls)
+ base.mapReduceOr(identity)(mergeRefined)
+ }
doms.map(baseTp).reduceLeft(AndType.apply)
}
}
}
- if (ctx.featureEnabled(defn.LanguageModuleClass, nme.keepUnions)) tp
- else tp match {
+
+ tp match {
case tp: OrType =>
- approximateOr(tp.tp1, tp.tp2) // Maybe refactor using liftToRec?
- case tp @ AndType(tp1, tp2) =>
- tp derived_& (approximateUnion(tp1), approximateUnion(tp2))
- case tp: RefinedType =>
- tp.derivedRefinedType(approximateUnion(tp.parent), tp.refinedName, tp.refinedInfo)
- case tp: RecType =>
- tp.rebind(approximateUnion(tp.parent))
+ approximateOr(tp.tp1, tp.tp2)
case _ =>
tp
}
}
+ /** Given a disjunction T1 | ... | Tn of types with potentially embedded
+ * type variables, constrain type variables further if this eliminates
+ * some of the branches of the disjunction. Do this also for disjunctions
+ * embedded in intersections, as parents in refinements, and in recursive types.
+ *
+ * For instance, if `A` is an unconstrained type variable, then
+ *
+ * ArrayBuffer[Int] | ArrayBuffer[A]
+ *
+ * is approximated by constraining `A` to be =:= to `Int` and returning `ArrayBuffer[Int]`
+ * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
+ */
+ def harmonizeUnion(tp: Type): Type = tp match {
+ case tp: OrType =>
+ joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2))
+ case tp @ AndType(tp1, tp2) =>
+ tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2))
+ case tp: RefinedType =>
+ tp.derivedRefinedType(harmonizeUnion(tp.parent), tp.refinedName, tp.refinedInfo)
+ case tp: RecType =>
+ tp.rebind(harmonizeUnion(tp.parent))
+ case _ =>
+ tp
+ }
+
+ /** Under -language:Scala2: Replace or-types with their joins */
+ private def joinIfScala2(tp: Type) = tp match {
+ case tp: OrType if scala2Mode => tp.join
+ case _ => tp
+ }
+
/** Not currently needed:
*
def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = {
@@ -493,7 +517,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
*/
def featureEnabled(owner: ClassSymbol, feature: TermName): Boolean = {
def toPrefix(sym: Symbol): String =
- if (!sym.exists || (sym eq defn.LanguageModuleClass) || (sym eq defn.Scala2LanguageModuleClass)) ""
+ if (!sym.exists || (sym eq defn.LanguageModuleClass)) ""
else toPrefix(sym.owner) + sym.name + "."
def featureName = toPrefix(owner) + feature
def hasImport(implicit ctx: Context): Boolean = {
@@ -512,13 +536,13 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
/** Is auto-tupling enabled? */
def canAutoTuple =
- !featureEnabled(defn.Scala2LanguageModuleClass, nme.noAutoTupling)
+ !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling)
def scala2Mode =
featureEnabled(defn.LanguageModuleClass, nme.Scala2)
def dynamicsEnabled =
- featureEnabled(defn.Scala2LanguageModuleClass, nme.dynamics)
+ featureEnabled(defn.LanguageModuleClass, nme.dynamics)
def testScala2Mode(msg: String, pos: Position) = {
if (scala2Mode) migrationWarning(msg, pos)
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 2f1b6b829..0f81f8c38 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -436,8 +436,12 @@ object Types {
tp.cls.findMember(name, pre, excluded)
case AndType(l, r) =>
goAnd(l, r)
- case OrType(l, r) =>
- goOr(l, r)
+ case tp: OrType =>
+ // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
+ // achieved that by narrowing `pre` to each alternative, but it led to merge errors in
+ // lots of places. The present strategy is instead of widen `tp` using `join` to be a
+ // supertype of `pre`.
+ go(tp.join)
case tp: JavaArrayType =>
defn.ObjectType.findMember(name, pre, excluded)
case ErrorType =>
@@ -556,7 +560,6 @@ object Types {
def goAnd(l: Type, r: Type) = {
go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
}
- def goOr(l: Type, r: Type) = go(l) | (go(r), pre)
{ val recCount = ctx.findMemberCount + 1
ctx.findMemberCount = recCount
@@ -1230,28 +1233,6 @@ object Types {
*/
def simplified(implicit ctx: Context) = ctx.simplify(this, null)
- /** Approximations of union types: We replace a union type Tn | ... | Tn
- * by the smallest intersection type of baseclass instances of T1,...,Tn.
- * Example: Given
- *
- * trait C[+T]
- * trait D
- * class A extends C[A] with D
- * class B extends C[B] with D with E
- *
- * we approximate `A | B` by `C[A | B] with D`
- *
- * As a second measure we also homogenize refinements containing
- * type variables. For instance, if `A` is an instantiatable type variable,
- * then
- *
- * ArrayBuffer[Int] | ArrayBuffer[A]
- *
- * is approximated by instantiating `A` to `Int` and returning `ArrayBuffer[Int]`
- * instead of `ArrayBuffer[_ >: Int | A <: Int & A]`
- */
- def approximateUnion(implicit ctx: Context) = ctx.approximateUnion(this)
-
/** customized hash code of this type.
* NotCached for uncached types. Cached types
* compute hash and use it as the type's hashCode.
@@ -2233,9 +2214,24 @@ object Types {
}
abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
+
assert(tp1.isInstanceOf[ValueType] && tp2.isInstanceOf[ValueType])
def isAnd = false
+ private[this] var myJoin: Type = _
+ private[this] var myJoinPeriod: Period = Nowhere
+
+ /** Replace or type by the closest non-or type above it */
+ def join(implicit ctx: Context): Type = {
+ if (myJoinPeriod != ctx.period) {
+ myJoin = ctx.orDominator(this)
+ core.println(i"join of $this == $myJoin")
+ assert(myJoin != this)
+ myJoinPeriod = ctx.period
+ }
+ myJoin
+ }
+
def derivedOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
else OrType.make(tp1, tp2)
diff --git a/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
index 618a0f108..7bb65e575 100644
--- a/src/dotty/tools/dotc/transform/ExplicitSelf.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitSelf.scala
@@ -20,12 +20,21 @@ import Flags._
*
* where `S` is the self type of `C`.
* See run/i789.scala for a test case why this is needed.
+ *
+ * Also replaces idents referring to the self type with ThisTypes.
*/
class ExplicitSelf extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
override def phaseName = "explicitSelf"
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match {
+ case tp: ThisType =>
+ ctx.debuglog(s"owner = ${ctx.owner}, context = ${ctx}")
+ This(tp.cls) withPos tree.pos
+ case _ => tree
+ }
+
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
case Select(thiz: This, name) if name.isTermName =>
val cls = thiz.symbol.asClass
diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala
index 18b030913..19fb3dd0c 100644
--- a/src/dotty/tools/dotc/transform/LambdaLift.scala
+++ b/src/dotty/tools/dotc/transform/LambdaLift.scala
@@ -121,7 +121,10 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet =
f.getOrElseUpdate(sym, newSymSet)
- def freeVars(sym: Symbol): List[Symbol] = free.getOrElse(sym, Nil).toList
+ def freeVars(sym: Symbol): List[Symbol] = free get sym match {
+ case Some(set) => set.toList
+ case None => Nil
+ }
def proxyOf(sym: Symbol, fv: Symbol) = proxyMap.getOrElse(sym, Map.empty)(fv)
diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala
index efcf95ede..d62be1a82 100644
--- a/src/dotty/tools/dotc/transform/Splitter.scala
+++ b/src/dotty/tools/dotc/transform/Splitter.scala
@@ -6,25 +6,34 @@ import ast.Trees._
import core._
import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotations._, Names._
-/** This transform makes sure every identifier and select node
- * carries a symbol. To do this, certain qualifiers with a union type
- * have to be "splitted" with a type test.
- *
- * For now, only self references are treated.
+/** Distribute applications into Block and If nodes
*/
class Splitter extends MiniPhaseTransform { thisTransform =>
import ast.tpd._
override def phaseName: String = "splitter"
- /** Replace self referencing idents with ThisTypes. */
- override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = tree.tpe match {
- case tp: ThisType =>
- ctx.debuglog(s"owner = ${ctx.owner}, context = ${ctx}")
- This(tp.cls) withPos tree.pos
- case _ => tree
+ /** Distribute arguments among splitted branches */
+ def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = {
+ def recur(fn: Tree): Tree = fn match {
+ case Block(stats, expr) => Block(stats, recur(expr))
+ case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep))
+ case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos
+ }
+ recur(tree.fun)
}
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
+ distribute(tree, typeApply)
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
+ distribute(tree, apply)
+
+ private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx)
+ private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx)
+
+/* The following is no longer necessary, since we select members on the join of an or type:
+ *
/** If we select a name, make sure the node has a symbol.
* If necessary, split the qualifier with type tests.
* Example: Assume:
@@ -108,23 +117,5 @@ class Splitter extends MiniPhaseTransform { thisTransform =>
evalOnce(qual)(qual => choose(qual, candidates(qual.tpe)))
}
}
-
- /** Distribute arguments among splitted branches */
- def distribute(tree: GenericApply[Type], rebuild: (Tree, List[Tree]) => Context => Tree)(implicit ctx: Context) = {
- def recur(fn: Tree): Tree = fn match {
- case Block(stats, expr) => Block(stats, recur(expr))
- case If(cond, thenp, elsep) => If(cond, recur(thenp), recur(elsep))
- case _ => rebuild(fn, tree.args)(ctx) withPos tree.pos
- }
- recur(tree.fun)
- }
-
- override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
- distribute(tree, typeApply)
-
- override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
- distribute(tree, apply)
-
- private val typeApply = (fn: Tree, args: List[Tree]) => (ctx: Context) => TypeApply(fn, args)(ctx)
- private val apply = (fn: Tree, args: List[Tree]) => (ctx: Context) => Apply(fn, args)(ctx)
+*/
}
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index 065bcb397..d99a48af3 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -252,7 +252,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
} else targs
val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
- val thisPassed = if(this.method.owner.isClass) method appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) else method
+ val thisPassed =
+ if (this.method.owner.isClass)
+ method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
+ else method
val res =
if (thisPassed.tpe.widen.isParameterless) thisPassed
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index fd8e41dc3..808178369 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -133,10 +133,7 @@ class TreeChecker extends Phase with SymTransformer {
catch {
case NonFatal(ex) => //TODO CHECK. Check that we are bootstrapped
implicit val ctx: Context = checkingCtx
- ctx.echo(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***")
- ctx.echo(ex.toString)
- ctx.echo(ex.getStackTrace.take(30).deep.mkString("\n"))
- ctx.echo("<<<")
+ println(i"*** error while checking ${ctx.compilationUnit} after phase ${checkingCtx.phase.prev} ***")
throw ex
}
}
@@ -331,8 +328,30 @@ class TreeChecker extends Phase with SymTransformer {
checkNotRepeated(super.typedIdent(tree, pt))
}
+ /** Makes sure the symbol in the tree can be approximately reconstructed by
+ * calling `member` on the qualifier type.
+ * Approximately means: The two symbols might be different but one still overrides the other.
+ */
override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
+ val tpe = tree.typeOpt
+ val sym = tree.symbol
+ if (!tpe.isInstanceOf[WithFixedSym] && sym.exists && !sym.is(Private)) {
+ val qualTpe = tree.qualifier.typeOpt
+ val member =
+ if (sym.is(Private)) qualTpe.member(tree.name)
+ else qualTpe.nonPrivateMember(tree.name)
+ val memberSyms = member.alternatives.map(_.symbol)
+ assert(memberSyms.exists(mbr =>
+ sym == mbr ||
+ sym.overriddenSymbol(mbr.owner.asClass) == mbr ||
+ mbr.overriddenSymbol(sym.owner.asClass) == sym),
+ ex"""symbols differ for $tree
+ |was : $sym
+ |alternatives by type: $memberSyms%, % of types ${memberSyms.map(_.info)}%, %
+ |qualifier type : ${tree.qualifier.typeOpt}
+ |tree type : ${tree.typeOpt} of class ${tree.typeOpt.getClass}""")
+ }
checkNotRepeated(super.typedSelect(tree, pt))
}
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 7ba66e3d8..3461facc1 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -542,6 +542,13 @@ trait Checking {
errorTree(tpt, ex"missing type parameter for ${tpt.tpe}")
}
else tpt
+
+ /** Check that `tpt` does not refer to a singleton type */
+ def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree =
+ if (tpt.tpe.isInstanceOf[SingletonType]) {
+ errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where")
+ }
+ else tpt
}
trait NoChecking extends Checking {
@@ -556,4 +563,5 @@ trait NoChecking extends Checking {
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
+ override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt
}
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 4f4278468..00e92cbfb 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -898,7 +898,7 @@ class Namer { typer: Typer =>
// definition is inline (i.e. final in Scala2).
def widenRhs(tp: Type): Type = tp.widenTermRefExpr match {
case tp: ConstantType if isInline => tp
- case _ => tp.widen.approximateUnion
+ case _ => ctx.harmonizeUnion(tp.widen)
}
// Replace aliases to Unit by Unit itself. If we leave the alias in
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 0e6697fb7..dd5705fbf 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -43,6 +43,11 @@ object ProtoTypes {
isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
}
+ private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match {
+ case _: OrType => true
+ case pt => pt.isRef(defn.UnitClass)
+ }
+
/** Check that the result type of the current method
* fits the given expected result type.
*/
@@ -54,7 +59,7 @@ object ProtoTypes {
case _ =>
true
}
- case _: ValueTypeOrProto if !(pt isRef defn.UnitClass) =>
+ case _: ValueTypeOrProto if !disregardProto(pt) =>
mt match {
case mt: MethodType =>
mt.isDependent || isCompatible(normalize(mt, pt), pt)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index bbb20bcf5..e423082d5 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -999,8 +999,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") {
- val left1 = typed(tree.left)
- val right1 = typed(tree.right)
+ val where = "in a union type"
+ val left1 = checkNotSingleton(typed(tree.left), where)
+ val right1 = checkNotSingleton(typed(tree.right), where)
assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1)
}