aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-07-08 11:05:55 +0200
committerMartin Odersky <odersky@gmail.com>2013-07-11 10:07:32 +0200
commitc9679f6c0f3c8200e1b1f537e89488094cfc2576 (patch)
tree59f142f2b241049737bfb71838235a4451d40cc1 /src/dotty/tools/dotc/typer
parent0af96c0f5179104fca02cf1aa144c6176bdb71eb (diff)
downloaddotty-c9679f6c0f3c8200e1b1f537e89488094cfc2576.tar.gz
dotty-c9679f6c0f3c8200e1b1f537e89488094cfc2576.tar.bz2
dotty-c9679f6c0f3c8200e1b1f537e89488094cfc2576.zip
Added functionality to deal with function applications.
- Added Applications class to represent applications - Added Constraint class to represent type constraints - Added TyperState class to represent typer state - Added Diagnostic class to buffer errors and warnings - Added Inferencing class that contains some common functionality for type inferencing (this one's still rudimentary). - Added extractor for FunctionType in Definitions - Added desugaring of default parameters to default getters in Desugar - Added flags to deal with default parameters - Added substitutions that replace bound parameters
Diffstat (limited to 'src/dotty/tools/dotc/typer')
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala645
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala82
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala1
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala88
4 files changed, 765 insertions, 51 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
new file mode 100644
index 000000000..9fdf5084c
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -0,0 +1,645 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast.{Trees, untpd, tpd, TreeInfo}
+import util.Positions._
+import Trees.Untyped
+import Contexts._
+import Types._
+import Flags._
+import Denotations._
+import NameOps._
+import Symbols._
+import Types._
+import Decorators._
+import Names._
+import StdNames._
+import Constants._
+import Inferencing._
+import collection.mutable
+import language.implicitConversions
+
+object Applications {
+
+ private val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]]
+ def hasNamedArg(args: List[Any]) = args exists isNamedArg
+}
+
+trait Applications { self: Typer =>
+
+ import Applications._
+ import Trees._
+
+ private def state(implicit ctx: Context) = ctx.typerState
+
+ def lift(defs: mutable.ListBuffer[tpd.Tree], expr: tpd.Tree, prefix: String = "")(implicit ctx: Context): tpd.Tree =
+ if (TreeInfo.isIdempotentExpr(expr)) expr
+ else {
+ val name = ctx.freshName(prefix).toTermName
+ val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe, coord = positionCoord(expr.pos))
+ defs += tpd.ValDef(sym, expr)
+ tpd.Ident(sym.symRef)
+ }
+
+ def liftArgs(defs: mutable.ListBuffer[tpd.Tree], methType: Type, args: List[tpd.Tree])(implicit ctx: Context) = {
+ val paramPrefixes = methType match {
+ case MethodType(paramNames, _) => paramNames map (_.toString)
+ case _ => args map (_ => "")
+ }
+ for ((arg, prefix) <- args zip paramPrefixes) yield lift(defs, arg, prefix)
+ }
+
+ def liftApp(defs: mutable.ListBuffer[tpd.Tree], tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+ case Apply(fn, args) =>
+ tree.derivedApply(liftApp(defs, fn), liftArgs(defs, fn.tpe, args))
+ case TypeApply(fn, targs) =>
+ tree.derivedTypeApply(liftApp(defs, fn), targs)
+ case Select(pre, name) =>
+ tree.derivedSelect(lift(defs, pre), name)
+ case Ident(name) =>
+ lift(defs, tree)
+ case Block(stats, expr) =>
+ liftApp(defs ++= stats, expr)
+ case _ =>
+ tree
+ }
+
+ def isCompatible(tp: Type, pt: Type): Boolean = ???
+
+ /**
+ * @param Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type
+ * @param methRef the reference to the method of the application
+ * @param funType the type of the function part of the application
+ * @param args the arguments of the application
+ * @param resultType the expected result type of the application
+ */
+ abstract class Application[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context) {
+
+ /** The type of typed arguments: either tpd.Tree or Type */
+ type TypedArg
+
+ /** Given an original argument and the type of the corresponding formal
+ * parameter, produce a typed argument.
+ */
+ protected def typedArg(arg: Arg, formal: Type): TypedArg
+
+ /** Turn a typed tree into an argument */
+ protected def treeToArg(arg: tpd.Tree): Arg
+
+ /** Check that argument corresponds to type `formal` and
+ * possibly add it to the list of adapted arguments
+ */
+ protected def addArg(arg: TypedArg, formal: Type): Unit
+
+ /** Is this an argument of the form `expr: _*` or a RepeatedParamType
+ * derived from such an argument?
+ */
+ protected def isVarArg(arg: Arg): Boolean
+
+ /** If constructing trees, turn last `n` processed arguments into a
+ * `SeqLiteral` tree with element type `elemFormal`.
+ */
+ protected def makeVarArg(n: Int, elemFormal: Type): Unit
+
+ /** Signal failure with given message at position of given argument */
+ protected def fail(msg: => String, arg: Arg): Unit
+
+ /** Signal failure with given message at position of the application itself */
+ protected def fail(msg: => String): Unit
+
+ /** If constructing trees, the current function part, which might be
+ * affected by lifting. EmptyTree otherwise.
+ */
+ protected def normalizedFun: tpd.Tree
+
+ /** If constructing trees, pull out all parts of the function
+ * which are not idempotent into separate prefix definitions
+ */
+ protected def liftFun(): Unit = ()
+
+ /** A flag signalling that the application was so far succesful */
+ protected var ok = true
+
+ /** The function's type after widening and instantiating polytypes
+ * with polyparams or typevars in constraint set
+ */
+ val methType = funType.widen match {
+ case funType: MethodType => funType
+ case funType: PolyType => polyResult(ctx.track(funType))
+ case _ => funType
+ }
+
+ /** The arguments re-ordered so that each named argument matches the
+ * same-named formal parameter.
+ */
+ val orderedArgs =
+ if (hasNamedArg(args))
+ reorder(args.asInstanceOf[List[untpd.Tree]]).asInstanceOf[List[Arg]]
+ else
+ args
+
+ methType match {
+ case methType: MethodType =>
+ // apply the result type constraint, unless method type is dependent
+ if (!methType.isDependent)
+ ok = ok && constrainResult(methType.resultType, resultType)
+ // match all arguments with corresponding formal parameters
+ matchArgs(orderedArgs, methType.paramTypes, 0)
+ case _ =>
+ if (methType.isError) ok = false
+ else fail(s"$methString does not take parameters")
+ }
+
+ /** The application was succesful */
+ def success = ok
+
+ private def state = ctx.typerState
+
+ protected def methodType = methType.asInstanceOf[MethodType]
+ private def methString: String = s"method ${methRef.name}: ${methType.show}"
+
+ /** The result type of a polytype; overridden in TypedApplication */
+ protected def polyResult(polytpe: PolyType): Type = polytpe.resultType
+
+ /** 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]] =
+ (for (NamedArg(name, arg1) <- args) yield (name, arg1)).toMap
+
+ def badNamedArg(arg: Tree[_ >: Untyped]): Unit = {
+ val NamedArg(name, _) = arg
+ def msg =
+ if (methodType.paramNames contains name)
+ s"parameter $name of $methString is already instantiated"
+ else
+ s"$methString does not have a parameter $name"
+ fail(msg, arg.asInstanceOf[Arg])
+ }
+
+ def recur(pnames: List[Name], args: List[Tree[T]]): List[Tree[T]] = pnames match {
+ case pname :: pnames1 =>
+ namedToArg get pname match {
+ case Some(arg) =>
+ namedToArg -= pname
+ arg :: recur(pnames1, args)
+ case None =>
+ args match {
+ case (arg @ NamedArg(aname, _)) :: args1 =>
+ if (namedToArg contains aname)
+ emptyTree[T]() :: recur(pnames1, args)
+ else {
+ badNamedArg(arg)
+ recur(pnames1, args1)
+ }
+ case arg :: args1 =>
+ arg :: recur(pnames1, args1)
+ case Nil =>
+ recur(pnames1, args)
+ }
+ }
+ case nil =>
+ if (hasNamedArg(args)) {
+ val (namedArgs, otherArgs) = args partition isNamedArg
+ namedArgs foreach badNamedArg
+ otherArgs
+ }
+ else args
+ }
+
+ recur(methodType.paramNames, args)
+ }
+
+ /** 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)
+ case _ => meth
+ }
+
+ /** Find reference to default parameter getter for parameter #n in current
+ * parameter list, or NoType if none was found
+ */
+ def findDefaultGetter(n: Int)(implicit ctx: Context): Type = {
+ def getterName = methRef.name.toTermName.defaultGetterName(n)
+ def ref(pre: Type, sym: Symbol): Type =
+ if (pre.exists && sym.isTerm) TermRef.withSym(pre, sym.asTerm) else NoType
+ val meth = methRef.symbol
+ if (meth.hasDefaultParams)
+ methRef.prefix match {
+ case NoPrefix =>
+ def findDefault(cx: Context): Type = {
+ if (cx eq NoContext) NoType
+ else if (cx.scope != cx.outer.scope &&
+ cx.lookup(methRef.name)
+ .filterWithPredicate(_.symbol == meth).exists) {
+ val denot = cx.lookup(getterName).toDenot(NoPrefix)
+ NamedType(NoPrefix, getterName).withDenot(denot)
+ } else findDefault(cx.outer)
+ }
+ findDefault(ctx)
+ case mpre =>
+ val cls = meth.owner
+ val pre =
+ if (meth.isClassConstructor) {
+ mpre.baseType(cls) match {
+ case TypeRef(clspre, _) => ref(clspre, cls.companionModule)
+ case _ => NoType
+ }
+ } else mpre
+ ref(pre, pre.member(getterName).symbol)
+ }
+ else NoType
+ }
+
+ /** Match re-ordered arguments against formal parameters
+ * @param n The position of the first parameter in formals in `methType`.
+ */
+ def matchArgs(args: List[Arg], formals: List[Type], n: Int): Unit = {
+ if (success) formals match {
+ case formal :: formals1 =>
+
+ def addTyped(arg: Arg, formal: Type) =
+ addArg(typedArg(arg, formal), formal)
+
+ def missingArg(n: Int): Unit = {
+ val pname = methodType.paramNames(n)
+ fail(
+ if (pname contains '$') s"not enough arguments for $methString"
+ else s"missing argument for parameter $pname of $methString")
+ }
+
+ def tryDefault(n: Int, args1: List[Arg]): Unit = {
+ findDefaultGetter(n + TreeInfo.numArgs(normalizedFun)) match {
+ case dref: NamedType =>
+ liftFun()
+ addTyped(treeToArg(spliceMeth(tpd.Ident(dref), normalizedFun)), formal)
+ matchArgs(args1, formals1, n + 1)
+ case _ =>
+ missingArg(n)
+ }
+ }
+
+ if (formal.isRepeatedParam)
+ args match {
+ case arg :: Nil if isVarArg(arg) =>
+ addTyped(arg, formal)
+ case _ =>
+ val elemFormal = formal.typeArgs.head
+ args foreach (addTyped(_, elemFormal))
+ makeVarArg(args.length, elemFormal)
+ }
+ else args match {
+ case EmptyTree :: args1 =>
+ tryDefault(n, args1)
+ case arg :: args1 =>
+ addTyped(arg, formal)
+ matchArgs(args1, formals1, n + 1)
+ case nil =>
+ tryDefault(n, args)
+ }
+
+ case nil =>
+ args match {
+ case arg :: args1 => fail(s"too many arguments for $methString", arg)
+ case nil =>
+ }
+ }
+ }
+
+ /** Take into account that the result type of the current method
+ * must fit the given expected result type.
+ */
+ def constrainResult(mt: Type, pt: Type): Boolean = pt match {
+ case FunProtoType(_, result) =>
+ mt match {
+ case mt: MethodType if !mt.isDependent =>
+ constrainResult(mt.resultType, pt.resultType)
+ case _ =>
+ true
+ }
+ case pt: ValueType =>
+ mt match {
+ case mt: ImplicitMethodType if !mt.isDependent =>
+ constrainResult(mt.resultType, pt)
+ case _ =>
+ isCompatible(mt, pt)
+ }
+ case _ =>
+ true
+ }
+ }
+
+ /** Subclass of Application for the cases where we are interested only
+ * in a "can/cannot apply" answer, without needing to construct trees or
+ * issue error messages.
+ */
+ abstract class TestApplication[Arg](methRef: TermRef, funType: Type, args: List[Arg], resultType: Type)(implicit ctx: Context)
+ extends Application[Arg](methRef, funType, args, resultType) {
+ type TypedArg = Arg
+ type Result = Unit
+
+ /** The type of the given argument */
+ protected def argType(arg: Arg): Type
+
+ def typedArg(arg: Arg, formal: Type): Arg = arg
+ def addArg(arg: TypedArg, formal: Type) =
+ ok = ok & isCompatible(argType(arg), formal)
+ def makeVarArg(n: Int, elemFormal: Type) = {}
+ def fail(msg: => String, arg: Arg) =
+ ok = false
+ def fail(msg: => String) =
+ ok = false
+ def normalizedFun = tpd.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)
+ }
+
+ /** Subclass of Application for applicability tests with trees as arguments. */
+ class ApplicableToTrees(methRef: TermRef, args: List[tpd.Tree], resultType: Type)(implicit ctx: Context)
+ extends TestApplication(methRef, methRef, args, resultType) with TreeApplication[Type] {
+ def argType(arg: tpd.Tree): Type = arg.tpe
+ def treeToArg(arg: tpd.Tree): tpd.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 isVarArg(arg: Type): Boolean = arg.isRepeatedParam
+ }
+
+ /** Subclass of Application for type checking an Apply node, where
+ * 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)
+ 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
+
+ def addArg(arg: tpd.Tree, formal: Type): Unit =
+ typedArgBuf += adapt(arg, Mode.Expr, formal)
+
+ def makeVarArg(n: Int, elemFormal: Type): Unit = {
+ val args = typedArgBuf.takeRight(n).toList
+ typedArgBuf.trimEnd(n)
+ typedArgBuf += SeqLiteral(TypeTree(elemFormal), args)
+ }
+
+ def fail(msg: => String, arg: Tree[T]) = {
+ ctx.error(msg, arg.pos)
+ ok = false
+ }
+
+ def fail(msg: => String) = {
+ ctx.error(msg, app.pos)
+ ok = false
+ }
+
+ def normalizedFun = myNormalizedFun
+
+ override def liftFun(): Unit =
+ if (liftedDefs == null) {
+ liftedDefs = new mutable.ListBuffer[tpd.Tree]
+ myNormalizedFun = liftApp(liftedDefs, myNormalizedFun)
+ }
+
+ /** Replace all parameters of tracked polytype by fresh type vars,
+ * and make function a TypeApply node with these type vars as arguments.
+ */
+ override def polyResult(poly: PolyType): Type = {
+ val tvars = ctx.newTypeVars(poly)
+ myNormalizedFun = tpd.TypeApply(normalizedFun, tvars map (tpd.TypeTree(_)))
+ poly.substParams(poly, tvars)
+ }
+
+ /** The index of the first difference between lists of trees `xs` and `ys`,
+ * 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 {
+ case x :: xs1 =>
+ ys match {
+ case EmptyTree :: ys1 => firstDiff(xs1, ys1, n)
+ case y :: ys1 => if (x ne y) n else firstDiff(xs1, ys1, n + 1)
+ case nil => n
+ }
+ case nil =>
+ ys match {
+ case EmptyTree :: ys1 => firstDiff(xs, ys1, n)
+ case y :: ys1 => n
+ 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
+ }
+ 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
+ }
+ }
+
+ /** 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)
+ extends TypedApply(app, fun, methRef, args, resultType) {
+ def typedArg(arg: untpd.Tree, formal: Type): TypedArg = typed(arg, Mode.Expr, formal)
+ def treeToArg(arg: tpd.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)
+ extends TypedApply(app, fun, methRef, args, resultType) {
+ def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg
+ def treeToArg(arg: tpd.Tree): tpd.Tree = arg
+ }
+
+ /** 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) =
+ new ApplicableToTrees(methRef, args, resultType)(ctx.fresh.withNewTyperState).success
+
+ /** Is given method reference applicable to arguments `args`?
+ * @param resultType The expected result type of the application
+ */
+ def isApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type = WildcardType)(implicit ctx: Context) =
+ new ApplicableToTypes(methRef, args, resultType)(ctx.fresh.withNewTyperState).success
+
+ /** Is `tp` a subtype of `pt`? */
+ def isSubType(tp: Type, pt: Type)(implicit ctx: Context) = (tp <:< pt)(ctx.fresh.withNewTyperState)
+
+ /** In a set of overloaded applicable alternatives, is `alt1` at least as good as
+ * `alt2`? `alt1` and `alt2` are nonoverloaded references.
+ */
+ def isAsGood(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Boolean = {
+
+ /** Is class or module class `sym1` derived from class or module class `sym2`? */
+ def isDerived(sym1: Symbol, sym2: Symbol): Boolean =
+ if (sym1 isSubClass sym2) true
+ else if (sym2 is Module) isDerived(sym1, sym2.companionClass)
+ else (sym1 is Module) && isDerived(sym1.companionClass, sym2)
+
+ /** Is alternative `alt1` with type `tp1` as specific as alternative
+ * `alt2` with type `tp2` ? This is the case if `tp2` can be applied to
+ * `tp1` or `tp2` is a supertype of `tp1`.
+ */
+ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = tp1 match {
+ case tp1: PolyType =>
+ def bounds(tparamRefs: List[TypeRef]) = tp1.paramBounds map (_.substParams(tp1, tparamRefs))
+ val tparams = ctx.newTypeParams(alt1.symbol.owner, tp1.paramNames, EmptyFlags, bounds)
+ isAsSpecific(alt1, tp1.instantiate(tparams map (_.symRef)), alt2, tp2)
+ case tp1: MethodType =>
+ isApplicableToTypes(alt2, tp1.paramTypes)
+ case _ =>
+ isSubType(tp1, tp2)
+ }
+
+ val owner1 = alt1.symbol.owner
+ val owner2 = alt2.symbol.owner
+ val tp1 = alt1.widen
+ val tp2 = alt2.widen
+
+ def winsOwner1 = isDerived(owner1, owner2)
+ def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
+ def winsOwner2 = isDerived(owner2, owner1)
+ def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
+
+ // Assume the following probabilities:
+ //
+ // P(winsOwnerX) = 2/3
+ // P(winsTypeX) = 1/3
+ //
+ // Then the call probabilities of the 4 basic operations are as follows:
+ //
+ // winsOwner1: 1/1
+ // winsOwner2: 1/1
+ // winsType1 : 7/9
+ // winsType2 : 4/9
+
+ if (winsOwner1) /* 6/9 */ !winsOwner2 || /* 4/9 */ winsType1 || /* 8/27 */ !winsType2
+ else if (winsOwner2) /* 2/9 */ winsType1 && /* 2/27 */ !winsType2
+ else /* 1/9 */ winsType1 || /* 2/27 */ !winsType2
+ }
+
+ /** Resolve overloaded alternative `alts`, given expected type `pt`. */
+ def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = {
+
+ def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty
+
+ /** The shape of given tree as a type; cannot handle named arguments. */
+ def typeShape(tree: untpd.Tree): Type = tree match {
+ case untpd.Function(args, body) =>
+ defn.FunctionType(args map Function.const(defn.AnyType), typeShape(body))
+ case _ =>
+ defn.NothingType
+ }
+
+ /** 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 {
+ case NamedArg(name, arg) =>
+ val argShape = treeShape(arg)
+ tree.withType(argShape.tpe).derivedNamedArg(name, argShape)
+ case _ =>
+ Literal(Constant(null)) withType typeShape(tree)
+ }
+
+ def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
+ alts filter (isApplicableToTypes(_, argTypes, resultType))
+
+ def narrowMostSpecific(alts: List[TermRef]): List[TermRef] = (alts: @unchecked) match {
+ case alt :: alts1 =>
+ def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match {
+ case alt :: alts1 =>
+ winner(if (isAsGood(alt, bestSoFar)) alt else bestSoFar, alts1)
+ case nil =>
+ bestSoFar
+ }
+ val best = winner(alt, alts1)
+ def asGood(alts: List[TermRef]): List[TermRef] = alts match {
+ case alt :: alts1 =>
+ if ((alt eq best) || !isAsGood(alt, best)) asGood(alts1)
+ else alt :: asGood(alts1)
+ case nil =>
+ Nil
+ }
+ best :: asGood(alts1)
+ }
+
+ val candidates = pt match {
+ case pt @ FunProtoType(args, resultType) =>
+ val numArgs = args.length
+
+ def sizeFits(alt: TermRef, tp: Type): Boolean = tp match {
+ case tp: PolyType => sizeFits(alt, tp.resultType)
+ case MethodType(_, ptypes) =>
+ val numParams = ptypes.length
+ def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam
+ def hasDefault = alt.symbol.hasDefaultParams
+ if (numParams == numArgs) true
+ else if (numParams < numArgs) isVarArgs
+ else if (numParams > numArgs + 1) hasDefault
+ else isVarArgs || hasDefault
+ }
+
+ def narrowBySize(alts: List[TermRef]): List[TermRef] =
+ alts filter (alt => sizeFits(alt, alt.widen))
+
+ def narrowByShapes(alts: List[TermRef]): List[TermRef] =
+ if (args exists (_.isInstanceOf[untpd.Function]))
+ if (args exists (_.isInstanceOf[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] =
+ alts filter (isApplicableToTrees(_, args, resultType))
+
+ val alts1 = narrowBySize(alts)
+ if (isDetermined(alts1)) alts1
+ else {
+ val alts2 = narrowByShapes(alts1)
+ if (isDetermined(alts2)) alts2
+ else narrowByTrees(alts2, pt.typedArgs, resultType)
+ }
+
+ case defn.FunctionType(args, resultType) =>
+ narrowByTypes(alts, args, resultType)
+
+ case tp =>
+ alts filter (isSubType(_, tp))
+ }
+
+ if (isDetermined(candidates)) candidates
+ else narrowMostSpecific(candidates)
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
new file mode 100644
index 000000000..9d244ea3e
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -0,0 +1,82 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import Contexts._, Types._, Flags._, Denotations._, NameOps._, Symbols._
+import annotation.unchecked
+
+object Inferencing {
+
+ implicit class Infer(val ictx: Context) extends AnyVal {
+
+ implicit private def ctx = ictx
+ private def state = ctx.typerState
+
+ /** Add all parameters in given polytype `pt` to the constraint's domain.
+ * If the constraint contains already some of these parameters in its domain,
+ * make a copy of the polytype and add the copy's type parameters instead.
+ * Return either the original polytype, or the copy, if one was made.
+ */
+ def track(pt: PolyType): PolyType = {
+ val tracked =
+ if (state.constraint contains pt) pt.copy(pt.paramNames, pt.paramBounds, pt.resultType)
+ else pt
+ state.constraint = state.constraint + tracked
+ 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`.
+ */
+ 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)
+ }
+ 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
+ }
+
+ def isSubTypes(actuals: List[Type], formals: List[Type])(implicit ctx: Context): Boolean = formals match {
+ case formal :: formals1 =>
+ actuals match {
+ case actual :: actuals1 => actual <:< formal && isSubTypes(actuals1, formals1)
+ case _ => false
+ }
+ case nil =>
+ actuals.isEmpty
+ }
+/* not needed right now
+ def formalParameters[T](mtp: MethodType, actuals: List[T])(isRepeated: T => Boolean)(implicit ctx: Context) =
+ if (mtp.isVarArgs && !(actuals.nonEmpty && isRepeated(actuals.last))) {
+ val leading = mtp.paramTypes.init
+ val repeated = mtp.paramTypes.last.typeArgs.head
+ val trailing = List.fill(actuals.length - leading.length)(repeated)
+ leading ++ trailing
+ }
+ else mtp.paramTypes
+ */
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 16d643552..43589aac5 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -237,6 +237,7 @@ class Namer { typer: Typer =>
lazy val schema = paramFn(WildcardType)
val site = sym.owner.thisType
val inherited = {
+ // TODO: Look only at member of supertype instead?
((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) =>
val itpe = cls.info
.nonPrivateDecl(sym.name)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 98f0d4d6e..853575277 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -4,8 +4,20 @@ package typer
import core._
import ast._
-import Trees._, Constants._, StdNames._, Scopes._, Denotations._
-import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
+import Trees._
+import Constants._
+import StdNames._
+import Scopes._
+import Denotations._
+import Inferencing.Infer
+import Contexts._
+import Symbols._
+import Types._
+import SymDenotations._
+import Names._
+import NameOps._
+import Flags._
+import Decorators._
import util.Positions._
import util.SourcePosition
import collection.mutable
@@ -33,7 +45,7 @@ object Typer {
}
}
-class Typer extends Namer {
+class Typer extends Namer with Applications {
import tpd._
import Typer._
@@ -183,13 +195,10 @@ class Typer extends Namer {
if (ctx eq NoContext) previous
else {
val outer = ctx.outer
- val curScope = ctx.scope
- val curOwner = ctx.owner
- if (curScope ne outer.scope) {
- val defDenots =
- if (curOwner.isClass && (curOwner ne outer.owner)) curOwner.asClass.membersNamed(name)
- else curScope.denotsNamed(name)
+ if (ctx.scope ne outer.scope) {
+ val defDenots = ctx.lookup(name)
if (defDenots.exists) {
+ val curOwner = ctx.owner
val pre = curOwner.thisType
val found = NamedType(pre, name).withDenot(defDenots toDenot pre)
if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenots))
@@ -234,55 +243,32 @@ class Typer extends Namer {
tree.withType(ownType).derivedSelect(qual1, tree.name)
}
- class FunProtoType(args: List[untpd.Tree], resultType: Type)(implicit ctx: Context) extends UncachedGroundType {
- private var _typedArgs: List[tpd.Tree] = null
- private var _argTypes: List[Type] = null
- def typedArgs = {
- if (_typedArgs == null)
- _typedArgs = args mapconserve (typed(_, Mode.Expr, WildcardType))
- _typedArgs
- }
- def argTypes = {
- if (_argTypes == null)
- _argTypes = typedArgs map (_.exprType)
- _argTypes
- }
- def adaptedArgs(formals: List[Type]) = {
- var fs = formals
- if (_typedArgs == null)
- _typedArgs = args mapconserve { arg =>
- val arg1 = typed(arg, Mode.Expr, fs.head)
- fs = fs.tail
- arg1
- }
- else {
- val adapted = typedArgs mapconserve { arg =>
- val arg1 = adapt(arg, Mode.Expr, fs.head)
- fs = fs.tail
- arg1
- }
- if (adapted ne typedArgs) {
- _typedArgs = adapted
- _argTypes = null
- }
- }
- typedArgs
- }
- def isApplicable(denot: SingleDenotation) = {
+ case class FunProtoType(args: List[untpd.Tree], override val resultType: Type)(implicit ctx: Context) extends UncachedGroundType {
+ private var myTypedArgs: List[tpd.Tree] = null
+
+ def argsAreTyped: Boolean = myTypedArgs != null
+ def typedArgs: List[tpd.Tree] = {
+ if (myTypedArgs == null)
+ myTypedArgs = args mapconserve (typed(_, pt = WildcardType))
+ myTypedArgs
}
}
def typedApply(tree: untpd.Apply, mode: Mode, pt: Type)(implicit ctx: Context): Tree = {
val proto = new FunProtoType(tree.args, pt)
val fun1 = typed(tree.fun, Mode.Expr, proto)
- fun1.exprType match {
- case mt @ MethodType(_, formals) =>
- val args1 = proto.adaptedArgs(formals)
- val restpe = mt.instantiate(proto.argTypes)
- tree.withType(restpe).derivedApply(fun1, args1)
- case ErrorType =>
- tree.withType(ErrorType)
+ 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)
+ app.result
+ case _ =>
+ fun1.exprType match {
+ case ErrorType =>
+ tree.withType(ErrorType)
+ }
}
}