aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-08-01 18:21:23 +0200
committerMartin Odersky <odersky@gmail.com>2013-08-01 18:21:23 +0200
commitdbb4b3f7923427af4ba6e04f258309421d5ee1ab (patch)
treec8d47cbae32a0778d0bff3a22117d9d4a7c5ff7f
parent413f364887d5bde7610adbbc08020e23470b4c8c (diff)
downloaddotty-dbb4b3f7923427af4ba6e04f258309421d5ee1ab.tar.gz
dotty-dbb4b3f7923427af4ba6e04f258309421d5ee1ab.tar.bz2
dotty-dbb4b3f7923427af4ba6e04f258309421d5ee1ab.zip
Handling typevars in inference.
Fleshed out handling of typevars for type inference. Also added some more methods to typer, for blocks, ifs and assignments. (Closures are still wip).
-rw-r--r--src/dotty/tools/dotc/ast/CheckTrees.scala43
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala5
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala6
-rw-r--r--src/dotty/tools/dotc/ast/TypedTrees.scala24
-rw-r--r--src/dotty/tools/dotc/ast/UntypedTrees.scala2
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala6
-rw-r--r--src/dotty/tools/dotc/core/TyperState.scala42
-rw-r--r--src/dotty/tools/dotc/core/Types.scala138
-rw-r--r--src/dotty/tools/dotc/core/pickling/UnPickler.scala2
-rw-r--r--src/dotty/tools/dotc/printing/PlainPrinter.scala2
-rw-r--r--src/dotty/tools/dotc/typer/ErrorReporting.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala77
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala177
-rw-r--r--src/dotty/tools/dotc/util/SimpleMap.scala8
15 files changed, 388 insertions, 148 deletions
diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala
index a004789e6..b1806df4b 100644
--- a/src/dotty/tools/dotc/ast/CheckTrees.scala
+++ b/src/dotty/tools/dotc/ast/CheckTrees.scala
@@ -17,6 +17,27 @@ object CheckTrees {
check(bounds contains arg.tpe)
}
+ def escapingRefs(block: Block)(implicit ctx: Context): Set[NamedType] = {
+ var hoisted: Set[Symbol] = Set()
+ lazy val locals = localSyms(block.stats).toSet
+ def isNonLocal(sym: Symbol): Boolean =
+ !(locals contains sym) || isHoistableClass(sym)
+ def isHoistableClass(sym: Symbol) =
+ sym.isClass && {
+ (hoisted contains sym) || {
+ hoisted += sym
+ !classLeaks(sym.asClass)
+ }
+ }
+ def leakingTypes(tp: Type): Set[NamedType] =
+ tp namedPartsWith (tp => isNonLocal(tp.symbol))
+ def typeLeaks(tp: Type) = leakingTypes(tp).isEmpty
+ def classLeaks(sym: ClassSymbol): Boolean =
+ (sym.info.parents exists typeLeaks) ||
+ (sym.decls.toList exists (t => typeLeaks(t.info)))
+ leakingTypes(block.tpe)
+ }
+
def checkType(tree: Tree)(implicit ctx: Context): Unit = tree match {
case Ident(name) =>
case Select(qualifier, name) =>
@@ -79,27 +100,9 @@ object CheckTrees {
check(false)
}
check(rhs.tpe <:< lhs.tpe.widen)
- case Block(stats, expr) =>
- var hoisted: Set[Symbol] = Set()
- lazy val locals = localSyms(stats).toSet
+ case tree @ Block(stats, expr) =>
check(expr.isValue)
- def isNonLocal(sym: Symbol): Boolean =
- !(locals contains sym) || isHoistableClass(sym)
- def isHoistableClass(sym: Symbol) =
- sym.isClass && {
- (hoisted contains sym) || {
- hoisted += sym
- noLeaksInClass(sym.asClass)
- }
- }
- def noLeaksIn(tp: Type): Boolean = tp forallParts {
- case tp: NamedType => isNonLocal(tp.symbol)
- case _ => true
- }
- def noLeaksInClass(sym: ClassSymbol): Boolean =
- (sym.info.parents forall noLeaksIn) &&
- (sym.decls.toList forall (t => noLeaksIn(t.info)))
- check(noLeaksIn(tree.tpe))
+ check(escapingRefs(tree).isEmpty)
case If(cond, thenp, elsep) =>
check(cond.isValue); check(thenp.isValue); check(elsep.isValue)
check(cond.tpe.derivesFrom(defn.BooleanClass))
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala
index de00cee7e..9fcbeeea4 100644
--- a/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -99,6 +99,11 @@ trait TreeInfo[T >: Untyped] { self: Trees.Instance[T] =>
case _ => false
}
+ def isSuperSelection(tree: untpd.Tree) = tree match {
+ case Select(Super(_, _), _) => true
+ case _ => false
+ }
+
def isSelfOrSuperConstrCall(tree: Tree): Boolean = methPart(tree) match {
case Ident(nme.CONSTRUCTOR)
| Select(This(_), nme.CONSTRUCTOR)
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index 74f792220..cfc603501 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -436,7 +436,7 @@ object Trees {
}
/** A closure with an environment and a reference to a method */
- case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: RefTree[T])
+ case class Closure[-T >: Untyped] private[ast] (env: List[Tree[T]], meth: Tree[T])
extends TermTree[T] {
type ThisTree[-T >: Untyped] = Closure[T]
}
@@ -847,7 +847,7 @@ object Trees {
case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree
case _ => finalize(tree, untpd.If(cond, thenp, elsep))
}
- def Closure(tree: Tree, env: List[Tree], meth: RefTree): Closure = tree match {
+ def Closure(tree: Tree, env: List[Tree], meth: Tree): Closure = tree match {
case tree: Closure if (env eq tree.env) && (meth eq tree.meth) => tree
case _ => finalize(tree, untpd.Closure(env, meth))
}
@@ -986,7 +986,7 @@ object Trees {
case If(cond, thenp, elsep) =>
cpy.If(tree, transform(cond), transform(thenp), transform(elsep))
case Closure(env, meth) =>
- cpy.Closure(tree, transform(env), transformSub(meth))
+ cpy.Closure(tree, transform(env), transform(meth))
case Match(selector, cases) =>
cpy.Match(tree, transform(selector), transformSub(cases))
case CaseDef(pat, guard, body) =>
diff --git a/src/dotty/tools/dotc/ast/TypedTrees.scala b/src/dotty/tools/dotc/ast/TypedTrees.scala
index cea799116..037180161 100644
--- a/src/dotty/tools/dotc/ast/TypedTrees.scala
+++ b/src/dotty/tools/dotc/ast/TypedTrees.scala
@@ -85,15 +85,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
untpd.Assign(lhs, rhs).withType(defn.UnitType).checked
- def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = {
+ def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block =
+ untpd.Block(stats, expr).withType(blockType(stats, expr.tpe)).checked
+
+ def blockType(stats: List[Tree], exprType: Type)(implicit ctx: Context): Type = {
lazy val locals = localSyms(stats).toSet
- val blk = untpd.Block(stats, expr)
def widen(tp: Type): Type = tp match {
case tp: TermRef if locals contains tp.symbol =>
widen(tp.info)
case _ => tp
}
- blk.withType(widen(expr.tpe))
+ widen(exprType)
}
def maybeBlock(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree =
@@ -102,14 +104,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
untpd.If(cond, thenp, elsep).withType(thenp.tpe | elsep.tpe).checked
- def Closure(env: List[Tree], meth: RefTree)(implicit ctx: Context): Closure = {
- val ownType = meth.tpe.widen match {
- case mt @ MethodType(_, formals) =>
- assert(!mt.isDependent)
- val formals1 = formals mapConserve (_.underlyingIfRepeated)
- defn.FunctionType(formals1, mt.resultType)
- }
- untpd.Closure(env, meth).withType(ownType).checked
+ def Closure(env: List[Tree], meth: RefTree)(implicit ctx: Context): Closure =
+ untpd.Closure(env, meth).withType(closureType(meth.tpe.widen)).checked
+
+ def closureType(tp: Type)(implicit ctx: Context) = tp match {
+ case mt @ MethodType(_, formals) =>
+ assert(!mt.isDependent)
+ val formals1 = formals mapConserve (_.underlyingIfRepeated)
+ defn.FunctionType(formals1, mt.resultType)
}
/** A function def
diff --git a/src/dotty/tools/dotc/ast/UntypedTrees.scala b/src/dotty/tools/dotc/ast/UntypedTrees.scala
index 22e8ab56e..79bef00e8 100644
--- a/src/dotty/tools/dotc/ast/UntypedTrees.scala
+++ b/src/dotty/tools/dotc/ast/UntypedTrees.scala
@@ -63,7 +63,7 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] {
def Assign(lhs: Tree, rhs: Tree): Assign = new Assign(lhs, rhs)
def Block(stats: List[Tree], expr: Tree): Block = new Block(stats, expr)
def If(cond: Tree, thenp: Tree, elsep: Tree): If = new If(cond, thenp, elsep)
- def Closure(env: List[Tree], meth: RefTree): Closure = new Closure(env, meth)
+ def Closure(env: List[Tree], meth: Tree): Closure = new Closure(env, meth)
def Match(selector: Tree, cases: List[CaseDef]): Match = new Match(selector, cases)
def CaseDef(pat: Tree, guard: Tree, body: Tree): CaseDef = new CaseDef(pat, guard, body)
def Return(expr: Tree, from: Tree): Return = new Return(expr, from)
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 16c75e38b..8b5dcc5dc 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -659,7 +659,7 @@ object Denotations {
def recur(path: Name, len: Int): Denotation = {
val point = path.lastIndexOf('.', len - 1)
val owner =
- if (point > 0) recur(path.toTermName, point).disambiguate(_.isParameterless)
+ if (point > 0) recur(path.toTermName, point).disambiguate(_.info.isParameterless)
else if (path.isTermName) defn.RootClass.denot
else defn.EmptyPackageClass.denot
if (!owner.exists) owner
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 258604df4..d2be45444 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -287,12 +287,6 @@ object SymDenotations {
/** Is this a user defined "def" method? Excluded are accessors and stable values */
final def isSourceMethod = this is (Method, butNot = Accessor)
- /** Is this either not a method at all, or a parameterless method? */
- final def isParameterless(implicit ctx: Context) = info match {
- case _: MethodType | _: PolyType => false
- case _ => true
- }
-
/** Is this a setter? */
final def isGetter = (this is Accessor) && !originalName.isSetterName
diff --git a/src/dotty/tools/dotc/core/TyperState.scala b/src/dotty/tools/dotc/core/TyperState.scala
index e492eee60..b00e55e29 100644
--- a/src/dotty/tools/dotc/core/TyperState.scala
+++ b/src/dotty/tools/dotc/core/TyperState.scala
@@ -14,10 +14,17 @@ class TyperState(val reporter: Reporter = ThrowingReporter) extends DotClass {
def constraint: Constraint = new Constraint(SimpleMap.Empty)
/** The currently uninstantiated TypeVars */
- def undetVars: List[TypeVar] = Nil
+ def undetVars: Set[TypeVar] = Set()
+
+ /** A map that records for instantiated type vars their instance type.
+ * Used only in a temporary way for contexts that may be retracted
+ * without also retracting the type var.
+ */
+ def instType: SimpleMap[TypeVar, Type] = SimpleMap.Empty
def constraint_=(c: Constraint): Unit = {}
- def undetVars_=(vs: List[TypeVar]): Unit = unsupported("undetVars_=")
+ def undetVars_=(vs: Set[TypeVar]): Unit = unsupported("undetVars_=")
+ def instType_=(m: SimpleMap[TypeVar, Type]): Unit = unsupported("instType_=")
def fresh: TyperState = this
@@ -28,19 +35,42 @@ class MutableTyperState(previous: TyperState, reporter: Reporter)
extends TyperState(reporter) {
private var myConstraint: Constraint = previous.constraint
- private var myUndetVars: List[TypeVar] = previous.undetVars
+ private var myUndetVars: Set[TypeVar] = previous.undetVars
+ private var myInstType: SimpleMap[TypeVar, Type] = previous.instType
override def constraint = myConstraint
override def undetVars = myUndetVars
+ override def instType = myInstType
override def constraint_=(c: Constraint) = myConstraint = c
- override def undetVars_=(vs: List[TypeVar]) = myUndetVars = vs
+ override def undetVars_=(vs: Set[TypeVar]) = myUndetVars = vs
+ override def instType_=(m: SimpleMap[TypeVar, Type]): Unit = myInstType = m
override def fresh: TyperState = new MutableTyperState(this, new StoreReporter)
+ /** Commit typer state so that its information is copied into current typer state
+ * In addition (1) the owning state of undetermined or temporarily instantiated
+ * type variables changes from this typer state to the current one. (2) Variables
+ * that were temporarily instantiated in the current typer state are permanently
+ * instantiated instead.
+ */
override def commit()(implicit ctx: Context) = {
- ctx.typerState.constraint = constraint
- ctx.typerState.undetVars = undetVars
+ var targetState = ctx.typerState
+ targetState.constraint = constraint
+ targetState.undetVars = undetVars
+ targetState.instType = instType
+
+ def adjustOwningState(tvar: TypeVar) =
+ if (tvar.owningState eq this) tvar.owningState = targetState
+ undetVars foreach adjustOwningState
+ instType foreachKey { case tvar: TypeVar =>
+ adjustOwningState(tvar)
+ if (tvar.owningState == targetState) {
+ tvar.inst = instType(tvar)
+ targetState.instType = targetState.instType remove tvar
+ }
+ }
+
reporter.flush()
}
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index c3e15d94c..8df651180 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -150,7 +150,8 @@ object Types {
}
/** Does this type occur as a part of type `that`? */
- final def occursIn(that: Type): Boolean = that.existsPart(this == _)
+ final def occursIn(that: Type)(implicit ctx: Context): Boolean =
+ that.existsPart(this == _)
def isRepeatedParam(implicit ctx: Context): Boolean =
defn.RepeatedParamAliases contains typeSymbol
@@ -159,16 +160,21 @@ object Types {
/** Returns true if there is a part of this type that satisfies predicate `p`.
*/
- final def existsPart(p: Type => Boolean): Boolean =
- new ExistsAccumulator(p)(false, this)
+ final def existsPart(p: Type => Boolean)(implicit ctx: Context): Boolean =
+ new ExistsAccumulator(p).apply(false, this)
/** Returns true if all parts of this type satisfy predicate `p`.
*/
- final def forallParts(p: Type => Boolean): Boolean = !existsPart(!p(_))
+ final def forallParts(p: Type => Boolean)(implicit ctx: Context): Boolean = !existsPart(!p(_))
/** The parts of this type which are type or term refs */
final def namedParts(implicit ctx: Context): Set[NamedType] =
- new PartsAccumulator().apply(Set(), this)
+ namedPartsWith(Function.const(true))
+
+ final def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): Set[NamedType] =
+ new NamedPartsAccumulator(p).apply(Set(), this)
+
+ final def foreach(f: Type => Unit): Unit = ???
/** Map function over elements of an AndType, rebuilding with & */
def mapAnd(f: Type => Type)(implicit ctx: Context): Type = thisInstance match {
@@ -498,7 +504,7 @@ object Types {
/** Map a TypeVar to either its instance if it is instantiated, or its origin,
* if not. Identity on all other types.
*/
- def thisInstance: Type = this
+ def thisInstance(implicit ctx: Context): Type = this
/** Widen from singleton type to its underlying non-singleton
* base type by applying one or more `underlying` dereferences,
@@ -535,7 +541,7 @@ object Types {
/** If this is a refinement type, the unrefined parent,
* else the type itself.
*/
- final def unrefine: Type = thisInstance match {
+ final def unrefine(implicit ctx: Context): Type = thisInstance match {
case tp @ RefinedType(tycon, _) => tycon.unrefine
case tp => tp
}
@@ -594,6 +600,15 @@ object Types {
case pt: PolyType => pt.resultType.paramTypess
case _ => Nil
}
+
+ /** Is this either not a method at all, or a parameterless method? */
+ final def isParameterless: Boolean = this match {
+ case mt: MethodType => false
+ case pt: PolyType => pt.resultType.isParameterless
+ case _ => true
+ }
+
+
/* Not sure whether we'll need this
final def firstParamTypes: List[Type] = this match {
case mt: MethodType => mt.paramTypes
@@ -908,6 +923,12 @@ object Types {
def toText(printer: Printer): Text = printer.toText(this)
+ /** `tp` is either a type variable or poly param. Returns
+ * Covariant if all occurrences of `tp` in this type are covariant
+ * Contravariant if all occurrences of `tp` in this type are contravariant
+ * Covariant | Contravariant if there are no occurrences of `tp` in this type
+ * EmptyFlags if `tp` occurs noon-variantly in this type
+ */
def varianceOf(tp: Type): FlagSet = ???
// ----- hashing ------------------------------------------------------
@@ -1403,9 +1424,18 @@ object Types {
def isJava = false
def isImplicit = false
- lazy val isDependent = resultType existsPart {
- case MethodParam(mt, _) => mt eq this
- case _ => false
+ private[this] var myIsDependent: Boolean = _
+ private[this] var isDepKnown = false
+
+ def isDependent(implicit ctx: Context) = {
+ if (!isDepKnown) {
+ myIsDependent = resultType existsPart {
+ case MethodParam(mt, _) => mt eq this
+ case _ => false
+ }
+ isDepKnown = true
+ }
+ myIsDependent
}
private[this] var _signature: Signature = _
@@ -1623,14 +1653,76 @@ object Types {
override def toString = s"RefinedThis(${binder.hashCode})"
}
- final case class TypeVar(origin: PolyParam) extends UncachedProxyType with ValueType {
- private var inst: Type = NoType
- def isInstantiated = inst ne NoType
- def instantiateWith(tp: Type) = inst = tp
- override def thisInstance = if (isInstantiated) inst else origin
+ /** A type variable is essentially a switch that models some part of a substitution.
+ * It is first linked to `origin`, a poly param that's in the current constraint set.
+ * It can then be (once) instantiated to some other type. The instantiation is
+ * recorded in the type variable itself, or else, if the current type state
+ * is different from the variable's creation state (meaning unrolls are possible)
+ * in the current typer state. Every type variable is referred to by exactly
+ * one inferred type parameter in a TypeApply tree.
+ *
+ * @param origin The parameter that's tracked by the type variable.
+ * @param creatorState The typer state in which the variable was created.
+ * @param pos The position of the TypeApply tree that introduces
+ * the type variable.
+ */
+ final class TypeVar(val origin: PolyParam, creatorState: TyperState, val pos: Position) extends UncachedProxyType with ValueType {
+
+ /** The permanent instance type of the the variable, or NoType is none is given yet */
+ private[core] var inst: Type = NoType
+
+ /** The state owning the variable. This is at first creationState, but it can
+ * be changed to an enclosing state on a commit
+ */
+ private[core] var owningState = creatorState
+
+ assert(!(creatorState.undetVars contains this))
+ creatorState.undetVars += this
+
+ /** The instance type of this variable, or NoType if the variable is currently
+ * uninstantiated
+ */
+ def instanceOpt(implicit ctx: Context): Type =
+ if (inst.exists) inst
+ else {
+ val i = ctx.typerState.instType(this)
+ if (i == null) NoType else i
+ }
+
+ /** Is the variable already instantiated? */
+ def isInstantiated(implicit ctx: Context) = instanceOpt.exists
+
+ /** Instantiate variable with given type */
+ def instantiateWith(tp: Type)(implicit ctx: Context): Type = {
+ assert(owningState.undetVars contains this)
+ owningState.undetVars -= this
+ if (ctx.typerState eq creatorState) inst = tp
+ else ctx.typerState.instType = ctx.typerState.instType.updated(this, tp)
+ tp
+ }
+
+ /** Instantiate variable from the constraints over its `origin`.
+ * If `fromBelow` is true, the variable is instantiated to the lub
+ * of its lower bounds in the current constraint; otherwie it is
+ * instantiated to the glb of its upper bounds.
+ */
+ def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type =
+ instantiateWith(ctx.typeComparer.approximate(origin, fromBelow))
+
+ /** If the variable is instantiated, its instance, otherwise its origin */
+ override def thisInstance(implicit ctx: Context) = {
+ val inst = instanceOpt
+ if (inst.exists) inst else origin
+ }
+
+ /** Same as `thisInstance` */
override def underlying(implicit ctx: Context): Type = thisInstance
+
+ override def hashCode: Int = System.identityHashCode(this)
override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
- override def toString = thisInstance.toString
+
+ override def toString =
+ if (inst.exists) inst.toString else s"TypeVar($origin)"
}
// ------ ClassInfo, Type Bounds ------------------------------------------------------------
@@ -1871,12 +1963,12 @@ object Types {
case _ =>
false
}
- def unapply(tp: Type)(implicit ctx: Context): Option[(SingleDenotation, List[Type], Type)] =
+ def unapply(tp: Type)(implicit ctx: Context): Option[SingleDenotation] =
if (isInstantiatable(tp)) {
val absMems = tp.abstractTermMembers
if (absMems.size == 1)
absMems.head.info match {
- case mt: MethodType if !mt.isDependent => Some((absMems.head, mt.paramTypes, mt.resultType))
+ case mt: MethodType if !mt.isDependent => Some(absMems.head)
case _=> None
}
else None
@@ -1927,7 +2019,7 @@ object Types {
case tp @ AnnotatedType(annot, underlying) =>
tp.derivedAnnotatedType(mapOver(annot), this(underlying))
- case tp @ TypeVar(_) =>
+ case tp: TypeVar =>
apply(tp.thisInstance)
case tp @ WildcardType =>
@@ -1980,7 +2072,7 @@ object Types {
// ----- TypeAccumulators ----------------------------------------------------
- abstract class TypeAccumulator[T] extends ((T, Type) => T) {
+ abstract class TypeAccumulator[T](implicit ctx: Context) extends ((T, Type) => T) {
def apply(x: T, tp: Type): T
protected def apply(x: T, annot: Annotation): T = x // don't go into annotations
@@ -2024,13 +2116,13 @@ object Types {
}
}
- class ExistsAccumulator(p: Type => Boolean) extends TypeAccumulator[Boolean] {
+ class ExistsAccumulator(p: Type => Boolean)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
def apply(x: Boolean, tp: Type) = x || p(tp) || foldOver(x, tp)
}
- class PartsAccumulator(implicit ctx: Context) extends TypeAccumulator[Set[NamedType]] {
+ class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[Set[NamedType]] {
def apply(x: Set[NamedType], tp: Type): Set[NamedType] = tp match {
- case tp: NamedType =>
+ case tp: NamedType if (p(tp)) =>
foldOver(x + tp, tp)
case tp: ThisType =>
apply(x, tp.underlying)
diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
index a488e0e18..4c96c5e4b 100644
--- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
@@ -596,7 +596,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot:
else ThisType(cls)
case SINGLEtpe =>
val pre = readTypeRef()
- val sym = readDisambiguatedSymbolRef(_.isParameterless)
+ val sym = readDisambiguatedSymbolRef(_.info.isParameterless)
if (isLocal(sym) || (pre == NoPrefix)) TermRef.withSym(pre, sym.asTerm)
else TermRef.withSig(pre, sym.name.asTermName, NotAMethod)
case SUPERtpe =>
diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala
index b903a5ce1..04afa5709 100644
--- a/src/dotty/tools/dotc/printing/PlainPrinter.scala
+++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala
@@ -137,6 +137,8 @@ class PlainPrinter(_ctx: Context) extends Printer {
toText(polyParamName(pt.paramNames(n)))
case AnnotatedType(annot, tpe) =>
toTextLocal(tpe) ~ " " ~ toText(annot)
+ case tp: TypeVar =>
+ toTextLocal(tp.underlying)
case _ =>
tp.fallbackToText(this)
}
diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala
index e0ff8e351..7a91adde6 100644
--- a/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -13,7 +13,7 @@ object ErrorReporting {
import tpd._
- def errorTree(tree: Tree, msg: => String)(implicit ctx: Context): tpd.Tree =
+ def errorTree(tree: untpd.Tree, msg: => String)(implicit ctx: Context): tpd.Tree =
tree withType errorType(msg, tree.pos)
def errorType(msg: => String, pos: Position)(implicit ctx: Context): ErrorType = {
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 1ae33d87a..5c9c86c0c 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -14,11 +14,39 @@ object Inferencing {
import tpd._
+ /** Is type fully defined, meaning the type does not contain wildcard types
+ * or uninstantiated type variables. As a side effect, this will minimize
+ * any uninstantiated type variables, provided that
+ * - the instance type for the variable is not Nothing or Null
+ * - the overall result of `isFullYDefined` is `true`.
+ * Variables that are succesfully minimized do not count as uninstantiated.
+ */
+ def isFullyDefined(tp: Type)(implicit ctx: Context): Boolean = {
+ val nestedCtx = ctx.fresh.withNewTyperState
+ val result = new IsFullyDefinedAccumulator()(nestedCtx).traverse(tp)
+ if (result) nestedCtx.typerState.commit()
+ result
+ }
+
+ private class IsFullyDefinedAccumulator(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ def traverse(tp: Type): Boolean = apply(true, tp)
+ def apply(x: Boolean, tp: Type) = !x || isOK(tp) && foldOver(x, tp)
+ def isOK(tp: Type): Boolean = tp match {
+ case _: WildcardType =>
+ false
+ case tvar: TypeVar if !tvar.isInstantiated =>
+ val inst = tvar.instantiate(fromBelow = true)
+ inst != defn.NothingType && inst != defn.NullType
+ case _ =>
+ true
+ }
+ }
+
def checkBounds(args: List[Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = {
}
- def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
+ def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Type = {
if (!tp.isStable)
ctx.error(s"Prefix ${tp.show} is not stable", pos)
tp
@@ -57,38 +85,27 @@ object Inferencing {
tracked
}
- /** Interpolate undetermined variables.
- * If a variable appears covariantly in type `tp`, approximate it by
- * its lower bound. Otherwise, if it appears contravariantly in type `tp`,
- * approximate it by its upper bound. Otherwise, if `always` is true,
- * approximate it also by its lower bound.
- * Instantiated variables are removed from `undetVars`.
+ /** Interpolate those undetermined type variables whose position
+ * is included in the position `pos` of the current tree.
+ * If such a variable appears covariantly in type `tp` or does not appear at all,
+ * approximate it by its lower bound. Otherwise, if it appears contravariantly
+ * in type `tp` approximate it by its upper bound.
*/
- def interpolateUndetVars(upTo: List[TypeVar], tp: Type, always: Boolean = false): Unit = {
- def recur(undets: List[TypeVar]): List[TypeVar] =
- if (undets eq upTo) undets
- else (undets: @unchecked) match {
- case tvar :: rest =>
- def instantiate(fromBelow: Boolean) = {
- tvar.instantiateWith(ctx.typeComparer.approximate(tvar.origin, fromBelow))
- recur(rest)
- }
- val v = tp varianceOf tvar
- if (v is Covariant) instantiate(fromBelow = true)
- else if (v is Contravariant) instantiate(fromBelow = false)
- else if (always) instantiate(fromBelow = true)
- else tvar :: recur(rest)
+ def interpolateUndetVars(tp: Type, pos: Position): Unit =
+ for (tvar <- ctx.typerState.undetVars)
+ if (pos contains tvar.pos) {
+ val v = tp varianceOf tvar
+ if (v is Covariant) tvar.instantiate(fromBelow = true)
+ else if (v is Contravariant) tvar.instantiate(fromBelow = false)
}
- state.undetVars = recur(state.undetVars)
- }
- def newTypeVars(pt: PolyType): List[TypeVar] = {
- val tvars =
- for (n <- (0 until pt.paramNames.length).toList)
- yield TypeVar(PolyParam(pt, n))
- state.undetVars = tvars ++ state.undetVars
- tvars
- }
+ /** Create new type variables for the parameters of a poly type.
+ * @param pos The position of the new type variables (relevant for
+ * interpolateUndetVars
+ */
+ def newTypeVars(pt: PolyType, pos: Position): List[TypeVar] =
+ for (n <- (0 until pt.paramNames.length).toList)
+ yield new TypeVar(PolyParam(pt, n), ctx.typerState, pos)
def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match {
case formal :: formals1 =>
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 733eac739..ffd4b2ce1 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -346,21 +346,106 @@ class Typer extends Namer with Applications with Implicits {
cpy.Pair(tree, left1, right1).withType(defn.PairType.appliedTo(left1.tpe :: right1.tpe :: Nil))
}
- def TypedTyped(tree: untpd.Typed)(implicit ctx: Context) = {
+ def typedTyped(tree: untpd.Typed)(implicit ctx: Context) = {
val tpt1 = typedType(tree.tpt)
val expr1 = typedExpr(tree.expr, tpt1.tpe)
cpy.Typed(tree, tpt1, expr1).withType(tpt1.tpe)
}
- def NamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = {
+ def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = {
val arg1 = typed(tree.arg, pt)
cpy.NamedArg(tree, tree.name, arg1).withType(arg1.tpe)
}
- def Assign(tree: untpd.Assign)(implicit ctx: Context) = {
+ def typedAssign(tree: untpd.Assign)(implicit ctx: Context) = tree.lhs match {
+ case lhs @ Apply(fn, args) =>
+ typed(cpy.Apply(lhs, untpd.Select(fn, nme.update), args :+ tree.rhs))
+ case lhs =>
+ val lhs1 = typed(lhs)
+ def reassignmentToVal =
+ errorTree(cpy.Assign(tree, lhs1, typed(tree.rhs, lhs1.tpe.widen)),
+ "reassignment to val")
+ lhs1.tpe match {
+ case ref: TermRef if ref.symbol is Mutable =>
+ cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info)).withType(defn.UnitType)
+ case ref: TermRef if ref.info.isParameterless =>
+ val pre = ref.prefix
+ val setterName = ref.name.getterToSetter
+ val setter = pre.member(setterName)
+ lhs1 match {
+ case lhs1: RefTree if setter.exists =>
+ val setterTypeRaw = TermRef(pre, setterName).withDenot(setter)
+ val setterType = checkAccessible(setterTypeRaw, isSuperSelection(tree), tree.pos)
+ val lhs2 = lhs1.withName(setterName).withType(setterType)
+ typed(cpy.Apply(tree, untpd.TypedSplice(lhs2), tree.rhs :: Nil))
+ case _ =>
+ reassignmentToVal
+ }
+ case _ =>
+ reassignmentToVal
+ }
+ }
+
+ def typedBlock(tree: Block, pt: Type)(implicit ctx: Context) = {
+ val exprCtx = enterSyms(tree.stats)
+ val stats1 = typedStats(tree.stats, ctx.owner)
+ val expr1 = typedExpr(tree.expr, pt)(exprCtx)
+ val result = cpy.Block(tree, stats1, expr1).withType(blockType(stats1, expr1.tpe))
+ val leaks = CheckTrees.escapingRefs(result)
+ if (leaks.isEmpty) result
+ else if (isFullyDefined(pt)) {
+ val expr2 = typed(untpd.Typed(untpd.TypedSplice(expr1), untpd.TypeTree(pt)))
+ untpd.Block(stats1, expr2) withType expr2.tpe
+ } else errorTree(result,
+ s"local definition of ${leaks.head.name} escapes as part of block's type ${result.tpe.show}")
+ }
+
+ def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = {
+ val cond1 = typed(tree.cond, defn.BooleanType)
+ val thenp1 = typed(tree.thenp, pt)
+ val elsep1 = typed(tree.elsep, pt)
+ cpy.If(tree, cond1, thenp1, elsep1).withType(thenp1.tpe | elsep1.tpe)
+ }
+
+ def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = {
+ val params = tree.args.asInstanceOf[List[ValDef]]
+ val protoFormals: List[Type] = pt match {
+ case _ if pt.typeSymbol == defn.FunctionClass(params.length) =>
+ pt.typeArgs take params.length
+ case MethodType(_, paramTypes) =>
+ paramTypes
+ case _ =>
+ params map Function.const(WildcardType)
+ }
+ val inferredParams =
+ for ((param, formal) <- (params, protoFormals).zipped)
+ if (param.tpt.isEmpty && isFullyDefined(formal))
+ cpy.ValDef(param, param.mods, param.name, untpd.TypeTree(formal), param.rhs)
+ else
+ param
+
???
}
+ def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = {
+ val env1 = tree.env map (typed(_))
+ val meth1 = typed(tree.meth)
+ pt match {
+ case SAMType(meth) if !defn.isFunctionType(pt) =>
+ ???
+ case _ =>
+ val ownType = meth1.tpe.widen match {
+ case mt: MethodType if !mt.isDependent =>
+ closureType(mt)
+ case mt: MethodType =>
+ errorType(s"cannot turn dependent method types into closures", tree.pos)
+ case tp =>
+ errorType(s"internal error: closing over non-method $tp", tree.pos)
+ }
+ cpy.Closure(tree, env1, meth1).withType(ownType)
+ }
+ }
+
def typedModifiers(mods: untpd.Modifiers)(implicit ctx: Context): Modifiers = {
val annotations1 = mods.annotations mapconserve typedAnnotation
if (annotations1 eq mods.annotations) mods.asInstanceOf[Modifiers]
@@ -464,8 +549,10 @@ class Typer extends Namer with Applications with Implicits {
case none => tree
}
case _ => tree
- }
- typedExpanded(xtree, pt)
+ }
+ val tree1 = typedExpanded(xtree, pt)
+ ctx.interpolateUndetVars(tree1.tpe.widen, tree1.pos)
+ adapt(tree1, pt)
}
def typedTrees(trees: List[untpd.Tree])(implicit ctx: Context): List[Tree] =
@@ -482,7 +569,8 @@ class Typer extends Namer with Applications with Implicits {
buf += typed(mdef)
traverse(rest)
case stat :: rest =>
- buf += typed(stat)(ctx.fresh.withOwner(exprOwner))
+ val nestedCtx = if (exprOwner == ctx.owner) ctx else ctx.fresh.withOwner(exprOwner)
+ buf += typed(stat)(nestedCtx)
traverse(rest)
case _ =>
buf.toList
@@ -515,45 +603,44 @@ class Typer extends Namer with Applications with Implicits {
fallBack
}
- /**
- * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
- * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
- */
+ /** (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
+ * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
+ */
- /** Perform the following adaptations of expression, pattern or type `tree` wrt to
- * given prototype `pt`:
- * (1) Resolve overloading
- * (2) Apply parameterless functions
- * (3) Apply polymorphic types to fresh instances of their type parameters and
- * store these instances in context.undetparams,
- * unless followed by explicit type application.
- * (4) Do the following to unapplied methods used as values:
- * (4.1) If the method has only implicit parameters pass implicit arguments
- * (4.2) otherwise, if `pt` is a function type and method is not a constructor,
- * convert to function by eta-expansion,
- * (4.3) otherwise, if the method is nullary with a result type compatible to `pt`
- * and it is not a constructor, apply it to ()
- * otherwise issue an error
- * (5) Convert constructors in a pattern as follows:
- * (5.1) If constructor refers to a case class factory, set tree's type to the unique
- * instance of its primary constructor that is a subtype of the expected type.
- * (5.2) If constructor refers to an extractor, convert to application of
- * unapply or unapplySeq method.
- *
- * (6) Convert all other types to TypeTree nodes.
- * (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized
- * (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity
- * (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
- * (9) If there are undetermined type variables and not POLYmode, infer expression instance
- * Then, if tree's type is not a subtype of expected type, try the following adaptations:
- * (10) If the expected type is Byte, Short or Char, and the expression
- * is an integer fitting in the range of that type, convert it to that type.
- * (11) Widen numeric literals to their expected type, if necessary
- * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
- * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
- * (14) When in mode EXPRmode, apply a view
- * If all this fails, error
- */
+ /** Perform the following adaptations of expression, pattern or type `tree` wrt to
+ * given prototype `pt`:
+ * (1) Resolve overloading
+ * (2) Apply parameterless functions
+ * (3) Apply polymorphic types to fresh instances of their type parameters and
+ * store these instances in context.undetparams,
+ * unless followed by explicit type application.
+ * (4) Do the following to unapplied methods used as values:
+ * (4.1) If the method has only implicit parameters pass implicit arguments
+ * (4.2) otherwise, if `pt` is a function type and method is not a constructor,
+ * convert to function by eta-expansion,
+ * (4.3) otherwise, if the method is nullary with a result type compatible to `pt`
+ * and it is not a constructor, apply it to ()
+ * otherwise issue an error
+ * (5) Convert constructors in a pattern as follows:
+ * (5.1) If constructor refers to a case class factory, set tree's type to the unique
+ * instance of its primary constructor that is a subtype of the expected type.
+ * (5.2) If constructor refers to an extractor, convert to application of
+ * unapply or unapplySeq method.
+ *
+ * (6) Convert all other types to TypeTree nodes.
+ * (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized
+ * (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity
+ * (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
+ * (9) If there are undetermined type variables and not POLYmode, infer expression instance
+ * Then, if tree's type is not a subtype of expected type, try the following adaptations:
+ * (10) If the expected type is Byte, Short or Char, and the expression
+ * is an integer fitting in the range of that type, convert it to that type.
+ * (11) Widen numeric literals to their expected type, if necessary
+ * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
+ * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
+ * (14) When in mode EXPRmode, apply a view
+ * If all this fails, error
+ */
def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree = {
def adaptOverloaded(ref: TermRef) = {
@@ -630,7 +717,7 @@ class Typer extends Namer with Applications with Implicits {
if (pt.isInstanceOf[PolyProtoType]) tree
else {
val tracked = ctx.track(poly)
- val tvars = ctx.newTypeVars(tracked)
+ val tvars = ctx.newTypeVars(tracked, tree.pos)
adapt(tpd.TypeApply(tree, tvars map (tpd.TypeTree(_))), pt)
}
case tp =>
diff --git a/src/dotty/tools/dotc/util/SimpleMap.scala b/src/dotty/tools/dotc/util/SimpleMap.scala
index 218af2fc5..3acc5c4b8 100644
--- a/src/dotty/tools/dotc/util/SimpleMap.scala
+++ b/src/dotty/tools/dotc/util/SimpleMap.scala
@@ -6,6 +6,7 @@ abstract class SimpleMap[-K, +V >: Null] {
def updated[V1 >: V](k: K, v: V1): SimpleMap[K, V1]
def contains(k: K): Boolean = apply(k) != null
def mapValues[V1 >: V](f: V1 => V1): SimpleMap[K, V1]
+ def foreachKey(f: Any => Unit): Unit
}
object SimpleMap {
@@ -15,6 +16,7 @@ object SimpleMap {
def remove(k: Any) = this
def updated[V1 >: Null](k: Any, v: V1) = new Map1(k, v)
def mapValues[V1 >: Null](f: V1 => V1) = this
+ def foreachKey(f: Any => Unit) = ()
}
class Map1[-K, +V >: Null] (k1: K, v1: V) extends SimpleMap[K, V] {
@@ -29,6 +31,7 @@ object SimpleMap {
else new Map2(k1, v1, k, v)
def mapValues[V1 >: V](f: V1 => V1) =
new Map1(k1, f(v1))
+ def foreachKey(f: Any => Unit) = f(k1)
}
class Map2[-K, +V >: Null] (k1: K, v1: V, k2: K, v2: V) extends SimpleMap[K, V] {
@@ -46,6 +49,7 @@ object SimpleMap {
else new Map3(k1, v1, k2, v2, k, v)
def mapValues[V1 >: V](f: V1 => V1) =
new Map2(k1, f(v1), k2, f(v2))
+ def foreachKey(f: Any => Unit) = { f(k1); f(k2) }
}
class Map3[-K, +V >: Null] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V) extends SimpleMap[K, V] {
@@ -66,6 +70,7 @@ object SimpleMap {
else new Map4(k1, v1, k2, v2, k3, v3, k, v)
def mapValues[V1 >: V](f: V1 => V1) =
new Map3(k1, f(v1), k2, f(v2), k3, f(v3))
+ def foreachKey(f: Any => Unit) = { f(k1); f(k2); f(k3) }
}
class Map4[-K, +V >: Null] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V, k4: K, v4: V) extends SimpleMap[K, V] {
@@ -89,6 +94,7 @@ object SimpleMap {
else new Map5(k1, v1, k2, v2, k3, v3, k4, v4, k, v)
def mapValues[V1 >: V](f: V1 => V1) =
new Map4(k1, f(v1), k2, f(v2), k3, f(v3), k4, f(v4))
+ def foreachKey(f: Any => Unit) = { f(k1); f(k2); f(k3); f(k4) }
}
class Map5[-K, +V >: Null] (k1: K, v1: V, k2: K, v2: V, k3: K, v3: V, k4: K, v4: V, k5: K, v5: V) extends SimpleMap[K, V] {
@@ -115,6 +121,7 @@ object SimpleMap {
else new MapMore(Map(k1 -> v1, k2 -> v2, k3 -> v3, k4 -> v4, k5 -> v5, k -> v))
def mapValues[V1 >: V](f: V1 => V1) =
new Map5(k1, f(v1), k2, f(v2), k3, f(v3), k4, f(v4), k5, f(v5))
+ def foreachKey(f: Any => Unit) = { f(k1); f(k2); f(k3); f(k4); f(k5) }
}
class MapMore[-K, +V >: Null] (m: Map[K, V]) extends SimpleMap[K, V] {
@@ -135,5 +142,6 @@ object SimpleMap {
override def contains(k: K) = m contains k
def mapValues[V1 >: V](f: V1 => V1) =
new MapMore(m mapValues f)
+ def foreachKey(f: Any => Unit) = { m.keysIterator foreach f }
}
}