aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/Applications.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/typer/Applications.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/Applications.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala1351
1 files changed, 1351 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
new file mode 100644
index 000000000..6c398cd72
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -0,0 +1,1351 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast.{Trees, untpd, tpd, TreeInfo}
+import util.Positions._
+import util.Stats.track
+import Trees.Untyped
+import Mode.ImplicitsEnabled
+import Contexts._
+import Flags._
+import Denotations._
+import NameOps._
+import Symbols._
+import Types._
+import Decorators._
+import ErrorReporting._
+import Trees._
+import config.Config
+import Names._
+import StdNames._
+import ProtoTypes._
+import EtaExpansion._
+import Inferencing._
+import collection.mutable
+import config.Printers.{typr, unapp, overload}
+import TypeApplications._
+import language.implicitConversions
+import reporting.diagnostic.Message
+
+object Applications {
+ import tpd._
+
+ def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx: Context) = {
+ val ref = tp.member(name).suchThat(_.info.isParameterless)
+ if (ref.isOverloaded)
+ errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos)
+ else if (ref.info.isInstanceOf[PolyType])
+ errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", errorPos)
+ else
+ ref.info.widenExpr.dealias
+ }
+
+ def productSelectorTypes(tp: Type, errorPos: Position = NoPosition)(implicit ctx: Context): List[Type] = {
+ val sels = for (n <- Iterator.from(0)) yield extractorMemberType(tp, nme.selectorName(n), errorPos)
+ sels.takeWhile(_.exists).toList
+ }
+
+ def productSelectors(tp: Type)(implicit ctx: Context): List[Symbol] = {
+ val sels = for (n <- Iterator.from(0)) yield tp.member(nme.selectorName(n)).symbol
+ sels.takeWhile(_.exists).toList
+ }
+
+ def getUnapplySelectors(tp: Type, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] =
+ if (args.length > 1 && !(tp.derivesFrom(defn.SeqClass))) {
+ val sels = productSelectorTypes(tp, pos)
+ if (sels.length == args.length) sels
+ else tp :: Nil
+ } else tp :: Nil
+
+ def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: Position = NoPosition)(implicit ctx: Context): List[Type] = {
+
+ def seqSelector = defn.RepeatedParamType.appliedTo(unapplyResult.elemType :: Nil)
+ def getTp = extractorMemberType(unapplyResult, nme.get, pos)
+
+ // println(s"unapply $unapplyResult ${extractorMemberType(unapplyResult, nme.isDefined)}")
+ if (extractorMemberType(unapplyResult, nme.isDefined, pos) isRef defn.BooleanClass) {
+ if (getTp.exists)
+ if (unapplyFn.symbol.name == nme.unapplySeq) {
+ val seqArg = boundsToHi(getTp.elemType)
+ if (seqArg.exists) return args map Function.const(seqArg)
+ }
+ else return getUnapplySelectors(getTp, args, pos)
+ else if (defn.isProductSubType(unapplyResult)) return productSelectorTypes(unapplyResult, pos)
+ }
+ if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil
+ else if (unapplyResult isRef defn.BooleanClass) Nil
+ else {
+ ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", pos)
+ Nil
+ }
+ }
+
+ def wrapDefs(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree =
+ if (defs != null && defs.nonEmpty) tpd.Block(defs.toList, tree) else tree
+}
+
+import Applications._
+
+trait Applications extends Compatibility { self: Typer with Dynamic =>
+
+ import Applications._
+ import tpd.{ cpy => _, _ }
+ import untpd.cpy
+ import Dynamic.isDynamicMethod
+
+ /** @tparam 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: 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
+
+ /** If all `args` have primitive numeric types, make sure it's the same one */
+ protected def harmonizeArgs(args: List[TypedArg]): List[TypedArg]
+
+ /** Signal failure with given message at position of given argument */
+ protected def fail(msg: => Message, arg: Arg): Unit
+
+ /** Signal failure with given message at position of the application itself */
+ protected def fail(msg: => Message): Unit
+
+ protected def appPos: Position
+
+ /** The current function part, which might be affected by lifting.
+ */
+ protected def normalizedFun: 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 typechecking the application was so far successful */
+ private[this] var _ok = true
+
+ def ok = _ok
+ def ok_=(x: Boolean) = {
+ assert(x || ctx.reporter.errorsReported || !ctx.typerState.isCommittable) // !!! DEBUG
+ _ok = x
+ }
+
+ /** The function's type after widening and instantiating polytypes
+ * with polyparams in constraint set
+ */
+ val methType = funType.widen match {
+ case funType: MethodType => funType
+ case funType: PolyType => constrained(funType).resultType
+ case tp => tp //was: funType
+ }
+
+ /** The arguments re-ordered so that each named argument matches the
+ * same-named formal parameter.
+ */
+ lazy val orderedArgs =
+ if (hasNamedArg(args))
+ reorder(args.asInstanceOf[List[untpd.Tree]]).asInstanceOf[List[Arg]]
+ else
+ args
+
+ protected def init() = methType match {
+ case methType: MethodType =>
+ // apply the result type constraint, unless method type is dependent
+ if (!methType.isDependent) {
+ val savedConstraint = ctx.typerState.constraint
+ if (!constrainResult(methType.resultType, resultType))
+ if (ctx.typerState.isCommittable)
+ // defer the problem until after the application;
+ // it might be healed by an implicit conversion
+ assert(ctx.typerState.constraint eq savedConstraint)
+ else
+ fail(err.typeMismatchMsg(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 successful */
+ def success = ok
+
+ protected def methodType = methType.asInstanceOf[MethodType]
+ private def methString: String = i"${methRef.symbol}: ${methType.show}"
+
+ /** Re-order arguments to correctly align named arguments */
+ def reorder[T >: Untyped](args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = {
+
+ /** @param pnames The list of parameter names that are missing arguments
+ * @param args The list of arguments that are not yet passed, or that are waiting to be dropped
+ * @param nameToArg A map from as yet unseen names to named arguments
+ * @param toDrop A set of names that have already be passed as named arguments
+ *
+ * For a well-typed application we have the invariants
+ *
+ * 1. `(args diff toDrop)` can be reordered to match `pnames`
+ * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
+ */
+ def recur(pnames: List[Name], args: List[Trees.Tree[T]],
+ nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name]): List[Trees.Tree[T]] = pnames match {
+ case pname :: pnames1 if nameToArg contains pname =>
+ // there is a named argument for this parameter; pick it
+ nameToArg(pname) :: recur(pnames1, args, nameToArg - pname, toDrop + pname)
+ case _ =>
+ def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
+ args match {
+ case (arg @ NamedArg(aname, _)) :: args1 =>
+ if (toDrop contains aname) // argument is already passed
+ recur(pnames, args1, nameToArg, toDrop - aname)
+ else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree
+ genericEmptyTree :: recur(pnames.tail, args, nameToArg, toDrop)
+ else { // name not (or no longer) available for named arg
+ def msg =
+ if (methodType.paramNames contains aname)
+ s"parameter $aname of $methString is already instantiated"
+ else
+ s"$methString does not have a parameter $aname"
+ fail(msg, arg.asInstanceOf[Arg])
+ arg :: recur(pnamesRest, args1, nameToArg, toDrop)
+ }
+ case arg :: args1 =>
+ arg :: recur(pnamesRest, args1, nameToArg, toDrop) // unnamed argument; pick it
+ case Nil => // no more args, continue to pick up any preceding named args
+ if (pnames.isEmpty) Nil
+ else recur(pnamesRest, args, nameToArg, toDrop)
+ }
+ }
+ val nameAssocs = for (arg @ NamedArg(name, _) <- args) yield (name, arg)
+ recur(methodType.paramNames, args, nameAssocs.toMap, Set())
+ }
+
+ /** Splice new method reference into existing application */
+ 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
+ }
+
+ /** 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): Tree = {
+ val meth = methRef.symbol.asTerm
+ val receiver: Tree = methPart(normalizedFun) match {
+ case Select(receiver, _) => receiver
+ case mr => mr.tpe.normalizedPrefix match {
+ case mr: TermRef => ref(mr)
+ case mr =>
+ if (this.isInstanceOf[TestApplication[_]])
+ // In this case it is safe to skolemize now; we will produce a stable prefix for the actual call.
+ ref(mr.narrow)
+ else
+ EmptyTree
+ }
+ }
+ val getterPrefix =
+ if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else meth.name
+ def getterName = getterPrefix.defaultGetterName(n)
+ if (!meth.hasDefaultParams)
+ EmptyTree
+ else if (receiver.isEmpty) {
+ def findGetter(cx: Context): Tree = {
+ if (cx eq NoContext) EmptyTree
+ else if (cx.scope != cx.outer.scope &&
+ cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) {
+ val denot = cx.denotNamed(getterName)
+ assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)")
+ ref(TermRef(cx.owner.thisType, getterName, denot))
+ } else findGetter(cx.outer)
+ }
+ findGetter(ctx)
+ }
+ else {
+ def selectGetter(qual: Tree): Tree = {
+ val getterDenot = qual.tpe.member(getterName)
+ if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot))
+ else EmptyTree
+ }
+ if (!meth.isClassConstructor)
+ selectGetter(receiver)
+ else {
+ // default getters for class constructors are found in the companion object
+ val cls = meth.owner
+ val companion = cls.companionModule
+ receiver.tpe.baseTypeRef(cls) match {
+ case tp: TypeRef if companion.isTerm =>
+ selectGetter(ref(TermRef(tp.prefix, companion.asTerm)))
+ case _ =>
+ EmptyTree
+ }
+ }
+ }
+ }
+
+ /** 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 = {
+ liftFun()
+ val getter = findDefaultGetter(n + numArgs(normalizedFun))
+ if (getter.isEmpty) missingArg(n)
+ else {
+ addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal)
+ matchArgs(args1, formals1, n + 1)
+ }
+ }
+
+ if (formal.isRepeatedParam)
+ args match {
+ case arg :: Nil if isVarArg(arg) =>
+ addTyped(arg, formal)
+ case _ =>
+ val elemFormal = formal.widenExpr.argTypesLo.head
+ val origConstraint = ctx.typerState.constraint
+ var typedArgs = args.map(typedArg(_, elemFormal))
+ val harmonizedArgs = harmonizeArgs(typedArgs)
+ if (harmonizedArgs ne typedArgs) {
+ ctx.typerState.constraint = origConstraint
+ typedArgs = harmonizedArgs
+ }
+ typedArgs.foreach(addArg(_, 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 =>
+ }
+ }
+ }
+ }
+
+ /** 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, formal: Type): Type
+
+ def typedArg(arg: Arg, formal: Type): Arg = arg
+ def addArg(arg: TypedArg, formal: Type) =
+ ok = ok & isCompatible(argType(arg, formal), formal)
+ def makeVarArg(n: Int, elemFormal: Type) = {}
+ def fail(msg: => Message, arg: Arg) =
+ ok = false
+ def fail(msg: => Message) =
+ ok = false
+ def appPos = NoPosition
+ lazy val normalizedFun = ref(methRef)
+ init()
+ }
+
+ /** Subclass of Application for applicability tests with type arguments and value
+ * argument trees.
+ */
+ class ApplicableToTrees(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context)
+ extends TestApplication(methRef, methRef.widen.appliedTo(targs), args, resultType) {
+ def argType(arg: Tree, formal: Type): Type = normalize(arg.tpe, formal)
+ def treeToArg(arg: Tree): Tree = arg
+ def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg)
+ def harmonizeArgs(args: List[Tree]) = harmonize(args)
+ }
+
+ /** Subclass of Application for applicability tests with type arguments and value
+ * argument trees.
+ */
+ class ApplicableToTreesDirectly(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context) extends ApplicableToTrees(methRef, targs, args, resultType)(ctx) {
+ override def addArg(arg: TypedArg, formal: Type) =
+ ok = ok & (argType(arg, formal) <:< formal)
+ }
+
+ /** Subclass of Application for applicability tests with value argument types. */
+ class ApplicableToTypes(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context)
+ extends TestApplication(methRef, methRef, args, resultType) {
+ def argType(arg: Type, formal: Type): Type = arg
+ def treeToArg(arg: Tree): Type = arg.tpe
+ def isVarArg(arg: Type): Boolean = arg.isRepeatedParam
+ def harmonizeArgs(args: List[Type]) = harmonizeTypes(args)
+ }
+
+ /** 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: Tree, methRef: TermRef, args: List[Trees.Tree[T]], resultType: Type)(implicit ctx: Context)
+ extends Application(methRef, fun.tpe, args, resultType) {
+ type TypedArg = Tree
+ def isVarArg(arg: Trees.Tree[T]): Boolean = untpd.isWildcardStarArg(arg)
+ private var typedArgBuf = new mutable.ListBuffer[Tree]
+ private var liftedDefs: mutable.ListBuffer[Tree] = null
+ private var myNormalizedFun: Tree = fun
+ init()
+
+ def addArg(arg: Tree, formal: Type): Unit =
+ typedArgBuf += adaptInterpolated(arg, formal.widenExpr, EmptyTree)
+
+ def makeVarArg(n: Int, elemFormal: Type): Unit = {
+ val args = typedArgBuf.takeRight(n).toList
+ typedArgBuf.trimEnd(n)
+ val elemtpt = TypeTree(elemFormal)
+ val seqLit =
+ if (methodType.isJava) JavaSeqLiteral(args, elemtpt)
+ else SeqLiteral(args, elemtpt)
+ typedArgBuf += seqToRepeated(seqLit)
+ }
+
+ def harmonizeArgs(args: List[TypedArg]) = harmonize(args)
+
+ override def appPos = app.pos
+
+ def fail(msg: => Message, arg: Trees.Tree[T]) = {
+ ctx.error(msg, arg.pos)
+ ok = false
+ }
+
+ def fail(msg: => Message) = {
+ ctx.error(msg, app.pos)
+ ok = false
+ }
+
+ def normalizedFun = myNormalizedFun
+
+ override def liftFun(): Unit =
+ if (liftedDefs == null) {
+ liftedDefs = new mutable.ListBuffer[Tree]
+ myNormalizedFun = liftApp(liftedDefs, myNormalizedFun)
+ }
+
+ /** 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 <: 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)
+ 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
+ }
+ }
+ private def sameSeq[T <: Trees.Tree[_]](xs: List[T], ys: List[T]): Boolean = firstDiff(xs, ys) < 0
+
+ val result = {
+ var typedArgs = typedArgBuf.toList
+ def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later
+ val app1 =
+ if (!success) app0.withType(ErrorType)
+ else {
+ if (!sameSeq(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]]
+ assignType(app0, normalizedFun, typedArgs)
+ }
+ wrapDefs(liftedDefs, app1)
+ }
+ }
+
+ /** Subclass of Application for type checking an Apply node with untyped arguments. */
+ class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, proto: FunProto, resultType: Type)(implicit ctx: Context)
+ extends TypedApply(app, fun, methRef, proto.args, resultType) {
+ def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal.widenExpr)
+ 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: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context)
+ extends TypedApply[Type](app, fun, methRef, args, resultType) {
+ // Dotty deviation: Dotc infers Untyped for the supercall. This seems to be according to the rules
+ // (of both Scala and Dotty). Untyped is legal, and a subtype of Typed, whereas TypeApply
+ // is invariant in the type parameter, so the minimal type should be inferred. But then typedArg does
+ // not match the abstract method in Application and an abstract class error results.
+ def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg
+ def treeToArg(arg: Tree): Tree = arg
+ }
+
+ /** If `app` is a `this(...)` constructor call, the this-call argument context,
+ * otherwise the current context.
+ */
+ def argCtx(app: untpd.Tree)(implicit ctx: Context): Context =
+ if (untpd.isSelfConstrCall(app)) ctx.thisCallArgContext else ctx
+
+ def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
+
+ def realApply(implicit ctx: Context): Tree = track("realApply") {
+ val originalProto = new FunProto(tree.args, IgnoredProto(pt), this)(argCtx(tree))
+ val fun1 = typedExpr(tree.fun, originalProto)
+
+ // Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
+ // a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application,
+ // until, possibly, we have to fall back to insert an implicit on the qualifier.
+ // This crucially relies on he fact that `proto` is used only in a single call of `adapt`,
+ // otherwise we would get possible cross-talk between different `adapt` calls using the same
+ // prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with
+ // a modified tree but this would be more convoluted and less efficient.
+ val proto = if (originalProto.isTupled) originalProto.tupled else originalProto
+
+ // If some of the application's arguments are function literals without explicitly declared
+ // parameter types, relate the normalized result type of the application with the
+ // expected type through `constrainResult`. This can add more constraints which
+ // help sharpen the inferred parameter types for the argument function literal(s).
+ // This tweak is needed to make i1378 compile.
+ if (tree.args.exists(untpd.isFunctionWithUnknownParamType(_)))
+ if (!constrainResult(fun1.tpe.widen, proto.derivedFunProto(resultType = pt)))
+ typr.println(i"result failure for $tree with type ${fun1.tpe.widen}, expected = $pt")
+
+ /** Type application where arguments come from prototype, and no implicits are inserted */
+ def simpleApply(fun1: Tree, proto: FunProto)(implicit ctx: Context): Tree =
+ methPart(fun1).tpe match {
+ case funRef: TermRef =>
+ val app =
+ if (proto.allArgTypesAreCurrent())
+ new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
+ else
+ new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree))
+ convertNewGenericArray(ConstFold(app.result))
+ case _ =>
+ handleUnexpectedFunType(tree, fun1)
+ }
+
+ /** Try same application with an implicit inserted around the qualifier of the function
+ * part. Return an optional value to indicate success.
+ */
+ def tryWithImplicitOnQualifier(fun1: Tree, proto: FunProto)(implicit ctx: Context): Option[Tree] =
+ tryInsertImplicitOnQualifier(fun1, proto) flatMap { fun2 =>
+ tryEither {
+ implicit ctx => Some(simpleApply(fun2, proto)): Option[Tree]
+ } {
+ (_, _) => None
+ }
+ }
+
+ fun1.tpe match {
+ case ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(ErrorType)
+ case TryDynamicCallType => typedDynamicApply(tree, pt)
+ case _ =>
+ tryEither {
+ implicit ctx => simpleApply(fun1, proto)
+ } {
+ (failedVal, failedState) =>
+ def fail = { failedState.commit(); failedVal }
+ // Try once with original prototype and once (if different) with tupled one.
+ // The reason we need to try both is that the decision whether to use tupled
+ // or not was already taken but might have to be revised when an implicit
+ // is inserted on the qualifier.
+ tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
+ if (proto eq originalProto) fail
+ else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
+ }
+ }
+ }
+
+ /** Convert expression like
+ *
+ * e += (args)
+ *
+ * where the lifted-for-assignment version of e is { val xs = es; e' } to
+ *
+ * { val xs = es; e' = e' + args }
+ */
+ def typedOpAssign: Tree = track("typedOpAssign") {
+ val Apply(Select(lhs, name), rhss) = tree
+ val lhs1 = typedExpr(lhs)
+ val liftedDefs = new mutable.ListBuffer[Tree]
+ val lhs2 = untpd.TypedSplice(liftAssigned(liftedDefs, lhs1))
+ val assign = untpd.Assign(lhs2, untpd.Apply(untpd.Select(lhs2, name.init), rhss))
+ wrapDefs(liftedDefs, typed(assign))
+ }
+
+ if (untpd.isOpAssign(tree))
+ tryEither {
+ implicit ctx => realApply
+ } { (failedVal, failedState) =>
+ tryEither {
+ implicit ctx => typedOpAssign
+ } { (_, _) =>
+ failedState.commit()
+ failedVal
+ }
+ }
+ else {
+ val app = realApply
+ app match {
+ case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType =>
+ val op = fn.symbol
+ if (op == defn.Any_== || op == defn.Any_!=)
+ checkCanEqual(left.tpe.widen, right.tpe.widen, app.pos)
+ case _ =>
+ }
+ app
+ }
+ }
+
+ /** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
+ protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
+ throw new Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
+
+ def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) =
+ for (arg @ NamedArg(id, argtpt) <- args) yield {
+ val argtpt1 = typedType(argtpt)
+ cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe)
+ }
+
+ def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") {
+ val isNamed = hasNamedArg(tree.args)
+ val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_))
+ val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt))
+ typedFn.tpe.widen match {
+ case pt: PolyType =>
+ if (typedArgs.length <= pt.paramBounds.length && !isNamed)
+ if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) {
+ val arg = typedArgs.head
+ checkClassType(arg.tpe, arg.pos, traitReq = false, stablePrefixReq = false)
+ }
+ case _ =>
+ }
+ def tryDynamicTypeApply(): Tree = typedFn match {
+ case typedFn: Select if !pt.isInstanceOf[FunProto] => typedDynamicSelect(typedFn, typedArgs, pt)
+ case _ => tree.withType(TryDynamicCallType)
+ }
+ if (typedFn.tpe eq TryDynamicCallType) tryDynamicTypeApply()
+ else assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
+ }
+
+ /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray.
+ * It is performed during typer as creation of generic arrays needs a classTag.
+ * we rely on implicit search to find one.
+ */
+ def convertNewGenericArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+ case Apply(TypeApply(tycon, targs@(targ :: Nil)), args) if tycon.symbol == defn.ArrayConstructor =>
+ fullyDefinedType(tree.tpe, "array", tree.pos)
+
+ def newGenericArrayCall =
+ ref(defn.DottyArraysModule)
+ .select(defn.newGenericArrayMethod).withPos(tree.pos)
+ .appliedToTypeTrees(targs).appliedToArgs(args)
+
+ if (TypeErasure.isUnboundedGeneric(targ.tpe))
+ newGenericArrayCall
+ else tree
+ case _ =>
+ tree
+ }
+
+ def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") {
+ val Apply(qual, args) = tree
+
+ def notAnExtractor(tree: Tree) =
+ errorTree(tree, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method")
+
+ /** If this is a term ref tree, try to typecheck with its type name.
+ * If this refers to a type alias, follow the alias, and if
+ * one finds a class, reference the class companion module.
+ */
+ def followTypeAlias(tree: untpd.Tree): untpd.Tree = {
+ tree match {
+ case tree: untpd.RefTree =>
+ val ttree = typedType(untpd.rename(tree, tree.name.toTypeName))
+ ttree.tpe match {
+ case alias: TypeRef if alias.info.isAlias =>
+ companionRef(alias) match {
+ case companion: TermRef => return untpd.ref(companion) withPos tree.pos
+ case _ =>
+ }
+ case _ =>
+ }
+ case _ =>
+ }
+ untpd.EmptyTree
+ }
+
+ /** A typed qual.unapply or qual.unapplySeq tree, if this typechecks.
+ * Otherwise fallBack with (maltyped) qual.unapply as argument
+ * Note: requires special handling for overloaded occurrences of
+ * unapply or unapplySeq. We first try to find a non-overloaded
+ * method which matches any type. If that fails, we try to find an
+ * overloaded variant which matches one of the argument types.
+ * In fact, overloaded unapply's are problematic because a non-
+ * overloaded unapply does *not* need to be applicable to its argument
+ * whereas overloaded variants need to have a conforming variant.
+ */
+ def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = {
+ val genericProto = new UnapplyFunProto(WildcardType, this)
+ def specificProto = new UnapplyFunProto(selType, this)
+ // try first for non-overloaded, then for overloaded ocurrences
+ def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree =
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, name), specificProto)
+ } {
+ (sel, _) =>
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, name), genericProto)
+ } {
+ (_, _) => fallBack(sel)
+ }
+ }
+ // try first for unapply, then for unapplySeq
+ tryWithName(nme.unapply) {
+ sel => tryWithName(nme.unapplySeq)(_ => fallBack(sel)) // for backwards compatibility; will be dropped
+ }
+ }
+
+ /** Produce a typed qual.unapply or qual.unapplySeq tree, or
+ * else if this fails follow a type alias and try again.
+ */
+ val unapplyFn = trySelectUnapply(qual) { sel =>
+ val qual1 = followTypeAlias(qual)
+ if (qual1.isEmpty) notAnExtractor(sel)
+ else trySelectUnapply(qual1)(_ => notAnExtractor(sel))
+ }
+
+ def fromScala2x = unapplyFn.symbol.exists && (unapplyFn.symbol.owner is Scala2x)
+
+ /** Is `subtp` a subtype of `tp` or of some generalization of `tp`?
+ * The generalizations of a type T are the smallest set G such that
+ *
+ * - T is in G
+ * - If a typeref R in G represents a class or trait, R's superclass is in G.
+ * - If a type proxy P is not a reference to a class, P's supertype is in G
+ */
+ def isSubTypeOfParent(subtp: Type, tp: Type)(implicit ctx: Context): Boolean =
+ if (subtp <:< tp) true
+ else tp match {
+ case tp: TypeRef if tp.symbol.isClass => isSubTypeOfParent(subtp, tp.firstParent)
+ case tp: TypeProxy => isSubTypeOfParent(subtp, tp.superType)
+ case _ => false
+ }
+
+ unapplyFn.tpe.widen match {
+ case mt: MethodType if mt.paramTypes.length == 1 =>
+ val unapplyArgType = mt.paramTypes.head
+ unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $selType")
+ val ownType =
+ if (selType <:< unapplyArgType) {
+ unapp.println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}")
+ selType
+ } else if (isSubTypeOfParent(unapplyArgType, selType)(ctx.addMode(Mode.GADTflexible))) {
+ maximizeType(unapplyArgType) match {
+ case Some(tvar) =>
+ def msg =
+ ex"""There is no best instantiation of pattern type $unapplyArgType
+ |that makes it a subtype of selector type $selType.
+ |Non-variant type variable ${tvar.origin} cannot be uniquely instantiated."""
+ if (fromScala2x) {
+ // We can't issue an error here, because in Scala 2, ::[B] is invariant
+ // whereas List[+T] is covariant. According to the strict rule, a pattern
+ // match of a List[C] against a case x :: xs is illegal, because
+ // B cannot be uniquely instantiated. Of course :: should have been
+ // covariant in the first place, but in the Scala libraries it isn't.
+ // So for now we allow these kinds of patterns, even though they
+ // can open unsoundness holes. See SI-7952 for an example of the hole this opens.
+ if (ctx.settings.verbose.value) ctx.warning(msg, tree.pos)
+ } else {
+ unapp.println(s" ${unapplyFn.symbol.owner} ${unapplyFn.symbol.owner is Scala2x}")
+ ctx.strictWarning(msg, tree.pos)
+ }
+ case _ =>
+ }
+ unapp.println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}")
+ unapplyArgType
+ } else {
+ unapp.println("Neither sub nor super")
+ unapp.println(TypeComparer.explained(implicit ctx => unapplyArgType <:< selType))
+ errorType(
+ ex"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $selType",
+ tree.pos)
+ }
+
+ val dummyArg = dummyTreeOfType(ownType)
+ val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil)))
+ val unapplyImplicits = unapplyApp match {
+ case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); args2
+ case Apply(unapply, `dummyArg` :: Nil) => Nil
+ }
+
+ var argTypes = unapplyArgs(unapplyApp.tpe, unapplyFn, args, tree.pos)
+ for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show)
+ val bunchedArgs = argTypes match {
+ case argType :: Nil =>
+ if (argType.isRepeatedParam) untpd.SeqLiteral(args, untpd.TypeTree()) :: Nil
+ else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil
+ else args
+ case _ => args
+ }
+ if (argTypes.length != bunchedArgs.length) {
+ ctx.error(em"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
+ argTypes = argTypes.take(args.length) ++
+ List.fill(argTypes.length - args.length)(WildcardType)
+ }
+ val unapplyPatterns = (bunchedArgs, argTypes).zipped map (typed(_, _))
+ val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType)
+ unapp.println(s"unapply patterns = $unapplyPatterns")
+ if ((ownType eq selType) || ownType.isError) result
+ else Typed(result, TypeTree(ownType))
+ case tp =>
+ val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn)
+ val typedArgsErr = args mapconserve (typed(_, defn.AnyType))
+ cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType
+ }
+ }
+
+ /** A typed unapply hook, can be overridden by re any-typers between frontend
+ * and pattern matcher.
+ */
+ def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply =
+ throw new UnsupportedOperationException("cannot type check an UnApply node")
+
+ /** Is given method reference applicable to type arguments `targs` and argument trees `args`?
+ * @param resultType The expected result type of the application
+ */
+ def isApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = {
+ val nestedContext = ctx.fresh.setExploreTyperState
+ new ApplicableToTrees(methRef, targs, args, resultType)(nestedContext).success
+ }
+
+ /** Is given method reference applicable to type arguments `targs` and argument trees `args` without inferring views?
+ * @param resultType The expected result type of the application
+ */
+ def isDirectlyApplicable(methRef: TermRef, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean = {
+ val nestedContext = ctx.fresh.setExploreTyperState
+ new ApplicableToTreesDirectly(methRef, targs, args, resultType)(nestedContext).success
+ }
+
+ /** Is given method reference applicable to argument types `args`?
+ * @param resultType The expected result type of the application
+ */
+ def isApplicable(methRef: TermRef, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean = {
+ val nestedContext = ctx.fresh.setExploreTyperState
+ new ApplicableToTypes(methRef, args, resultType)(nestedContext).success
+ }
+
+ /** Is given type applicable to type arguments `targs` and argument trees `args`,
+ * possibly after inserting an `apply`?
+ * @param resultType The expected result type of the application
+ */
+ def isApplicable(tp: Type, targs: List[Type], args: List[Tree], resultType: Type)(implicit ctx: Context): Boolean =
+ onMethod(tp, isApplicable(_, targs, args, resultType))
+
+ /** Is given type applicable to argument types `args`, possibly after inserting an `apply`?
+ * @param resultType The expected result type of the application
+ */
+ def isApplicable(tp: Type, args: List[Type], resultType: Type)(implicit ctx: Context): Boolean =
+ onMethod(tp, isApplicable(_, args, resultType))
+
+ private def onMethod(tp: Type, p: TermRef => Boolean)(implicit ctx: Context): Boolean = tp match {
+ case methRef: TermRef if methRef.widenSingleton.isInstanceOf[MethodicType] =>
+ p(methRef)
+ case mt: MethodicType =>
+ p(mt.narrow)
+ case _ =>
+ tp.member(nme.apply).hasAltWith(d => p(TermRef(tp, nme.apply, d)))
+ }
+
+ /** In a set of overloaded applicable alternatives, is `alt1` at least as good as
+ * `alt2`? `alt1` and `alt2` are non-overloaded references.
+ */
+ def isAsGood(alt1: TermRef, alt2: TermRef)(implicit ctx: Context): Boolean = track("isAsGood") { ctx.traceIndented(i"isAsGood($alt1, $alt2)", overload) {
+
+ assert(alt1 ne alt2)
+
+ /** Is class or module class `sym1` derived from class or module class `sym2`?
+ * Module classes also inherit the relationship from their companions.
+ */
+ 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` ?
+ *
+ * 1. A method `alt1` of type (p1: T1, ..., pn: Tn)U is as specific as `alt2`
+ * if `alt2` is applicable to arguments (p1, ..., pn) of types T1,...,Tn
+ * or if `alt1` is nullary.
+ * 2. A polymorphic member of type [a1 >: L1 <: U1, ..., an >: Ln <: Un]T is as
+ * specific as `alt2` of type `tp2` if T is as specific as `tp2` under the
+ * assumption that for i = 1,...,n each ai is an abstract type name bounded
+ * from below by Li and from above by Ui.
+ * 3. A member of any other type `tp1` is:
+ * a. always as specific as a method or a polymorphic method.
+ * b. as specific as a member of any other type `tp2` if `tp1` is compatible
+ * with `tp2`.
+ */
+ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match {
+ case tp1: MethodType => // (1)
+ def repeatedToSingle(tp: Type): Type = tp match {
+ case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1))
+ case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp
+ }
+ val formals1 =
+ if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramTypes map repeatedToSingle
+ else tp1.paramTypes
+ isApplicable(alt2, formals1, WildcardType) ||
+ tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly]
+ case tp1: PolyType => // (2)
+ val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds)
+ isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2)
+ case _ => // (3)
+ tp2 match {
+ case tp2: MethodType => true // (3a)
+ case tp2: PolyType if tp2.isPolymorphicMethodType => true // (3a)
+ case tp2: PolyType => // (3b)
+ val nestedCtx = ctx.fresh.setExploreTyperState
+
+ {
+ implicit val ctx: Context = nestedCtx
+ isAsSpecificValueType(tp1, constrained(tp2).resultType)
+ }
+ case _ => // (3b)
+ isAsSpecificValueType(tp1, tp2)
+ }
+ }}
+
+ /** Test whether value type `tp1` is as specific as value type `tp2`.
+ * Let's abbreviate this to `tp1 <:s tp2`.
+ * Previously, `<:s` was the same as `<:`. This behavior is still
+ * available under mode `Mode.OldOverloadingResolution`. The new behavior
+ * is different, however. Here, `T <:s U` iff
+ *
+ * flip(T) <: flip(U)
+ *
+ * where `flip` changes top-level contravariant type aliases to covariant ones.
+ * Intuitively `<:s` means subtyping `<:`, except that all top-level arguments
+ * to contravariant parameters are compared as if they were covariant. E.g. given class
+ *
+ * class Cmp[-X]
+ *
+ * `Cmp[T] <:s Cmp[U]` if `T <: U`. On the other hand, nested occurrences
+ * of parameters are not affected.
+ * So `T <: U` would imply `List[Cmp[U]] <:s List[Cmp[T]]`, as usual.
+ *
+ * This relation might seem strange, but it models closely what happens for methods.
+ * Indeed, if we integrate the existing rules for methods into `<:s` we have now that
+ *
+ * (T)R <:s (U)R
+ *
+ * iff
+ *
+ * T => R <:s U => R
+ */
+ def isAsSpecificValueType(tp1: Type, tp2: Type)(implicit ctx: Context) =
+ if (ctx.mode.is(Mode.OldOverloadingResolution))
+ isCompatible(tp1, tp2)
+ else {
+ val flip = new TypeMap {
+ def apply(t: Type) = t match {
+ case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1)
+ case t: TypeBounds => t
+ case _ => mapOver(t)
+ }
+ }
+ isCompatible(flip(tp1), flip(tp2))
+ }
+
+ /** Drop any implicit parameter section */
+ def stripImplicit(tp: Type): Type = tp match {
+ case mt: ImplicitMethodType if !mt.isDependent =>
+ mt.resultType
+ // todo: make sure implicit method types are not dependent?
+ // but check test case in /tests/pos/depmet_implicit_chaining_zw.scala
+ case pt: PolyType =>
+ pt.derivedPolyType(pt.paramNames, pt.paramBounds, stripImplicit(pt.resultType))
+ case _ =>
+ tp
+ }
+
+ val owner1 = if (alt1.symbol.exists) alt1.symbol.owner else NoSymbol
+ val owner2 = if (alt2.symbol.exists) alt2.symbol.owner else NoSymbol
+ val tp1 = stripImplicit(alt1.widen)
+ val tp2 = stripImplicit(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)
+
+ overload.println(i"isAsGood($alt1, $alt2)? $tp1 $tp2 $winsOwner1 $winsType1 $winsOwner2 $winsType2")
+
+ // 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
+ }}
+
+ def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
+ alts match {
+ case Nil => alts
+ case _ :: Nil => alts
+ 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(alts)
+ }
+ }
+
+ /** Resolve overloaded alternative `alts`, given expected type `pt` and
+ * possibly also type argument `targs` that need to be applied to each alternative
+ * to form the method type.
+ * todo: use techniques like for implicits to pick candidates quickly?
+ */
+ def resolveOverloaded(alts: List[TermRef], pt: Type)(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") {
+
+ /** Is `alt` a method or polytype whose result type after the first value parameter
+ * section conforms to the expected type `resultType`? If `resultType`
+ * is a `IgnoredProto`, pick the underlying type instead.
+ */
+ def resultConforms(alt: Type, resultType: Type)(implicit ctx: Context): Boolean = resultType match {
+ case IgnoredProto(ignored) => resultConforms(alt, ignored)
+ case _: ValueType =>
+ alt.widen match {
+ case tp: PolyType => resultConforms(constrained(tp).resultType, resultType)
+ case tp: MethodType => constrainResult(tp.resultType, resultType)
+ case _ => true
+ }
+ case _ => true
+ }
+
+ /** If the `chosen` alternative has a result type incompatible with the expected result
+ * type `pt`, run overloading resolution again on all alternatives that do match `pt`.
+ * If the latter succeeds with a single alternative, return it, otherwise
+ * fallback to `chosen`.
+ *
+ * Note this order of events is done for speed. One might be tempted to
+ * preselect alternatives by result type. But is slower, because it discriminates
+ * less. The idea is when searching for a best solution, as is the case in overloading
+ * resolution, we should first try criteria which are cheap and which have a high
+ * probability of pruning the search. result type comparisons are neither cheap nor
+ * do they prune much, on average.
+ */
+ def adaptByResult(chosen: TermRef) = {
+ def nestedCtx = ctx.fresh.setExploreTyperState
+ pt match {
+ case pt: FunProto if !resultConforms(chosen, pt.resultType)(nestedCtx) =>
+ alts.filter(alt =>
+ (alt ne chosen) && resultConforms(alt, pt.resultType)(nestedCtx)) match {
+ case Nil => chosen
+ case alt2 :: Nil => alt2
+ case alts2 =>
+ resolveOverloaded(alts2, pt) match {
+ case alt2 :: Nil => alt2
+ case _ => chosen
+ }
+ }
+ case _ => chosen
+ }
+ }
+
+ var found = resolveOverloaded(alts, pt, Nil)(ctx.retractMode(Mode.ImplicitsEnabled))
+ if (found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled))
+ found = resolveOverloaded(alts, pt, Nil)
+ found match {
+ case alt :: Nil => adaptByResult(alt) :: Nil
+ case _ => found
+ }
+ }
+
+ /** This private version of `resolveOverloaded` does the bulk of the work of
+ * overloading resolution, but does not do result adaptation. It might be
+ * called twice from the public `resolveOverloaded` method, once with
+ * implicits enabled, and once without.
+ */
+ private def resolveOverloaded(alts: List[TermRef], pt: Type, targs: List[Type])(implicit ctx: Context): List[TermRef] = track("resolveOverloaded") {
+
+ 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.FunctionOf(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): Tree = tree match {
+ case NamedArg(name, arg) =>
+ val argShape = treeShape(arg)
+ cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe)
+ case _ =>
+ dummyTreeOfType(typeShape(tree))
+ }
+
+ def narrowByTypes(alts: List[TermRef], argTypes: List[Type], resultType: Type): List[TermRef] =
+ alts filter (isApplicable(_, argTypes, resultType))
+
+ val candidates = pt match {
+ case pt @ FunProto(args, resultType, _) =>
+ val numArgs = args.length
+ val normArgs = args.mapConserve {
+ case Block(Nil, expr) => expr
+ case x => x
+ }
+
+ 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
+ case _ =>
+ numArgs == 0
+ }
+
+ def narrowBySize(alts: List[TermRef]): List[TermRef] =
+ alts filter (alt => sizeFits(alt, alt.widen))
+
+ def narrowByShapes(alts: List[TermRef]): List[TermRef] = {
+ if (normArgs exists (_.isInstanceOf[untpd.Function]))
+ if (hasNamedArg(args)) narrowByTrees(alts, args map treeShape, resultType)
+ else narrowByTypes(alts, normArgs map typeShape, resultType)
+ else
+ alts
+ }
+
+ def narrowByTrees(alts: List[TermRef], args: List[Tree], resultType: Type): List[TermRef] = {
+ val alts2 = alts.filter(alt =>
+ isDirectlyApplicable(alt, targs, args, resultType)
+ )
+ if (alts2.isEmpty && !ctx.isAfterTyper)
+ alts.filter(alt =>
+ isApplicable(alt, targs, args, resultType)
+ )
+ else
+ alts2
+ }
+
+ val alts1 = narrowBySize(alts)
+ //ctx.log(i"narrowed by size: ${alts1.map(_.symbol.showDcl)}%, %")
+ if (isDetermined(alts1)) alts1
+ else {
+ val alts2 = narrowByShapes(alts1)
+ //ctx.log(i"narrowed by shape: ${alts1.map(_.symbol.showDcl)}%, %")
+ if (isDetermined(alts2)) alts2
+ else {
+ pretypeArgs(alts2, pt)
+ narrowByTrees(alts2, pt.typedArgs, resultType)
+ }
+ }
+
+ case pt @ PolyProto(targs1, pt1) =>
+ assert(targs.isEmpty)
+ val alts1 = alts filter pt.isMatchedBy
+ resolveOverloaded(alts1, pt1, targs1)
+
+ case defn.FunctionOf(args, resultType) =>
+ narrowByTypes(alts, args, resultType)
+
+ case pt =>
+ alts filter (normalizedCompatible(_, pt))
+ }
+ val found = narrowMostSpecific(candidates)
+ if (found.length <= 1) found
+ else {
+ val noDefaults = alts.filter(!_.symbol.hasDefaultParams)
+ if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists
+ else {
+ val deepPt = pt.deepenProto
+ if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs)
+ else alts
+ }
+ }
+ }
+
+ /** Try to typecheck any arguments in `pt` that are function values missing a
+ * parameter type. The expected type for these arguments is the lub of the
+ * corresponding formal parameter types of all alternatives. Type variables
+ * in formal parameter types are replaced by wildcards. The result of the
+ * typecheck is stored in `pt`, to be retrieved when its `typedArgs` are selected.
+ * The benefit of doing this is to allow idioms like this:
+ *
+ * def map(f: Char => Char): String = ???
+ * def map[U](f: Char => U): Seq[U] = ???
+ * map(x => x.toUpper)
+ *
+ * Without `pretypeArgs` we'd get a "missing parameter type" error for `x`.
+ * With `pretypeArgs`, we use the union of the two formal parameter types
+ * `Char => Char` and `Char => ?` as the expected type of the closure `x => x.toUpper`.
+ * That union is `Char => Char`, so we have an expected parameter type `Char`
+ * for `x`, and the code typechecks.
+ */
+ private def pretypeArgs(alts: List[TermRef], pt: FunProto)(implicit ctx: Context): Unit = {
+ def recur(altFormals: List[List[Type]], args: List[untpd.Tree]): Unit = args match {
+ case arg :: args1 if !altFormals.exists(_.isEmpty) =>
+ def isUnknownParamType(t: untpd.Tree) = t match {
+ case ValDef(_, tpt, _) => tpt.isEmpty
+ case _ => false
+ }
+ arg match {
+ case arg: untpd.Function if arg.args.exists(isUnknownParamType) =>
+ def isUniform[T](xs: List[T])(p: (T, T) => Boolean) = xs.forall(p(_, xs.head))
+ val formalsForArg: List[Type] = altFormals.map(_.head)
+ // For alternatives alt_1, ..., alt_n, test whether formal types for current argument are of the form
+ // (p_1_1, ..., p_m_1) => r_1
+ // ...
+ // (p_1_n, ..., p_m_n) => r_n
+ val decomposedFormalsForArg: List[Option[(List[Type], Type)]] =
+ formalsForArg.map(defn.FunctionOf.unapply)
+ if (decomposedFormalsForArg.forall(_.isDefined)) {
+ val formalParamTypessForArg: List[List[Type]] =
+ decomposedFormalsForArg.map(_.get._1)
+ if (isUniform(formalParamTypessForArg)((x, y) => x.length == y.length)) {
+ val commonParamTypes = formalParamTypessForArg.transpose.map(ps =>
+ // Given definitions above, for i = 1,...,m,
+ // ps(i) = List(p_i_1, ..., p_i_n) -- i.e. a column
+ // If all p_i_k's are the same, assume the type as formal parameter
+ // type of the i'th parameter of the closure.
+ if (isUniform(ps)(ctx.typeComparer.isSameTypeWhenFrozen(_, _))) ps.head
+ else WildcardType)
+ val commonFormal = defn.FunctionOf(commonParamTypes, WildcardType)
+ overload.println(i"pretype arg $arg with expected type $commonFormal")
+ pt.typedArg(arg, commonFormal)
+ }
+ }
+ case _ =>
+ }
+ recur(altFormals.map(_.tail), args1)
+ case _ =>
+ }
+ def paramTypes(alt: Type): List[Type] = alt match {
+ case mt: MethodType => mt.paramTypes
+ case mt: PolyType => paramTypes(mt.resultType)
+ case _ => Nil
+ }
+ recur(alts.map(alt => paramTypes(alt.widen)), pt.args)
+ }
+
+ private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = {
+ def numericClasses(ts: List[T], acc: Set[Symbol]): Set[Symbol] = ts match {
+ case t :: ts1 =>
+ val sym = tpe(t).widen.classSymbol
+ if (sym.isNumericValueClass) numericClasses(ts1, acc + sym)
+ else Set()
+ case Nil =>
+ acc
+ }
+ val clss = numericClasses(ts, Set())
+ if (clss.size > 1) {
+ val lub = defn.ScalaNumericValueTypeList.find(lubTpe =>
+ clss.forall(cls => defn.isValueSubType(cls.typeRef, lubTpe))).get
+ ts.mapConserve(adapt(_, lub))
+ }
+ else ts
+ }
+
+ /** If `trees` all have numeric value types, and they do not have all the same type,
+ * pick a common numeric supertype and convert all trees to this type.
+ */
+ def harmonize(trees: List[Tree])(implicit ctx: Context): List[Tree] = {
+ def adapt(tree: Tree, pt: Type): Tree = tree match {
+ case cdef: CaseDef => tpd.cpy.CaseDef(cdef)(body = adapt(cdef.body, pt))
+ case _ => adaptInterpolated(tree, pt, tree)
+ }
+ if (ctx.isAfterTyper) trees else harmonizeWith(trees)(_.tpe, adapt)
+ }
+
+ /** If all `types` are numeric value types, and they are not all the same type,
+ * pick a common numeric supertype and return it instead of every original type.
+ */
+ def harmonizeTypes(tpes: List[Type])(implicit ctx: Context): List[Type] =
+ harmonizeWith(tpes)(identity, (tp, pt) => pt)
+}
+