aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala1351
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Checking.scala557
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ConstFold.scala182
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Docstrings.scala56
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Dynamic.scala104
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala153
-rw-r--r--compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala191
-rw-r--r--compiler/src/dotty/tools/dotc/typer/FrontEnd.scala83
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Implicits.scala844
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ImportInfo.scala117
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Inferencing.scala362
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Inliner.scala539
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Namer.scala1061
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala488
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ReTyper.scala108
-rw-r--r--compiler/src/dotty/tools/dotc/typer/RefChecks.scala1526
-rw-r--r--compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala524
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala1952
-rw-r--r--compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala148
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Variances.scala116
20 files changed, 10462 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)
+}
+
diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala
new file mode 100644
index 000000000..dbfc89f6c
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala
@@ -0,0 +1,557 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Contexts._
+import Types._
+import Flags._
+import Denotations._
+import Names._
+import StdNames._
+import NameOps._
+import Symbols._
+import Trees._
+import ProtoTypes._
+import Constants._
+import Scopes._
+import CheckRealizable._
+import ErrorReporting.errorTree
+import annotation.unchecked
+import util.Positions._
+import util.{Stats, SimpleMap}
+import util.common._
+import transform.SymUtils._
+import Decorators._
+import Uniques._
+import ErrorReporting.{err, errorType}
+import config.Printers.typr
+import collection.mutable
+import SymDenotations.NoCompleter
+
+object Checking {
+ import tpd._
+
+ /** A general checkBounds method that can be used for TypeApply nodes as
+ * well as for AppliedTypeTree nodes. Also checks that type arguments to
+ * *-type parameters are fully applied.
+ */
+ def checkBounds(args: List[tpd.Tree], boundss: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context): Unit = {
+ (args, boundss).zipped.foreach { (arg, bound) =>
+ if (!bound.isHK && arg.tpe.isHK)
+ ctx.error(ex"missing type parameter(s) for $arg", arg.pos)
+ }
+ for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate))
+ ctx.error(
+ ex"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
+ arg.pos.focus)
+ }
+
+ /** Check that type arguments `args` conform to corresponding bounds in `poly`
+ * Note: This does not check the bounds of AppliedTypeTrees. These
+ * are handled by method checkBounds in FirstTransform
+ */
+ def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit =
+ checkBounds(args, poly.paramBounds, _.substParams(poly, _))
+
+ /** Check applied type trees for well-formedness. This means
+ * - all arguments are within their corresponding bounds
+ * - if type is a higher-kinded application with wildcard arguments,
+ * check that it or one of its supertypes can be reduced to a normal application.
+ * Unreducible applications correspond to general existentials, and we
+ * cannot handle those.
+ */
+ def checkAppliedType(tree: AppliedTypeTree)(implicit ctx: Context) = {
+ val AppliedTypeTree(tycon, args) = tree
+ // If `args` is a list of named arguments, return corresponding type parameters,
+ // otherwise return type parameters unchanged
+ val tparams = tycon.tpe.typeParams
+ def argNamed(tparam: TypeParamInfo) = args.find {
+ case NamedArg(name, _) => name == tparam.paramName
+ case _ => false
+ }.getOrElse(TypeTree(tparam.paramRef))
+ val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args
+ val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe))
+ def instantiate(bound: Type, args: List[Type]) =
+ bound.LambdaAbstract(tparams).appliedTo(args)
+ checkBounds(orderedArgs, bounds, instantiate)
+
+ def checkWildcardHKApply(tp: Type, pos: Position): Unit = tp match {
+ case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) =>
+ tycon match {
+ case tycon: PolyType =>
+ ctx.errorOrMigrationWarning(
+ ex"unreducible application of higher-kinded type $tycon to wildcard arguments",
+ pos)
+ case _ =>
+ checkWildcardHKApply(tp.superType, pos)
+ }
+ case _ =>
+ }
+ def checkValidIfHKApply(implicit ctx: Context): Unit =
+ checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos)
+ checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply))
+ }
+
+ /** Check that `tp` refers to a nonAbstract class
+ * and that the instance conforms to the self type of the created class.
+ */
+ def checkInstantiable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ tp.underlyingClassRef(refinementOK = false) match {
+ case tref: TypeRef =>
+ val cls = tref.symbol
+ if (cls.is(AbstractOrTrait))
+ ctx.error(em"$cls is abstract; cannot be instantiated", pos)
+ if (!cls.is(Module)) {
+ // Create a synthetic singleton type instance, and check whether
+ // it conforms to the self type of the class as seen from that instance.
+ val stp = SkolemType(tp)
+ val selfType = tref.givenSelfType.asSeenFrom(stp, cls)
+ if (selfType.exists && !(stp <:< selfType))
+ ctx.error(ex"$tp does not conform to its self type $selfType; cannot be instantiated")
+ }
+ case _ =>
+ }
+
+ /** Check that type `tp` is realizable. */
+ def checkRealizable(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
+ val rstatus = realizability(tp)
+ if (rstatus ne Realizable) {
+ def msg = em"$tp is not a legal path\n since it${rstatus.msg}"
+ if (ctx.scala2Mode) ctx.migrationWarning(msg, pos) else ctx.error(msg, pos)
+ }
+ }
+
+ /** A type map which checks that the only cycles in a type are F-bounds
+ * and that protects all F-bounded references by LazyRefs.
+ */
+ class CheckNonCyclicMap(sym: Symbol, reportErrors: Boolean)(implicit ctx: Context) extends TypeMap {
+
+ /** Are cycles allowed within nested refinedInfos of currently checked type? */
+ private var nestedCycleOK = false
+
+ /** Are cycles allowed within currently checked type? */
+ private var cycleOK = false
+
+ /** A diagnostic output string that indicates the position of the last
+ * part of a type bounds checked by checkInfo. Possible choices:
+ * alias, lower bound, upper bound.
+ */
+ var where: String = ""
+
+ /** The last type top-level type checked when a CyclicReference occurs. */
+ var lastChecked: Type = NoType
+
+ /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles,
+ * break direct cycle with a LazyRef for legal, F-bounded cycles.
+ */
+ def checkInfo(tp: Type): Type = tp match {
+ case tp @ TypeAlias(alias) =>
+ try tp.derivedTypeAlias(apply(alias))
+ finally {
+ where = "alias"
+ lastChecked = alias
+ }
+ case tp @ TypeBounds(lo, hi) =>
+ val lo1 = try apply(lo) finally {
+ where = "lower bound"
+ lastChecked = lo
+ }
+ val saved = nestedCycleOK
+ nestedCycleOK = true
+ try tp.derivedTypeBounds(lo1, apply(hi))
+ finally {
+ nestedCycleOK = saved
+ where = "upper bound"
+ lastChecked = hi
+ }
+ case _ =>
+ tp
+ }
+
+ private def apply(tp: Type, cycleOK: Boolean, nestedCycleOK: Boolean): Type = {
+ val savedCycleOK = this.cycleOK
+ val savedNestedCycleOK = this.nestedCycleOK
+ this.cycleOK = cycleOK
+ this.nestedCycleOK = nestedCycleOK
+ try apply(tp)
+ finally {
+ this.cycleOK = savedCycleOK
+ this.nestedCycleOK = savedNestedCycleOK
+ }
+ }
+
+ def apply(tp: Type): Type = tp match {
+ case tp: TermRef =>
+ this(tp.info)
+ mapOver(tp)
+ case tp @ RefinedType(parent, name, rinfo) =>
+ tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK))
+ case tp: RecType =>
+ tp.rebind(this(tp.parent))
+ case tp @ HKApply(tycon, args) =>
+ tp.derivedAppliedType(this(tycon), args.map(this(_, nestedCycleOK, nestedCycleOK)))
+ case tp @ TypeRef(pre, name) =>
+ try {
+ // A prefix is interesting if it might contain (transitively) a reference
+ // to symbol `sym` itself. We only check references with interesting
+ // prefixes for cycles. This pruning is done in order not to force
+ // global symbols when doing the cyclicity check.
+ def isInteresting(prefix: Type): Boolean = prefix.stripTypeVar match {
+ case NoPrefix => true
+ case prefix: ThisType => sym.owner.isClass && prefix.cls.isContainedIn(sym.owner)
+ case prefix: NamedType => !prefix.symbol.isStaticOwner && isInteresting(prefix.prefix)
+ case SuperType(thistp, _) => isInteresting(thistp)
+ case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2)
+ case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2)
+ case _: RefinedOrRecType | _: HKApply => true
+ case _ => false
+ }
+ if (isInteresting(pre)) {
+ val pre1 = this(pre, false, false)
+ checkInfo(tp.info)
+ if (pre1 eq pre) tp else tp.newLikeThis(pre1)
+ }
+ else tp
+ } catch {
+ case ex: CyclicReference =>
+ ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK")
+ if (cycleOK) LazyRef(() => tp)
+ else if (reportErrors) throw ex
+ else tp
+ }
+ case _ => mapOver(tp)
+ }
+ }
+
+ /** Check that `info` of symbol `sym` is not cyclic.
+ * @pre sym is not yet initialized (i.e. its type is a Completer).
+ * @return `info` where every legal F-bounded reference is proctected
+ * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
+ */
+ def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = {
+ val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.addMode(Mode.CheckCyclic))
+ try checker.checkInfo(info)
+ catch {
+ case ex: CyclicReference =>
+ if (reportErrors) {
+ ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos)
+ ErrorType
+ }
+ else info
+ }
+ }
+
+ /** Check that refinement satisfies the following two conditions
+ * 1. No part of it refers to a symbol that's defined in the same refinement
+ * at a textually later point.
+ * 2. All references to the refinement itself via `this` are followed by
+ * selections.
+ * Note: It's not yet clear what exactly we want to allow and what we want to rule out.
+ * This depends also on firming up the DOT calculus. For the moment we only issue
+ * deprecated warnings, not errors.
+ */
+ def checkRefinementNonCyclic(refinement: Tree, refineCls: ClassSymbol, seen: mutable.Set[Symbol])
+ (implicit ctx: Context): Unit = {
+ def flag(what: String, tree: Tree) =
+ ctx.deprecationWarning(i"$what reference in refinement is deprecated", tree.pos)
+ def forwardRef(tree: Tree) = flag("forward", tree)
+ def selfRef(tree: Tree) = flag("self", tree)
+ val checkTree = new TreeAccumulator[Unit] {
+ def checkRef(tree: Tree, sym: Symbol) =
+ if (sym.maybeOwner == refineCls && !seen(sym)) forwardRef(tree)
+ def apply(x: Unit, tree: Tree)(implicit ctx: Context) = tree match {
+ case tree: MemberDef =>
+ foldOver(x, tree)
+ seen += tree.symbol
+ case tree @ Select(This(_), _) =>
+ checkRef(tree, tree.symbol)
+ case tree: RefTree =>
+ checkRef(tree, tree.symbol)
+ foldOver(x, tree)
+ case tree: This =>
+ selfRef(tree)
+ case tree: TypeTree =>
+ val checkType = new TypeAccumulator[Unit] {
+ def apply(x: Unit, tp: Type): Unit = tp match {
+ case tp: NamedType =>
+ checkRef(tree, tp.symbol)
+ tp.prefix match {
+ case pre: ThisType =>
+ case pre => foldOver(x, pre)
+ }
+ case tp: ThisType if tp.cls == refineCls =>
+ selfRef(tree)
+ case _ =>
+ foldOver(x, tp)
+ }
+ }
+ checkType((), tree.tpe)
+ case _ =>
+ foldOver(x, tree)
+ }
+ }
+ checkTree((), refinement)
+ }
+
+ /** Check that symbol's definition is well-formed. */
+ def checkWellFormed(sym: Symbol)(implicit ctx: Context): Unit = {
+ //println(i"check wf $sym with flags ${sym.flags}")
+ def fail(msg: String) = ctx.error(msg, sym.pos)
+ def varNote =
+ if (sym.is(Mutable)) "\n(Note that variables need to be initialized to be defined)"
+ else ""
+
+ def checkWithDeferred(flag: FlagSet) =
+ if (sym.is(flag))
+ fail(i"abstract member may not have `$flag' modifier")
+ def checkNoConflict(flag1: FlagSet, flag2: FlagSet) =
+ if (sym.is(allOf(flag1, flag2)))
+ fail(i"illegal combination of modifiers: $flag1 and $flag2 for: $sym")
+
+ if (sym.is(ImplicitCommon)) {
+ if (sym.owner.is(Package))
+ fail(i"`implicit' modifier cannot be used for top-level definitions")
+ if (sym.isType)
+ fail(i"`implicit' modifier cannot be used for types or traits")
+ }
+ if (!sym.isClass && sym.is(Abstract))
+ fail(i"`abstract' modifier can be used only for classes; it should be omitted for abstract members")
+ if (sym.is(AbsOverride) && !sym.owner.is(Trait))
+ fail(i"`abstract override' modifier only allowed for members of traits")
+ if (sym.is(Trait) && sym.is(Final))
+ fail(i"$sym may not be `final'")
+ if (sym.hasAnnotation(defn.NativeAnnot)) {
+ if (!sym.is(Deferred))
+ fail(i"`@native' members may not have implementation")
+ }
+ else if (sym.is(Deferred, butNot = Param) && !sym.isSelfSym) {
+ if (!sym.owner.isClass || sym.owner.is(Module) || sym.owner.isAnonymousClass)
+ fail(i"only classes can have declared but undefined members$varNote")
+ checkWithDeferred(Private)
+ checkWithDeferred(Final)
+ checkWithDeferred(Inline)
+ }
+ if (sym.isValueClass && sym.is(Trait) && !sym.isRefinementClass)
+ fail(i"$sym cannot extend AnyVal")
+ checkNoConflict(Final, Sealed)
+ checkNoConflict(Private, Protected)
+ checkNoConflict(Abstract, Override)
+ }
+
+ /** Check the type signature of the symbol `M` defined by `tree` does not refer
+ * to a private type or value which is invisible at a point where `M` is still
+ * visible. As an exception, we allow references to type aliases if the underlying
+ * type of the alias is not a leak. So type aliases are transparent as far as
+ * leak testing is concerned.
+ * @return The `info` of `sym`, with problematic aliases expanded away.
+ * See i997.scala for tests, i1130.scala for a case where it matters that we
+ * transform leaky aliases away.
+ */
+ def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = {
+ class NotPrivate extends TypeMap {
+ type Errors = List[(String, Position)]
+ var errors: Errors = Nil
+ def accessBoundary(sym: Symbol): Symbol =
+ if (sym.is(Private)) sym.owner
+ else if (sym.privateWithin.exists) sym.privateWithin
+ else if (sym.is(Package)) sym
+ else accessBoundary(sym.owner)
+ def apply(tp: Type): Type = tp match {
+ case tp: NamedType =>
+ val prevErrors = errors
+ var tp1 =
+ if (tp.symbol.is(Private) &&
+ !accessBoundary(sym).isContainedIn(tp.symbol.owner)) {
+ errors = (em"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}",
+ sym.pos) :: errors
+ tp
+ }
+ else mapOver(tp)
+ if ((errors ne prevErrors) && tp.info.isAlias) {
+ // try to dealias to avoid a leak error
+ val savedErrors = errors
+ errors = prevErrors
+ val tp2 = apply(tp.superType)
+ if (errors eq prevErrors) tp1 = tp2
+ else errors = savedErrors
+ }
+ tp1
+ case tp: ClassInfo =>
+ tp.derivedClassInfo(
+ prefix = apply(tp.prefix),
+ classParents = tp.parentsWithArgs.map(p =>
+ apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef]))
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ val notPrivate = new NotPrivate
+ val info = notPrivate(sym.info)
+ notPrivate.errors.foreach { case (msg, pos) => ctx.errorOrMigrationWarning(msg, pos) }
+ info
+ }
+}
+
+trait Checking {
+
+ import tpd._
+
+ def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type =
+ Checking.checkNonCyclic(sym, info, reportErrors)
+
+ /** Check that Java statics and packages can only be used in selections.
+ */
+ def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = {
+ if (!proto.isInstanceOf[SelectionProto]) {
+ val sym = tree.tpe.termSymbol
+ // The check is avoided inside Java compilation units because it always fails
+ // on the singleton type Module.type.
+ if ((sym is Package) || ((sym is JavaModule) && !ctx.compilationUnit.isJava)) ctx.error(em"$sym is not a value", tree.pos)
+ }
+ tree
+ }
+
+ /** Check that type `tp` is stable. */
+ def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ if (!tp.isStable) ctx.error(ex"$tp is not stable", pos)
+
+ /** Check that all type members of `tp` have realizable bounds */
+ def checkRealizableBounds(tp: Type, pos: Position)(implicit ctx: Context): Unit = {
+ val rstatus = boundsRealizability(tp)
+ if (rstatus ne Realizable)
+ ctx.error(ex"$tp cannot be instantiated since it${rstatus.msg}", pos)
+ }
+
+ /** Check that `tp` is a class type.
+ * Also, if `traitReq` is true, check that `tp` is a trait.
+ * Also, if `stablePrefixReq` is true and phase is not after RefChecks,
+ * check that class prefix is stable.
+ * @return `tp` itself if it is a class or trait ref, ObjectType if not.
+ */
+ def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type =
+ tp.underlyingClassRef(refinementOK = false) match {
+ case tref: TypeRef =>
+ if (traitReq && !(tref.symbol is Trait)) ctx.error(ex"$tref is not a trait", pos)
+ if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
+ tp
+ case _ =>
+ ctx.error(ex"$tp is not a class type", pos)
+ defn.ObjectType
+ }
+
+ /** Check that a non-implicit parameter making up the first parameter section of an
+ * implicit conversion is not a singleton type.
+ */
+ def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
+ case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) =>
+ if (vparam.tpt.tpe.isInstanceOf[SingletonType])
+ ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos)
+ case _ =>
+ }
+
+ /** Check that any top-level type arguments in this type are feasible, i.e. that
+ * their lower bound conforms to their upper bound. If a type argument is
+ * infeasible, issue and error and continue with upper bound.
+ */
+ def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match {
+ case tp: RefinedType =>
+ tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where))
+ case tp: RecType =>
+ tp.rebind(tp.parent)
+ case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
+ ctx.error(ex"no type exists between low bound $lo and high bound $hi$where", pos)
+ TypeAlias(hi)
+ case _ =>
+ tp
+ }
+
+ /** Check that `tree` is a pure expression of constant type */
+ def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context): Unit =
+ tree.tpe match {
+ case tp: TermRef if tp.symbol.is(InlineParam) => // ok
+ case tp => tp.widenTermRefExpr match {
+ case tp: ConstantType if isPureExpr(tree) => // ok
+ case tp if defn.isFunctionType(tp) && isPureExpr(tree) => // ok
+ case _ => ctx.error(em"$what must be a constant expression or a function", tree.pos)
+ }
+ }
+
+ /** Check that class does not define same symbol twice */
+ def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
+ val seen = new mutable.HashMap[Name, List[Symbol]] {
+ override def default(key: Name) = Nil
+ }
+ typr.println(i"check no double defs $cls")
+
+ def checkDecl(decl: Symbol): Unit = {
+ for (other <- seen(decl.name)) {
+ typr.println(i"conflict? $decl $other")
+ if (decl.matches(other)) {
+ def doubleDefError(decl: Symbol, other: Symbol): Unit = {
+ def ofType = if (decl.isType) "" else em": ${other.info}"
+ def explanation =
+ if (!decl.isRealMethod) ""
+ else "\n (the definitions have matching type signatures)"
+ ctx.error(em"$decl is already defined as $other$ofType$explanation", decl.pos)
+ }
+ if (decl is Synthetic) doubleDefError(other, decl)
+ else doubleDefError(decl, other)
+ }
+ if ((decl is HasDefaultParams) && (other is HasDefaultParams)) {
+ ctx.error(em"two or more overloaded variants of $decl have default arguments")
+ decl resetFlag HasDefaultParams
+ }
+ }
+ seen(decl.name) = decl :: seen(decl.name)
+ }
+
+ cls.info.decls.foreach(checkDecl)
+ cls.info match {
+ case ClassInfo(_, _, _, _, selfSym: Symbol) => checkDecl(selfSym)
+ case _ =>
+ }
+ }
+
+ def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) =
+ if (!ctx.isAfterTyper) {
+ val called = call.tpe.classSymbol
+ if (caller is Trait)
+ ctx.error(i"$caller may not call constructor of $called", call.pos)
+ else if (called.is(Trait) && !caller.mixins.contains(called))
+ ctx.error(i"""$called is already implemented by super${caller.superClass},
+ |its constructor cannot be called again""", call.pos)
+ }
+
+ /** Check that `tpt` does not define a higher-kinded type */
+ def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree =
+ if (tpt.tpe.isHK && !ctx.compilationUnit.isJava) {
+ // be more lenient with missing type params in Java,
+ // needed to make pos/java-interop/t1196 work.
+ errorTree(tpt, ex"missing type parameter for ${tpt.tpe}")
+ }
+ else tpt
+
+ /** Check that `tpt` does not refer to a singleton type */
+ def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree =
+ if (tpt.tpe.isInstanceOf[SingletonType]) {
+ errorTree(tpt, ex"Singleton type ${tpt.tpe} is not allowed $where")
+ }
+ else tpt
+}
+
+trait NoChecking extends Checking {
+ import tpd._
+ override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
+ override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
+ override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
+ override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
+ override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
+ override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = ()
+ override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
+ override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = ()
+ override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt
+ override def checkNotSingleton(tpt: Tree, where: String)(implicit ctx: Context): Tree = tpt
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/ConstFold.scala b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala
new file mode 100644
index 000000000..68a5d05f5
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/ConstFold.scala
@@ -0,0 +1,182 @@
+package dotty.tools.dotc
+package typer
+
+import java.lang.ArithmeticException
+
+import ast._
+import Trees._
+import core._
+import Types._
+import Constants._
+import Names._
+import StdNames._
+import Contexts._
+
+object ConstFold {
+
+ import tpd._
+
+ /** If tree is a constant operation, replace with result. */
+ def apply(tree: Tree)(implicit ctx: Context): Tree = finish(tree) {
+ tree match {
+ case Apply(Select(xt, op), yt :: Nil) =>
+ xt.tpe.widenTermRefExpr match {
+ case ConstantType(x) =>
+ yt.tpe.widenTermRefExpr match {
+ case ConstantType(y) => foldBinop(op, x, y)
+ case _ => null
+ }
+ case _ => null
+ }
+ case Select(xt, op) =>
+ xt.tpe.widenTermRefExpr match {
+ case ConstantType(x) => foldUnop(op, x)
+ case _ => null
+ }
+ case _ => null
+ }
+ }
+
+ /** If tree is a constant value that can be converted to type `pt`, perform
+ * the conversion.
+ */
+ def apply(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ finish(apply(tree)) {
+ tree.tpe.widenTermRefExpr match {
+ case ConstantType(x) => x convertTo pt
+ case _ => null
+ }
+ }
+
+ private def finish(tree: Tree)(compX: => Constant)(implicit ctx: Context): Tree =
+ try {
+ val x = compX
+ if (x ne null) tree withType ConstantType(x)
+ else tree
+ } catch {
+ case _: ArithmeticException => tree // the code will crash at runtime,
+ // but that is better than the
+ // compiler itself crashing
+ }
+
+ private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match {
+ case (nme.UNARY_!, BooleanTag) => Constant(!x.booleanValue)
+
+ case (nme.UNARY_~ , IntTag ) => Constant(~x.intValue)
+ case (nme.UNARY_~ , LongTag ) => Constant(~x.longValue)
+
+ case (nme.UNARY_+ , IntTag ) => Constant(x.intValue)
+ case (nme.UNARY_+ , LongTag ) => Constant(x.longValue)
+ case (nme.UNARY_+ , FloatTag ) => Constant(x.floatValue)
+ case (nme.UNARY_+ , DoubleTag ) => Constant(x.doubleValue)
+
+ case (nme.UNARY_- , IntTag ) => Constant(-x.intValue)
+ case (nme.UNARY_- , LongTag ) => Constant(-x.longValue)
+ case (nme.UNARY_- , FloatTag ) => Constant(-x.floatValue)
+ case (nme.UNARY_- , DoubleTag ) => Constant(-x.doubleValue)
+
+ case _ => null
+ }
+
+ /** These are local helpers to keep foldBinop from overly taxing the
+ * optimizer.
+ */
+ private def foldBooleanOp(op: Name, x: Constant, y: Constant): Constant = op match {
+ case nme.ZOR => Constant(x.booleanValue | y.booleanValue)
+ case nme.OR => Constant(x.booleanValue | y.booleanValue)
+ case nme.XOR => Constant(x.booleanValue ^ y.booleanValue)
+ case nme.ZAND => Constant(x.booleanValue & y.booleanValue)
+ case nme.AND => Constant(x.booleanValue & y.booleanValue)
+ case nme.EQ => Constant(x.booleanValue == y.booleanValue)
+ case nme.NE => Constant(x.booleanValue != y.booleanValue)
+ case _ => null
+ }
+ private def foldSubrangeOp(op: Name, x: Constant, y: Constant): Constant = op match {
+ case nme.OR => Constant(x.intValue | y.intValue)
+ case nme.XOR => Constant(x.intValue ^ y.intValue)
+ case nme.AND => Constant(x.intValue & y.intValue)
+ case nme.LSL => Constant(x.intValue << y.intValue)
+ case nme.LSR => Constant(x.intValue >>> y.intValue)
+ case nme.ASR => Constant(x.intValue >> y.intValue)
+ case nme.EQ => Constant(x.intValue == y.intValue)
+ case nme.NE => Constant(x.intValue != y.intValue)
+ case nme.LT => Constant(x.intValue < y.intValue)
+ case nme.GT => Constant(x.intValue > y.intValue)
+ case nme.LE => Constant(x.intValue <= y.intValue)
+ case nme.GE => Constant(x.intValue >= y.intValue)
+ case nme.ADD => Constant(x.intValue + y.intValue)
+ case nme.SUB => Constant(x.intValue - y.intValue)
+ case nme.MUL => Constant(x.intValue * y.intValue)
+ case nme.DIV => Constant(x.intValue / y.intValue)
+ case nme.MOD => Constant(x.intValue % y.intValue)
+ case _ => null
+ }
+ private def foldLongOp(op: Name, x: Constant, y: Constant): Constant = op match {
+ case nme.OR => Constant(x.longValue | y.longValue)
+ case nme.XOR => Constant(x.longValue ^ y.longValue)
+ case nme.AND => Constant(x.longValue & y.longValue)
+ case nme.LSL => Constant(x.longValue << y.longValue)
+ case nme.LSR => Constant(x.longValue >>> y.longValue)
+ case nme.ASR => Constant(x.longValue >> y.longValue)
+ case nme.EQ => Constant(x.longValue == y.longValue)
+ case nme.NE => Constant(x.longValue != y.longValue)
+ case nme.LT => Constant(x.longValue < y.longValue)
+ case nme.GT => Constant(x.longValue > y.longValue)
+ case nme.LE => Constant(x.longValue <= y.longValue)
+ case nme.GE => Constant(x.longValue >= y.longValue)
+ case nme.ADD => Constant(x.longValue + y.longValue)
+ case nme.SUB => Constant(x.longValue - y.longValue)
+ case nme.MUL => Constant(x.longValue * y.longValue)
+ case nme.DIV => Constant(x.longValue / y.longValue)
+ case nme.MOD => Constant(x.longValue % y.longValue)
+ case _ => null
+ }
+ private def foldFloatOp(op: Name, x: Constant, y: Constant): Constant = op match {
+ case nme.EQ => Constant(x.floatValue == y.floatValue)
+ case nme.NE => Constant(x.floatValue != y.floatValue)
+ case nme.LT => Constant(x.floatValue < y.floatValue)
+ case nme.GT => Constant(x.floatValue > y.floatValue)
+ case nme.LE => Constant(x.floatValue <= y.floatValue)
+ case nme.GE => Constant(x.floatValue >= y.floatValue)
+ case nme.ADD => Constant(x.floatValue + y.floatValue)
+ case nme.SUB => Constant(x.floatValue - y.floatValue)
+ case nme.MUL => Constant(x.floatValue * y.floatValue)
+ case nme.DIV => Constant(x.floatValue / y.floatValue)
+ case nme.MOD => Constant(x.floatValue % y.floatValue)
+ case _ => null
+ }
+ private def foldDoubleOp(op: Name, x: Constant, y: Constant): Constant = op match {
+ case nme.EQ => Constant(x.doubleValue == y.doubleValue)
+ case nme.NE => Constant(x.doubleValue != y.doubleValue)
+ case nme.LT => Constant(x.doubleValue < y.doubleValue)
+ case nme.GT => Constant(x.doubleValue > y.doubleValue)
+ case nme.LE => Constant(x.doubleValue <= y.doubleValue)
+ case nme.GE => Constant(x.doubleValue >= y.doubleValue)
+ case nme.ADD => Constant(x.doubleValue + y.doubleValue)
+ case nme.SUB => Constant(x.doubleValue - y.doubleValue)
+ case nme.MUL => Constant(x.doubleValue * y.doubleValue)
+ case nme.DIV => Constant(x.doubleValue / y.doubleValue)
+ case nme.MOD => Constant(x.doubleValue % y.doubleValue)
+ case _ => null
+ }
+
+ private def foldBinop(op: Name, x: Constant, y: Constant): Constant = {
+ val optag =
+ if (x.tag == y.tag) x.tag
+ else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag)
+ else NoTag
+
+ try optag match {
+ case BooleanTag => foldBooleanOp(op, x, y)
+ case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y)
+ case LongTag => foldLongOp(op, x, y)
+ case FloatTag => foldFloatOp(op, x, y)
+ case DoubleTag => foldDoubleOp(op, x, y)
+ case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue)
+ case _ => null
+ }
+ catch {
+ case ex: ArithmeticException => null
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Docstrings.scala b/compiler/src/dotty/tools/dotc/typer/Docstrings.scala
new file mode 100644
index 000000000..370844e65
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Docstrings.scala
@@ -0,0 +1,56 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import Contexts._, Symbols._, Decorators._, Comments._
+import util.Positions._
+import ast.tpd
+
+trait Docstrings { self: Typer =>
+
+ /** The Docstrings typer will handle the expansion of `@define` and
+ * `@inheritdoc` if there is a `DocContext` present as a property in the
+ * supplied `ctx`.
+ *
+ * It will also type any `@usecase` available in function definitions.
+ */
+ def cookComments(syms: List[Symbol], owner: Symbol)(implicit ctx: Context): Unit =
+ ctx.docCtx.foreach { docbase =>
+ val relevantSyms = syms.filter(docbase.docstring(_).isDefined)
+ relevantSyms.foreach { sym =>
+ expandParentDocs(sym)
+ val usecases = docbase.docstring(sym).map(_.usecases).getOrElse(Nil)
+
+ usecases.foreach { usecase =>
+ enterSymbol(createSymbol(usecase.untpdCode))
+
+ typedStats(usecase.untpdCode :: Nil, owner) match {
+ case List(df: tpd.DefDef) => usecase.tpdCode = df
+ case _ => ctx.error("`@usecase` was not a valid definition", usecase.codePos)
+ }
+ }
+ }
+ }
+
+ private def expandParentDocs(sym: Symbol)(implicit ctx: Context): Unit =
+ ctx.docCtx.foreach { docCtx =>
+ docCtx.docstring(sym).foreach { cmt =>
+ def expandDoc(owner: Symbol): Unit = if (!cmt.isExpanded) {
+ val tplExp = docCtx.templateExpander
+ tplExp.defineVariables(sym)
+
+ val newCmt = cmt
+ .expand(tplExp.expandedDocComment(sym, owner, _))
+ .withUsecases
+
+ docCtx.addDocstring(sym, Some(newCmt))
+ }
+
+ if (sym ne NoSymbol) {
+ expandParentDocs(sym.owner)
+ expandDoc(sym.owner)
+ }
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
new file mode 100644
index 000000000..b5ace87d3
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala
@@ -0,0 +1,104 @@
+package dotty.tools
+package dotc
+package typer
+
+import dotty.tools.dotc.ast.Trees._
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.ast.untpd
+import dotty.tools.dotc.core.Constants.Constant
+import dotty.tools.dotc.core.Contexts.Context
+import dotty.tools.dotc.core.Names.Name
+import dotty.tools.dotc.core.StdNames._
+import dotty.tools.dotc.core.Types._
+import dotty.tools.dotc.core.Decorators._
+
+object Dynamic {
+ def isDynamicMethod(name: Name): Boolean =
+ name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
+}
+
+/** Translates selection that does not typecheck according to the scala.Dynamic rules:
+ * foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux)
+ * foo.bar = baz ~~> foo.updateDynamic("bar")(baz)
+ * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
+ * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
+ * foo.bar ~~> foo.selectDynamic(bar)
+ *
+ * The first matching rule of is applied.
+ */
+trait Dynamic { self: Typer with Applications =>
+ import Dynamic._
+ import tpd._
+
+ /** Translate selection that does not typecheck according to the normal rules into a applyDynamic/applyDynamicNamed.
+ * foo.bar(baz0, baz1, ...) ~~> foo.applyDynamic(bar)(baz0, baz1, ...)
+ * foo.bar[T0, ...](baz0, baz1, ...) ~~> foo.applyDynamic[T0, ...](bar)(baz0, baz1, ...)
+ * foo.bar(x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
+ * foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
+ */
+ def typedDynamicApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
+ def typedDynamicApply(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = {
+ def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
+ val args = tree.args
+ val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
+ if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
+ ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos)
+ tree.withType(ErrorType)
+ } else {
+ def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
+ def namedArgs = args.map {
+ case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
+ case arg => namedArgTuple("", arg)
+ }
+ val args1 = if (dynName == nme.applyDynamic) args else namedArgs
+ typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targs), args1), pt)
+ }
+ }
+
+ tree.fun match {
+ case Select(qual, name) if !isDynamicMethod(name) =>
+ typedDynamicApply(qual, name, Nil)
+ case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
+ typedDynamicApply(qual, name, targs)
+ case TypeApply(fun, targs) =>
+ typedDynamicApply(fun, nme.apply, targs)
+ case fun =>
+ typedDynamicApply(fun, nme.apply, Nil)
+ }
+ }
+
+ /** Translate selection that does not typecheck according to the normal rules into a selectDynamic.
+ * foo.bar ~~> foo.selectDynamic(bar)
+ * foo.bar[T0, ...] ~~> foo.selectDynamic[T0, ...](bar)
+ *
+ * Note: inner part of translation foo.bar(baz) = quux ~~> foo.selectDynamic(bar).update(baz, quux) is achieved
+ * through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)].
+ */
+ def typedDynamicSelect(tree: untpd.Select, targs: List[Tree], pt: Type)(implicit ctx: Context): Tree =
+ typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, targs), pt)
+
+ /** Translate selection that does not typecheck according to the normal rules into a updateDynamic.
+ * foo.bar = baz ~~> foo.updateDynamic(bar)(baz)
+ */
+ def typedDynamicAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = {
+ def typedDynamicAssign(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree =
+ typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targs), tree.rhs), pt)
+ tree.lhs match {
+ case Select(qual, name) if !isDynamicMethod(name) =>
+ typedDynamicAssign(qual, name, Nil)
+ case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
+ typedDynamicAssign(qual, name, targs)
+ case _ =>
+ ctx.error("reassignment to val", tree.pos)
+ tree.withType(ErrorType)
+ }
+ }
+
+ private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = {
+ val select = untpd.Select(qual, dynName)
+ val selectWithTypes =
+ if (targs.isEmpty) select
+ else untpd.TypeApply(select, targs)
+ untpd.Apply(selectWithTypes, Literal(Constant(name.toString)))
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
new file mode 100644
index 000000000..a18c83ff8
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -0,0 +1,153 @@
+package dotty.tools
+package dotc
+package typer
+
+import ast._
+import core._
+import Trees._
+import Types._, ProtoTypes._, Contexts._, Decorators._, Denotations._, Symbols._
+import Applications._, Implicits._, Flags._
+import util.Positions._
+import printing.{Showable, RefinedPrinter}
+import scala.collection.mutable
+import java.util.regex.Matcher.quoteReplacement
+import reporting.diagnostic.Message
+import reporting.diagnostic.messages._
+
+object ErrorReporting {
+
+ import tpd._
+
+ def errorTree(tree: untpd.Tree, msg: => Message)(implicit ctx: Context): tpd.Tree =
+ tree withType errorType(msg, tree.pos)
+
+ def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
+ ctx.error(msg, pos)
+ ErrorType
+ }
+
+ def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
+ val cycleSym = ex.denot.symbol
+ def errorMsg(msg: String, cx: Context): String =
+ if (cx.mode is Mode.InferringReturnType) {
+ cx.tree match {
+ case tree: untpd.ValOrDefDef =>
+ // Dotty deviation: Was Trees.ValOrDefDef[_], but this gives ValOrDefDef[Nothing] instead of
+ // ValOrDefDel[Null]. Scala handles it, but it looks accidental because bounds propagation
+ // fails if the parameter is invariant or cotravariant.
+ // See test pending/pos/boundspropagation.scala
+ val treeSym = ctx.symOfContextTree(tree)
+ if (treeSym.exists && treeSym.name == cycleSym.name && treeSym.owner == cycleSym.owner) {
+ val result = if (cycleSym is Method) " result" else ""
+ em"overloaded or recursive $cycleSym needs$result type"
+ }
+ else errorMsg(msg, cx.outer)
+ case _ =>
+ errorMsg(msg, cx.outer)
+ }
+ } else msg
+ errorMsg(ex.show, ctx)
+ }
+
+ def wrongNumberOfArgs(fntpe: Type, kind: String, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) =
+ errorType(WrongNumberOfArgs(fntpe, kind, expectedArgs, actual)(ctx), pos)
+
+ class Errors(implicit ctx: Context) {
+
+ /** An explanatory note to be added to error messages
+ * when there's a problem with abstract var defs */
+ def abstractVarMessage(sym: Symbol): String =
+ if (sym.underlyingSymbol.is(Mutable))
+ "\n(Note that variables need to be initialized to be defined)"
+ else ""
+
+ def expectedTypeStr(tp: Type): String = tp match {
+ case tp: PolyProto =>
+ em"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}"
+ case tp: FunProto =>
+ val result = tp.resultType match {
+ case _: WildcardType | _: IgnoredProto => ""
+ case tp => em" and expected result type $tp"
+ }
+ em"arguments (${tp.typedArgs.tpes}%, %)$result"
+ case _ =>
+ em"expected type $tp"
+ }
+
+ def anonymousTypeMemberStr(tpe: Type) = {
+ val kind = tpe match {
+ case _: TypeBounds => "type with bounds"
+ case _: PolyType | _: MethodType => "method"
+ case _ => "value of type"
+ }
+ em"$kind $tpe"
+ }
+
+ def overloadedAltsStr(alts: List[SingleDenotation]) =
+ em"overloaded alternatives of ${denotStr(alts.head)} with types\n" +
+ em" ${alts map (_.info)}%\n %"
+
+ def denotStr(denot: Denotation): String =
+ if (denot.isOverloaded) overloadedAltsStr(denot.alternatives)
+ else if (denot.symbol.exists) denot.symbol.showLocated
+ else anonymousTypeMemberStr(denot.info)
+
+ def refStr(tp: Type): String = tp match {
+ case tp: NamedType => denotStr(tp.denot)
+ case _ => anonymousTypeMemberStr(tp)
+ }
+
+ def exprStr(tree: Tree): String = refStr(tree.tpe)
+
+ def patternConstrStr(tree: Tree): String = ???
+
+ def typeMismatch(tree: Tree, pt: Type, implicitFailure: SearchFailure = NoImplicitMatches): Tree =
+ errorTree(tree, typeMismatchMsg(normalize(tree.tpe, pt), pt, implicitFailure.postscript))
+
+ /** A subtype log explaining why `found` does not conform to `expected` */
+ def whyNoMatchStr(found: Type, expected: Type) =
+ if (ctx.settings.explaintypes.value)
+ "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_))
+ else
+ ""
+
+ def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = {
+ // replace constrained polyparams and their typevars by their bounds where possible
+ object reported extends TypeMap {
+ def setVariance(v: Int) = variance = v
+ val constraint = ctx.typerState.constraint
+ def apply(tp: Type): Type = tp match {
+ case tp: PolyParam =>
+ constraint.entry(tp) match {
+ case bounds: TypeBounds =>
+ if (variance < 0) apply(constraint.fullUpperBound(tp))
+ else if (variance > 0) apply(constraint.fullLowerBound(tp))
+ else tp
+ case NoType => tp
+ case instType => apply(instType)
+ }
+ case tp: TypeVar => apply(tp.stripTypeVar)
+ case _ => mapOver(tp)
+ }
+ }
+ val found1 = reported(found)
+ reported.setVariance(-1)
+ val expected1 = reported(expected)
+ TypeMismatch(found1, expected1, whyNoMatchStr(found, expected), postScript)
+ }
+
+ /** Format `raw` implicitNotFound argument, replacing all
+ * occurrences of `${X}` where `X` is in `paramNames` with the
+ * corresponding shown type in `args`.
+ */
+ def implicitNotFoundString(raw: String, paramNames: List[String], args: List[Type]): String = {
+ def translate(name: String): Option[String] = {
+ val idx = paramNames.indexOf(name)
+ if (idx >= 0) Some(quoteReplacement(ex"${args(idx)}")) else None
+ }
+ """\$\{\w*\}""".r.replaceSomeIn(raw, m => translate(m.matched.drop(2).init))
+ }
+ }
+
+ def err(implicit ctx: Context): Errors = new Errors
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala
new file mode 100644
index 000000000..c390ae808
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala
@@ -0,0 +1,191 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast.{Trees, untpd, tpd, TreeInfo}
+import Contexts._
+import Types._
+import Flags._
+import NameOps._
+import Symbols._
+import Decorators._
+import Names._
+import StdNames._
+import Trees._
+import Inferencing._
+import util.Positions._
+import collection.mutable
+
+object EtaExpansion {
+
+ import tpd._
+
+ private def lift(defs: mutable.ListBuffer[Tree], expr: Tree, prefix: String = "")(implicit ctx: Context): Tree =
+ if (isPureExpr(expr)) expr
+ else {
+ val name = ctx.freshName(prefix).toTermName
+ val liftedType = fullyDefinedType(expr.tpe.widen, "lifted expression", expr.pos)
+ val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, liftedType, coord = positionCoord(expr.pos))
+ defs += ValDef(sym, expr)
+ ref(sym.valRef)
+ }
+
+ /** Lift out common part of lhs tree taking part in an operator assignment such as
+ *
+ * lhs += expr
+ */
+ def liftAssigned(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case Apply(MaybePoly(fn @ Select(pre, name), targs), args) =>
+ cpy.Apply(tree)(
+ cpy.Select(fn)(
+ lift(defs, pre), name).appliedToTypeTrees(targs),
+ liftArgs(defs, fn.tpe, args))
+ case Select(pre, name) =>
+ cpy.Select(tree)(lift(defs, pre), name)
+ case _ =>
+ tree
+ }
+
+ /** Lift a function argument, stripping any NamedArg wrapper */
+ def liftArg(defs: mutable.ListBuffer[Tree], arg: Tree, prefix: String = "")(implicit ctx: Context): Tree =
+ arg match {
+ case arg @ NamedArg(name, arg1) => cpy.NamedArg(arg)(name, lift(defs, arg1, prefix))
+ case arg => lift(defs, arg, prefix)
+ }
+
+ /** Lift arguments that are not-idempotent into ValDefs in buffer `defs`
+ * and replace by the idents of so created ValDefs.
+ */
+ def liftArgs(defs: mutable.ListBuffer[Tree], methRef: Type, args: List[Tree])(implicit ctx: Context) =
+ methRef.widen match {
+ case MethodType(paramNames, paramTypes) =>
+ (args, paramNames, paramTypes).zipped map { (arg, name, tp) =>
+ if (tp.isInstanceOf[ExprType]) arg
+ else liftArg(defs, arg, if (name contains '$') "" else name.toString + "$")
+ }
+ case _ =>
+ args map (liftArg(defs, _))
+ }
+
+ /** Lift out function prefix and all arguments from application
+ *
+ * pre.f(arg1, ..., argN) becomes
+ *
+ * val x0 = pre
+ * val x1 = arg1
+ * ...
+ * val xN = argN
+ * x0.f(x1, ..., xN)
+ *
+ * But leave idempotent expressions alone.
+ *
+ */
+ def liftApp(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case Apply(fn, args) =>
+ cpy.Apply(tree)(liftApp(defs, fn), liftArgs(defs, fn.tpe, args))
+ case TypeApply(fn, targs) =>
+ cpy.TypeApply(tree)(liftApp(defs, fn), targs)
+ case Select(pre, name) if isPureRef(tree) =>
+ cpy.Select(tree)(liftPrefix(defs, pre), name)
+ case Block(stats, expr) =>
+ liftApp(defs ++= stats, expr)
+ case New(tpt) =>
+ tree
+ case _ =>
+ lift(defs, tree)
+ }
+
+ /** Lift prefix `pre` of an application `pre.f(...)` to
+ *
+ * val x0 = pre
+ * x0.f(...)
+ *
+ * unless `pre` is a `New` or `pre` is idempotent.
+ */
+ def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case New(_) => tree
+ case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree)
+ }
+
+ /** Eta-expanding a tree means converting a method reference to a function value.
+ * @param tree The tree to expand
+ * @param mt The type of the method reference
+ * @param xarity The arity of the expected function type
+ * and assume the lifted application of `tree` (@see liftApp) is
+ *
+ * { val xs = es; expr }
+ *
+ * If xarity matches the number of parameters in `mt`, the eta-expansion is
+ *
+ * { val xs = es; (x1, ..., xn) => expr(x1, ..., xn) }
+ *
+ * Note that the function value's parameters are untyped, hence the type will
+ * be supplied by the environment (or if missing be supplied by the target
+ * method as a fallback). On the other hand, if `xarity` is different from
+ * the number of parameters in `mt`, then we cannot propagate parameter types
+ * from the expected type, and we fallback to using the method's original
+ * parameter types instead.
+ *
+ * In either case, the result is an untyped tree, with `es` and `expr` as typed splices.
+ */
+ def etaExpand(tree: Tree, mt: MethodType, xarity: Int)(implicit ctx: Context): untpd.Tree = {
+ import untpd._
+ assert(!ctx.isAfterTyper)
+ val defs = new mutable.ListBuffer[tpd.Tree]
+ val lifted: Tree = TypedSplice(liftApp(defs, tree))
+ val paramTypes: List[Tree] =
+ if (mt.paramTypes.length == xarity) mt.paramTypes map (_ => TypeTree())
+ else mt.paramTypes map TypeTree
+ val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) =>
+ ValDef(name, tpe, EmptyTree).withFlags(Synthetic | Param).withPos(tree.pos))
+ var ids: List[Tree] = mt.paramNames map (name => Ident(name).withPos(tree.pos))
+ if (mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam)
+ ids = ids.init :+ repeated(ids.last)
+ var body: Tree = Apply(lifted, ids)
+ mt.resultType match {
+ case rt: MethodType if !rt.isImplicit => body = PostfixOp(body, nme.WILDCARD)
+ case _ =>
+ }
+ val fn = untpd.Function(params, body)
+ if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn
+ }
+}
+
+ /** <p> not needed
+ * Expand partial function applications of type `type`.
+ * </p><pre>
+ * p.f(es_1)...(es_n)
+ * ==> {
+ * <b>private synthetic val</b> eta$f = p.f // if p is not stable
+ * ...
+ * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable
+ * ...
+ * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
+ * }</pre>
+ * <p>
+ * tree is already attributed
+ * </p>
+ def etaExpandUntyped(tree: Tree)(implicit ctx: Context): untpd.Tree = { // kept as a reserve for now
+ def expand(tree: Tree): untpd.Tree = tree.tpe match {
+ case mt @ MethodType(paramNames, paramTypes) if !mt.isImplicit =>
+ val paramsArgs: List[(untpd.ValDef, untpd.Tree)] =
+ (paramNames, paramTypes).zipped.map { (name, tp) =>
+ val droppedStarTpe = defn.underlyingOfRepeated(tp)
+ val param = ValDef(
+ Modifiers(Param), name,
+ untpd.TypedSplice(TypeTree(droppedStarTpe)), untpd.EmptyTree)
+ var arg: untpd.Tree = Ident(name)
+ if (defn.isRepeatedParam(tp))
+ arg = Typed(arg, Ident(tpnme.WILDCARD_STAR))
+ (param, arg)
+ }
+ val (params, args) = paramsArgs.unzip
+ untpd.Function(params, Apply(untpd.TypedSplice(tree), args))
+ }
+
+ val defs = new mutable.ListBuffer[Tree]
+ val tree1 = liftApp(defs, tree)
+ Block(defs.toList map untpd.TypedSplice, expand(tree1))
+ }
+ */
diff --git a/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
new file mode 100644
index 000000000..c444631ae
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/FrontEnd.scala
@@ -0,0 +1,83 @@
+package dotty.tools.dotc
+package typer
+
+import core._
+import Phases._
+import Contexts._
+import Symbols._
+import dotty.tools.dotc.parsing.JavaParsers.JavaParser
+import parsing.Parsers.Parser
+import config.Config
+import config.Printers.{typr, default}
+import util.Stats._
+import scala.util.control.NonFatal
+import ast.Trees._
+
+class FrontEnd extends Phase {
+
+ override def phaseName = "frontend"
+ override def isTyper = true
+ import ast.tpd
+
+ def monitor(doing: String)(body: => Unit)(implicit ctx: Context) =
+ try body
+ catch {
+ case NonFatal(ex) =>
+ ctx.echo(s"exception occurred while $doing ${ctx.compilationUnit}")
+ throw ex
+ }
+
+ def parse(implicit ctx: Context) = monitor("parsing") {
+ val unit = ctx.compilationUnit
+ unit.untpdTree =
+ if (unit.isJava) new JavaParser(unit.source).parse()
+ else new Parser(unit.source).parse()
+ val printer = if (ctx.settings.Xprint.value.contains("parser")) default else typr
+ printer.println("parsed:\n" + unit.untpdTree.show)
+ if (Config.checkPositions)
+ unit.untpdTree.checkPos(nonOverlapping = !unit.isJava && !ctx.reporter.hasErrors)
+ }
+
+ def enterSyms(implicit ctx: Context) = monitor("indexing") {
+ val unit = ctx.compilationUnit
+ ctx.typer.index(unit.untpdTree)
+ typr.println("entered: " + unit.source)
+ }
+
+ def typeCheck(implicit ctx: Context) = monitor("typechecking") {
+ val unit = ctx.compilationUnit
+ unit.tpdTree = ctx.typer.typedExpr(unit.untpdTree)
+ typr.println("typed: " + unit.source)
+ record("retained untyped trees", unit.untpdTree.treeSize)
+ record("retained typed trees after typer", unit.tpdTree.treeSize)
+ }
+
+ private def firstTopLevelDef(trees: List[tpd.Tree])(implicit ctx: Context): Symbol = trees match {
+ case PackageDef(_, defs) :: _ => firstTopLevelDef(defs)
+ case Import(_, _) :: defs => firstTopLevelDef(defs)
+ case (tree @ TypeDef(_, _)) :: _ => tree.symbol
+ case _ => NoSymbol
+ }
+
+ protected def discardAfterTyper(unit: CompilationUnit)(implicit ctx: Context) =
+ unit.isJava || firstTopLevelDef(unit.tpdTree :: Nil).isPrimitiveValueClass
+
+ override def runOn(units: List[CompilationUnit])(implicit ctx: Context): List[CompilationUnit] = {
+ val unitContexts = for (unit <- units) yield {
+ ctx.inform(s"compiling ${unit.source}")
+ ctx.fresh.setCompilationUnit(unit)
+ }
+ unitContexts foreach (parse(_))
+ record("parsedTrees", ast.Trees.ntrees)
+ unitContexts foreach (enterSyms(_))
+ unitContexts foreach (typeCheck(_))
+ record("total trees after typer", ast.Trees.ntrees)
+ unitContexts.map(_.compilationUnit).filterNot(discardAfterTyper)
+ }
+
+ override def run(implicit ctx: Context): Unit = {
+ parse
+ enterSyms
+ typeCheck
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
new file mode 100644
index 000000000..f3dceea71
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala
@@ -0,0 +1,844 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast.{Trees, untpd, tpd, TreeInfo}
+import util.Positions._
+import util.Stats.{track, record, monitored}
+import printing.Showable
+import Contexts._
+import Types._
+import Flags._
+import TypeErasure.{erasure, hasStableErasure}
+import Mode.ImplicitsEnabled
+import Denotations._
+import NameOps._
+import SymDenotations._
+import Symbols._
+import Types._
+import Decorators._
+import Names._
+import StdNames._
+import Constants._
+import Applications._
+import ProtoTypes._
+import ErrorReporting._
+import Inferencing.fullyDefinedType
+import Trees._
+import Hashable._
+import config.Config
+import config.Printers.{implicits, implicitsDetailed}
+import collection.mutable
+
+/** Implicit resolution */
+object Implicits {
+
+ /** A common base class of contextual implicits and of-type implicits which
+ * represents a set of implicit references.
+ */
+ abstract class ImplicitRefs(initctx: Context) {
+ implicit val ctx: Context =
+ if (initctx == NoContext) initctx else initctx retractMode Mode.ImplicitsEnabled
+
+ /** The implicit references */
+ def refs: List[TermRef]
+
+ /** Return those references in `refs` that are compatible with type `pt`. */
+ protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") {
+
+ def refMatches(ref: TermRef)(implicit ctx: Context) = /*ctx.traceIndented(i"refMatches $ref $pt")*/ {
+
+ def discardForView(tpw: Type, argType: Type): Boolean = tpw match {
+ case mt: MethodType =>
+ mt.isImplicit ||
+ mt.paramTypes.length != 1 ||
+ !(argType relaxed_<:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState)
+ case poly: PolyType =>
+ // We do not need to call ProtoTypes#constrained on `poly` because
+ // `refMatches` is always called with mode TypevarsMissContext enabled.
+ poly.resultType match {
+ case mt: MethodType =>
+ mt.isImplicit ||
+ mt.paramTypes.length != 1 ||
+ !(argType relaxed_<:< wildApprox(mt.paramTypes.head)(ctx.fresh.setExploreTyperState))
+ case rtp =>
+ discardForView(wildApprox(rtp), argType)
+ }
+ case tpw: TermRef =>
+ false // can't discard overloaded refs
+ case tpw =>
+ //if (ctx.typer.isApplicable(tp, argType :: Nil, resultType))
+ // println(i"??? $tp is applicable to $this / typeSymbol = ${tpw.typeSymbol}")
+ !tpw.derivesFrom(defn.FunctionClass(1)) ||
+ ref.symbol == defn.Predef_conforms //
+ // as an implicit conversion, Predef.$conforms is a no-op, so exclude it
+ }
+
+ def discardForValueType(tpw: Type): Boolean = tpw match {
+ case mt: MethodType => !mt.isImplicit
+ case mt: PolyType => discardForValueType(tpw.resultType)
+ case _ => false
+ }
+
+ def discard = pt match {
+ case pt: ViewProto => discardForView(ref.widen, pt.argType)
+ case _: ValueTypeOrProto => !defn.isFunctionType(pt) && discardForValueType(ref.widen)
+ case _ => false
+ }
+
+ (ref.symbol isAccessibleFrom ref.prefix) && {
+ if (discard) {
+ record("discarded eligible")
+ false
+ }
+ else NoViewsAllowed.isCompatible(normalize(ref, pt), pt)
+ }
+ }
+
+ if (refs.isEmpty) refs
+ else refs filter (refMatches(_)(ctx.fresh.addMode(Mode.TypevarsMissContext).setExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution
+ }
+ }
+
+ /** The implicit references coming from the implicit scope of a type.
+ * @param tp the type determining the implicit scope
+ * @param companionRefs the companion objects in the implicit scope.
+ */
+ class OfTypeImplicits(tp: Type, val companionRefs: TermRefSet)(initctx: Context) extends ImplicitRefs(initctx) {
+ assert(initctx.typer != null)
+ lazy val refs: List[TermRef] = {
+ val buf = new mutable.ListBuffer[TermRef]
+ for (companion <- companionRefs) buf ++= companion.implicitMembers
+ buf.toList
+ }
+
+ /** The implicit references that are eligible for expected type `tp` */
+ lazy val eligible: List[TermRef] =
+ /*>|>*/ track("eligible in tpe") /*<|<*/ {
+ /*>|>*/ ctx.traceIndented(i"eligible($tp), companions = ${companionRefs.toList}%, %", implicitsDetailed, show = true) /*<|<*/ {
+ if (refs.nonEmpty && monitored) record(s"check eligible refs in tpe", refs.length)
+ filterMatching(tp)
+ }
+ }
+
+ override def toString =
+ i"OfTypeImplicits($tp), companions = ${companionRefs.toList}%, %; refs = $refs%, %."
+ }
+
+ /** The implicit references coming from the context.
+ * @param refs the implicit references made visible by the current context.
+ * Note: The name of the reference might be different from the name of its symbol.
+ * In the case of a renaming import a => b, the name of the reference is the renamed
+ * name, b, whereas the name of the symbol is the original name, a.
+ * @param outerCtx the next outer context that makes visible further implicits
+ */
+ class ContextualImplicits(val refs: List[TermRef], val outerImplicits: ContextualImplicits)(initctx: Context) extends ImplicitRefs(initctx) {
+ private val eligibleCache = new mutable.AnyRefMap[Type, List[TermRef]]
+
+ /** The implicit references that are eligible for type `tp`. */
+ def eligible(tp: Type): List[TermRef] = /*>|>*/ track(s"eligible in ctx") /*<|<*/ {
+ if (tp.hash == NotCached) computeEligible(tp)
+ else eligibleCache get tp match {
+ case Some(eligibles) =>
+ def elided(ci: ContextualImplicits): Int = {
+ val n = ci.refs.length
+ if (ci.outerImplicits == NoContext.implicits) n
+ else n + elided(ci.outerImplicits)
+ }
+ if (monitored) record(s"elided eligible refs", elided(this))
+ eligibles
+ case None =>
+ val savedEphemeral = ctx.typerState.ephemeral
+ ctx.typerState.ephemeral = false
+ try {
+ val result = computeEligible(tp)
+ if (ctx.typerState.ephemeral) record("ephemeral cache miss: eligible")
+ else eligibleCache(tp) = result
+ result
+ }
+ finally ctx.typerState.ephemeral |= savedEphemeral
+ }
+ }
+
+ private def computeEligible(tp: Type): List[TermRef] = /*>|>*/ ctx.traceIndented(i"computeEligible $tp in $refs%, %", implicitsDetailed) /*<|<*/ {
+ if (monitored) record(s"check eligible refs in ctx", refs.length)
+ val ownEligible = filterMatching(tp)
+ if (outerImplicits == NoContext.implicits) ownEligible
+ else ownEligible ::: {
+ val shadowed = (ownEligible map (_.name)).toSet
+ outerImplicits.eligible(tp) filterNot (ref => shadowed contains ref.name)
+ }
+ }
+
+ override def toString = {
+ val own = s"(implicits: ${refs mkString ","})"
+ if (outerImplicits == NoContext.implicits) own else own + "\n " + outerImplicits
+ }
+
+ /** This context, or a copy, ensuring root import from symbol `root`
+ * is not present in outer implicits.
+ */
+ def exclude(root: Symbol): ContextualImplicits =
+ if (this == NoContext.implicits) this
+ else {
+ val outerExcluded = outerImplicits exclude root
+ if (ctx.importInfo.site.termSymbol == root) outerExcluded
+ else if (outerExcluded eq outerImplicits) this
+ else new ContextualImplicits(refs, outerExcluded)(ctx)
+ }
+ }
+
+ /** The result of an implicit search */
+ abstract class SearchResult
+
+ /** A successful search
+ * @param ref The implicit reference that succeeded
+ * @param tree The typed tree that needs to be inserted
+ * @param ctx The context after the implicit search
+ */
+ case class SearchSuccess(tree: tpd.Tree, ref: TermRef, tstate: TyperState) extends SearchResult {
+ override def toString = s"SearchSuccess($tree, $ref)"
+ }
+
+ /** A failed search */
+ abstract class SearchFailure extends SearchResult {
+ /** A note describing the failure in more detail - this
+ * is either empty or starts with a '\n'
+ */
+ def postscript(implicit ctx: Context): String = ""
+ }
+
+ /** A "no matching implicit found" failure */
+ case object NoImplicitMatches extends SearchFailure
+
+ /** A search failure that can show information about the cause */
+ abstract class ExplainedSearchFailure extends SearchFailure {
+ protected def pt: Type
+ protected def argument: tpd.Tree
+ protected def qualify(implicit ctx: Context) =
+ if (argument.isEmpty) em"match type $pt"
+ else em"convert from ${argument.tpe} to $pt"
+
+ /** An explanation of the cause of the failure as a string */
+ def explanation(implicit ctx: Context): String
+ }
+
+ /** An ambiguous implicits failure */
+ class AmbiguousImplicits(alt1: TermRef, alt2: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
+ def explanation(implicit ctx: Context): String =
+ em"both ${err.refStr(alt1)} and ${err.refStr(alt2)} $qualify"
+ override def postscript(implicit ctx: Context) =
+ "\nNote that implicit conversions cannot be applied because they are ambiguous;" +
+ "\n " + explanation
+ }
+
+ class NonMatchingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
+ def explanation(implicit ctx: Context): String =
+ em"${err.refStr(ref)} does not $qualify"
+ }
+
+ class ShadowedImplicit(ref: TermRef, shadowing: Type, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
+ def explanation(implicit ctx: Context): String =
+ em"${err.refStr(ref)} does $qualify but is shadowed by ${err.refStr(shadowing)}"
+ }
+
+ class DivergingImplicit(ref: TermRef, val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
+ def explanation(implicit ctx: Context): String =
+ em"${err.refStr(ref)} produces a diverging implicit search when trying to $qualify"
+ }
+
+ class FailedImplicit(failures: List[ExplainedSearchFailure], val pt: Type, val argument: tpd.Tree) extends ExplainedSearchFailure {
+ def explanation(implicit ctx: Context): String =
+ if (failures.isEmpty) s" No implicit candidates were found that $qualify"
+ else " " + (failures map (_.explanation) mkString "\n ")
+ override def postscript(implicit ctx: Context): String =
+ i"""
+ |Implicit search failure summary:
+ |$explanation"""
+ }
+}
+
+import Implicits._
+
+/** Info relating to implicits that is kept for one run */
+trait ImplicitRunInfo { self: RunInfo =>
+
+ private val implicitScopeCache = mutable.AnyRefMap[Type, OfTypeImplicits]()
+
+ /** The implicit scope of a type `tp`
+ * @param liftingCtx A context to be used when computing the class symbols of
+ * a type. Types may contain type variables with their instances
+ * recorded in the current context. To find out the instance of
+ * a type variable, we need the current context, the current
+ * runinfo context does not do.
+ */
+ def implicitScope(tp: Type, liftingCtx: Context): OfTypeImplicits = {
+
+ val seen: mutable.Set[Type] = mutable.Set()
+
+ /** Replace every typeref that does not refer to a class by a conjunction of class types
+ * that has the same implicit scope as the original typeref. The motivation for applying
+ * this map is that it reduces the total number of types for which we need to
+ * compute and cache the implicit scope; all variations wrt type parameters or
+ * abstract types are eliminated.
+ */
+ object liftToClasses extends TypeMap {
+ override implicit protected val ctx: Context = liftingCtx
+ override def stopAtStatic = true
+ def apply(tp: Type) = tp match {
+ case tp: TypeRef if tp.symbol.isAbstractOrAliasType =>
+ val pre = tp.prefix
+ def joinClass(tp: Type, cls: ClassSymbol) =
+ AndType.make(tp, cls.typeRef.asSeenFrom(pre, cls.owner))
+ val lead = if (tp.prefix eq NoPrefix) defn.AnyType else apply(tp.prefix)
+ (lead /: tp.classSymbols)(joinClass)
+ case tp: TypeVar =>
+ apply(tp.underlying)
+ case tp: HKApply =>
+ def applyArg(arg: Type) = arg match {
+ case TypeBounds(lo, hi) => AndType.make(lo, hi)
+ case _: WildcardType => defn.AnyType
+ case _ => arg
+ }
+ (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg)))
+ case tp: PolyType =>
+ apply(tp.resType)
+ case _ =>
+ mapOver(tp)
+ }
+ }
+
+ def iscopeRefs(tp: Type): TermRefSet =
+ if (seen contains tp) EmptyTermRefSet
+ else {
+ seen += tp
+ iscope(tp).companionRefs
+ }
+
+ // todo: compute implicits directly, without going via companionRefs?
+ def collectCompanions(tp: Type): TermRefSet = track("computeImplicitScope") {
+ ctx.traceIndented(i"collectCompanions($tp)", implicits) {
+ val comps = new TermRefSet
+ tp match {
+ case tp: NamedType =>
+ val pre = tp.prefix
+ comps ++= iscopeRefs(pre)
+ def addClassScope(cls: ClassSymbol): Unit = {
+ def addRef(companion: TermRef): Unit = {
+ val compSym = companion.symbol
+ if (compSym is Package)
+ addRef(TermRef.withSig(companion, nme.PACKAGE, Signature.NotAMethod))
+ else if (compSym.exists)
+ comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef]
+ }
+ def addParentScope(parent: TypeRef): Unit = {
+ iscopeRefs(parent) foreach addRef
+ for (param <- parent.typeParamSymbols)
+ comps ++= iscopeRefs(tp.member(param.name).info)
+ }
+ val companion = cls.companionModule
+ if (companion.exists) addRef(companion.valRef)
+ cls.classParents foreach addParentScope
+ }
+ tp.classSymbols(liftingCtx) foreach addClassScope
+ case _ =>
+ // We exclude lower bounds to conform to SLS 7.2:
+ // "The parts of a type T are: [...] if T is an abstract type, the parts of its upper bound"
+ for (part <- tp.namedPartsWith(_.isType, excludeLowerBounds = true))
+ comps ++= iscopeRefs(part)
+ }
+ comps
+ }
+ }
+
+ /** The implicit scope of type `tp`
+ * @param isLifted Type `tp` is the result of a `liftToClasses` application
+ */
+ def iscope(tp: Type, isLifted: Boolean = false): OfTypeImplicits = {
+ def computeIScope(cacheResult: Boolean) = {
+ val savedEphemeral = ctx.typerState.ephemeral
+ ctx.typerState.ephemeral = false
+ try {
+ val liftedTp = if (isLifted) tp else liftToClasses(tp)
+ val refs =
+ if (liftedTp ne tp)
+ iscope(liftedTp, isLifted = true).companionRefs
+ else
+ collectCompanions(tp)
+ val result = new OfTypeImplicits(tp, refs)(ctx)
+ if (ctx.typerState.ephemeral) record("ephemeral cache miss: implicitScope")
+ else if (cacheResult) implicitScopeCache(tp) = result
+ result
+ }
+ finally ctx.typerState.ephemeral |= savedEphemeral
+ }
+
+ if (tp.hash == NotCached || !Config.cacheImplicitScopes)
+ computeIScope(cacheResult = false)
+ else implicitScopeCache get tp match {
+ case Some(is) => is
+ case None =>
+ // Implicit scopes are tricky to cache because of loops. For example
+ // in `tests/pos/implicit-scope-loop.scala`, the scope of B contains
+ // the scope of A which contains the scope of B. We break the loop
+ // by returning EmptyTermRefSet in `collectCompanions` for types
+ // that we have already seen, but this means that we cannot cache
+ // the computed scope of A, it is incomplete.
+ // Keeping track of exactly where these loops happen would require a
+ // lot of book-keeping, instead we choose to be conservative and only
+ // cache scopes before any type has been seen. This is unfortunate
+ // because loops are very common for types in scala.collection.
+ computeIScope(cacheResult = seen.isEmpty)
+ }
+ }
+
+ iscope(tp)
+ }
+
+ /** A map that counts the number of times an implicit ref was picked */
+ val useCount = new mutable.HashMap[TermRef, Int] {
+ override def default(key: TermRef) = 0
+ }
+
+ def clear() = implicitScopeCache.clear()
+}
+
+/** The implicit resolution part of type checking */
+trait Implicits { self: Typer =>
+
+ import tpd._
+
+ override def viewExists(from: Type, to: Type)(implicit ctx: Context): Boolean = (
+ !from.isError
+ && !to.isError
+ && !ctx.isAfterTyper
+ && (ctx.mode is Mode.ImplicitsEnabled)
+ && from.isValueType
+ && ( from.isValueSubType(to)
+ || inferView(dummyTreeOfType(from), to)
+ (ctx.fresh.addMode(Mode.ImplicitExploration).setExploreTyperState)
+ .isInstanceOf[SearchSuccess]
+ )
+ )
+
+ /** Find an implicit conversion to apply to given tree `from` so that the
+ * result is compatible with type `to`.
+ */
+ def inferView(from: Tree, to: Type)(implicit ctx: Context): SearchResult = track("inferView") {
+ if ( (to isRef defn.AnyClass)
+ || (to isRef defn.ObjectClass)
+ || (to isRef defn.UnitClass)
+ || (from.tpe isRef defn.NothingClass)
+ || (from.tpe isRef defn.NullClass)
+ || (from.tpe eq NoPrefix)) NoImplicitMatches
+ else
+ try inferImplicit(to.stripTypeVar.widenExpr, from, from.pos)
+ catch {
+ case ex: AssertionError =>
+ implicits.println(s"view $from ==> $to")
+ implicits.println(ctx.typerState.constraint.show)
+ implicits.println(TypeComparer.explained(implicit ctx => from.tpe <:< to))
+ throw ex
+ }
+ }
+
+ /** Find an implicit argument for parameter `formal`.
+ * @param error An error handler that gets an error message parameter
+ * which is itself parameterized by another string,
+ * indicating where the implicit parameter is needed
+ */
+ def inferImplicitArg(formal: Type, error: (String => String) => Unit, pos: Position)(implicit ctx: Context): Tree =
+ inferImplicit(formal, EmptyTree, pos) match {
+ case SearchSuccess(arg, _, _) =>
+ arg
+ case ambi: AmbiguousImplicits =>
+ error(where => s"ambiguous implicits: ${ambi.explanation} of $where")
+ EmptyTree
+ case failure: SearchFailure =>
+ val arg = synthesizedClassTag(formal, pos)
+ if (!arg.isEmpty) arg
+ else {
+ var msgFn = (where: String) =>
+ em"no implicit argument of type $formal found for $where" + failure.postscript
+ for {
+ notFound <- formal.typeSymbol.getAnnotation(defn.ImplicitNotFoundAnnot)
+ Trees.Literal(Constant(raw: String)) <- notFound.argument(0)
+ } {
+ msgFn = where =>
+ err.implicitNotFoundString(
+ raw,
+ formal.typeSymbol.typeParams.map(_.name.unexpandedName.toString),
+ formal.argInfos)
+ }
+ error(msgFn)
+ EmptyTree
+ }
+ }
+
+ /** If `formal` is of the form ClassTag[T], where `T` is a class type,
+ * synthesize a class tag for `T`.
+ */
+ def synthesizedClassTag(formal: Type, pos: Position)(implicit ctx: Context): Tree = {
+ if (formal.isRef(defn.ClassTagClass))
+ formal.argTypes match {
+ case arg :: Nil =>
+ val tp = fullyDefinedType(arg, "ClassTag argument", pos)
+ if (hasStableErasure(tp))
+ return ref(defn.ClassTagModule)
+ .select(nme.apply)
+ .appliedToType(tp)
+ .appliedTo(clsOf(erasure(tp)))
+ .withPos(pos)
+ case _ =>
+ }
+ EmptyTree
+ }
+
+ private def assumedCanEqual(ltp: Type, rtp: Type)(implicit ctx: Context) = {
+ val lift = new TypeMap {
+ def apply(t: Type) = t match {
+ case t: TypeRef =>
+ t.info match {
+ case TypeBounds(lo, hi) if lo ne hi => hi
+ case _ => t
+ }
+ case _ =>
+ if (variance > 0) mapOver(t) else t
+ }
+ }
+ ltp.isError || rtp.isError || ltp <:< lift(rtp) || rtp <:< lift(ltp)
+ }
+
+ /** Check that equality tests between types `ltp` and `rtp` make sense */
+ def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit =
+ if (!ctx.isAfterTyper && !assumedCanEqual(ltp, rtp)) {
+ val res = inferImplicitArg(
+ defn.EqType.appliedTo(ltp, rtp), msgFun => ctx.error(msgFun(""), pos), pos)
+ implicits.println(i"Eq witness found: $res: ${res.tpe}")
+ }
+
+ /** Find an implicit parameter or conversion.
+ * @param pt The expected type of the parameter or conversion.
+ * @param argument If an implicit conversion is searched, the argument to which
+ * it should be applied, EmptyTree otherwise.
+ * @param pos The position where errors should be reported.
+ * !!! todo: catch potential cycles
+ */
+ def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") {
+ assert(!ctx.isAfterTyper,
+ if (argument.isEmpty) i"missing implicit parameter of type $pt after typer"
+ else i"type error: ${argument.tpe} does not conform to $pt${err.whyNoMatchStr(argument.tpe, pt)}")
+ val prevConstr = ctx.typerState.constraint
+ ctx.traceIndented(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) {
+ assert(!pt.isInstanceOf[ExprType])
+ val isearch =
+ if (ctx.settings.explaintypes.value) new ExplainedImplicitSearch(pt, argument, pos)
+ else new ImplicitSearch(pt, argument, pos)
+ val result = isearch.bestImplicit
+ result match {
+ case result: SearchSuccess =>
+ result.tstate.commit()
+ result
+ case result: AmbiguousImplicits =>
+ val deepPt = pt.deepenProto
+ if (deepPt ne pt) inferImplicit(deepPt, argument, pos)
+ else if (ctx.scala2Mode && !ctx.mode.is(Mode.OldOverloadingResolution)) {
+ inferImplicit(pt, argument, pos)(ctx.addMode(Mode.OldOverloadingResolution)) match {
+ case altResult: SearchSuccess =>
+ ctx.migrationWarning(
+ s"According to new implicit resolution rules, this will be ambiguous:\n ${result.explanation}",
+ pos)
+ altResult
+ case _ =>
+ result
+ }
+ }
+ else result
+ case _ =>
+ assert(prevConstr eq ctx.typerState.constraint)
+ result
+ }
+ }
+ }
+
+ /** An implicit search; parameters as in `inferImplicit` */
+ class ImplicitSearch(protected val pt: Type, protected val argument: Tree, pos: Position)(implicit ctx: Context) {
+
+ private def nestedContext = ctx.fresh.setMode(ctx.mode &~ Mode.ImplicitsEnabled)
+
+ private def implicitProto(resultType: Type, f: Type => Type) =
+ if (argument.isEmpty) f(resultType) else ViewProto(f(argument.tpe.widen), f(resultType))
+ // Not clear whether we need to drop the `.widen` here. All tests pass with it in place, though.
+
+ assert(argument.isEmpty || argument.tpe.isValueType || argument.tpe.isInstanceOf[ExprType],
+ em"found: $argument: ${argument.tpe}, expected: $pt")
+
+ /** The expected type for the searched implicit */
+ lazy val fullProto = implicitProto(pt, identity)
+
+ lazy val funProto = fullProto match {
+ case proto: ViewProto =>
+ FunProto(untpd.TypedSplice(dummyTreeOfType(proto.argType)) :: Nil, proto.resultType, self)
+ case proto => proto
+ }
+
+ /** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */
+ val wildProto = implicitProto(pt, wildApprox(_))
+
+ /** Search failures; overridden in ExplainedImplicitSearch */
+ protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches
+ protected def divergingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches
+ protected def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure = NoImplicitMatches
+ protected def failedSearch: SearchFailure = NoImplicitMatches
+
+ /** Search a list of eligible implicit references */
+ def searchImplicits(eligible: List[TermRef], contextual: Boolean): SearchResult = {
+ val constr = ctx.typerState.constraint
+
+ /** Try to typecheck an implicit reference */
+ def typedImplicit(ref: TermRef)(implicit ctx: Context): SearchResult = track("typedImplicit") { ctx.traceIndented(i"typed implicit $ref, pt = $pt, implicitsEnabled == ${ctx.mode is ImplicitsEnabled}", implicits, show = true) {
+ assert(constr eq ctx.typerState.constraint)
+ var generated: Tree = tpd.ref(ref).withPos(pos)
+ if (!argument.isEmpty)
+ generated = typedUnadapted(
+ untpd.Apply(untpd.TypedSplice(generated), untpd.TypedSplice(argument) :: Nil),
+ pt)
+ val generated1 = adapt(generated, pt)
+ lazy val shadowing =
+ typed(untpd.Ident(ref.name) withPos pos.toSynthetic, funProto)(
+ nestedContext.addMode(Mode.ImplicitShadowing).setExploreTyperState)
+ def refMatches(shadowing: Tree): Boolean =
+ ref.symbol == closureBody(shadowing).symbol || {
+ shadowing match {
+ case Trees.Select(qual, nme.apply) => refMatches(qual)
+ case _ => false
+ }
+ }
+ // Does there exist an implicit value of type `Eq[tp, tp]`?
+ def hasEq(tp: Type): Boolean =
+ new ImplicitSearch(defn.EqType.appliedTo(tp, tp), EmptyTree, pos).bestImplicit match {
+ case result: SearchSuccess => result.ref.symbol != defn.Predef_eqAny
+ case result: AmbiguousImplicits => true
+ case _ => false
+ }
+ def validEqAnyArgs(tp1: Type, tp2: Type) = {
+ List(tp1, tp2).foreach(fullyDefinedType(_, "eqAny argument", pos))
+ assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) ||
+ { implicits.println(i"invalid eqAny[$tp1, $tp2]"); false }
+ }
+ if (ctx.reporter.hasErrors)
+ nonMatchingImplicit(ref)
+ else if (contextual && !ctx.mode.is(Mode.ImplicitShadowing) &&
+ !shadowing.tpe.isError && !refMatches(shadowing)) {
+ implicits.println(i"SHADOWING $ref in ${ref.termSymbol.owner} is shadowed by $shadowing in ${shadowing.symbol.owner}")
+ shadowedImplicit(ref, methPart(shadowing).tpe)
+ }
+ else generated1 match {
+ case TypeApply(fn, targs @ (arg1 :: arg2 :: Nil))
+ if fn.symbol == defn.Predef_eqAny && !validEqAnyArgs(arg1.tpe, arg2.tpe) =>
+ nonMatchingImplicit(ref)
+ case _ =>
+ SearchSuccess(generated1, ref, ctx.typerState)
+ }
+ }}
+
+ /** Given a list of implicit references, produce a list of all implicit search successes,
+ * where the first is supposed to be the best one.
+ * @param pending The list of implicit references that remain to be investigated
+ * @param acc An accumulator of successful matches found so far.
+ */
+ def rankImplicits(pending: List[TermRef], acc: List[SearchSuccess]): List[SearchSuccess] = pending match {
+ case ref :: pending1 =>
+ val history = ctx.searchHistory nest wildProto
+ val result =
+ if (history eq ctx.searchHistory) divergingImplicit(ref)
+ else typedImplicit(ref)(nestedContext.setNewTyperState.setSearchHistory(history))
+ result match {
+ case fail: SearchFailure =>
+ rankImplicits(pending1, acc)
+ case best: SearchSuccess =>
+ if (ctx.mode.is(Mode.ImplicitExploration)) best :: Nil
+ else {
+ val newPending = pending1 filter (isAsGood(_, best.ref)(nestedContext.setExploreTyperState))
+ rankImplicits(newPending, best :: acc)
+ }
+ }
+ case nil => acc
+ }
+
+ /** If the (result types of) the expected type, and both alternatives
+ * are all numeric value types, return the alternative which has
+ * the smaller numeric subtype as result type, if it exists.
+ * (This alternative is then discarded).
+ */
+ def numericValueTieBreak(alt1: SearchSuccess, alt2: SearchSuccess): SearchResult = {
+ def isNumeric(tp: Type) = tp.typeSymbol.isNumericValueClass
+ def isProperSubType(tp1: Type, tp2: Type) =
+ tp1.isValueSubType(tp2) && !tp2.isValueSubType(tp1)
+ val rpt = pt.resultType
+ val rt1 = alt1.ref.widen.resultType
+ val rt2 = alt2.ref.widen.resultType
+ if (isNumeric(rpt) && isNumeric(rt1) && isNumeric(rt2))
+ if (isProperSubType(rt1, rt2)) alt1
+ else if (isProperSubType(rt2, rt1)) alt2
+ else NoImplicitMatches
+ else NoImplicitMatches
+ }
+
+ /** Convert a (possibly empty) list of search successes into a single search result */
+ def condense(hits: List[SearchSuccess]): SearchResult = hits match {
+ case best :: alts =>
+ alts find (alt => isAsGood(alt.ref, best.ref)(ctx.fresh.setExploreTyperState)) match {
+ case Some(alt) =>
+ /* !!! DEBUG
+ println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}")
+ isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState)
+ */
+ numericValueTieBreak(best, alt) match {
+ case eliminated: SearchSuccess => condense(hits.filter(_ ne eliminated))
+ case _ => new AmbiguousImplicits(best.ref, alt.ref, pt, argument)
+ }
+ case None =>
+ ctx.runInfo.useCount(best.ref) += 1
+ best
+ }
+ case Nil =>
+ failedSearch
+ }
+
+ /** Sort list of implicit references according to their popularity
+ * (# of times each was picked in current run).
+ */
+ def sort(eligible: List[TermRef]) = eligible match {
+ case Nil => eligible
+ case e1 :: Nil => eligible
+ case e1 :: e2 :: Nil =>
+ if (ctx.runInfo.useCount(e1) < ctx.runInfo.useCount(e2)) e2 :: e1 :: Nil
+ else eligible
+ case _ => eligible.sortBy(-ctx.runInfo.useCount(_))
+ }
+
+ condense(rankImplicits(sort(eligible), Nil))
+ }
+
+ /** Find a unique best implicit reference */
+ def bestImplicit: SearchResult = {
+ searchImplicits(ctx.implicits.eligible(wildProto), contextual = true) match {
+ case result: SearchSuccess => result
+ case result: AmbiguousImplicits => result
+ case result: SearchFailure =>
+ searchImplicits(implicitScope(wildProto).eligible, contextual = false)
+ }
+ }
+
+ def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx)
+ }
+
+ final class ExplainedImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context)
+ extends ImplicitSearch(pt, argument, pos) {
+ private var myFailures = new mutable.ListBuffer[ExplainedSearchFailure]
+ private def record(fail: ExplainedSearchFailure) = {
+ myFailures += fail
+ fail
+ }
+ def failures = myFailures.toList
+ override def nonMatchingImplicit(ref: TermRef) =
+ record(new NonMatchingImplicit(ref, pt, argument))
+ override def divergingImplicit(ref: TermRef) =
+ record(new DivergingImplicit(ref, pt, argument))
+ override def shadowedImplicit(ref: TermRef, shadowing: Type): SearchFailure =
+ record(new ShadowedImplicit(ref, shadowing, pt, argument))
+ override def failedSearch: SearchFailure = {
+ //println(s"wildProto = $wildProto")
+ //println(s"implicit scope = ${implicitScope(wildProto).companionRefs}")
+ new FailedImplicit(failures, pt, argument)
+ }
+ }
+}
+
+/** Records the history of currently open implicit searches
+ * @param searchDepth The number of open searches.
+ * @param seen A map that records for each class symbol of a type
+ * that's currently searched for the complexity of the
+ * type that is searched for (wrt `typeSize`). The map
+ * is populated only once `searchDepth` is greater than
+ * the threshold given in the `XminImplicitSearchDepth` setting.
+ */
+class SearchHistory(val searchDepth: Int, val seen: Map[ClassSymbol, Int]) {
+
+ /** The number of RefinementTypes in this type, after all aliases are expanded */
+ private def typeSize(tp: Type)(implicit ctx: Context): Int = {
+ val accu = new TypeAccumulator[Int] {
+ def apply(n: Int, tp: Type): Int = tp match {
+ case tp: RefinedType =>
+ foldOver(n + 1, tp)
+ case tp: TypeRef if tp.info.isAlias =>
+ apply(n, tp.superType)
+ case _ =>
+ foldOver(n, tp)
+ }
+ }
+ accu.apply(0, tp)
+ }
+
+ /** Check for possible divergence. If one is detected return the current search history
+ * (this will be used as a criterion to abandon the implicit search in rankImplicits).
+ * If no divergence is detected, produce a new search history nested in the current one
+ * which records that we are now also looking for type `proto`.
+ *
+ * As long as `searchDepth` is lower than the `XminImplicitSearchDepth` value
+ * in settings, a new history is always produced, so the implicit search is always
+ * undertaken. If `searchDepth` matches or exceeds the `XminImplicitSearchDepth` value,
+ * we test that the new search is for a class that is either not yet in the set of
+ * `seen` classes, or the complexity of the type `proto` being searched for is strictly
+ * lower than the complexity of the type that was previously encountered and that had
+ * the same class symbol as `proto`. A possible divergence is detected if that test fails.
+ */
+ def nest(proto: Type)(implicit ctx: Context): SearchHistory = {
+ if (searchDepth < ctx.settings.XminImplicitSearchDepth.value)
+ new SearchHistory(searchDepth + 1, seen)
+ else {
+ val size = typeSize(proto)
+ def updateMap(csyms: List[ClassSymbol], seen: Map[ClassSymbol, Int]): SearchHistory = csyms match {
+ case csym :: csyms1 =>
+ seen get csym match {
+ // proto complexity is >= than the last time it was seen → diverge
+ case Some(prevSize) if size >= prevSize => this
+ case _ => updateMap(csyms1, seen.updated(csym, size))
+ }
+ case _ =>
+ new SearchHistory(searchDepth + 1, seen)
+ }
+ if (proto.classSymbols.isEmpty) this
+ else updateMap(proto.classSymbols, seen)
+ }
+ }
+}
+
+/** A set of term references where equality is =:= */
+class TermRefSet(implicit ctx: Context) extends mutable.Traversable[TermRef] {
+ import collection.JavaConverters._
+ private val elems = (new java.util.LinkedHashMap[TermSymbol, List[Type]]).asScala
+
+ def += (ref: TermRef): Unit = {
+ val pre = ref.prefix
+ val sym = ref.symbol.asTerm
+ elems get sym match {
+ case Some(prefixes) =>
+ if (!(prefixes exists (_ =:= pre))) elems(sym) = pre :: prefixes
+ case None =>
+ elems(sym) = pre :: Nil
+ }
+ }
+
+ def ++= (refs: TraversableOnce[TermRef]): Unit =
+ refs foreach +=
+
+ override def foreach[U](f: TermRef => U): Unit =
+ for (sym <- elems.keysIterator)
+ for (pre <- elems(sym))
+ f(TermRef(pre, sym))
+}
+
+@sharable object EmptyTermRefSet extends TermRefSet()(NoContext)
diff --git a/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
new file mode 100644
index 000000000..3aa289181
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/ImportInfo.scala
@@ -0,0 +1,117 @@
+package dotty.tools
+package dotc
+package typer
+
+import ast.{tpd, untpd}
+import ast.Trees._
+import core._
+import util.SimpleMap
+import Symbols._, Names._, Denotations._, Types._, Contexts._, StdNames._, Flags._
+import Decorators.StringInterpolators
+
+object ImportInfo {
+ /** The import info for a root import from given symbol `sym` */
+ def rootImport(refFn: () => TermRef)(implicit ctx: Context) = {
+ val selectors = untpd.Ident(nme.WILDCARD) :: Nil
+ def expr = tpd.Ident(refFn())
+ def imp = tpd.Import(expr, selectors)
+ new ImportInfo(imp.symbol, selectors, isRootImport = true)
+ }
+}
+
+/** Info relating to an import clause
+ * @param sym The import symbol defined by the clause
+ * @param selectors The selector clauses
+ * @param rootImport true if this is one of the implicit imports of scala, java.lang
+ * or Predef in the start context, false otherwise.
+ */
+class ImportInfo(symf: => Symbol, val selectors: List[untpd.Tree], val isRootImport: Boolean = false)(implicit ctx: Context) {
+
+ lazy val sym = symf
+
+ /** The (TermRef) type of the qualifier of the import clause */
+ def site(implicit ctx: Context): Type = {
+ val ImportType(expr) = sym.info
+ expr.tpe
+ }
+
+ /** The names that are excluded from any wildcard import */
+ def excluded: Set[TermName] = { ensureInitialized(); myExcluded }
+
+ /** A mapping from renamed to original names */
+ def reverseMapping: SimpleMap[TermName, TermName] = { ensureInitialized(); myMapped }
+
+ /** The original names imported by-name before renaming */
+ def originals: Set[TermName] = { ensureInitialized(); myOriginals }
+
+ /** Does the import clause end with wildcard? */
+ def isWildcardImport = { ensureInitialized(); myWildcardImport }
+
+ private var myExcluded: Set[TermName] = null
+ private var myMapped: SimpleMap[TermName, TermName] = null
+ private var myOriginals: Set[TermName] = null
+ private var myWildcardImport: Boolean = false
+
+ /** Compute info relating to the selector list */
+ private def ensureInitialized(): Unit = if (myExcluded == null) {
+ myExcluded = Set()
+ myMapped = SimpleMap.Empty
+ myOriginals = Set()
+ def recur(sels: List[untpd.Tree]): Unit = sels match {
+ case sel :: sels1 =>
+ sel match {
+ case Thicket(Ident(name: TermName) :: Ident(nme.WILDCARD) :: Nil) =>
+ myExcluded += name
+ case Thicket(Ident(from: TermName) :: Ident(to: TermName) :: Nil) =>
+ myMapped = myMapped.updated(to, from)
+ myExcluded += from
+ myOriginals += from
+ case Ident(nme.WILDCARD) =>
+ myWildcardImport = true
+ case Ident(name: TermName) =>
+ myMapped = myMapped.updated(name, name)
+ myOriginals += name
+ }
+ recur(sels1)
+ case nil =>
+ }
+ recur(selectors)
+ }
+
+ /** The implicit references imported by this import clause */
+ def importedImplicits: List[TermRef] = {
+ val pre = site
+ if (isWildcardImport) {
+ val refs = pre.implicitMembers
+ if (excluded.isEmpty) refs
+ else refs filterNot (ref => excluded contains ref.name.toTermName)
+ } else
+ for {
+ renamed <- reverseMapping.keys
+ denot <- pre.member(reverseMapping(renamed)).altsWith(_ is Implicit)
+ } yield TermRef.withSigAndDenot(pre, renamed, denot.signature, denot)
+ }
+
+ /** The root import symbol hidden by this symbol, or NoSymbol if no such symbol is hidden.
+ * Note: this computation needs to work even for un-initialized import infos, and
+ * is not allowed to force initialization.
+ */
+ lazy val hiddenRoot: Symbol = {
+ val sym = site.termSymbol
+ def hasMaskingSelector = selectors exists {
+ case Thicket(_ :: Ident(nme.WILDCARD) :: Nil) => true
+ case _ => false
+ }
+ if ((defn.RootImportTypes exists (_.symbol == sym)) && hasMaskingSelector) sym else NoSymbol
+ }
+
+ override def toString = {
+ val siteStr = site.show
+ val exprStr = if (siteStr endsWith ".type") siteStr dropRight 5 else siteStr
+ val selectorStr = selectors match {
+ case Ident(name) :: Nil => name.show
+ case _ => "{...}"
+ }
+ i"import $exprStr.$selectorStr"
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala
new file mode 100644
index 000000000..aede4974a
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -0,0 +1,362 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
+import Trees._
+import Constants._
+import Scopes._
+import ProtoTypes._
+import annotation.unchecked
+import util.Positions._
+import util.{Stats, SimpleMap}
+import util.common._
+import Decorators._
+import Uniques._
+import config.Printers.{typr, constr}
+import annotation.tailrec
+import reporting._
+import collection.mutable
+
+object Inferencing {
+
+ import tpd._
+
+ /** Is type fully defined, meaning the type does not contain wildcard types
+ * or uninstantiated type variables. As a side effect, this will minimize
+ * any uninstantiated type variables, according to the given force degree,
+ * but only if the overall result of `isFullyDefined` is `true`.
+ * Variables that are successfully minimized do not count as uninstantiated.
+ */
+ def isFullyDefined(tp: Type, force: ForceDegree.Value)(implicit ctx: Context): Boolean = {
+ val nestedCtx = ctx.fresh.setNewTyperState
+ val result = new IsFullyDefinedAccumulator(force)(nestedCtx).process(tp)
+ if (result) nestedCtx.typerState.commit()
+ result
+ }
+
+ /** The fully defined type, where all type variables are forced.
+ * Throws an error if type contains wildcards.
+ */
+ def fullyDefinedType(tp: Type, what: String, pos: Position)(implicit ctx: Context) =
+ if (isFullyDefined(tp, ForceDegree.all)) tp
+ else throw new Error(i"internal error: type of $what $tp is not fully defined, pos = $pos") // !!! DEBUG
+
+
+ /** Instantiate selected type variables `tvars` in type `tp` */
+ def instantiateSelected(tp: Type, tvars: List[Type])(implicit ctx: Context): Unit =
+ new IsFullyDefinedAccumulator(new ForceDegree.Value(tvars.contains, minimizeAll = true)).process(tp)
+
+ /** The accumulator which forces type variables using the policy encoded in `force`
+ * and returns whether the type is fully defined. The direction in which
+ * a type variable is instantiated is determined as follows:
+ * 1. T is minimized if the constraint over T is only from below (i.e.
+ * constrained lower bound != given lower bound and
+ * constrained upper bound == given upper bound).
+ * 2. T is maximized if the constraint over T is only from above (i.e.
+ * constrained upper bound != given upper bound and
+ * constrained lower bound == given lower bound).
+ * If (1) and (2) do not apply:
+ * 3. T is maximized if it appears only contravariantly in the given type.
+ * 4. T is minimized in all other cases.
+ *
+ * The instantiation is done in two phases:
+ * 1st Phase: Try to instantiate minimizable type variables to
+ * their lower bound. Record whether successful.
+ * 2nd Phase: If first phase was successful, instantiate all remaining type variables
+ * to their upper bound.
+ */
+ private class IsFullyDefinedAccumulator(force: ForceDegree.Value)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ private def instantiate(tvar: TypeVar, fromBelow: Boolean): Type = {
+ val inst = tvar.instantiate(fromBelow)
+ typr.println(i"forced instantiation of ${tvar.origin} = $inst")
+ inst
+ }
+ private var toMaximize: Boolean = false
+ def apply(x: Boolean, tp: Type): Boolean = tp.dealias match {
+ case _: WildcardType | _: ProtoType =>
+ false
+ case tvar: TypeVar
+ if !tvar.isInstantiated && ctx.typerState.constraint.contains(tvar) =>
+ force.appliesTo(tvar) && {
+ val direction = instDirection(tvar.origin)
+ if (direction != 0) {
+ //if (direction > 0) println(s"inst $tvar dir = up")
+ instantiate(tvar, direction < 0)
+ }
+ else {
+ val minimize =
+ force.minimizeAll ||
+ variance >= 0 && !(
+ force == ForceDegree.noBottom &&
+ defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true)))
+ if (minimize) instantiate(tvar, fromBelow = true)
+ else toMaximize = true
+ }
+ foldOver(x, tvar)
+ }
+ case tp =>
+ foldOver(x, tp)
+ }
+
+ private class UpperInstantiator(implicit ctx: Context) extends TypeAccumulator[Unit] {
+ def apply(x: Unit, tp: Type): Unit = {
+ tp match {
+ case tvar: TypeVar if !tvar.isInstantiated =>
+ instantiate(tvar, fromBelow = false)
+ case _ =>
+ }
+ foldOver(x, tp)
+ }
+ }
+
+ def process(tp: Type): Boolean = {
+ val res = apply(true, tp)
+ if (res && toMaximize) new UpperInstantiator().apply((), tp)
+ res
+ }
+ }
+
+ /** The list of uninstantiated type variables bound by some prefix of type `T` which
+ * occur in at least one formal parameter type of a prefix application.
+ * Considered prefixes are:
+ * - The function `f` of an application node `f(e1, .., en)`
+ * - The function `f` of a type application node `f[T1, ..., Tn]`
+ * - The prefix `p` of a selection `p.f`.
+ * - The result expression `e` of a block `{s1; .. sn; e}`.
+ */
+ def tvarsInParams(tree: Tree)(implicit ctx: Context): List[TypeVar] = {
+ @tailrec def boundVars(tree: Tree, acc: List[TypeVar]): List[TypeVar] = tree match {
+ case Apply(fn, _) => boundVars(fn, acc)
+ case TypeApply(fn, targs) =>
+ val tvars = targs.tpes.collect {
+ case tvar: TypeVar if !tvar.isInstantiated => tvar
+ }
+ boundVars(fn, acc ::: tvars)
+ case Select(pre, _) => boundVars(pre, acc)
+ case Block(_, expr) => boundVars(expr, acc)
+ case _ => acc
+ }
+ @tailrec def occurring(tree: Tree, toTest: List[TypeVar], acc: List[TypeVar]): List[TypeVar] =
+ if (toTest.isEmpty) acc
+ else tree match {
+ case Apply(fn, _) =>
+ fn.tpe.widen match {
+ case mtp: MethodType =>
+ val (occ, nocc) = toTest.partition(tvar => mtp.paramTypes.exists(tvar.occursIn))
+ occurring(fn, nocc, occ ::: acc)
+ case _ =>
+ occurring(fn, toTest, acc)
+ }
+ case TypeApply(fn, targs) => occurring(fn, toTest, acc)
+ case Select(pre, _) => occurring(pre, toTest, acc)
+ case Block(_, expr) => occurring(expr, toTest, acc)
+ case _ => acc
+ }
+ occurring(tree, boundVars(tree, Nil), Nil)
+ }
+
+ /** The instantiation direction for given poly param computed
+ * from the constraint:
+ * @return 1 (maximize) if constraint is uniformly from above,
+ * -1 (minimize) if constraint is uniformly from below,
+ * 0 if unconstrained, or constraint is from below and above.
+ */
+ private def instDirection(param: PolyParam)(implicit ctx: Context): Int = {
+ val constrained = ctx.typerState.constraint.fullBounds(param)
+ val original = param.binder.paramBounds(param.paramNum)
+ val cmp = ctx.typeComparer
+ val approxBelow =
+ if (!cmp.isSubTypeWhenFrozen(constrained.lo, original.lo)) 1 else 0
+ val approxAbove =
+ if (!cmp.isSubTypeWhenFrozen(original.hi, constrained.hi)) 1 else 0
+ approxAbove - approxBelow
+ }
+
+ /** Recursively widen and also follow type declarations and type aliases. */
+ def widenForMatchSelector(tp: Type)(implicit ctx: Context): Type = tp.widen match {
+ case tp: TypeRef if !tp.symbol.isClass =>
+ widenForMatchSelector(tp.superType)
+ case tp: HKApply =>
+ widenForMatchSelector(tp.superType)
+ case tp: AnnotatedType =>
+ tp.derivedAnnotatedType(widenForMatchSelector(tp.tpe), tp.annot)
+ case tp => tp
+ }
+
+ /** Following type aliases and stripping refinements and annotations, if one arrives at a
+ * class type reference where the class has a companion module, a reference to
+ * that companion module. Otherwise NoType
+ */
+ def companionRef(tp: Type)(implicit ctx: Context): Type =
+ tp.underlyingClassRef(refinementOK = true) match {
+ case tp: TypeRef =>
+ val companion = tp.classSymbol.companionModule
+ if (companion.exists)
+ companion.valRef.asSeenFrom(tp.prefix, companion.symbol.owner)
+ else NoType
+ case _ => NoType
+ }
+
+ /** Interpolate those undetermined type variables in the widened type of this tree
+ * which are introduced by type application contained in the tree.
+ * If such a variable appears covariantly in type `tp` or does not appear at all,
+ * approximate it by its lower bound. Otherwise, if it appears contravariantly
+ * in type `tp` approximate it by its upper bound.
+ * @param ownedBy if it is different from NoSymbol, all type variables owned by
+ * `ownedBy` qualify, independent of position.
+ * Without that second condition, it can be that certain variables escape
+ * interpolation, for instance when their tree was eta-lifted, so
+ * the typechecked tree is no longer the tree in which the variable
+ * was declared. A concrete example of this phenomenon can be
+ * observed when compiling core.TypeOps#asSeenFrom.
+ */
+ def interpolateUndetVars(tree: Tree, ownedBy: Symbol)(implicit ctx: Context): Unit = {
+ val constraint = ctx.typerState.constraint
+ val qualifies = (tvar: TypeVar) =>
+ (tree contains tvar.owningTree) || ownedBy.exists && tvar.owner == ownedBy
+ def interpolate() = Stats.track("interpolateUndetVars") {
+ val tp = tree.tpe.widen
+ constr.println(s"interpolate undet vars in ${tp.show}, pos = ${tree.pos}, mode = ${ctx.mode}, undets = ${constraint.uninstVars map (tvar => s"${tvar.show}@${tvar.owningTree.pos}")}")
+ constr.println(s"qualifying undet vars: ${constraint.uninstVars filter qualifies map (tvar => s"$tvar / ${tvar.show}")}, constraint: ${constraint.show}")
+
+ val vs = variances(tp, qualifies)
+ val hasUnreportedErrors = ctx.typerState.reporter match {
+ case r: StoreReporter if r.hasErrors => true
+ case _ => false
+ }
+ // Avoid interpolating variables if typerstate has unreported errors.
+ // Reason: The errors might reflect unsatisfiable constraints. In that
+ // case interpolating without taking account the constraints risks producing
+ // nonsensical types that then in turn produce incomprehensible errors.
+ // An example is in neg/i1240.scala. Without the condition in the next code line
+ // we get for
+ //
+ // val y: List[List[String]] = List(List(1))
+ //
+ // i1430.scala:5: error: type mismatch:
+ // found : Int(1)
+ // required: Nothing
+ // val y: List[List[String]] = List(List(1))
+ // ^
+ // With the condition, we get the much more sensical:
+ //
+ // i1430.scala:5: error: type mismatch:
+ // found : Int(1)
+ // required: String
+ // val y: List[List[String]] = List(List(1))
+ if (!hasUnreportedErrors)
+ vs foreachBinding { (tvar, v) =>
+ if (v != 0) {
+ typr.println(s"interpolate ${if (v == 1) "co" else "contra"}variant ${tvar.show} in ${tp.show}")
+ tvar.instantiate(fromBelow = v == 1)
+ }
+ }
+ for (tvar <- constraint.uninstVars)
+ if (!(vs contains tvar) && qualifies(tvar)) {
+ typr.println(s"instantiating non-occurring ${tvar.show} in ${tp.show} / $tp")
+ tvar.instantiate(fromBelow = true)
+ }
+ }
+ if (constraint.uninstVars exists qualifies) interpolate()
+ }
+
+ /** Instantiate undetermined type variables to that type `tp` is
+ * maximized and return None. If this is not possible, because a non-variant
+ * typevar is not uniquely determined, return that typevar in a Some.
+ */
+ def maximizeType(tp: Type)(implicit ctx: Context): Option[TypeVar] = Stats.track("maximizeType") {
+ val vs = variances(tp, alwaysTrue)
+ var result: Option[TypeVar] = None
+ vs foreachBinding { (tvar, v) =>
+ if (v == 1) tvar.instantiate(fromBelow = false)
+ else if (v == -1) tvar.instantiate(fromBelow = true)
+ else {
+ val bounds = ctx.typerState.constraint.fullBounds(tvar.origin)
+ if (!(bounds.hi <:< bounds.lo)) result = Some(tvar)
+ tvar.instantiate(fromBelow = false)
+ }
+ }
+ result
+ }
+
+ type VarianceMap = SimpleMap[TypeVar, Integer]
+
+ /** All occurrences of type vars in this type that satisfy predicate
+ * `include` mapped to their variances (-1/0/1) in this type, where
+ * -1 means: only covariant occurrences
+ * +1 means: only covariant occurrences
+ * 0 means: mixed or non-variant occurrences
+ *
+ * Note: We intentionally use a relaxed version of variance here,
+ * where the variance does not change under a prefix of a named type
+ * (the strict version makes prefixes invariant). This turns out to be
+ * better for type inference. In a nutshell, if a type variable occurs
+ * like this:
+ *
+ * (U? >: x.type) # T
+ *
+ * we want to instantiate U to x.type right away. No need to wait further.
+ */
+ private def variances(tp: Type, include: TypeVar => Boolean)(implicit ctx: Context): VarianceMap = Stats.track("variances") {
+ val constraint = ctx.typerState.constraint
+
+ object accu extends TypeAccumulator[VarianceMap] {
+ def setVariance(v: Int) = variance = v
+ def apply(vmap: VarianceMap, t: Type): VarianceMap = t match {
+ case t: TypeVar
+ if !t.isInstantiated && (ctx.typerState.constraint contains t) && include(t) =>
+ val v = vmap(t)
+ if (v == null) vmap.updated(t, variance)
+ else if (v == variance || v == 0) vmap
+ else vmap.updated(t, 0)
+ case _ =>
+ foldOver(vmap, t)
+ }
+ override def applyToPrefix(vmap: VarianceMap, t: NamedType) =
+ apply(vmap, t.prefix)
+ }
+
+ /** Include in `vmap` type variables occurring in the constraints of type variables
+ * already in `vmap`. Specifically:
+ * - if `tvar` is covariant in `vmap`, include all variables in its lower bound
+ * (because they influence the minimal solution of `tvar`),
+ * - if `tvar` is contravariant in `vmap`, include all variables in its upper bound
+ * at flipped variances (because they influence the maximal solution of `tvar`),
+ * - if `tvar` is nonvariant in `vmap`, include all variables in its upper and lower
+ * bounds as non-variant.
+ * Do this in a fixpoint iteration until `vmap` stabilizes.
+ */
+ def propagate(vmap: VarianceMap): VarianceMap = {
+ var vmap1 = vmap
+ def traverse(tp: Type) = { vmap1 = accu(vmap1, tp) }
+ vmap.foreachBinding { (tvar, v) =>
+ val param = tvar.origin
+ val e = constraint.entry(param)
+ accu.setVariance(v)
+ if (v >= 0) {
+ traverse(e.bounds.lo)
+ constraint.lower(param).foreach(p => traverse(constraint.typeVarOfParam(p)))
+ }
+ if (v <= 0) {
+ traverse(e.bounds.hi)
+ constraint.upper(param).foreach(p => traverse(constraint.typeVarOfParam(p)))
+ }
+ }
+ if (vmap1 eq vmap) vmap else propagate(vmap1)
+ }
+
+ propagate(accu(SimpleMap.Empty, tp))
+ }
+}
+
+/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
+@sharable object ForceDegree {
+ class Value(val appliesTo: TypeVar => Boolean, val minimizeAll: Boolean)
+ val none = new Value(_ => false, minimizeAll = false)
+ val all = new Value(_ => true, minimizeAll = false)
+ val noBottom = new Value(_ => true, minimizeAll = false)
+}
+
diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
new file mode 100644
index 000000000..3931fcaf4
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
@@ -0,0 +1,539 @@
+package dotty.tools
+package dotc
+package typer
+
+import dotty.tools.dotc.ast.Trees.NamedArg
+import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap}
+import Trees._
+import core._
+import Flags._
+import Symbols._
+import Types._
+import Decorators._
+import Constants._
+import StdNames.nme
+import Contexts.Context
+import Names.{Name, TermName}
+import NameOps._
+import SymDenotations.SymDenotation
+import Annotations._
+import transform.ExplicitOuter
+import Inferencing.fullyDefinedType
+import config.Printers.inlining
+import ErrorReporting.errorTree
+import collection.mutable
+import transform.TypeUtils._
+
+object Inliner {
+ import tpd._
+
+ /** Adds accessors accessors for all non-public term members accessed
+ * from `tree`. Non-public type members are currently left as they are.
+ * This means that references to a private type will lead to typing failures
+ * on the code when it is inlined. Less than ideal, but hard to do better (see below).
+ *
+ * @return If there are accessors generated, a thicket consisting of the rewritten `tree`
+ * and all accessors, otherwise the original tree.
+ */
+ private def makeInlineable(tree: Tree)(implicit ctx: Context) = {
+
+ /** A tree map which inserts accessors for all non-public term members accessed
+ * from inlined code. Accesors are collected in the `accessors` buffer.
+ */
+ object addAccessors extends TreeMap {
+ val inlineMethod = ctx.owner
+ val accessors = new mutable.ListBuffer[MemberDef]
+
+ /** A definition needs an accessor if it is private, protected, or qualified private */
+ def needsAccessor(sym: Symbol)(implicit ctx: Context) =
+ sym.is(AccessFlags) || sym.privateWithin.exists
+
+ /** The name of the next accessor to be generated */
+ def accessorName(implicit ctx: Context) =
+ ctx.freshNames.newName(inlineMethod.name.asTermName.inlineAccessorName.toString)
+
+ /** A fresh accessor symbol.
+ *
+ * @param tree The tree representing the original access to the non-public member
+ * @param accessorInfo The type of the accessor
+ */
+ def accessorSymbol(tree: Tree, accessorInfo: Type)(implicit ctx: Context): Symbol =
+ ctx.newSymbol(
+ owner = inlineMethod.owner,
+ name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName,
+ flags = if (tree.isTerm) Synthetic | Method else Synthetic,
+ info = accessorInfo,
+ coord = tree.pos).entered
+
+ /** Add an accessor to a non-public method and replace the original access with a
+ * call to the accessor.
+ *
+ * @param tree The original access to the non-public symbol
+ * @param refPart The part that refers to the method or field of the original access
+ * @param targs All type arguments passed in the access, if any
+ * @param argss All value arguments passed in the access, if any
+ * @param accessedType The type of the accessed method or field, as seen from the access site.
+ * @param rhs A function that builds the right-hand side of the accessor,
+ * given a reference to the accessed symbol and any type and
+ * value arguments the need to be integrated.
+ * @return The call to the accessor method that replaces the original access.
+ */
+ def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]],
+ accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = {
+ val qual = qualifier(refPart)
+ def refIsLocal = qual match {
+ case qual: This => qual.symbol == refPart.symbol.owner
+ case _ => false
+ }
+ val (accessorDef, accessorRef) =
+ if (refPart.symbol.isStatic || refIsLocal) {
+ // Easy case: Reference to a static symbol or a symbol referenced via `this.`
+ val accessorType = accessedType.ensureMethodic
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(refPart, tps, argss))
+ val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss)
+ (accessorDef, accessorRef)
+ } else {
+ // Hard case: Reference needs to go via a dynamic prefix
+ inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))")
+
+ // Need to dealias in order to catch all possible references to abstracted over types in
+ // substitutions
+ val dealiasMap = new TypeMap {
+ def apply(t: Type) = mapOver(t.dealias)
+ }
+
+ val qualType = dealiasMap(qual.tpe.widen)
+
+ // Add qualifier type as leading method argument to argument `tp`
+ def addQualType(tp: Type): Type = tp match {
+ case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, addQualType(tp.resultType))
+ case tp: ExprType => addQualType(tp.resultType)
+ case tp => MethodType(qualType :: Nil, tp)
+ }
+
+ // The types that are local to the inlined method, and that therefore have
+ // to be abstracted out in the accessor, which is external to the inlined method
+ val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList
+
+ // Abstract accessed type over local refs
+ def abstractQualType(mtpe: Type): Type =
+ if (localRefs.isEmpty) mtpe
+ else mtpe.LambdaAbstract(localRefs.map(_.symbol)).asInstanceOf[PolyType].flatten
+
+ val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail))
+
+ val accessorRef = ref(accessor)
+ .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
+ .appliedToArgss((qual :: Nil) :: argss)
+ (accessorDef, accessorRef)
+ }
+ accessors += accessorDef
+ inlining.println(i"added inline accessor: $accessorDef")
+ accessorRef
+ }
+
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform {
+ tree match {
+ case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) =>
+ if (tree.isTerm) {
+ val (methPart, targs, argss) = decomposeCall(tree)
+ addAccessor(tree, methPart, targs, argss,
+ accessedType = methPart.tpe.widen,
+ rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
+ } else {
+ // TODO: Handle references to non-public types.
+ // This is quite tricky, as such types can appear anywhere, including as parts
+ // of types of other things. For the moment we do nothing and complain
+ // at the implicit expansion site if there's a reference to an inaccessible type.
+ // Draft code (incomplete):
+ //
+ // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType
+ // myAccessors += TypeDef(accessor)
+ // ref(accessor)
+ //
+ tree
+ }
+ case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
+ addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
+ accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType),
+ rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
+ case _ => tree
+ }
+ }
+ }
+
+ val tree1 = addAccessors.transform(tree)
+ flatTree(tree1 :: addAccessors.accessors.toList)
+ }
+
+ /** Register inline info for given inline method `sym`.
+ *
+ * @param sym The symbol denotatioon of the inline method for which info is registered
+ * @param treeExpr A function that computes the tree to be inlined, given a context
+ * This tree may still refer to non-public members.
+ * @param ctx The context to use for evaluating `treeExpr`. It needs
+ * to have the inlined method as owner.
+ */
+ def registerInlineInfo(
+ sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
+ sym.unforcedAnnotation(defn.BodyAnnot) match {
+ case Some(ann: ConcreteBodyAnnotation) =>
+ case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
+ case _ =>
+ if (!ctx.isAfterTyper) {
+ val inlineCtx = ctx
+ sym.updateAnnotation(LazyBodyAnnotation { _ =>
+ implicit val ctx: Context = inlineCtx
+ ctx.withNoError(treeExpr(ctx))(makeInlineable)
+ })
+ }
+ }
+ }
+
+ /** `sym` has an inline method with a known body to inline (note: definitions coming
+ * from Scala2x class files might be `@inline`, but still lack that body.
+ */
+ def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
+ sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
+
+ private def bodyAndAccessors(sym: SymDenotation)(implicit ctx: Context): (Tree, List[MemberDef]) =
+ sym.unforcedAnnotation(defn.BodyAnnot).get.tree match {
+ case Thicket(body :: accessors) => (body, accessors.asInstanceOf[List[MemberDef]])
+ case body => (body, Nil)
+ }
+
+ /** The body to inline for method `sym`.
+ * @pre hasBodyToInline(sym)
+ */
+ def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
+ bodyAndAccessors(sym)._1
+
+ /** The accessors to non-public members needed by the inlinable body of `sym`.
+ * These accessors are dropped as a side effect of calling this method.
+ * @pre hasBodyToInline(sym)
+ */
+ def removeInlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] = {
+ val (body, accessors) = bodyAndAccessors(sym)
+ if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation(body))
+ accessors
+ }
+
+ /** Try to inline a call to a `@inline` method. Fail with error if the maximal
+ * inline depth is exceeded.
+ *
+ * @param tree The call to inline
+ * @param pt The expected type of the call.
+ * @return An `Inlined` node that refers to the original call and the inlined bindings
+ * and body that replace it.
+ */
+ def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ if (enclosingInlineds.length < ctx.settings.xmaxInlines.value)
+ new Inliner(tree, bodyToInline(tree.symbol)).inlined(pt)
+ else errorTree(
+ tree,
+ i"""|Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded,
+ |Maybe this is caused by a recursive inline method?
+ |You can use -Xmax:inlines to change the limit."""
+ )
+
+ /** Replace `Inlined` node by a block that contains its bindings and expansion */
+ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = {
+ val reposition = new TreeMap {
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ super.transform(tree).withPos(inlined.call.pos)
+ }
+ }
+ tpd.seq(inlined.bindings, reposition.transform(inlined.expansion))
+ }
+
+ /** The qualifier part of a Select or Ident.
+ * For an Ident, this is the `This` of the current class. (TODO: use elsewhere as well?)
+ */
+ private def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
+ case Select(qual, _) => qual
+ case _ => This(ctx.owner.enclosingClass.asClass)
+ }
+}
+
+/** Produces an inlined version of `call` via its `inlined` method.
+ *
+ * @param call The original call to a `@inline` method
+ * @param rhs The body of the inline method that replaces the call.
+ */
+class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
+ import tpd._
+ import Inliner._
+
+ private val (methPart, targs, argss) = decomposeCall(call)
+ private val meth = methPart.symbol
+ private val prefix = qualifier(methPart)
+
+ // Make sure all type arguments to the call are fully determined
+ for (targ <- targs) fullyDefinedType(targ.tpe, "inlined type argument", targ.pos)
+
+ /** A map from parameter names of the inline method to references of the actual arguments.
+ * For a type argument this is the full argument type.
+ * For a value argument, it is a reference to either the argument value
+ * (if the argument is a pure expression of singleton type), or to `val` or `def` acting
+ * as a proxy (if the argument is something else).
+ */
+ private val paramBinding = new mutable.HashMap[Name, Type]
+
+ /** A map from references to (type and value) parameters of the inline method
+ * to their corresponding argument or proxy references, as given by `paramBinding`.
+ */
+ private val paramProxy = new mutable.HashMap[Type, Type]
+
+ /** A map from the classes of (direct and outer) this references in `rhs`
+ * to references of their proxies.
+ * Note that we can't index by the ThisType itself since there are several
+ * possible forms to express what is logicaly the same ThisType. E.g.
+ *
+ * ThisType(TypeRef(ThisType(p), cls))
+ *
+ * vs
+ *
+ * ThisType(TypeRef(TermRef(ThisType(<root>), p), cls))
+ *
+ * These are different (wrt ==) types but represent logically the same key
+ */
+ private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef]
+
+ /** A buffer for bindings that define proxies for actual arguments */
+ val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]
+
+ computeParamBindings(meth.info, targs, argss)
+
+ private def newSym(name: Name, flags: FlagSet, info: Type): Symbol =
+ ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos)
+
+ /** Populate `paramBinding` and `bindingsBuf` by matching parameters with
+ * corresponding arguments. `bindingbuf` will be further extended later by
+ * proxies to this-references.
+ */
+ private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Unit = tp match {
+ case tp: PolyType =>
+ (tp.paramNames, targs).zipped.foreach { (name, arg) =>
+ paramBinding(name) = arg.tpe.stripTypeVar
+ }
+ computeParamBindings(tp.resultType, Nil, argss)
+ case tp: MethodType =>
+ (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) =>
+ def isByName = paramtp.dealias.isInstanceOf[ExprType]
+ paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match {
+ case argtpe: SingletonType if isByName || isIdempotentExpr(arg) => argtpe
+ case argtpe =>
+ val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags
+ val (bindingFlags, bindingType) =
+ if (isByName) (inlineFlag | Method, ExprType(argtpe.widen))
+ else (inlineFlag, argtpe.widen)
+ val boundSym = newSym(name, bindingFlags, bindingType).asTerm
+ val binding =
+ if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym))
+ else ValDef(boundSym, arg)
+ bindingsBuf += binding
+ boundSym.termRef
+ }
+ }
+ computeParamBindings(tp.resultType, targs, argss.tail)
+ case _ =>
+ assert(targs.isEmpty)
+ assert(argss.isEmpty)
+ }
+
+ /** Populate `thisProxy` and `paramProxy` as follows:
+ *
+ * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference,
+ * 1b. If given type refers to an instance this, create a proxy symbol and bind the thistype to
+ * refer to the proxy. The proxy is not yet entered in `bindingsBuf` that will come later.
+ * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
+ * in `paramNames` under the parameter's name. This roundabout way to bind parameter
+ * references to proxies is done because we not known a priori what the parameter
+ * references of a method are (we only know the method's type, but that contains PolyParams
+ * and MethodParams, not TypeRefs or TermRefs.
+ */
+ private def registerType(tpe: Type): Unit = tpe match {
+ case tpe: ThisType
+ if !ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package) &&
+ !thisProxy.contains(tpe.cls) =>
+ if (tpe.cls.isStaticOwner)
+ thisProxy(tpe.cls) = tpe.cls.sourceModule.termRef
+ else {
+ val proxyName = s"${tpe.cls.name}_this".toTermName
+ val proxyType = tpe.asSeenFrom(prefix.tpe, meth.owner)
+ thisProxy(tpe.cls) = newSym(proxyName, EmptyFlags, proxyType).termRef
+ registerType(meth.owner.thisType) // make sure we have a base from which to outer-select
+ }
+ case tpe: NamedType
+ if tpe.symbol.is(Param) && tpe.symbol.owner == meth &&
+ !paramProxy.contains(tpe) =>
+ paramProxy(tpe) = paramBinding(tpe.name)
+ case _ =>
+ }
+
+ /** Register type of leaf node */
+ private def registerLeaf(tree: Tree): Unit = tree match {
+ case _: This | _: Ident | _: TypeTree =>
+ tree.tpe.foreachPart(registerType, stopAtStatic = true)
+ case _ =>
+ }
+
+ /** The Inlined node representing the inlined call */
+ def inlined(pt: Type) = {
+ // make sure prefix is executed if it is impure
+ if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType)
+
+ // Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined.
+ rhs.foreachSubTree(registerLeaf)
+
+ // The class that the this-proxy `selfSym` represents
+ def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
+
+ // The name of the outer selector that computes the rhs of `selfSym`
+ def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT
+
+ // The total nesting depth of the class represented by `selfSym`.
+ def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length
+
+ // All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first)
+ val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_))
+
+ // Compute val-definitions for all this-proxies and append them to `bindingsBuf`
+ var lastSelf: Symbol = NoSymbol
+ for (selfSym <- accessedSelfSyms) {
+ val rhs =
+ if (!lastSelf.exists)
+ prefix
+ else
+ untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info)
+ bindingsBuf += ValDef(selfSym.asTerm, rhs)
+ lastSelf = selfSym
+ }
+
+ // The type map to apply to the inlined tree. This maps references to this-types
+ // and parameters to type references of their arguments or proxies.
+ val typeMap = new TypeMap {
+ def apply(t: Type) = t match {
+ case t: ThisType => thisProxy.getOrElse(t.cls, t)
+ case t: TypeRef => paramProxy.getOrElse(t, mapOver(t))
+ case t: SingletonType => paramProxy.getOrElse(t, mapOver(t))
+ case t => mapOver(t)
+ }
+ }
+
+ // The tree map to apply to the inlined tree. This maps references to this-types
+ // and parameters to references of their arguments or their proxies.
+ def treeMap(tree: Tree) = {
+ tree match {
+ case _: This =>
+ tree.tpe match {
+ case thistpe: ThisType =>
+ thisProxy.get(thistpe.cls) match {
+ case Some(t) => ref(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _ => tree
+ }
+ case _: Ident =>
+ paramProxy.get(tree.tpe) match {
+ case Some(t: SingletonType) if tree.isTerm => singleton(t).withPos(tree.pos)
+ case Some(t) if tree.isType => TypeTree(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _ => tree
+ }}
+
+ // The complete translation maps referenves to this and parameters to
+ // corresponding arguments or proxies on the type and term level. It also changes
+ // the owner from the inlined method to the current owner.
+ val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)
+
+ val expansion = inliner(rhs.withPos(call.pos))
+ ctx.traceIndented(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) {
+
+ // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
+ val expansion1 = InlineTyper.typed(expansion, pt)(inlineContext(call))
+
+ /** Does given definition bind a closure that will be inlined? */
+ def bindsDeadClosure(defn: ValOrDefDef) = Ident(defn.symbol.termRef) match {
+ case InlineableClosure(_) => !InlineTyper.retainedClosures.contains(defn.symbol)
+ case _ => false
+ }
+
+ /** All bindings in `bindingsBuf` except bindings of inlineable closures */
+ val bindings = bindingsBuf.toList.filterNot(bindsDeadClosure).map(_.withPos(call.pos))
+
+ tpd.Inlined(call, bindings, expansion1)
+ }
+ }
+
+ /** An extractor for references to closure arguments that refer to `@inline` methods */
+ private object InlineableClosure {
+ lazy val paramProxies = paramProxy.values.toSet
+ def unapply(tree: Ident)(implicit ctx: Context): Option[Tree] =
+ if (paramProxies.contains(tree.tpe)) {
+ bindingsBuf.find(_.name == tree.name) match {
+ case Some(ddef: ValDef) if ddef.symbol.is(Inline) =>
+ ddef.rhs match {
+ case closure(_, meth, _) => Some(meth)
+ case _ => None
+ }
+ case _ => None
+ }
+ } else None
+ }
+
+ /** A typer for inlined code. Its purpose is:
+ * 1. Implement constant folding over inlined code
+ * 2. Selectively expand ifs with constant conditions
+ * 3. Inline arguments that are inlineable closures
+ * 4. Make sure inlined code is type-correct.
+ * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
+ */
+ private object InlineTyper extends ReTyper {
+
+ var retainedClosures = Set[Symbol]()
+
+ override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = {
+ val tree1 = super.typedIdent(tree, pt)
+ tree1 match {
+ case InlineableClosure(_) => retainedClosures += tree.symbol
+ case _ =>
+ }
+ tree1
+ }
+
+ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
+ val res = super.typedSelect(tree, pt)
+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos)
+ res
+ }
+
+ override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = {
+ val cond1 = typed(tree.cond, defn.BooleanType)
+ cond1.tpe.widenTermRefExpr match {
+ case ConstantType(Constant(condVal: Boolean)) =>
+ val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
+ if (isIdempotentExpr(cond1)) selected
+ else Block(cond1 :: Nil, selected)
+ case _ =>
+ val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1))
+ super.typedIf(if1, pt)
+ }
+ }
+
+ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = tree.asInstanceOf[tpd.Tree] match {
+ case Apply(Select(InlineableClosure(fn), nme.apply), args) =>
+ inlining.println(i"reducing $tree with closure $fn")
+ typed(fn.appliedToArgs(args), pt)
+ case _ =>
+ super.typedApply(tree, pt)
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala
new file mode 100644
index 000000000..148cf1da7
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala
@@ -0,0 +1,1061 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Trees._, Constants._, StdNames._, Scopes._, Denotations._, Comments._
+import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
+import ast.desugar, ast.desugar._
+import ProtoTypes._
+import util.Positions._
+import util.{Property, SourcePosition, DotClass}
+import collection.mutable
+import annotation.tailrec
+import ErrorReporting._
+import tpd.ListOfTreeDecorator
+import config.Config
+import config.Printers.{typr, completions, noPrinter}
+import Annotations._
+import Inferencing._
+import transform.ValueClasses._
+import TypeApplications._
+import language.implicitConversions
+import reporting.diagnostic.messages._
+
+trait NamerContextOps { this: Context =>
+
+ /** Enter symbol into current class, if current class is owner of current context,
+ * or into current scope, if not. Should always be called instead of scope.enter
+ * in order to make sure that updates to class members are reflected in
+ * finger prints.
+ */
+ def enter(sym: Symbol): Symbol = {
+ ctx.owner match {
+ case cls: ClassSymbol => cls.enter(sym)
+ case _ => this.scope.openForMutations.enter(sym)
+ }
+ sym
+ }
+
+ /** The denotation with the given name in current context */
+ def denotNamed(name: Name): Denotation =
+ if (owner.isClass)
+ if (outer.owner == owner) { // inner class scope; check whether we are referring to self
+ if (scope.size == 1) {
+ val elem = scope.lastEntry
+ if (elem.name == name) return elem.sym.denot // return self
+ }
+ assert(scope.size <= 1, scope)
+ owner.thisType.member(name)
+ }
+ else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
+ owner.findMember(name, owner.thisType, EmptyFlags)
+ else
+ scope.denotsNamed(name).toDenot(NoPrefix)
+
+ /** Either the current scope, or, if the current context owner is a class,
+ * the declarations of the current class.
+ */
+ def effectiveScope: Scope =
+ if (owner != null && owner.isClass) owner.asClass.unforcedDecls
+ else scope
+
+ /** The symbol (stored in some typer's symTree) of an enclosing context definition */
+ def symOfContextTree(tree: untpd.Tree) = {
+ def go(ctx: Context): Symbol = {
+ ctx.typeAssigner match {
+ case typer: Typer =>
+ tree.getAttachment(typer.SymOfTree) match {
+ case Some(sym) => sym
+ case None =>
+ var cx = ctx.outer
+ while (cx.typeAssigner eq typer) cx = cx.outer
+ go(cx)
+ }
+ case _ => NoSymbol
+ }
+ }
+ go(this)
+ }
+
+ /** Context where `sym` is defined, assuming we are in a nested context. */
+ def defContext(sym: Symbol) =
+ outersIterator
+ .dropWhile(_.owner != sym)
+ .dropWhile(_.owner == sym)
+ .next
+
+ /** The given type, unless `sym` is a constructor, in which case the
+ * type of the constructed instance is returned
+ */
+ def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type) =
+ if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams map (_.typeRef))
+ else given
+
+ /** if isConstructor, make sure it has one non-implicit parameter list */
+ def normalizeIfConstructor(paramSymss: List[List[Symbol]], isConstructor: Boolean) =
+ if (isConstructor &&
+ (paramSymss.isEmpty || paramSymss.head.nonEmpty && (paramSymss.head.head is Implicit)))
+ Nil :: paramSymss
+ else
+ paramSymss
+
+ /** The method type corresponding to given parameters and result type */
+ def methodType(typeParams: List[Symbol], valueParamss: List[List[Symbol]], resultType: Type, isJava: Boolean = false)(implicit ctx: Context): Type = {
+ val monotpe =
+ (valueParamss :\ resultType) { (params, resultType) =>
+ val make =
+ if (params.nonEmpty && (params.head is Implicit)) ImplicitMethodType
+ else if (isJava) JavaMethodType
+ else MethodType
+ if (isJava)
+ for (param <- params)
+ if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType
+ make.fromSymbols(params, resultType)
+ }
+ if (typeParams.nonEmpty) monotpe.LambdaAbstract(typeParams)
+ else if (valueParamss.isEmpty) ExprType(monotpe)
+ else monotpe
+ }
+
+ /** Find moduleClass/sourceModule in effective scope */
+ private def findModuleBuddy(name: Name)(implicit ctx: Context) = {
+ val scope = effectiveScope
+ val it = scope.lookupAll(name).filter(_ is Module)
+ assert(it.hasNext, s"no companion $name in $scope")
+ it.next
+ }
+
+ /** Add moduleClass or sourceModule functionality to completer
+ * for a module or module class
+ */
+ def adjustModuleCompleter(completer: LazyType, name: Name) =
+ if (name.isTermName)
+ completer withModuleClass (_ => findModuleBuddy(name.moduleClassName))
+ else
+ completer withSourceModule (_ => findModuleBuddy(name.sourceModuleName))
+}
+
+/** This class creates symbols from definitions and imports and gives them
+ * lazy types.
+ *
+ * Timeline:
+ *
+ * During enter, trees are expanded as necessary, populating the expandedTree map.
+ * Symbols are created, and the symOfTree map is set up.
+ *
+ * Symbol completion causes some trees to be already typechecked and typedTree
+ * entries are created to associate the typed trees with the untyped expanded originals.
+ *
+ * During typer, original trees are first expanded using expandedTree. For each
+ * expanded member definition or import we extract and remove the corresponding symbol
+ * from the symOfTree map and complete it. We then consult the typedTree map to see
+ * whether a typed tree exists already. If yes, the typed tree is returned as result.
+ * Otherwise, we proceed with regular type checking.
+ *
+ * The scheme is designed to allow sharing of nodes, as long as each duplicate appears
+ * in a different method.
+ */
+class Namer { typer: Typer =>
+
+ import untpd._
+
+ val TypedAhead = new Property.Key[tpd.Tree]
+ val ExpandedTree = new Property.Key[Tree]
+ val SymOfTree = new Property.Key[Symbol]
+
+ /** A partial map from unexpanded member and pattern defs and to their expansions.
+ * Populated during enterSyms, emptied during typer.
+ */
+ //lazy val expandedTree = new mutable.AnyRefMap[DefTree, Tree]
+ /*{
+ override def default(tree: DefTree) = tree // can't have defaults on AnyRefMaps :-(
+ }*/
+
+ /** A map from expanded MemberDef, PatDef or Import trees to their symbols.
+ * Populated during enterSyms, emptied at the point a typed tree
+ * with the same symbol is created (this can be when the symbol is completed
+ * or at the latest when the tree is typechecked.
+ */
+ //lazy val symOfTree = new mutable.AnyRefMap[Tree, Symbol]
+
+ /** A map from expanded trees to their typed versions.
+ * Populated when trees are typechecked during completion (using method typedAhead).
+ */
+ // lazy val typedTree = new mutable.AnyRefMap[Tree, tpd.Tree]
+
+ /** A map from method symbols to nested typers.
+ * Populated when methods are completed. Emptied when they are typechecked.
+ * The nested typer contains new versions of the four maps above including this
+ * one, so that trees that are shared between different DefDefs can be independently
+ * used as indices. It also contains a scope that contains nested parameters.
+ */
+ lazy val nestedTyper = new mutable.AnyRefMap[Symbol, Typer]
+
+ /** The scope of the typer.
+ * For nested typers this is a place parameters are entered during completion
+ * and where they survive until typechecking. A context with this typer also
+ * has this scope.
+ */
+ val scope = newScope
+
+ /** The symbol of the given expanded tree. */
+ def symbolOfTree(tree: Tree)(implicit ctx: Context): Symbol = {
+ val xtree = expanded(tree)
+ xtree.getAttachment(TypedAhead) match {
+ case Some(ttree) => ttree.symbol
+ case none => xtree.attachment(SymOfTree)
+ }
+ }
+
+ /** The enclosing class with given name; error if none exists */
+ def enclosingClassNamed(name: TypeName, pos: Position)(implicit ctx: Context): Symbol = {
+ if (name.isEmpty) NoSymbol
+ else {
+ val cls = ctx.owner.enclosingClassNamed(name)
+ if (!cls.exists) ctx.error(s"no enclosing class or object is named $name", pos)
+ cls
+ }
+ }
+
+ /** Record `sym` as the symbol defined by `tree` */
+ def recordSym(sym: Symbol, tree: Tree)(implicit ctx: Context): Symbol = {
+ val refs = tree.attachmentOrElse(References, Nil)
+ if (refs.nonEmpty) {
+ tree.removeAttachment(References)
+ refs foreach (_.pushAttachment(OriginalSymbol, sym))
+ }
+ tree.pushAttachment(SymOfTree, sym)
+ sym
+ }
+
+ /** If this tree is a member def or an import, create a symbol of it
+ * and store in symOfTree map.
+ */
+ def createSymbol(tree: Tree)(implicit ctx: Context): Symbol = {
+
+ def privateWithinClass(mods: Modifiers) =
+ enclosingClassNamed(mods.privateWithin, mods.pos)
+
+ def checkFlags(flags: FlagSet) =
+ if (flags.isEmpty) flags
+ else {
+ val (ok, adapted, kind) = tree match {
+ case tree: TypeDef => (flags.isTypeFlags, flags.toTypeFlags, "type")
+ case _ => (flags.isTermFlags, flags.toTermFlags, "value")
+ }
+ if (!ok)
+ ctx.error(i"modifier(s) `$flags' incompatible with $kind definition", tree.pos)
+ adapted
+ }
+
+ /** Add moduleClass/sourceModule to completer if it is for a module val or class */
+ def adjustIfModule(completer: LazyType, tree: MemberDef) =
+ if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode)
+ else completer
+
+ typr.println(i"creating symbol for $tree in ${ctx.mode}")
+
+ def checkNoConflict(name: Name): Name = {
+ def errorName(msg: => String) = {
+ ctx.error(msg, tree.pos)
+ name.freshened
+ }
+ def preExisting = ctx.effectiveScope.lookup(name)
+ if (ctx.owner is PackageClass)
+ if (preExisting.isDefinedInCurrentRun)
+ errorName(s"${preExisting.showLocated} has already been compiled\nonce during this run")
+ else name
+ else
+ if ((!ctx.owner.isClass || name.isTypeName) && preExisting.exists)
+ errorName(i"$name is already defined as $preExisting")
+ else name
+ }
+
+ val inSuperCall = if (ctx.mode is Mode.InSuperCall) InSuperCall else EmptyFlags
+
+ tree match {
+ case tree: TypeDef if tree.isClassDef =>
+ val name = checkNoConflict(tree.name.encode).asTypeName
+ val flags = checkFlags(tree.mods.flags &~ Implicit)
+ val cls = recordSym(ctx.newClassSymbol(
+ ctx.owner, name, flags | inSuperCall,
+ cls => adjustIfModule(new ClassCompleter(cls, tree)(ctx), tree),
+ privateWithinClass(tree.mods), tree.namePos, ctx.source.file), tree)
+ cls.completer.asInstanceOf[ClassCompleter].init()
+ cls
+ case tree: MemberDef =>
+ val name = checkNoConflict(tree.name.encode)
+ val flags = checkFlags(tree.mods.flags)
+ val isDeferred = lacksDefinition(tree)
+ val deferred = if (isDeferred) Deferred else EmptyFlags
+ val method = if (tree.isInstanceOf[DefDef]) Method else EmptyFlags
+ val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall
+ // suppress inSuperCall for constructor parameters
+ val higherKinded = tree match {
+ case TypeDef(_, PolyTypeTree(_, _)) if isDeferred => HigherKinded
+ case _ => EmptyFlags
+ }
+
+ // to complete a constructor, move one context further out -- this
+ // is the context enclosing the class. Note that the context in which a
+ // constructor is recorded and the context in which it is completed are
+ // different: The former must have the class as owner (because the
+ // constructor is owned by the class), the latter must not (because
+ // constructor parameters are interpreted as if they are outside the class).
+ // Don't do this for Java constructors because they need to see the import
+ // of the companion object, and it is not necessary for them because they
+ // have no implementation.
+ val cctx = if (tree.name == nme.CONSTRUCTOR && !(tree.mods is JavaDefined)) ctx.outer else ctx
+
+ val completer = tree match {
+ case tree: TypeDef => new TypeDefCompleter(tree)(cctx)
+ case _ => new Completer(tree)(cctx)
+ }
+
+ recordSym(ctx.newSymbol(
+ ctx.owner, name, flags | deferred | method | higherKinded | inSuperCall1,
+ adjustIfModule(completer, tree),
+ privateWithinClass(tree.mods), tree.namePos), tree)
+ case tree: Import =>
+ recordSym(ctx.newSymbol(
+ ctx.owner, nme.IMPORT, Synthetic, new Completer(tree), NoSymbol, tree.pos), tree)
+ case _ =>
+ NoSymbol
+ }
+ }
+
+ /** If `sym` exists, enter it in effective scope. Check that
+ * package members are not entered twice in the same run.
+ */
+ def enterSymbol(sym: Symbol)(implicit ctx: Context) = {
+ if (sym.exists) {
+ typr.println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}")
+ ctx.enter(sym)
+ }
+ sym
+ }
+
+ /** Create package if it does not yet exist. */
+ private def createPackageSymbol(pid: RefTree)(implicit ctx: Context): Symbol = {
+ val pkgOwner = pid match {
+ case Ident(_) => if (ctx.owner eq defn.EmptyPackageClass) defn.RootClass else ctx.owner
+ case Select(qual: RefTree, _) => createPackageSymbol(qual).moduleClass
+ }
+ val existing = pkgOwner.info.decls.lookup(pid.name)
+
+ if ((existing is Package) && (pkgOwner eq existing.owner)) existing
+ else {
+ /** If there's already an existing type, then the package is a dup of this type */
+ val existingType = pkgOwner.info.decls.lookup(pid.name.toTypeName)
+ if (existingType.exists) {
+ ctx.error(PkgDuplicateSymbol(existingType), pid.pos)
+ ctx.newCompletePackageSymbol(pkgOwner, (pid.name ++ "$_error_").toTermName).entered
+ }
+ else ctx.newCompletePackageSymbol(pkgOwner, pid.name.asTermName).entered
+ }
+ }
+
+ /** Expand tree and store in `expandedTree` */
+ def expand(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case mdef: DefTree =>
+ val expanded = desugar.defTree(mdef)
+ typr.println(i"Expansion: $mdef expands to $expanded")
+ if (expanded ne mdef) mdef.pushAttachment(ExpandedTree, expanded)
+ case _ =>
+ }
+
+ /** The expanded version of this tree, or tree itself if not expanded */
+ def expanded(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case ddef: DefTree => ddef.attachmentOrElse(ExpandedTree, ddef)
+ case _ => tree
+ }
+
+ /** A new context that summarizes an import statement */
+ def importContext(sym: Symbol, selectors: List[Tree])(implicit ctx: Context) =
+ ctx.fresh.setImportInfo(new ImportInfo(sym, selectors))
+
+ /** A new context for the interior of a class */
+ def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = {
+ val localCtx: Context = ctx.fresh.setNewScope
+ selfInfo match {
+ case sym: Symbol if sym.exists && sym.name != nme.WILDCARD =>
+ localCtx.scope.openForMutations.enter(sym)
+ case _ =>
+ }
+ localCtx
+ }
+
+ /** For all class definitions `stat` in `xstats`: If the companion class if
+ * not also defined in `xstats`, invalidate it by setting its info to
+ * NoType.
+ */
+ def invalidateCompanions(pkg: Symbol, xstats: List[untpd.Tree])(implicit ctx: Context): Unit = {
+ val definedNames = xstats collect { case stat: NameTree => stat.name }
+ def invalidate(name: TypeName) =
+ if (!(definedNames contains name)) {
+ val member = pkg.info.decl(name).asSymDenotation
+ if (member.isClass && !(member is Package)) member.info = NoType
+ }
+ xstats foreach {
+ case stat: TypeDef if stat.isClassDef =>
+ invalidate(stat.name.moduleClassName)
+ case _ =>
+ }
+ }
+
+ /** Expand tree and create top-level symbols for statement and enter them into symbol table */
+ def index(stat: Tree)(implicit ctx: Context): Context = {
+ expand(stat)
+ indexExpanded(stat)
+ }
+
+ /** Create top-level symbols for all statements in the expansion of this statement and
+ * enter them into symbol table
+ */
+ def indexExpanded(origStat: Tree)(implicit ctx: Context): Context = {
+ def recur(stat: Tree): Context = stat match {
+ case pcl: PackageDef =>
+ val pkg = createPackageSymbol(pcl.pid)
+ index(pcl.stats)(ctx.fresh.setOwner(pkg.moduleClass))
+ invalidateCompanions(pkg, Trees.flatten(pcl.stats map expanded))
+ setDocstring(pkg, stat)
+ ctx
+ case imp: Import =>
+ importContext(createSymbol(imp), imp.selectors)
+ case mdef: DefTree =>
+ val sym = enterSymbol(createSymbol(mdef))
+ setDocstring(sym, origStat)
+ addEnumConstants(mdef, sym)
+ ctx
+ case stats: Thicket =>
+ stats.toList.foreach(recur)
+ ctx
+ case _ =>
+ ctx
+ }
+ recur(expanded(origStat))
+ }
+
+ /** Determines whether this field holds an enum constant.
+ * To qualify, the following conditions must be met:
+ * - The field's class has the ENUM flag set
+ * - The field's class extends java.lang.Enum
+ * - The field has the ENUM flag set
+ * - The field is static
+ * - The field is stable
+ */
+ def isEnumConstant(vd: ValDef)(implicit ctx: Context) = {
+ // val ownerHasEnumFlag =
+ // Necessary to check because scalac puts Java's static members into the companion object
+ // while Scala's enum constants live directly in the class.
+ // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal
+ // cyclic reference error. See the commit message for details.
+ // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum)
+ vd.mods.is(allOf(Enum, Stable, JavaStatic, JavaDefined)) // && ownerHasEnumFlag
+ }
+
+ /** Add java enum constants */
+ def addEnumConstants(mdef: DefTree, sym: Symbol)(implicit ctx: Context): Unit = mdef match {
+ case vdef: ValDef if (isEnumConstant(vdef)) =>
+ val enumClass = sym.owner.linkedClass
+ if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed)
+ enumClass.addAnnotation(Annotation.makeChild(sym))
+ case _ =>
+ }
+
+
+ def setDocstring(sym: Symbol, tree: Tree)(implicit ctx: Context) = tree match {
+ case t: MemberDef if t.rawComment.isDefined =>
+ ctx.docCtx.foreach(_.addDocstring(sym, t.rawComment))
+ case _ => ()
+ }
+
+ /** Create top-level symbols for statements and enter them into symbol table */
+ def index(stats: List[Tree])(implicit ctx: Context): Context = {
+
+ val classDef = mutable.Map[TypeName, TypeDef]()
+ val moduleDef = mutable.Map[TypeName, TypeDef]()
+
+ /** Merge the definitions of a synthetic companion generated by a case class
+ * and the real companion, if both exist.
+ */
+ def mergeCompanionDefs() = {
+ for (cdef @ TypeDef(name, _) <- stats)
+ if (cdef.isClassDef) {
+ classDef(name) = cdef
+ cdef.attachmentOrElse(ExpandedTree, cdef) match {
+ case Thicket(cls :: mval :: (mcls @ TypeDef(_, _: Template)) :: crest) =>
+ moduleDef(name) = mcls
+ case _ =>
+ }
+ }
+ for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
+ val typName = name.toTypeName
+ val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree)
+ moduleDef(typName) = mcls
+ classDef get name.toTypeName match {
+ case Some(cdef) =>
+ cdef.attachmentOrElse(ExpandedTree, cdef) match {
+ case Thicket(cls :: mval :: TypeDef(_, compimpl: Template) :: crest) =>
+ val mcls1 = cpy.TypeDef(mcls)(
+ rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
+ mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil))
+ moduleDef(typName) = mcls1
+ cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
+ case _ =>
+ }
+ case none =>
+ }
+ }
+ }
+
+ def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
+ val claz = ctx.denotNamed(classTree.name.encode).symbol
+ val modl = ctx.denotNamed(moduleTree.name.encode).symbol
+ ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered
+ ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered
+ }
+
+ def createCompanionLinks(implicit ctx: Context): Unit = {
+ for (cdef @ TypeDef(name, _) <- classDef.values) {
+ moduleDef.getOrElse(name, EmptyTree) match {
+ case t: TypeDef =>
+ createLinks(cdef, t)
+ case EmptyTree =>
+ }
+ }
+ }
+
+ stats foreach expand
+ mergeCompanionDefs()
+ val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
+ createCompanionLinks(ctxWithStats)
+ ctxWithStats
+ }
+
+ /** The completer of a symbol defined by a member def or import (except ClassSymbols) */
+ class Completer(val original: Tree)(implicit ctx: Context) extends LazyType {
+
+ protected def localContext(owner: Symbol) = ctx.fresh.setOwner(owner).setTree(original)
+
+ protected def typeSig(sym: Symbol): Type = original match {
+ case original: ValDef =>
+ if (sym is Module) moduleValSig(sym)
+ else valOrDefDefSig(original, sym, Nil, Nil, identity)(localContext(sym).setNewScope)
+ case original: DefDef =>
+ val typer1 = ctx.typer.newLikeThis
+ nestedTyper(sym) = typer1
+ typer1.defDefSig(original, sym)(localContext(sym).setTyper(typer1))
+ case imp: Import =>
+ try {
+ val expr1 = typedAheadExpr(imp.expr, AnySelectionProto)
+ ImportType(expr1)
+ } catch {
+ case ex: CyclicReference =>
+ typr.println(s"error while completing ${imp.expr}")
+ throw ex
+ }
+ }
+
+ final override def complete(denot: SymDenotation)(implicit ctx: Context) = {
+ if (completions != noPrinter && ctx.typerState != this.ctx.typerState) {
+ completions.println(completions.getClass.toString)
+ def levels(c: Context): Int =
+ if (c.typerState eq this.ctx.typerState) 0
+ else if (c.typerState == null) -1
+ else if (c.outer.typerState == c.typerState) levels(c.outer)
+ else levels(c.outer) + 1
+ completions.println(s"!!!completing ${denot.symbol.showLocated} in buried typerState, gap = ${levels(ctx)}")
+ }
+ completeInCreationContext(denot)
+ }
+
+ protected def addAnnotations(denot: SymDenotation): Unit = original match {
+ case original: untpd.MemberDef =>
+ var hasInlineAnnot = false
+ for (annotTree <- untpd.modsDeco(original).mods.annotations) {
+ val cls = typedAheadAnnotation(annotTree)
+ val ann = Annotation.deferred(cls, implicit ctx => typedAnnotation(annotTree))
+ denot.addAnnotation(ann)
+ if (cls == defn.InlineAnnot && denot.is(Method, butNot = Accessor))
+ denot.setFlag(Inline)
+ }
+ case _ =>
+ }
+
+ private def addInlineInfo(denot: SymDenotation) = original match {
+ case original: untpd.DefDef if denot.isInlineMethod =>
+ Inliner.registerInlineInfo(
+ denot,
+ implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs
+ )(localContext(denot.symbol))
+ case _ =>
+ }
+
+ /** Intentionally left without `implicit ctx` parameter. We need
+ * to pick up the context at the point where the completer was created.
+ */
+ def completeInCreationContext(denot: SymDenotation): Unit = {
+ addAnnotations(denot)
+ addInlineInfo(denot)
+ denot.info = typeSig(denot.symbol)
+ Checking.checkWellFormed(denot.symbol)
+ }
+ }
+
+ class TypeDefCompleter(original: TypeDef)(ictx: Context) extends Completer(original)(ictx) with TypeParamsCompleter {
+ private var myTypeParams: List[TypeSymbol] = null
+ private var nestedCtx: Context = null
+ assert(!original.isClassDef)
+
+ def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = {
+ if (myTypeParams == null) {
+ //println(i"completing type params of $sym in ${sym.owner}")
+ nestedCtx = localContext(sym).setNewScope
+ myTypeParams = {
+ implicit val ctx: Context = nestedCtx
+ val tparams = original.rhs match {
+ case PolyTypeTree(tparams, _) => tparams
+ case _ => Nil
+ }
+ completeParams(tparams)
+ tparams.map(symbolOfTree(_).asType)
+ }
+ }
+ myTypeParams
+ }
+
+ override protected def typeSig(sym: Symbol): Type =
+ typeDefSig(original, sym, completerTypeParams(sym)(ictx))(nestedCtx)
+ }
+
+ class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) {
+ withDecls(newScope)
+
+ protected implicit val ctx: Context = localContext(cls).setMode(ictx.mode &~ Mode.InSuperCall)
+
+ val TypeDef(name, impl @ Template(constr, parents, self, _)) = original
+
+ val (params, rest) = impl.body span {
+ case td: TypeDef => td.mods is Param
+ case vd: ValDef => vd.mods is ParamAccessor
+ case _ => false
+ }
+
+ def init() = index(params)
+
+ /** The type signature of a ClassDef with given symbol */
+ override def completeInCreationContext(denot: SymDenotation): Unit = {
+
+ /* The type of a parent constructor. Types constructor arguments
+ * only if parent type contains uninstantiated type parameters.
+ */
+ def parentType(parent: untpd.Tree)(implicit ctx: Context): Type =
+ if (parent.isType) {
+ typedAheadType(parent, AnyTypeConstructorProto).tpe
+ } else {
+ val (core, targs) = stripApply(parent) match {
+ case TypeApply(core, targs) => (core, targs)
+ case core => (core, Nil)
+ }
+ val Select(New(tpt), nme.CONSTRUCTOR) = core
+ val targs1 = targs map (typedAheadType(_))
+ val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
+ if (ptype.typeParams.isEmpty) ptype
+ else typedAheadExpr(parent).tpe
+ }
+
+ /* Check parent type tree `parent` for the following well-formedness conditions:
+ * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
+ * (2) If may not derive from itself
+ * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride)
+ * (4) The class is not final
+ * (5) If the class is sealed, it is defined in the same compilation unit as the current class
+ */
+ def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = {
+ val ptype = parentType(parent)(ctx.superCallContext)
+ if (cls.isRefinementClass) ptype
+ else {
+ val pt = checkClassType(ptype, parent.pos,
+ traitReq = parent ne parents.head, stablePrefixReq = true)
+ if (pt.derivesFrom(cls)) {
+ val addendum = parent match {
+ case Select(qual: Super, _) if ctx.scala2Mode =>
+ "\n(Note that inheriting a class of the same name is no longer allowed)"
+ case _ => ""
+ }
+ ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos)
+ defn.ObjectType
+ }
+ else if (!paramAccessors.forall(checkTypeParamOverride(pt, _)))
+ defn.ObjectType
+ else {
+ val pclazz = pt.typeSymbol
+ if (pclazz.is(Final))
+ ctx.error(em"cannot extend final $pclazz", cls.pos)
+ if (pclazz.is(Sealed) && pclazz.associatedFile != cls.associatedFile)
+ ctx.error(em"cannot extend sealed $pclazz in different compilation unit", cls.pos)
+ pt
+ }
+ }
+ }
+
+ /* Check that every parameter with the same name as a visible named parameter in the parent
+ * class satisfies the following two conditions:
+ * (1) The overriding parameter is also named (i.e. not local/name mangled).
+ * (2) The overriding parameter is passed on directly to the parent parameter, or the
+ * parent parameter is not fully defined.
+ * @return true if conditions are satisfied, false otherwise.
+ */
+ def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = {
+ var ok = true
+ val pname = paramAccessor.name
+
+ def illegal(how: String): Unit = {
+ ctx.error(em"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos)
+ ok = false
+ }
+
+ def checkAlias(tp: Type): Unit = tp match {
+ case tp: RefinedType =>
+ if (tp.refinedName == pname)
+ tp.refinedInfo match {
+ case TypeAlias(alias) =>
+ alias match {
+ case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) =>
+ // OK, parameter is passed on directly
+ case _ =>
+ illegal(em".\nParameter is both redeclared and instantiated with $alias.")
+ }
+ case _ => // OK, argument is not fully defined
+ }
+ else checkAlias(tp.parent)
+ case _ =>
+ }
+ if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param))
+ if (paramAccessor is Private)
+ illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.")
+ else
+ checkAlias(parent)
+ ok
+ }
+
+ addAnnotations(denot)
+
+ val selfInfo =
+ if (self.isEmpty) NoType
+ else if (cls.is(Module)) {
+ val moduleType = cls.owner.thisType select sourceModule
+ if (self.name == nme.WILDCARD) moduleType
+ else recordSym(
+ ctx.newSymbol(cls, self.name, self.mods.flags, moduleType, coord = self.pos),
+ self)
+ }
+ else createSymbol(self)
+
+ // pre-set info, so that parent types can refer to type params
+ val tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo)
+ denot.info = tempInfo
+
+ // Ensure constructor is completed so that any parameter accessors
+ // which have type trees deriving from its parameters can be
+ // completed in turn. Note that parent types access such parameter
+ // accessors, that's why the constructor needs to be completed before
+ // the parent types are elaborated.
+ index(constr)
+ symbolOfTree(constr).ensureCompleted()
+
+ index(rest)(inClassContext(selfInfo))
+
+ val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
+ val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
+ val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls)
+ typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs")
+
+ tempInfo.finalize(denot, parentRefs)
+
+ Checking.checkWellFormed(cls)
+ if (isDerivedValueClass(cls)) cls.setFlag(Final)
+ cls.setApplicableFlags(
+ (NoInitsInterface /: impl.body)((fs, stat) => fs & defKind(stat)))
+ }
+ }
+
+ /** Typecheck tree during completion, and remember result in typedtree map */
+ private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree = {
+ val xtree = expanded(tree)
+ xtree.getAttachment(TypedAhead) match {
+ case Some(ttree) => ttree
+ case none =>
+ val ttree = typer.typed(tree, pt)
+ xtree.pushAttachment(TypedAhead, ttree)
+ ttree
+ }
+ }
+
+ def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
+ typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type)
+
+ def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
+ typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType)
+
+ def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match {
+ case Apply(fn, _) => typedAheadAnnotation(fn)
+ case TypeApply(fn, _) => typedAheadAnnotation(fn)
+ case Select(qual, nme.CONSTRUCTOR) => typedAheadAnnotation(qual)
+ case New(tpt) => typedAheadType(tpt).tpe.classSymbol
+ }
+
+ /** Enter and typecheck parameter list */
+ def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
+ index(params)
+ for (param <- params) typedAheadExpr(param)
+ }
+
+ /** The signature of a module valdef.
+ * This will compute the corresponding module class TypeRef immediately
+ * without going through the defined type of the ValDef. This is necessary
+ * to avoid cyclic references involving imports and module val defs.
+ */
+ def moduleValSig(sym: Symbol)(implicit ctx: Context): Type = {
+ val clsName = sym.name.moduleClassName
+ val cls = ctx.denotNamed(clsName) suchThat (_ is ModuleClass)
+ ctx.owner.thisType select (clsName, cls)
+ }
+
+ /** The type signature of a ValDef or DefDef
+ * @param mdef The definition
+ * @param sym Its symbol
+ * @param paramFn A wrapping function that produces the type of the
+ * defined symbol, given its final return type
+ */
+ def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, typeParams: List[Symbol], paramss: List[List[Symbol]], paramFn: Type => Type)(implicit ctx: Context): Type = {
+
+ def inferredType = {
+ /** A type for this definition that might be inherited from elsewhere:
+ * If this is a setter parameter, the corresponding getter type.
+ * If this is a class member, the conjunction of all result types
+ * of overridden methods.
+ * NoType if neither case holds.
+ */
+ val inherited =
+ if (sym.owner.isTerm) NoType
+ else {
+ // TODO: Look only at member of supertype instead?
+ lazy val schema = paramFn(WildcardType)
+ val site = sym.owner.thisType
+ ((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) =>
+ def instantiatedResType(info: Type, tparams: List[Symbol], paramss: List[List[Symbol]]): Type = info match {
+ case info: PolyType =>
+ if (info.paramNames.length == typeParams.length)
+ instantiatedResType(info.instantiate(tparams.map(_.typeRef)), Nil, paramss)
+ else NoType
+ case info: MethodType =>
+ paramss match {
+ case params :: paramss1 if info.paramNames.length == params.length =>
+ instantiatedResType(info.instantiate(params.map(_.termRef)), tparams, paramss1)
+ case _ =>
+ NoType
+ }
+ case _ =>
+ if (tparams.isEmpty && paramss.isEmpty) info.widenExpr
+ else NoType
+ }
+ val iRawInfo =
+ cls.info.nonPrivateDecl(sym.name).matchingDenotation(site, schema).info
+ val iResType = instantiatedResType(iRawInfo, typeParams, paramss).asSeenFrom(site, cls)
+ if (iResType.exists)
+ typr.println(i"using inherited type for ${mdef.name}; raw: $iRawInfo, inherited: $iResType")
+ tp & iResType
+ }
+ }
+
+ /** The proto-type to be used when inferring the result type from
+ * the right hand side. This is `WildcardType` except if the definition
+ * is a default getter. In that case, the proto-type is the type of
+ * the corresponding parameter where bound parameters are replaced by
+ * Wildcards.
+ */
+ def rhsProto = {
+ val name = sym.asTerm.name
+ val idx = name.defaultGetterIndex
+ if (idx < 0) WildcardType
+ else {
+ val original = name.defaultGetterToMethod
+ val meth: Denotation =
+ if (original.isConstructorName && (sym.owner is ModuleClass))
+ sym.owner.companionClass.info.decl(nme.CONSTRUCTOR)
+ else
+ ctx.defContext(sym).denotNamed(original)
+ def paramProto(paramss: List[List[Type]], idx: Int): Type = paramss match {
+ case params :: paramss1 =>
+ if (idx < params.length) wildApprox(params(idx))
+ else paramProto(paramss1, idx - params.length)
+ case nil =>
+ WildcardType
+ }
+ val defaultAlts = meth.altsWith(_.hasDefaultParams)
+ if (defaultAlts.length == 1)
+ paramProto(defaultAlts.head.info.widen.paramTypess, idx)
+ else
+ WildcardType
+ }
+ }
+
+ // println(s"final inherited for $sym: ${inherited.toString}") !!!
+ // println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
+ def isInline = sym.is(FinalOrInline, butNot = Method | Mutable)
+
+ // Widen rhs type and approximate `|' but keep ConstantTypes if
+ // definition is inline (i.e. final in Scala2).
+ def widenRhs(tp: Type): Type = tp.widenTermRefExpr match {
+ case tp: ConstantType if isInline => tp
+ case _ => ctx.harmonizeUnion(tp.widen)
+ }
+
+ // Replace aliases to Unit by Unit itself. If we leave the alias in
+ // it would be erased to BoxedUnit.
+ def dealiasIfUnit(tp: Type) = if (tp.isRef(defn.UnitClass)) defn.UnitType else tp
+
+ val rhsCtx = ctx.addMode(Mode.InferringReturnType)
+ def rhsType = typedAheadExpr(mdef.rhs, inherited orElse rhsProto)(rhsCtx).tpe
+ def cookedRhsType = ctx.deskolemize(dealiasIfUnit(widenRhs(rhsType)))
+ lazy val lhsType = fullyDefinedType(cookedRhsType, "right-hand side", mdef.pos)
+ //if (sym.name.toString == "y") println(i"rhs = $rhsType, cooked = $cookedRhsType")
+ if (inherited.exists)
+ if (sym.is(Final, butNot = Method) && lhsType.isInstanceOf[ConstantType])
+ lhsType // keep constant types that fill in for a non-constant (to be revised when inline has landed).
+ else inherited
+ else {
+ if (sym is Implicit) {
+ val resStr = if (mdef.isInstanceOf[DefDef]) "result " else ""
+ ctx.error(s"${resStr}type of implicit definition needs to be given explicitly", mdef.pos)
+ sym.resetFlag(Implicit)
+ }
+ lhsType orElse WildcardType
+ }
+ }
+
+ val tptProto = mdef.tpt match {
+ case _: untpd.DerivedTypeTree =>
+ WildcardType
+ case TypeTree() =>
+ inferredType
+ case TypedSplice(tpt: TypeTree) if !isFullyDefined(tpt.tpe, ForceDegree.none) =>
+ val rhsType = typedAheadExpr(mdef.rhs, tpt.tpe).tpe
+ mdef match {
+ case mdef: DefDef if mdef.name == nme.ANON_FUN =>
+ val hygienicType = avoid(rhsType, paramss.flatten)
+ if (!(hygienicType <:< tpt.tpe))
+ ctx.error(i"return type ${tpt.tpe} of lambda cannot be made hygienic;\n" +
+ i"it is not a supertype of the hygienic type $hygienicType", mdef.pos)
+ //println(i"lifting $rhsType over $paramss -> $hygienicType = ${tpt.tpe}")
+ //println(TypeComparer.explained { implicit ctx => hygienicType <:< tpt.tpe })
+ case _ =>
+ }
+ WildcardType
+ case _ =>
+ WildcardType
+ }
+ paramFn(typedAheadType(mdef.tpt, tptProto).tpe)
+ }
+
+ /** The type signature of a DefDef with given symbol */
+ def defDefSig(ddef: DefDef, sym: Symbol)(implicit ctx: Context) = {
+ val DefDef(name, tparams, vparamss, _, _) = ddef
+ val isConstructor = name == nme.CONSTRUCTOR
+
+ // The following 3 lines replace what was previously just completeParams(tparams).
+ // But that can cause bad bounds being computed, as witnessed by
+ // tests/pos/paramcycle.scala. The problematic sequence is this:
+ // 0. Class constructor gets completed.
+ // 1. Type parameter CP of constructor gets completed
+ // 2. As a first step CP's bounds are set to Nothing..Any.
+ // 3. CP's real type bound demands the completion of corresponding type parameter DP
+ // of enclosing class.
+ // 4. Type parameter DP has a rhs a DerivedFromParam tree, as installed by
+ // desugar.classDef
+ // 5. The completion of DP then copies the current bounds of CP, which are still Nothing..Any.
+ // 6. The completion of CP finishes installing the real type bounds.
+ // Consequence: CP ends up with the wrong bounds!
+ // To avoid this we always complete type parameters of a class before the type parameters
+ // of the class constructor, but after having indexed the constructor parameters (because
+ // indexing is needed to provide a symbol to copy for DP's completion.
+ // With the patch, we get instead the following sequence:
+ // 0. Class constructor gets completed.
+ // 1. Class constructor parameter CP is indexed.
+ // 2. Class parameter DP starts completion.
+ // 3. Info of CP is computed (to be copied to DP).
+ // 4. CP is completed.
+ // 5. Info of CP is copied to DP and DP is completed.
+ index(tparams)
+ if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted())
+ for (tparam <- tparams) typedAheadExpr(tparam)
+
+ vparamss foreach completeParams
+ def typeParams = tparams map symbolOfTree
+ val paramSymss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor)
+ def wrapMethType(restpe: Type): Type = {
+ val restpe1 = // try to make anonymous functions non-dependent, so that they can be used in closures
+ if (name == nme.ANON_FUN) avoid(restpe, paramSymss.flatten)
+ else restpe
+ ctx.methodType(tparams map symbolOfTree, paramSymss, restpe1, isJava = ddef.mods is JavaDefined)
+ }
+ if (isConstructor) {
+ // set result type tree to unit, but take the current class as result type of the symbol
+ typedAheadType(ddef.tpt, defn.UnitType)
+ wrapMethType(ctx.effectiveResultType(sym, typeParams, NoType))
+ }
+ else valOrDefDefSig(ddef, sym, typeParams, paramSymss, wrapMethType)
+ }
+
+ def typeDefSig(tdef: TypeDef, sym: Symbol, tparamSyms: List[TypeSymbol])(implicit ctx: Context): Type = {
+ def abstracted(tp: Type): Type =
+ if (tparamSyms.nonEmpty) tp.LambdaAbstract(tparamSyms) else tp
+
+ val dummyInfo = abstracted(TypeBounds.empty)
+ sym.info = dummyInfo
+ // Temporarily set info of defined type T to ` >: Nothing <: Any.
+ // This is done to avoid cyclic reference errors for F-bounds.
+ // This is subtle: `sym` has now an empty TypeBounds, but is not automatically
+ // made an abstract type. If it had been made an abstract type, it would count as an
+ // abstract type of its enclosing class, which might make that class an invalid
+ // prefix. I verified this would lead to an error when compiling io.ClassPath.
+ // A distilled version is in pos/prefix.scala.
+ //
+ // The scheme critically relies on an implementation detail of isRef, which
+ // inspects a TypeRef's info, instead of simply dealiasing alias types.
+
+ val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree]
+ val rhs = tdef.rhs match {
+ case PolyTypeTree(_, body) => body
+ case rhs => rhs
+ }
+ val rhsBodyType = typedAheadType(rhs).tpe
+ val rhsType = if (isDerived) rhsBodyType else abstracted(rhsBodyType)
+ val unsafeInfo = rhsType match {
+ case bounds: TypeBounds => bounds
+ case alias => TypeAlias(alias, if (sym is Local) sym.variance else 0)
+ }
+ if (isDerived) sym.info = unsafeInfo
+ else {
+ sym.info = NoCompleter
+ sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true)
+ }
+
+ // Here we pay the price for the cavalier setting info to TypeBounds.empty above.
+ // We need to compensate by invalidating caches in references that might
+ // still contain the TypeBounds.empty. If we do not do this, stdlib factories
+ // fail with a bounds error in PostTyper.
+ def ensureUpToDate(tp: Type, outdated: Type) = tp match {
+ case tref: TypeRef if tref.info == outdated && sym.info != outdated =>
+ tref.uncheckedSetSym(null)
+ case _ =>
+ }
+ ensureUpToDate(sym.typeRef, dummyInfo)
+ ensureUpToDate(sym.typeRef.appliedTo(tparamSyms.map(_.typeRef)), TypeBounds.empty)
+ sym.info
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
new file mode 100644
index 000000000..9a20a452e
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -0,0 +1,488 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
+import Trees._
+import Constants._
+import Scopes._
+import annotation.unchecked
+import util.Positions._
+import util.{Stats, SimpleMap}
+import util.common._
+import Decorators._
+import Uniques._
+import ErrorReporting.errorType
+import config.Printers.typr
+import collection.mutable
+
+object ProtoTypes {
+
+ import tpd._
+
+ /** A trait defining an `isCompatible` method. */
+ trait Compatibility {
+
+ /** Is there an implicit conversion from `tp` to `pt`? */
+ def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean
+
+ /** A type `tp` is compatible with a type `pt` if one of the following holds:
+ * 1. `tp` is a subtype of `pt`
+ * 2. `pt` is by name parameter type, and `tp` is compatible with its underlying type
+ * 3. there is an implicit conversion from `tp` to `pt`.
+ * 4. `tp` is a numeric subtype of `pt` (this case applies even if implicit conversions are disabled)
+ */
+ def isCompatible(tp: Type, pt: Type)(implicit ctx: Context): Boolean =
+ (tp.widenExpr relaxed_<:< pt.widenExpr) || viewExists(tp, pt)
+
+ /** Test compatibility after normalization in a fresh typerstate. */
+ def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = {
+ val nestedCtx = ctx.fresh.setExploreTyperState
+ isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
+ }
+
+ private def disregardProto(pt: Type)(implicit ctx: Context): Boolean = pt.dealias match {
+ case _: OrType => true
+ case pt => pt.isRef(defn.UnitClass)
+ }
+
+ /** Check that the result type of the current method
+ * fits the given expected result type.
+ */
+ def constrainResult(mt: Type, pt: Type)(implicit ctx: Context): Boolean = pt match {
+ case pt: FunProto =>
+ mt match {
+ case mt: MethodType =>
+ mt.isDependent || constrainResult(mt.resultType, pt.resultType)
+ case _ =>
+ true
+ }
+ case _: ValueTypeOrProto if !disregardProto(pt) =>
+ mt match {
+ case mt: MethodType =>
+ mt.isDependent || isCompatible(normalize(mt, pt), pt)
+ case _ =>
+ isCompatible(mt, pt)
+ }
+ case _: WildcardType =>
+ isCompatible(mt, pt)
+ case _ =>
+ true
+ }
+ }
+
+ object NoViewsAllowed extends Compatibility {
+ override def viewExists(tp: Type, pt: Type)(implicit ctx: Context): Boolean = false
+ }
+
+ /** A trait for prototypes that match all types */
+ trait MatchAlways extends ProtoType {
+ def isMatchedBy(tp1: Type)(implicit ctx: Context) = true
+ def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x
+ }
+
+ /** A class marking ignored prototypes that can be revealed by `deepenProto` */
+ case class IgnoredProto(ignored: Type) extends UncachedGroundType with MatchAlways {
+ override def deepenProto(implicit ctx: Context): Type = ignored
+ }
+
+ /** A prototype for expressions [] that are part of a selection operation:
+ *
+ * [ ].name: proto
+ */
+ abstract case class SelectionProto(val name: Name, val memberProto: Type, val compat: Compatibility)
+ extends CachedProxyType with ProtoType with ValueTypeOrProto {
+
+ override def isMatchedBy(tp1: Type)(implicit ctx: Context) = {
+ name == nme.WILDCARD || {
+ val mbr = tp1.member(name)
+ def qualifies(m: SingleDenotation) =
+ memberProto.isRef(defn.UnitClass) ||
+ compat.normalizedCompatible(m.info, memberProto)
+ mbr match { // hasAltWith inlined for performance
+ case mbr: SingleDenotation => mbr.exists && qualifies(mbr)
+ case _ => mbr hasAltWith qualifies
+ }
+ }
+ }
+
+ def underlying(implicit ctx: Context) = WildcardType
+
+ def derivedSelectionProto(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context) =
+ if ((name eq this.name) && (memberProto eq this.memberProto) && (compat eq this.compat)) this
+ else SelectionProto(name, memberProto, compat)
+
+ override def equals(that: Any): Boolean = that match {
+ case that: SelectionProto =>
+ (name eq that.name) && (memberProto == that.memberProto) && (compat eq that.compat)
+ case _ =>
+ false
+ }
+
+ def map(tm: TypeMap)(implicit ctx: Context) = derivedSelectionProto(name, tm(memberProto), compat)
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context) = ta(x, memberProto)
+
+ override def deepenProto(implicit ctx: Context) = derivedSelectionProto(name, memberProto.deepenProto, compat)
+
+ override def computeHash = addDelta(doHash(name, memberProto), if (compat eq NoViewsAllowed) 1 else 0)
+ }
+
+ class CachedSelectionProto(name: Name, memberProto: Type, compat: Compatibility) extends SelectionProto(name, memberProto, compat)
+
+ object SelectionProto {
+ def apply(name: Name, memberProto: Type, compat: Compatibility)(implicit ctx: Context): SelectionProto = {
+ val selproto = new CachedSelectionProto(name, memberProto, compat)
+ if (compat eq NoViewsAllowed) unique(selproto) else selproto
+ }
+ }
+
+ /** Create a selection proto-type, but only one level deep;
+ * treat constructors specially
+ */
+ def selectionProto(name: Name, tp: Type, typer: Typer)(implicit ctx: Context) =
+ if (name.isConstructorName) WildcardType
+ else tp match {
+ case tp: UnapplyFunProto => new UnapplySelectionProto(name)
+ case tp => SelectionProto(name, IgnoredProto(tp), typer)
+ }
+
+ /** A prototype for expressions [] that are in some unspecified selection operation
+ *
+ * [].?: ?
+ *
+ * Used to indicate that expression is in a context where the only valid
+ * operation is further selection. In this case, the expression need not be a value.
+ * @see checkValue
+ */
+ @sharable object AnySelectionProto extends SelectionProto(nme.WILDCARD, WildcardType, NoViewsAllowed)
+
+ /** A prototype for selections in pattern constructors */
+ class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType, NoViewsAllowed)
+
+ trait ApplyingProto extends ProtoType
+
+ /** A prototype for expressions that appear in function position
+ *
+ * [](args): resultType
+ */
+ case class FunProto(args: List[untpd.Tree], resType: Type, typer: Typer)(implicit ctx: Context)
+ extends UncachedGroundType with ApplyingProto {
+ private var myTypedArgs: List[Tree] = Nil
+
+ override def resultType(implicit ctx: Context) = resType
+
+ /** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
+ private var myTypedArg: SimpleMap[untpd.Tree, Tree] = SimpleMap.Empty
+
+ /** A map recording the typer states in which arguments stored in myTypedArg were typed */
+ private var evalState: SimpleMap[untpd.Tree, TyperState] = SimpleMap.Empty
+
+ def isMatchedBy(tp: Type)(implicit ctx: Context) =
+ typer.isApplicable(tp, Nil, typedArgs, resultType)
+
+ def derivedFunProto(args: List[untpd.Tree] = this.args, resultType: Type, typer: Typer = this.typer) =
+ if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
+ else new FunProto(args, resultType, typer)
+
+ override def notApplied = WildcardType
+
+ /** Forget the types of any arguments that have been typed producing a constraint in a
+ * typer state that is not yet committed into the one of the current context `ctx`.
+ * This is necessary to avoid "orphan" PolyParams that are referred to from
+ * type variables in the typed arguments, but that are not registered in the
+ * current constraint. A test case is pos/t1756.scala.
+ * @return True if all arguments have types (in particular, no types were forgotten).
+ */
+ def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = {
+ evalState foreachBinding { (arg, tstate) =>
+ if (tstate.uncommittedAncestor.constraint ne ctx.typerState.constraint) {
+ typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstate.constraint}, current = ${ctx.typerState.constraint}")
+ myTypedArg = myTypedArg.remove(arg)
+ evalState = evalState.remove(arg)
+ }
+ }
+ myTypedArg.size == args.length
+ }
+
+ private def cacheTypedArg(arg: untpd.Tree, typerFn: untpd.Tree => Tree)(implicit ctx: Context): Tree = {
+ var targ = myTypedArg(arg)
+ if (targ == null) {
+ targ = typerFn(arg)
+ if (!ctx.reporter.hasPending) {
+ myTypedArg = myTypedArg.updated(arg, targ)
+ evalState = evalState.updated(arg, ctx.typerState)
+ }
+ }
+ targ
+ }
+
+ /** The typed arguments. This takes any arguments already typed using
+ * `typedArg` into account.
+ */
+ def typedArgs: List[Tree] = {
+ if (myTypedArgs.size != args.length)
+ myTypedArgs = args.mapconserve(cacheTypedArg(_, typer.typed(_)))
+ myTypedArgs
+ }
+
+ /** Type single argument and remember the unadapted result in `myTypedArg`.
+ * used to avoid repeated typings of trees when backtracking.
+ */
+ def typedArg(arg: untpd.Tree, formal: Type)(implicit ctx: Context): Tree = {
+ val targ = cacheTypedArg(arg, typer.typedUnadapted(_, formal))
+ typer.adapt(targ, formal, arg)
+ }
+
+ private var myTupled: Type = NoType
+
+ /** The same proto-type but with all arguments combined in a single tuple */
+ def tupled: FunProto = myTupled match {
+ case pt: FunProto =>
+ pt
+ case _ =>
+ myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer)
+ tupled
+ }
+
+ /** Somebody called the `tupled` method of this prototype */
+ def isTupled: Boolean = myTupled.isInstanceOf[FunProto]
+
+ override def toString = s"FunProto(${args mkString ","} => $resultType)"
+
+ def map(tm: TypeMap)(implicit ctx: Context): FunProto =
+ derivedFunProto(args, tm(resultType), typer)
+
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
+ ta(ta.foldOver(x, typedArgs.tpes), resultType)
+
+ override def deepenProto(implicit ctx: Context) = derivedFunProto(args, resultType.deepenProto, typer)
+ }
+
+
+ /** A prototype for expressions that appear in function position
+ *
+ * [](args): resultType, where args are known to be typed
+ */
+ class FunProtoTyped(args: List[tpd.Tree], resultType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(args, resultType, typer)(ctx) {
+ override def typedArgs = args
+ }
+
+ /** A prototype for implicitly inferred views:
+ *
+ * []: argType => resultType
+ */
+ abstract case class ViewProto(argType: Type, resType: Type)
+ extends CachedGroundType with ApplyingProto {
+
+ override def resultType(implicit ctx: Context) = resType
+
+ def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean =
+ ctx.typer.isApplicable(tp, argType :: Nil, resultType)
+
+ def derivedViewProto(argType: Type, resultType: Type)(implicit ctx: Context) =
+ if ((argType eq this.argType) && (resultType eq this.resultType)) this
+ else ViewProto(argType, resultType)
+
+ def map(tm: TypeMap)(implicit ctx: Context): ViewProto = derivedViewProto(tm(argType), tm(resultType))
+
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
+ ta(ta(x, argType), resultType)
+
+ override def deepenProto(implicit ctx: Context) = derivedViewProto(argType, resultType.deepenProto)
+ }
+
+ class CachedViewProto(argType: Type, resultType: Type) extends ViewProto(argType, resultType) {
+ override def computeHash = doHash(argType, resultType)
+ }
+
+ object ViewProto {
+ def apply(argType: Type, resultType: Type)(implicit ctx: Context) =
+ unique(new CachedViewProto(argType, resultType))
+ }
+
+ class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
+ untpd.TypedSplice(dummyTreeOfType(argType))(ctx) :: Nil, WildcardType, typer)
+
+ /** A prototype for expressions [] that are type-parameterized:
+ *
+ * [] [targs] resultType
+ */
+ case class PolyProto(targs: List[Type], resType: Type) extends UncachedGroundType with ProtoType {
+
+ override def resultType(implicit ctx: Context) = resType
+
+ override def isMatchedBy(tp: Type)(implicit ctx: Context) = {
+ def isInstantiatable(tp: Type) = tp.widen match {
+ case tp: PolyType => tp.paramNames.length == targs.length
+ case _ => false
+ }
+ isInstantiatable(tp) || tp.member(nme.apply).hasAltWith(d => isInstantiatable(d.info))
+ }
+
+ def derivedPolyProto(targs: List[Type], resultType: Type) =
+ if ((targs eq this.targs) && (resType eq this.resType)) this
+ else PolyProto(targs, resType)
+
+ override def notApplied = WildcardType
+
+ def map(tm: TypeMap)(implicit ctx: Context): PolyProto =
+ derivedPolyProto(targs mapConserve tm, tm(resultType))
+
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T =
+ ta(ta.foldOver(x, targs), resultType)
+
+ override def deepenProto(implicit ctx: Context) = derivedPolyProto(targs, resultType.deepenProto)
+ }
+
+ /** A prototype for expressions [] that are known to be functions:
+ *
+ * [] _
+ */
+ @sharable object AnyFunctionProto extends UncachedGroundType with MatchAlways
+
+ /** A prototype for type constructors that are followed by a type application */
+ @sharable object AnyTypeConstructorProto extends UncachedGroundType with MatchAlways
+
+ /** 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.
+ * Also, if `owningTree` is non-empty, add a type variable for each parameter.
+ * @return The added polytype, and the list of created type variables.
+ */
+ def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeVar]) = {
+ val state = ctx.typerState
+ assert(!(ctx.typerState.isCommittable && owningTree.isEmpty),
+ s"inconsistent: no typevars were added to committable constraint ${state.constraint}")
+
+ def newTypeVars(pt: PolyType): List[TypeVar] =
+ for (n <- (0 until pt.paramNames.length).toList)
+ yield new TypeVar(PolyParam(pt, n), state, owningTree, ctx.owner)
+
+ val added =
+ if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType)
+ else pt
+ val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added)
+ ctx.typeComparer.addToConstraint(added, tvars)
+ (added, tvars)
+ }
+
+ /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */
+ def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1
+
+ /** The normalized form of a type
+ * - unwraps polymorphic types, tracking their parameters in the current constraint
+ * - skips implicit parameters; if result type depends on implicit parameter,
+ * replace with Wildcard.
+ * - converts non-dependent method types to the corresponding function types
+ * - dereferences parameterless method types
+ * - dereferences nullary method types provided the corresponding function type
+ * is not a subtype of the expected type.
+ * Note: We need to take account of the possibility of inserting a () argument list in normalization. Otherwise, a type with a
+ * def toString(): String
+ * member would not count as a valid solution for ?{toString: String}. This would then lead to an implicit
+ * insertion, with a nice explosion of inference search because of course every implicit result has some sort
+ * of toString method. The problem is solved by dereferencing nullary method types if the corresponding
+ * function type is not compatible with the prototype.
+ */
+ def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
+ tp.widenSingleton match {
+ case poly: PolyType => normalize(constrained(poly).resultType, pt)
+ case mt: MethodType =>
+ if (mt.isImplicit)
+ if (mt.isDependent)
+ mt.resultType.substParams(mt, mt.paramTypes.map(Function.const(WildcardType)))
+ else mt.resultType
+ else
+ if (mt.isDependent) tp
+ else {
+ val rt = normalize(mt.resultType, pt)
+ pt match {
+ case pt: IgnoredProto => mt
+ case pt: ApplyingProto => mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt)
+ case _ =>
+ val ft = defn.FunctionOf(mt.paramTypes, rt)
+ if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt
+ }
+ }
+ case et: ExprType => et.resultType
+ case _ => tp
+ }
+ }
+
+ /** Approximate occurrences of parameter types and uninstantiated typevars
+ * by wildcard types.
+ */
+ final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match {
+ case tp: NamedType => // default case, inlined for speed
+ if (tp.symbol.isStatic) tp
+ else tp.derivedSelect(wildApprox(tp.prefix, theMap))
+ case tp: RefinedType => // default case, inlined for speed
+ tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap))
+ case tp: TypeAlias => // default case, inlined for speed
+ tp.derivedTypeAlias(wildApprox(tp.alias, theMap))
+ case tp @ PolyParam(poly, pnum) =>
+ def unconstrainedApprox = WildcardType(wildApprox(poly.paramBounds(pnum)).bounds)
+ if (ctx.mode.is(Mode.TypevarsMissContext))
+ unconstrainedApprox
+ else
+ ctx.typerState.constraint.entry(tp) match {
+ case bounds: TypeBounds => wildApprox(WildcardType(bounds))
+ case NoType => unconstrainedApprox
+ case inst => wildApprox(inst)
+ }
+ case MethodParam(mt, pnum) =>
+ WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum))))
+ case tp: TypeVar =>
+ wildApprox(tp.underlying)
+ case tp @ HKApply(tycon, args) =>
+ wildApprox(tycon) match {
+ case _: WildcardType => WildcardType // this ensures we get a * type
+ case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_)))
+ }
+ case tp: AndType =>
+ val tp1a = wildApprox(tp.tp1)
+ val tp2a = wildApprox(tp.tp2)
+ def wildBounds(tp: Type) =
+ if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp)
+ if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
+ WildcardType(wildBounds(tp1a) & wildBounds(tp2a))
+ else
+ tp.derivedAndType(tp1a, tp2a)
+ case tp: OrType =>
+ val tp1a = wildApprox(tp.tp1)
+ val tp2a = wildApprox(tp.tp2)
+ if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
+ WildcardType(tp1a.bounds | tp2a.bounds)
+ else
+ tp.derivedOrType(tp1a, tp2a)
+ case tp: LazyRef =>
+ WildcardType
+ case tp: SelectionProto =>
+ tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed)
+ case tp: ViewProto =>
+ tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType))
+ case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed
+ tp
+ case _ =>
+ (if (theMap != null) theMap else new WildApproxMap).mapOver(tp)
+ }
+
+ @sharable object AssignProto extends UncachedGroundType with MatchAlways
+
+ private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap {
+ def apply(tp: Type) = wildApprox(tp, this)
+ }
+
+ /** Dummy tree to be used as an argument of a FunProto or ViewProto type */
+ object dummyTreeOfType {
+ def apply(tp: Type): Tree = untpd.Literal(Constant(null)) withTypeUnchecked tp
+ def unapply(tree: Tree): Option[Type] = tree match {
+ case Literal(Constant(null)) => Some(tree.typeOpt)
+ case _ => None
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala
new file mode 100644
index 000000000..2413c0c22
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala
@@ -0,0 +1,108 @@
+package dotty.tools.dotc
+package typer
+
+import core._
+import Contexts._
+import Types._
+import Symbols._
+import Decorators._
+import typer.ProtoTypes._
+import ast.{tpd, untpd}
+import ast.Trees._
+import scala.util.control.NonFatal
+import util.Positions.Position
+import config.Printers.typr
+
+/** A version of Typer that keeps all symbols defined and referenced in a
+ * previously typed tree.
+ *
+ * All definition nodes keep their symbols. All leaf nodes for idents, selects,
+ * and TypeTrees keep their types. Indexing is a no-op.
+ *
+ * Otherwise, everything is as in Typer.
+ */
+class ReTyper extends Typer {
+ import tpd._
+
+ /** Checks that the given tree has been typed */
+ protected def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ assert(tree.hasType, i"$tree ${tree.getClass} ${tree.uniqueId}")
+ tree.withType(tree.typeOpt)
+ }
+
+ override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree =
+ promote(tree)
+
+ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
+ assert(tree.hasType, tree)
+ val qual1 = typed(tree.qualifier, AnySelectionProto)
+ untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt)
+ }
+
+ override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
+ promote(tree)
+
+ override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
+ promote(tree)
+
+ override def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree =
+ promote(tree)
+
+ override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree =
+ promote(tree)
+
+ override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = {
+ assert(tree.hasType)
+ val body1 = typed(tree.body, pt)
+ untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt)
+ }
+
+ override def typedUnApply(tree: untpd.UnApply, selType: Type)(implicit ctx: Context): UnApply = {
+ val fun1 = typedExpr(tree.fun, AnyFunctionProto)
+ val implicits1 = tree.implicits.map(typedExpr(_))
+ val patterns1 = tree.patterns.mapconserve(pat => typed(pat, pat.tpe))
+ untpd.cpy.UnApply(tree)(fun1, implicits1, patterns1).withType(tree.tpe)
+ }
+
+ override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol
+
+ override def retrieveSym(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol
+ override def symbolOfTree(tree: untpd.Tree)(implicit ctx: Context): Symbol = tree.symbol
+
+ override def localTyper(sym: Symbol) = this
+
+ override def index(trees: List[untpd.Tree])(implicit ctx: Context) = ctx
+
+ override def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
+ fallBack(tree, ctx.typerState)
+
+ override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = ()
+
+ override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] =
+ parents
+
+ override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree
+
+ override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match {
+ case mt @ MethodType(_, formals) =>
+ val args: List[Tree] = tree.args.zipWithConserve(formals)(typedExpr(_, _)).asInstanceOf[List[Tree]]
+ assignType(untpd.cpy.Apply(tree)(fun, args), fun, args)
+ case _ =>
+ super.handleUnexpectedFunType(tree, fun)
+ }
+
+ override def typedUnadapted(tree: untpd.Tree, pt: Type)(implicit ctx: Context) =
+ try super.typedUnadapted(tree, pt)
+ catch {
+ case NonFatal(ex) =>
+ if (ctx.isAfterTyper)
+ println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}")
+ throw ex
+ }
+
+ override def checkVariance(tree: Tree)(implicit ctx: Context) = ()
+ override def inferView(from: Tree, to: Type)(implicit ctx: Context): Implicits.SearchResult =
+ Implicits.NoImplicitMatches
+ override def checkCanEqual(ltp: Type, rtp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] = mdef :: Nil
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
new file mode 100644
index 000000000..46bdbf3b3
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -0,0 +1,1526 @@
+package dotty.tools.dotc
+package typer
+
+import transform._
+import core._
+import config._
+import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._
+import StdNames._, Denotations._, Scopes._, Constants.Constant, SymUtils._
+import Annotations._
+import util.Positions._
+import scala.collection.{ mutable, immutable }
+import ast._
+import Trees._
+import TreeTransforms._
+import util.DotClass
+import scala.util.{Try, Success, Failure}
+import config.{ScalaVersion, NoScalaVersion}
+import Decorators._
+import typer.ErrorReporting._
+import DenotTransformers._
+import ValueClasses.isDerivedValueClass
+
+object RefChecks {
+ import tpd._
+
+ private def isDefaultGetter(name: Name): Boolean =
+ name.isTermName && name.asTermName.defaultGetterIndex >= 0
+
+ private val defaultMethodFilter = new NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name)
+ }
+
+ /** Only one overloaded alternative is allowed to define default arguments */
+ private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = {
+ // Using the default getters (such as methodName$default$1) as a cheap way of
+ // finding methods with default parameters. This way, we can limit the members to
+ // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods
+ // directly requires inspecting the parameter list of every one. That modification
+ // shaved 95% off the time spent in this method.
+
+ for (
+ defaultGetterClass <- List(clazz, clazz.companionModule.moduleClass);
+ if defaultGetterClass.isClass
+ ) {
+ val defaultGetterNames = defaultGetterClass.asClass.memberNames(defaultMethodFilter)
+ val defaultMethodNames = defaultGetterNames map (_.asTermName.defaultGetterToMethod)
+
+ for (name <- defaultMethodNames) {
+ val methods = clazz.info.member(name).alternatives.map(_.symbol)
+ val haveDefaults = methods.filter(_.hasDefaultParams)
+ if (haveDefaults.length > 1) {
+ val owners = haveDefaults map (_.owner)
+ // constructors of different classes are allowed to have defaults
+ if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size)
+ ctx.error(
+ "in " + clazz +
+ ", multiple overloaded alternatives of " + haveDefaults.head +
+ " define default arguments" + (
+ if (owners.forall(_ == clazz)) "."
+ else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")),
+ clazz.pos)
+ }
+ }
+ }
+
+ // Check for doomed attempt to overload applyDynamic
+ if (clazz derivesFrom defn.DynamicClass) {
+ for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.symbol.typeParams.length)) {
+ ctx.error("implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)",
+ m1.symbol.pos)
+ }
+ }
+ }
+
+ /** Check that self type of this class conforms to self types of parents.
+ * and required classes.
+ */
+ private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match {
+ case cinfo: ClassInfo =>
+ def checkSelfConforms(other: TypeRef, category: String, relation: String) = {
+ val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol)
+ if (otherSelf.exists && !(cinfo.selfType <:< otherSelf))
+ ctx.error(ex"$category: self type ${cinfo.selfType} of $cls does not conform to self type $otherSelf of $relation ${other.classSymbol}", cls.pos)
+ }
+ for (parent <- cinfo.classParents)
+ checkSelfConforms(parent, "illegal inheritance", "parent")
+ for (reqd <- cinfo.givenSelfType.classSymbols)
+ checkSelfConforms(reqd.typeRef, "missing requirement", "required")
+ case _ =>
+ }
+
+ /** Check that a class and its companion object to not both define
+ * a class or module with same name
+ */
+ private def checkCompanionNameClashes(cls: Symbol)(implicit ctx: Context): Unit =
+ if (!(cls.owner is ModuleClass)) {
+ val other = cls.owner.linkedClass.info.decl(cls.name)
+ if (other.symbol.isClass)
+ ctx.error(s"name clash: ${cls.owner} defines $cls" + "\n" +
+ s"and its companion ${cls.owner.companionModule} also defines $other",
+ cls.pos)
+ }
+
+ // Override checking ------------------------------------------------------------
+
+ /** 1. Check all members of class `clazz` for overriding conditions.
+ * That is for overriding member M and overridden member O:
+ *
+ * 1.1. M must have the same or stronger access privileges as O.
+ * 1.2. O must not be final.
+ * 1.3. O is deferred, or M has `override` modifier.
+ * 1.4. If O is stable, then so is M.
+ * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias
+ * 1.6. If O is a type alias, then M is an alias of O.
+ * 1.7. If O is an abstract type then
+ * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds.
+ * or M is a type alias or class which conforms to O's bounds.
+ * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M
+ * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds
+ * 1.8. If O and M are values, then
+ * 1.8.1 M's type is a subtype of O's type, or
+ * 1.8.2 M is of type []S, O is of type ()T and S <: T, or
+ * 1.8.3 M is of type ()S, O is of type []T and S <: T, or
+ * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O.
+ * 1.10. If M is not a macro def, O cannot be a macro def.
+ * 2. Check that only abstract classes have deferred members
+ * 3. Check that concrete classes do not have deferred definitions
+ * that are not implemented in a subclass.
+ * 4. Check that every member with an `override` modifier
+ * overrides some other member.
+ * TODO check that classes are not overridden
+ * TODO This still needs to be cleaned up; the current version is a staright port of what was there
+ * before, but it looks too complicated and method bodies are far too large.
+ */
+ private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = {
+ val self = clazz.thisType
+ var hasErrors = false
+
+ case class MixinOverrideError(member: Symbol, msg: String)
+
+ val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]()
+
+ def printMixinOverrideErrors(): Unit = {
+ mixinOverrideErrors.toList match {
+ case List() =>
+ case List(MixinOverrideError(_, msg)) =>
+ ctx.error(msg, clazz.pos)
+ case MixinOverrideError(member, msg) :: others =>
+ val others1 = others.map(_.member).filter(_.name != member.name).distinct
+ def othersMsg = {
+ val others1 = others.map(_.member)
+ .filter(_.name != member.name)
+ .map(_.show).distinct
+ if (others1.isEmpty) ""
+ else i";\n other members with override errors are:: $others1%, %"
+ }
+ ctx.error(msg + othersMsg, clazz.pos)
+ }
+ }
+
+ def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz)
+ def infoStringWithLocation(sym: Symbol) = infoString0(sym, true)
+
+ def infoString0(sym: Symbol, showLocation: Boolean) = {
+ val sym1 = sym.underlyingSymbol
+ def info = self.memberInfo(sym1)
+ i"${if (showLocation) sym1.showLocated else sym1}${
+ if (sym1.isAliasType) i", which equals ${info.bounds.hi}"
+ else if (sym1.isAbstractType) i" with bounds$info"
+ else if (sym1.is(Module)) ""
+ else if (sym1.isTerm) i" of type $info"
+ else ""
+ }"
+ }
+
+ /* Check that all conditions for overriding `other` by `member`
+ * of class `clazz` are met.
+ */
+ def checkOverride(member: Symbol, other: Symbol): Unit = {
+ def memberTp = self.memberInfo(member)
+ def otherTp = self.memberInfo(other)
+
+ ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated))
+
+ def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous
+
+ def overrideErrorMsg(msg: String): String = {
+ val isConcreteOverAbstract =
+ (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred)
+ val addendum =
+ if (isConcreteOverAbstract)
+ ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format(
+ infoStringWithLocation(other),
+ infoStringWithLocation(member))
+ else if (ctx.settings.debug.value)
+ err.typeMismatchMsg(memberTp, otherTp)
+ else ""
+
+ "overriding %s;\n %s %s%s".format(
+ infoStringWithLocation(other), infoString(member), msg, addendum)
+ }
+
+ def emitOverrideError(fullmsg: String) =
+ if (!(hasErrors && member.is(Synthetic) && member.is(Module))) {
+ // suppress errors relating toi synthetic companion objects if other override
+ // errors (e.g. relating to the companion class) have already been reported.
+ if (member.owner == clazz) ctx.error(fullmsg, member.pos)
+ else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
+ hasErrors = true
+ }
+
+ def overrideError(msg: String) = {
+ if (noErrorType)
+ emitOverrideError(overrideErrorMsg(msg))
+ }
+
+ def autoOverride(sym: Symbol) =
+ sym.is(Synthetic) && (
+ desugar.isDesugaredCaseClassMethodName(member.name) || // such names are added automatically, can't have an override preset.
+ sym.is(Module)) // synthetic companion
+
+ def overrideAccessError() = {
+ ctx.log(i"member: ${member.showLocated} ${member.flags}") // DEBUG
+ ctx.log(i"other: ${other.showLocated} ${other.flags}") // DEBUG
+ val otherAccess = (other.flags & AccessFlags).toString
+ overrideError("has weaker access privileges; it should be " +
+ (if (otherAccess == "") "public" else "at least " + otherAccess))
+ }
+
+ def compatibleTypes =
+ if (member.isType) { // intersection of bounds to refined types must be nonempty
+ member.is(BaseTypeArg) ||
+ (memberTp frozen_<:< otherTp) || {
+ val jointBounds = (memberTp.bounds & otherTp.bounds).bounds
+ jointBounds.lo frozen_<:< jointBounds.hi
+ }
+ }
+ else
+ isDefaultGetter(member.name) || // default getters are not checked for compatibility
+ memberTp.overrides(otherTp)
+
+ def domain(sym: Symbol): Set[Name] = sym.info.namedTypeParams.map(_.name)
+
+ //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG
+
+ // return if we already checked this combination elsewhere
+ if (member.owner != clazz) {
+ def deferredCheck = member.is(Deferred) || !other.is(Deferred)
+ def subOther(s: Symbol) = s derivesFrom other.owner
+ def subMember(s: Symbol) = s derivesFrom member.owner
+
+ if (subOther(member.owner) && deferredCheck) {
+ //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
+ return
+ }
+ val parentSymbols = clazz.info.parents.map(_.typeSymbol)
+ if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) {
+ //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ if (parentSymbols forall (p => subOther(p) == subMember(p))) {
+ //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ }
+
+ /* Is the intersection between given two lists of overridden symbols empty? */
+ def intersectionIsEmpty(syms1: Iterator[Symbol], syms2: Iterator[Symbol]) = {
+ val set2 = syms2.toSet
+ !(syms1 exists (set2 contains _))
+ }
+
+ // o: public | protected | package-protected (aka java's default access)
+ // ^-may be overridden by member with access privileges-v
+ // m: public | public/protected | public/protected/package-protected-in-same-package-as-o
+
+ if (member.is(Private)) // (1.1)
+ overrideError("has weaker access privileges; it should not be private")
+
+ // todo: align accessibility implication checking with isAccessible in Contexts
+ val ob = other.accessBoundary(member.owner)
+ val mb = member.accessBoundary(member.owner)
+ def isOverrideAccessOK = (
+ (member.flags & AccessFlags).isEmpty // member is public
+ || // - or -
+ (!other.is(Protected) || member.is(Protected)) && // if o is protected, so is m, and
+ (ob.isContainedIn(mb) || other.is(JavaProtected)) // m relaxes o's access boundary,
+ // or o is Java defined and protected (see #3946)
+ )
+ if (!isOverrideAccessOK) {
+ overrideAccessError()
+ } else if (other.isClass) {
+ overrideError("cannot be used here - class definitions cannot be overridden")
+ } else if (!other.is(Deferred) && member.isClass) {
+ overrideError("cannot be used here - classes can only override abstract types")
+ } else if (other.isEffectivelyFinal) { // (1.2)
+ overrideError(i"cannot override final member ${other.showLocated}")
+ } else if (!other.is(Deferred) &&
+ !isDefaultGetter(other.name) &&
+ !member.isAnyOverride) {
+ // (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to
+ // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
+ if (autoOverride(member))
+ member.setFlag(Override)
+ else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
+ emitOverrideError(
+ clazz + " inherits conflicting members:\n "
+ + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member)
+ + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)")
+ else
+ overrideError("needs `override' modifier")
+ } else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) {
+ overrideError("needs `abstract override' modifiers")
+ } else if (member.is(Override) && other.is(Accessor) &&
+ other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) {
+ // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here.
+ // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches.
+ if (!ctx.settings.overrideVars.value)
+ overrideError("cannot override a mutable variable")
+ } else if (member.isAnyOverride &&
+ !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) &&
+ !member.is(Deferred) && !other.is(Deferred) &&
+ intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) {
+ overrideError("cannot override a concrete member without a third member that's overridden by both " +
+ "(this rule is designed to prevent ``accidental overrides'')")
+ } else if (other.isStable && !member.isStable) { // (1.4)
+ overrideError("needs to be a stable, immutable value")
+ } else if (member.is(ModuleVal) && !other.isRealMethod && !other.is(Deferred | Lazy)) {
+ overrideError("may not override a concrete non-lazy value")
+ } else if (member.is(Lazy, butNot = Module) && !other.isRealMethod && !other.is(Lazy)) {
+ overrideError("may not override a non-lazy value")
+ } else if (other.is(Lazy) && !other.isRealMethod && !member.is(Lazy)) {
+ overrideError("must be declared lazy to override a lazy value")
+ } else if (other.is(Deferred) && member.is(Macro) && member.extendedOverriddenSymbols.forall(_.is(Deferred))) { // (1.9)
+ overrideError("cannot be used here - term macros cannot override abstract methods")
+ } else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
+ overrideError("cannot be used here - only term macros can override term macros")
+ } else if (!compatibleTypes) {
+ overrideError("has incompatible type" + err.whyNoMatchStr(memberTp, otherTp))
+ } else if (member.isType && domain(member) != domain(other)) {
+ overrideError("has different named type parameters: "+
+ i"[${domain(member).toList}%, %] instead of [${domain(other).toList}%, %]")
+ } else {
+ checkOverrideDeprecated()
+ }
+ }
+
+ /* TODO enable; right now the annotation is scala-private, so cannot be seen
+ * here.
+ */
+ def checkOverrideDeprecated() = { /*
+ if (other.hasDeprecatedOverridingAnnotation) {
+ val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse ""
+ val msg = s"overriding ${other.fullLocationString} is deprecated$suffix"
+ unit.deprecationWarning(member.pos, msg)
+ }*/
+ }
+
+ try {
+ val opc = new OverridingPairs.Cursor(clazz)
+ while (opc.hasNext) {
+ checkOverride(opc.overriding, opc.overridden)
+ opc.next()
+ }
+ } catch {
+ case ex: MergeError =>
+ val addendum = ex.tp1 match {
+ case tp1: ClassInfo =>
+ "\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)"
+ case _ => ""
+ }
+ ctx.error(ex.getMessage + addendum, clazz.pos)
+ }
+ printMixinOverrideErrors()
+
+ // Verifying a concrete class has nothing unimplemented.
+ if (!clazz.is(AbstractOrTrait)) {
+ val abstractErrors = new mutable.ListBuffer[String]
+ def abstractErrorMessage =
+ // a little formatting polish
+ if (abstractErrors.size <= 2) abstractErrors mkString " "
+ else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "")
+
+ def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = {
+ def prelude = (
+ if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
+ else if (mustBeMixin) clazz + " needs to be a mixin"
+ else clazz + " needs to be abstract") + ", since"
+
+ if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
+ else abstractErrors += msg
+ }
+
+ def hasJavaErasedOverriding(sym: Symbol): Boolean =
+ !ctx.erasurePhase.exists || // can't do the test, assume the best
+ ctx.atPhase(ctx.erasurePhase.next) { implicit ctx =>
+ clazz.info.nonPrivateMember(sym.name).hasAltWith { alt =>
+ alt.symbol.is(JavaDefined, butNot = Deferred) &&
+ !sym.owner.derivesFrom(alt.symbol.owner) &&
+ alt.matches(sym)
+ }
+ }
+
+ def ignoreDeferred(member: SingleDenotation) =
+ member.isType ||
+ member.symbol.is(SuperAccessor) || // not yet synthesized
+ member.symbol.is(JavaDefined) && hasJavaErasedOverriding(member.symbol)
+
+ // 2. Check that only abstract classes have deferred members
+ def checkNoAbstractMembers(): Unit = {
+ // Avoid spurious duplicates: first gather any missing members.
+ val missing = clazz.thisType.abstractTermMembers.filterNot(ignoreDeferred)
+ // Group missing members by the name of the underlying symbol,
+ // to consolidate getters and setters.
+ val grouped: Map[Name, Seq[SingleDenotation]] = missing groupBy (_.symbol.underlyingSymbol.name)
+ // Dotty deviation: Added type annotation for `grouped`.
+ // The inferred type is Map[Symbol#ThisName, Seq[SingleDenotation]]
+ // but then the definition of isMultiple fails with an error:
+ // RefChecks.scala:379: error: type mismatch:
+ // found : underlying.ThisName
+ // required: dotty.tools.dotc.core.Symbols.Symbol#ThisName
+ //
+ // val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+ // ^
+ // As far as I can see, the complaint is correct, even under the
+ // old reading where Symbol#ThisName means x.ThisName forSome { val x }
+
+ val missingMethods = grouped.toList flatMap {
+ case (name, syms) =>
+ val withoutSetters = syms filterNot (_.symbol.isSetter)
+ if (withoutSetters.nonEmpty) withoutSetters else syms
+ }
+
+ def stubImplementations: List[String] = {
+ // Grouping missing methods by the declaring class
+ val regrouped = missingMethods.groupBy(_.symbol.owner).toList
+ def membersStrings(members: List[SingleDenotation]) =
+ members.sortBy(_.symbol.name.toString).map(_.showDcl + " = ???")
+
+ if (regrouped.tail.isEmpty)
+ membersStrings(regrouped.head._2)
+ else (regrouped.sortBy("" + _._1.name) flatMap {
+ case (owner, members) =>
+ ("// Members declared in " + owner.fullName) +: membersStrings(members) :+ ""
+ }).init
+ }
+
+ // If there are numerous missing methods, we presume they are aware of it and
+ // give them a nicely formatted set of method signatures for implementing.
+ if (missingMethods.size > 1) {
+ abstractClassError(false, "it has " + missingMethods.size + " unimplemented members.")
+ val preface =
+ """|/** As seen from %s, the missing signatures are as follows.
+ | * For convenience, these are usable as stub implementations.
+ | */
+ |""".stripMargin.format(clazz)
+ abstractErrors += stubImplementations.map(" " + _ + "\n").mkString(preface, "", "")
+ return
+ }
+
+ for (member <- missing) {
+ val memberSym = member.symbol
+ def undefined(msg: String) =
+ abstractClassError(false, s"${member.showDcl} is not defined $msg")
+ val underlying = memberSym.underlyingSymbol
+
+ // Give a specific error message for abstract vars based on why it fails:
+ // It could be unimplemented, have only one accessor, or be uninitialized.
+ if (underlying.is(Mutable)) {
+ val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+
+ // If both getter and setter are missing, squelch the setter error.
+ if (memberSym.isSetter && isMultiple) ()
+ else undefined(
+ if (memberSym.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)"
+ else if (memberSym.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)"
+ else err.abstractVarMessage(memberSym))
+ } else if (underlying.is(Method)) {
+ // If there is a concrete method whose name matches the unimplemented
+ // abstract method, and a cursory examination of the difference reveals
+ // something obvious to us, let's make it more obvious to them.
+ val abstractParams = underlying.info.firstParamTypes
+ val matchingName = clazz.info.nonPrivateMember(underlying.name).alternatives
+ val matchingArity = matchingName filter { m =>
+ !m.symbol.is(Deferred) &&
+ m.info.firstParamTypes.length == abstractParams.length
+ }
+
+ matchingArity match {
+ // So far so good: only one candidate method
+ case concrete :: Nil =>
+ val mismatches =
+ abstractParams.zip(concrete.info.firstParamTypes)
+ .filterNot { case (x, y) => x =:= y }
+ mismatches match {
+ // Only one mismatched parameter: say something useful.
+ case (pa, pc) :: Nil =>
+ val abstractSym = pa.typeSymbol
+ val concreteSym = pc.typeSymbol
+ def subclassMsg(c1: Symbol, c2: Symbol) =
+ s": ${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly."
+ val addendum =
+ if (abstractSym == concreteSym) {
+ val paArgs = pa.argInfos
+ val pcArgs = pc.argInfos
+ val paConstr = pa.withoutArgs(paArgs)
+ val pcConstr = pc.withoutArgs(pcArgs)
+ (paConstr, pcConstr) match {
+ case (TypeRef(pre1, _), TypeRef(pre2, _)) =>
+ if (pre1 =:= pre2) ": their type parameters differ"
+ else ": their prefixes (i.e. enclosing instances) differ"
+ case _ =>
+ ""
+ }
+ } else if (abstractSym isSubClass concreteSym)
+ subclassMsg(abstractSym, concreteSym)
+ else if (concreteSym isSubClass abstractSym)
+ subclassMsg(concreteSym, abstractSym)
+ else ""
+
+ undefined(s"\n(Note that ${pa.show} does not match ${pc.show}$addendum)")
+ case xs =>
+ undefined(s"\n(The class implements a member with a different type: ${concrete.showDcl})")
+ }
+ case Nil =>
+ undefined("")
+ case concretes =>
+ undefined(s"\n(The class implements members with different types: ${concretes.map(_.showDcl)}%\n %)")
+ }
+ } else undefined("")
+ }
+ }
+
+ // 3. Check that concrete classes do not have deferred definitions
+ // that are not implemented in a subclass.
+ // Note that this is not the same as (2); In a situation like
+ //
+ // class C { def m: Int = 0}
+ // class D extends C { def m: Int }
+ //
+ // (3) is violated but not (2).
+ def checkNoAbstractDecls(bc: Symbol): Unit = {
+ for (decl <- bc.info.decls) {
+ if (decl.is(Deferred) && !ignoreDeferred(decl)) {
+ val impl = decl.matchingMember(clazz.thisType)
+ if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) {
+ val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG
+ ctx.log(i"${impl1}: ${impl1.info}") // DEBUG
+ ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG
+ abstractClassError(false, "there is a deferred declaration of " + infoString(decl) +
+ " which is not implemented in a subclass" + err.abstractVarMessage(decl))
+ }
+ }
+ }
+ if (bc.asClass.superClass.is(Abstract))
+ checkNoAbstractDecls(bc.asClass.superClass)
+ }
+
+ checkNoAbstractMembers()
+ if (abstractErrors.isEmpty)
+ checkNoAbstractDecls(clazz)
+
+ if (abstractErrors.nonEmpty)
+ ctx.error(abstractErrorMessage, clazz.pos)
+ } else if (clazz.is(Trait) && !(clazz derivesFrom defn.AnyValClass)) {
+ // For non-AnyVal classes, prevent abstract methods in interfaces that override
+ // final members in Object; see #4431
+ for (decl <- clazz.info.decls) {
+ // Have to use matchingSymbol, not a method involving overridden symbols,
+ // because the scala type system understands that an abstract method here does not
+ // override a concrete method in Object. The jvm, however, does not.
+ val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType)
+ if (overridden.is(Final))
+ ctx.error("trait cannot redefine final method from class AnyRef", decl.pos)
+ }
+ }
+
+ /* Returns whether there is a symbol declared in class `inclazz`
+ * (which must be different from `clazz`) whose name and type
+ * seen as a member of `class.thisType` matches `member`'s.
+ */
+ def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = {
+
+ def isSignatureMatch(sym: Symbol) = !sym.isTerm ||
+ clazz.thisType.memberInfo(sym).matchesLoosely(member.info)
+
+ /* The rules for accessing members which have an access boundary are more
+ * restrictive in java than scala. Since java has no concept of package nesting,
+ * a member with "default" (package-level) access can only be accessed by members
+ * in the exact same package. Example:
+ *
+ * package a.b;
+ * public class JavaClass { void foo() { } }
+ *
+ * The member foo() can be accessed only from members of package a.b, and not
+ * nested packages like a.b.c. In the analogous scala class:
+ *
+ * package a.b
+ * class ScalaClass { private[b] def foo() = () }
+ *
+ * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic
+ * is restricting the set of matching signatures according to the above semantics.
+ */
+ def javaAccessCheck(sym: Symbol) = (
+ !inclazz.is(JavaDefined) // not a java defined member
+ || !sym.privateWithin.exists // no access boundary
+ || sym.is(Protected) // marked protected in java, thus accessible to subclasses
+ || sym.privateWithin == member.enclosingPackageClass // exact package match
+ )
+ def classDecls = inclazz.info.nonPrivateDecl(member.name)
+
+ (inclazz != clazz) &&
+ classDecls.hasAltWith(d => isSignatureMatch(d.symbol) && javaAccessCheck(d.symbol))
+ }
+
+ // 4. Check that every defined member with an `override` modifier overrides some other member.
+ for (member <- clazz.info.decls)
+ if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
+ // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG
+
+ val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final))
+ def issueError(suffix: String) =
+ ctx.error(i"$member overrides nothing$suffix", member.pos)
+ nonMatching match {
+ case Nil =>
+ issueError("")
+ case ms =>
+ val superSigs = ms.map(_.showDcl).mkString("\n")
+ issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}")
+ }
+ member.resetFlag(Override)
+ member.resetFlag(AbsOverride)
+ }
+ }
+
+ // Note: if a symbol has both @deprecated and @migration annotations and both
+ // warnings are enabled, only the first one checked here will be emitted.
+ // I assume that's a consequence of some code trying to avoid noise by suppressing
+ // warnings after the first, but I think it'd be better if we didn't have to
+ // arbitrarily choose one as more important than the other.
+ private def checkUndesiredProperties(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = {
+ // If symbol is deprecated, and the point of reference is not enclosed
+ // in either a deprecated member or a scala bridge method, issue a warning.
+ if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated)) {
+ ctx.deprecationWarning("%s%s is deprecated%s".format(
+ sym, sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse "", pos))
+ }
+ // Similar to deprecation: check if the symbol is marked with @migration
+ // indicating it has changed semantics between versions.
+ if (sym.hasAnnotation(defn.MigrationAnnot) && ctx.settings.Xmigration.value != NoScalaVersion) {
+ val symVersion: scala.util.Try[ScalaVersion] = sym.migrationVersion.get
+ val changed = symVersion match {
+ case scala.util.Success(v) =>
+ ctx.settings.Xmigration.value < v
+ case Failure(ex) =>
+ ctx.warning(s"${sym.showLocated} has an unparsable version number: ${ex.getMessage()}", pos)
+ false
+ }
+ if (changed)
+ ctx.warning(s"${sym.showLocated} has changed semantics in version $symVersion:\n${sym.migrationMessage.get}")
+ }
+ /* (Not enabled yet)
+ * See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly.
+ *
+ if (sym.isCompileTimeOnly) {
+ def defaultMsg =
+ sm"""Reference to ${sym.fullLocationString} should not have survived past type checking,
+ |it should have been processed and eliminated during expansion of an enclosing macro."""
+ // The getOrElse part should never happen, it's just here as a backstop.
+ ctx.error(sym.compileTimeOnlyMessage getOrElse defaultMsg, pos)
+ }*/
+ }
+
+ /** Check that a deprecated val or def does not override a
+ * concrete, non-deprecated method. If it does, then
+ * deprecation is meaningless.
+ */
+ private def checkDeprecatedOvers(tree: Tree)(implicit ctx: Context): Unit = {
+ val symbol = tree.symbol
+ if (symbol.isDeprecated) {
+ val concrOvers =
+ symbol.allOverriddenSymbols.filter(sym =>
+ !sym.isDeprecated && !sym.is(Deferred))
+ if (!concrOvers.isEmpty)
+ ctx.deprecationWarning(
+ symbol.toString + " overrides concrete, non-deprecated symbol(s):" +
+ concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos)
+ }
+ }
+
+ /** Verify classes extending AnyVal meet the requirements */
+ private def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = {
+ def checkValueClassMember(stat: Tree) = stat match {
+ case _: ValDef if !stat.symbol.is(ParamAccessor) =>
+ ctx.error(s"value class may not define non-parameter field", stat.pos)
+ case _: DefDef if stat.symbol.isConstructor =>
+ ctx.error(s"value class may not define secondary constructor", stat.pos)
+ case _: MemberDef | _: Import | EmptyTree =>
+ // ok
+ case _ =>
+ ctx.error(s"value class may not contain initialization statements", stat.pos)
+ }
+ if (isDerivedValueClass(clazz)) {
+ if (clazz.is(Trait))
+ ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos)
+ if (clazz.is(Abstract))
+ ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
+ if (!clazz.isStatic)
+ ctx.error(s"value class may not be a ${if (clazz.owner.isTerm) "local class" else "member of another class"}", clazz.pos)
+ else {
+ val clParamAccessors = clazz.asClass.paramAccessors.filter(sym => sym.isTerm && !sym.is(Method))
+ clParamAccessors match {
+ case List(param) =>
+ if (param.is(Mutable))
+ ctx.error("value class parameter must not be a var", param.pos)
+ case _ =>
+ ctx.error("value class needs to have exactly one val parameter", clazz.pos)
+ }
+ }
+ stats.foreach(checkValueClassMember)
+ }
+ }
+
+ type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]
+
+ class OptLevelInfo extends DotClass {
+ def levelAndIndex: LevelAndIndex = Map()
+ def enterReference(sym: Symbol, pos: Position): Unit = ()
+ }
+
+ /** A class to help in forward reference checking */
+ class LevelInfo(outerLevelAndIndex: LevelAndIndex, stats: List[Tree])(implicit ctx: Context)
+ extends OptLevelInfo {
+ override val levelAndIndex: LevelAndIndex =
+ ((outerLevelAndIndex, 0) /: stats) {(mi, stat) =>
+ val (m, idx) = mi
+ val m1 = stat match {
+ case stat: MemberDef => m.updated(stat.symbol, (this, idx))
+ case _ => m
+ }
+ (m1, idx + 1)
+ }._1
+ var maxIndex: Int = Int.MinValue
+ var refPos: Position = _
+ var refSym: Symbol = _
+
+ override def enterReference(sym: Symbol, pos: Position): Unit =
+ if (sym.exists && sym.owner.isTerm)
+ levelAndIndex.get(sym) match {
+ case Some((level, idx)) if (level.maxIndex < idx) =>
+ level.maxIndex = idx
+ level.refPos = pos
+ level.refSym = sym
+ case _ =>
+ }
+ }
+
+ val NoLevelInfo = new OptLevelInfo()
+}
+import RefChecks._
+
+/** Post-attribution checking and transformation, which fulfills the following roles
+ *
+ * 1. This phase performs the following checks.
+ *
+ * - only one overloaded alternative defines default arguments
+ * - applyDynamic methods are not overloaded
+ * - all overrides conform to rules laid down by `checkAllOverrides`.
+ * - any value classes conform to rules laid down by `checkDerivedValueClass`.
+ * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals).
+ * - no forward reference in a local block jumps over a non-lazy val definition.
+ * - a class and its companion object do not both define a class or module with the same name.
+ *
+ * 2. It warns about references to symbols labeled deprecated or migration.
+
+ * 3. It performs the following transformations:
+ *
+ * - if (true) A else B --> A
+ * if (false) A else B --> B
+ * - macro definitions are eliminated.
+ *
+ * 4. It makes members not private where necessary. The following members
+ * cannot be private in the Java model:
+ * - term members of traits
+ * - the primary constructor of a value class
+ * - the parameter accessor of a value class
+ * - members accessed from an inner or companion class.
+ * All these members are marked as NotJavaPrivate.
+ * Unlike in Scala 2.x not-private members keep their name. It is
+ * up to the backend to find a unique expanded name for them. The
+ * rationale to do name changes that late is that they are very fragile.
+
+ * todo: But RefChecks is not done yet. It's still a somewhat dirty port from the Scala 2 version.
+ * todo: move untrivial logic to their own mini-phases
+ */
+class RefChecks extends MiniPhase { thisTransformer =>
+
+ import tpd._
+
+ override def phaseName: String = "refchecks"
+
+ val treeTransform = new Transform(NoLevelInfo)
+
+ class Transform(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform {
+ def phase = thisTransformer
+
+ override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = {
+ // println(i"preparing for $trees%; %, owner = ${ctx.owner}")
+ if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees))
+ else this
+ }
+
+ override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
+
+ override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ val sym = tree.symbol
+ if (sym.exists && sym.owner.isTerm && !sym.is(Lazy))
+ currentLevel.levelAndIndex.get(sym) match {
+ case Some((level, symIdx)) if symIdx < level.maxIndex =>
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error(s"forward reference extends over definition of $sym", level.refPos)
+ case _ =>
+ }
+ tree
+ }
+
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ if (tree.symbol is Macro) EmptyTree else tree
+ }
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = try {
+ val cls = ctx.owner
+ checkOverloadedRestrictions(cls)
+ checkParents(cls)
+ checkCompanionNameClashes(cls)
+ checkAllOverrides(cls)
+ checkDerivedValueClass(cls, tree.body)
+ tree
+ } catch {
+ case ex: MergeError =>
+ ctx.error(ex.getMessage, tree.pos)
+ tree
+ }
+
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ currentLevel.enterReference(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = {
+ if (isSelfConstrCall(tree)) {
+ assert(currentLevel.isInstanceOf[LevelInfo], ctx.owner + "/" + i"$tree")
+ val level = currentLevel.asInstanceOf[LevelInfo]
+ if (level.maxIndex > 0) {
+ // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error("forward reference not allowed from self constructor invocation", level.refPos)
+ }
+ }
+ tree
+ }
+
+ override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
+ tree.cond.tpe match {
+ case ConstantType(value) => if (value.booleanValue) tree.thenp else tree.elsep
+ case _ => tree
+ }
+
+ override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = {
+ currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos)
+ tree
+ }
+
+ override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ tree.fun match {
+ case fun@Select(qual, selector) =>
+ val sym = tree.symbol
+
+ if (sym == defn.Any_isInstanceOf) {
+ val argType = tree.args.head.tpe
+ val qualCls = qual.tpe.widen.classSymbol
+ val argCls = argType.classSymbol
+ if (qualCls.isPrimitiveValueClass && !argCls.isPrimitiveValueClass) ctx.error("isInstanceOf cannot test if value types are references", tree.pos)
+ }
+ case _ =>
+ }
+ tree
+ }
+ }
+}
+
+/* todo: rewrite and re-enable
+
+// Comparison checking -------------------------------------------------------
+
+ object normalizeAll extends TypeMap {
+ def apply(tp: Type) = mapOver(tp).normalize
+ }
+
+ def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match {
+ case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply =>
+ unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567
+ case _ =>
+ }
+
+ private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match {
+ case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true
+ case _ => false
+ }
+ /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */
+ private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = {
+ def isReferenceOp = sym == Object_eq || sym == Object_ne
+ def isNew(tree: Tree) = tree match {
+ case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
+ case _ => false
+ }
+ def underlyingClass(tp: Type): Symbol = {
+ val sym = tp.widen.typeSymbol
+ if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi)
+ else sym
+ }
+ val actual = underlyingClass(other.tpe)
+ val receiver = underlyingClass(qual.tpe)
+ def onTrees[T](f: List[Tree] => T) = f(List(qual, other))
+ def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual))
+
+ // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol`
+ def typesString = normalizeAll(qual.tpe.widen)+" and " + normalizeAll(other.tpe.widen)
+
+ /* Symbols which limit the warnings we can issue since they may be value types */
+ val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass)
+
+ // Whether def equals(other: Any) has known behavior: it is the default
+ // inherited from java.lang.Object, or it is a synthetically generated
+ // case equals. TODO - more cases are warnable if the target is a synthetic
+ // equals.
+ def isUsingWarnableEquals = {
+ val m = receiver.info.member(nme.equals_)
+ ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m))
+ }
+ def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase
+ def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_))
+ // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
+ def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!=
+ def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual)
+
+ // Whether the operands+operator represent a warnable combo (assuming anyrefs)
+ // Looking for comparisons performed with ==/!= in combination with either an
+ // equals method inherited from Object or a case class synthetic equals (for
+ // which we know the logic.)
+ def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals)
+ def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info)
+ def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass
+ def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass
+ def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass
+ def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s)
+ def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass
+ def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass
+ // includes java.lang.Number if appropriate [SI-5779]
+ def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s)
+ def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s)
+ // used to short-circuit unrelatedTypes check if both sides are special
+ def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s)
+ val nullCount = onSyms(_ filter (_ == NullClass) size)
+ def isNonsenseValueClassCompare = (
+ !haveSubclassRelationship
+ && isUsingDefaultScalaOp
+ && isEitherValueClass
+ && !isCaseEquals
+ )
+
+ // Have we already determined that the comparison is non-sensible? I mean, non-sensical?
+ var isNonSensible = false
+
+ def nonSensibleWarning(what: String, alwaysEqual: Boolean) = {
+ val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
+ unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg")
+ isNonSensible = true
+ }
+ def nonSensible(pre: String, alwaysEqual: Boolean) =
+ nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual)
+ def nonSensiblyEq() = nonSensible("", alwaysEqual = true)
+ def nonSensiblyNeq() = nonSensible("", alwaysEqual = false)
+ def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false)
+
+ def unrelatedMsg = name match {
+ case nme.EQ | nme.eq => "never compare equal"
+ case _ => "always compare unequal"
+ }
+ def unrelatedTypes() = if (!isNonSensible) {
+ val weaselWord = if (isEitherValueClass) "" else " most likely"
+ unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg")
+ }
+
+ if (nullCount == 2) // null == null
+ nonSensiblyEq()
+ else if (nullCount == 1) {
+ if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
+ nonSensiblyNeq()
+ else if (onTrees( _ exists isNew)) // null == new AnyRef
+ nonSensiblyNew()
+ }
+ else if (isBoolean(receiver)) {
+ if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5
+ nonSensiblyNeq()
+ }
+ else if (isUnit(receiver)) {
+ if (isUnit(actual)) // () == ()
+ nonSensiblyEq()
+ else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isNumeric(receiver)) {
+ if (!isNumeric(actual))
+ if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isWarnable && !isCaseEquals) {
+ if (isNew(qual)) // new X == y
+ nonSensiblyNew()
+ else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
+ nonSensiblyNew()
+ else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y
+ if (isEitherNullable)
+ nonSensible("non-null ", false)
+ else
+ nonSensiblyNeq()
+ }
+ }
+
+ // warn if one but not the other is a derived value class
+ // this is especially important to enable transitioning from
+ // regular to value classes without silent failures.
+ if (isNonsenseValueClassCompare)
+ unrelatedTypes()
+ // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean
+ else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) {
+ // better to have lubbed and lost
+ def warnIfLubless(): Unit = {
+ val common = global.lub(List(actual.tpe, receiver.tpe))
+ if (ObjectTpe <:< common)
+ unrelatedTypes()
+ }
+ // warn if actual has a case parent that is not same as receiver's;
+ // if actual is not a case, then warn if no common supertype, as below
+ if (isCaseEquals) {
+ def thisCase = receiver.info.member(nme.equals_).owner
+ actual.info.baseClasses.find(_.isCase) match {
+ case Some(p) if p != thisCase => nonSensible("case class ", false)
+ case None =>
+ // stronger message on (Some(1) == None)
+ //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq()
+ //else
+ // if a class, it must be super to thisCase (and receiver) since not <: thisCase
+ if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq()
+ else if (!haveSubclassRelationship) warnIfLubless()
+ case _ =>
+ }
+ }
+ // warn only if they have no common supertype below Object
+ else if (!haveSubclassRelationship) {
+ warnIfLubless()
+ }
+ }
+ }
+ /** Sensibility check examines flavors of equals. */
+ def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match {
+ case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) =>
+ checkSensibleEquals(pos, qual, name, fn.symbol, args.head)
+ case _ =>
+ }
+*/
+
+/* --------------- Overflow -------------------------------------------------
+ *
+
+ def accessFlagsToString(sym: Symbol) = flagsToString(
+ sym getFlag (PRIVATE | PROTECTED),
+ if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
+ )
+
+ def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match {
+ case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
+ rtp1 <:< rtp2
+ case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
+ rtp1 <:< rtp2
+ case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
+ overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix)
+ case _ =>
+ def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner)
+
+ (tp1 <:< tp2) || ( // object override check
+ tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
+ val cb1 = classBoundAsSeen(tp1)
+ val cb2 = classBoundAsSeen(tp2)
+ (cb1 <:< cb2) && {
+ log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
+ true
+ }
+ }
+ )
+ }
+ private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean)(implicit ctx: Context) = tp match {
+ case TypeRef(pre, sym, args) =>
+ tree match {
+ case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types
+ // FIXME: reconcile this check with one in resetAttrs
+ case _ => checkUndesiredProperties(sym, tree.pos)
+ }
+ if (sym.isJavaDefined)
+ sym.typeParams foreach (_.cookJavaRawInfo())
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ case _ =>
+ }
+
+ private def checkTypeRefBounds(tp: Type, tree: Tree) = {
+ var skipBounds = false
+ tp match {
+ case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass =>
+ skipBounds = true
+ underlying
+ case TypeRef(pre, sym, args) =>
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ tp
+ case _ =>
+ tp
+ }
+ }
+
+ private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp =>
+ checkTypeRef(tp, tree, skipBounds = false)
+ checkTypeRefBounds(tp, tree)
+ }
+ private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
+
+ private def applyRefchecksToAnnotations(tree: Tree)(implicit ctx: Context): Unit = {
+ def applyChecks(annots: List[Annotation]) = {
+ checkAnnotations(annots map (_.atp), tree)
+ transformTrees(annots flatMap (_.args))
+ }
+
+ tree match {
+ case m: MemberDef =>
+ val sym = m.symbol
+ applyChecks(sym.annotations)
+ // validate implicitNotFoundMessage
+ analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
+ unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn")
+ }
+
+ case tpt@TypeTree() =>
+ if (tpt.original != null) {
+ tpt.original foreach {
+ case dc@TypeTreeWithDeferredRefCheck() =>
+ applyRefchecksToAnnotations(dc.check()) // #2416
+ case _ =>
+ }
+ }
+
+ doTypeTraversal(tree) {
+ case tp @ AnnotatedType(annots, _) =>
+ applyChecks(annots)
+ case tp =>
+ }
+ case _ =>
+ }
+ }
+
+ private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
+ val sym = tree.symbol
+
+ def isClassTypeAccessible(tree: Tree): Boolean = tree match {
+ case TypeApply(fun, targs) =>
+ isClassTypeAccessible(fun)
+ case Select(module, apply) =>
+ ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`;
+ // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`.
+ treeInfo.isQualifierSafeToElide(module) &&
+ // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case,
+ // the companion class is actually not a ClassSymbol, but a reference to an abstract type.
+ module.symbol.companionClass.isClass
+ )
+ }
+
+ val doTransform =
+ sym.isRealMethod &&
+ sym.isCase &&
+ sym.name == nme.apply &&
+ isClassTypeAccessible(tree)
+
+ if (doTransform) {
+ tree foreach {
+ case i@Ident(_) =>
+ enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()`
+ case _ =>
+ }
+ toConstructor(tree.pos, tree.tpe)
+ }
+ else {
+ ifNot
+ tree
+ }
+ }
+
+ private def transformApply(tree: Apply): Tree = tree match {
+ case Apply(
+ Select(qual, nme.filter | nme.withFilter),
+ List(Function(
+ List(ValDef(_, pname, tpt, _)),
+ Match(_, CaseDef(pat1, _, _) :: _))))
+ if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) &&
+ isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) =>
+
+ transform(qual)
+
+ case Apply(fn, args) =>
+ // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability
+ // analyses in the pattern matcher
+ if (!inPattern) {
+ checkImplicitViewOptionApply(tree.pos, fn, args)
+ checkSensible(tree.pos, fn, args)
+ }
+ currentApplication = tree
+ tree
+ }
+ private def transformSelect(tree: Select): Tree = {
+ val Select(qual, _) = tree
+ val sym = tree.symbol
+
+ checkUndesiredProperties(sym, tree.pos)
+ checkDelayedInitSelect(qual, sym, tree.pos)
+
+ if (!sym.exists)
+ devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe)
+ else if (sym.isLocalToThis)
+ varianceValidator.checkForEscape(sym, currentClass)
+
+ def checkSuper(mix: Name) =
+ // term should have been eliminated by super accessors
+ assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix))
+
+ transformCaseApply(tree,
+ qual match {
+ case Super(_, mix) => checkSuper(mix)
+ case _ =>
+ }
+ )
+ }
+ private def transformIf(tree: If): Tree = {
+ val If(cond, thenpart, elsepart) = tree
+ def unitIfEmpty(t: Tree): Tree =
+ if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t
+
+ cond.tpe match {
+ case ConstantType(value) =>
+ val res = if (value.booleanValue) thenpart else elsepart
+ unitIfEmpty(res)
+ case _ => tree
+ }
+ }
+
+ // Warning about nullary methods returning Unit. TODO: move to lint
+ private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match {
+ case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass =>
+ // this may be the implementation of e.g. a generic method being parameterized
+ // on Unit, in which case we had better let it slide.
+ val isOk = (
+ sym.isGetter
+ || (sym.name containsName nme.DEFAULT_GETTER_STRING)
+ || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType))
+ )
+ if (!isOk)
+ unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead")
+ case _ => ()
+ }
+
+ /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */
+ def toConstructor(pos: Position, tpe: Type)(implicit ctx: Context): Tree = {
+ val rtpe = tpe.finalResultType
+ assert(rtpe.typeSymbol.is(Case), tpe)
+ New(rtpe).withPos(pos).select(rtpe.typeSymbol.primaryConstructor)
+ }
+ private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match {
+ case Apply(_, args) =>
+ val clazz = pat.tpe.typeSymbol
+ clazz == seltpe.typeSymbol &&
+ clazz.isCaseClass &&
+ (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable)
+ case Typed(pat, tpt) =>
+ seltpe <:< tpt.tpe
+ case Ident(tpnme.WILDCARD) =>
+ true
+ case Bind(_, pat) =>
+ isIrrefutable(pat, seltpe)
+ case _ =>
+ false
+ }
+ private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = {
+ def isLikelyUninitialized = (
+ (sym.owner isSubClass DelayedInitClass)
+ && !qual.tpe.isInstanceOf[ThisType]
+ && sym.accessedOrSelf.isVal
+ )
+ if (settings.lint.value && isLikelyUninitialized)
+ unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value")
+ }
+ private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = (
+ (otherSym != NoSymbol)
+ && !otherSym.isProtected
+ && !otherSym.isTypeParameterOrSkolem
+ && !otherSym.isExistentiallyBound
+ && (otherSym isLessAccessibleThan memberSym)
+ && (otherSym isLessAccessibleThan memberSym.enclClass)
+ )
+ private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = {
+ val extras = other match {
+ case TypeRef(pre, _, args) =>
+ // checking the prefix here gives us spurious errors on e.g. a private[process]
+ // object which contains a type alias, which normalizes to a visible type.
+ args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym))
+ case _ =>
+ Nil
+ }
+ if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras
+ else extras
+ }
+ private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) {
+ val comparison = accessFlagsToString(memberSym) match {
+ case "" => ""
+ case acc => " is " + acc + " but"
+ }
+ val cannot =
+ if (memberSym.isDeferred) "may be unable to provide a concrete implementation of"
+ else "may be unable to override"
+
+ unit.warning(memberSym.pos,
+ "%s%s references %s %s.".format(
+ memberSym.fullLocationString, comparison,
+ accessFlagsToString(otherSym), otherSym
+ ) + "\nClasses which cannot access %s %s %s.".format(
+ otherSym.decodedName, cannot, memberSym.decodedName)
+ )
+ }
+
+ /** Warn about situations where a method signature will include a type which
+ * has more restrictive access than the method itself.
+ */
+ private def checkAccessibilityOfReferencedTypes(tree: Tree) {
+ val member = tree.symbol
+
+ def checkAccessibilityOfType(tpe: Type) {
+ val inaccessible = lessAccessibleSymsInType(tpe, member)
+ // if the unnormalized type is accessible, that's good enough
+ if (inaccessible.isEmpty) ()
+ // or if the normalized type is, that's good too
+ else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) ()
+ // otherwise warn about the inaccessible syms in the unnormalized type
+ else inaccessible foreach (sym => warnLessAccessible(sym, member))
+ }
+
+ // types of the value parameters
+ mapParamss(member)(p => checkAccessibilityOfType(p.tpe))
+ // upper bounds of type parameters
+ member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType
+ }
+
+ private def checkByNameRightAssociativeDef(tree: DefDef) {
+ tree match {
+ case DefDef(_, name, _, params :: _, _, _) =>
+ if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol)))
+ unit.warning(tree.pos,
+ "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.")
+ case _ =>
+ }
+ }
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ //val savedLocalTyper = localTyper
+ try {
+ val sym = tree.symbol
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+
+ // Apply RefChecks to annotations. Makes sure the annotations conform to
+ // type bounds (bug #935), issues deprecation warnings for symbols used
+ // inside annotations.
+ // applyRefchecksToAnnotations(tree) ???
+ var result: Tree = tree match {
+ case tree: ValOrDefDef =>
+ // move to lint:
+ // if (settings.warnNullaryUnit)
+ // checkNullaryMethodReturnType(sym)
+ // if (settings.warnInaccessible) {
+ // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
+ // checkAccessibilityOfReferencedTypes(tree)
+ // }
+ // tree match {
+ // case dd: DefDef => checkByNameRightAssociativeDef(dd)
+ // case _ =>
+ // }
+ tree
+
+ case Template(constr, parents, self, body) =>
+ // localTyper = localTyper.atOwner(tree, currentOwner)
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+ case tpt: TypeTree =>
+ transform(tpt.original)
+ tree
+
+ case TypeApply(fn, args) =>
+ checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe))
+ transformCaseApply(tree, ())
+
+ case x @ Apply(_, _) =>
+ transformApply(x)
+
+ case x @ If(_, _, _) =>
+ transformIf(x)
+
+ case New(tpt) =>
+ enterReference(tree.pos, tpt.tpe.typeSymbol)
+ tree
+
+ case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) =>
+ unit.error(tree.pos, "no `: _*' annotation allowed here\n" +
+ "(such annotations are only allowed in arguments to *-parameters)")
+ tree
+
+ case Ident(name) =>
+ checkUndesiredProperties(sym, tree.pos)
+ transformCaseApply(tree,
+ if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) {
+ assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug
+ enterReference(tree.pos, sym)
+ }
+ )
+
+ case x @ Select(_, _) =>
+ transformSelect(x)
+
+ case UnApply(fun, args) =>
+ transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun)
+ // transformTrees(args) // TODO: is this necessary? could there be forward references in the args??
+ // probably not, until we allow parameterised extractors
+ tree
+
+
+ case _ => tree
+ }
+
+ // skip refchecks in patterns....
+ result = result match {
+ case CaseDef(pat, guard, body) =>
+ val pat1 = savingInPattern {
+ inPattern = true
+ transform(pat)
+ }
+ treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
+ case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ savingInPattern {
+ inPattern = true
+ deriveLabelDef(result)(transform)
+ }
+ case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) =>
+ savingInPattern {
+ // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals
+ // that we are in the user-supplied code in the case body.
+ //
+ // Relies on the translation of:
+ // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }'
+ // to:
+ // <synthetic> val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]);
+ // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply.
+ inPattern = false
+ super.transform(result)
+ }
+ case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector.
+ case _ =>
+ super.transform(result)
+ }
+ result match {
+ case ClassDef(_, _, _, _)
+ | TypeDef(_, _, _, _) =>
+ if (result.symbol.isLocalToBlock || result.symbol.isTopLevel)
+ varianceValidator.traverse(result)
+ case tt @ TypeTree() if tt.original != null =>
+ varianceValidator.traverse(tt.original) // See SI-7872
+ case _ =>
+ }
+
+ checkUnexpandedMacro(result)
+
+ result
+ } catch {
+ case ex: TypeError =>
+ if (settings.debug) ex.printStackTrace()
+ unit.error(tree.pos, ex.getMessage())
+ tree
+ } finally {
+ localTyper = savedLocalTyper
+ currentApplication = savedCurrentApplication
+ }
+ }
+*/
+
diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
new file mode 100644
index 000000000..ee2d68278
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -0,0 +1,524 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._
+import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._
+import TypeApplications.AppliedType
+import util.Positions._
+import config.Printers.typr
+import ast.Trees._
+import NameOps._
+import collection.mutable
+import reporting.diagnostic.Message
+import reporting.diagnostic.messages._
+
+trait TypeAssigner {
+ import tpd._
+
+ /** The qualifying class of a this or super with prefix `qual` (which might be empty).
+ * @param packageOk The qualifier may refer to a package.
+ */
+ def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(implicit ctx: Context): Symbol = {
+ def qualifies(sym: Symbol) =
+ sym.isClass && (
+ qual.isEmpty ||
+ sym.name == qual ||
+ sym.is(Module) && sym.name.stripModuleClassSuffix == qual)
+ ctx.outersIterator.map(_.owner).find(qualifies) match {
+ case Some(c) if packageOK || !(c is Package) =>
+ c
+ case _ =>
+ ctx.error(
+ if (qual.isEmpty) tree.show + " can be used only in a class, object, or template"
+ else qual.show + " is not an enclosing class", tree.pos)
+ NoSymbol
+ }
+ }
+
+ /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`.
+ * Approximation steps are:
+ *
+ * - follow aliases and upper bounds if the original refers to a forbidden symbol
+ * - widen termrefs that refer to a forbidden symbol
+ * - replace ClassInfos of forbidden classes by the intersection of their parents, refined by all
+ * non-private fields, methods, and type members.
+ * - if the prefix of a class refers to a forbidden symbol, first try to replace the prefix,
+ * if this is not possible, replace the ClassInfo as above.
+ * - drop refinements referring to a forbidden symbol.
+ */
+ def avoid(tp: Type, symsToAvoid: => List[Symbol])(implicit ctx: Context): Type = {
+ val widenMap = new TypeMap {
+ lazy val forbidden = symsToAvoid.toSet
+ def toAvoid(tp: Type): Boolean =
+ // TODO: measure the cost of using `existsPart`, and if necessary replace it
+ // by a `TypeAccumulator` where we have set `stopAtStatic = true`.
+ tp existsPart {
+ case tp: NamedType => forbidden contains tp.symbol
+ case tp: ThisType => forbidden contains tp.cls
+ case _ => false
+ }
+ def apply(tp: Type): Type = tp match {
+ case tp: TermRef
+ if toAvoid(tp) && (variance > 0 || tp.info.widenExpr <:< tp) =>
+ // Can happen if `x: y.type`, then `x.type =:= y.type`, hence we can widen `x.type`
+ // to y.type in all contexts, not just covariant ones.
+ apply(tp.info.widenExpr)
+ case tp: TypeRef if toAvoid(tp) =>
+ tp.info match {
+ case TypeAlias(ref) =>
+ apply(ref)
+ case info: ClassInfo if variance > 0 =>
+ if (!(forbidden contains tp.symbol)) {
+ val prefix = apply(tp.prefix)
+ val tp1 = tp.derivedSelect(prefix)
+ if (tp1.typeSymbol.exists)
+ return tp1
+ }
+ val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _))
+ def addRefinement(parent: Type, decl: Symbol) = {
+ val inherited =
+ parentType.findMember(decl.name, info.cls.thisType, Private)
+ .suchThat(decl.matches(_))
+ val inheritedInfo = inherited.info
+ if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) {
+ val r = RefinedType(parent, decl.name, decl.info)
+ typr.println(i"add ref $parent $decl --> " + r)
+ r
+ }
+ else
+ parent
+ }
+ val refinableDecls = info.decls.filterNot(
+ sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor)
+ val fullType = (parentType /: refinableDecls)(addRefinement)
+ mapOver(fullType)
+ case TypeBounds(lo, hi) if variance > 0 =>
+ apply(hi)
+ case _ =>
+ mapOver(tp)
+ }
+ case tp @ HKApply(tycon, args) if toAvoid(tycon) =>
+ apply(tp.superType)
+ case tp @ AppliedType(tycon, args) if toAvoid(tycon) =>
+ val base = apply(tycon)
+ var args = tp.baseArgInfos(base.typeSymbol)
+ if (base.typeParams.length != args.length)
+ args = base.typeParams.map(_.paramBounds)
+ apply(base.appliedTo(args))
+ case tp @ RefinedType(parent, name, rinfo) if variance > 0 =>
+ val parent1 = apply(tp.parent)
+ val refinedInfo1 = apply(rinfo)
+ if (toAvoid(refinedInfo1)) {
+ typr.println(s"dropping refinement from $tp")
+ if (name.isTypeName) tp.derivedRefinedType(parent1, name, TypeBounds.empty)
+ else parent1
+ } else {
+ tp.derivedRefinedType(parent1, name, refinedInfo1)
+ }
+ case tp: TypeVar if ctx.typerState.constraint.contains(tp) =>
+ val lo = ctx.typerState.constraint.fullLowerBound(tp.origin)
+ val lo1 = avoid(lo, symsToAvoid)
+ if (lo1 ne lo) lo1 else tp
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ widenMap(tp)
+ }
+
+ def avoidingType(expr: Tree, bindings: List[Tree])(implicit ctx: Context): Type =
+ avoid(expr.tpe, localSyms(bindings).filter(_.isTerm))
+
+ def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree =
+ Typed(tree, TypeTree(tree.tpe.widen.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)))
+
+ /** A denotation exists really if it exists and does not point to a stale symbol. */
+ final def reallyExists(denot: Denotation)(implicit ctx: Context): Boolean = try
+ denot match {
+ case denot: SymDenotation =>
+ denot.exists && {
+ denot.ensureCompleted
+ !denot.isAbsent
+ }
+ case denot: SingleDenotation =>
+ val sym = denot.symbol
+ (sym eq NoSymbol) || reallyExists(sym.denot)
+ case _ =>
+ true
+ }
+ catch {
+ case ex: StaleSymbol => false
+ }
+
+ /** If `tpe` is a named type, check that its denotation is accessible in the
+ * current context. Return the type with those alternatives as denotations
+ * which are accessible.
+ *
+ * Also performs the following normalizations on the type `tpe`.
+ * (1) parameter accessors are always dereferenced.
+ * (2) if the owner of the denotation is a package object, it is assured
+ * that the package object shows up as the prefix.
+ */
+ def ensureAccessible(tpe: Type, superAccess: Boolean, pos: Position)(implicit ctx: Context): Type = {
+ def test(tpe: Type, firstTry: Boolean): Type = tpe match {
+ case tpe: NamedType =>
+ val pre = tpe.prefix
+ val name = tpe.name
+ val d = tpe.denot.accessibleFrom(pre, superAccess)
+ if (!d.exists) {
+ // it could be that we found an inaccessible private member, but there is
+ // an inherited non-private member with the same name and signature.
+ val d2 = pre.nonPrivateMember(name)
+ if (reallyExists(d2) && firstTry)
+ test(tpe.shadowed.withDenot(d2), false)
+ else if (pre.derivesFrom(defn.DynamicClass)) {
+ TryDynamicCallType
+ } else {
+ val alts = tpe.denot.alternatives.map(_.symbol).filter(_.exists)
+ val what = alts match {
+ case Nil =>
+ name.toString
+ case sym :: Nil =>
+ if (sym.owner == pre.typeSymbol) sym.show else sym.showLocated
+ case _ =>
+ em"none of the overloaded alternatives named $name"
+ }
+ val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else ""
+ val whyNot = new StringBuffer
+ alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot))
+ if (!tpe.isError)
+ ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos)
+ ErrorType
+ }
+ }
+ else if (d.symbol is TypeParamAccessor)
+ if (d.info.isAlias)
+ ensureAccessible(d.info.bounds.hi, superAccess, pos)
+ else // It's a named parameter, use the non-symbolic representation to pick up inherited versions as well
+ d.symbol.owner.thisType.select(d.symbol.name)
+ else
+ ctx.makePackageObjPrefixExplicit(tpe withDenot d)
+ case _ =>
+ tpe
+ }
+ test(tpe, true)
+ }
+
+ /** The type of a selection with `name` of a tree with type `site`.
+ */
+ def selectionType(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = {
+ val mbr = site.member(name)
+ if (reallyExists(mbr)) site.select(name, mbr)
+ else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) {
+ TryDynamicCallType
+ } else {
+ if (!site.isErroneous) {
+ def kind = if (name.isTypeName) "type" else "value"
+ def addendum =
+ if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?"
+ else ""
+ ctx.error(
+ if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor"
+ else NotAMember(site, name, kind),
+ pos)
+ }
+ ErrorType
+ }
+ }
+
+ /** The selection type, which is additionally checked for accessibility.
+ */
+ def accessibleSelectionType(tree: untpd.RefTree, qual1: Tree)(implicit ctx: Context): Type = {
+ val ownType = selectionType(qual1.tpe.widenIfUnstable, tree.name, tree.pos)
+ ensureAccessible(ownType, qual1.isInstanceOf[Super], tree.pos)
+ }
+
+ /** Type assignment method. Each method takes as parameters
+ * - an untpd.Tree to which it assigns a type,
+ * - typed child trees it needs to access to cpmpute that type,
+ * - any further information it needs to access to compute that type.
+ */
+
+ def assignType(tree: untpd.Ident, tp: Type)(implicit ctx: Context) =
+ tree.withType(tp)
+
+ def assignType(tree: untpd.Select, qual: Tree)(implicit ctx: Context): Select = {
+ def qualType = qual.tpe.widen
+ def arrayElemType = {
+ val JavaArrayType(elemtp) = qualType
+ elemtp
+ }
+ val p = nme.primitive
+ val tp = tree.name match {
+ case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType)
+ case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType)
+ case p.arrayLength => MethodType(Nil, defn.IntType)
+
+ // Note that we do not need to handle calls to Array[T]#clone() specially:
+ // The JLS section 10.7 says "The return type of the clone method of an array type
+ // T[] is T[]", but the actual return type at the bytecode level is Object which
+ // is casted to T[] by javac. Since the return type of Array[T]#clone() is Array[T],
+ // this is exactly what Erasure will do.
+
+ case _ => accessibleSelectionType(tree, qual)
+ }
+ tree.withType(tp)
+ }
+
+ def assignType(tree: untpd.New, tpt: Tree)(implicit ctx: Context) =
+ tree.withType(tpt.tpe)
+
+ def assignType(tree: untpd.Literal)(implicit ctx: Context) =
+ tree.withType {
+ val value = tree.const
+ value.tag match {
+ case UnitTag => defn.UnitType
+ case NullTag => defn.NullType
+ case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value)
+ }
+ }
+
+ def assignType(tree: untpd.This)(implicit ctx: Context) = {
+ val cls = qualifyingClass(tree, tree.qual.name, packageOK = false)
+ tree.withType(cls.thisType)
+ }
+
+ def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = {
+ val mix = tree.mix
+ val qtype @ ThisType(_) = qual.tpe
+ val cls = qtype.cls
+
+ def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match {
+ case p :: Nil =>
+ p
+ case Nil =>
+ errorType(em"$mix does not name a parent class of $cls", tree.pos)
+ case p :: q :: _ =>
+ errorType("ambiguous parent class qualifier", tree.pos)
+ }
+ val owntype =
+ if (mixinClass.exists) mixinClass.typeRef
+ else if (!mix.isEmpty) findMixinSuper(cls.info)
+ else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent
+ else {
+ val ps = cls.classInfo.parentsWithArgs
+ if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y)
+ }
+ tree.withType(SuperType(cls.thisType, owntype))
+ }
+
+ def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
+ val ownType = fn.tpe.widen match {
+ case fntpe @ MethodType(_, ptypes) =>
+ if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes)
+ else wrongNumberOfArgs(fn.tpe, "", fntpe.typeParams, args, tree.pos)
+ case t =>
+ errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos)
+ }
+ tree.withType(ownType)
+ }
+
+ def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
+ val ownType = fn.tpe.widen match {
+ case pt: PolyType =>
+ val paramNames = pt.paramNames
+ if (hasNamedArg(args)) {
+ // Type arguments which are specified by name (immutable after this first loop)
+ val namedArgMap = new mutable.HashMap[Name, Type]
+ for (NamedArg(name, arg) <- args)
+ if (namedArgMap.contains(name))
+ ctx.error("duplicate name", arg.pos)
+ else if (!paramNames.contains(name))
+ ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos)
+ else
+ namedArgMap(name) = arg.tpe
+
+ // Holds indexes of non-named typed arguments in paramNames
+ val gapBuf = new mutable.ListBuffer[Int]
+ def nextPoly(idx: Int) = {
+ val newIndex = gapBuf.length
+ gapBuf += idx
+ // Re-index unassigned type arguments that remain after transformation
+ PolyParam(pt, newIndex)
+ }
+
+ // Type parameters after naming assignment, conserving paramNames order
+ val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
+ namedArgMap.getOrElse(pname, nextPoly(idx))
+ }
+
+ val transform = new TypeMap {
+ def apply(t: Type) = t match {
+ case PolyParam(`pt`, idx) => normArgs(idx)
+ case _ => mapOver(t)
+ }
+ }
+ val resultType1 = transform(pt.resultType)
+ if (gapBuf.isEmpty) resultType1
+ else {
+ val gaps = gapBuf.toList
+ pt.derivedPolyType(
+ gaps.map(paramNames),
+ gaps.map(idx => transform(pt.paramBounds(idx)).bounds),
+ resultType1)
+ }
+ }
+ else {
+ val argTypes = args.tpes
+ if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
+ else wrongNumberOfArgs(fn.tpe, "type", pt.typeParams, args, tree.pos)
+ }
+ case _ =>
+ errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos)
+ }
+
+ tree.withType(ownType)
+ }
+
+ def assignType(tree: untpd.Typed, tpt: Tree)(implicit ctx: Context) =
+ tree.withType(tpt.tpe)
+
+ def assignType(tree: untpd.NamedArg, arg: Tree)(implicit ctx: Context) =
+ tree.withType(arg.tpe)
+
+ def assignType(tree: untpd.Assign)(implicit ctx: Context) =
+ tree.withType(defn.UnitType)
+
+ def assignType(tree: untpd.Block, stats: List[Tree], expr: Tree)(implicit ctx: Context) =
+ tree.withType(avoidingType(expr, stats))
+
+ def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(implicit ctx: Context) =
+ tree.withType(avoidingType(expansion, bindings))
+
+ def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(implicit ctx: Context) =
+ tree.withType(thenp.tpe | elsep.tpe)
+
+ def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) =
+ tree.withType(
+ if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length)
+ else target.tpe)
+
+ def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) =
+ tree.withType(body.tpe)
+
+ def assignType(tree: untpd.Match, cases: List[CaseDef])(implicit ctx: Context) =
+ tree.withType(ctx.typeComparer.lub(cases.tpes))
+
+ def assignType(tree: untpd.Return)(implicit ctx: Context) =
+ tree.withType(defn.NothingType)
+
+ def assignType(tree: untpd.Try, expr: Tree, cases: List[CaseDef])(implicit ctx: Context) =
+ if (cases.isEmpty) tree.withType(expr.tpe)
+ else tree.withType(ctx.typeComparer.lub(expr.tpe :: cases.tpes))
+
+ def assignType(tree: untpd.SeqLiteral, elems: List[Tree], elemtpt: Tree)(implicit ctx: Context) = {
+ val ownType = tree match {
+ case tree: untpd.JavaSeqLiteral => defn.ArrayOf(elemtpt.tpe)
+ case _ => if (ctx.erasedTypes) defn.SeqType else defn.SeqType.appliedTo(elemtpt.tpe)
+ }
+ tree.withType(ownType)
+ }
+
+ def assignType(tree: untpd.SingletonTypeTree, ref: Tree)(implicit ctx: Context) =
+ tree.withType(ref.tpe)
+
+ def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
+ tree.withType(left.tpe & right.tpe)
+
+ def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) =
+ tree.withType(left.tpe | right.tpe)
+
+ /** Assign type of RefinedType.
+ * Refinements are typed as if they were members of refinement class `refineCls`.
+ */
+ def assignType(tree: untpd.RefinedTypeTree, parent: Tree, refinements: List[Tree], refineCls: ClassSymbol)(implicit ctx: Context) = {
+ def addRefinement(parent: Type, refinement: Tree): Type = {
+ val rsym = refinement.symbol
+ val rinfo = if (rsym is Accessor) rsym.info.resultType else rsym.info
+ RefinedType(parent, rsym.name, rinfo)
+ }
+ val refined = (parent.tpe /: refinements)(addRefinement)
+ tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt))))
+ }
+
+ def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = {
+ val tparams = tycon.tpe.typeParams
+ lazy val ntparams = tycon.tpe.namedTypeParams
+ def refineNamed(tycon: Type, arg: Tree) = arg match {
+ case ast.Trees.NamedArg(name, argtpt) =>
+ // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error
+ val tparam = tparams.find(_.paramName == name) match {
+ case Some(tparam) => tparam
+ case none => ntparams.find(_.name == name).getOrElse(NoSymbol)
+ }
+ if (tparam.isTypeParam) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam))
+ else errorType(i"$tycon does not have a parameter or abstract type member named $name", arg.pos)
+ case _ =>
+ errorType(s"named and positional type arguments may not be mixed", arg.pos)
+ }
+ val ownType =
+ if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed)
+ else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes)
+ else wrongNumberOfArgs(tycon.tpe, "type", tparams, args, tree.pos)
+ tree.withType(ownType)
+ }
+
+ def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) =
+ tree.withType(body.tpe.LambdaAbstract(tparamDefs.map(_.symbol)))
+
+ def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) =
+ tree.withType(ExprType(result.tpe))
+
+ def assignType(tree: untpd.TypeBoundsTree, lo: Tree, hi: Tree)(implicit ctx: Context) =
+ tree.withType(if (lo eq hi) TypeAlias(lo.tpe) else TypeBounds(lo.tpe, hi.tpe))
+
+ def assignType(tree: untpd.Bind, sym: Symbol)(implicit ctx: Context) =
+ tree.withType(NamedType.withFixedSym(NoPrefix, sym))
+
+ def assignType(tree: untpd.Alternative, trees: List[Tree])(implicit ctx: Context) =
+ tree.withType(ctx.typeComparer.lub(trees.tpes))
+
+ def assignType(tree: untpd.UnApply, proto: Type)(implicit ctx: Context) =
+ tree.withType(proto)
+
+ def assignType(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) =
+ tree.withType(if (sym.exists) assertExists(symbolicIfNeeded(sym).orElse(sym.valRef)) else NoType)
+
+ def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) =
+ tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig))
+
+ def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) =
+ tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef))
+
+ private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = {
+ val owner = sym.owner
+ owner.infoOrCompleter match {
+ case info: ClassInfo if info.givenSelfType.exists =>
+ // In that case a simple typeRef/termWithWithSig could return a member of
+ // the self type, not the symbol itself. To avoid this, we make the reference
+ // symbolic. In general it seems to be faster to keep the non-symblic
+ // reference, since there is less pressure on the uniqueness tables that way
+ // and less work to update all the different references. That's why symbolic references
+ // are only used if necessary.
+ NamedType.withFixedSym(owner.thisType, sym)
+ case _ => NoType
+ }
+ }
+
+ def assertExists(tp: Type) = { assert(tp != NoType); tp }
+
+ def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) =
+ tree.withType(sym.nonMemberTermRef)
+
+ def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(implicit ctx: Context) =
+ tree.withType(AnnotatedType(arg.tpe.widen, Annotation(annot)))
+
+ def assignType(tree: untpd.PackageDef, pid: Tree)(implicit ctx: Context) =
+ tree.withType(pid.symbol.valRef)
+}
+
+object TypeAssigner extends TypeAssigner
+
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
new file mode 100644
index 000000000..64936e106
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -0,0 +1,1952 @@
+package dotty.tools
+package dotc
+package typer
+
+import core._
+import ast._
+import Trees._
+import Constants._
+import StdNames._
+import Scopes._
+import Denotations._
+import ProtoTypes._
+import Contexts._
+import Comments._
+import Symbols._
+import Types._
+import SymDenotations._
+import Annotations._
+import Names._
+import NameOps._
+import Flags._
+import Decorators._
+import ErrorReporting._
+import Checking._
+import Inferencing._
+import EtaExpansion.etaExpand
+import dotty.tools.dotc.transform.Erasure.Boxing
+import util.Positions._
+import util.common._
+import util.SourcePosition
+import collection.mutable
+import annotation.tailrec
+import Implicits._
+import util.Stats.{track, record}
+import config.Printers.{typr, gadts}
+import rewrite.Rewrites.patch
+import NavigateAST._
+import transform.SymUtils._
+import language.implicitConversions
+import printing.SyntaxHighlighting._
+
+object Typer {
+
+ /** The precedence of bindings which determines which of several bindings will be
+ * accessed by an Ident.
+ */
+ object BindingPrec {
+ val definition = 4
+ val namedImport = 3
+ val wildImport = 2
+ val packageClause = 1
+ val nothingBound = 0
+ def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport
+ }
+
+ /** Assert tree has a position, unless it is empty or a typed splice */
+ def assertPositioned(tree: untpd.Tree)(implicit ctx: Context) =
+ if (!tree.isEmpty && !tree.isInstanceOf[untpd.TypedSplice] && ctx.typerState.isGlobalCommittable)
+ assert(tree.pos.exists, s"position not set for $tree # ${tree.uniqueId}")
+}
+
+class Typer extends Namer with TypeAssigner with Applications with Implicits with Dynamic with Checking with Docstrings {
+
+ import Typer._
+ import tpd.{cpy => _, _}
+ import untpd.cpy
+ import Dynamic.isDynamicMethod
+ import reporting.diagnostic.Message
+ import reporting.diagnostic.messages._
+
+ /** A temporary data item valid for a single typed ident:
+ * The set of all root import symbols that have been
+ * encountered as a qualifier of an import so far.
+ * Note: It would be more proper to move importedFromRoot into typedIdent.
+ * We should check that this has no performance degradation, however.
+ */
+ private var importedFromRoot: Set[Symbol] = Set()
+
+ /** Temporary data item for single call to typed ident:
+ * This symbol would be found under Scala2 mode, but is not
+ * in dotty (because dotty conforms to spec section 2
+ * wrt to package member resolution but scalac doe not).
+ */
+ private var foundUnderScala2: Type = NoType
+
+ def newLikeThis: Typer = new Typer
+
+ /** Attribute an identifier consisting of a simple name or wildcard
+ *
+ * @param tree The tree representing the identifier.
+ * Transformations: (1) Prefix class members with this.
+ * (2) Change imported symbols to selections.
+ * (3) Change pattern Idents id (but not wildcards) to id @ _
+ */
+ def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = track("typedIdent") {
+ val refctx = ctx
+ val name = tree.name
+ val noImports = ctx.mode.is(Mode.InPackageClauseName)
+
+ /** Method is necessary because error messages need to bind to
+ * to typedIdent's context which is lost in nested calls to findRef
+ */
+ def error(msg: => Message, pos: Position) = ctx.error(msg, pos)
+
+ /** Is this import a root import that has been shadowed by an explicit
+ * import in the same program?
+ */
+ def isDisabled(imp: ImportInfo, site: Type): Boolean = {
+ if (imp.isRootImport && (importedFromRoot contains site.termSymbol)) return true
+ if (imp.hiddenRoot.exists) importedFromRoot += imp.hiddenRoot
+ false
+ }
+
+ /** Does this identifier appear as a constructor of a pattern? */
+ def isPatternConstr =
+ if (ctx.mode.isExpr && (ctx.outer.mode is Mode.Pattern))
+ ctx.outer.tree match {
+ case Apply(`tree`, _) => true
+ case _ => false
+ }
+ else false
+
+ /** A symbol qualifies if it really exists. In addition,
+ * if we are in a constructor of a pattern, we ignore all definitions
+ * which are methods and not accessors (note: if we don't do that
+ * case x :: xs in class List would return the :: method).
+ */
+ def qualifies(denot: Denotation): Boolean =
+ reallyExists(denot) && !(
+ pt.isInstanceOf[UnapplySelectionProto] &&
+ (denot.symbol is (Method, butNot = Accessor)))
+
+ /** Find the denotation of enclosing `name` in given context `ctx`.
+ * @param previous A denotation that was found in a more deeply nested scope,
+ * or else `NoDenotation` if nothing was found yet.
+ * @param prevPrec The binding precedence of the previous denotation,
+ * or else `nothingBound` if nothing was found yet.
+ * @param prevCtx The context of the previous denotation,
+ * or else `NoContext` if nothing was found yet.
+ */
+ def findRef(previous: Type, prevPrec: Int, prevCtx: Context)(implicit ctx: Context): Type = {
+ import BindingPrec._
+
+ /** A string which explains how something was bound; Depending on `prec` this is either
+ * imported by <tree>
+ * or defined in <symbol>
+ */
+ def bindingString(prec: Int, whereFound: Context, qualifier: String = "") =
+ if (prec == wildImport || prec == namedImport) {
+ ex"""imported$qualifier by ${hl"${whereFound.importInfo.toString}"}"""
+ } else
+ ex"""defined$qualifier in ${hl"${whereFound.owner.toString}"}"""
+
+ /** Check that any previously found result from an inner context
+ * does properly shadow the new one from an outer context.
+ * @param found The newly found result
+ * @param newPrec Its precedence
+ * @param scala2pkg Special mode where we check members of the same package, but defined
+ * in different compilation units under Scala2. If set, and the
+ * previous and new contexts do not have the same scope, we select
+ * the previous (inner) definition. This models what scalac does.
+ */
+ def checkNewOrShadowed(found: Type, newPrec: Int, scala2pkg: Boolean = false)(implicit ctx: Context): Type =
+ if (!previous.exists || ctx.typeComparer.isSameRef(previous, found)) found
+ else if ((prevCtx.scope eq ctx.scope) &&
+ (newPrec == definition ||
+ newPrec == namedImport && prevPrec == wildImport)) {
+ // special cases: definitions beat imports, and named imports beat
+ // wildcard imports, provided both are in contexts with same scope
+ found
+ }
+ else {
+ if (!scala2pkg && !previous.isError && !found.isError) {
+ error(
+ ex"""|reference to `$name` is ambiguous
+ |it is both ${bindingString(newPrec, ctx, "")}
+ |and ${bindingString(prevPrec, prevCtx, " subsequently")}""",
+ tree.pos)
+ }
+ previous
+ }
+
+ /** The type representing a named import with enclosing name when imported
+ * from given `site` and `selectors`.
+ */
+ def namedImportRef(site: Type, selectors: List[untpd.Tree])(implicit ctx: Context): Type = {
+ def checkUnambiguous(found: Type) = {
+ val other = namedImportRef(site, selectors.tail)
+ if (other.exists && found.exists && (found != other))
+ error(em"reference to `$name` is ambiguous; it is imported twice in ${ctx.tree}",
+ tree.pos)
+ found
+ }
+ val Name = name.toTermName.decode
+ selectors match {
+ case selector :: rest =>
+ selector match {
+ case Thicket(fromId :: Ident(Name) :: _) =>
+ val Ident(from) = fromId
+ val selName = if (name.isTypeName) from.toTypeName else from
+ // Pass refctx so that any errors are reported in the context of the
+ // reference instead of the context of the import.
+ checkUnambiguous(selectionType(site, selName, tree.pos)(refctx))
+ case Ident(Name) =>
+ checkUnambiguous(selectionType(site, name, tree.pos)(refctx))
+ case _ =>
+ namedImportRef(site, rest)
+ }
+ case nil =>
+ NoType
+ }
+ }
+
+ /** The type representing a wildcard import with enclosing name when imported
+ * from given import info
+ */
+ def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type = {
+ if (imp.isWildcardImport) {
+ val pre = imp.site
+ if (!isDisabled(imp, pre) && !(imp.excluded contains name.toTermName) && name != nme.CONSTRUCTOR) {
+ val denot = pre.member(name).accessibleFrom(pre)(refctx)
+ if (reallyExists(denot)) return pre.select(name, denot)
+ }
+ }
+ NoType
+ }
+
+ /** Is (some alternative of) the given predenotation `denot`
+ * defined in current compilation unit?
+ */
+ def isDefinedInCurrentUnit(denot: Denotation)(implicit ctx: Context): Boolean = denot match {
+ case MultiDenotation(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2)
+ case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source.file
+ }
+
+ /** Is `denot` the denotation of a self symbol? */
+ def isSelfDenot(denot: Denotation)(implicit ctx: Context) = denot match {
+ case denot: SymDenotation => denot is SelfName
+ case _ => false
+ }
+
+ /** Would import of kind `prec` be not shadowed by a nested higher-precedence definition? */
+ def isPossibleImport(prec: Int)(implicit ctx: Context) =
+ !noImports &&
+ (prevPrec < prec || prevPrec == prec && (prevCtx.scope eq ctx.scope))
+
+ @tailrec def loop(implicit ctx: Context): Type = {
+ if (ctx.scope == null) previous
+ else {
+ val outer = ctx.outer
+ var result: Type = NoType
+
+ // find definition
+ if ((ctx.scope ne outer.scope) || (ctx.owner ne outer.owner)) {
+ val defDenot = ctx.denotNamed(name)
+ if (qualifies(defDenot)) {
+ val curOwner = ctx.owner
+ val found =
+ if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
+ else curOwner.thisType.select(name, defDenot)
+ if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenot))
+ result = checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry
+ else {
+ if (ctx.scala2Mode && !foundUnderScala2.exists)
+ foundUnderScala2 = checkNewOrShadowed(found, definition, scala2pkg = true)
+ if (defDenot.symbol is Package)
+ result = checkNewOrShadowed(previous orElse found, packageClause)
+ else if (prevPrec < packageClause)
+ result = findRef(found, packageClause, ctx)(outer)
+ }
+ }
+ }
+
+ if (result.exists) result
+ else { // find import
+ val curImport = ctx.importInfo
+ if (ctx.owner.is(Package) && curImport != null && curImport.isRootImport && previous.exists)
+ previous // no more conflicts possible in this case
+ else if (isPossibleImport(namedImport) && (curImport ne outer.importInfo) && !curImport.sym.isCompleting) {
+ val namedImp = namedImportRef(curImport.site, curImport.selectors)
+ if (namedImp.exists)
+ findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer)
+ else if (isPossibleImport(wildImport)) {
+ val wildImp = wildImportRef(curImport)
+ if (wildImp.exists)
+ findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer)
+ else loop(outer)
+ }
+ else loop(outer)
+ }
+ else loop(outer)
+ }
+ }
+ }
+
+ loop
+ }
+
+ // begin typedIdent
+ def kind = if (name.isTermName) "" else "type "
+ typr.println(s"typed ident $kind$name in ${ctx.owner}")
+ if (ctx.mode is Mode.Pattern) {
+ if (name == nme.WILDCARD)
+ return tree.withType(pt)
+ if (isVarPattern(tree) && name.isTermName)
+ return typed(desugar.patternVar(tree), pt)
+ }
+
+
+ val rawType = {
+ val saved1 = importedFromRoot
+ val saved2 = foundUnderScala2
+ importedFromRoot = Set.empty
+ foundUnderScala2 = NoType
+ try {
+ var found = findRef(NoType, BindingPrec.nothingBound, NoContext)
+ if (foundUnderScala2.exists && !(foundUnderScala2 =:= found)) {
+ ctx.migrationWarning(
+ ex"""Name resolution will change.
+ | currently selected : $foundUnderScala2
+ | in the future, without -language:Scala2: $found""", tree.pos)
+ found = foundUnderScala2
+ }
+ found
+ }
+ finally {
+ importedFromRoot = saved1
+ foundUnderScala2 = saved2
+ }
+ }
+
+ val ownType =
+ if (rawType.exists)
+ ensureAccessible(rawType, superAccess = false, tree.pos)
+ else {
+ error(new MissingIdent(tree, kind, name.show), tree.pos)
+ ErrorType
+ }
+
+ val tree1 = ownType match {
+ case ownType: NamedType if !prefixIsElidable(ownType) =>
+ ref(ownType).withPos(tree.pos)
+ case _ =>
+ tree.withType(ownType)
+ }
+
+ checkValue(tree1, pt)
+ }
+
+ private def typedSelect(tree: untpd.Select, pt: Type, qual: Tree)(implicit ctx: Context): Select =
+ healNonvariant(
+ checkValue(assignType(cpy.Select(tree)(qual, tree.name), qual), pt),
+ pt)
+
+ /** Let `tree = p.n` where `p: T`. If tree's type is an unsafe instantiation
+ * (see TypeOps#asSeenFrom for how this can happen), rewrite the prefix `p`
+ * to `(p: <unknown skolem of type T>)` and try again with the new (stable)
+ * prefix. If the result has another unsafe instantiation, raise an error.
+ */
+ private def healNonvariant[T <: Tree](tree: T, pt: Type)(implicit ctx: Context): T =
+ if (ctx.unsafeNonvariant == ctx.runId && tree.tpe.widen.hasUnsafeNonvariant)
+ tree match {
+ case tree @ Select(qual, _) if !qual.tpe.isStable =>
+ val alt = typedSelect(tree, pt, Typed(qual, TypeTree(SkolemType(qual.tpe.widen))))
+ typr.println(i"healed type: ${tree.tpe} --> $alt")
+ alt.asInstanceOf[T]
+ case _ =>
+ ctx.error(ex"unsafe instantiation of type ${tree.tpe}", tree.pos)
+ tree
+ }
+ else tree
+
+ def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") {
+ def typeSelectOnTerm(implicit ctx: Context): Tree = {
+ val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this))
+ if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos)
+ val select = typedSelect(tree, pt, qual1)
+ if (select.tpe ne TryDynamicCallType) select
+ else if (pt.isInstanceOf[PolyProto] || pt.isInstanceOf[FunProto] || pt == AssignProto) select
+ else typedDynamicSelect(tree, Nil, pt)
+ }
+
+ def typeSelectOnType(qual: untpd.Tree)(implicit ctx: Context) =
+ typedSelect(untpd.cpy.Select(tree)(qual, tree.name.toTypeName), pt)
+
+ def tryJavaSelectOnType(implicit ctx: Context): Tree = tree.qualifier match {
+ case Select(qual, name) => typeSelectOnType(untpd.Select(qual, name.toTypeName))
+ case Ident(name) => typeSelectOnType(untpd.Ident(name.toTypeName))
+ case _ => errorTree(tree, "cannot convert to type selection") // will never be printed due to fallback
+ }
+
+ def selectWithFallback(fallBack: Context => Tree) =
+ tryAlternatively(typeSelectOnTerm(_))(fallBack)
+
+ if (tree.qualifier.isType) {
+ val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this))
+ assignType(cpy.Select(tree)(qual1, tree.name), qual1)
+ }
+ else if (ctx.compilationUnit.isJava && tree.name.isTypeName)
+ // SI-3120 Java uses the same syntax, A.B, to express selection from the
+ // value A and from the type A. We have to try both.
+ selectWithFallback(tryJavaSelectOnType(_)) // !!! possibly exponential bcs of qualifier retyping
+ else if (tree.name == nme.withFilter && tree.getAttachment(desugar.MaybeFilter).isDefined)
+ selectWithFallback {
+ implicit ctx =>
+ typedSelect(untpd.cpy.Select(tree)(tree.qualifier, nme.filter), pt) // !!! possibly exponential bcs of qualifier retyping
+ }
+ else
+ typeSelectOnTerm(ctx)
+ }
+
+ def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = track("typedThis") {
+ assignType(tree)
+ }
+
+ def typedSuper(tree: untpd.Super, pt: Type)(implicit ctx: Context): Tree = track("typedSuper") {
+ val qual1 = typed(tree.qual)
+ val inConstrCall = pt match {
+ case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true
+ case _ => false
+ }
+ pt match {
+ case pt: SelectionProto if pt.name.isTypeName =>
+ qual1 // don't do super references for types; they are meaningless anyway
+ case _ =>
+ assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall)
+ }
+ }
+
+ def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") {
+ assignType(tree)
+ }
+
+ def typedNew(tree: untpd.New, pt: Type)(implicit ctx: Context) = track("typedNew") {
+ tree.tpt match {
+ case templ: untpd.Template =>
+ import untpd._
+ val x = tpnme.ANON_CLASS
+ val clsDef = TypeDef(x, templ).withFlags(Final)
+ typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt)
+ case _ =>
+ var tpt1 = typedType(tree.tpt)
+ tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.pos))
+ tpt1.tpe.dealias match {
+ case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon)
+ case _ =>
+ }
+ checkClassType(tpt1.tpe, tpt1.pos, traitReq = false, stablePrefixReq = true)
+
+ tpt1 match {
+ case AppliedTypeTree(_, targs) =>
+ for (targ @ TypeBoundsTree(_, _) <- targs)
+ ctx.error("type argument must be fully defined", targ.pos)
+ case _ =>
+ }
+
+ assignType(cpy.New(tree)(tpt1), tpt1)
+ // todo in a later phase: checkInstantiatable(cls, tpt1.pos)
+ }
+ }
+
+ def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") {
+ /* Handles three cases:
+ * @param ifPat how to handle a pattern (_: T)
+ * @param ifExpr how to handle an expression (e: T)
+ * @param wildName what name `w` to use in the rewriting of
+ * (x: T) to (x @ (w: T)). This is either `_` or `_*`.
+ */
+ def cases(ifPat: => Tree, ifExpr: => Tree, wildName: TermName) = tree.expr match {
+ case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) =>
+ if (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) ifPat
+ else {
+ import untpd._
+ typed(Bind(id.name, Typed(Ident(wildName), tree.tpt)).withPos(id.pos), pt)
+ }
+ case _ => ifExpr
+ }
+ def ascription(tpt: Tree, isWildcard: Boolean) = {
+ val underlyingTreeTpe =
+ if (isRepeatedParamType(tpt)) TypeTree(defn.SeqType.appliedTo(pt :: Nil))
+ else tpt
+
+ val expr1 =
+ if (isRepeatedParamType(tpt)) tree.expr.withType(defn.SeqType.appliedTo(pt :: Nil))
+ else if (isWildcard) tree.expr.withType(tpt.tpe)
+ else typed(tree.expr, tpt.tpe.widenSkolem)
+ assignType(cpy.Typed(tree)(expr1, tpt), underlyingTreeTpe)
+ }
+ if (untpd.isWildcardStarArg(tree))
+ cases(
+ ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true),
+ ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)),
+ wildName = nme.WILDCARD_STAR)
+ else {
+ def typedTpt = checkSimpleKinded(typedType(tree.tpt))
+ def handlePattern: Tree = {
+ val tpt1 = typedTpt
+ // special case for an abstract type that comes with a class tag
+ tpt1.tpe.dealias match {
+ case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper =>
+ inferImplicit(defn.ClassTagType.appliedTo(tref),
+ EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match {
+ case SearchSuccess(arg, _, _) =>
+ return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt)
+ case _ =>
+ }
+ case _ =>
+ if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible))
+ }
+ ascription(tpt1, isWildcard = true)
+ }
+ cases(
+ ifPat = handlePattern,
+ ifExpr = ascription(typedTpt, isWildcard = false),
+ wildName = nme.WILDCARD)
+ }
+ }
+
+ def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") {
+ val arg1 = typed(tree.arg, pt)
+ assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1)
+ }
+
+ def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = track("typedAssign") {
+ tree.lhs match {
+ case lhs @ Apply(fn, args) =>
+ typed(cpy.Apply(lhs)(untpd.Select(fn, nme.update), args :+ tree.rhs), pt)
+ case untpd.TypedSplice(Apply(MaybePoly(Select(fn, app), targs), args)) if app == nme.apply =>
+ val rawUpdate: untpd.Tree = untpd.Select(untpd.TypedSplice(fn), nme.update)
+ val wrappedUpdate =
+ if (targs.isEmpty) rawUpdate
+ else untpd.TypeApply(rawUpdate, targs map (untpd.TypedSplice(_)))
+ val appliedUpdate = cpy.Apply(fn)(wrappedUpdate, (args map (untpd.TypedSplice(_))) :+ tree.rhs)
+ typed(appliedUpdate, pt)
+ case lhs =>
+ val lhsCore = typedUnadapted(lhs, AssignProto)
+ def lhs1 = typed(untpd.TypedSplice(lhsCore))
+ def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields
+ sym.is(Mutable, butNot = Accessor) ||
+ ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner ||
+ ctx.owner.name.isTraitSetterName || ctx.owner.isStaticConstructor
+ lhsCore.tpe match {
+ case ref: TermRef if canAssign(ref.symbol) =>
+ assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info)))
+ case _ =>
+ def reassignmentToVal =
+ errorTree(cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)),
+ "reassignment to val")
+ lhsCore.tpe match {
+ case ref: TermRef => // todo: further conditions to impose on getter?
+ val pre = ref.prefix
+ val setterName = ref.name.setterName
+ val setter = pre.member(setterName)
+ lhsCore match {
+ case lhsCore: RefTree if setter.exists =>
+ val setterTypeRaw = pre.select(setterName, setter)
+ val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos)
+ val lhs2 = healNonvariant(
+ untpd.rename(lhsCore, setterName).withType(setterType), WildcardType)
+ typedUnadapted(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil))
+ case _ =>
+ reassignmentToVal
+ }
+ case TryDynamicCallType =>
+ typedDynamicAssign(tree, pt)
+ case tpe =>
+ reassignmentToVal
+ }
+ }
+ }
+ }
+
+ def typedBlockStats(stats: List[untpd.Tree])(implicit ctx: Context): (Context, List[tpd.Tree]) =
+ (index(stats), typedStats(stats, ctx.owner))
+
+ def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") {
+ val (exprCtx, stats1) = typedBlockStats(tree.stats)
+ val ept =
+ if (tree.isInstanceOf[untpd.InfixOpBlock])
+ // Right-binding infix operations are expanded to InfixBlocks, which may be followed by arguments.
+ // Example: `(a /: bs)(op)` expands to `{ val x = a; bs./:(x) } (op)` where `{...}` is an InfixBlock.
+ pt
+ else pt.notApplied
+ val expr1 = typedExpr(tree.expr, ept)(exprCtx)
+ ensureNoLocalRefs(
+ assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt, localSyms(stats1))
+ }
+
+ def escapingRefs(block: Tree, localSyms: => List[Symbol])(implicit ctx: Context): collection.Set[NamedType] = {
+ lazy val locals = localSyms.toSet
+ block.tpe namedPartsWith (tp => locals.contains(tp.symbol))
+ }
+
+ /** Check that expression's type can be expressed without references to locally defined
+ * symbols. The following two remedies are tried before giving up:
+ * 1. If the expected type of the expression is fully defined, pick it as the
+ * type of the result expressed by adding a type ascription.
+ * 2. If (1) fails, force all type variables so that the block's type is
+ * fully defined and try again.
+ */
+ protected def ensureNoLocalRefs(tree: Tree, pt: Type, localSyms: => List[Symbol], forcedDefined: Boolean = false)(implicit ctx: Context): Tree = {
+ def ascribeType(tree: Tree, pt: Type): Tree = tree match {
+ case block @ Block(stats, expr) =>
+ val expr1 = ascribeType(expr, pt)
+ cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant
+ case _ =>
+ Typed(tree, TypeTree(pt.simplified))
+ }
+ val leaks = escapingRefs(tree, localSyms)
+ if (leaks.isEmpty) tree
+ else if (isFullyDefined(pt, ForceDegree.none)) ascribeType(tree, pt)
+ else if (!forcedDefined) {
+ fullyDefinedType(tree.tpe, "block", tree.pos)
+ val tree1 = ascribeType(tree, avoid(tree.tpe, localSyms))
+ ensureNoLocalRefs(tree1, pt, localSyms, forcedDefined = true)
+ } else
+ errorTree(tree,
+ em"local definition of ${leaks.head.name} escapes as part of expression's type ${tree.tpe}"/*; full type: ${result.tpe.toString}"*/)
+ }
+
+ def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context): Tree = track("typedIf") {
+ val cond1 = typed(tree.cond, defn.BooleanType)
+ val thenp1 = typed(tree.thenp, pt.notApplied)
+ val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
+ val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil)
+ assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2)
+ }
+
+ private def decomposeProtoFunction(pt: Type, defaultArity: Int)(implicit ctx: Context): (List[Type], Type) = pt match {
+ case _ if defn.isFunctionType(pt) =>
+ // if expected parameter type(s) are wildcards, approximate from below.
+ // if expected result type is a wildcard, approximate from above.
+ // this can type the greatest set of admissible closures.
+ (pt.dealias.argTypesLo.init, pt.dealias.argTypesHi.last)
+ case SAMType(meth) =>
+ val mt @ MethodType(_, paramTypes) = meth.info
+ (paramTypes, mt.resultType)
+ case _ =>
+ (List.range(0, defaultArity) map alwaysWildcardType, WildcardType)
+ }
+
+ def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") {
+ val untpd.Function(args, body) = tree
+ if (ctx.mode is Mode.Type)
+ typed(cpy.AppliedTypeTree(tree)(
+ untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt)
+ else {
+ val params = args.asInstanceOf[List[untpd.ValDef]]
+
+ pt match {
+ case pt: TypeVar if untpd.isFunctionWithUnknownParamType(tree) =>
+ // try to instantiate `pt` if this is possible. If it does not
+ // work the error will be reported later in `inferredParam`,
+ // when we try to infer the parameter type.
+ isFullyDefined(pt, ForceDegree.noBottom)
+ case _ =>
+ }
+
+ val (protoFormals, protoResult) = decomposeProtoFunction(pt, params.length)
+
+ def refersTo(arg: untpd.Tree, param: untpd.ValDef): Boolean = arg match {
+ case Ident(name) => name == param.name
+ case _ => false
+ }
+
+ /** The function body to be returned in the closure. Can become a TypedSplice
+ * of a typed expression if this is necessary to infer a parameter type.
+ */
+ var fnBody = tree.body
+
+ /** If function is of the form
+ * (x1, ..., xN) => f(x1, ..., XN)
+ * the type of `f`, otherwise NoType. (updates `fnBody` as a side effect).
+ */
+ def calleeType: Type = fnBody match {
+ case Apply(expr, args) if (args corresponds params)(refersTo) =>
+ expr match {
+ case untpd.TypedSplice(expr1) =>
+ expr1.tpe
+ case _ =>
+ val protoArgs = args map (_ withType WildcardType)
+ val callProto = FunProto(protoArgs, WildcardType, this)
+ val expr1 = typedExpr(expr, callProto)
+ fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args)
+ expr1.tpe
+ }
+ case _ =>
+ NoType
+ }
+
+ /** Two attempts: First, if expected type is fully defined pick this one.
+ * Second, if function is of the form
+ * (x1, ..., xN) => f(x1, ..., XN)
+ * and f has a method type MT, pick the corresponding parameter type in MT,
+ * if this one is fully defined.
+ * If both attempts fail, issue a "missing parameter type" error.
+ */
+ def inferredParamType(param: untpd.ValDef, formal: Type): Type = {
+ if (isFullyDefined(formal, ForceDegree.noBottom)) return formal
+ calleeType.widen match {
+ case mtpe: MethodType =>
+ val pos = params indexWhere (_.name == param.name)
+ if (pos < mtpe.paramTypes.length) {
+ val ptype = mtpe.paramTypes(pos)
+ if (isFullyDefined(ptype, ForceDegree.noBottom)) return ptype
+ }
+ case _ =>
+ }
+ val ofFun =
+ if (nme.syntheticParamNames(args.length + 1) contains param.name)
+ i" of expanded function $tree"
+ else
+ ""
+ errorType(i"missing parameter type for parameter ${param.name}$ofFun, expected = $pt", param.pos)
+ }
+
+ def protoFormal(i: Int): Type =
+ if (protoFormals.length == params.length) protoFormals(i)
+ else errorType(i"wrong number of parameters, expected: ${protoFormals.length}", tree.pos)
+
+ /** Is `formal` a product type which is elementwise compatible with `params`? */
+ def ptIsCorrectProduct(formal: Type) = {
+ val pclass = defn.ProductNType(params.length).symbol
+ isFullyDefined(formal, ForceDegree.noBottom) &&
+ formal.derivesFrom(pclass) &&
+ formal.baseArgTypes(pclass).corresponds(params) {
+ (argType, param) =>
+ param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe
+ }
+ }
+
+ val desugared =
+ if (protoFormals.length == 1 && params.length != 1 && ptIsCorrectProduct(protoFormals.head)) {
+ desugar.makeTupledFunction(params, fnBody)
+ }
+ else {
+ val inferredParams: List[untpd.ValDef] =
+ for ((param, i) <- params.zipWithIndex) yield
+ if (!param.tpt.isEmpty) param
+ else cpy.ValDef(param)(
+ tpt = untpd.TypeTree(
+ inferredParamType(param, protoFormal(i)).underlyingIfRepeated(isJava = false)))
+
+ // Define result type of closure as the expected type, thereby pushing
+ // down any implicit searches. We do this even if the expected type is not fully
+ // defined, which is a bit of a hack. But it's needed to make the following work
+ // (see typers.scala and printers/PlainPrinter.scala for examples).
+ //
+ // def double(x: Char): String = s"$x$x"
+ // "abc" flatMap double
+ //
+ val resultTpt = protoResult match {
+ case WildcardType(_) => untpd.TypeTree()
+ case _ => untpd.TypeTree(protoResult)
+ }
+ val inlineable = pt.hasAnnotation(defn.InlineParamAnnot)
+ desugar.makeClosure(inferredParams, fnBody, resultTpt, inlineable)
+ }
+ typed(desugared, pt)
+ }
+ }
+
+ def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = track("typedClosure") {
+ val env1 = tree.env mapconserve (typed(_))
+ val meth1 = typedUnadapted(tree.meth)
+ val target =
+ if (tree.tpt.isEmpty)
+ meth1.tpe.widen match {
+ case mt: MethodType =>
+ pt match {
+ case SAMType(meth) if !defn.isFunctionType(pt) && mt <:< meth.info =>
+ if (!isFullyDefined(pt, ForceDegree.all))
+ ctx.error(ex"result type of closure is an underspecified SAM type $pt", tree.pos)
+ TypeTree(pt)
+ case _ =>
+ if (!mt.isDependent) EmptyTree
+ else throw new java.lang.Error(i"internal error: cannot turn dependent method type $mt into closure, position = ${tree.pos}, raw type = ${mt.toString}") // !!! DEBUG. Eventually, convert to an error?
+ }
+ case tp =>
+ throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")
+ }
+ else typed(tree.tpt)
+ //println(i"typing closure $tree : ${meth1.tpe.widen}")
+ assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target)
+ }
+
+ def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") {
+ tree.selector match {
+ case EmptyTree =>
+ val (protoFormals, _) = decomposeProtoFunction(pt, 1)
+ val unchecked = pt <:< defn.PartialFunctionType
+ typed(desugar.makeCaseLambda(tree.cases, protoFormals.length, unchecked) withPos tree.pos, pt)
+ case _ =>
+ val sel1 = typedExpr(tree.selector)
+ val selType = widenForMatchSelector(
+ fullyDefinedType(sel1.tpe, "pattern selector", tree.pos))
+
+ val cases1 = typedCases(tree.cases, selType, pt.notApplied)
+ val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]]
+ assignType(cpy.Match(tree)(sel1, cases2), cases2)
+ }
+ }
+
+ def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context) = {
+
+ /** gadtSyms = "all type parameters of enclosing methods that appear
+ * non-variantly in the selector type" todo: should typevars
+ * which appear with variances +1 and -1 (in different
+ * places) be considered as well?
+ */
+ val gadtSyms: Set[Symbol] = ctx.traceIndented(i"GADT syms of $selType", gadts) {
+ val accu = new TypeAccumulator[Set[Symbol]] {
+ def apply(tsyms: Set[Symbol], t: Type): Set[Symbol] = {
+ val tsyms1 = t match {
+ case tr: TypeRef if (tr.symbol is TypeParam) && tr.symbol.owner.isTerm && variance == 0 =>
+ tsyms + tr.symbol
+ case _ =>
+ tsyms
+ }
+ foldOver(tsyms1, t)
+ }
+ }
+ accu(Set.empty, selType)
+ }
+
+ cases mapconserve (typedCase(_, pt, selType, gadtSyms))
+ }
+
+ /** Type a case. Overridden in ReTyper, that's why it's separate from
+ * typedCases.
+ */
+ def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") {
+ val originalCtx = ctx
+
+ /** - replace all references to symbols associated with wildcards by their GADT bounds
+ * - enter all symbols introduced by a Bind in current scope
+ */
+ val indexPattern = new TreeMap {
+ val elimWildcardSym = new TypeMap {
+ def apply(t: Type) = t match {
+ case ref @ TypeRef(_, tpnme.WILDCARD) if ctx.gadt.bounds.contains(ref.symbol) =>
+ ctx.gadt.bounds(ref.symbol)
+ case TypeAlias(ref @ TypeRef(_, tpnme.WILDCARD)) if ctx.gadt.bounds.contains(ref.symbol) =>
+ ctx.gadt.bounds(ref.symbol)
+ case _ =>
+ mapOver(t)
+ }
+ }
+ override def transform(trt: Tree)(implicit ctx: Context) =
+ super.transform(trt.withType(elimWildcardSym(trt.tpe))) match {
+ case b: Bind =>
+ if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
+ else ctx.error(new DuplicateBind(b, tree), b.pos)
+ b.symbol.info = elimWildcardSym(b.symbol.info)
+ b
+ case t => t
+ }
+ }
+
+ def caseRest(pat: Tree)(implicit ctx: Context) = {
+ val pat1 = indexPattern.transform(pat)
+ val guard1 = typedExpr(tree.guard, defn.BooleanType)
+ val body1 = ensureNoLocalRefs(typedExpr(tree.body, pt), pt, ctx.scope.toList)
+ .ensureConforms(pt)(originalCtx) // insert a cast if body does not conform to expected type if we disregard gadt bounds
+ assignType(cpy.CaseDef(tree)(pat1, guard1, body1), body1)
+ }
+
+ val gadtCtx =
+ if (gadtSyms.isEmpty) ctx
+ else {
+ val c = ctx.fresh.setFreshGADTBounds
+ for (sym <- gadtSyms)
+ if (!c.gadt.bounds.contains(sym))
+ c.gadt.setBounds(sym, TypeBounds.empty)
+ c
+ }
+ val pat1 = typedPattern(tree.pat, selType)(gadtCtx)
+ caseRest(pat1)(gadtCtx.fresh.setNewScope)
+ }
+
+ def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") {
+ def returnProto(owner: Symbol, locals: Scope): Type =
+ if (owner.isConstructor) defn.UnitType
+ else owner.info match {
+ case info: PolyType =>
+ val tparams = locals.toList.takeWhile(_ is TypeParam)
+ assert(info.paramNames.length == tparams.length,
+ i"return mismatch from $owner, tparams = $tparams, locals = ${locals.toList}%, %")
+ info.instantiate(tparams.map(_.typeRef)).finalResultType
+ case info =>
+ info.finalResultType
+ }
+ def enclMethInfo(cx: Context): (Tree, Type) = {
+ val owner = cx.owner
+ if (cx == NoContext || owner.isType) {
+ ctx.error("return outside method definition", tree.pos)
+ (EmptyTree, WildcardType)
+ }
+ else if (owner != cx.outer.owner && owner.isRealMethod) {
+ if (owner.isInlineMethod)
+ (EmptyTree, errorType(em"no explicit return allowed from inline $owner", tree.pos))
+ else if (!owner.isCompleted)
+ (EmptyTree, errorType(em"$owner has return statement; needs result type", tree.pos))
+ else {
+ val from = Ident(TermRef(NoPrefix, owner.asTerm))
+ val proto = returnProto(owner, cx.scope)
+ (from, proto)
+ }
+ }
+ else enclMethInfo(cx.outer)
+ }
+ val (from, proto) =
+ if (tree.from.isEmpty) enclMethInfo(ctx)
+ else {
+ val from = tree.from.asInstanceOf[tpd.Tree]
+ val proto =
+ if (ctx.erasedTypes) from.symbol.info.finalResultType
+ else WildcardType // We cannot reliably detect the internal type view of polymorphic or dependent methods
+ // because we do not know the internal type params and method params.
+ // Hence no adaptation is possible, and we assume WildcardType as prototype.
+ (from, proto)
+ }
+ val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withPos(tree.pos), proto)
+ assignType(cpy.Return(tree)(expr1, from))
+ }
+
+ def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
+ val expr1 = typed(tree.expr, pt.notApplied)
+ val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
+ val finalizer1 = typed(tree.finalizer, defn.UnitType)
+ val expr2 :: cases2x = harmonize(expr1 :: cases1)
+ val cases2 = cases2x.asInstanceOf[List[CaseDef]]
+ assignType(cpy.Try(tree)(expr2, cases2, finalizer1), expr2, cases2)
+ }
+
+ def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Tree = track("typedThrow") {
+ val expr1 = typed(tree.expr, defn.ThrowableType)
+ Throw(expr1).withPos(tree.pos)
+ }
+
+ def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") {
+ val proto1 = pt.elemType match {
+ case NoType => WildcardType
+ case bounds: TypeBounds => WildcardType(bounds)
+ case elemtp => elemtp
+ }
+ val elems1 = tree.elems mapconserve (typed(_, proto1))
+ val proto2 = // the computed type of the `elemtpt` field
+ if (!tree.elemtpt.isEmpty) WildcardType
+ else if (isFullyDefined(proto1, ForceDegree.none)) proto1
+ else if (tree.elems.isEmpty && tree.isInstanceOf[Trees.JavaSeqLiteral[_]])
+ defn.ObjectType // generic empty Java varargs are of type Object[]
+ else ctx.typeComparer.lub(elems1.tpes)
+ val elemtpt1 = typed(tree.elemtpt, proto2)
+ assignType(cpy.SeqLiteral(tree)(elems1, elemtpt1), elems1, elemtpt1)
+ }
+
+ def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context): Inlined = {
+ val (exprCtx, bindings1) = typedBlockStats(tree.bindings)
+ val expansion1 = typed(tree.expansion, pt)(inlineContext(tree.call)(exprCtx))
+ assignType(cpy.Inlined(tree)(tree.call, bindings1.asInstanceOf[List[MemberDef]], expansion1),
+ bindings1, expansion1)
+ }
+
+ def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") {
+ tree match {
+ case tree: untpd.DerivedTypeTree =>
+ tree.ensureCompletions
+ try
+ TypeTree(tree.derivedType(tree.attachment(untpd.OriginalSymbol))) withPos tree.pos
+ // btw, no need to remove the attachment. The typed
+ // tree is different from the untyped one, so the
+ // untyped tree is no longer accessed after all
+ // accesses with typedTypeTree are done.
+ catch {
+ case ex: NoSuchElementException =>
+ println(s"missing OriginalSymbol for ${ctx.owner.ownersIterator.toList}")
+ throw ex
+ }
+ case _ =>
+ assert(isFullyDefined(pt, ForceDegree.none))
+ tree.withType(pt)
+ }
+ }
+
+ def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") {
+ val ref1 = typedExpr(tree.ref)
+ checkStable(ref1.tpe, tree.pos)
+ assignType(cpy.SingletonTypeTree(tree)(ref1), ref1)
+ }
+
+ def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") {
+ val left1 = typed(tree.left)
+ val right1 = typed(tree.right)
+ assignType(cpy.AndTypeTree(tree)(left1, right1), left1, right1)
+ }
+
+ def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") {
+ val where = "in a union type"
+ val left1 = checkNotSingleton(typed(tree.left), where)
+ val right1 = checkNotSingleton(typed(tree.right), where)
+ assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1)
+ }
+
+ def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") {
+ val tpt1 = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else typedAheadType(tree.tpt)
+ val refineClsDef = desugar.refinedTypeToClass(tpt1, tree.refinements)
+ val refineCls = createSymbol(refineClsDef).asClass
+ val TypeDef(_, impl: Template) = typed(refineClsDef)
+ val refinements1 = impl.body
+ assert(tree.refinements.length == refinements1.length, s"${tree.refinements} != $refinements1")
+ val seen = mutable.Set[Symbol]()
+ for (refinement <- refinements1) { // TODO: get clarity whether we want to enforce these conditions
+ typr.println(s"adding refinement $refinement")
+ checkRefinementNonCyclic(refinement, refineCls, seen)
+ val rsym = refinement.symbol
+ if (rsym.is(Method) && rsym.allOverriddenSymbols.isEmpty)
+ ctx.error(i"refinement $rsym without matching type in parent $tpt1", refinement.pos)
+ }
+ assignType(cpy.RefinedTypeTree(tree)(tpt1, refinements1), tpt1, refinements1, refineCls)
+ }
+
+ def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") {
+ val tpt1 = typed(tree.tpt, AnyTypeConstructorProto)(ctx.retractMode(Mode.Pattern))
+ val tparams = tpt1.tpe.typeParams
+ if (tparams.isEmpty) {
+ ctx.error(ex"${tpt1.tpe} does not take type parameters", tree.pos)
+ tpt1
+ }
+ else {
+ var args = tree.args
+ val args1 =
+ if (hasNamedArg(args)) typedNamedArgs(args)
+ else {
+ if (args.length != tparams.length) {
+ wrongNumberOfArgs(tpt1.tpe, "type", tparams, args, tree.pos)
+ args = args.take(tparams.length)
+ }
+ def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = {
+ val (desugaredArg, argPt) =
+ if (ctx.mode is Mode.Pattern)
+ (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramBounds)
+ else
+ (arg, WildcardType)
+ typed(desugaredArg, argPt)
+ }
+ args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]]
+ }
+ // check that arguments conform to bounds is done in phase PostTyper
+ assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1)
+ }
+ }
+
+ def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") {
+ val PolyTypeTree(tparams, body) = tree
+ index(tparams)
+ val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
+ val body1 = typedType(tree.body)
+ assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1)
+ }
+
+ def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") {
+ val result1 = typed(tree.result)
+ assignType(cpy.ByNameTypeTree(tree)(result1), result1)
+ }
+
+ /** Define a new symbol associated with a Bind or pattern wildcard and
+ * make it gadt narrowable.
+ */
+ private def newPatternBoundSym(name: Name, info: Type, pos: Position)(implicit ctx: Context) = {
+ val flags = if (name.isTypeName) BindDefinedType else EmptyFlags
+ val sym = ctx.newSymbol(ctx.owner, name, flags | Case, info, coord = pos)
+ if (name.isTypeName) ctx.gadt.setBounds(sym, info.bounds)
+ sym
+ }
+
+ def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") {
+ val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree)
+ val lo1 = typed(lo)
+ val hi1 = typed(hi)
+ val tree1 = assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1)
+ if (ctx.mode.is(Mode.Pattern)) {
+ // Associate a pattern-bound type symbol with the wildcard.
+ // The bounds of the type symbol can be constrained when comparing a pattern type
+ // with an expected type in typedTyped. The type symbol is eliminated once
+ // the enclosing pattern has been typechecked; see `indexPattern` in `typedCase`.
+ val wildcardSym = newPatternBoundSym(tpnme.WILDCARD, tree1.tpe, tree.pos)
+ tree1.withType(wildcardSym.typeRef)
+ }
+ else tree1
+ }
+
+ def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") {
+ val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos)
+ val body1 = typed(tree.body, pt1)
+ typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}")
+ body1 match {
+ case UnApply(fn, Nil, arg :: Nil) if tree.body.isInstanceOf[untpd.Typed] =>
+ // A typed pattern `x @ (_: T)` with an implicit `ctag: ClassTag[T]`
+ // was rewritten to `x @ ctag(_)`.
+ // Rewrite further to `ctag(x @ _)`
+ assert(fn.symbol.owner == defn.ClassTagClass)
+ tpd.cpy.UnApply(body1)(fn, Nil,
+ typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil)
+ case _ =>
+ val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos)
+ assignType(cpy.Bind(tree)(tree.name, body1), sym)
+ }
+ }
+
+ def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") {
+ val trees1 = tree.trees mapconserve (typed(_, pt))
+ assignType(cpy.Alternative(tree)(trees1), trees1)
+ }
+
+ def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(implicit ctx: Context): Unit = {
+ // necessary to force annotation trees to be computed.
+ sym.annotations.foreach(_.ensureCompleted)
+ val annotCtx = ctx.outersIterator.dropWhile(_.owner == sym).next
+ // necessary in order to mark the typed ahead annotations as definitely typed:
+ untpd.modsDeco(mdef).mods.annotations.foreach(typedAnnotation(_)(annotCtx))
+ }
+
+ def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = track("typedAnnotation") {
+ typed(annot, defn.AnnotationType)
+ }
+
+ def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = track("typedValDef") {
+ val ValDef(name, tpt, _) = vdef
+ completeAnnotations(vdef, sym)
+ val tpt1 = checkSimpleKinded(typedType(tpt))
+ val rhs1 = vdef.rhs match {
+ case rhs @ Ident(nme.WILDCARD) => rhs withType tpt1.tpe
+ case rhs => typedExpr(rhs, tpt1.tpe)
+ }
+ val vdef1 = assignType(cpy.ValDef(vdef)(name, tpt1, rhs1), sym)
+ if (sym.is(Inline, butNot = DeferredOrParamAccessor))
+ checkInlineConformant(rhs1, em"right-hand side of inline $sym")
+ patchIfLazy(vdef1)
+ vdef1
+ }
+
+ /** Add a @volitile to lazy vals when rewriting from Scala2 */
+ private def patchIfLazy(vdef: ValDef)(implicit ctx: Context): Unit = {
+ val sym = vdef.symbol
+ if (sym.is(Lazy, butNot = Deferred | Module | Synthetic) && !sym.isVolatile &&
+ ctx.scala2Mode && ctx.settings.rewrite.value.isDefined &&
+ !ctx.isAfterTyper)
+ patch(Position(toUntyped(vdef).pos.start), "@volatile ")
+ }
+
+ def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") {
+ val DefDef(name, tparams, vparamss, tpt, _) = ddef
+ completeAnnotations(ddef, sym)
+ val tparams1 = tparams mapconserve (typed(_).asInstanceOf[TypeDef])
+ val vparamss1 = vparamss nestedMapconserve (typed(_).asInstanceOf[ValDef])
+ if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1)
+ var tpt1 = checkSimpleKinded(typedType(tpt))
+
+ var rhsCtx = ctx
+ if (sym.isConstructor && !sym.isPrimaryConstructor && tparams1.nonEmpty) {
+ // for secondary constructors we need a context that "knows"
+ // that their type parameters are aliases of the class type parameters.
+ // See pos/i941.scala
+ rhsCtx = ctx.fresh.setFreshGADTBounds
+ (tparams1, sym.owner.typeParams).zipped.foreach ((tdef, tparam) =>
+ rhsCtx.gadt.setBounds(tdef.symbol, TypeAlias(tparam.typeRef)))
+ }
+ val rhs1 = typedExpr(ddef.rhs, tpt1.tpe)(rhsCtx)
+
+ // Overwrite inline body to make sure it is not evaluated twice
+ if (sym.isInlineMethod) Inliner.registerInlineInfo(sym, _ => rhs1)
+
+ if (sym.isAnonymousFunction) {
+ // If we define an anonymous function, make sure the return type does not
+ // refer to parameters. This is necessary because closure types are
+ // function types so no dependencies on parameters are allowed.
+ tpt1 = tpt1.withType(avoid(tpt1.tpe, vparamss1.flatMap(_.map(_.symbol))))
+ }
+
+ assignType(cpy.DefDef(ddef)(name, tparams1, vparamss1, tpt1, rhs1), sym)
+ //todo: make sure dependent method types do not depend on implicits or by-name params
+ }
+
+ def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") {
+ val TypeDef(name, rhs) = tdef
+ completeAnnotations(tdef, sym)
+ val rhs1 = tdef.rhs match {
+ case rhs @ PolyTypeTree(tparams, body) =>
+ val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
+ val body1 = typedType(body)
+ assignType(cpy.PolyTypeTree(rhs)(tparams1, body1), tparams1, body1)
+ case rhs =>
+ typedType(rhs)
+ }
+ assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
+ }
+
+ def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") {
+ val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef
+ val superCtx = ctx.superCallContext
+
+ /** If `ref` is an implicitly parameterized trait, pass an implicit argument list.
+ * Otherwise, if `ref` is a parameterized trait, error.
+ * Note: Traits and classes currently always have at least an empty parameter list ()
+ * before the implicit parameters (this is inserted if not given in source).
+ * We skip this parameter list when deciding whether a trait is parameterless or not.
+ * @param ref The tree referring to the (parent) trait
+ * @param psym Its type symbol
+ * @param cinfo The info of its constructor
+ */
+ def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match {
+ case cinfo: PolyType =>
+ maybeCall(ref, psym, cinfo.resultType)
+ case cinfo @ MethodType(Nil, _) if cinfo.resultType.isInstanceOf[ImplicitMethodType] =>
+ val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone
+ typedExpr(untpd.TypedSplice(icall))(superCtx)
+ case cinfo @ MethodType(Nil, _) if !cinfo.resultType.isInstanceOf[MethodType] =>
+ ref
+ case cinfo: MethodType =>
+ if (!ctx.erasedTypes) { // after constructors arguments are passed in super call.
+ typr.println(i"constr type: $cinfo")
+ ctx.error(em"parameterized $psym lacks argument list", ref.pos)
+ }
+ ref
+ case _ =>
+ ref
+ }
+
+ def typedParent(tree: untpd.Tree): Tree =
+ if (tree.isType) {
+ val result = typedType(tree)(superCtx)
+ val psym = result.tpe.typeSymbol
+ if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym))
+ maybeCall(result, psym, psym.primaryConstructor.info)
+ else
+ result
+ }
+ else {
+ val result = typedExpr(tree)(superCtx)
+ checkParentCall(result, cls)
+ result
+ }
+
+ completeAnnotations(cdef, cls)
+ val constr1 = typed(constr).asInstanceOf[DefDef]
+ val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic)
+ val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx)
+ val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
+ val dummy = localDummy(cls, impl)
+ val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
+
+ // Expand comments and type usecases
+ cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)
+
+ checkNoDoubleDefs(cls)
+ val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1)
+ .withType(dummy.nonMemberTermRef)
+ checkVariance(impl1)
+ if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos)
+ val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
+ if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) {
+ val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass))
+ ctx.featureWarning(nme.dynamics.toString, "extension of type scala.Dynamic", isScala2Feature = true,
+ cls, isRequired, cdef.pos)
+ }
+ cdef1
+
+ // todo later: check that
+ // 1. If class is non-abstract, it is instantiatable:
+ // - self type is s supertype of own type
+ // - all type members have consistent bounds
+ // 2. all private type members have consistent bounds
+ // 3. Types do not override classes.
+ // 4. Polymorphic type defs override nothing.
+ }
+
+ /** Ensure that the first type in a list of parent types Ps points to a non-trait class.
+ * If that's not already the case, add one. The added class type CT is determined as follows.
+ * First, let C be the unique class such that
+ * - there is a parent P_i such that P_i derives from C, and
+ * - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
+ * Then, let CT be the smallest type which
+ * - has C as its class symbol, and
+ * - for all parents P_i: If P_i derives from C then P_i <:< CT.
+ */
+ def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = {
+ def realClassParent(cls: Symbol): ClassSymbol =
+ if (!cls.isClass) defn.ObjectClass
+ else if (!(cls is Trait)) cls.asClass
+ else cls.asClass.classParents match {
+ case parentRef :: _ => realClassParent(parentRef.symbol)
+ case nil => defn.ObjectClass
+ }
+ def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = {
+ val pcls = realClassParent(parent.classSymbol)
+ if (pcls derivesFrom candidate) pcls else candidate
+ }
+ parents match {
+ case p :: _ if p.classSymbol.isRealClass => parents
+ case _ =>
+ val pcls = (defn.ObjectClass /: parents)(improve)
+ typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %")
+ val ptype = ctx.typeComparer.glb(
+ defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls)))
+ ptype :: parents
+ }
+ }
+
+ /** Ensure that first parent tree refers to a real class. */
+ def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match {
+ case p :: ps if p.tpe.classSymbol.isRealClass => parents
+ case _ =>
+ // add synthetic class type
+ val first :: _ = ensureFirstIsClass(parents.tpes)
+ TypeTree(checkFeasible(first, pos, em"\n in inferred parent $first")).withPos(pos) :: parents
+ }
+
+ /** If this is a real class, make sure its first parent is a
+ * constructor call. Cannot simply use a type. Overridden in ReTyper.
+ */
+ def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = {
+ val firstParent :: otherParents = parents
+ if (firstParent.isType && !(cls is Trait) && !cls.is(JavaDefined))
+ typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents
+ else parents
+ }
+
+ /** Overridden in retyper */
+ def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree)
+
+ def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context): Symbol =
+ ctx.newLocalDummy(cls, impl.pos)
+
+ def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") {
+ val expr1 = typedExpr(imp.expr, AnySelectionProto)
+ checkStable(expr1.tpe, imp.expr.pos)
+ if (!ctx.isAfterTyper) checkRealizable(expr1.tpe, imp.expr.pos)
+ assignType(cpy.Import(imp)(expr1, imp.selectors), sym)
+ }
+
+ def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") {
+ val pid1 = typedExpr(tree.pid, AnySelectionProto)(ctx.addMode(Mode.InPackageClauseName))
+ val pkg = pid1.symbol
+
+ // Package will not exist if a duplicate type has already been entered, see
+ // `tests/neg/1708.scala`, else branch's error message should be supressed
+ if (pkg.exists) {
+ val packageContext =
+ if (pkg is Package) ctx.fresh.setOwner(pkg.moduleClass).setTree(tree)
+ else {
+ ctx.error(em"$pkg is already defined, cannot be a package", tree.pos)
+ ctx
+ }
+ val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageContext)
+ cpy.PackageDef(tree)(pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef
+ } else errorTree(tree, i"package ${tree.pid.name} does not exist")
+ }
+
+ def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") {
+ val annot1 = typedExpr(tree.annot, defn.AnnotationType)
+ val arg1 = typed(tree.arg, pt)
+ if (ctx.mode is Mode.Type)
+ assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
+ else {
+ val tpt = TypeTree(AnnotatedType(arg1.tpe.widen, Annotation(annot1)))
+ assignType(cpy.Typed(tree)(arg1, tpt), tpt)
+ }
+ }
+
+ def typedTypedSplice(tree: untpd.TypedSplice)(implicit ctx: Context): Tree =
+ tree.tree match {
+ case tree1: TypeTree => tree1 // no change owner necessary here ...
+ case tree1: Ident => tree1 // ... or here, since these trees cannot contain bindings
+ case tree1 =>
+ if (ctx.owner ne tree.owner) tree1.changeOwner(tree.owner, ctx.owner)
+ else tree1
+ }
+
+
+ def typedAsFunction(tree: untpd.PostfixOp, pt: Type)(implicit ctx: Context): Tree = {
+ val untpd.PostfixOp(qual, nme.WILDCARD) = tree
+ val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
+ var res = typed(qual, pt1)
+ if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) {
+ def msg = i"not a function: ${res.tpe}; cannot be followed by `_'"
+ if (ctx.scala2Mode) {
+ // Under -rewrite, patch `x _` to `(() => x)`
+ ctx.migrationWarning(msg, tree.pos)
+ patch(Position(tree.pos.start), "(() => ")
+ patch(Position(qual.pos.end, tree.pos.end), ")")
+ res = typed(untpd.Function(Nil, untpd.TypedSplice(res)))
+ }
+ else ctx.error(msg, tree.pos)
+ }
+ res
+ }
+
+ /** Retrieve symbol attached to given tree */
+ protected def retrieveSym(tree: untpd.Tree)(implicit ctx: Context) = tree.removeAttachment(SymOfTree) match {
+ case Some(sym) =>
+ sym.ensureCompleted()
+ sym
+ case none =>
+ NoSymbol
+ }
+
+ /** A fresh local context with given tree and owner.
+ * Owner might not exist (can happen for self valdefs), in which case
+ * no owner is set in result context
+ */
+ protected def localContext(tree: untpd.Tree, owner: Symbol)(implicit ctx: Context): FreshContext = {
+ val freshCtx = ctx.fresh.setTree(tree)
+ if (owner.exists) freshCtx.setOwner(owner) else freshCtx
+ }
+
+ protected def localTyper(sym: Symbol): Typer = nestedTyper.remove(sym).get
+
+ def typedUnadapted(initTree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = {
+ record("typedUnadapted")
+ val xtree = expanded(initTree)
+ xtree.removeAttachment(TypedAhead) match {
+ case Some(ttree) => ttree
+ case none =>
+
+ def typedNamed(tree: untpd.NameTree, pt: Type)(implicit ctx: Context): Tree = {
+ val sym = retrieveSym(xtree)
+ tree match {
+ case tree: untpd.Ident => typedIdent(tree, pt)
+ case tree: untpd.Select => typedSelect(tree, pt)
+ case tree: untpd.Bind => typedBind(tree, pt)
+ case tree: untpd.ValDef =>
+ if (tree.isEmpty) tpd.EmptyValDef
+ else typedValDef(tree, sym)(localContext(tree, sym).setNewScope)
+ case tree: untpd.DefDef =>
+ val typer1 = localTyper(sym)
+ typer1.typedDefDef(tree, sym)(localContext(tree, sym).setTyper(typer1))
+ case tree: untpd.TypeDef =>
+ if (tree.isClassDef)
+ typedClassDef(tree, sym.asClass)(localContext(tree, sym).setMode(ctx.mode &~ Mode.InSuperCall))
+ else
+ typedTypeDef(tree, sym)(localContext(tree, sym).setNewScope)
+ case _ => typedUnadapted(desugar(tree), pt)
+ }
+ }
+
+ def typedUnnamed(tree: untpd.Tree): Tree = tree match {
+ case tree: untpd.Apply =>
+ if (ctx.mode is Mode.Pattern) typedUnApply(tree, pt) else typedApply(tree, pt)
+ case tree: untpd.This => typedThis(tree)
+ case tree: untpd.Literal => typedLiteral(tree)
+ case tree: untpd.New => typedNew(tree, pt)
+ case tree: untpd.Typed => typedTyped(tree, pt)
+ case tree: untpd.NamedArg => typedNamedArg(tree, pt)
+ case tree: untpd.Assign => typedAssign(tree, pt)
+ case tree: untpd.Block => typedBlock(desugar.block(tree), pt)(ctx.fresh.setNewScope)
+ case tree: untpd.If => typedIf(tree, pt)
+ case tree: untpd.Function => typedFunction(tree, pt)
+ case tree: untpd.Closure => typedClosure(tree, pt)
+ case tree: untpd.Match => typedMatch(tree, pt)
+ case tree: untpd.Return => typedReturn(tree)
+ case tree: untpd.Try => typedTry(tree, pt)
+ case tree: untpd.Throw => typedThrow(tree)
+ case tree: untpd.TypeApply => typedTypeApply(tree, pt)
+ case tree: untpd.Super => typedSuper(tree, pt)
+ case tree: untpd.SeqLiteral => typedSeqLiteral(tree, pt)
+ case tree: untpd.Inlined => typedInlined(tree, pt)
+ case tree: untpd.TypeTree => typedTypeTree(tree, pt)
+ case tree: untpd.SingletonTypeTree => typedSingletonTypeTree(tree)
+ case tree: untpd.AndTypeTree => typedAndTypeTree(tree)
+ case tree: untpd.OrTypeTree => typedOrTypeTree(tree)
+ case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree)
+ case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree)
+ case tree: untpd.PolyTypeTree => typedPolyTypeTree(tree)(localContext(tree, NoSymbol).setNewScope)
+ case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree)
+ case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree)
+ case tree: untpd.Alternative => typedAlternative(tree, pt)
+ case tree: untpd.PackageDef => typedPackageDef(tree)
+ case tree: untpd.Annotated => typedAnnotated(tree, pt)
+ case tree: untpd.TypedSplice => typedTypedSplice(tree)
+ case tree: untpd.UnApply => typedUnApply(tree, pt)
+ case tree @ untpd.PostfixOp(qual, nme.WILDCARD) => typedAsFunction(tree, pt)
+ case untpd.EmptyTree => tpd.EmptyTree
+ case _ => typedUnadapted(desugar(tree), pt)
+ }
+
+ xtree match {
+ case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt)
+ case xtree: untpd.Import => typedImport(xtree, retrieveSym(xtree))
+ case xtree => typedUnnamed(xtree)
+ }
+ }
+ }
+
+ protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree =
+ untpd.rename(tree, tree.name.encode)
+
+ def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {
+ assertPositioned(tree)
+ try adapt(typedUnadapted(tree, pt), pt, tree)
+ catch {
+ case ex: CyclicReference => errorTree(tree, cyclicErrorMsg(ex))
+ case ex: TypeError => errorTree(tree, ex.getMessage)
+ }
+ }
+
+ def typedTrees(trees: List[untpd.Tree])(implicit ctx: Context): List[Tree] =
+ trees mapconserve (typed(_))
+
+ def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = {
+ val buf = new mutable.ListBuffer[Tree]
+ @tailrec def traverse(stats: List[untpd.Tree])(implicit ctx: Context): List[Tree] = stats match {
+ case (imp: untpd.Import) :: rest =>
+ val imp1 = typed(imp)
+ buf += imp1
+ traverse(rest)(importContext(imp1.symbol, imp.selectors))
+ case (mdef: untpd.DefTree) :: rest =>
+ mdef.removeAttachment(ExpandedTree) match {
+ case Some(xtree) =>
+ traverse(xtree :: rest)
+ case none =>
+ typed(mdef) match {
+ case mdef1: DefDef if Inliner.hasBodyToInline(mdef1.symbol) =>
+ buf ++= inlineExpansion(mdef1)
+ case mdef1 =>
+ buf += mdef1
+ }
+ traverse(rest)
+ }
+ case Thicket(stats) :: rest =>
+ traverse(stats ++ rest)
+ case stat :: rest =>
+ buf += typed(stat)(ctx.exprContext(stat, exprOwner))
+ traverse(rest)
+ case nil =>
+ buf.toList
+ }
+ traverse(stats)
+ }
+
+ /** Given an inline method `mdef`, the method rewritten so that its body
+ * uses accessors to access non-public members, followed by the accessor definitions.
+ * Overwritten in Retyper to return `mdef` unchanged.
+ */
+ protected def inlineExpansion(mdef: DefDef)(implicit ctx: Context): List[Tree] =
+ tpd.cpy.DefDef(mdef)(rhs = Inliner.bodyToInline(mdef.symbol)) ::
+ Inliner.removeInlineAccessors(mdef.symbol)
+
+ def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
+ typed(tree, pt)(ctx retractMode Mode.PatternOrType)
+ def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern?
+ typed(tree, pt)(ctx addMode Mode.Type)
+ def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(implicit ctx: Context): Tree =
+ typed(tree, selType)(ctx addMode Mode.Pattern)
+
+ def tryEither[T](op: Context => T)(fallBack: (T, TyperState) => T)(implicit ctx: Context) = {
+ val nestedCtx = ctx.fresh.setNewTyperState
+ val result = op(nestedCtx)
+ if (nestedCtx.reporter.hasErrors)
+ fallBack(result, nestedCtx.typerState)
+ else {
+ nestedCtx.typerState.commit()
+ result
+ }
+ }
+
+ /** Try `op1`, if there are errors, try `op2`, if `op2` also causes errors, fall back
+ * to errors and result of `op1`.
+ */
+ def tryAlternatively[T](op1: Context => T)(op2: Context => T)(implicit ctx: Context): T =
+ tryEither(op1) { (failedVal, failedState) =>
+ tryEither(op2) { (_, _) =>
+ failedState.commit
+ failedVal
+ }
+ }
+
+ /** Add apply node or implicit conversions. Two strategies are tried, and the first
+ * that is successful is picked. If neither of the strategies are successful, continues with
+ * `fallBack`.
+ *
+ * 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
+ * 2nd strategy: If tree is a select `qual.name`, try to insert an implicit conversion
+ * around the qualifier part `qual` so that the result conforms to the expected type
+ * with wildcard result type.
+ */
+ def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
+ tryEither { implicit ctx =>
+ val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
+ if (sel.tpe.isError) sel else adapt(sel, pt)
+ } { (failedTree, failedState) =>
+ tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack(failedTree, failedState))
+ }
+
+ /** If this tree is a select node `qual.name`, try to insert an implicit conversion
+ * `c` around `qual` so that `c(qual).name` conforms to `pt`.
+ */
+ def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
+ tree match {
+ case Select(qual, name) =>
+ val qualProto = SelectionProto(name, pt, NoViewsAllowed)
+ tryEither { implicit ctx =>
+ val qual1 = adaptInterpolated(qual, qualProto, EmptyTree)
+ if ((qual eq qual1) || ctx.reporter.hasErrors) None
+ else Some(typed(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt))
+ } { (_, _) => None
+ }
+ case _ => None
+ }
+ }
+
+ def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ {
+ /*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ {
+ if (tree.isDef) interpolateUndetVars(tree, tree.symbol)
+ else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol)
+ tree.overwriteType(tree.tpe.simplified)
+ adaptInterpolated(tree, pt, original)
+ }
+ }
+
+ /** (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
+ * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
+ */
+
+ /** Perform the following adaptations of expression, pattern or type `tree` wrt to
+ * given prototype `pt`:
+ * (1) Resolve overloading
+ * (2) Apply parameterless functions
+ * (3) Apply polymorphic types to fresh instances of their type parameters and
+ * store these instances in context.undetparams,
+ * unless followed by explicit type application.
+ * (4) Do the following to unapplied methods used as values:
+ * (4.1) If the method has only implicit parameters pass implicit arguments
+ * (4.2) otherwise, if `pt` is a function type and method is not a constructor,
+ * convert to function by eta-expansion,
+ * (4.3) otherwise, if the method is nullary with a result type compatible to `pt`
+ * and it is not a constructor, apply it to ()
+ * otherwise issue an error
+ * (5) Convert constructors in a pattern as follows:
+ * (5.1) If constructor refers to a case class factory, set tree's type to the unique
+ * instance of its primary constructor that is a subtype of the expected type.
+ * (5.2) If constructor refers to an extractor, convert to application of
+ * unapply or unapplySeq method.
+ *
+ * (6) Convert all other types to TypeTree nodes.
+ * (7) When in TYPEmode but not FUNmode or HKmode, check that types are fully parameterized
+ * (7.1) In HKmode, higher-kinded types are allowed, but they must have the expected kind-arity
+ * (8) When in both EXPRmode and FUNmode, add apply method calls to values of object type.
+ * (9) If there are undetermined type variables and not POLYmode, infer expression instance
+ * Then, if tree's type is not a subtype of expected type, try the following adaptations:
+ * (10) If the expected type is Byte, Short or Char, and the expression
+ * is an integer fitting in the range of that type, convert it to that type.
+ * (11) Widen numeric literals to their expected type, if necessary
+ * (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
+ * (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
+ * (14) When in mode EXPRmode, apply a view
+ * If all this fails, error
+ */
+ def adaptInterpolated(tree: Tree, pt: Type, original: untpd.Tree)(implicit ctx: Context): Tree = {
+
+ assert(pt.exists)
+
+ def methodStr = err.refStr(methPart(tree).tpe)
+
+ def missingArgs = errorTree(tree,
+ em"""missing arguments for $methodStr
+ |follow this method with `_' if you want to treat it as a partially applied function""")
+
+ def adaptOverloaded(ref: TermRef) = {
+ val altDenots = ref.denot.alternatives
+ typr.println(i"adapt overloaded $ref with alternatives ${altDenots map (_.info)}%, %")
+ val alts = altDenots map (alt =>
+ TermRef.withSigAndDenot(ref.prefix, ref.name, alt.info.signature, alt))
+ def expectedStr = err.expectedTypeStr(pt)
+ resolveOverloaded(alts, pt) match {
+ case alt :: Nil =>
+ adapt(tree.withType(alt), pt, original)
+ case Nil =>
+ def noMatches =
+ errorTree(tree,
+ em"""none of the ${err.overloadedAltsStr(altDenots)}
+ |match $expectedStr""")
+ def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil
+ pt match {
+ case pt: FunProto =>
+ tryInsertApplyOrImplicit(tree, pt)((_, _) => noMatches)
+ case _ =>
+ if (altDenots exists (_.info.paramTypess == ListOfNil))
+ typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt)
+ else
+ noMatches
+ }
+ case alts =>
+ val remainingDenots = alts map (_.denot.asInstanceOf[SingleDenotation])
+ def all = if (remainingDenots.length == 2) "both" else "all"
+ errorTree(tree,
+ em"""Ambiguous overload. The ${err.overloadedAltsStr(remainingDenots)}
+ |$all match $expectedStr""")
+ }
+ }
+
+ def isUnary(tp: Type): Boolean = tp match {
+ case tp: MethodicType =>
+ tp.firstParamTypes match {
+ case ptype :: Nil => !ptype.isRepeatedParam
+ case _ => false
+ }
+ case tp: TermRef =>
+ tp.denot.alternatives.forall(alt => isUnary(alt.info))
+ case _ =>
+ false
+ }
+
+ def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match {
+ case _: MethodType | _: PolyType =>
+ if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple)
+ adaptInterpolated(tree, pt.tupled, original)
+ else
+ tree
+ case _ => tryInsertApplyOrImplicit(tree, pt) {
+ val more = tree match {
+ case Apply(_, _) => " more"
+ case _ => ""
+ }
+ (_, _) => errorTree(tree, em"$methodStr does not take$more parameters")
+ }
+ }
+
+ /** If `tp` is a TypeVar which is fully constrained (i.e. its upper bound `hi` conforms
+ * to its lower bound `lo`), replace `tp` by `hi`. This is necessary to
+ * keep the right constraints for some implicit search problems. The paradigmatic case
+ * is `implicitNums.scala`. Without the healing done in `followAlias`, we cannot infer
+ * implicitly[_3], where _2 is the typelevel number 3. The problem here is that if a
+ * prototype is, say, Succ[Succ[Zero]], we can infer that it's argument type is Succ[Zero].
+ * But if the prototype is N? >: Succ[Succ[Zero]] <: Succ[Succ[Zero]], the same
+ * decomposition does not work - we'd get a N?#M where M is the element type name of Succ
+ * instead.
+ */
+ def followAlias(tp: Type)(implicit ctx: Context): Type = {
+ val constraint = ctx.typerState.constraint
+ def inst(tp: Type): Type = tp match {
+ case TypeBounds(lo, hi)
+ if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) =>
+ inst(lo)
+ case tp: PolyParam =>
+ constraint.typeVarOfParam(tp).orElse(tp)
+ case _ => tp
+ }
+ tp match {
+ case tp: TypeVar if constraint.contains(tp) => inst(constraint.entry(tp.origin))
+ case _ => tp
+ }
+ }
+
+ def adaptNoArgs(wtp: Type): Tree = wtp match {
+ case wtp: ExprType =>
+ adaptInterpolated(tree.withType(wtp.resultType), pt, original)
+ case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) =>
+ val tvarsToInstantiate = tvarsInParams(tree)
+ wtp.paramTypes.foreach(instantiateSelected(_, tvarsToInstantiate))
+ val constr = ctx.typerState.constraint
+ def addImplicitArgs(implicit ctx: Context) = {
+ val errors = new mutable.ListBuffer[() => String]
+ def implicitArgError(msg: => String) = {
+ errors += (() => msg)
+ EmptyTree
+ }
+ def issueErrors() = {
+ for (err <- errors) ctx.error(err(), tree.pos.endPos)
+ tree.withType(wtp.resultType)
+ }
+ val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) =>
+ def implicitArgError(msg: String => String) =
+ errors += (() => msg(em"parameter $pname of $methodStr"))
+ inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
+ }
+ if (errors.nonEmpty) {
+ // If there are several arguments, some arguments might already
+ // have influenced the context, binding variables, but later ones
+ // might fail. In that case the constraint needs to be reset.
+ ctx.typerState.constraint = constr
+
+ // If method has default params, fall back to regular application
+ // where all inferred implicits are passed as named args.
+ if (tree.symbol.hasDefaultParams) {
+ val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) =>
+ arg match {
+ case EmptyTree => Nil
+ case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
+ }
+ }
+ tryEither { implicit ctx =>
+ typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt)
+ } { (_, _) =>
+ issueErrors()
+ }
+ } else issueErrors()
+ }
+ else adapt(tpd.Apply(tree, args), pt)
+ }
+ if ((pt eq WildcardType) || original.isEmpty) addImplicitArgs(argCtx(tree))
+ else
+ ctx.typerState.tryWithFallback(addImplicitArgs(argCtx(tree))) {
+ adapt(typed(original, WildcardType), pt, EmptyTree)
+ }
+ case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
+ val arity =
+ if (defn.isFunctionType(pt))
+ if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
+ // if method type is fully defined, but expected type is not,
+ // prioritize method parameter types as parameter types of the eta-expanded closure
+ 0
+ else defn.functionArity(pt)
+ else if (pt eq AnyFunctionProto) wtp.paramTypes.length
+ else -1
+ if (arity >= 0 && !tree.symbol.isConstructor)
+ typed(etaExpand(tree, wtp, arity), pt)
+ else if (wtp.paramTypes.isEmpty)
+ adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree)
+ else if (wtp.isImplicit)
+ err.typeMismatch(tree, pt)
+ else
+ missingArgs
+ case _ =>
+ ctx.typeComparer.GADTused = false
+ if (ctx.mode is Mode.Pattern) {
+ tree match {
+ case _: RefTree | _: Literal if !isVarPattern(tree) =>
+ checkCanEqual(pt, wtp, tree.pos)(ctx.retractMode(Mode.Pattern))
+ case _ =>
+ }
+ tree
+ }
+ else if (tree.tpe <:< pt) {
+ if (pt.hasAnnotation(defn.InlineParamAnnot))
+ checkInlineConformant(tree, "argument to inline parameter")
+ if (Inliner.hasBodyToInline(tree.symbol) &&
+ !ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
+ !ctx.settings.YnoInline.value &&
+ !ctx.isAfterTyper)
+ adapt(Inliner.inlineCall(tree, pt), pt)
+ else if (ctx.typeComparer.GADTused && pt.isValueType)
+ // Insert an explicit cast, so that -Ycheck in later phases succeeds.
+ // I suspect, but am not 100% sure that this might affect inferred types,
+ // if the expected type is a supertype of the GADT bound. It would be good to come
+ // up with a test case for this.
+ tree.asInstance(pt)
+ else
+ tree
+ }
+ else if (wtp.isInstanceOf[MethodType]) missingArgs
+ else {
+ typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
+ //typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
+ adaptToSubType(wtp)
+ }
+ }
+ /** Adapt an expression of constant type to a different constant type `tpe`. */
+ def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {
+ def lit = Literal(tpe.value).withPos(tree.pos)
+ tree match {
+ case Literal(c) => lit
+ case tree @ Block(stats, expr) => tpd.cpy.Block(tree)(stats, adaptConstant(expr, tpe))
+ case tree =>
+ if (isIdempotentExpr(tree)) lit // See discussion in phase Literalize why we demand isIdempotentExpr
+ else Block(tree :: Nil, lit)
+ }
+ }
+
+ def adaptToSubType(wtp: Type): Tree = {
+ // try converting a constant to the target type
+ val folded = ConstFold(tree, pt)
+ if (folded ne tree) return adaptConstant(folded, folded.tpe.asInstanceOf[ConstantType])
+ // drop type if prototype is Unit
+ if (pt isRef defn.UnitClass)
+ // local adaptation makes sure every adapted tree conforms to its pt
+ // so will take the code path that decides on inlining
+ return tpd.Block(adapt(tree, WildcardType) :: Nil, Literal(Constant(())))
+ // convert function literal to SAM closure
+ tree match {
+ case Closure(Nil, id @ Ident(nme.ANON_FUN), _)
+ if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
+ pt match {
+ case SAMType(meth)
+ if wtp <:< meth.info.toFunctionType() =>
+ // was ... && isFullyDefined(pt, ForceDegree.noBottom)
+ // but this prevents case blocks from implementing polymorphic partial functions,
+ // since we do not know the result parameter a priori. Have to wait until the
+ // body is typechecked.
+ return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt)
+ case _ =>
+ }
+ case _ =>
+ }
+ // try an implicit conversion
+ inferView(tree, pt) match {
+ case SearchSuccess(inferred, _, _) =>
+ adapt(inferred, pt)
+ case failure: SearchFailure =>
+ if (pt.isInstanceOf[ProtoType] && !failure.isInstanceOf[AmbiguousImplicits]) tree
+ else err.typeMismatch(tree, pt, failure)
+ }
+ }
+
+ def adaptType(tp: Type): Tree = {
+ val tree1 =
+ if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree
+ else tree.withType(tree.tpe.EtaExpand(tp.typeParamSymbols))
+ if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1
+ else err.typeMismatch(tree1, pt)
+ }
+
+ tree match {
+ case _: MemberDef | _: PackageDef | _: Import | _: WithoutTypeOrPos[_] => tree
+ case _ => tree.tpe.widen match {
+ case _: ErrorType =>
+ tree
+ case ref: TermRef =>
+ pt match {
+ case pt: FunProto
+ if pt.args.lengthCompare(1) > 0 && isUnary(ref) && ctx.canAutoTuple =>
+ adaptInterpolated(tree, pt.tupled, original)
+ case _ =>
+ adaptOverloaded(ref)
+ }
+ case poly: PolyType if !(ctx.mode is Mode.Type) =>
+ if (pt.isInstanceOf[PolyProto]) tree
+ else {
+ var typeArgs = tree match {
+ case Select(qual, nme.CONSTRUCTOR) => qual.tpe.widenDealias.argTypesLo
+ case _ => Nil
+ }
+ if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2
+ convertNewGenericArray(
+ adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original))
+ }
+ case wtp =>
+ pt match {
+ case pt: FunProto =>
+ adaptToArgs(wtp, pt)
+ case pt: PolyProto =>
+ tryInsertApplyOrImplicit(tree, pt) {
+ (_, _) => tree // error will be reported in typedTypeApply
+ }
+ case _ =>
+ if (ctx.mode is Mode.Type) adaptType(tree.tpe)
+ else adaptNoArgs(wtp)
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala
new file mode 100644
index 000000000..d5dd5a024
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala
@@ -0,0 +1,148 @@
+package dotty.tools.dotc
+package typer
+
+import dotty.tools.dotc.ast.{ Trees, tpd }
+import core._
+import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._
+import Decorators._
+import Variances._
+import util.Positions._
+import rewrite.Rewrites.patch
+import config.Printers.variances
+
+/** Provides `check` method to check that all top-level definitions
+ * in tree are variance correct. Does not recurse inside methods.
+ * The method should be invoked once for each Template.
+ */
+object VarianceChecker {
+ case class VarianceError(tvar: Symbol, required: Variance)
+ def check(tree: tpd.Tree)(implicit ctx: Context) =
+ new VarianceChecker()(ctx).Traverser.traverse(tree)
+}
+
+class VarianceChecker()(implicit ctx: Context) {
+ import VarianceChecker._
+ import tpd._
+
+ private object Validator extends TypeAccumulator[Option[VarianceError]] {
+ private var base: Symbol = _
+
+ /** Is no variance checking needed within definition of `base`? */
+ def ignoreVarianceIn(base: Symbol): Boolean = (
+ base.isTerm
+ || base.is(Package)
+ || base.is(Local)
+ )
+
+ /** The variance of a symbol occurrence of `tvar` seen at the level of the definition of `base`.
+ * The search proceeds from `base` to the owner of `tvar`.
+ * Initially the state is covariant, but it might change along the search.
+ */
+ def relativeVariance(tvar: Symbol, base: Symbol, v: Variance = Covariant): Variance = /*ctx.traceIndented(i"relative variance of $tvar wrt $base, so far: $v")*/ {
+ if (base == tvar.owner) v
+ else if ((base is Param) && base.owner.isTerm)
+ relativeVariance(tvar, paramOuter(base.owner), flip(v))
+ else if (ignoreVarianceIn(base.owner)) Bivariant
+ else if (base.isAliasType) relativeVariance(tvar, base.owner, Invariant)
+ else relativeVariance(tvar, base.owner, v)
+ }
+
+ /** The next level to take into account when determining the
+ * relative variance with a method parameter as base. The method
+ * is always skipped. If the method is a constructor, we also skip
+ * its class owner, because constructors are not checked for variance
+ * relative to the type parameters of their own class. On the other
+ * hand constructors do count for checking the variance of type parameters
+ * of enclosing classes. I believe the Scala 2 rules are too lenient in
+ * that respect.
+ */
+ private def paramOuter(meth: Symbol) =
+ if (meth.isConstructor) meth.owner.owner else meth.owner
+
+ /** Check variance of abstract type `tvar` when referred from `base`. */
+ private def checkVarianceOfSymbol(tvar: Symbol): Option[VarianceError] = {
+ val relative = relativeVariance(tvar, base)
+ if (relative == Bivariant || tvar.is(BaseTypeArg)) None
+ else {
+ val required = compose(relative, this.variance)
+ def tvar_s = s"$tvar (${varianceString(tvar.flags)} ${tvar.showLocated})"
+ def base_s = s"$base in ${base.owner}" + (if (base.owner.isClass) "" else " in " + base.owner.enclosingClass)
+ ctx.log(s"verifying $tvar_s is ${varianceString(required)} at $base_s")
+ ctx.log(s"relative variance: ${varianceString(relative)}")
+ ctx.log(s"current variance: ${this.variance}")
+ ctx.log(s"owner chain: ${base.ownersIterator.toList}")
+ if (tvar is required) None
+ else Some(VarianceError(tvar, required))
+ }
+ }
+
+ /** For PolyTypes, type parameters are skipped because they are defined
+ * explicitly (their TypeDefs will be passed here.) For MethodTypes, the
+ * same is true of the parameters (ValDefs).
+ */
+ def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance", variances) {
+ if (status.isDefined) status
+ else tp match {
+ case tp: TypeRef =>
+ val sym = tp.symbol
+ if (sym.variance != 0 && base.isContainedIn(sym.owner)) checkVarianceOfSymbol(sym)
+ else if (sym.isAliasType) this(status, sym.info.bounds.hi)
+ else foldOver(status, tp)
+ case tp: MethodType =>
+ this(status, tp.resultType) // params will be checked in their TypeDef nodes.
+ case tp: PolyType =>
+ this(status, tp.resultType) // params will be checked in their ValDef nodes.
+ case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot =>
+ status
+ //case tp: ClassInfo =>
+ // ??? not clear what to do here yet. presumably, it's all checked at local typedefs
+ case _ =>
+ foldOver(status, tp)
+ }
+ }
+
+ def validateDefinition(base: Symbol): Option[VarianceError] = {
+ val saved = this.base
+ this.base = base
+ try apply(None, base.info)
+ finally this.base = saved
+ }
+ }
+
+ private object Traverser extends TreeTraverser {
+ def checkVariance(sym: Symbol, pos: Position) = Validator.validateDefinition(sym) match {
+ case Some(VarianceError(tvar, required)) =>
+ def msg = i"${varianceString(tvar.flags)} $tvar occurs in ${varianceString(required)} position in type ${sym.info} of $sym"
+ if (ctx.scala2Mode && sym.owner.isConstructor) {
+ ctx.migrationWarning(s"According to new variance rules, this is no longer accepted; need to annotate with @uncheckedVariance:\n$msg", sym.pos)
+ patch(Position(pos.end), " @scala.annotation.unchecked.uncheckedVariance") // TODO use an import or shorten if possible
+ }
+ else ctx.error(msg, sym.pos)
+ case None =>
+ }
+
+ override def traverse(tree: Tree)(implicit ctx: Context) = {
+ def sym = tree.symbol
+ // No variance check for private/protected[this] methods/values.
+ def skip =
+ !sym.exists ||
+ sym.is(Local) || // !!! watch out for protected local!
+ sym.is(TypeParam) && sym.owner.isClass // already taken care of in primary constructor of class
+ tree match {
+ case defn: MemberDef if skip =>
+ ctx.debuglog(s"Skipping variance check of ${sym.showDcl}")
+ case tree: TypeDef =>
+ checkVariance(sym, tree.pos)
+ case tree: ValDef =>
+ checkVariance(sym, tree.pos)
+ case DefDef(_, tparams, vparamss, _, _) =>
+ checkVariance(sym, tree.pos)
+ tparams foreach traverse
+ vparamss foreach (_ foreach traverse)
+ case Template(_, _, _, body) =>
+ traverseChildren(tree)
+ case _ =>
+ }
+ }
+ }
+}
diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala
new file mode 100644
index 000000000..92bd9fd74
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala
@@ -0,0 +1,116 @@
+package dotty.tools.dotc
+package typer
+
+import dotty.tools.dotc.ast.{Trees, tpd}
+import core._
+import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._
+import Decorators._
+
+object Variances {
+ import tpd._
+
+ type Variance = FlagSet
+ val Bivariant = VarianceFlags
+ val Invariant = EmptyFlags
+
+ /** Flip between covariant and contravariant */
+ def flip(v: Variance): Variance = {
+ if (v == Covariant) Contravariant
+ else if (v == Contravariant) Covariant
+ else v
+ }
+
+ /** Map everything below Bivariant to Invariant */
+ def cut(v: Variance): Variance =
+ if (v == Bivariant) v else Invariant
+
+ def compose(v: Variance, boundsVariance: Int) =
+ if (boundsVariance == 1) v
+ else if (boundsVariance == -1) flip(v)
+ else cut(v)
+
+ /** Compute variance of type parameter `tparam' in types of all symbols `sym'. */
+ def varianceInSyms(syms: List[Symbol])(tparam: Symbol)(implicit ctx: Context): Variance =
+ (Bivariant /: syms) ((v, sym) => v & varianceInSym(sym)(tparam))
+
+ /** Compute variance of type parameter `tparam' in type of symbol `sym'. */
+ def varianceInSym(sym: Symbol)(tparam: Symbol)(implicit ctx: Context): Variance =
+ if (sym.isAliasType) cut(varianceInType(sym.info)(tparam))
+ else varianceInType(sym.info)(tparam)
+
+ /** Compute variance of type parameter `tparam' in all types `tps'. */
+ def varianceInTypes(tps: List[Type])(tparam: Symbol)(implicit ctx: Context): Variance =
+ (Bivariant /: tps) ((v, tp) => v & varianceInType(tp)(tparam))
+
+ /** Compute variance of type parameter `tparam' in all type arguments
+ * <code>tps</code> which correspond to formal type parameters `tparams1'.
+ */
+ def varianceInArgs(tps: List[Type], tparams1: List[Symbol])(tparam: Symbol)(implicit ctx: Context): Variance = {
+ var v: Variance = Bivariant;
+ for ((tp, tparam1) <- tps zip tparams1) {
+ val v1 = varianceInType(tp)(tparam)
+ v = v & (if (tparam1.is(Covariant)) v1
+ else if (tparam1.is(Contravariant)) flip(v1)
+ else cut(v1))
+ }
+ v
+ }
+
+ /** Compute variance of type parameter `tparam' in all type annotations `annots'. */
+ def varianceInAnnots(annots: List[Annotation])(tparam: Symbol)(implicit ctx: Context): Variance = {
+ (Bivariant /: annots) ((v, annot) => v & varianceInAnnot(annot)(tparam))
+ }
+
+ /** Compute variance of type parameter `tparam' in type annotation `annot'. */
+ def varianceInAnnot(annot: Annotation)(tparam: Symbol)(implicit ctx: Context): Variance = {
+ varianceInType(annot.tree.tpe)(tparam)
+ }
+
+ /** Compute variance of type parameter <code>tparam</code> in type <code>tp</code>. */
+ def varianceInType(tp: Type)(tparam: Symbol)(implicit ctx: Context): Variance = tp match {
+ case TermRef(pre, _) =>
+ varianceInType(pre)(tparam)
+ case tp @ TypeRef(pre, _) =>
+ if (tp.symbol == tparam) Covariant else varianceInType(pre)(tparam)
+ case tp @ TypeBounds(lo, hi) =>
+ if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance)
+ else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam)
+ case tp @ RefinedType(parent, _, rinfo) =>
+ varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam)
+ case tp: RecType =>
+ varianceInType(tp.parent)(tparam)
+ case tp @ MethodType(_, paramTypes) =>
+ flip(varianceInTypes(paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam)
+ case ExprType(restpe) =>
+ varianceInType(restpe)(tparam)
+ case tp @ HKApply(tycon, args) =>
+ def varianceInArgs(v: Variance, args: List[Type], tparams: List[TypeParamInfo]): Variance =
+ args match {
+ case arg :: args1 =>
+ varianceInArgs(
+ v & compose(varianceInType(arg)(tparam), tparams.head.paramVariance),
+ args1, tparams.tail)
+ case nil =>
+ v
+ }
+ varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams)
+ case tp: PolyType =>
+ flip(varianceInTypes(tp.paramBounds)(tparam)) & varianceInType(tp.resultType)(tparam)
+ case AnnotatedType(tp, annot) =>
+ varianceInType(tp)(tparam) & varianceInAnnot(annot)(tparam)
+ case tp: AndOrType =>
+ varianceInType(tp.tp1)(tparam) & varianceInType(tp.tp2)(tparam)
+ case _ =>
+ Bivariant
+ }
+
+ def varianceString(v: Variance) =
+ if (v is Covariant) "covariant"
+ else if (v is Contravariant) "contravariant"
+ else "invariant"
+
+ def varianceString(v: Int) =
+ if (v > 0) "+"
+ else if (v < 0) "-"
+ else ""
+}