aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer/Applications.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-07-25 22:16:07 +0200
committerMartin Odersky <odersky@gmail.com>2013-07-25 22:22:42 +0200
commitcf65e84a6da2a151286a36297c057b72545960c8 (patch)
tree55f3d8a30f5751602836d62d0b1a4ae6269b64bd /src/dotty/tools/dotc/typer/Applications.scala
parent0a86c0ae8668070f62df25c7a4ba12369f23b216 (diff)
downloaddotty-cf65e84a6da2a151286a36297c057b72545960c8.tar.gz
dotty-cf65e84a6da2a151286a36297c057b72545960c8.tar.bz2
dotty-cf65e84a6da2a151286a36297c057b72545960c8.zip
More typer logic, in particular dealing with variants of applications
Diffstat (limited to 'src/dotty/tools/dotc/typer/Applications.scala')
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala273
1 files changed, 208 insertions, 65 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index f4b8d8b12..3ce735c12 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -13,7 +13,10 @@ import Denotations._
import NameOps._
import Symbols._
import Types._
+import Typer.TreeDecorator
import Decorators._
+import ErrorReporting._
+import Trees._
import Names._
import StdNames._
import Constants._
@@ -24,6 +27,8 @@ import language.implicitConversions
object Applications {
+ import tpd._
+
private val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]]
def hasNamedArg(args: List[Any]) = args exists isNamedArg
@@ -61,16 +66,18 @@ object Applications {
}
case class FunProtoType(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context) extends UncachedGroundType {
- private var myTypedArgs: List[tpd.Tree] = null
+ private var myTypedArgs: List[Tree] = null
def argsAreTyped: Boolean = myTypedArgs != null
- def typedArgs: List[tpd.Tree] = {
+ def typedArgs: List[Tree] = {
if (myTypedArgs == null)
myTypedArgs = args mapconserve (typer.typed(_))
myTypedArgs
}
}
+
+ case class PolyProtoType(nargs: Int, override val resultType: Type) extends UncachedGroundType
}
import Applications._
@@ -78,7 +85,7 @@ import Applications._
trait Applications extends Compatibility{ self: Typer =>
import Applications._
- import Trees._
+ import tpd._
private def state(implicit ctx: Context) = ctx.typerState
@@ -100,7 +107,7 @@ trait Applications extends Compatibility{ self: Typer =>
protected def typedArg(arg: Arg, formal: Type): TypedArg
/** Turn a typed tree into an argument */
- protected def treeToArg(arg: tpd.Tree): Arg
+ protected def treeToArg(arg: Tree): Arg
/** Check that argument corresponds to type `formal` and
* possibly add it to the list of adapted arguments
@@ -126,7 +133,7 @@ trait Applications extends Compatibility{ self: Typer =>
/** If constructing trees, the current function part, which might be
* affected by lifting. EmptyTree otherwise.
*/
- protected def normalizedFun: tpd.Tree
+ protected def normalizedFun: Tree
/** If constructing trees, pull out all parts of the function
* which are not idempotent into separate prefix definitions
@@ -175,11 +182,11 @@ trait Applications extends Compatibility{ self: Typer =>
private def methString: String = s"method ${methRef.name}: ${methType.show}"
/** Re-order arguments to correctly align named arguments */
- def reorder[T >: Untyped](args: List[Tree[T]]): List[Tree[T]] = {
- var namedToArg: Map[Name, Tree[T]] =
+ def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
+ var namedToArg: Map[Name, Trees.Tree[T]] =
(for (NamedArg(name, arg1) <- args) yield (name, arg1)).toMap
- def badNamedArg(arg: Tree[_ >: Untyped]): Unit = {
+ def badNamedArg(arg: Trees.Tree[_ >: Untyped]): Unit = {
val NamedArg(name, _) = arg
def msg =
if (methodType.paramNames contains name)
@@ -189,7 +196,7 @@ trait Applications extends Compatibility{ self: Typer =>
fail(msg, arg.asInstanceOf[Arg])
}
- def recur(pnames: List[Name], args: List[Tree[T]]): List[Tree[T]] = pnames match {
+ def recur(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = pnames match {
case pname :: pnames1 =>
namedToArg get pname match {
case Some(arg) =>
@@ -223,9 +230,9 @@ trait Applications extends Compatibility{ self: Typer =>
}
/** Splice new method reference into existing application */
- def spliceMeth(meth: tpd.Tree, app: tpd.Tree): tpd.Tree = app match {
- case Apply(fn, args) => tpd.Apply(spliceMeth(meth, fn), args)
- case TypeApply(fn, targs) => tpd.TypeApply(spliceMeth(meth, fn), targs)
+ def spliceMeth(meth: Tree, app: Tree): Tree = app match {
+ case Apply(fn, args) => Apply(spliceMeth(meth, fn), args)
+ case TypeApply(fn, targs) => TypeApply(spliceMeth(meth, fn), targs)
case _ => meth
}
@@ -285,7 +292,7 @@ trait Applications extends Compatibility{ self: Typer =>
findDefaultGetter(n + TreeInfo.numArgs(normalizedFun)) match {
case dref: NamedType =>
liftFun()
- addTyped(treeToArg(spliceMeth(tpd.Ident(dref), normalizedFun)), formal)
+ addTyped(treeToArg(spliceMeth(Ident(dref), normalizedFun)), formal)
matchArgs(args1, formals1, n + 1)
case _ =>
missingArg(n)
@@ -362,29 +369,29 @@ trait Applications extends Compatibility{ self: Typer =>
ok = false
def fail(msg: => String) =
ok = false
- def normalizedFun = tpd.EmptyTree
+ def normalizedFun = EmptyTree
}
/** Subtrait of Application for the cases where arguments are (typed or
* untyped) trees.
*/
- trait TreeApplication[T >: Untyped] extends Application[Tree[T]] {
- type TypeArg = tpd.Tree
- def isVarArg(arg: Tree[T]): Boolean = TreeInfo.isWildcardStarArg(arg)
+ trait TreeApplication[T >: Untyped] extends Application[Trees.Tree[T]] {
+ type TypeArg = Tree
+ def isVarArg(arg: Trees.Tree[T]): Boolean = TreeInfo.isWildcardStarArg(arg)
}
/** Subclass of Application for applicability tests with trees as arguments. */
- class ApplicableToTrees(methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context)
+ class ApplicableToTrees(methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context)
extends TestApplication(methRef, methRef, args, resultType) with TreeApplication[Type] {
- def argType(arg: tpd.Tree): Type = normalize(arg.tpe)
- def treeToArg(arg: tpd.Tree): tpd.Tree = arg
+ def argType(arg: Tree): Type = normalize(arg.tpe)
+ def treeToArg(arg: Tree): Tree = arg
}
/** Subclass of Application for applicability tests with types as arguments. */
class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context)
extends TestApplication(methRef, methRef, args, resultType) {
def argType(arg: Type): Type = arg
- def treeToArg(arg: tpd.Tree): Type = arg.tpe
+ def treeToArg(arg: Tree): Type = arg.tpe
def isVarArg(arg: Type): Boolean = arg.isRepeatedParam
}
@@ -392,24 +399,24 @@ trait Applications extends Compatibility{ self: Typer =>
* types of arguments are either known or unknown.
*/
abstract class TypedApply[T >: Untyped](
- app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[Tree[T]], resultType: Type)(implicit ctx: Context)
+ app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Trees.Tree[T]], resultType: Type)(implicit ctx: Context)
extends Application(methRef, fun.tpe, args, resultType) with TreeApplication[T] {
- type TypedArg = tpd.Tree
- private var typedArgBuf = new mutable.ListBuffer[tpd.Tree]
- private var liftedDefs: mutable.ListBuffer[tpd.Tree] = null
- private var myNormalizedFun: tpd.Tree = fun
+ type TypedArg = Tree
+ private var typedArgBuf = new mutable.ListBuffer[Tree]
+ private var liftedDefs: mutable.ListBuffer[Tree] = null
+ private var myNormalizedFun: Tree = fun
- def addArg(arg: tpd.Tree, formal: Type): Unit =
+ def addArg(arg: Tree, formal: Type): Unit =
typedArgBuf += adapt(arg, formal)
def makeVarArg(n: Int, elemFormal: Type): Unit = {
val args = typedArgBuf.takeRight(n).toList
typedArgBuf.trimEnd(n)
val seqType = if (methodType.isJava) defn.ArrayType else defn.SeqType
- typedArgBuf += tpd.SeqLiteral(seqType.appliedTo(elemFormal :: Nil), args)
+ typedArgBuf += SeqLiteral(seqType.appliedTo(elemFormal :: Nil), args)
}
- def fail(msg: => String, arg: Tree[T]) = {
+ def fail(msg: => String, arg: Trees.Tree[T]) = {
ctx.error(msg, arg.pos)
ok = false
}
@@ -423,7 +430,7 @@ trait Applications extends Compatibility{ self: Typer =>
override def liftFun(): Unit =
if (liftedDefs == null) {
- liftedDefs = new mutable.ListBuffer[tpd.Tree]
+ liftedDefs = new mutable.ListBuffer[Tree]
myNormalizedFun = liftApp(liftedDefs, myNormalizedFun)
}
@@ -431,7 +438,7 @@ trait Applications extends Compatibility{ self: Typer =>
* where `EmptyTree`s in the second list are skipped.
* -1 if there are no differences.
*/
- private def firstDiff[T <: Tree[_]](xs: List[T], ys: List[T], n: Int = 0): Int = xs match {
+ private def firstDiff[T <: Trees.Tree[_]](xs: List[T], ys: List[T], n: Int = 0): Int = xs match {
case x :: xs1 =>
ys match {
case EmptyTree :: ys1 => firstDiff(xs1, ys1, n)
@@ -445,53 +452,183 @@ trait Applications extends Compatibility{ self: Typer =>
case nil => -1
}
}
- def sameSeq[T <: Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0
-
- val result: tpd.Tree =
- if (!success) app withType ErrorType
- else {
- var typedArgs = typedArgBuf.toList
- if (!sameSeq(app.args, orderedArgs)) {
- // need to lift arguments to maintain evaluation order in the
- // presence of argument reorderings.
- liftFun()
- val eqSuffixLength = firstDiff(app.args.reverse, orderedArgs.reverse)
- val (liftable, rest) = typedArgs splitAt (typedArgs.length - eqSuffixLength)
- typedArgs = liftArgs(liftedDefs, methType, liftable) ++ rest
+ def sameSeq[T <: Trees.Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0
+
+ val result = {
+ var typedArgs = typedArgBuf.toList
+ val ownType =
+ if (!success) ErrorType
+ else {
+ if (!sameSeq(app.args, orderedArgs)) {
+ // need to lift arguments to maintain evaluation order in the
+ // presence of argument reorderings.
+ liftFun()
+ val eqSuffixLength = firstDiff(app.args.reverse, orderedArgs.reverse)
+ val (liftable, rest) = typedArgs splitAt (typedArgs.length - eqSuffixLength)
+ typedArgs = liftArgs(liftedDefs, methType, liftable) ++ rest
+ }
+ if (sameSeq(typedArgs, args)) // trick to cut down on tree copying
+ typedArgs = args.asInstanceOf[List[Tree]]
+ methodType.instantiate(typedArgs map (_.tpe))
}
- if (sameSeq(typedArgs, args)) // trick to cut down on tree copying
- typedArgs = args.asInstanceOf[List[tpd.Tree]]
- val app1 = app.withType(methodType.instantiate(typedArgs map (_.tpe)))
- .derivedApply(normalizedFun, typedArgs)
- if (liftedDefs != null && liftedDefs.nonEmpty) tpd.Block(liftedDefs.toList, app1)
- else app1
- }
+ val app1 = app.withType(ownType).derivedApply(normalizedFun, typedArgs)
+ if (liftedDefs != null && liftedDefs.nonEmpty) Block(liftedDefs.toList, app1)
+ else app1
+ }
}
/** Subclass of Application for type checking an Apply node with untyped arguments. */
- class ApplyToUntyped(app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[untpd.Tree], resultType: Type)(implicit ctx: Context)
+ class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[untpd.Tree], resultType: Type)(implicit ctx: Context)
extends TypedApply(app, fun, methRef, args, resultType) {
def typedArg(arg: untpd.Tree, formal: Type): TypedArg = typed(arg, formal)
- def treeToArg(arg: tpd.Tree): untpd.Tree = untpd.TypedSplice(arg)
+ def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg)
}
/** Subclass of Application for type checking an Apply node with typed arguments. */
- class ApplyToTyped(app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context)
+ class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context)
extends TypedApply(app, fun, methRef, args, resultType) {
- def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg
- def treeToArg(arg: tpd.Tree): tpd.Tree = arg
+ def typedArg(arg: Tree, formal: Type): TypedArg = arg
+ def treeToArg(arg: Tree): Tree = arg
}
- def typedApply(app: untpd.Apply, fun: tpd.Tree, methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context): tpd.Tree =
+ def typedApply(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree =
new ApplyToTyped(app, fun, methRef, args, resultType).result
- def typedApply(fun: tpd.Tree, methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context): tpd.Tree =
- typedApply(Apply(untpd.TypedSplice(fun), Nil), fun, methRef, args, resultType)
+ def typedApply(fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context): Tree =
+ typedApply(Trees.Apply(untpd.TypedSplice(fun), Nil), fun, methRef, args, resultType)
+
+ def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
+ if (ctx.mode is Mode.Pattern)
+ typedUnApply(tree.fun, tree.args, tree, pt)
+ else {
+
+ def realApply(implicit ctx: Context) = {
+ val proto = new FunProtoType(tree.args, pt, this)
+ val fun1 = typedExpr(tree.fun, proto)
+ TreeInfo.methPart(fun1).tpe match {
+ case funRef: TermRef =>
+ val app =
+ if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
+ else new ApplyToUntyped(tree, fun1, funRef, tree.args, pt)
+ val result = app.result
+ ConstFold(result) orElse result
+ case _ =>
+ fun1.exprType match {
+ case ErrorType =>
+ tree.withType(ErrorType)
+ }
+ }
+ }
+
+ def typedOpAssign: Tree = {
+ val Apply(Select(lhs, name), rhss) = tree
+ val lhs1 = typedExpr(lhs)
+ val lifted = new mutable.ListBuffer[Tree]
+ val lhs2 = untpd.TypedSplice(liftApp(lifted, lhs1))
+ val assign = Trees.Assign(lhs2, Trees.Apply(Trees.Select(lhs2, name.init), rhss))
+ typed(assign)
+ }
+
+ realApply
+ if (TreeInfo.isOpAssign(tree))
+ tryEither {
+ implicit ctx => realApply
+ } { failed =>
+ tryEither {
+ implicit ctx => typedOpAssign
+ } { _ =>
+ failed.commit()
+ }
+ }
+ else realApply
+ }
+ }
+
+ def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = {
+ val typedFn = typedExpr(tree.fun, PolyProtoType(tree.args.length, pt))
+ val typedArgs = tree.args map (typedType(_))
+ val ownType = typedFn.tpe.widen match {
+ case pt: PolyType =>
+ checkBounds(typedArgs, pt, tree.pos)
+ pt.resultType.substParams(pt, typedArgs map (_.tpe))
+ case _ =>
+ ctx.error(s"${err.exprStr(typedFn)} does not take type parameters", tree.pos)
+ ErrorType
+ }
+ tree.withType(ownType).derivedTypeApply(typedFn, typedArgs)
+ }
+
+ def typedUnApply(qual: untpd.Tree, args: List[untpd.Tree], tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
+
+ def unapplyArgs(unapplyResult: Type)(implicit ctx: Context): List[Type] = {
+ def recur(tp: Type): List[Type] = {
+ def nonOverloadedMember(name: Name) = {
+ val ref = tp member name
+ if (ref.isOverloaded) {
+ errorType(s"Overloaded reference to $ref is not allowed in extractor", tree.pos)
+ }
+ else
+ ref.info
+ }
+
+ def productSelectors: List[Type] = {
+ val sels = for (n <- Iterator.from(0)) yield nonOverloadedMember(("_" + n).toTermName)
+ sels.takeWhile(_.exists).toList
+ }
+ def seqSelector = defn.RepeatedParamType.appliedTo(tp.elemType :: Nil)
+
+ if (tp derivesFrom defn.ProductClass) productSelectors
+ else if (tp derivesFrom defn.SeqClass) seqSelector :: Nil
+ else if (tp.typeSymbol == defn.BooleanClass) Nil
+ else if (nonOverloadedMember(nme.isDefined).exists &&
+ nonOverloadedMember(nme.get).exists) recur(nonOverloadedMember(nme.get))
+ else {
+ ctx.error(s"${unapplyResult.show} is not a valid result type of an unapply method of an extractor", tree.pos)
+ Nil
+ }
+ }
+
+ recur(unapplyResult)
+ }
+
+ val fn = {
+ val dummyArg = untpd.TypedSplice(dummyTreeOfType(WildcardType))
+ val unappProto = FunProtoType(dummyArg :: Nil, pt, this)
+ tryEither {
+ implicit ctx => typedExpr(Trees.Select(qual, nme.unapply), unappProto)
+ } {
+ s => tryEither {
+ implicit ctx => typedExpr(Trees.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
+ } {
+ _ => errorTree(s.value, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method")
+ }
+ }
+ }
+ fn.tpe.widen match {
+ case mt: MethodType =>
+ val ownType = mt.resultType
+ ownType <:< pt // done for registering the constraints; error message would come later
+ var argTypes = unapplyArgs(ownType)
+ val bunchedArgs = argTypes match {
+ case argType :: Nil if argType.isRepeatedParam => Trees.SeqLiteral(args) :: Nil
+ case _ => args
+ }
+ if (argTypes.length != bunchedArgs.length) {
+ ctx.error(s"wrong number of argument patterns for ${err.patternConstrStr(fn)}", tree.pos)
+ argTypes = argTypes.take(args.length) ++
+ List.fill(argTypes.length - args.length)(WildcardType)
+ }
+ val typedArgs = (bunchedArgs, argTypes).zipped map (typed(_, _))
+ Trees.UnApply(fn, typedArgs).withPos(tree.pos).withType(ownType)
+ case et: ErrorType =>
+ tree.withType(ErrorType)
+ }
+ }
/** Is given method reference applicable to argument types `args`?
* @param resultType The expected result type of the application
*/
- def isApplicableToTrees(methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context) =
+ def isApplicableToTrees(methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context) =
new ApplicableToTrees(methRef, args, resultType)(ctx.fresh.withNewTyperState).success
/** Is given method reference applicable to arguments `args`?
@@ -576,8 +713,8 @@ trait Applications extends Compatibility{ self: Typer =>
best :: asGood(alts1)
}
- private val dummyTree = Literal(Constant(null))
- def dummyTreeOfType(tp: Type): tpd.Tree = dummyTree withType tp
+ private val dummyTree = Trees.Literal(Constant(null))
+ def dummyTreeOfType(tp: Type): Tree = dummyTree withType tp
/** Resolve overloaded alternative `alts`, given expected type `pt`. */
def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = {
@@ -595,7 +732,7 @@ trait Applications extends Compatibility{ self: Typer =>
/** The shape of given tree as a type; is more expensive than
* typeShape but can can handle named arguments.
*/
- def treeShape(tree: untpd.Tree): tpd.Tree = tree match {
+ def treeShape(tree: untpd.Tree): Tree = tree match {
case NamedArg(name, arg) =>
val argShape = treeShape(arg)
tree.withType(argShape.tpe).derivedNamedArg(name, argShape)
@@ -627,14 +764,14 @@ trait Applications extends Compatibility{ self: Typer =>
def narrowByShapes(alts: List[TermRef]): List[TermRef] =
if (args exists (_.isInstanceOf[untpd.Function]))
- if (args exists (_.isInstanceOf[NamedArg[_]]))
+ if (args exists (_.isInstanceOf[Trees.NamedArg[_]]))
narrowByTrees(alts, args map treeShape, resultType)
else
narrowByTypes(alts, args map typeShape, resultType)
else
alts
- def narrowByTrees(alts: List[TermRef], args: List[tpd.Tree], resultType: Type): List[TermRef] =
+ def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] =
alts filter (isApplicableToTrees(_, args, resultType))
val alts1 = narrowBySize(alts)
@@ -645,6 +782,12 @@ trait Applications extends Compatibility{ self: Typer =>
else narrowByTrees(alts2, pt.typedArgs, resultType)
}
+ case pt @ PolyProtoType(nargs, _) =>
+ alts filter ( alt => alt.widen match {
+ case PolyType(pnames) if pnames.length == nargs => true
+ case _ => false
+ })
+
case defn.FunctionType(args, resultType) =>
narrowByTypes(alts, args, resultType)