|
|
@@ -1,6384 +1,79 @@
|
|
|
-/*
|
|
|
- * Scala (https://www.scala-lang.org)
|
|
|
- *
|
|
|
- * Copyright EPFL and Lightbend, Inc.
|
|
|
- *
|
|
|
- * Licensed under Apache License 2.0
|
|
|
- * (http://www.apache.org/licenses/LICENSE-2.0).
|
|
|
- *
|
|
|
- * See the NOTICE file distributed with this work for
|
|
|
- * additional information regarding copyright ownership.
|
|
|
- */
|
|
|
+package mypackage
|
|
|
|
|
|
-package scala
|
|
|
-package tools.nsc
|
|
|
-package typechecker
|
|
|
+import scala.otherpackage
|
|
|
|
|
|
-import scala.annotation.tailrec
|
|
|
-import scala.collection.mutable
|
|
|
-import scala.reflect.internal.{Chars, TypesStats}
|
|
|
-import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics}
|
|
|
-import scala.tools.nsc.Reporting.{MessageFilter, Suppression, WConf, WarningCategory}
|
|
|
-import scala.util.chaining._
|
|
|
-import mutable.ListBuffer
|
|
|
-import symtab.Flags._
|
|
|
-import Mode._
|
|
|
+trait MyTrait extends SuperTrait with OtherTrait {
|
|
|
+ self: SelfType =>
|
|
|
|
|
|
-/** A provider of methods to assign types to trees.
|
|
|
- *
|
|
|
- * @author Martin Odersky
|
|
|
- */
|
|
|
-trait Typers extends Adaptations with Tags with TypersTracking with PatternTypers {
|
|
|
- self: Analyzer =>
|
|
|
+ final val valAssignee = assigned
|
|
|
|
|
|
- import global._
|
|
|
- import definitions._
|
|
|
- import statistics._
|
|
|
-
|
|
|
- final def forArgMode(fun: Tree, mode: Mode) =
|
|
|
- if (treeInfo.isSelfOrSuperConstrCall(fun)) mode | SCCmode else mode
|
|
|
-
|
|
|
- final val shortenImports = false
|
|
|
-
|
|
|
- // All typechecked RHS of ValDefs for right-associative operator desugaring
|
|
|
private val rightAssocValDefs = new mutable.AnyRefMap[Symbol, Tree]
|
|
|
- // Symbols of ValDefs for right-associative operator desugaring which are passed by name and have been inlined
|
|
|
- private val inlinedRightAssocValDefs = new mutable.HashSet[Symbol]
|
|
|
-
|
|
|
- // For each class, we collect a mapping from constructor param accessors that are aliases of their superclass
|
|
|
- // param accessors. At the end of the typer phase, when this information is available all the way up the superclass
|
|
|
- // chain, this is used to determine which are true aliases, ones where the field can be elided from this class.
|
|
|
- // And yes, if you were asking, this is yet another binary fragility, as we bake knowledge of the super class into
|
|
|
- // this class.
|
|
|
- private val superConstructorCalls: mutable.AnyRefMap[Symbol, collection.Map[Symbol, Symbol]] = perRunCaches.newAnyRefMap()
|
|
|
|
|
|
- // allows override of the behavior of the resetTyper method w.r.t comments
|
|
|
- def resetDocComments() = clearDocComments()
|
|
|
+ sealed abstract class MyClass[+T] {
|
|
|
+ def attribute: Boolean
|
|
|
|
|
|
- def resetTyper(): Unit = {
|
|
|
- //println("resetTyper called")
|
|
|
- resetContexts()
|
|
|
- resetImplicits()
|
|
|
- resetDocComments()
|
|
|
- rightAssocValDefs.clear()
|
|
|
- inlinedRightAssocValDefs.clear()
|
|
|
- superConstructorCalls.clear()
|
|
|
- }
|
|
|
-
|
|
|
- sealed abstract class SilentResult[+T] {
|
|
|
- def isEmpty: Boolean
|
|
|
- def nonEmpty = !isEmpty
|
|
|
-
|
|
|
- @inline final def fold[U](none: => U)(f: T => U): U = this match {
|
|
|
- case SilentResultValue(value) => f(value)
|
|
|
+ @inline final def methodWithTypeParam[U](none: => U)(f: T => U): U = this match {
|
|
|
+ case SubType(attr) => f(attr)
|
|
|
case _ => none
|
|
|
}
|
|
|
- @inline final def map[U](f: T => U): SilentResult[U] = this match {
|
|
|
- case SilentResultValue(value) => SilentResultValue(f(value))
|
|
|
- case x: SilentTypeError => x
|
|
|
- }
|
|
|
- @inline final def filter(p: T => Boolean): SilentResult[T] = this match {
|
|
|
- case SilentResultValue(value) if !p(value) => SilentTypeError(TypeErrorWrapper(new TypeError(NoPosition, "!p")))
|
|
|
- case _ => this
|
|
|
- }
|
|
|
- @inline final def orElse[T1 >: T](f: Seq[AbsTypeError] => T1): T1 = this match {
|
|
|
- case SilentResultValue(value) => value
|
|
|
- case s : SilentTypeError => f(s.reportableErrors)
|
|
|
- }
|
|
|
- }
|
|
|
- class SilentTypeError private(val errors: List[AbsTypeError], val warnings: List[(Position, String, WarningCategory, Symbol)]) extends SilentResult[Nothing] {
|
|
|
- override def isEmpty = true
|
|
|
- def err: AbsTypeError = errors.head
|
|
|
- def reportableErrors = errors match {
|
|
|
- case (e1: AmbiguousImplicitTypeError) :: _ =>
|
|
|
- List(e1) // DRYer error reporting for neg/t6436b.scala
|
|
|
- case all =>
|
|
|
- all
|
|
|
- }
|
|
|
- }
|
|
|
- object SilentTypeError {
|
|
|
- def apply(errors: AbsTypeError*): SilentTypeError = apply(errors.toList, Nil)
|
|
|
- def apply(errors: List[AbsTypeError], warnings: List[(Position, String, WarningCategory, Symbol)]): SilentTypeError = new SilentTypeError(errors, warnings)
|
|
|
- // todo: this extracts only one error, should be a separate extractor.
|
|
|
- def unapply(error: SilentTypeError): Option[AbsTypeError] = error.errors.headOption
|
|
|
- }
|
|
|
-
|
|
|
- // todo: should include reporter warnings in SilentResultValue.
|
|
|
- // e.g. tryTypedApply could print warnings on arguments when the typing succeeds.
|
|
|
- case class SilentResultValue[+T](value: T) extends SilentResult[T] { override def isEmpty = false }
|
|
|
-
|
|
|
- def newTyper(context: Context): Typer = new NormalTyper(context)
|
|
|
|
|
|
- private class NormalTyper(context : Context) extends Typer(context)
|
|
|
-
|
|
|
- // A transient flag to mark members of anonymous classes
|
|
|
- // that are turned private by typedBlock
|
|
|
- private final val SYNTHETIC_PRIVATE = TRANS_FLAG
|
|
|
-
|
|
|
- private final val InterpolatorCodeRegex = """\$\{\s*(.*?)\s*\}""".r
|
|
|
- private final val InterpolatorIdentRegex = """\$[$\w]+""".r // note that \w doesn't include $
|
|
|
-
|
|
|
- /** Check that type of given tree does not contain local or private
|
|
|
- * components.
|
|
|
- */
|
|
|
- object checkNoEscaping extends TypeMap {
|
|
|
- private var owner: Symbol = _
|
|
|
- private var scope: Scope = _
|
|
|
- private var hiddenSymbols: List[Symbol] = _
|
|
|
-
|
|
|
- /** Check that type `tree` does not refer to private
|
|
|
- * components unless itself is wrapped in something private
|
|
|
- * (`owner` tells where the type occurs).
|
|
|
- */
|
|
|
- def privates[T <: Tree](typer: Typer, owner: Symbol, tree: T): T =
|
|
|
- if (owner.isJavaDefined) tree else check(typer, owner, EmptyScope, WildcardType, tree)
|
|
|
-
|
|
|
- @tailrec
|
|
|
- private def check[T <: Tree](typer: Typer, owner: Symbol, scope: Scope, pt: Type, tree: T): T = {
|
|
|
- this.owner = owner
|
|
|
- this.scope = scope
|
|
|
- hiddenSymbols = Nil
|
|
|
- import typer.TyperErrorGen._
|
|
|
- val tp1 = apply(tree.tpe)
|
|
|
- if (hiddenSymbols.isEmpty) tree setType tp1
|
|
|
- else if (hiddenSymbols exists (_.isErroneous)) HiddenSymbolWithError(tree)
|
|
|
- else if (isFullyDefined(pt)) tree setType pt
|
|
|
- else if (tp1.typeSymbol.isAnonymousClass)
|
|
|
- check(typer, owner, scope, pt, tree setType tp1.typeSymbol.classBound)
|
|
|
- else if (owner == NoSymbol)
|
|
|
- tree setType packSymbols(hiddenSymbols.reverse, tp1)
|
|
|
- else if (!isPastTyper) { // privates
|
|
|
- val badSymbol = hiddenSymbols.head
|
|
|
- SymbolEscapesScopeError(tree, badSymbol)
|
|
|
- } else tree
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- def addHidden(sym: Symbol) =
|
|
|
- if (!(hiddenSymbols contains sym)) hiddenSymbols = sym :: hiddenSymbols
|
|
|
+ object MyObject extends SuperType {
|
|
|
|
|
|
override def apply(t: Type): Type = {
|
|
|
def checkNoEscape(sym: Symbol): Unit = {
|
|
|
- if (sym.isPrivate && !sym.hasFlag(SYNTHETIC_PRIVATE)) {
|
|
|
- var o = owner
|
|
|
- while (o != NoSymbol && o != sym.owner && o != sym.owner.linkedClassOfClass &&
|
|
|
- !o.isLocalToBlock && !o.isPrivate &&
|
|
|
- !o.privateWithin.hasTransOwner(sym.owner))
|
|
|
- o = o.owner
|
|
|
- if (o == sym.owner || o == sym.owner.linkedClassOfClass)
|
|
|
- addHidden(sym)
|
|
|
- } else if (sym.owner.isTerm && !sym.isTypeParameterOrSkolem) {
|
|
|
- var e = scope.lookupEntry(sym.name)
|
|
|
- var found = false
|
|
|
- while (!found && (e ne null) && e.owner == scope) {
|
|
|
- if (e.sym == sym) {
|
|
|
- found = true
|
|
|
- addHidden(sym)
|
|
|
- } else {
|
|
|
- e = scope.lookupNextEntry(e)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ while (o != NoSymbol && o != sym.owner && o != sym.owner.linkedClassOfClass &&
|
|
|
+ !o.isLocalToBlock && !o.isPrivate &&
|
|
|
+ !o.privateWithin.hasTransOwner(sym.owner))
|
|
|
+ o = o.owner
|
|
|
}
|
|
|
- mapOver(
|
|
|
- t match {
|
|
|
- case TypeRef(_, sym, args) =>
|
|
|
- checkNoEscape(sym)
|
|
|
- if (!hiddenSymbols.isEmpty && hiddenSymbols.head == sym &&
|
|
|
- sym.isAliasType && sameLength(sym.typeParams, args)) {
|
|
|
- hiddenSymbols = hiddenSymbols.tail
|
|
|
- t.dealias
|
|
|
- } else t
|
|
|
- case SingleType(_, sym) =>
|
|
|
- checkNoEscape(sym)
|
|
|
- t
|
|
|
- case _ =>
|
|
|
- t
|
|
|
- })
|
|
|
+
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private final val typerFreshNameCreators = perRunCaches.newAnyRefMap[Symbol, FreshNameCreator]()
|
|
|
- def freshNameCreatorFor(context: Context) = typerFreshNameCreators.getOrElseUpdate(context.outermostContextAtCurrentPos.enclClassOrMethod.owner, new FreshNameCreator)
|
|
|
-
|
|
|
- abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors {
|
|
|
- private def unit = context.unit
|
|
|
- import typeDebug.ptTree
|
|
|
- import TyperErrorGen._
|
|
|
- implicit def fresh: FreshNameCreator = freshNameCreatorFor(context)
|
|
|
-
|
|
|
- private def transformed: mutable.Map[Tree, Tree] = unit.transformed
|
|
|
-
|
|
|
- val infer = new Inferencer {
|
|
|
- def context = Typer.this.context
|
|
|
- // See scala/bug#3281 re undoLog
|
|
|
- override def isCoercible(tp: Type, pt: Type) = undoLog undo viewExists(tp, pt)
|
|
|
- }
|
|
|
-
|
|
|
- /** Overridden to false in scaladoc and/or interactive. */
|
|
|
- def canAdaptConstantTypeToLiteral = true
|
|
|
- def canTranslateEmptyListToNil = true
|
|
|
- def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree
|
|
|
|
|
|
- // used to exempt synthetic accessors (i.e. those that are synthesized by the compiler to access a field)
|
|
|
- // from skolemization because there's a weird bug that causes spurious type mismatches
|
|
|
- // (it seems to have something to do with existential abstraction over values
|
|
|
- // https://github.com/scala/scala-dev/issues/165
|
|
|
- // when we're past typer, lazy accessors are synthetic, but before they are user-defined
|
|
|
- // to make this hack less hacky, we could rework our flag assignment to allow for
|
|
|
- // requiring both the ACCESSOR and the SYNTHETIC bits to trigger the exemption
|
|
|
- private def isSyntheticAccessor(sym: Symbol) = sym.isAccessor && (!sym.isLazy || isPastTyper)
|
|
|
-
|
|
|
- // when type checking during erasure, generate erased types in spots that aren't transformed by erasure
|
|
|
- // (it erases in TypeTrees, but not in, e.g., the type a Function node)
|
|
|
- def phasedAppliedType(sym: Symbol, args: List[Type]) = {
|
|
|
- val tp = appliedType(sym, args)
|
|
|
- if (phase.erasedTypes) erasure.specialScalaErasureFor(sym)(tp) else tp
|
|
|
- }
|
|
|
+ abstract class MyClass2(constrArg: ParamType) extends SuperClass with Trait1 with Trait2 {
|
|
|
|
|
|
- def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree =
|
|
|
- typed(docDef.definition, mode, pt)
|
|
|
+ val anonymousSubclassObject = new scala.AnyVal { }
|
|
|
|
|
|
- /** Find implicit arguments and pass them to given tree.
|
|
|
- */
|
|
|
def applyImplicitArgs(fun: Tree): Tree = fun.tpe match {
|
|
|
case MethodType(params, _) =>
|
|
|
- val argResultsBuff = new ListBuffer[SearchResult]()
|
|
|
- val argBuff = new ListBuffer[Tree]()
|
|
|
- // paramFailed cannot be initialized with params.exists(_.tpe.isError) because that would
|
|
|
- // hide some valid errors for params preceding the erroneous one.
|
|
|
- var paramFailed = false
|
|
|
var mkArg: (Name, Tree) => Tree = (_, tree) => tree
|
|
|
|
|
|
- // DEPMETTODO: instantiate type vars that depend on earlier implicit args (see adapt (4.1))
|
|
|
- //
|
|
|
- // apply the substitutions (undet type param -> type) that were determined
|
|
|
- // by implicit resolution of implicit arguments on the left of this argument
|
|
|
for(param <- params) {
|
|
|
var paramTp = param.tpe
|
|
|
- for(ar <- argResultsBuff)
|
|
|
- paramTp = paramTp.subst(ar.subst.from, ar.subst.to)
|
|
|
-
|
|
|
- val res =
|
|
|
- if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure
|
|
|
- else inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors)
|
|
|
- argResultsBuff += res
|
|
|
-
|
|
|
- if (res.isSuccess) {
|
|
|
- argBuff += mkArg(param.name, res.tree)
|
|
|
- } else {
|
|
|
- mkArg = gen.mkNamedArg // don't pass the default argument (if any) here, but start emitting named arguments for the following args
|
|
|
- if (!param.hasDefault && !paramFailed) {
|
|
|
- context.reporter.reportFirstDivergentError(fun, param, paramTp)(context)
|
|
|
- paramFailed = true
|
|
|
- }
|
|
|
- /* else {
|
|
|
- TODO: alternative (to expose implicit search failure more) -->
|
|
|
- resolve argument, do type inference, keep emitting positional args, infer type params based on default value for arg
|
|
|
- for (ar <- argResultsBuff) ar.subst traverse defaultVal
|
|
|
- val targs = exprTypeArgs(context.undetparams, defaultVal.tpe, paramTp)
|
|
|
- substExpr(tree, tparams, targs, pt)
|
|
|
- }*/
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val args = argBuff.toList
|
|
|
- for (ar <- argResultsBuff) {
|
|
|
- ar.subst traverse fun
|
|
|
- for (arg <- args) ar.subst traverse arg
|
|
|
}
|
|
|
|
|
|
- new ApplyToImplicitArgs(fun, args) setPos fun.pos
|
|
|
- case ErrorType =>
|
|
|
- fun
|
|
|
case x => throw new MatchError(x)
|
|
|
}
|
|
|
|
|
|
- def viewExists(from: Type, to: Type): Boolean = (
|
|
|
- !from.isError
|
|
|
- && !to.isError
|
|
|
- && context.implicitsEnabled
|
|
|
- && (inferView(context.tree, from, to, reportAmbiguous = false) != EmptyTree)
|
|
|
- // scala/bug#8230 / scala/bug#8463 We'd like to change this to `saveErrors = false`, but can't.
|
|
|
- // For now, we can at least pass in `context.tree` rather then `EmptyTree` so as
|
|
|
- // to avoid unpositioned type errors.
|
|
|
- )
|
|
|
-
|
|
|
- // Get rid of any special ProtoTypes, so that implicit search won't have to deal with them
|
|
|
- private def normalizeProtoForView(proto: Type): Type = proto match {
|
|
|
- case proto: OverloadedArgProto => proto.underlying
|
|
|
- case pt => pt
|
|
|
- }
|
|
|
-
|
|
|
- /** Infer an implicit conversion (`view`) between two types.
|
|
|
- * @param tree The tree which needs to be converted.
|
|
|
- * @param from The source type of the conversion
|
|
|
- * @param to The target type of the conversion
|
|
|
- * @param reportAmbiguous Should ambiguous implicit errors be reported?
|
|
|
- * False iff we search for a view to find out
|
|
|
- * whether one type is coercible to another.
|
|
|
- * @param saveErrors Should ambiguous and divergent implicit errors that were buffered
|
|
|
- * during the inference of a view be put into the original buffer.
|
|
|
- * False iff we don't care about them.
|
|
|
- */
|
|
|
- def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree =
|
|
|
- if (isPastTyper || from.isInstanceOf[MethodType] || from.isInstanceOf[OverloadedType] || from.isInstanceOf[PolyType]) EmptyTree
|
|
|
- else {
|
|
|
- debuglog(s"Inferring view from $from to $to for $tree (reportAmbiguous= $reportAmbiguous, saveErrors=$saveErrors)")
|
|
|
-
|
|
|
- val fromNoAnnot = from.withoutAnnotations
|
|
|
- val toNorm = normalizeProtoForView(to)
|
|
|
- val result = inferImplicitView(fromNoAnnot, toNorm, tree, context, reportAmbiguous, saveErrors) match {
|
|
|
- case fail if fail.isFailure => inferImplicitView(byNameType(fromNoAnnot), toNorm, tree, context, reportAmbiguous, saveErrors)
|
|
|
- case ok => ok
|
|
|
- }
|
|
|
-
|
|
|
- if (result.subst != EmptyTreeTypeSubstituter) {
|
|
|
- result.subst traverse tree
|
|
|
- notifyUndetparamsInferred(result.subst.from, result.subst.to)
|
|
|
- }
|
|
|
- result.tree
|
|
|
- }
|
|
|
-
|
|
|
- import infer._
|
|
|
-
|
|
|
- private var namerCache: Namer = null
|
|
|
- def namer: Namer = {
|
|
|
- if ((namerCache eq null) || namerCache.context != context)
|
|
|
- namerCache = newNamer(context)
|
|
|
- namerCache
|
|
|
- }
|
|
|
-
|
|
|
- var context = context0
|
|
|
- def context1 = context
|
|
|
-
|
|
|
- def dropExistential(tp: Type): Type = tp match {
|
|
|
- case ExistentialType(tparams, _) =>
|
|
|
- new SubstWildcardMap(tparams).apply(tp)
|
|
|
- case TypeRef(_, sym, _) if sym.isAliasType =>
|
|
|
- val tp0 = tp.dealias
|
|
|
- if (tp eq tp0) {
|
|
|
- devWarning(s"dropExistential did not progress dealiasing $tp, see scala/bug#7126")
|
|
|
- tp
|
|
|
- } else {
|
|
|
- val tp1 = dropExistential(tp0)
|
|
|
- if (tp1 eq tp0) tp else tp1
|
|
|
- }
|
|
|
- case _ => tp
|
|
|
- }
|
|
|
-
|
|
|
- private def errorNotClass(tpt: Tree, found: Type) = { ClassTypeRequiredError(tpt, found); false }
|
|
|
- private def errorNotStable(tpt: Tree, found: Type) = { TypeNotAStablePrefixError(tpt, found); false }
|
|
|
-
|
|
|
- /** Check that `tpt` refers to a non-refinement class or module type */
|
|
|
- def checkClassOrModuleType(tpt: Tree): Boolean = {
|
|
|
- val tpe = unwrapToClass(tpt.tpe)
|
|
|
- def isModule = tpe match {
|
|
|
- case SingleType(_, sym) => sym.isModule
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- isNonRefinementClassType(tpe) || isModule || errorNotClass(tpt, tpe)
|
|
|
- }
|
|
|
-
|
|
|
- /** Check that `tpt` refers to a class type with a stable prefix. */
|
|
|
- def checkStablePrefixClassType(tpt: Tree): Boolean = {
|
|
|
- val tpe = unwrapToStableClass(tpt.tpe)
|
|
|
- def prefixIsStable = {
|
|
|
- def checkPre = tpe match {
|
|
|
- case TypeRef(pre, _, _) => pre.isStable || errorNotStable(tpt, pre)
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- // A type projection like X#Y can get by the stable check if the
|
|
|
- // prefix is singleton-bounded, so peek at the tree too.
|
|
|
- def checkTree = tpt match {
|
|
|
- case SelectFromTypeTree(qual, _) => isSingleType(qual.tpe) || errorNotClass(tpt, tpe)
|
|
|
- case _ => true
|
|
|
- }
|
|
|
- checkPre && checkTree
|
|
|
- }
|
|
|
-
|
|
|
- ( (isNonRefinementClassType(tpe) || errorNotClass(tpt, tpe))
|
|
|
- && (isPastTyper || prefixIsStable)
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /** Check that type `tp` is not a subtype of itself.
|
|
|
- */
|
|
|
- def checkNonCyclic(pos: Position, tp: Type): Boolean = {
|
|
|
- def checkNotLocked(sym: Symbol) = {
|
|
|
- sym.initialize.lockOK || { CyclicAliasingOrSubtypingError(pos, sym); false }
|
|
|
- }
|
|
|
- tp match {
|
|
|
- case TypeRef(pre, sym, args) =>
|
|
|
- checkNotLocked(sym) &&
|
|
|
- ((!sym.isNonClassType) || checkNonCyclic(pos, appliedType(pre.memberInfo(sym), args), sym))
|
|
|
- // @M! info for a type ref to a type parameter now returns a polytype
|
|
|
- // @M was: checkNonCyclic(pos, pre.memberInfo(sym).subst(sym.typeParams, args), sym)
|
|
|
-
|
|
|
- case SingleType(_, sym) =>
|
|
|
- checkNotLocked(sym)
|
|
|
- case st: SubType =>
|
|
|
- checkNonCyclic(pos, st.supertype)
|
|
|
- case ct: CompoundType =>
|
|
|
- ct.parents forall (x => checkNonCyclic(pos, x))
|
|
|
- case _ =>
|
|
|
- true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def checkNonCyclic(pos: Position, tp: Type, lockedSym: Symbol): Boolean = try {
|
|
|
- if (!lockedSym.lock(CyclicReferenceError(pos, tp, lockedSym))) false
|
|
|
- else checkNonCyclic(pos, tp)
|
|
|
+ def tryCatchFinally() = try {
|
|
|
+ if (true) false
|
|
|
+ else otherMethod(arg)
|
|
|
+ } catch {
|
|
|
+ case ex: CyclicReference => throw ex
|
|
|
+ case _ => return true
|
|
|
} finally {
|
|
|
- lockedSym.unlock()
|
|
|
- }
|
|
|
-
|
|
|
- def checkNonCyclic(sym: Symbol): Unit = {
|
|
|
- if (!checkNonCyclic(sym.pos, sym.tpe_*)) sym.setInfo(ErrorType)
|
|
|
- }
|
|
|
-
|
|
|
- def checkNonCyclic(defn: Tree, tpt: Tree): Unit = {
|
|
|
- if (!checkNonCyclic(defn.pos, tpt.tpe, defn.symbol)) {
|
|
|
- tpt setType ErrorType
|
|
|
- defn.symbol.setInfo(ErrorType)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def reenterValueParams(vparamss: List[List[ValDef]]): Unit = {
|
|
|
- for (vparams <- vparamss)
|
|
|
- for (vparam <- vparams)
|
|
|
- context.scope enter vparam.symbol
|
|
|
- }
|
|
|
-
|
|
|
- def reenterTypeParams(tparams: List[TypeDef]): List[Symbol] =
|
|
|
- for (tparam <- tparams) yield {
|
|
|
- context.scope enter tparam.symbol
|
|
|
- tparam.symbol.deSkolemize
|
|
|
- }
|
|
|
-
|
|
|
- /** The qualifying class
|
|
|
- * of a this or super with prefix `qual`.
|
|
|
- * packageOk is equal false when qualifying class symbol
|
|
|
- */
|
|
|
- def qualifyingClass(tree: Tree, qual: Name, packageOK: Boolean, immediate: Boolean) =
|
|
|
- context.enclClass.owner.ownersIterator.find(o => qual.isEmpty || o.isClass && o.name == qual) match {
|
|
|
- case Some(c) if packageOK || !c.isPackageClass => c
|
|
|
- case _ =>
|
|
|
- QualifyingClassError(tree, qual)
|
|
|
- // Delay `setError` in namer, scala/bug#10748
|
|
|
- if (immediate) setError(tree) else unit.toCheck += (() => setError(tree))
|
|
|
- NoSymbol
|
|
|
- }
|
|
|
-
|
|
|
- /** The typer for an expression, depending on where we are. If we are before a superclass
|
|
|
- * call, this is a typer over a constructor context; otherwise it is the current typer.
|
|
|
- */
|
|
|
- final def constrTyperIf(inConstr: Boolean): Typer =
|
|
|
- if (inConstr) {
|
|
|
- assert(context.undetparams.isEmpty, context.undetparams)
|
|
|
- newTyper(context.makeConstructorContext)
|
|
|
- } else this
|
|
|
-
|
|
|
- @inline
|
|
|
- final def withCondConstrTyper[T](inConstr: Boolean)(f: Typer => T): T =
|
|
|
- if (inConstr) {
|
|
|
- assert(context.undetparams.isEmpty, context.undetparams)
|
|
|
- val c = context.makeConstructorContext
|
|
|
- typerWithLocalContext(c)(f)
|
|
|
- } else {
|
|
|
- f(this)
|
|
|
- }
|
|
|
-
|
|
|
- @inline
|
|
|
- final def typerWithCondLocalContext[T](c: => Context)(cond: Boolean)(f: Typer => T): T =
|
|
|
- if (cond) typerWithLocalContext(c)(f) else f(this)
|
|
|
-
|
|
|
- @inline
|
|
|
- final def typerWithLocalContext[T](c: Context)(f: Typer => T): T = {
|
|
|
- try f(newTyper(c))
|
|
|
- finally c.reporter.propagateErrorsTo(context.reporter)
|
|
|
- }
|
|
|
-
|
|
|
- /** The typer for a label definition. If this is part of a template we
|
|
|
- * first have to enter the label definition.
|
|
|
- */
|
|
|
- def labelTyper(ldef: LabelDef): Typer =
|
|
|
- if (ldef.symbol == NoSymbol) { // labeldef is part of template
|
|
|
- val typer1 = newTyper(context.makeNewScope(ldef, context.owner))
|
|
|
- typer1.enterLabelDef(ldef)
|
|
|
- typer1
|
|
|
- } else this
|
|
|
-
|
|
|
- /** Is symbol defined and not stale?
|
|
|
- */
|
|
|
- def reallyExists(sym: Symbol) = {
|
|
|
- if (isStale(sym)) sym.setInfo(NoType)
|
|
|
- sym.exists
|
|
|
- }
|
|
|
-
|
|
|
- /** A symbol is stale if it is toplevel, to be loaded from a classfile, and
|
|
|
- * the classfile is produced from a sourcefile which is compiled in the current run.
|
|
|
- */
|
|
|
- def isStale(sym: Symbol): Boolean = {
|
|
|
- sym.rawInfo.isInstanceOf[loaders.ClassfileLoader] && {
|
|
|
- sym.rawInfo.load(sym)
|
|
|
- (sym.sourceFile ne null) &&
|
|
|
- (currentRun.compiledFiles contains sym.sourceFile.path)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Does the context of tree `tree` require a stable type?
|
|
|
- */
|
|
|
- private def isStableContext(tree: Tree, mode: Mode, pt: Type) = {
|
|
|
- def ptSym = pt.typeSymbol
|
|
|
- def expectsStable = (
|
|
|
- pt.isStable
|
|
|
- || mode.inQualMode && !tree.symbol.isConstant
|
|
|
- || !(tree.tpe <:< pt) && (ptSym.isAbstractType && pt.lowerBound.isStable || ptSym.isRefinementClass)
|
|
|
- )
|
|
|
-
|
|
|
- ( isNarrowable(tree.tpe)
|
|
|
- && mode.typingExprNotLhs
|
|
|
- && expectsStable
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /** Make symbol accessible. This means:
|
|
|
- * If symbol refers to package object, insert `.package` as second to last selector.
|
|
|
- * (exception for some symbols in scala package which are dealiased immediately)
|
|
|
- * Call checkAccessible, which sets tree's attributes.
|
|
|
- * Also note that checkAccessible looks up sym on pre without checking that pre is well-formed
|
|
|
- * (illegal type applications in pre will be skipped -- that's why typedSelect wraps the resulting tree in a TreeWithDeferredChecks)
|
|
|
- * @return modified tree and new prefix type
|
|
|
- */
|
|
|
- private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Any /*Type | (Tree, Type)*/ =
|
|
|
- if (!unit.isJava && context.isInPackageObject(sym, pre.typeSymbol)) {
|
|
|
- val qual = typedQualifier { atPos(tree.pos.makeTransparent) {
|
|
|
- tree match {
|
|
|
- case Ident(_) =>
|
|
|
- val packageObject =
|
|
|
- if (!sym.isOverloaded && sym.owner.isModuleClass) sym.owner.sourceModule // historical optimization, perhaps no longer needed
|
|
|
- else pre.typeSymbol.packageObject
|
|
|
- Ident(packageObject)
|
|
|
- case Select(qual, _) => Select(qual, nme.PACKAGEkw)
|
|
|
- case SelectFromTypeTree(qual, _) => Select(qual, nme.PACKAGEkw)
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- }}
|
|
|
- val tree1 = atPos(tree.pos) {
|
|
|
- tree match {
|
|
|
- case Ident(name) => Select(qual, name)
|
|
|
- case Select(_, name) => Select(qual, name)
|
|
|
- case SelectFromTypeTree(_, name) => SelectFromTypeTree(qual, name)
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- }
|
|
|
- (checkAccessible(tree1, sym, qual.tpe, qual, unit.isJava), qual.tpe)
|
|
|
- } else {
|
|
|
- checkAccessible(tree, sym, pre, site, unit.isJava)
|
|
|
- }
|
|
|
-
|
|
|
- /** Post-process an identifier or selection node, performing the following:
|
|
|
- * 1. Check that non-function pattern expressions are stable (ignoring volatility concerns -- scala/bug#6815)
|
|
|
- * (and narrow the type of modules: a module reference in a pattern has type Foo.type, not "object Foo")
|
|
|
- * 2. Check that packages and static modules are not used as values
|
|
|
- * 3. Turn tree type into stable type if possible and required by context.
|
|
|
- * 4. Give getClass calls a more precise type based on the type of the target of the call.
|
|
|
- */
|
|
|
- protected def stabilize(tree: Tree, pre: Type, mode: Mode, pt: Type): Tree = {
|
|
|
-
|
|
|
- // Side effect time! Don't be an idiot like me and think you
|
|
|
- // can move "val sym = tree.symbol" before this line, because
|
|
|
- // inferExprAlternative side-effects the tree's symbol.
|
|
|
- if (tree.symbol.isOverloaded && !mode.inFunMode)
|
|
|
- inferExprAlternative(tree, pt)
|
|
|
-
|
|
|
- val sym = tree.symbol
|
|
|
- val isStableIdPattern = mode.typingPatternNotConstructor && tree.isTerm
|
|
|
-
|
|
|
- def isModuleTypedExpr = (
|
|
|
- treeInfo.admitsTypeSelection(tree)
|
|
|
- && (isStableContext(tree, mode, pt) || sym.isModuleNotMethod)
|
|
|
- )
|
|
|
- def isStableValueRequired = (
|
|
|
- isStableIdPattern
|
|
|
- || mode.in(all = EXPRmode, none = QUALmode) && !phase.erasedTypes
|
|
|
- )
|
|
|
- // To fully benefit from special casing the return type of
|
|
|
- // getClass, we have to catch it immediately so expressions like
|
|
|
- // x.getClass().newInstance() are typed with the type of x. TODO: If the
|
|
|
- // type of the qualifier is inaccessible, we can cause private types to
|
|
|
- // escape scope here, e.g. pos/t1107. I'm not sure how to properly handle
|
|
|
- // this so for now it requires the type symbol be public.
|
|
|
- def isGetClassCall = isGetClass(sym) && pre.typeSymbol.isPublic
|
|
|
-
|
|
|
- def narrowIf(tree: Tree, condition: Boolean) =
|
|
|
- if (condition) tree setType singleType(pre, sym) else tree
|
|
|
-
|
|
|
- def checkStable(tree: Tree): Tree =
|
|
|
- if (treeInfo.isStableIdentifierPattern(tree)) tree
|
|
|
- else UnstableTreeError(tree)
|
|
|
-
|
|
|
- if (tree.isErrorTyped)
|
|
|
- tree
|
|
|
- else if (!sym.isValue && isStableValueRequired) // (2)
|
|
|
- NotAValueError(tree, sym)
|
|
|
- else if (isStableIdPattern) // (1)
|
|
|
- // A module reference in a pattern has type Foo.type, not "object Foo"
|
|
|
- narrowIf(checkStable(tree), sym.isModuleNotMethod)
|
|
|
- else if (isModuleTypedExpr) // (3)
|
|
|
- narrowIf(tree, true)
|
|
|
- else if (isGetClassCall) // (4)
|
|
|
- tree setType MethodType(Nil, getClassReturnType(pre))
|
|
|
- else
|
|
|
- tree
|
|
|
- }
|
|
|
-
|
|
|
- private def isNarrowable(tpe: Type): Boolean = unwrapWrapperTypes(tpe) match {
|
|
|
- case TypeRef(_, _, _) | RefinedType(_, _) => true
|
|
|
- case _ => !phase.erasedTypes
|
|
|
- }
|
|
|
-
|
|
|
- def stabilizeFun(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- val sym = tree.symbol
|
|
|
- val pre = tree match {
|
|
|
- case Select(qual, _) => qual.tpe
|
|
|
- case _ => NoPrefix
|
|
|
- }
|
|
|
- def stabilizable = (
|
|
|
- pre.isStable
|
|
|
- && sym.tpe.params.isEmpty
|
|
|
- && (isStableContext(tree, mode, pt) || sym.isModule)
|
|
|
- )
|
|
|
- tree.tpe match {
|
|
|
- case MethodType(_, _) if stabilizable => tree setType MethodType(Nil, singleType(pre, sym)) // TODO: should this be a NullaryMethodType?
|
|
|
- case _ => tree
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @deprecated("Use the overload accepting a Type.", "2.12.9")
|
|
|
- def member(qual: Tree, name: Name): Symbol = member(qual.tpe, name)
|
|
|
- /** The member with given name of given qualifier type */
|
|
|
- def member(qual: Type, name: Name): Symbol = {
|
|
|
- def callSiteWithinClass(clazz: Symbol) = context.enclClass.owner hasTransOwner clazz
|
|
|
- val includeLocals = qual match {
|
|
|
- case ThisType(clazz) if callSiteWithinClass(clazz) => true
|
|
|
- case SuperType(clazz, _) if callSiteWithinClass(clazz.typeSymbol) => true
|
|
|
- case _ => phase.next.erasedTypes
|
|
|
- }
|
|
|
- if (includeLocals) qual member name
|
|
|
- else qual nonLocalMember name
|
|
|
- }
|
|
|
-
|
|
|
- def silent[T](op: Typer => T,
|
|
|
- reportAmbiguousErrors: Boolean = context.ambiguousErrors,
|
|
|
- newtree: Tree = context.tree): SilentResult[T] = {
|
|
|
- val findMemberStart = if (settings.areStatisticsEnabled) statistics.startCounter(findMemberFailed) else null
|
|
|
- val subtypeStart = if (settings.areStatisticsEnabled) statistics.startCounter(subtypeFailed) else null
|
|
|
- val failedSilentStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedSilentNanos) else null
|
|
|
- def stopStats() = {
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopCounter(findMemberFailed, findMemberStart)
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopCounter(subtypeFailed, subtypeStart)
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopTimer(failedSilentNanos, failedSilentStart)
|
|
|
- }
|
|
|
- @inline def wrapResult(reporter: ContextReporter, result: T) =
|
|
|
- if (reporter.hasErrors) {
|
|
|
- stopStats()
|
|
|
- SilentTypeError(reporter.errors.toList, reporter.warnings.toList)
|
|
|
- } else SilentResultValue(result)
|
|
|
-
|
|
|
- try {
|
|
|
- if (context.reportErrors ||
|
|
|
- reportAmbiguousErrors != context.ambiguousErrors ||
|
|
|
- newtree != context.tree) {
|
|
|
- val context1 = context.makeSilent(reportAmbiguousErrors, newtree)
|
|
|
- context1.undetparams = context.undetparams
|
|
|
- context1.savedTypeBounds = context.savedTypeBounds
|
|
|
- context1.pendingStabilizers = context.pendingStabilizers
|
|
|
- val typer1 = newTyper(context1)
|
|
|
- val result = op(typer1)
|
|
|
- context.undetparams = context1.undetparams
|
|
|
- context.savedTypeBounds = context1.savedTypeBounds
|
|
|
- context.pendingStabilizers = context1.pendingStabilizers
|
|
|
-
|
|
|
- // If we have a successful result, emit any warnings it created.
|
|
|
- if (!context1.reporter.hasErrors)
|
|
|
- context1.reporter.emitWarnings()
|
|
|
-
|
|
|
- wrapResult(context1.reporter, result)
|
|
|
- } else {
|
|
|
- assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer")
|
|
|
-
|
|
|
- context.reporter.withFreshErrorBuffer {
|
|
|
- wrapResult(context.reporter, op(this))
|
|
|
- }
|
|
|
- }
|
|
|
- } catch {
|
|
|
- case ex: CyclicReference => throw ex
|
|
|
- case ex: TypeError =>
|
|
|
- // fallback in case TypeError is still thrown
|
|
|
- // @H this happens for example in cps annotation checker
|
|
|
- stopStats()
|
|
|
- SilentTypeError(TypeErrorWrapper(ex))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Check whether feature given by `featureTrait` is enabled.
|
|
|
- * If it is not, issue an error or a warning depending on whether the feature is required.
|
|
|
- * @param construct A string expression that is substituted for "#" in the feature description string
|
|
|
- * @param immediate When set, feature check is run immediately, otherwise it is run
|
|
|
- * at the end of the typechecking run for the enclosing unit. This
|
|
|
- * is done to avoid potential cyclic reference errors by implicits
|
|
|
- * that are forced too early.
|
|
|
- * @return if feature check is run immediately: true if feature is enabled, false otherwise
|
|
|
- * if feature check is delayed or suppressed because we are past typer: true
|
|
|
- */
|
|
|
- def checkFeature(pos: Position, featureTrait: Symbol, construct: => String = "", immediate: Boolean = false): Boolean =
|
|
|
- isPastTyper || {
|
|
|
- val nestedOwners =
|
|
|
- featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse
|
|
|
- val featureName = nestedOwners.map(s => s"${s.name}.").mkString + featureTrait.name
|
|
|
- def action(): Boolean = {
|
|
|
- def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess
|
|
|
- def hasOption = settings.language.contains(featureName)
|
|
|
- hasOption || hasImport || {
|
|
|
- val Some(AnnotationInfo(_, List(Literal(Constant(featureDesc: String)), Literal(Constant(required: Boolean))), _)) =
|
|
|
- featureTrait.getAnnotation(LanguageFeatureAnnot): @unchecked
|
|
|
- context.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required)
|
|
|
- false
|
|
|
- }
|
|
|
- }
|
|
|
- if (immediate) {
|
|
|
- action()
|
|
|
- } else {
|
|
|
- unit.toCheck += (() => action())
|
|
|
- true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def checkExistentialsFeature(pos: Position, tpe: Type, prefix: String) = tpe match {
|
|
|
- case extp: ExistentialType if !extp.isRepresentableWithWildcards && !tpe.isError =>
|
|
|
- checkFeature(pos, currentRun.runDefinitions.ExistentialsFeature, prefix+" "+tpe)
|
|
|
- case _ =>
|
|
|
+ cleanUp()
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- /** Perform the following adaptations of expression, pattern or type `tree` wrt to
|
|
|
- * given mode `mode` and given prototype `pt`:
|
|
|
- * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do
|
|
|
- * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode)
|
|
|
- * (1) Resolve overloading, unless mode contains FUNmode
|
|
|
- * (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 (see adaptToImplicitMethod)
|
|
|
- * (4.2) otherwise, if the method is 0-ary and it can be auto-applied (see checkCanAutoApply), apply it to ()
|
|
|
- * (4.3) otherwise, if the method is not a constructor, and can be eta-expanded (see checkCanEtaExpand), eta-expand
|
|
|
- * 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, do SAM conversion
|
|
|
- * (15) When in mode EXPRmode, apply a view
|
|
|
- * If all this fails, error
|
|
|
- *
|
|
|
- * Note: the `original` tree parameter is for re-typing implicit method invocations (see below)
|
|
|
- * and should not be used otherwise. TODO: can it be replaced with a tree attachment?
|
|
|
- */
|
|
|
- protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = {
|
|
|
- def hasUndets = !context.undetparams.isEmpty
|
|
|
- def hasUndetsInMonoMode = hasUndets && !mode.inPolyMode
|
|
|
-
|
|
|
- def adaptToImplicitMethod(mt: MethodType): Tree = {
|
|
|
- if (hasUndets) { // (9) -- should revisit dropped condition `hasUndetsInMonoMode`
|
|
|
- // dropped so that type args of implicit method are inferred even if polymorphic expressions are allowed
|
|
|
- // needed for implicits in 2.8 collection library -- maybe once #3346 is fixed, we can reinstate the condition?
|
|
|
- context.undetparams = inferExprInstance(tree, context.extractUndetparams(), pt,
|
|
|
- // approximate types that depend on arguments since dependency on implicit argument is like dependency on type parameter
|
|
|
- mt.approximate,
|
|
|
- keepNothings = false,
|
|
|
- useWeaklyCompatible = true) // #3808
|
|
|
- }
|
|
|
-
|
|
|
- // avoid throwing spurious DivergentImplicit errors
|
|
|
- if (context.reporter.hasErrors)
|
|
|
- setError(tree)
|
|
|
- else
|
|
|
- withCondConstrTyper(treeInfo.isSelfOrSuperConstrCall(tree))(typer1 =>
|
|
|
- if (original != EmptyTree && !pt.isWildcard) {
|
|
|
- typer1 silent { tpr =>
|
|
|
- val withImplicitArgs = tpr.applyImplicitArgs(tree)
|
|
|
- if (tpr.context.reporter.hasErrors) tree // silent will wrap it in SilentTypeError anyway
|
|
|
- else tpr.typed(withImplicitArgs, mode, pt)
|
|
|
- } orElse { originalErrors =>
|
|
|
- // Re-try typing (applying to implicit args) without expected type. Add in 53d98e7d42 to
|
|
|
- // for better error message (scala/bug#2180, https://www.scala-lang.org/old/node/3453.html)
|
|
|
- val resetTree = resetAttrs(original)
|
|
|
- resetTree match {
|
|
|
- case treeInfo.Applied(fun, _, _) =>
|
|
|
- if (fun.symbol != null && fun.symbol.isError)
|
|
|
- // scala/bug#9041 Without this, we leak error symbols past the typer!
|
|
|
- // because the fallback typechecking notices the error-symbol,
|
|
|
- // refuses to re-attempt typechecking, and presumes that someone
|
|
|
- // else was responsible for issuing the related type error!
|
|
|
- fun.setSymbol(NoSymbol)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- debuglog(s"fallback on implicits: $tree/$resetTree")
|
|
|
- // scala/bug#10066 Need to patch the enclosing tree in the context to make translation of Dynamic
|
|
|
- // work during fallback typechecking below.
|
|
|
- val resetContext: Context = {
|
|
|
- object substResetForOriginal extends AstTransformer {
|
|
|
- override def transform(tree: Tree): Tree = {
|
|
|
- if (tree eq original) resetTree
|
|
|
- else super.transform(tree)
|
|
|
- }
|
|
|
- }
|
|
|
- context.make(substResetForOriginal.transform(context.tree))
|
|
|
- }
|
|
|
- typerWithLocalContext(resetContext) { typer1 =>
|
|
|
- typer1.silent { typer1 =>
|
|
|
- val tree1 = typer1.typed(resetTree, mode)
|
|
|
- // Q: `typed` already calls `pluginsTyped` and `adapt`. the only difference here is that
|
|
|
- // we pass `EmptyTree` as the `original`. intended? added in 2009 (53d98e7d42) by martin.
|
|
|
- tree1 setType pluginsTyped(tree1.tpe, typer1, tree1, mode, pt)
|
|
|
- if (tree1.isEmpty) tree1 else typer1.adapt(tree1, mode, pt, original = EmptyTree)
|
|
|
- } orElse { _ =>
|
|
|
- originalErrors.foreach(context.issue)
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- typer1.typed(typer1.applyImplicitArgs(tree), mode, pt)
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- def adaptMethodTypeToExpr(mt: MethodType): Tree = {
|
|
|
- val meth =
|
|
|
- tree match {
|
|
|
- // a partial named application is a block (see comment in EtaExpansion)
|
|
|
- // How about user-written blocks? Can they ever have a MethodType?
|
|
|
- case Block(_, tree1) => tree1.symbol
|
|
|
- case _ => tree.symbol
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- val arity = mt.params.length
|
|
|
-
|
|
|
- def warnTree = original orElse tree
|
|
|
-
|
|
|
- def warnEtaZero(): Boolean = {
|
|
|
- if (!settings.warnEtaZero) return true
|
|
|
- context.warning(tree.pos,
|
|
|
- s"""An unapplied 0-arity method was eta-expanded (due to the expected type $pt), rather than applied to `()`.
|
|
|
- |Write ${Apply(warnTree, Nil)} to invoke method ${meth.decodedName}, or change the expected type.""".stripMargin,
|
|
|
- WarningCategory.LintEtaZero)
|
|
|
- true
|
|
|
- }
|
|
|
-
|
|
|
- def warnEtaSam(): Boolean = {
|
|
|
- if (!settings.warnEtaSam) return true
|
|
|
- val sam = samOf(pt)
|
|
|
- val samClazz = sam.owner
|
|
|
- // TODO: we allow a Java class as a SAM type, whereas Java only allows the @FunctionalInterface on interfaces -- align?
|
|
|
- if (sam.exists && (!samClazz.hasFlag(JAVA) || samClazz.hasFlag(INTERFACE)) && !samClazz.hasAnnotation(definitions.FunctionalInterfaceClass))
|
|
|
- context.warning(tree.pos,
|
|
|
- s"""Eta-expansion performed to meet expected type $pt, which is SAM-equivalent to ${samToFunctionType(pt)},
|
|
|
- |even though $samClazz is not annotated with `@FunctionalInterface`;
|
|
|
- |to suppress warning, add the annotation or write out the equivalent function literal.""".stripMargin,
|
|
|
- WarningCategory.LintEtaSam)
|
|
|
- true
|
|
|
- }
|
|
|
-
|
|
|
- // note that isFunctionProto(pt) does not work properly for Function0
|
|
|
- lazy val ptUnderlying =
|
|
|
- (pt match {
|
|
|
- case oapt: OverloadedArgProto => oapt.underlying
|
|
|
- case pt => pt
|
|
|
- }).dealiasWiden
|
|
|
-
|
|
|
- // (4.3) condition for eta-expansion by arity & -Xsource level
|
|
|
- //
|
|
|
- // for arity == 0:
|
|
|
- // - if Function0 is expected -- SAM types do not eta-expand because it could be an accidental SAM scala/bug#9489
|
|
|
- // for arity > 0:
|
|
|
- // - 2.13: if function or sam type is expected
|
|
|
- // - 3.0: unconditionally
|
|
|
- //
|
|
|
- // warnings:
|
|
|
- // - for arity == 0: eta-expansion of zero-arg methods was deprecated (scala/bug#7187)
|
|
|
- // - for arity > 0: expected type is a SAM that is not annotated with `@FunctionalInterface`
|
|
|
- def checkCanEtaExpand(): Boolean = {
|
|
|
- def expectingSamOfArity = {
|
|
|
- val sam = samOf(ptUnderlying)
|
|
|
- sam.exists && sam.info.params.lengthIs == arity
|
|
|
- }
|
|
|
-
|
|
|
- val expectingFunctionOfArity = {
|
|
|
- val ptSym = ptUnderlying.typeSymbolDirect
|
|
|
- (ptSym eq FunctionClass(arity)) || (arity > 0 && (ptSym eq FunctionClass(1))) // allowing for tupling conversion
|
|
|
- }
|
|
|
-
|
|
|
- if (arity == 0)
|
|
|
- expectingFunctionOfArity && warnEtaZero()
|
|
|
- else
|
|
|
- expectingFunctionOfArity || expectingSamOfArity && warnEtaSam() || currentRun.isScala3
|
|
|
- }
|
|
|
-
|
|
|
- def matchNullaryLoosely: Boolean = {
|
|
|
- def test(sym: Symbol) = sym.isJavaDefined || sym.owner == AnyClass
|
|
|
- test(meth) || meth.overrides.exists(test)
|
|
|
- }
|
|
|
-
|
|
|
- // (4.2) condition for auto-application
|
|
|
- //
|
|
|
- // Currently the condition is more involved to give slack to Scala methods overriding Java-defined ones;
|
|
|
- // I (moors) think we should resolve that by introducing slack in overriding e.g. a Java-defined `def toString()` by a Scala-defined `def toString`.
|
|
|
- // This also works better for dealing with accessors overriding Java-defined methods. The current strategy in methodSig is problematic:
|
|
|
- // > // Add a () parameter section if this overrides some method with () parameters
|
|
|
- // > val vparamSymssOrEmptyParamsFromOverride =
|
|
|
- // This means an accessor that overrides a Java-defined method gets a MethodType instead of a NullaryMethodType, which breaks lots of assumptions about accessors)
|
|
|
- def checkCanAutoApply(): Boolean = {
|
|
|
- if (!isPastTyper && !matchNullaryLoosely) {
|
|
|
- context.deprecationWarning(tree.pos, NoSymbol, s"Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method ${meth.decodedName},\n" +
|
|
|
- s"or remove the empty argument list from its definition (Java-defined methods are exempt).\n"+
|
|
|
- s"In Scala 3, an unapplied method like this will be eta-expanded into a function.", "2.13.3")
|
|
|
- }
|
|
|
- true
|
|
|
- }
|
|
|
-
|
|
|
- if (!meth.isConstructor && checkCanEtaExpand()) typedEtaExpansion(tree, mode, pt)
|
|
|
- else if (arity == 0 && checkCanAutoApply()) {
|
|
|
- val apply = Apply(tree, Nil).setPos(tree.pos).updateAttachment(AutoApplicationAttachment)
|
|
|
- if (tree.hasAttachment[PostfixAttachment.type]) apply.updateAttachment(InfixAttachment)
|
|
|
- adapt(typed(apply), mode, pt, original)
|
|
|
- } else
|
|
|
- if (context.implicitsEnabled) MissingArgsForMethodTpeError(tree, meth) // `context.implicitsEnabled` implies we are not in a pattern
|
|
|
- else UnstableTreeError(tree)
|
|
|
- }
|
|
|
-
|
|
|
- def adaptType(): Tree = {
|
|
|
- // @M When not typing a type constructor (!context.inTypeConstructorAllowed)
|
|
|
- // or raw type, types must be of kind *,
|
|
|
- // and thus parameterized types must be applied to their type arguments
|
|
|
- // @M TODO: why do kind-* tree's have symbols, while higher-kinded ones don't?
|
|
|
- def properTypeRequired = (
|
|
|
- tree.hasSymbolField
|
|
|
- && !context.inTypeConstructorAllowed
|
|
|
- && !context.unit.isJava
|
|
|
- )
|
|
|
- // @M: don't check tree.tpe.symbol.typeParams. check tree.tpe.typeParams!!!
|
|
|
- // (e.g., m[Int] --> tree.tpe.symbol.typeParams.length == 1, tree.tpe.typeParams.length == 0!)
|
|
|
- // @M: removed check for tree.hasSymbolField and replace tree.symbol by tree.tpe.symbol
|
|
|
- // (TypeTree's must also be checked here, and they don't directly have a symbol)
|
|
|
- def kindArityMismatch = (
|
|
|
- context.inTypeConstructorAllowed
|
|
|
- && !sameLength(tree.tpe.typeParams, pt.typeParams)
|
|
|
- )
|
|
|
- // Note that we treat Any and Nothing as kind-polymorphic.
|
|
|
- // We can't perform this check when typing type arguments to an overloaded method before the overload is resolved
|
|
|
- // (or in the case of an error type) -- this is indicated by pt == WildcardType (see case TypeApply in typed1).
|
|
|
- def kindArityMismatchOk = tree.tpe.typeSymbol match {
|
|
|
- case NothingClass | AnyClass => true
|
|
|
- case _ => pt == WildcardType
|
|
|
- }
|
|
|
-
|
|
|
- // todo. It would make sense when mode.inFunMode to instead use
|
|
|
- // tree setType tree.tpe.normalize
|
|
|
- // when typechecking, say, TypeApply(Ident(`some abstract type symbol`), List(...))
|
|
|
- // because otherwise Ident will have its tpe set to a TypeRef, not to a PolyType, and `typedTypeApply` will fail
|
|
|
- // but this needs additional investigation, because it crashes t5228, gadts1 and maybe something else
|
|
|
- if (mode.inFunMode)
|
|
|
- tree
|
|
|
- else if (properTypeRequired && tree.symbol.typeParams.nonEmpty) // (7)
|
|
|
- MissingTypeParametersError(tree)
|
|
|
- else if (kindArityMismatch && !kindArityMismatchOk) // (7.1) @M: check kind-arity
|
|
|
- KindArityMismatchError(tree, pt)
|
|
|
- else tree match { // (6)
|
|
|
- case TypeTree() => tree
|
|
|
- case _ => TypeTree(tree.tpe) setOriginal tree
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def insertApply(): Tree = {
|
|
|
- assert(!context.inTypeConstructorAllowed, mode) //@M
|
|
|
- val adapted = adaptToName(unmarkDynamicRewrite(tree), nme.apply)
|
|
|
- val qual = gen.stabilize(adapted)
|
|
|
- val t = atPos(tree.pos)(Select(qual setPos tree.pos.makeTransparent, nme.apply))
|
|
|
- wrapErrors(t, _.typed(t, mode, pt))
|
|
|
- }
|
|
|
- def adaptConstant(value: Constant): Tree = {
|
|
|
- val sym = tree.symbol
|
|
|
- if (sym != null && sym.isDeprecated)
|
|
|
- context.deprecationWarning(tree.pos, sym)
|
|
|
- tree match {
|
|
|
- case Literal(`value`) => tree
|
|
|
- case _ =>
|
|
|
- // If the original tree is not a literal, make it available to plugins in an attachment
|
|
|
- treeCopy.Literal(tree, value).updateAttachment(OriginalTreeAttachment(tree))
|
|
|
- }
|
|
|
+ def yieldingMethodWithGuard(values: List[Int]): List[Int] =
|
|
|
+ for (value <- values if value > 0) yield {
|
|
|
+ value
|
|
|
}
|
|
|
|
|
|
- // Ignore type errors raised in later phases that are due to mismatching types with existential skolems
|
|
|
- // We have lift crashing in 2.9 with an adapt failure in the pattern matcher.
|
|
|
- // Here's my hypothesis why this happens. The pattern matcher defines a variable of type
|
|
|
- //
|
|
|
- // val x: T = expr
|
|
|
- //
|
|
|
- // where T is the type of expr, but T contains existential skolems ts.
|
|
|
- // In that case, this value definition does not typecheck.
|
|
|
- // The value definition
|
|
|
- //
|
|
|
- // val x: T forSome { ts } = expr
|
|
|
- //
|
|
|
- // would typecheck. Or one can simply leave out the type of the `val`:
|
|
|
- //
|
|
|
- // val x = expr
|
|
|
- //
|
|
|
- // scala/bug#6029 shows another case where we also fail (in uncurry), but this time the expected
|
|
|
- // type is an existential type.
|
|
|
- //
|
|
|
- // The reason for both failures have to do with the way we (don't) transform
|
|
|
- // skolem types along with the trees that contain them. We'd need a
|
|
|
- // radically different approach to do it. But before investing a lot of time to
|
|
|
- // to do this (I have already sunk 3 full days with in the end futile attempts
|
|
|
- // to consistently transform skolems and fix 6029), I'd like to
|
|
|
- // investigate ways to avoid skolems completely.
|
|
|
- //
|
|
|
- // upd. The same problem happens when we try to typecheck the result of macro expansion against its expected type
|
|
|
- // (which is the return type of the macro definition instantiated in the context of expandee):
|
|
|
- //
|
|
|
- // Test.scala:2: error: type mismatch;
|
|
|
- // found : $u.Expr[Class[_ <: Object]]
|
|
|
- // required: reflect.runtime.universe.Expr[Class[?0(in value <local Test>)]] where type ?0(in value <local Test>) <: Object
|
|
|
- // scala.reflect.runtime.universe.reify(new Object().getClass)
|
|
|
- // ^
|
|
|
- // Therefore following Martin's advice I use this logic to recover from skolem errors after macro expansions
|
|
|
- // (by adding the ` || tree.attachments.get[MacroExpansionAttachment].isDefined` clause to the conditional above).
|
|
|
- //
|
|
|
- def adaptMismatchedSkolems() = {
|
|
|
- def canIgnoreMismatch = (
|
|
|
- !context.reportErrors && isPastTyper
|
|
|
- || tree.hasAttachment[MacroExpansionAttachment]
|
|
|
+ def validateParentClasses(parents: List[Tree], selfType: Type, clazzIsTrait: Boolean): Unit = {
|
|
|
+ (parent.tpe :: ps).collectFirst {
|
|
|
+ case p if hasTraitParams(p.typeSymbol) =>
|
|
|
+ p.typeSymbol.attachments.get[DottyParameterisedTrait].foreach(attach =>
|
|
|
+ pending += ParentIsScala3TraitError(parent, p.typeSymbol, attach.params, psym)
|
|
|
)
|
|
|
- def bound = pt match {
|
|
|
- case ExistentialType(qs, _) => qs
|
|
|
- case _ => Nil
|
|
|
- }
|
|
|
- def msg = sm"""
|
|
|
- |Recovering from existential or skolem type error in
|
|
|
- | $tree
|
|
|
- |with type: ${tree.tpe}
|
|
|
- | pt: $pt
|
|
|
- | context: ${context.tree}
|
|
|
- | adapted
|
|
|
- """.trim
|
|
|
-
|
|
|
- val boundOrSkolems = if (canIgnoreMismatch) bound ++ pt.skolemsExceptMethodTypeParams else Nil
|
|
|
- boundOrSkolems match {
|
|
|
- case Nil => AdaptTypeError(tree, tree.tpe, pt)
|
|
|
- case _ => logResult(msg)(adapt(tree, mode, deriveTypeWithWildcards(boundOrSkolems)(pt), original = EmptyTree))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def adaptExprNotFunMode(): Tree = {
|
|
|
- def lastTry(err: AbsTypeError = null): Tree = {
|
|
|
- debuglog("error tree = " + tree)
|
|
|
- if (settings.isDebug && settings.explaintypes.value) explainTypes(tree.tpe, pt)
|
|
|
- if (err ne null) context.issue(err)
|
|
|
- if (tree.tpe.isErroneous || pt.isErroneous) setError(tree)
|
|
|
- else adaptMismatchedSkolems()
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: should we even get to fallbackAfterVanillaAdapt for an ill-typed tree?
|
|
|
- if (mode.typingExprNotFun && !tree.tpe.isErroneous) {
|
|
|
- @inline def tpdPos(transformed: Tree) = typedPos(tree.pos, mode, pt)(transformed)
|
|
|
- @inline def tpd(transformed: Tree) = typed(transformed, mode, pt)
|
|
|
-
|
|
|
- @inline def warnValueDiscard(): Unit = if (!isPastTyper && settings.warnValueDiscard.value) {
|
|
|
- def isThisTypeResult = (tree, tree.tpe) match {
|
|
|
- case (Apply(Select(receiver, _), _), SingleType(_, sym)) => sym == receiver.symbol
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- if (!isThisTypeResult && !explicitlyUnit(tree)) context.warning(tree.pos, "discarded non-Unit value", WarningCategory.WFlagValueDiscard)
|
|
|
- }
|
|
|
- @inline def warnNumericWiden(tpSym: Symbol, ptSym: Symbol): Unit =
|
|
|
- if (!isPastTyper) {
|
|
|
- val isInharmonic = (
|
|
|
- (tpSym == IntClass && ptSym == FloatClass)
|
|
|
- || (tpSym == LongClass && (ptSym == FloatClass || ptSym == DoubleClass))
|
|
|
- )
|
|
|
- if (isInharmonic)
|
|
|
- // not `context.deprecationWarning` because they are not buffered in silent mode
|
|
|
- context.warning(tree.pos, s"Widening conversion from ${tpSym.name} to ${ptSym.name} is deprecated because it loses precision. Write `.to${ptSym.name}` instead.", WarningCategory.Deprecation)
|
|
|
- else if (settings.warnNumericWiden.value) context.warning(tree.pos, "implicit numeric widening", WarningCategory.WFlagNumericWiden)
|
|
|
- }
|
|
|
-
|
|
|
- // The <: Any requirement inhibits attempts to adapt continuation types to non-continuation types.
|
|
|
- val anyTyped = tree.tpe <:< AnyTpe
|
|
|
-
|
|
|
- pt.dealias match {
|
|
|
- case TypeRef(_, UnitClass, _) if anyTyped => // (12)
|
|
|
- warnValueDiscard() ; tpdPos(gen.mkUnitBlock(tree))
|
|
|
- case TypeRef(_, numValueCls, _) if anyTyped && isNumericValueClass(numValueCls) && isNumericSubType(tree.tpe, pt) => // (10) (11)
|
|
|
- warnNumericWiden(tree.tpe.widen.typeSymbol, numValueCls) ; tpdPos(Select(tree, s"to${numValueCls.name}"))
|
|
|
- case dealiased if dealiased.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt) => // (13)
|
|
|
- tpd(adaptAnnotations(tree, this, mode, pt))
|
|
|
- case _ =>
|
|
|
- if (hasUndets) instantiate(tree, mode, pt)
|
|
|
- else {
|
|
|
- // (14) sam conversion
|
|
|
- // TODO: figure out how to avoid partially duplicating typedFunction (samMatchingFunction)
|
|
|
- // Could we infer the SAM type, assign it to the tree and add the attachment,
|
|
|
- // all in one fell swoop at the end of typedFunction?
|
|
|
- val didInferSamType = inferSamType(tree, pt, mode)
|
|
|
-
|
|
|
- if (didInferSamType) tree
|
|
|
- else { // (15) implicit view application
|
|
|
- val coercion =
|
|
|
- if (context.implicitsEnabled) inferView(tree, tree.tpe, pt)
|
|
|
- else EmptyTree
|
|
|
- if (coercion ne EmptyTree) {
|
|
|
- def msg = s"inferred view from ${tree.tpe} to $pt via $coercion: ${coercion.tpe}"
|
|
|
- if (settings.logImplicitConv.value) context.echo(tree.pos, msg)
|
|
|
- else debuglog(msg)
|
|
|
-
|
|
|
- val viewApplied = new ApplyImplicitView(coercion, List(tree)) setPos tree.pos
|
|
|
- val silentContext = context.makeImplicit(context.ambiguousErrors)
|
|
|
- val typedView = newTyper(silentContext).typed(viewApplied, mode, pt)
|
|
|
-
|
|
|
- silentContext.reporter.firstError match {
|
|
|
- case None => typedView
|
|
|
- case Some(err) => lastTry(err)
|
|
|
- }
|
|
|
- } else lastTry()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else lastTry()
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- def vanillaAdapt(tree: Tree) = {
|
|
|
- def applyPossible = {
|
|
|
- def applyMeth = member(adaptToName(tree, nme.apply).tpe, nme.apply)
|
|
|
- def hasPolymorphicApply = applyMeth.alternatives exists (_.tpe.typeParams.nonEmpty)
|
|
|
- def hasMonomorphicApply = applyMeth.alternatives exists (_.tpe.paramSectionCount > 0)
|
|
|
-
|
|
|
- def badDynamicApply() = {
|
|
|
- tree match {
|
|
|
- case Apply(fun, _) => DynamicRewriteError(tree, ApplyWithoutArgsError(tree, fun))
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- false
|
|
|
- }
|
|
|
- if (acceptsApplyDynamic(tree.tpe))
|
|
|
- !isDynamicRewrite(tree) || badDynamicApply()
|
|
|
- else if (mode.inTappMode)
|
|
|
- tree.tpe.typeParams.isEmpty && hasPolymorphicApply
|
|
|
- else
|
|
|
- hasMonomorphicApply
|
|
|
- }
|
|
|
- def shouldInsertApply(tree: Tree) = mode.typingExprFun && {
|
|
|
- tree.tpe match {
|
|
|
- case _: MethodType | _: OverloadedType | _: PolyType => false
|
|
|
- case _ => applyPossible
|
|
|
- }
|
|
|
- }
|
|
|
- if (tree.isType) // (6)
|
|
|
- adaptType()
|
|
|
- else if (mode.typingExprNotFun &&
|
|
|
- treeInfo.isMacroApplication(tree) &&
|
|
|
- !isMacroExpansionSuppressed(tree))
|
|
|
- macroExpand(this, tree, mode, pt)
|
|
|
- else if (mode.typingConstructorPattern)
|
|
|
- typedConstructorPattern(tree, pt)
|
|
|
- else if (shouldInsertApply(tree)) // (8)
|
|
|
- insertApply()
|
|
|
- else if (hasUndetsInMonoMode) // (9)
|
|
|
- // This used to have
|
|
|
- // assert(!context.inTypeConstructorAllowed, context)
|
|
|
- // but that's not guaranteed to be true in the face of erroneous code; errors in typedApply might mean we
|
|
|
- // never get around to inferring them, and they leak out and wind up here.
|
|
|
- instantiatePossiblyExpectingUnit(tree, mode, pt)
|
|
|
- else if (isScalaRepeatedParamType(tree.tpe) && !isScalaRepeatedParamType(pt))
|
|
|
- // TODO: we really shouldn't use T* as a first class types (e.g. for repeated case fields),
|
|
|
- // but we can't allow T* to conform to other types (see isCompatible) because that breaks overload resolution
|
|
|
- adapt(tree.setType(repeatedToSeq(tree.tpe)), mode, pt, original = EmptyTree)
|
|
|
- else if (tree.tpe <:< pt)
|
|
|
- tree
|
|
|
- else if (mode.inPatternMode && { inferModulePattern(tree, pt); isPopulated(tree.tpe, approximateAbstracts(pt)) })
|
|
|
- tree
|
|
|
- else {
|
|
|
- val constFolded = constfold(tree, pt, context.owner)
|
|
|
- if (constFolded.tpe <:< pt) adapt(constFolded, mode, pt, original) // set stage for (0)
|
|
|
- else adaptExprNotFunMode() // (10) -- (15)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // begin adapt
|
|
|
- if (isMacroImplRef(tree)) {
|
|
|
- if (treeInfo.isMacroApplication(tree)) adapt(unmarkMacroImplRef(tree), mode, pt, original)
|
|
|
- else tree
|
|
|
- } else tree.tpe match {
|
|
|
- case AnnotatedType(_, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1)
|
|
|
- adaptAnnotations(tree, this, mode, pt)
|
|
|
- case ct @ FoldableConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0)
|
|
|
- adaptConstant(value)
|
|
|
- case OverloadedType(_, _) if !mode.inFunMode => // (1)
|
|
|
- inferExprAlternative(tree, pt)
|
|
|
- adaptAfterOverloadResolution(tree, mode, pt, original)
|
|
|
- case NullaryMethodType(restpe) => // (2)
|
|
|
- if (hasUndets && settings.lintUniversalMethods && (isCastSymbol(tree.symbol) || isTypeTestSymbol(tree.symbol)) && context.undetparams.exists(_.owner == tree.symbol))
|
|
|
- context.warning(tree.pos, s"missing type argument to ${tree.symbol}", WarningCategory.LintAdaptedArgs)
|
|
|
- val resTpDeconst = // keep constant types when they are safe to fold. erasure eliminates constant types modulo some exceptions, so keep those.
|
|
|
- if (isBeforeErasure && tree.symbol.isAccessor && tree.symbol.hasFlag(STABLE) && treeInfo.isExprSafeToInline(tree)) restpe
|
|
|
- else restpe.deconst
|
|
|
- adapt(tree setType resTpDeconst, mode, pt, original)
|
|
|
- case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
|
|
|
- adapt(tree setType arg, mode, pt, original)
|
|
|
- case tp if mode.typingExprNotLhs && isExistentialType(tp) && !isSyntheticAccessor(context.owner) =>
|
|
|
- adapt(tree setType tp.dealias.skolemizeExistential(context.owner, tree), mode, pt, original)
|
|
|
- case PolyType(tparams, restpe) if mode.inNone(TAPPmode | PATTERNmode) && !context.inTypeConstructorAllowed => // (3)
|
|
|
- // assert((mode & HKmode) == 0) //@M a PolyType in HKmode represents an anonymous type function,
|
|
|
- // we're in HKmode since a higher-kinded type is expected --> hence, don't implicitly apply it to type params!
|
|
|
- // ticket #2197 triggered turning the assert into a guard
|
|
|
- // I guess this assert wasn't violated before because type aliases weren't expanded as eagerly
|
|
|
- // (the only way to get a PolyType for an anonymous type function is by normalisation, which applies eta-expansion)
|
|
|
- // -- are we sure we want to expand aliases this early?
|
|
|
- // -- what caused this change in behaviour??
|
|
|
- val tparams1 = cloneSymbols(tparams)
|
|
|
- val tree1 =
|
|
|
- if (tree.isType) tree
|
|
|
- else TypeApply(tree, tparams1.map(tparam => TypeTree(tparam.tpeHK).setPos(tree.pos.focus))).setPos(tree.pos)
|
|
|
-
|
|
|
- context.undetparams ++= tparams1
|
|
|
- notifyUndetparamsAdded(tparams1)
|
|
|
- adapt(tree1 setType restpe.substSym(tparams, tparams1), mode, pt, original)
|
|
|
-
|
|
|
- case mt: MethodType if mode.typingExprNotFunNotLhs && mt.isImplicit => // (4.1)
|
|
|
- adaptToImplicitMethod(mt)
|
|
|
- case mt: MethodType if mode.typingExprNotFunNotLhs && !hasUndetsInMonoMode && !treeInfo.isMacroApplicationOrBlock(tree) => // (4.2) - (4.3)
|
|
|
- adaptMethodTypeToExpr(mt)
|
|
|
- case _ =>
|
|
|
- vanillaAdapt(tree)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // This just exists to help keep track of the spots where we have to adapt a tree after
|
|
|
- // overload resolution. These proved hard to find during the fix for scala/bug#8267.
|
|
|
- def adaptAfterOverloadResolution(tree: Tree, mode: Mode, pt: Type = WildcardType, original: Tree = EmptyTree): Tree = {
|
|
|
- adapt(tree, mode, pt, original)
|
|
|
- }
|
|
|
-
|
|
|
- def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- inferExprInstance(tree, context.extractUndetparams(), pt, useWeaklyCompatible = true)
|
|
|
- adapt(tree, mode, pt)
|
|
|
- }
|
|
|
- /** If the expected type is Unit: try instantiating type arguments
|
|
|
- * with expected type Unit, but if that fails, try again with pt = WildcardType
|
|
|
- * and discard the expression.
|
|
|
- */
|
|
|
- def instantiateExpectingUnit(tree: Tree, mode: Mode): Tree = {
|
|
|
- val savedUndetparams = context.undetparams
|
|
|
- silent(_.instantiate(tree, mode, UnitTpe)) orElse { _ =>
|
|
|
- context.undetparams = savedUndetparams
|
|
|
- val valueDiscard = atPos(tree.pos)(gen.mkUnitBlock(instantiate(tree, mode, WildcardType)))
|
|
|
- typed(valueDiscard, mode, UnitTpe)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def instantiatePossiblyExpectingUnit(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- if (mode.typingExprNotFun && pt.typeSymbol == UnitClass && !tree.tpe.isInstanceOf[MethodType])
|
|
|
- instantiateExpectingUnit(tree, mode)
|
|
|
- else
|
|
|
- instantiate(tree, mode, pt)
|
|
|
- }
|
|
|
-
|
|
|
- private def isAdaptableWithView(qual: Tree) = {
|
|
|
- val qtpe = qual.tpe.widen
|
|
|
- ( !isPastTyper
|
|
|
- && qual.isTerm
|
|
|
- && !qual.isInstanceOf[Super]
|
|
|
- && ((qual.symbol eq null) || !qual.symbol.isTerm || qual.symbol.isValue)
|
|
|
- && !qtpe.isError
|
|
|
- && !qtpe.typeSymbol.isBottomClass
|
|
|
- && !qtpe.isWildcard
|
|
|
- && !qual.isInstanceOf[ApplyImplicitView] // don't chain views
|
|
|
- && (context.implicitsEnabled || context.enrichmentEnabled)
|
|
|
- // Elaborating `context.implicitsEnabled`:
|
|
|
- // don't try to adapt a top-level type that's the subject of an implicit search
|
|
|
- // this happens because, if isView, typedImplicit tries to apply the "current" implicit value to
|
|
|
- // a value that needs to be coerced, so we check whether the implicit value has an `apply` method.
|
|
|
- // (If we allow this, we get divergence, e.g., starting at `conforms` during ant quick.bin)
|
|
|
- // Note: implicit arguments are still inferred (this kind of "chaining" is allowed)
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = {
|
|
|
- if (isAdaptableWithView(qual)) {
|
|
|
- qual.tpe.dealiasWiden match {
|
|
|
- case et: ExistentialType =>
|
|
|
- qual setType et.skolemizeExistential(context.owner, qual) // open the existential
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- inferView(qual, qual.tpe, searchTemplate, reportAmbiguous, saveErrors) match {
|
|
|
- case EmptyTree => qual
|
|
|
- case coercion =>
|
|
|
- if (settings.logImplicitConv.value)
|
|
|
- context.echo(qual.pos, s"applied implicit conversion from ${qual.tpe} to ${searchTemplate} = ${coercion.symbol.defString}")
|
|
|
-
|
|
|
- typedQualifier(atPos(qual.pos)(new ApplyImplicitView(coercion, List(qual))))
|
|
|
- }
|
|
|
- }
|
|
|
- else qual
|
|
|
- }
|
|
|
-
|
|
|
- /** Try to apply an implicit conversion to `qual` to that it contains
|
|
|
- * a method `name` which can be applied to arguments `args` with expected type `pt`.
|
|
|
- * If `pt` is defined, there is a fallback to try again with pt = ?.
|
|
|
- * This helps avoiding propagating result information too far and solves
|
|
|
- * #1756.
|
|
|
- * If no conversion is found, return `qual` unchanged.
|
|
|
- *
|
|
|
- */
|
|
|
- def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = {
|
|
|
- def doAdapt(restpe: Type) =
|
|
|
- //util.trace("adaptToArgs "+qual+", name = "+name+", argtpes = "+(args map (_.tpe))+", pt = "+pt+" = ")
|
|
|
- adaptToMember(qual, HasMethodMatching(name, args map (_.tpe), normalizeProtoForView(restpe)), reportAmbiguous, saveErrors)
|
|
|
-
|
|
|
- if (pt.isWildcard)
|
|
|
- doAdapt(pt)
|
|
|
- else silent(_ => doAdapt(pt)) filter (_ != qual) orElse (_ =>
|
|
|
- logResult(s"fallback on implicits in adaptToArguments: $qual.$name")(doAdapt(WildcardType))
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /** Try to apply an implicit conversion to `qual` so that it contains
|
|
|
- * a method `name`. If that's ambiguous try taking arguments into
|
|
|
- * account using `adaptToArguments`.
|
|
|
- */
|
|
|
- def adaptToMemberWithArgs(tree: Tree, qual: Tree, name: Name, mode: Mode, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = {
|
|
|
- def onError(reportError: => Tree): Tree = context.tree match {
|
|
|
- case Apply(tree1, args) if (tree1 eq tree) && args.nonEmpty =>
|
|
|
- silent(_.typedArgs(args.map(_.duplicate), mode))
|
|
|
- .filter(!_.exists(_.isErrorTyped))
|
|
|
- .fold(reportError)(adaptToArguments(qual, name, _, WildcardType, reportAmbiguous, saveErrors))
|
|
|
- case _ =>
|
|
|
- reportError
|
|
|
- }
|
|
|
-
|
|
|
- silent(_.adaptToMember(qual, HasMember(name), reportAmbiguous = false)) orElse (errs =>
|
|
|
- onError {
|
|
|
- if (reportAmbiguous) errs foreach (context issue _)
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /** Try to apply an implicit conversion to `qual` to that it contains a
|
|
|
- * member `name` of arbitrary type.
|
|
|
- * If no conversion is found, return `qual` unchanged.
|
|
|
- */
|
|
|
- def adaptToName(qual: Tree, name: Name) =
|
|
|
- if (member(qual.tpe, name) != NoSymbol) qual
|
|
|
- else adaptToMember(qual, HasMember(name))
|
|
|
-
|
|
|
- private def validateNoCaseAncestor(clazz: Symbol) = {
|
|
|
- if (!phase.erasedTypes) {
|
|
|
- for (ancestor <- clazz.ancestors find (_.isCase)) {
|
|
|
- context.error(clazz.pos, (
|
|
|
- "case %s has case ancestor %s, but case-to-case inheritance is prohibited."+
|
|
|
- " To overcome this limitation, use extractors to pattern match on non-leaf nodes."
|
|
|
- ).format(clazz, ancestor.fullName))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private def checkEphemeral(clazz: Symbol, body: List[Tree]) = {
|
|
|
- // NOTE: Code appears to be messy in this method for good reason: it clearly
|
|
|
- // communicates the fact that it implements rather ad-hoc, arbitrary and
|
|
|
- // non-regular set of rules that identify features that interact badly with
|
|
|
- // value classes. This code can be cleaned up a lot once implementation
|
|
|
- // restrictions are addressed.
|
|
|
- val isValueClass = !clazz.isTrait
|
|
|
- def where = if (isValueClass) "value class" else "universal trait extending from class Any"
|
|
|
- def implRestriction(tree: Tree, what: String) =
|
|
|
- context.error(tree.pos, s"implementation restriction: $what is not allowed in $where" +
|
|
|
- "\nThis restriction is planned to be removed in subsequent releases.")
|
|
|
- /**
|
|
|
- * Deeply traverses the tree in search of constructs that are not allowed
|
|
|
- * in value classes (at any nesting level).
|
|
|
- *
|
|
|
- * All restrictions this object imposes are probably not fundamental but require
|
|
|
- * fair amount of work and testing. We are conservative for now when it comes
|
|
|
- * to allowing language features to interact with value classes.
|
|
|
- * */
|
|
|
- object checkEphemeralDeep extends Traverser {
|
|
|
- override def traverse(tree: Tree): Unit = if (isValueClass) {
|
|
|
- tree match {
|
|
|
- case _: ModuleDef =>
|
|
|
- //see https://github.com/scala/bug/issues/6359
|
|
|
- implRestriction(tree, "nested object")
|
|
|
- //see https://github.com/scala/bug/issues/6444
|
|
|
- //see https://github.com/scala/bug/issues/6463
|
|
|
- case cd: ClassDef if !cd.symbol.isAnonymousClass => // Don't warn about partial functions, etc. scala/bug#7571
|
|
|
- implRestriction(tree, "nested class") // avoiding Type Tests that might check the $outer pointer.
|
|
|
- case Select(sup @ Super(qual, mix), selector) if selector != nme.CONSTRUCTOR && qual.symbol == clazz && mix != tpnme.EMPTY =>
|
|
|
- //see https://github.com/scala/bug/issues/6483
|
|
|
- implRestriction(sup, "qualified super reference")
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- super.traverse(tree)
|
|
|
- }
|
|
|
- }
|
|
|
- for (stat <- body) {
|
|
|
- def notAllowed(what: String) = context.error(stat.pos, s"$what is not allowed in $where")
|
|
|
- stat match {
|
|
|
- // see https://github.com/scala/bug/issues/6444
|
|
|
- // see https://github.com/scala/bug/issues/6463
|
|
|
- case ClassDef(mods, _, _, _) if isValueClass =>
|
|
|
- implRestriction(stat, s"nested ${ if (mods.isTrait) "trait" else "class" }")
|
|
|
- case _: Import | _: ClassDef | _: TypeDef | EmptyTree => // OK
|
|
|
- case DefDef(_, name, _, _, _, rhs) =>
|
|
|
- if (stat.symbol.isAuxiliaryConstructor)
|
|
|
- notAllowed("secondary constructor")
|
|
|
- else if (isValueClass && (name == nme.equals_ || name == nme.hashCode_) && !stat.symbol.isSynthetic)
|
|
|
- notAllowed(s"redefinition of $name method. See SIP-15, criterion 4.")
|
|
|
- else if (stat.symbol != null && stat.symbol.isParamAccessor)
|
|
|
- notAllowed("additional parameter")
|
|
|
- checkEphemeralDeep.traverse(rhs)
|
|
|
- case _: ValDef =>
|
|
|
- notAllowed("field definition")
|
|
|
- case _: ModuleDef =>
|
|
|
- //see https://github.com/scala/bug/issues/6359
|
|
|
- implRestriction(stat, "nested object")
|
|
|
- case _ =>
|
|
|
- notAllowed("this statement")
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private def validateDerivedValueClass(clazz: Symbol, body: List[Tree]) = {
|
|
|
- if (clazz.isTrait)
|
|
|
- context.error(clazz.pos, "only classes (not traits) are allowed to extend AnyVal")
|
|
|
- if (!clazz.isStatic)
|
|
|
- context.error(clazz.pos, "value class may not be a "+
|
|
|
- (if (clazz.owner.isTerm) "local class" else "member of another class"))
|
|
|
- if (!clazz.isPrimitiveValueClass) {
|
|
|
- clazz.primaryConstructor.paramss match {
|
|
|
- case List(List(_)) =>
|
|
|
- val decls = clazz.info.decls
|
|
|
- val paramAccessor = clazz.constrParamAccessors.head
|
|
|
- if (paramAccessor.isMutable)
|
|
|
- context.error(paramAccessor.pos, "value class parameter must not be a var")
|
|
|
- val accessor = decls.toList.find(x => x.isMethod && x.accessedOrSelf == paramAccessor)
|
|
|
- accessor match {
|
|
|
- case None =>
|
|
|
- context.error(paramAccessor.pos, "value class parameter must be a val and not be private[this]")
|
|
|
- case Some(acc) if acc.isProtectedLocal =>
|
|
|
- context.error(paramAccessor.pos, "value class parameter must not be protected[this]")
|
|
|
- case Some(acc) =>
|
|
|
- /* check all base classes, since derived value classes might lurk in refinement parents */
|
|
|
- if (acc.tpe.typeSymbol.baseClasses exists (_.isDerivedValueClass))
|
|
|
- context.error(acc.pos, "value class may not wrap another user-defined value class")
|
|
|
- def referencesUnderlying(sym: Symbol) = sym != null && sym.accessedOrSelf == paramAccessor
|
|
|
- checkEphemeral(clazz, body.filterNot(stat => referencesUnderlying(stat.symbol)))
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- context.error(clazz.pos, "value class needs to have exactly one val parameter")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (tparam <- clazz.typeParams)
|
|
|
- if (tparam hasAnnotation definitions.SpecializedClass)
|
|
|
- context.error(tparam.pos, "type parameter of value class may not be specialized")
|
|
|
- }
|
|
|
-
|
|
|
- private def warnMultiargInfix(tree: Tree): Unit =
|
|
|
- context.warning(tree.pos, "multiarg infix syntax looks like a tuple and will be deprecated", WarningCategory.LintMultiargInfix)
|
|
|
-
|
|
|
- /** Typechecks a parent type reference.
|
|
|
- *
|
|
|
- * This typecheck is harder than it might look, because it should honor early
|
|
|
- * definitions and also perform type argument inference with the help of super call
|
|
|
- * arguments provided in `encodedtpt`.
|
|
|
- *
|
|
|
- * The method is called in batches (batch = 1 time per each parent type referenced),
|
|
|
- * two batches per definition: once from namer, when entering a ClassDef or a ModuleDef
|
|
|
- * and once from typer, when typechecking the definition.
|
|
|
- *
|
|
|
- * ***Arguments***
|
|
|
- *
|
|
|
- * `encodedtpt` represents the parent type reference wrapped in an `Apply` node
|
|
|
- * which indicates value arguments (i.e. type macro arguments or super constructor call arguments)
|
|
|
- * If no value arguments are provided by the user, the `Apply` node is still
|
|
|
- * there, but its `args` will be set to `Nil`.
|
|
|
- * This argument is synthesized by `tools.nsc.ast.Parsers.templateParents`.
|
|
|
- *
|
|
|
- * `templ` is an enclosing template, which contains a primary constructor synthesized by the parser.
|
|
|
- * Such a constructor is a DefDef which contains early initializers and maybe a super constructor call
|
|
|
- * (I wrote "maybe" because trait constructors don't call super constructors).
|
|
|
- * This argument is synthesized by `tools.nsc.ast.Trees.Template`.
|
|
|
- *
|
|
|
- * `inMixinPosition` indicates whether the reference is not the first in the
|
|
|
- * list of parents (and therefore cannot be a class) or the opposite.
|
|
|
- *
|
|
|
- * ***Return value and side effects***
|
|
|
- *
|
|
|
- * Returns a `TypeTree` representing a resolved parent type.
|
|
|
- * If the typechecked parent reference implies non-nullary and non-empty argument list,
|
|
|
- * this argument list is attached to the returned value in SuperArgsAttachment.
|
|
|
- * The attachment is necessary for the subsequent typecheck to fixup a super constructor call
|
|
|
- * in the body of the primary constructor (see `typedTemplate` for details).
|
|
|
- *
|
|
|
- * This method might invoke `typedPrimaryConstrBody`, hence it might cause the side effects
|
|
|
- * described in the docs of that method. It might also attribute the Super(_, _) reference
|
|
|
- * (if present) inside the primary constructor of `templ`.
|
|
|
- *
|
|
|
- * ***Example***
|
|
|
- *
|
|
|
- * For the following definition:
|
|
|
- *
|
|
|
- * class D extends {
|
|
|
- * val x = 2
|
|
|
- * val y = 4
|
|
|
- * } with B(x)(3) with C(y) with T
|
|
|
- *
|
|
|
- * this method will be called six times:
|
|
|
- *
|
|
|
- * (3 times from the namer)
|
|
|
- * typedParentType(Apply(Apply(Ident(B), List(Ident(x))), List(3)), templ, inMixinPosition = false)
|
|
|
- * typedParentType(Apply(Ident(C), List(Ident(y))), templ, inMixinPosition = true)
|
|
|
- * typedParentType(Apply(Ident(T), List()), templ, inMixinPosition = true)
|
|
|
- *
|
|
|
- * (3 times from the typer)
|
|
|
- * <the same three calls>
|
|
|
- */
|
|
|
- private def typedParentType(encodedtpt: Tree, templ: Template, inMixinPosition: Boolean): Tree = {
|
|
|
- val app @ treeInfo.Applied(core, _, argss) = treeInfo.dissectApplied(encodedtpt)
|
|
|
- val decodedtpt = app.callee
|
|
|
- val argssAreTrivial = argss == Nil || argss == ListOfNil
|
|
|
-
|
|
|
- // we cannot avoid cyclic references with `initialize` here, because when type macros arrive,
|
|
|
- // we'll have to check the probe for isTypeMacro anyways.
|
|
|
- // therefore I think it's reasonable to trade a more specific "inherits itself" error
|
|
|
- // for a generic, yet understandable "cyclic reference" error
|
|
|
- var probe = typedTypeConstructor(core.duplicate).tpe.typeSymbol
|
|
|
- if (probe == null) probe = NoSymbol
|
|
|
- probe.initialize
|
|
|
-
|
|
|
- def cookIfNeeded(tpt: Tree) = if (context.unit.isJava) tpt modifyType rawToExistential else tpt
|
|
|
- cookIfNeeded(if (probe.isTrait || inMixinPosition) {
|
|
|
- if (!argssAreTrivial) {
|
|
|
- if (probe.isTrait) ConstrArgsInParentWhichIsTraitError(encodedtpt, probe)
|
|
|
- else () // a class in a mixin position - this warrants an error in `validateParentClasses`
|
|
|
- // therefore here we do nothing, e.g. don't check that the # of ctor arguments
|
|
|
- // matches the # of ctor parameters or stuff like that
|
|
|
- }
|
|
|
- typedType(decodedtpt)
|
|
|
- } else {
|
|
|
- val supertpt = typedTypeConstructor(decodedtpt)
|
|
|
- val supertparams = if (supertpt.hasSymbolField) supertpt.symbol.typeParams else Nil
|
|
|
- def inferParentTypeArgs: Tree = {
|
|
|
- typedPrimaryConstrBody(templ) {
|
|
|
- val supertpe = PolyType(supertparams, appliedType(supertpt.tpe, supertparams map (_.tpeHK)))
|
|
|
- val supercall = New(supertpe, mmap(argss)(_.duplicate))
|
|
|
- val treeInfo.Applied(Select(ctor, nme.CONSTRUCTOR), _, _) = supercall: @unchecked
|
|
|
- ctor setType supertpe // this is an essential hack, otherwise it will occasionally fail to typecheck
|
|
|
- atPos(supertpt.pos.focus)(supercall)
|
|
|
- } match {
|
|
|
- case EmptyTree => MissingTypeArgumentsParentTpeError(supertpt); supertpt
|
|
|
- case tpt => TypeTree(tpt.tpe) setPos supertpt.pos // scala/bug#7224: don't .focus positions of the TypeTree of a parent that exists in source
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val supertptWithTargs = if (supertparams.isEmpty || context.unit.isJava) supertpt else inferParentTypeArgs
|
|
|
-
|
|
|
- // this is the place where we tell the typer what argss should be used for the super call
|
|
|
- // if argss are nullary or empty, then (see the docs for `typedPrimaryConstrBody`)
|
|
|
- // the super call dummy is already good enough, so we don't need to do anything
|
|
|
- if (argssAreTrivial) supertptWithTargs else supertptWithTargs updateAttachment SuperArgsAttachment(argss)
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- /** Typechecks the mishmash of trees that happen to be stuffed into the primary constructor of a given template.
|
|
|
- * Before commencing the typecheck, replaces the `pendingSuperCall` dummy with the result of `actualSuperCall`.
|
|
|
- * `actualSuperCall` can return `EmptyTree`, in which case the dummy is replaced with a literal unit.
|
|
|
- *
|
|
|
- * ***Return value and side effects***
|
|
|
- *
|
|
|
- * If a super call is present in the primary constructor and is not erased by the transform, returns it typechecked.
|
|
|
- * Otherwise (e.g. if the primary constructor is missing or the super call isn't there) returns `EmptyTree`.
|
|
|
- *
|
|
|
- * As a side effect, this method attributes the underlying fields of early vals.
|
|
|
- * Early vals aren't typechecked anywhere else, so it's essential to call `typedPrimaryConstrBody`
|
|
|
- * at least once per definition. It'd be great to disentangle this logic at some point.
|
|
|
- *
|
|
|
- * ***Example***
|
|
|
- *
|
|
|
- * For the following definition:
|
|
|
- *
|
|
|
- * class D extends {
|
|
|
- * val x = 2
|
|
|
- * val y = 4
|
|
|
- * } with B(x)(3) with C(y) with T
|
|
|
- *
|
|
|
- * the primary constructor of `templ` will be:
|
|
|
- *
|
|
|
- * Block(List(
|
|
|
- * ValDef(NoMods, x, TypeTree(), 2)
|
|
|
- * ValDef(NoMods, y, TypeTree(), 4)
|
|
|
- * global.pendingSuperCall,
|
|
|
- * Literal(Constant(())))
|
|
|
- *
|
|
|
- * Note the `pendingSuperCall` part. This is the representation of a fill-me-in-later supercall dummy,
|
|
|
- * which encodes the fact that supercall argss are unknown during parsing and need to be transplanted
|
|
|
- * from one of the parent types. Read more about why the argss are unknown in `tools.nsc.ast.Trees.Template`.
|
|
|
- */
|
|
|
- private def typedPrimaryConstrBody(templ: Template)(actualSuperCall: => Tree): Tree =
|
|
|
- treeInfo.firstConstructor(templ.body) match {
|
|
|
- case ctor @ DefDef(_, _, _, vparamss, _, cbody @ Block(cstats, cunit)) =>
|
|
|
- val (preSuperStats, superCall) = {
|
|
|
- val (stats, rest) = cstats span (x => !treeInfo.isSuperConstrCall(x))
|
|
|
- (stats map (_.duplicate), if (rest.isEmpty) EmptyTree else rest.head.duplicate)
|
|
|
- }
|
|
|
- val superCall1 = (superCall match {
|
|
|
- case global.pendingSuperCall => actualSuperCall
|
|
|
- case EmptyTree => EmptyTree
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }) orElse cunit
|
|
|
- val cbody1 = treeCopy.Block(cbody, preSuperStats, superCall1)
|
|
|
- val clazz = context.owner
|
|
|
- assert(clazz != NoSymbol, templ)
|
|
|
- // scala/bug#9086 The position of this symbol is material: implicit search will avoid triggering
|
|
|
- // cyclic errors in an implicit search in argument to the super constructor call on
|
|
|
- // account of the "ignore symbols without complete info that succeed the implicit search"
|
|
|
- // in this source file. See `ImplicitSearch#isValid` and `ImplicitInfo#isCyclicOrErroneous`.
|
|
|
- val dummy = context.outer.owner.newLocalDummy(context.owner.pos)
|
|
|
- val cscope = context.outer.makeNewScope(ctor, dummy)
|
|
|
- if (dummy.isTopLevel) currentRun.symSource(dummy) = currentUnit.source.file
|
|
|
- val cbody2 = { // called both during completion AND typing.
|
|
|
- val typer1 = newTyper(cscope)
|
|
|
- // XXX: see about using the class's symbol....
|
|
|
- clazz.unsafeTypeParams foreach (sym => typer1.context.scope.enter(sym))
|
|
|
- typer1.namer.enterValueParams(vparamss map (_.map(_.duplicate)))
|
|
|
- typer1.typed(cbody1)
|
|
|
- }
|
|
|
-
|
|
|
- val preSuperVals = treeInfo.preSuperFields(templ.body)
|
|
|
- if (preSuperVals.isEmpty && preSuperStats.nonEmpty)
|
|
|
- devWarning("Wanted to zip empty presuper val list with " + preSuperStats)
|
|
|
- else
|
|
|
- foreach2(preSuperStats, preSuperVals)((ldef, gdef) => gdef.tpt setType ldef.symbol.tpe)
|
|
|
-
|
|
|
- if (superCall1 == cunit) EmptyTree
|
|
|
- else cbody2 match { // ???
|
|
|
- case Block(_, expr) => expr
|
|
|
- case tree => tree
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- EmptyTree
|
|
|
- }
|
|
|
-
|
|
|
- /** Makes sure that the first type tree in the list of parent types is always a class.
|
|
|
- * If the first parent is a trait, prepend its supertype to the list until it's a class.
|
|
|
- */
|
|
|
- private def normalizeFirstParent(parents: List[Tree]): List[Tree] = {
|
|
|
- @annotation.tailrec
|
|
|
- def explode0(parents: List[Tree]): List[Tree] = {
|
|
|
- val supertpt :: rest = parents: @unchecked // parents is always non-empty here - it only grows
|
|
|
- if (supertpt.tpe.typeSymbol == AnyClass) {
|
|
|
- supertpt setType AnyRefTpe
|
|
|
- parents
|
|
|
- } else if (treeInfo isTraitRef supertpt) {
|
|
|
- val supertpt1 = typedType(supertpt)
|
|
|
- def supersuper = TypeTree(supertpt1.tpe.firstParent) setPos supertpt.pos.focus
|
|
|
- if (supertpt1.isErrorTyped) rest
|
|
|
- else explode0(supersuper :: supertpt1 :: rest)
|
|
|
- } else parents
|
|
|
}
|
|
|
-
|
|
|
- def explode(parents: List[Tree]) =
|
|
|
- if (treeInfo isTraitRef parents.head) explode0(parents)
|
|
|
- else parents
|
|
|
-
|
|
|
- if (parents.isEmpty) Nil else explode(parents)
|
|
|
- }
|
|
|
-
|
|
|
- /** Certain parents are added in the parser before it is known whether
|
|
|
- * that class also declared them as parents. For instance, this is an
|
|
|
- * error unless we take corrective action here:
|
|
|
- *
|
|
|
- * case class Foo() extends Serializable
|
|
|
- *
|
|
|
- * So we strip the duplicates before typer.
|
|
|
- */
|
|
|
- private def fixDuplicateSyntheticParents(parents: List[Tree]): List[Tree] = parents match {
|
|
|
- case Nil => Nil
|
|
|
- case x :: xs =>
|
|
|
- val sym = x.symbol
|
|
|
- x :: fixDuplicateSyntheticParents(
|
|
|
- if (isPossibleSyntheticParent(sym)) xs.filter(_.symbol != sym)
|
|
|
- else xs
|
|
|
- )
|
|
|
}
|
|
|
|
|
|
- def typedParentTypes(templ: Template): List[Tree] = templ.parents match {
|
|
|
- case Nil => List(atPos(templ.pos)(TypeTree(AnyRefTpe)))
|
|
|
- case first :: rest =>
|
|
|
- try {
|
|
|
- val supertpts = fixDuplicateSyntheticParents(normalizeFirstParent(
|
|
|
- typedParentType(first, templ, inMixinPosition = false) +:
|
|
|
- (rest map (typedParentType(_, templ, inMixinPosition = true)))))
|
|
|
-
|
|
|
- // if that is required to infer the targs of a super call
|
|
|
- // typedParentType calls typedPrimaryConstrBody to do the inferring typecheck
|
|
|
- // as a side effect, that typecheck also assigns types to the fields underlying early vals
|
|
|
- // however if inference is not required, the typecheck doesn't happen
|
|
|
- // and therefore early fields have their type trees not assigned
|
|
|
- // here we detect this situation and take preventive measures
|
|
|
- if (treeInfo.hasUntypedPreSuperFields(templ.body))
|
|
|
- typedPrimaryConstrBody(templ)(EmptyTree)
|
|
|
-
|
|
|
- supertpts mapConserve (tpt => checkNoEscaping.privates(this, context.owner, tpt))
|
|
|
- }
|
|
|
- catch {
|
|
|
- case ex: TypeError if !global.propagateCyclicReferences =>
|
|
|
- // fallback in case of cyclic errors
|
|
|
- // @H none of the tests enter here but I couldn't rule it out
|
|
|
- // upd. @E when a definition inherits itself, we end up here
|
|
|
- // because `typedParentType` triggers `initialize` for parent types symbols
|
|
|
- log("Type error calculating parents in template " + templ)
|
|
|
- log("Error: " + ex)
|
|
|
- ParentTypesError(templ, ex)
|
|
|
- List(TypeTree(AnyRefTpe))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** <p>Check that</p>
|
|
|
- * <ul>
|
|
|
- * <li>all parents are class types,</li>
|
|
|
- * <li>first parent class is not a mixin; following classes are mixins,</li>
|
|
|
- * <li>final classes are not inherited,</li>
|
|
|
- * <li>
|
|
|
- * sealed classes are only inherited by classes which are
|
|
|
- * nested within definition of base class, or that occur within same
|
|
|
- * statement sequence,
|
|
|
- * </li>
|
|
|
- * <li>self-type of current class is a subtype of self-type of each parent class.</li>
|
|
|
- * <li>no two parents define same symbol.</li>
|
|
|
- * </ul>
|
|
|
- */
|
|
|
- def validateParentClasses(parents: List[Tree], selfType: Type, clazzIsTrait: Boolean): Unit = {
|
|
|
- val pending = ListBuffer[AbsTypeError]()
|
|
|
- def validateDynamicParent(parent: Symbol, parentPos: Position) =
|
|
|
- if (parent == DynamicClass) checkFeature(parentPos, currentRun.runDefinitions.DynamicsFeature)
|
|
|
-
|
|
|
- def validateParentClass(parent: Tree, superclazz: Symbol) =
|
|
|
- if (!parent.isErrorTyped) {
|
|
|
- val psym = parent.tpe.typeSymbol.initialize
|
|
|
|
|
|
- if (!context.unit.isJava)
|
|
|
- checkStablePrefixClassType(parent)
|
|
|
-
|
|
|
- if (psym != superclazz) {
|
|
|
- if (context.unit.isJava && psym.isJavaAnnotation) {
|
|
|
- // allowed
|
|
|
- } else if (psym.isTrait) {
|
|
|
- val ps = psym.info.parents
|
|
|
- if (!ps.isEmpty && !superclazz.isSubClass(ps.head.typeSymbol))
|
|
|
- pending += ParentSuperSubclassError(parent, superclazz, ps.head.typeSymbol, psym)
|
|
|
- if (!clazzIsTrait) {
|
|
|
- def hasTraitParams(sym: Symbol) =
|
|
|
- sym.isScala3Defined && sym.isTrait && sym.hasAttachment[DottyParameterisedTrait]
|
|
|
- // TODO perhaps there can be a flag to skip this when we know there can be no Scala 3 definitions
|
|
|
- // or otherwise use an optimised representation for trait parameters
|
|
|
- (parent.tpe :: ps).collectFirst {
|
|
|
- case p if hasTraitParams(p.typeSymbol) =>
|
|
|
- p.typeSymbol.attachments.get[DottyParameterisedTrait].foreach( attach =>
|
|
|
- pending += ParentIsScala3TraitError(parent, p.typeSymbol, attach.params, psym)
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- pending += ParentNotATraitMixinError(parent, psym)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (psym.isFinal)
|
|
|
- pending += ParentFinalInheritanceError(parent, psym)
|
|
|
-
|
|
|
- val sameSourceFile = context.unit.source.file == psym.sourceFile
|
|
|
-
|
|
|
- if (!isPastTyper && psym.hasDeprecatedInheritanceAnnotation &&
|
|
|
- !sameSourceFile && !context.owner.ownerChain.exists(_.isDeprecated)) {
|
|
|
- val version = psym.deprecatedInheritanceVersion.getOrElse("")
|
|
|
- val since = if (version.isEmpty) version else s" (since $version)"
|
|
|
- val message = psym.deprecatedInheritanceMessage.map(msg => s": $msg").getOrElse("")
|
|
|
- val report = s"inheritance from ${psym.fullLocationString} is deprecated$since$message"
|
|
|
- context.deprecationWarning(parent.pos, psym, report, version)
|
|
|
- }
|
|
|
-
|
|
|
- val parentTypeOfThis = parent.tpe.dealias.typeOfThis
|
|
|
-
|
|
|
- if (!phase.erasedTypes &&
|
|
|
- !context.owner.isSynthetic && // don't check synthetic concrete classes for virtuals (part of DEVIRTUALIZE)
|
|
|
- !context.unit.isJava && // don't check self types for Java (scala/bug#11917)
|
|
|
- !selfType.isErroneous &&
|
|
|
- !parent.tpe.isErroneous &&
|
|
|
- !(selfType <:< parentTypeOfThis)
|
|
|
- ) {
|
|
|
- pending += ParentSelfTypeConformanceError(parent, selfType)
|
|
|
- if (settings.explaintypes.value) explainTypes(selfType, parentTypeOfThis)
|
|
|
- }
|
|
|
-
|
|
|
- if (parents exists (p => p != parent && p.tpe.typeSymbol == psym && !psym.isError))
|
|
|
- pending += ParentInheritedTwiceError(parent, psym)
|
|
|
-
|
|
|
- validateDynamicParent(psym, parent.pos)
|
|
|
- }
|
|
|
-
|
|
|
- if (!parents.isEmpty && parents.forall(!_.isErrorTyped)) {
|
|
|
- val superclazz = parents.head.tpe.typeSymbol
|
|
|
- for (p <- parents) validateParentClass(p, superclazz)
|
|
|
- }
|
|
|
-
|
|
|
- pending.foreach(ErrorUtils.issueTypeError)
|
|
|
- }
|
|
|
-
|
|
|
- def checkFinitary(classinfo: ClassInfoType): Unit = {
|
|
|
- val clazz = classinfo.typeSymbol
|
|
|
-
|
|
|
- for (tparam <- clazz.typeParams) {
|
|
|
- if (classinfo.expansiveRefs(tparam) contains tparam) {
|
|
|
- val newinfo = ClassInfoType(
|
|
|
- classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefTpe))),
|
|
|
- classinfo.decls,
|
|
|
- clazz)
|
|
|
- updatePolyClassInfo(clazz, newinfo)
|
|
|
- FinitaryError(tparam)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private def updatePolyClassInfo(clazz: Symbol, newinfo: ClassInfoType): clazz.type = {
|
|
|
- clazz.setInfo {
|
|
|
- clazz.info match {
|
|
|
- case PolyType(tparams, _) => PolyType(tparams, newinfo)
|
|
|
- case _ => newinfo
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedClassDef(cdef: ClassDef): Tree = {
|
|
|
- val clazz = cdef.symbol
|
|
|
- currentRun.profiler.beforeTypedImplDef(clazz)
|
|
|
- try {
|
|
|
- val typedMods = typedModifiers(cdef.mods)
|
|
|
- assert(clazz != NoSymbol, cdef)
|
|
|
- reenterTypeParams(cdef.tparams)
|
|
|
- val tparams1 = cdef.tparams.mapConserve(typedTypeDef)
|
|
|
- val impl1 = newTyper(context.make(cdef.impl, clazz, newScope)).typedTemplate(cdef.impl, typedParentTypes(cdef.impl))
|
|
|
- val impl2 = finishMethodSynthesis(impl1, clazz, context)
|
|
|
- if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass)
|
|
|
- checkEphemeral(clazz, impl2.body)
|
|
|
-
|
|
|
- warnTypeParameterShadow(tparams1, clazz)
|
|
|
-
|
|
|
- if (!isPastTyper) {
|
|
|
- for (ann <- clazz.getAnnotation(DeprecatedAttr)) {
|
|
|
- val m = companionSymbolOf(clazz, context)
|
|
|
- if (m != NoSymbol)
|
|
|
- m.moduleClass.addAnnotation(AnnotationInfo(ann.atp, ann.args, Nil))
|
|
|
- }
|
|
|
- }
|
|
|
- treeCopy.ClassDef(cdef, typedMods, cdef.name, tparams1, impl2)
|
|
|
- .setType(NoType)
|
|
|
- } finally {
|
|
|
- currentRun.profiler.afterTypedImplDef(clazz)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedModuleDef(mdef: ModuleDef): Tree = {
|
|
|
- // initialize all constructors of the linked class: the type completer (Namer.methodSig)
|
|
|
- // might add default getters to this object. example: "object T; class T(x: Int = 1)"
|
|
|
- val linkedClass = companionSymbolOf(mdef.symbol, context)
|
|
|
- if (linkedClass != NoSymbol)
|
|
|
- linkedClass.info.decl(nme.CONSTRUCTOR).alternatives foreach (_.initialize)
|
|
|
-
|
|
|
- val clazz = mdef.symbol.moduleClass
|
|
|
- currentRun.profiler.beforeTypedImplDef(clazz)
|
|
|
- try {
|
|
|
-
|
|
|
- val typedMods = typedModifiers(mdef.mods)
|
|
|
- assert(clazz != NoSymbol, mdef)
|
|
|
- val noSerializable = (
|
|
|
- (linkedClass eq NoSymbol)
|
|
|
- || linkedClass.isErroneous
|
|
|
- || !linkedClass.isSerializable
|
|
|
- || clazz.isSerializable
|
|
|
- )
|
|
|
- val impl1 = newTyper(context.make(mdef.impl, clazz, newScope)).typedTemplate(mdef.impl, {
|
|
|
- typedParentTypes(mdef.impl) ++ (
|
|
|
- if (noSerializable) Nil
|
|
|
- else {
|
|
|
- clazz.makeSerializable()
|
|
|
- TypeTree(SerializableTpe).setPos(clazz.pos.focus) :: Nil
|
|
|
- }
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- val impl2 = finishMethodSynthesis(impl1, clazz, context)
|
|
|
-
|
|
|
- if (mdef.symbol == PredefModule)
|
|
|
- ensurePredefParentsAreInSameSourceFile(impl2)
|
|
|
-
|
|
|
- treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
|
|
|
- } finally {
|
|
|
- currentRun.profiler.afterTypedImplDef(clazz)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private def ensurePredefParentsAreInSameSourceFile(template: Template) = {
|
|
|
- val PredefModuleFile = PredefModule.associatedFile
|
|
|
- if (template.parents.exists(p => p.symbol != AnyRefClass && p.symbol.associatedFile != PredefModuleFile))
|
|
|
- context.error(template.pos, s"All parents of Predef must be defined in $PredefModuleFile.")
|
|
|
- }
|
|
|
- /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
|
|
|
- * all the time, it is exposed here the module/class typing methods go through it.
|
|
|
- * ...but it turns out it's also the ideal spot for namer/typer coordination for
|
|
|
- * the tricky method synthesis scenarios, so we'll make it that.
|
|
|
- */
|
|
|
- protected def finishMethodSynthesis(templ: Template, clazz: Symbol, context: Context): Template = {
|
|
|
- addSyntheticMethods(templ, clazz, context)
|
|
|
- }
|
|
|
- /** For flatMapping a list of trees when you want the DocDefs and Annotated
|
|
|
- * to be transparent.
|
|
|
- */
|
|
|
- def rewrappingWrapperTrees(f: Tree => List[Tree]): Tree => List[Tree] = {
|
|
|
- case dd @ DocDef(comment, defn) => f(defn) map (stat => DocDef(comment, stat) setPos dd.pos)
|
|
|
- case Annotated(annot, defn) => f(defn) map (stat => Annotated(annot, stat))
|
|
|
- case tree => f(tree)
|
|
|
- }
|
|
|
-
|
|
|
- protected def enterSyms(txt: Context, trees: List[Tree]) = {
|
|
|
- var txt0 = txt
|
|
|
- for (tree <- trees) txt0 = enterSym(txt0, tree)
|
|
|
- }
|
|
|
-
|
|
|
- protected def enterSym(txt: Context, tree: Tree): Context =
|
|
|
- if (txt eq context) namer enterSym tree
|
|
|
- else newNamer(txt) enterSym tree
|
|
|
-
|
|
|
- def typedTemplate(templ0: Template, parents1: List[Tree]): Template = {
|
|
|
- val templ = templ0
|
|
|
- // please FIXME: uncommenting this line breaks everything
|
|
|
- // val templ = treeCopy.Template(templ0, templ0.body, templ0.self, templ0.parents)
|
|
|
- val clazz = context.owner
|
|
|
-
|
|
|
- val parentTypes = parents1.map(_.tpe)
|
|
|
-
|
|
|
- // The parents may have been normalized by typedParentTypes.
|
|
|
- // We must update the info as well, or we won't find the super constructor for our now-first parent class
|
|
|
- // Consider `class C ; trait T extends C ; trait U extends T`
|
|
|
- // `U`'s info will start with parent `T`, but `typedParentTypes` will return `List(C, T)` (`== parents1`)
|
|
|
- // now, the super call in the primary ctor will fail to find `C`'s ctor, since it bases its search on
|
|
|
- // `U`'s info, not the trees.
|
|
|
- //
|
|
|
- // For correctness and performance, we restrict this rewrite to anonymous classes,
|
|
|
- // as others have their parents in order already (it seems!), and we certainly
|
|
|
- // don't want to accidentally rewire superclasses for e.g. the primitive value classes.
|
|
|
- //
|
|
|
- // TODO: Find an example of a named class needing this rewrite, I tried but couldn't find one.
|
|
|
- if (clazz.isAnonymousClass && clazz.info.parents != parentTypes) {
|
|
|
- // println(s"updating parents of $clazz from ${clazz.info.parents} to $parentTypes")
|
|
|
- updatePolyClassInfo(clazz, ClassInfoType(parentTypes, clazz.info.decls, clazz))
|
|
|
- }
|
|
|
-
|
|
|
- clazz.annotations.foreach(_.completeInfo())
|
|
|
- if (templ.symbol == NoSymbol)
|
|
|
- templ setSymbol clazz.newLocalDummy(templ.pos)
|
|
|
- val self1 = (templ.self: @unchecked) match {
|
|
|
- case vd @ ValDef(_, _, tpt, EmptyTree) =>
|
|
|
- val tpt1 = checkNoEscaping.privates(
|
|
|
- this,
|
|
|
- clazz.thisSym,
|
|
|
- treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe
|
|
|
- )
|
|
|
- copyValDef(vd)(tpt = tpt1, rhs = EmptyTree) setType NoType
|
|
|
- }
|
|
|
- // was:
|
|
|
- // val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt))
|
|
|
- // treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType
|
|
|
- // but this leads to cycles for existential self types ==> #2545
|
|
|
- if (self1.name != nme.WILDCARD)
|
|
|
- context.scope enter self1.symbol
|
|
|
-
|
|
|
- val selfType =
|
|
|
- if (clazz.isAnonymousClass && !phase.erasedTypes)
|
|
|
- intersectionType(clazz.info.parents, clazz.owner)
|
|
|
- else
|
|
|
- clazz.typeOfThis
|
|
|
-
|
|
|
- // the following is necessary for templates generated later
|
|
|
- assert(clazz.info.decls != EmptyScope, clazz)
|
|
|
- val body1 = pluginsEnterStats(this, namer.expandMacroAnnotations(templ.body))
|
|
|
- enterSyms(context.outer.make(templ, clazz, clazz.info.decls), body1)
|
|
|
- if (!templ.isErrorTyped) // if `parentTypes` has invalidated the template, don't validate it anymore
|
|
|
- validateParentClasses(parents1, selfType, clazz.isTrait)
|
|
|
- if (clazz.isCase)
|
|
|
- validateNoCaseAncestor(clazz)
|
|
|
- if (clazz.isTrait && hasSuperArgs(parents1.head))
|
|
|
- ConstrArgsInParentOfTraitError(parents1.head, clazz)
|
|
|
-
|
|
|
- if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members
|
|
|
- checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType])
|
|
|
-
|
|
|
- val bodyWithPrimaryCtor = {
|
|
|
- val primaryCtor = treeInfo.firstConstructor(body1)
|
|
|
- val primaryCtor1 = primaryCtor match {
|
|
|
- case DefDef(_, _, _, _, _, Block(earlyVals :+ global.pendingSuperCall, unit)) =>
|
|
|
- val argss = superArgs(parents1.head) getOrElse Nil
|
|
|
- val pos = wrappingPos(parents1.head.pos, primaryCtor :: argss.flatten).makeTransparent
|
|
|
- val superCall = atPos(pos)(PrimarySuperCall(argss))
|
|
|
- deriveDefDef(primaryCtor)(_ => Block(earlyVals :+ superCall, unit) setPos pos) setPos pos
|
|
|
- case _ => primaryCtor
|
|
|
- }
|
|
|
- body1 mapConserve { case `primaryCtor` => primaryCtor1; case stat => stat }
|
|
|
- }
|
|
|
-
|
|
|
- val body3 = typedStats(bodyWithPrimaryCtor, templ.symbol)
|
|
|
-
|
|
|
- if (clazz.info.firstParent.typeSymbol == AnyValClass)
|
|
|
- validateDerivedValueClass(clazz, body3)
|
|
|
-
|
|
|
- if (!clazz.isTrait && clazz.isNonBottomSubClass(ConstantAnnotationClass)) {
|
|
|
- val ctors = body3.iterator.collect { case method: DefDef if method.symbol.isConstructor => method }
|
|
|
- val primary = ctors.next() // there is always a primary constructor
|
|
|
- if (primary.symbol.paramss.lengthIs != 1) ConstantAnnotationNeedsSingleArgumentList(primary, clazz)
|
|
|
- ctors.foreach(AuxConstrInConstantAnnotation(_, clazz))
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if (clazz.isTrait) {
|
|
|
- for (decl <- clazz.info.decls if decl.isTerm && decl.isEarlyInitialized) {
|
|
|
- context.warning(decl.pos, "Implementation restriction: early definitions in traits are not initialized before the super class is initialized.", WarningCategory.Other)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- treeCopy.Template(templ, parents1, self1, body3) setType clazz.tpe_*
|
|
|
- }
|
|
|
-
|
|
|
- /** Remove definition annotations from modifiers (they have been saved
|
|
|
- * into the symbol's `annotations` in the type completer / namer)
|
|
|
- *
|
|
|
- * However reification does need annotation definitions to proceed.
|
|
|
- * Unfortunately, AnnotationInfo doesn't provide enough info to reify it in general case.
|
|
|
- * The biggest problem is with the "atp: Type" field, which cannot be reified in some situations
|
|
|
- * that involve locally defined annotations. See more about that in Reifiers.scala.
|
|
|
- *
|
|
|
- * That's why the original tree gets saved into `original` field of AnnotationInfo (happens elsewhere).
|
|
|
- * The field doesn't get pickled/unpickled and exists only during a single compilation run.
|
|
|
- * This simultaneously allows us to reify annotations and to preserve backward compatibility.
|
|
|
- */
|
|
|
- def typedModifiers(mods: Modifiers): Modifiers =
|
|
|
- mods.copy(annotations = Nil) setPositions mods.positions
|
|
|
-
|
|
|
- def typedValDef(vdef: ValDef): ValDef = {
|
|
|
- val sym = vdef.symbol
|
|
|
- currentRun.profiler.beforeTypedImplDef(sym)
|
|
|
- try {
|
|
|
- val valDefTyper = {
|
|
|
- val maybeConstrCtx =
|
|
|
- if ((sym.isParameter || sym.isEarlyInitialized) && sym.owner.isConstructor) context.makeConstructorContext
|
|
|
- else context
|
|
|
- newTyper(maybeConstrCtx.makeNewScope(vdef, sym))
|
|
|
- }
|
|
|
- valDefTyper.typedValDefImpl(vdef)
|
|
|
- } finally {
|
|
|
- currentRun.profiler.afterTypedImplDef(sym)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // use typedValDef instead. this version is called after creating a new context for the ValDef
|
|
|
- private def typedValDefImpl(vdef: ValDef): ValDef = {
|
|
|
- val sym = vdef.symbol.initialize
|
|
|
- val typedMods = if (nme.isLocalName(sym.name) && sym.isPrivateThis && !vdef.mods.isPrivateLocal) {
|
|
|
- // scala/bug#10009 This tree has been given a field symbol by `enterGetterSetter`, patch up the
|
|
|
- // modifiers accordingly so that we can survive resetAttrs and retypechecking.
|
|
|
- // Similarly, we use `sym.name` rather than `vdef.name` below to use the local name.
|
|
|
- typedModifiers(vdef.mods.copy(flags = sym.flags, privateWithin = tpnme.EMPTY))
|
|
|
- } else typedModifiers(vdef.mods)
|
|
|
-
|
|
|
- sym.annotations.foreach(_.completeInfo())
|
|
|
- sym.filterAnnotations(_ != UnmappableAnnotation)
|
|
|
-
|
|
|
- val tpt1 = checkNoEscaping.privates(this, sym, transformedOr(vdef.tpt, typedType(vdef.tpt)))
|
|
|
- checkNonCyclic(vdef, tpt1)
|
|
|
-
|
|
|
- // allow trait accessors: it's the only vehicle we have to hang on to annotations that must be passed down to
|
|
|
- // the field that's mixed into a subclass
|
|
|
- if (sym.hasAnnotation(definitions.VolatileAttr) && !((sym hasFlag MUTABLE | LAZY) || (sym hasFlag ACCESSOR) && sym.owner.isTrait))
|
|
|
- VolatileValueError(vdef)
|
|
|
-
|
|
|
- val rhs1 =
|
|
|
- if (vdef.rhs.isEmpty) {
|
|
|
- if (sym.isVariable && sym.owner.isTerm && !sym.isLazy && !isPastTyper)
|
|
|
- LocalVarUninitializedError(vdef)
|
|
|
- vdef.rhs
|
|
|
- } else {
|
|
|
- val tpt2 = if (sym.hasDefault) {
|
|
|
- // When typechecking default parameter, replace all type parameters in the expected type by Wildcard.
|
|
|
- // This allows defining "def foo[T](a: T = 1)"
|
|
|
- val tparams = sym.owner.skipConstructor.info.typeParams
|
|
|
- val subst = new SubstTypeMap(tparams, WildcardType.fillList(tparams.length)) {
|
|
|
- @tailrec
|
|
|
- override def matches(sym: Symbol, sym1: Symbol) =
|
|
|
- if (sym.isSkolem) matches(sym.deSkolemize, sym1)
|
|
|
- else if (sym1.isSkolem) matches(sym, sym1.deSkolemize)
|
|
|
- else super.matches(sym, sym1)
|
|
|
- }
|
|
|
- // allow defaults on by-name parameters
|
|
|
- if (sym hasFlag BYNAMEPARAM)
|
|
|
- if (tpt1.tpe.typeArgs.isEmpty) WildcardType // during erasure tpt1 is Function0
|
|
|
- else subst(tpt1.tpe.typeArgs.head)
|
|
|
- else subst(tpt1.tpe)
|
|
|
- } else tpt1.tpe
|
|
|
- transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2)
|
|
|
- }
|
|
|
- val vdef1 = treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(context, rhs1)) setType NoType
|
|
|
- if (sym.isSynthetic && sym.name.startsWith(nme.RIGHT_ASSOC_OP_PREFIX))
|
|
|
- rightAssocValDefs += ((sym, vdef1.rhs))
|
|
|
- if (sym.isSynthetic && sym.owner.isClass && (tpt1.tpe eq UnitTpe) && vdef.hasAttachment[PatVarDefAttachment.type] && sym.isPrivateThis && vdef.mods.isPrivateLocal && !sym.enclClassChain.exists(_.isInterpreterWrapper)) {
|
|
|
- context.warning(vdef.pos, s"Pattern definition introduces Unit-valued member of ${sym.owner.name}; consider wrapping it in `locally { ... }`.", WarningCategory.OtherMatchAnalysis)
|
|
|
- vdef.removeAttachment[PatVarDefAttachment.type]
|
|
|
- }
|
|
|
- vdef1
|
|
|
- }
|
|
|
-
|
|
|
- /** Analyze the super constructor call to record information used later to compute parameter aliases */
|
|
|
- def analyzeSuperConsructor(meth: Symbol, vparamss: List[List[ValDef]], rhs: Tree): Unit = {
|
|
|
- val clazz = meth.owner
|
|
|
- debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs")
|
|
|
- val pending = ListBuffer[AbsTypeError]()
|
|
|
-
|
|
|
- // !!! This method is redundant with other, less buggy ones.
|
|
|
- def decompose(call: Tree): (Tree, List[Tree]) = call match {
|
|
|
- case _ if call.isErrorTyped => // e.g. scala/bug#7636
|
|
|
- (call, Nil)
|
|
|
- case Apply(fn, args) =>
|
|
|
- // an object cannot be allowed to pass a reference to itself to a superconstructor
|
|
|
- // because of initialization issues; scala/bug#473, scala/bug#3913, scala/bug#6928.
|
|
|
- foreachSubTreeBoundTo(args, clazz) { tree =>
|
|
|
- if (tree.symbol.isModule)
|
|
|
- pending += SuperConstrReferenceError(tree)
|
|
|
- tree match {
|
|
|
- case This(_) =>
|
|
|
- pending += SuperConstrArgsThisReferenceError(tree)
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- }
|
|
|
- val (superConstr, preArgs) = decompose(fn)
|
|
|
- val params = fn.tpe.params
|
|
|
- // appending a dummy tree to represent Nil for an empty varargs (is this really necessary?)
|
|
|
- val applyArgs = if (args.sizeCompare(params) < 0) args :+ EmptyTree else args take params.length
|
|
|
-
|
|
|
- assert(sameLength(applyArgs, params) || call.isErrorTyped,
|
|
|
- s"arity mismatch but call is not error typed: $clazz (params=$params, args=$applyArgs)")
|
|
|
-
|
|
|
- (superConstr, preArgs ::: applyArgs)
|
|
|
- case Block(_ :+ superCall, _) =>
|
|
|
- decompose(superCall)
|
|
|
- case _ =>
|
|
|
- (call, Nil)
|
|
|
- }
|
|
|
-
|
|
|
- // associate superclass paramaccessors with their aliases
|
|
|
- val (superConstr, superArgs) = decompose(rhs)
|
|
|
- if (superConstr.symbol.isPrimaryConstructor) {
|
|
|
- val superClazz = superConstr.symbol.owner
|
|
|
- if (!superClazz.isJavaDefined) {
|
|
|
- val superParamAccessors = superClazz.constrParamAccessors
|
|
|
- if (sameLength(superParamAccessors, superArgs)) {
|
|
|
- val accToSuperAcc = mutable.AnyRefMap[Symbol, Symbol]()
|
|
|
- for ((superAcc, superArg@Ident(name)) <- superParamAccessors zip superArgs) {
|
|
|
- if (mexists(vparamss)(_.symbol == superArg.symbol)) {
|
|
|
- val ownAcc = clazz.info decl name suchThat (_.isParamAccessor) match {
|
|
|
- case acc if !acc.isDeferred && acc.hasAccessorFlag => acc.accessed
|
|
|
- case acc => acc
|
|
|
- }
|
|
|
- ownAcc match {
|
|
|
- case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
|
|
|
- accToSuperAcc(acc) = superAcc
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (!accToSuperAcc.isEmpty) {
|
|
|
- superConstructorCalls(clazz) = accToSuperAcc
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pending.foreach(ErrorUtils.issueTypeError)
|
|
|
- }
|
|
|
-
|
|
|
- // Check for scala/bug#4842.
|
|
|
- private def checkSelfConstructorArgs(ddef: DefDef, clazz: Symbol): Unit = {
|
|
|
- val pending = ListBuffer[AbsTypeError]()
|
|
|
- ddef.rhs match {
|
|
|
- case Block(stats, expr) =>
|
|
|
- val selfConstructorCall = stats.headOption.getOrElse(expr)
|
|
|
- foreachSubTreeBoundTo(List(selfConstructorCall), clazz) {
|
|
|
- case tree @ This(_) =>
|
|
|
- pending += SelfConstrArgsThisReferenceError(tree)
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- pending.foreach(ErrorUtils.issueTypeError)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Run the provided function for each sub tree of `trees` that
|
|
|
- * are bound to a symbol with `clazz` as a base class.
|
|
|
- *
|
|
|
- * @param f This function can assume that `tree.symbol` is non null
|
|
|
- */
|
|
|
- private def foreachSubTreeBoundTo[A](trees: List[Tree], clazz: Symbol)(f: Tree => Unit): Unit =
|
|
|
- for {
|
|
|
- tree <- trees
|
|
|
- subTree <- tree
|
|
|
- } {
|
|
|
- val sym = subTree.symbol
|
|
|
- if (sym != null && sym.info.baseClasses.contains(clazz))
|
|
|
- f(subTree)
|
|
|
- }
|
|
|
-
|
|
|
- /** Check if a structurally defined method violates implementation restrictions.
|
|
|
- * A method cannot be called if it is a non-private member of a refinement type
|
|
|
- * and if its parameter's types are any of:
|
|
|
- * - the self-type of the refinement
|
|
|
- * - a type member of the refinement
|
|
|
- * - an abstract type declared outside of the refinement.
|
|
|
- * - an instance of a value class
|
|
|
- * Furthermore, the result type may not be a value class either
|
|
|
- */
|
|
|
- def checkMethodStructuralCompatible(ddef: DefDef): Unit = {
|
|
|
- val meth = ddef.symbol
|
|
|
- def parentString = meth.owner.parentSymbols.filter(_ != ObjectClass) match {
|
|
|
- case Nil => ""
|
|
|
- case xs => xs.map(_.nameString).mkString(" (of ", " with ", ")")
|
|
|
- }
|
|
|
- def fail(pos: Position, msg: String): Boolean = {
|
|
|
- context.error(pos, msg)
|
|
|
- false
|
|
|
- }
|
|
|
- /* Have to examine all parameters in all lists.
|
|
|
- */
|
|
|
- def paramssTypes(tp: Type): List[List[Type]] = tp match {
|
|
|
- case mt @ MethodType(_, restpe) => mt.paramTypes :: paramssTypes(restpe)
|
|
|
- case PolyType(_, restpe) => paramssTypes(restpe)
|
|
|
- case _ => Nil
|
|
|
- }
|
|
|
- def resultType = meth.tpe_*.finalResultType
|
|
|
- def nthParamPos(n1: Int, n2: Int) =
|
|
|
- try ddef.vparamss(n1)(n2).pos catch { case _: IndexOutOfBoundsException => meth.pos }
|
|
|
-
|
|
|
- def failStruct(pos: Position, what: String, where: String = "Parameter type") =
|
|
|
- fail(pos, s"$where in structural refinement may not refer to $what")
|
|
|
-
|
|
|
- foreachWithIndex(paramssTypes(meth.tpe)) { (paramList, listIdx) =>
|
|
|
- foreachWithIndex(paramList) { (paramType, paramIdx) =>
|
|
|
- val sym = paramType.typeSymbol
|
|
|
- def paramPos = nthParamPos(listIdx, paramIdx)
|
|
|
-
|
|
|
- /* Not enough to look for abstract types; have to recursively check the bounds
|
|
|
- * of each abstract type for more abstract types. Almost certainly there are other
|
|
|
- * exploitable type soundness bugs which can be seen by bounding a type parameter
|
|
|
- * by an abstract type which itself is bounded by an abstract type.
|
|
|
- */
|
|
|
- def checkAbstract(tp0: Type, what: String): Boolean = {
|
|
|
- def check(sym: Symbol): Boolean = !sym.isAbstractType || {
|
|
|
- log(s"""checking $tp0 in refinement$parentString at ${meth.owner.owner.fullLocationString}""")
|
|
|
- ( (!sym.hasTransOwner(meth.owner) && failStruct(paramPos, "an abstract type defined outside that refinement", what))
|
|
|
- || (!sym.hasTransOwner(meth) && failStruct(paramPos, "a type member of that refinement", what))
|
|
|
- || checkAbstract(sym.info.upperBound, "Type bound")
|
|
|
- )
|
|
|
- }
|
|
|
- tp0.dealiasWidenChain forall (t => check(t.typeSymbol))
|
|
|
- }
|
|
|
- checkAbstract(paramType, "Parameter type")
|
|
|
-
|
|
|
- if (sym.isDerivedValueClass)
|
|
|
- failStruct(paramPos, "a user-defined value class")
|
|
|
- if (paramType.isInstanceOf[ThisType] && sym == meth.owner)
|
|
|
- failStruct(paramPos, "the type of that refinement (self type)")
|
|
|
- }
|
|
|
- }
|
|
|
- if (resultType.typeSymbol.isDerivedValueClass)
|
|
|
- failStruct(ddef.tpt.pos, "a user-defined value class", where = "Result type")
|
|
|
- }
|
|
|
-
|
|
|
- def typedDefDef(ddef: DefDef): DefDef = {
|
|
|
- val meth = ddef.symbol.initialize
|
|
|
- currentRun.profiler.beforeTypedImplDef(meth)
|
|
|
- try {
|
|
|
-
|
|
|
- reenterTypeParams(ddef.tparams)
|
|
|
- reenterValueParams(ddef.vparamss)
|
|
|
-
|
|
|
- // for `val` and `var` parameter, look at `target` meta-annotation
|
|
|
- if (!isPastTyper && meth.isPrimaryConstructor) {
|
|
|
- for (vparams <- ddef.vparamss; vd <- vparams) {
|
|
|
- if (vd.mods.isParamAccessor) {
|
|
|
- val sym = vd.symbol
|
|
|
- sym.setAnnotations(sym.annotations.filter(AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = true)))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val tparams1 = ddef.tparams mapConserve typedTypeDef
|
|
|
- val vparamss1 = ddef.vparamss.mapConserve(_.mapConserve(typedValDef))
|
|
|
-
|
|
|
- warnTypeParameterShadow(tparams1, meth)
|
|
|
-
|
|
|
- meth.annotations.foreach(_.completeInfo())
|
|
|
- // we only have to move annotations around for accessors -- see annotSig as used by AccessorTypeCompleter and ValTypeCompleter
|
|
|
- if (meth.isAccessor) meth.filterAnnotations(_ != UnmappableAnnotation)
|
|
|
-
|
|
|
- for (vparams1 <- vparamss1; vparam1 <- vparams1 dropRight 1)
|
|
|
- if (isRepeatedParamType(vparam1.symbol.tpe))
|
|
|
- StarParamNotLastError(vparam1)
|
|
|
-
|
|
|
- val tpt1 = checkNoEscaping.privates(this, meth, transformedOr(ddef.tpt, typedType(ddef.tpt)))
|
|
|
- checkNonCyclic(ddef, tpt1)
|
|
|
- ddef.tpt.setType(tpt1.tpe)
|
|
|
- val typedMods = typedModifiers(ddef.mods)
|
|
|
- val rhsAtOwner = // introduced for async, but could be universally handy
|
|
|
- ddef.getAndRemoveAttachment[ChangeOwnerAttachment] match {
|
|
|
- case None => ddef.rhs
|
|
|
- case Some(ChangeOwnerAttachment(originalOwner)) => ddef.rhs.changeOwner(originalOwner, ddef.symbol)
|
|
|
- }
|
|
|
- var rhs1 =
|
|
|
- if (ddef.name == nme.CONSTRUCTOR && !ddef.symbol.hasStaticFlag) { // need this to make it possible to generate static ctors
|
|
|
- if (!meth.isPrimaryConstructor &&
|
|
|
- (!meth.owner.isClass ||
|
|
|
- meth.owner.isModuleClass ||
|
|
|
- meth.owner.isAnonOrRefinementClass))
|
|
|
- InvalidConstructorDefError(ddef)
|
|
|
- typed(rhsAtOwner)
|
|
|
- } else if (meth.isMacro) {
|
|
|
- // typechecking macro bodies is sort of unconventional
|
|
|
- // that's why we employ our custom typing scheme orchestrated outside of the typer
|
|
|
- transformedOr(rhsAtOwner, typedMacroBody(this, ddef))
|
|
|
- } else {
|
|
|
- transformedOrTyped(rhsAtOwner, EXPRmode, tpt1.tpe)
|
|
|
- }
|
|
|
-
|
|
|
- if (meth.isClassConstructor && !isPastTyper && !meth.owner.isSubClass(AnyValClass) && !meth.isJava) {
|
|
|
- // There are no supercalls for AnyVal or constructors from Java sources, which
|
|
|
- // would blow up in analyzeSuperConsructor; there's nothing to be computed for them anyway.
|
|
|
- if (meth.isPrimaryConstructor)
|
|
|
- analyzeSuperConsructor(meth, vparamss1, rhs1)
|
|
|
- else
|
|
|
- checkSelfConstructorArgs(ddef, meth.owner)
|
|
|
- }
|
|
|
-
|
|
|
- if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
|
|
|
- rhs1 = checkDead(context, rhs1)
|
|
|
-
|
|
|
- if (!isPastTyper) {
|
|
|
- if (meth.owner.isClass && meth.paramss.exists(ps => ps.exists(_.hasDefault) && isRepeatedParamType(ps.last.tpe)))
|
|
|
- StarWithDefaultError(meth)
|
|
|
-
|
|
|
- for (pp <- meth.paramss ; p <- pp)
|
|
|
- for (n <- p.deprecatedParamName)
|
|
|
- if (mexists(meth.paramss)(p1 => p != p1 && (p1.name == n || p1.deprecatedParamName.contains(n))))
|
|
|
- DeprecatedParamNameError(p, n)
|
|
|
-
|
|
|
- if (settings.multiargInfix && !meth.isConstructor && meth.owner.isClass && !meth.isDeprecated && !meth.hasAnnotation(UnusedClass) && !meth.ownerChain.exists(_.isDeprecated) && !meth.isSynthetic)
|
|
|
- meth.paramss match {
|
|
|
- case (h :: _ :: _) :: Nil if !h.isImplicit && Chars.isOperatorPart(meth.name.decoded.head) =>
|
|
|
- warnMultiargInfix(ddef)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
-
|
|
|
- if (meth.isStructuralRefinementMember)
|
|
|
- checkMethodStructuralCompatible(ddef)
|
|
|
-
|
|
|
- if (meth.isImplicit && !meth.isSynthetic) meth.paramss match {
|
|
|
- case List(param) :: _ if !param.isImplicit =>
|
|
|
- checkFeature(ddef.pos, currentRun.runDefinitions.ImplicitConversionsFeature, meth.toString)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- treeCopy.DefDef(ddef, typedMods, ddef.name, tparams1, vparamss1, tpt1, rhs1) setType NoType
|
|
|
- } finally {
|
|
|
- currentRun.profiler.afterTypedImplDef(meth)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeDef(tdef: TypeDef): TypeDef =
|
|
|
- typerWithCondLocalContext(context.makeNewScope(tdef, tdef.symbol))(tdef.tparams.nonEmpty) {
|
|
|
- _.typedTypeDefImpl(tdef)
|
|
|
- }
|
|
|
-
|
|
|
- // use typedTypeDef instead. this version is called after creating a new context for the TypeDef
|
|
|
- private def typedTypeDefImpl(tdef: TypeDef): TypeDef = {
|
|
|
- tdef.symbol.initialize
|
|
|
- reenterTypeParams(tdef.tparams)
|
|
|
- val tparams1 = tdef.tparams mapConserve typedTypeDef
|
|
|
- val typedMods = typedModifiers(tdef.mods)
|
|
|
- tdef.symbol.annotations.foreach(_.completeInfo())
|
|
|
-
|
|
|
- warnTypeParameterShadow(tparams1, tdef.symbol)
|
|
|
-
|
|
|
- // @specialized should not be pickled when compiling with -no-specialize
|
|
|
- if (settings.nospecialization.value && currentRun.compiles(tdef.symbol)) {
|
|
|
- tdef.symbol.removeAnnotation(definitions.SpecializedClass)
|
|
|
- tdef.symbol.deSkolemize.removeAnnotation(definitions.SpecializedClass)
|
|
|
- }
|
|
|
-
|
|
|
- val rhs1 = checkNoEscaping.privates(this, tdef.symbol, typedType(tdef.rhs))
|
|
|
- checkNonCyclic(tdef.symbol)
|
|
|
- if (tdef.symbol.owner.isType)
|
|
|
- rhs1.tpe match {
|
|
|
- case TypeBounds(lo1, hi1) if !(lo1 <:< hi1) => LowerBoundError(tdef, lo1, hi1)
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
-
|
|
|
- treeCopy.TypeDef(tdef, typedMods, tdef.name, tparams1, rhs1) setType NoType
|
|
|
- }
|
|
|
-
|
|
|
- private def enterLabelDef(stat: Tree): Unit = {
|
|
|
- stat match {
|
|
|
- case ldef @ LabelDef(_, _, _) =>
|
|
|
- if (ldef.symbol == NoSymbol)
|
|
|
- ldef.symbol = namer.enterInScope(
|
|
|
- context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(Nil, UnitTpe))
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedLabelDef(ldef: LabelDef): LabelDef = {
|
|
|
- if (!nme.isLoopHeaderLabel(ldef.symbol.name) || isPastTyper) {
|
|
|
- val restpe = ldef.symbol.tpe.resultType
|
|
|
- val rhs1 = typed(ldef.rhs, restpe)
|
|
|
- ldef.params foreach (param => param setType param.symbol.tpe)
|
|
|
- deriveLabelDef(ldef)(_ => rhs1) setType restpe
|
|
|
- }
|
|
|
- else {
|
|
|
- val initpe = ldef.symbol.tpe.resultType
|
|
|
- val rhs1 = typed(ldef.rhs)
|
|
|
- val restpe = rhs1.tpe
|
|
|
- if (restpe == initpe) { // stable result, no need to check again
|
|
|
- ldef.params foreach (param => param setType param.symbol.tpe)
|
|
|
- treeCopy.LabelDef(ldef, ldef.name, ldef.params, rhs1) setType restpe
|
|
|
- } else {
|
|
|
- context.scope.unlink(ldef.symbol)
|
|
|
- val sym2 = namer.enterInScope(
|
|
|
- context.owner.newLabel(ldef.name, ldef.pos) setInfo MethodType(Nil, restpe))
|
|
|
- val LabelDef(_, _, rhs1) = resetAttrs(ldef): @unchecked
|
|
|
- val rhs2 = typed(brutallyResetAttrs(rhs1), restpe)
|
|
|
- ldef.params foreach (param => param setType param.symbol.tpe)
|
|
|
- deriveLabelDef(ldef)(_ => rhs2) setSymbol sym2 setType restpe
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedBlock(block0: Block, mode: Mode, pt: Type): Block = {
|
|
|
- val syntheticPrivates = new ListBuffer[Symbol]
|
|
|
- try {
|
|
|
- namer.enterSyms(block0.stats)
|
|
|
- val block = treeCopy.Block(block0, pluginsEnterStats(this, namer.expandMacroAnnotations(block0.stats)), block0.expr)
|
|
|
- for (stat <- block.stats) enterLabelDef(stat)
|
|
|
-
|
|
|
- if (phaseId(currentPeriod) <= currentRun.typerPhase.id) {
|
|
|
- // This is very tricky stuff, because we are navigating the Scylla and Charybdis of
|
|
|
- // anonymous classes and what to return from them here. On the one hand, we cannot admit
|
|
|
- // every non-private member of an anonymous class as a part of the structural type of the
|
|
|
- // enclosing block. This runs afoul of the restriction that a structural type may not
|
|
|
- // refer to an enclosing type parameter or abstract types (which in turn is necessitated
|
|
|
- // by what can be done in Java reflection). On the other hand, making every term member
|
|
|
- // private conflicts with private escape checking - see ticket #3174 for an example.
|
|
|
- //
|
|
|
- // The cleanest way forward is if we would find a way to suppress structural type checking
|
|
|
- // for these members and maybe defer type errors to the places where members are called.
|
|
|
- // But that would be a big refactoring and also a big departure from existing code. The
|
|
|
- // probably safest fix for 2.8 is to keep members of an anonymous class that are not
|
|
|
- // mentioned in a parent type private (as before) but to disable escape checking for code
|
|
|
- // that's in the same anonymous class. That's what's done here.
|
|
|
- //
|
|
|
- // We really should go back and think hard whether we find a better way to address the
|
|
|
- // problem of escaping idents on the one hand and well-formed structural types on the
|
|
|
- // other.
|
|
|
- block match {
|
|
|
- case Block(List(classDef @ ClassDef(_, _, _, _)), Apply(Select(New(_), _), _)) =>
|
|
|
- val classDecls = classDef.symbol.info.decls
|
|
|
- lazy val visibleMembers =
|
|
|
- pt.members match {
|
|
|
- case _: ErrorScope => classDecls.toList
|
|
|
- case ms => ms
|
|
|
- }
|
|
|
-
|
|
|
- def matchesVisibleMember(member: Symbol) = visibleMembers exists { vis =>
|
|
|
- (member.name == vis.name) &&
|
|
|
- (member.tpe <:< vis.tpe.substThis(vis.owner, classDef.symbol))
|
|
|
- }
|
|
|
- // The block is an anonymous class definitions/instantiation pair
|
|
|
- // -> members that are hidden by the type of the block are made private
|
|
|
- classDecls foreach { toHide =>
|
|
|
- if (toHide.isTerm
|
|
|
- && toHide.isPossibleInRefinement
|
|
|
- && toHide.isPublic
|
|
|
- && !matchesVisibleMember(toHide)) {
|
|
|
- (toHide
|
|
|
- resetFlag (PROTECTED | LOCAL)
|
|
|
- setFlag (PRIVATE | SYNTHETIC_PRIVATE)
|
|
|
- setPrivateWithin NoSymbol)
|
|
|
-
|
|
|
- syntheticPrivates += toHide
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
- val statsTyped = typedStats(block.stats, context.owner)
|
|
|
- val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode | APPSELmode), pt)
|
|
|
-
|
|
|
- // Remove ValDef for right-associative by-value operator desugaring which has been inlined into expr1
|
|
|
- val statsTyped2 = statsTyped match {
|
|
|
- case (vd: ValDef) :: Nil if inlinedRightAssocValDefs.remove(vd.symbol) => Nil
|
|
|
- case _ => statsTyped
|
|
|
- }
|
|
|
-
|
|
|
- treeCopy.Block(block, statsTyped2, expr1)
|
|
|
- .setType(if (treeInfo.isExprSafeToInline(block)) expr1.tpe else expr1.tpe.deconst)
|
|
|
- } finally {
|
|
|
- // enable escaping privates checking from the outside and recycle
|
|
|
- // transient flag
|
|
|
- syntheticPrivates foreach (_ resetFlag SYNTHETIC_PRIVATE)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedCase(cdef: CaseDef, pattpe: Type, pt: Type): CaseDef = {
|
|
|
- // verify no _* except in last position
|
|
|
- for (Apply(_, xs) <- cdef.pat ; x <- xs dropRight 1 ; if treeInfo isStar x)
|
|
|
- StarPositionInPatternError(x)
|
|
|
-
|
|
|
- // withoutAnnotations - see continuations-run/z1673.scala
|
|
|
- // This adjustment is awfully specific to continuations, but AFAICS the
|
|
|
- // whole AnnotationChecker framework is.
|
|
|
- val pat1 = typedPattern(cdef.pat, pattpe.withoutAnnotations)
|
|
|
-
|
|
|
- for (bind @ Bind(name, _) <- cdef.pat) {
|
|
|
- val sym = bind.symbol
|
|
|
- if (name.toTermName != nme.WILDCARD && sym != null) {
|
|
|
- if (sym == NoSymbol) {
|
|
|
- if (context.scope.lookup(name) == NoSymbol)
|
|
|
- namer.enterInScope(context.owner.newErrorSymbol(name))
|
|
|
- } else
|
|
|
- namer.enterIfNotThere(sym)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree
|
|
|
- else typed(cdef.guard, BooleanTpe)
|
|
|
- var body1: Tree = typed(cdef.body, pt)
|
|
|
-
|
|
|
- if (context.enclosingCaseDef.savedTypeBounds.nonEmpty) {
|
|
|
- body1 modifyType context.enclosingCaseDef.restoreTypeBounds
|
|
|
- // insert a cast if something typechecked under the GADT constraints,
|
|
|
- // but not in real life (i.e., now that's we've reset the method's type skolems'
|
|
|
- // infos back to their pre-GADT-constraint state)
|
|
|
- if (isFullyDefined(pt) && !(body1.tpe <:< pt)) {
|
|
|
- log(s"Adding cast to pattern because ${body1.tpe} does not conform to expected type $pt")
|
|
|
- body1 = typedPos(body1.pos)(gen.mkCast(body1, pt.dealiasWiden))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // body1 = checkNoEscaping.locals(context.scope, pt, body1)
|
|
|
- treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
|
|
|
- }
|
|
|
-
|
|
|
- def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =
|
|
|
- cases mapConserve { cdef =>
|
|
|
- newTyper(context.makeNewScope(cdef, context.owner)).typedCase(cdef, pattp, pt)
|
|
|
- }
|
|
|
-
|
|
|
- def adaptCase(cdef: CaseDef, mode: Mode, tpe: Type): CaseDef = deriveCaseDef(cdef)(adapt(_, mode, tpe))
|
|
|
-
|
|
|
- def packedTypes(trees: List[Tree]): List[Type] = trees map (c => packedType(c, context.owner).deconst)
|
|
|
-
|
|
|
- // takes untyped sub-trees of a match and type checks them
|
|
|
- def typedMatch(selector: Tree, cases: List[CaseDef], mode: Mode, pt: Type, tree: Tree = EmptyTree): Match = {
|
|
|
- val selector1 = checkDead(context, typedByValueExpr(selector))
|
|
|
- val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector)
|
|
|
- val casesTyped = typedCases(cases, selectorTp, pt)
|
|
|
-
|
|
|
- def finish(cases: List[CaseDef], matchType: Type) =
|
|
|
- treeCopy.Match(tree, selector1, cases) setType matchType
|
|
|
-
|
|
|
- if (isFullyDefined(pt))
|
|
|
- finish(casesTyped, pt)
|
|
|
- else packedTypes(casesTyped) match {
|
|
|
- case packed if sameWeakLubAsLub(packed) => finish(casesTyped, lub(packed))
|
|
|
- case packed =>
|
|
|
- val lub = weakLub(packed)
|
|
|
- finish(casesTyped map (adaptCase(_, mode, lub)), lub)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** synthesize and type check a PartialFunction implementation based on the match in `tree`
|
|
|
- *
|
|
|
- * `param => sel match { cases }` becomes:
|
|
|
- *
|
|
|
- * new AbstractPartialFunction[\$argTp, \$matchResTp] {
|
|
|
- * def applyOrElse[A1 <: \$argTp, B1 >: \$matchResTp](\$param: A1, default: A1 => B1): B1 =
|
|
|
- * \$selector match { \$cases }
|
|
|
- * def isDefinedAt(x: \$argTp): Boolean =
|
|
|
- * \$selector match { \$casesTrue }
|
|
|
- * }
|
|
|
- *
|
|
|
- * TODO: it would be nicer to generate the tree specified above at once and type it as a whole,
|
|
|
- * there are two gotchas:
|
|
|
- * - matchResTp may not be known until we've typed the match (can only use resTp when it's fully defined),
|
|
|
- * - if we typed the match in isolation first, you'd know its result type, but would have to re-jig the owner structure
|
|
|
- * - could we use a type variable for matchResTp and backpatch it?
|
|
|
- * - occurrences of `this` in `cases` or `sel` must resolve to the this of the class originally enclosing the match,
|
|
|
- * not of the anonymous partial function subclass
|
|
|
- *
|
|
|
- * an alternative TODO: add partial function AST node or equivalent and get rid of this synthesis --> do everything in uncurry (or later)
|
|
|
- * however, note that pattern matching codegen is designed to run *before* uncurry
|
|
|
- */
|
|
|
- def synthesizePartialFunction(paramName: TermName, paramPos: Position, paramSynthetic: Boolean,
|
|
|
- tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- assert(pt.typeSymbol == PartialFunctionClass, s"PartialFunction synthesis for match in $tree requires PartialFunction expected type, but got $pt.")
|
|
|
- val (argTp, resTp) = partialFunctionArgResTypeFromProto(pt)
|
|
|
-
|
|
|
- // if argTp isn't fully defined, we can't translate --> error
|
|
|
- // NOTE: resTp still might not be fully defined
|
|
|
- if (!isFullyDefined(argTp)) {
|
|
|
- MissingParameterTypeAnonMatchError(tree, pt)
|
|
|
- return setError(tree)
|
|
|
- }
|
|
|
-
|
|
|
- // targs must conform to Any for us to synthesize an applyOrElse (fallback to apply otherwise -- typically for @cps annotated targs)
|
|
|
- val targsValidParams = (argTp <:< AnyTpe) && (resTp <:< AnyTpe)
|
|
|
-
|
|
|
- val anonClass = context.owner newAnonymousFunctionClass tree.pos addAnnotation SerialVersionUIDAnnotation
|
|
|
-
|
|
|
- import CODE._
|
|
|
-
|
|
|
- val Match(sel, cases) = tree: @unchecked
|
|
|
-
|
|
|
- // need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up
|
|
|
- val casesTrue = cases.map(deriveCaseDef(_)(x => atPos(x.pos.focus)(TRUE)).duplicate)
|
|
|
-
|
|
|
- // must generate a new tree every time
|
|
|
- def selector(paramSym: Symbol): Tree = gen.mkUnchecked(
|
|
|
- if (sel != EmptyTree) sel.duplicate
|
|
|
- else atPos(tree.pos.focusStart)(
|
|
|
- // scala/bug#6925: subsume type of the selector to `argTp`
|
|
|
- // we don't want/need the match to see the `A1` type that we must use for variance reasons in the method signature
|
|
|
- //
|
|
|
- // this failed: replace `selector` by `Typed(selector, TypeTree(argTp))` -- as it's an upcast, this should never fail,
|
|
|
- // `(x: A1): A` doesn't always type check, even though `A1 <: A`, due to singleton types (test/files/pos/t4269.scala)
|
|
|
- // hence the cast, which will be erased in posterasure
|
|
|
- // (the cast originally caused extremely weird types to show up
|
|
|
- // in test/scaladoc/run/scala/bug#5933.scala because `variantToSkolem` was missing `tpSym.initialize`)
|
|
|
- gen.mkCastPreservingAnnotations(Ident(paramSym), argTp)
|
|
|
- ))
|
|
|
-
|
|
|
- def mkParam(methodSym: Symbol, tp: Type = argTp) =
|
|
|
- methodSym.newValueParameter(paramName, paramPos.focus, SYNTHETIC) setInfo tp
|
|
|
-
|
|
|
- def mkDefaultCase(body: Tree) =
|
|
|
- atPos(tree.pos.makeTransparent) {
|
|
|
- CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), body)
|
|
|
- }
|
|
|
-
|
|
|
- def synthMethodTyper(methodSym: MethodSymbol) = {
|
|
|
- val ctx = context.makeNewScope(context.tree, methodSym)
|
|
|
- // scala/bug#10291 make sure `Return`s are linked to the original enclosing method, not the one we're synthesizing
|
|
|
- ctx.enclMethod = context.enclMethod
|
|
|
- newTyper(ctx)
|
|
|
- }
|
|
|
-
|
|
|
- // `def applyOrElse[A1 <: $argTp, B1 >: $matchResTp](x: A1, default: A1 => B1): B1 =
|
|
|
- // ${`$selector match { $cases; case default$ => default(x) }`
|
|
|
- def applyOrElseMethodDef = {
|
|
|
- val methodSym = anonClass.newMethod(nme.applyOrElse, tree.pos, FINAL | OVERRIDE)
|
|
|
-
|
|
|
- // create the parameter that corresponds to the function's parameter
|
|
|
- val A1 = methodSym.newTypeParameter(newTypeName("A1")).setInfo(TypeBounds.upper(argTp))
|
|
|
- val x = mkParam(methodSym, A1.tpe)
|
|
|
-
|
|
|
- // applyOrElse's default parameter:
|
|
|
- val B1 = methodSym.newTypeParameter(newTypeName("B1")).setInfo(TypeBounds.empty)
|
|
|
- val default = methodSym.newValueParameter(newTermName("default"), tree.pos.focus, SYNTHETIC) setInfo functionType(List(A1.tpe), B1.tpe)
|
|
|
-
|
|
|
- val paramSyms = List(x, default)
|
|
|
- methodSym setInfo genPolyType(List(A1, B1), MethodType(paramSyms, B1.tpe))
|
|
|
-
|
|
|
- val methodBodyTyper = synthMethodTyper(methodSym)
|
|
|
- if (!paramSynthetic) methodBodyTyper.context.scope enter x
|
|
|
-
|
|
|
- // First, type without the default case; only the cases provided
|
|
|
- // by the user are typed. The LUB of these becomes `B`, the lower
|
|
|
- // bound of `B1`, which in turn is the result type of the default
|
|
|
- // case
|
|
|
- val match0 = methodBodyTyper.typedMatch(selector(x), cases, mode, resTp)
|
|
|
- val matchResTp = match0.tpe
|
|
|
-
|
|
|
- B1 setInfo TypeBounds.lower(matchResTp) // patch info
|
|
|
-
|
|
|
- // the default uses applyOrElse's first parameter since the scrut's type has been widened
|
|
|
- val match_ = {
|
|
|
- val cdef = mkDefaultCase(methodBodyTyper.typed1(REF(default).APPLY(REF(x)), mode, B1.tpe).setType(B1.tpe))
|
|
|
- val List(defaultCase) = methodBodyTyper.typedCases(List(cdef), argTp, B1.tpe): @unchecked
|
|
|
- treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase)
|
|
|
- }
|
|
|
- match_ setType B1.tpe
|
|
|
-
|
|
|
- // scala/bug#6187 Do you really want to know? Okay, here's what's going on here.
|
|
|
- //
|
|
|
- // Well behaved trees satisfy the property:
|
|
|
- //
|
|
|
- // typed(tree) == typed(resetAttrs(typed(tree))
|
|
|
- //
|
|
|
- // Trees constructed without low-level symbol manipulation get this for free;
|
|
|
- // references to local symbols are cleared by `ResetAttrs`, but bind to the
|
|
|
- // corresponding symbol in the re-typechecked tree. But PartialFunction synthesis
|
|
|
- // doesn't play by these rules.
|
|
|
- //
|
|
|
- // During typechecking of method bodies, references to method type parameter from
|
|
|
- // the declared types of the value parameters should bind to a fresh set of skolems,
|
|
|
- // which have been entered into scope by `Namer#methodSig`. A comment therein:
|
|
|
- //
|
|
|
- // "since the skolemized tparams are in scope, the TypeRefs in vparamSymss refer to skolemized tparams"
|
|
|
- //
|
|
|
- // But, if we retypecheck the reset `applyOrElse`, the TypeTree of the `default`
|
|
|
- // parameter contains no type. Somehow (where?!) it recovers a type that is _almost_ okay:
|
|
|
- // `A1 => B1`. But it should really be `A1&0 => B1&0`. In the test, run/t6187.scala, this
|
|
|
- // difference results in a type error, as `default.apply(x)` types as `B1`, which doesn't
|
|
|
- // conform to the required `B1&0`
|
|
|
- //
|
|
|
- // I see three courses of action.
|
|
|
- //
|
|
|
- // 1) synthesize a `asInstanceOf[B1]` below (I tried this first. But... ewwww.)
|
|
|
- // 2) install an 'original' TypeTree that will used after ResetAttrs (the solution below)
|
|
|
- // 3) Figure out how the almost-correct type is recovered on re-typechecking, and
|
|
|
- // substitute in the skolems.
|
|
|
- //
|
|
|
- // For 2.11, we'll probably shift this transformation back a phase or two, so macros
|
|
|
- // won't be affected. But in any case, we should satisfy retypecheckability.
|
|
|
- //
|
|
|
- val originals: Map[Symbol, Tree] = {
|
|
|
- def typedIdent(sym: Symbol) = methodBodyTyper.typedType(Ident(sym), mode)
|
|
|
- val A1Tpt = typedIdent(A1)
|
|
|
- val B1Tpt = typedIdent(B1)
|
|
|
- Map(
|
|
|
- x -> A1Tpt,
|
|
|
- default -> gen.scalaFunctionConstr(List(A1Tpt), B1Tpt)
|
|
|
- )
|
|
|
- }
|
|
|
- def newParam(param: Symbol): ValDef = {
|
|
|
- val vd = ValDef(param, EmptyTree)
|
|
|
- val tt @ TypeTree() = vd.tpt: @unchecked
|
|
|
- tt setOriginal (originals(param) setPos param.pos.focus)
|
|
|
- vd
|
|
|
- }
|
|
|
-
|
|
|
- val defdef = newDefDef(methodSym, match_)(vparamss = mapParamss(methodSym)(newParam), tpt = TypeTree(B1.tpe))
|
|
|
-
|
|
|
- (defdef, matchResTp)
|
|
|
- }
|
|
|
-
|
|
|
- // `def isDefinedAt(x: $argTp): Boolean = ${`$selector match { $casesTrue; case default$ => false } }`
|
|
|
- def isDefinedAtMethod = {
|
|
|
- val methodSym = anonClass.newMethod(nme.isDefinedAt, tree.pos.makeTransparent, FINAL)
|
|
|
- val paramSym = mkParam(methodSym)
|
|
|
-
|
|
|
- val methodBodyTyper = synthMethodTyper(methodSym) // should use the DefDef for the context's tree, but it doesn't exist yet (we need the typer we're creating to create it)
|
|
|
- if (!paramSynthetic) methodBodyTyper.context.scope enter paramSym
|
|
|
- methodSym setInfo MethodType(List(paramSym), BooleanTpe)
|
|
|
-
|
|
|
- val defaultCase = mkDefaultCase(FALSE)
|
|
|
- val match_ = methodBodyTyper.typedMatch(selector(paramSym), casesTrue :+ defaultCase, mode, BooleanTpe)
|
|
|
-
|
|
|
- DefDef(methodSym, match_)
|
|
|
- }
|
|
|
-
|
|
|
- // only used for @cps annotated partial functions
|
|
|
- // `def apply(x: $argTp): $matchResTp = $selector match { $cases }`
|
|
|
- def applyMethod = {
|
|
|
- val methodSym = anonClass.newMethod(nme.apply, tree.pos, FINAL | OVERRIDE)
|
|
|
- val paramSym = mkParam(methodSym)
|
|
|
-
|
|
|
- methodSym setInfo MethodType(List(paramSym), AnyTpe)
|
|
|
-
|
|
|
- val methodBodyTyper = synthMethodTyper(methodSym)
|
|
|
- if (!paramSynthetic) methodBodyTyper.context.scope enter paramSym
|
|
|
-
|
|
|
- val match_ = methodBodyTyper.typedMatch(selector(paramSym), cases, mode, resTp)
|
|
|
-
|
|
|
- val matchResTp = match_.tpe
|
|
|
- methodSym setInfo MethodType(List(paramSym), matchResTp) // patch info
|
|
|
-
|
|
|
- (DefDef(methodSym, match_), matchResTp)
|
|
|
- }
|
|
|
-
|
|
|
- def parents(resTp: Type) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, List(argTp, resTp)))
|
|
|
-
|
|
|
- val members = {
|
|
|
- val (applyMeth, matchResTp) = {
|
|
|
- // rig the show so we can get started typing the method body -- later we'll correct the infos...
|
|
|
- // targs were type arguments for PartialFunction, so we know they will work for AbstractPartialFunction as well
|
|
|
- anonClass setInfo ClassInfoType(parents(resTp), newScope, anonClass)
|
|
|
-
|
|
|
- // somehow @cps annotations upset the typer when looking at applyOrElse's signature, but not apply's
|
|
|
- // TODO: figure out the details (T @cps[U] is not a subtype of Any, but then why does it work for the apply method?)
|
|
|
- if (targsValidParams) applyOrElseMethodDef
|
|
|
- else applyMethod
|
|
|
- }
|
|
|
-
|
|
|
- // patch info to the class's definitive info
|
|
|
- anonClass setInfo ClassInfoType(parents(matchResTp), newScope, anonClass)
|
|
|
- List(applyMeth, isDefinedAtMethod)
|
|
|
- }
|
|
|
-
|
|
|
- members foreach (m => anonClass.info.decls enter m.symbol)
|
|
|
-
|
|
|
- val typedBlock = typedPos(tree.pos, mode, pt) {
|
|
|
- Block(ClassDef(anonClass, NoMods, ListOfNil, members, tree.pos.focus), atPos(tree.pos.focus)(
|
|
|
- Apply(Select(New(Ident(anonClass.name).setSymbol(anonClass)), nme.CONSTRUCTOR), Nil)
|
|
|
- ))
|
|
|
- }
|
|
|
-
|
|
|
- if (typedBlock.isErrorTyped) typedBlock
|
|
|
- else // Don't leak implementation details into the type, see scala/bug#6575
|
|
|
- typedPos(tree.pos, mode, pt) {
|
|
|
- Typed(typedBlock, TypeTree(typedBlock.tpe baseType PartialFunctionClass))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Synthesize and type check the implementation of a type with a Single Abstract Method.
|
|
|
- *
|
|
|
- * Based on a type checked Function node `{ (p1: T1, ..., pN: TN) => body } : S`
|
|
|
- * where `S` is the expected type that defines a single abstract method (call it `apply` for the example),
|
|
|
- * that has signature `(p1: T1', ..., pN: TN'): T'`, synthesize the instantiation of the following anonymous class
|
|
|
- *
|
|
|
- * {{{
|
|
|
- * new S {
|
|
|
- * def apply\$body(p1: T1, ..., pN: TN): T = body
|
|
|
- * def apply(p1: T1', ..., pN: TN'): T' = apply\$body(p1,..., pN)
|
|
|
- * }
|
|
|
- * }}}
|
|
|
- *
|
|
|
- * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `pt`,
|
|
|
- * If `pt` is not fully defined, we derive `samClassTpFullyDefined` by inferring any unknown type parameters.
|
|
|
- *
|
|
|
- * The types T1' ... TN' and T' are derived from the method signature of the sam method,
|
|
|
- * as seen from the fully defined `samClassTpFullyDefined`.
|
|
|
- *
|
|
|
- * The function's body is put in a (static) method in the class definition to enforce scoping.
|
|
|
- * S's members should not be in scope in `body`. (Putting it in the block outside the class runs into implementation problems described below)
|
|
|
- *
|
|
|
- * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list),
|
|
|
- * is to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple.
|
|
|
- *
|
|
|
- * Impl notes:
|
|
|
- * - `fun` has a FunctionType, but the expected type `pt` is some SAM type -- let's remedy that
|
|
|
- * - `fun` is fully attributed, so we'll have to wrangle some symbols into shape (owner change, vparam syms)
|
|
|
- * - after experimentation, it works best to type check function literals fully first and then adapt to a sam type,
|
|
|
- * as opposed to a sam-specific code paths earlier on in type checking (in typedFunction).
|
|
|
- * For one, we want to emit the same bytecode regardless of whether the expected
|
|
|
- * function type is a built-in FunctionN or some SAM type
|
|
|
- *
|
|
|
- */
|
|
|
- def inferSamType(fun: Tree, pt: Type, mode: Mode): Boolean = fun match {
|
|
|
- case fun@Function(vparams, _) if !isFunctionType(pt) =>
|
|
|
- // TODO: can we ensure there's always a SAMFunction attachment, instead of looking up the sam again???
|
|
|
- // seems like overloading complicates things?
|
|
|
- val sam = samOfProto(pt)
|
|
|
-
|
|
|
- if (!samMatchesFunctionBasedOnArity(sam, vparams)) false
|
|
|
- else {
|
|
|
- def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && {
|
|
|
- val samMethType = pt memberInfo sam
|
|
|
- fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType)
|
|
|
- }
|
|
|
-
|
|
|
- val samTp =
|
|
|
- if (!sam.exists) NoType
|
|
|
- else if (fullyDefinedMeetsExpectedFunTp(pt)) pt
|
|
|
- else try {
|
|
|
- val ptFullyDefined = instantiateSamFromFunction(fun.tpe, pt, sam)
|
|
|
- if (ptFullyDefined <:< pt && fullyDefinedMeetsExpectedFunTp(ptFullyDefined)) {
|
|
|
- debuglog(s"sam fully defined expected type: $ptFullyDefined from $pt for ${fun.tpe}")
|
|
|
- ptFullyDefined
|
|
|
- } else {
|
|
|
- debuglog(s"Could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)")
|
|
|
- NoType
|
|
|
- }
|
|
|
- } catch {
|
|
|
- case e@(_: NoInstance | _: TypeError) =>
|
|
|
- debuglog(s"Error during SAM synthesis: could not define type $pt using ${fun.tpe} <:< ${pt memberInfo sam} (for $sam)\n$e")
|
|
|
- NoType
|
|
|
- }
|
|
|
-
|
|
|
- if (samTp eq NoType) false
|
|
|
- else {
|
|
|
- /* Make a synthetic class symbol to represent the synthetic class that
|
|
|
- * will be spun up by LMF for this function. This is necessary because
|
|
|
- * it's possible that the SAM method might need bridges, and they have
|
|
|
- * to go somewhere. Erasure knows to compute bridges for these classes
|
|
|
- * just as if they were real templates extending the SAM type. */
|
|
|
- val synthCls = fun.symbol.owner.newClassWithInfo(
|
|
|
- name = tpnme.ANON_CLASS_NAME,
|
|
|
- parents = ObjectTpe :: samTp :: Nil,
|
|
|
- scope = newScope,
|
|
|
- pos = sam.pos,
|
|
|
- newFlags = SYNTHETIC | ARTIFACT
|
|
|
- )
|
|
|
-
|
|
|
- synthCls.info.decls.enter {
|
|
|
- val newFlags = (sam.flags & ~DEFERRED) | SYNTHETIC
|
|
|
- sam.cloneSymbol(synthCls, newFlags).setInfo(samTp memberInfo sam)
|
|
|
- }
|
|
|
-
|
|
|
- fun.setType(samTp)
|
|
|
-
|
|
|
- /* Arguably I should do `fun.setSymbol(samCls)` rather than leaning
|
|
|
- * on an attachment, but doing that confounds lambdalift's free var
|
|
|
- * analysis in a way which does not seem to be trivially reparable. */
|
|
|
- fun.updateAttachment(SAMFunction(samTp, sam, synthCls))
|
|
|
-
|
|
|
- true
|
|
|
- }
|
|
|
- }
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Deconstruct an expected function-ish type `pt` into `numVparams` argument prototypes and a result prototype.
|
|
|
- *
|
|
|
- * If the expected type `pt` does not denote a function-ish type with arity `numVparams`,
|
|
|
- * still return the expected number of ErrorType/NoType argument protos, and WildcardType for the result.
|
|
|
- *
|
|
|
- * @return (argProtos, resProto) where argProtos.lengthCompare(numVparams) == 0
|
|
|
- */
|
|
|
- private def argsResProtosFromFun(pt: Type, numVparams: Int): (List[Type], Type) =
|
|
|
- pt match {
|
|
|
- case pt: OverloadedArgProto if pt.hofParamTypes.lengthCompare(numVparams) == 0 => (pt.hofParamTypes, WildcardType)
|
|
|
- case _ =>
|
|
|
- val FunctionSymbol = FunctionClass(numVparams)
|
|
|
-
|
|
|
- // In case of any non-trivial type slack between `pt` and the built-in function types, we go the SAM route,
|
|
|
- // as a subclass could have (crazily) implemented the apply method and introduced another abstract method
|
|
|
- // to serve as the vehicle.
|
|
|
- val ptNorm = pt.typeSymbol match {
|
|
|
- case NoSymbol => NoType
|
|
|
- case FunctionSymbol | PartialFunctionClass => pt
|
|
|
- case _ =>
|
|
|
- val sam = samOf(pt)
|
|
|
- if (sam.exists && sam.info.params.lengthCompare(numVparams) == 0)
|
|
|
- wildcardExtrapolation(methodToExpressionTp(pt memberInfo sam))
|
|
|
- else pt // allow type slack (pos/6221)
|
|
|
- }
|
|
|
-
|
|
|
- unwrapWrapperTypes(ptNorm baseType FunctionSymbol) match {
|
|
|
- case TypeRef(_, _, args :+ res) => (args, res) // if it's a TypeRef, we know its symbol will be FunctionSymbol
|
|
|
- case _ =>
|
|
|
- val dummyPt = if (pt == ErrorType) ErrorType else NoType
|
|
|
- (List.fill(numVparams)(dummyPt), WildcardType) // dummyPt is in CBN position
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /** Type check a function literal.
|
|
|
- *
|
|
|
- * Based on the expected type pt, potentially synthesize an instance of
|
|
|
- * - PartialFunction,
|
|
|
- * - a type with a Single Abstract Method.
|
|
|
- */
|
|
|
- private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = {
|
|
|
- val vparams = fun.vparams
|
|
|
- val numVparams = vparams.length
|
|
|
- if (numVparams > definitions.MaxFunctionArity) MaxFunctionArityError(fun, s", but $numVparams given")
|
|
|
- else {
|
|
|
- val (argProtos, resProto) = argsResProtosFromFun(pt, numVparams)
|
|
|
-
|
|
|
- // After typer, no need for further checks, parameter type inference or PartialFunction synthesis.
|
|
|
- if (isPastTyper) doTypedFunction(fun, resProto)
|
|
|
- else {
|
|
|
- val paramsMissingType = mutable.ArrayBuffer.empty[ValDef] //.sizeHint(numVparams) probably useless, since initial size is 16 and max fun arity is 22
|
|
|
-
|
|
|
- // first, try to define param types from expected function's arg types if needed
|
|
|
- foreach2(vparams, argProtos) { (vparam, argpt) =>
|
|
|
- // TODO: do we need to exclude vparam.symbol.isError? (I don't think so,
|
|
|
- // because I don't see how we could recurse after the `setError(vparam)` call below
|
|
|
- if (vparam.tpt.isEmpty) {
|
|
|
- if (isFullyDefined(argpt)) vparam.tpt setType argpt
|
|
|
- else paramsMissingType += vparam
|
|
|
-
|
|
|
- if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (paramsMissingType.nonEmpty && pt != ErrorType) {
|
|
|
- // If we can resolve the missing parameter type by undoing eta-expansion and recursing, do that -- otherwise, report error and bail
|
|
|
- typedFunctionUndoingEtaExpansion(fun, mode, resProto) orElse {
|
|
|
- // we ran out of things to try, missing parameter types are an irrevocable error
|
|
|
- var issuedMissingParameterTypeError = false
|
|
|
- paramsMissingType.foreach { vparam =>
|
|
|
- setError(vparam) // see neg/t8675b.scala (we used to set vparam.tpt to ErrorType, but that isn't as effective)
|
|
|
- MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError)
|
|
|
- issuedMissingParameterTypeError = true
|
|
|
- }
|
|
|
-
|
|
|
- setError(fun)
|
|
|
- }
|
|
|
- } else if (numVparams == 1 && pt.typeSymbol == PartialFunctionClass) { // dodge auto-tupling with the == 1
|
|
|
- // translate `x => x match { <cases> }` : PartialFunction to
|
|
|
- // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }`
|
|
|
- val funBody = fun.body match {
|
|
|
- case Match(sel, _) if sel ne EmptyTree => fun.body
|
|
|
- case funBody =>
|
|
|
- atPos(funBody.pos.makeTransparent) {
|
|
|
- Match(EmptyTree, List(CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), funBody)))
|
|
|
- }
|
|
|
- }
|
|
|
- // go to outer context -- must discard the context that was created for the Function since we're discarding the function
|
|
|
- // thus, its symbol, which serves as the current context.owner, is not the right owner
|
|
|
- // you won't know you're using the wrong owner until lambda lift crashes (unless you know better than to use the wrong owner)
|
|
|
- val outerTyper = newTyper(context.outer)
|
|
|
- val p = vparams.head
|
|
|
- if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe
|
|
|
-
|
|
|
- outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, funBody, mode, pt)
|
|
|
- } else doTypedFunction(fun, resProto)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Retry typedFunction when parameter types are missing, and they might be recovered from
|
|
|
- * the method selection that was eta-expanded into `fun`.
|
|
|
- *
|
|
|
- * When typing `(a1: T1, ..., aN: TN) => m(a1,..., aN)`, where some Ti are not fully defined,
|
|
|
- * type `m` directly (undoing eta-expansion of method m) to determine the argument types.
|
|
|
- * We have to be careful to use the result of typing the method selection, as its tree
|
|
|
- * may be rewritten.
|
|
|
- *
|
|
|
- * This tree is the result from one of:
|
|
|
- * - manual eta-expansion with named arguments (x => f(x));
|
|
|
- * - wildcard-style eta expansion (`m(_, _,)`);
|
|
|
- * - (I don't think it can result from etaExpand, because we know the argument types there.)
|
|
|
- *
|
|
|
- * Note that method values are a separate thing (`m _`): they have the idiosyncratic shape
|
|
|
- * of `Typed(expr, Function(Nil, EmptyTree))`
|
|
|
- *
|
|
|
- * @return EmptyTree on failure, or a typed version of `fun` if we are successful
|
|
|
- */
|
|
|
- private def typedFunctionUndoingEtaExpansion(fun: Function, mode: Mode, resProto: Type) = {
|
|
|
- val vparams = fun.vparams
|
|
|
-
|
|
|
- fun.body match {
|
|
|
- // we can compare arguments and parameters by name because there cannot be a binder between
|
|
|
- // the function's valdefs and the Apply's arguments
|
|
|
- // If all vparams are constrained by the method application, see if we can derive expected types for them.
|
|
|
- // Note that not all method arguments need be references to a function param.
|
|
|
- case Apply(meth, args) =>
|
|
|
- // Map param with missing param type to the argument it's passed as in the eta-expanded method application
|
|
|
- // This list specifies a way to compute the expected parameter type for each of our function's arguments in order.
|
|
|
- // Either we already know it, and then we have a Type, or we don't, and then it's an index `idx` into
|
|
|
- // the arguments passed to `meth`, so we can derive it from its MethodType
|
|
|
- // (based on where the function's parameter is applied to `meth`)
|
|
|
- val formalsFromApply =
|
|
|
- vparams.map { vd =>
|
|
|
- if (!vd.tpt.isEmpty) {
|
|
|
- if (!vd.tpt.isTyped) {
|
|
|
- val vd1 = typedValDef(vd)
|
|
|
- if (vd1.isErroneous) Left(-1)
|
|
|
- else Right(vd1.tpt.tpe)
|
|
|
- }
|
|
|
- else Right(vd.tpt.tpe)
|
|
|
- }
|
|
|
- else Left(args.indexWhere {
|
|
|
- case Ident(name) => name == vd.name
|
|
|
- case _ => false // TODO: this does not catch eta-expansion of an overloaded method that involves numeric widening scala/bug#9738 (and maybe similar patterns?)
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // If some of the vparams without type annotation was not applied to `meth`,
|
|
|
- // we're not going to learn enough from typing `meth` to determine them.
|
|
|
- if (formalsFromApply.contains(Left(-1))) EmptyTree
|
|
|
- else {
|
|
|
- // We're looking for a method (as indicated by FUNmode in the silent typed below),
|
|
|
- // so let's make sure our expected type is a MethodType (of the right arity, but we can't easily say more about the argument types)
|
|
|
- val methArgs = NoSymbol.newSyntheticValueParams(WildcardType.fillList(args.length))
|
|
|
-
|
|
|
- silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, resProto))).fold(EmptyTree: Tree) { methTyped =>
|
|
|
- // if context.undetparams is not empty, the method was polymorphic,
|
|
|
- // so we need the missing arguments to infer its type. See #871
|
|
|
- if (context.undetparams.isEmpty) {
|
|
|
- // If we are sure this function type provides all the necessary info, so that we won't have
|
|
|
- // any undetermined argument types, recurse below (`typedFunction(fun, mode, ptUnrollingEtaExpansion)`)
|
|
|
- // and rest assured we won't end up right back here (and keep recursing).
|
|
|
- //
|
|
|
- // Be careful to reuse methTyped -- it may have changed from meth (scala/bug#9745)!
|
|
|
- //
|
|
|
- // TODO: CBN / varargs / implicits? should we use formalTypes?
|
|
|
- methodToExpressionTp(methTyped.tpe) match { // we don't know how many of the vparams of our function were actually applied to the method
|
|
|
- case TypeRef(_, _, argProtos :+ _) =>
|
|
|
- val argProtosRecovered =
|
|
|
- formalsFromApply.map {
|
|
|
- case Left(idx) =>
|
|
|
- val argPt = if (argProtos.isDefinedAt(idx)) argProtos(idx) else NoType // bounds check should not be needed due to expected type `MethodType(methArgs, resProto)` above
|
|
|
- if (isFullyDefined(argPt)) argPt else NoType
|
|
|
- case Right(tp) => tp
|
|
|
- }
|
|
|
-
|
|
|
- if (argProtosRecovered contains NoType) EmptyTree // cannot safely recurse
|
|
|
- else {
|
|
|
- val funPt = functionType(argProtosRecovered, resProto)
|
|
|
- // recursion is safe because now all parameter types can be derived from `argProtosRecovered` in the prototype `funPt` passed to typedFunction
|
|
|
- typedFunction(treeCopy.Function(fun, vparams, treeCopy.Apply(fun.body, methTyped, args)), mode, funPt)
|
|
|
- }
|
|
|
- case _ => EmptyTree
|
|
|
- }
|
|
|
- } else EmptyTree
|
|
|
- }
|
|
|
- }
|
|
|
- case _ => EmptyTree
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Assuming the expected number of parameters, which all have type annotations, do the happy path.
|
|
|
- private def doTypedFunction(fun: Function, bodyPt: Type) = {
|
|
|
- val vparams = fun.vparams
|
|
|
- val vparamSyms = vparams map { vparam =>
|
|
|
- enterSym(context, vparam)
|
|
|
- if (context.retyping) context.scope enter vparam.symbol
|
|
|
- vparam.symbol
|
|
|
- }
|
|
|
- val vparamsTyped = vparams mapConserve typedValDef
|
|
|
- val bodyTyped = typed(fun.body, bodyPt)
|
|
|
-
|
|
|
- val funSym = FunctionClass(vparams.length)
|
|
|
- val funTp =
|
|
|
- if (phase.erasedTypes) funSym.tpe
|
|
|
- else {
|
|
|
- val resTp =
|
|
|
- packedType(bodyTyped, fun.symbol).resultType.deconst match {
|
|
|
- case ct: ConstantType if (bodyPt eq WildcardType) || (ct.widen <:< bodyPt) => ct.widen
|
|
|
- case tp => tp
|
|
|
- }
|
|
|
-
|
|
|
- appliedType(funSym, vparamSyms.map(_.tpe) :+ resTp)
|
|
|
- }
|
|
|
-
|
|
|
- treeCopy.Function(fun, vparamsTyped, bodyTyped) setType funTp
|
|
|
- }
|
|
|
-
|
|
|
- // #2624: need to infer type arguments for eta expansion of a polymorphic method
|
|
|
- // context.undetparams contains clones of meth.typeParams (fresh ones were generated in etaExpand)
|
|
|
- // need to run typer on tree0, since etaExpansion sets the tpe's of its subtrees to null
|
|
|
- // can't type with the expected type, as we can't recreate the setup in (3) without calling typed
|
|
|
- // (note that (3) does not call typed to do the polymorphic type instantiation --
|
|
|
- // it is called after the tree has been typed with a polymorphic expected result type)
|
|
|
- def typedEtaExpansion(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt")
|
|
|
-
|
|
|
- if (tree.tpe.isDependentMethodType) DependentMethodTpeConversionToFunctionError(tree, tree.tpe) // TODO: support this
|
|
|
- else {
|
|
|
- val expansion = etaExpand(tree, context.owner)
|
|
|
- if (context.undetparams.isEmpty) typed(expansion, mode, pt)
|
|
|
- else instantiate(typed(expansion, mode), mode, pt)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedRefinement(templ: Template): Unit = {
|
|
|
- val stats = templ.body
|
|
|
- namer.enterSyms(stats)
|
|
|
-
|
|
|
- // need to delay rest of typedRefinement to avoid cyclic reference errors
|
|
|
- unit.toCheck += { () =>
|
|
|
- val stats1 = typedStats(stats, NoSymbol)
|
|
|
- // this code kicks in only after typer, so `stats` will never be filled in time
|
|
|
- // as a result, most of compound type trees with non-empty stats will fail to reify
|
|
|
- // todo. investigate whether something can be done about this
|
|
|
- val att = templ.attachments.get[CompoundTypeTreeOriginalAttachment].getOrElse(CompoundTypeTreeOriginalAttachment(Nil, Nil))
|
|
|
- templ.removeAttachment[CompoundTypeTreeOriginalAttachment]
|
|
|
- templ updateAttachment att.copy(stats = stats1)
|
|
|
- for (stat <- stats1 if stat.isDef && stat.symbol.isOverridingSymbol)
|
|
|
- stat.symbol setFlag OVERRIDE
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedImport(imp : Import): Import = transformed.remove(imp) match {
|
|
|
- case Some(imp1: Import) => imp1
|
|
|
- case _ => log(s"unhandled import: $imp in $unit"); imp
|
|
|
- }
|
|
|
-
|
|
|
- def typedStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
|
|
|
- val inBlock = exprOwner == context.owner
|
|
|
- def includesTargetPos(tree: Tree) =
|
|
|
- tree.pos.isRange && context.unit.exists && (tree.pos includes context.unit.targetPos)
|
|
|
- val localTarget = stats exists includesTargetPos
|
|
|
- def typedStat(stat: Tree): Tree = stat match {
|
|
|
- case s if context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(s) => OnlyDeclarationsError(s)
|
|
|
- case imp @ Import(_, _) =>
|
|
|
- imp.symbol.initialize
|
|
|
- if (!imp.symbol.isError) {
|
|
|
- context = context.make(imp)
|
|
|
- typedImport(imp)
|
|
|
- } else EmptyTree
|
|
|
- // skip typechecking of statements in a sequence where some other statement includes the targetposition
|
|
|
- case s if localTarget && !includesTargetPos(s) => s
|
|
|
- case _ =>
|
|
|
- val localTyper = if (inBlock || (stat.isDef && !stat.isInstanceOf[LabelDef])) this
|
|
|
- else newTyper(context.make(stat, exprOwner))
|
|
|
- // XXX this creates a spurious dead code warning if an exception is thrown
|
|
|
- // in a constructor, even if it is the only thing in the constructor.
|
|
|
- val result = checkDead(context, localTyper.typedByValueExpr(stat))
|
|
|
-
|
|
|
- if (treeInfo.isSelfOrSuperConstrCall(result)) {
|
|
|
- context.inConstructorSuffix = true
|
|
|
- if (treeInfo.isSelfConstrCall(result)) {
|
|
|
- val called = result.symbol
|
|
|
- val defined = exprOwner.enclMethod
|
|
|
- if (called == defined) ConstructorRecursesError(stat)
|
|
|
- else {
|
|
|
- val calledPos = called.pos.pointOrElse(0)
|
|
|
- val definedPos = defined.pos.pointOrElse(0)
|
|
|
- if (calledPos > definedPos) ConstructorsOrderError(stat)
|
|
|
- else if (calledPos == definedPos) {
|
|
|
- // Trees generated by a macro have the same position
|
|
|
- // Trees typechecked by a ToolBox have no position
|
|
|
- val constructors = defined.owner.info.decl(nme.CONSTRUCTOR).alternatives
|
|
|
- if (constructors.indexOf(called) > constructors.indexOf(defined)) ConstructorsOrderError(stat)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- result
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: adapt to new trait field encoding, figure out why this exemption is made
|
|
|
- // 'accessor' and 'accessed' are so similar it becomes very difficult to
|
|
|
- //follow the logic, so I renamed one to something distinct.
|
|
|
- def accesses(looker: Symbol, accessed: Symbol) = (
|
|
|
- accessed.isLocalToThis
|
|
|
- && (accessed.isParamAccessor || looker.hasAccessorFlag && !accessed.hasAccessorFlag && accessed.isPrivate)
|
|
|
- )
|
|
|
-
|
|
|
- def checkNoDoubleDefs(scope: Scope): Unit = {
|
|
|
- var e = scope.elems
|
|
|
- while ((e ne null) && e.owner == scope) {
|
|
|
- var e1 = scope.lookupNextEntry(e)
|
|
|
- while ((e1 ne null) && e1.owner == scope) {
|
|
|
- val sym = e.sym
|
|
|
- val sym1 = e1.sym
|
|
|
-
|
|
|
- /* From the spec (refchecks checks other conditions regarding erasing to the same type and default arguments):
|
|
|
- *
|
|
|
- * A block expression [... its] statement sequence may not contain two definitions or
|
|
|
- * declarations that bind the same name --> `inBlock`
|
|
|
- *
|
|
|
- * It is an error if a template directly defines two matching members.
|
|
|
- *
|
|
|
- * A member definition $M$ _matches_ a member definition $M'$, if $M$ and $M'$ bind the same name,
|
|
|
- * and one of following holds:
|
|
|
- * 1. Neither $M$ nor $M'$ is a method definition.
|
|
|
- * 2. $M$ and $M'$ define both monomorphic methods with equivalent argument types.
|
|
|
- * 3. $M$ defines a parameterless method and $M'$ defines a method with an empty parameter list `()` or _vice versa_.
|
|
|
- * 4. $M$ and $M'$ define both polymorphic methods with equal number of argument types $\overline T$, $\overline T'$
|
|
|
- * and equal numbers of type parameters $\overline t$, $\overline t'$, say,
|
|
|
- * and $\overline T' = [\overline t'/\overline t]\overline T$.
|
|
|
- */
|
|
|
- if (!(accesses(sym, sym1) || accesses(sym1, sym)) // TODO: does this purely defer errors until later?
|
|
|
- && (inBlock || !(sym.isMethod || sym1.isMethod) || (sym.tpe matches sym1.tpe))
|
|
|
- // default getters are defined twice when multiple overloads have defaults.
|
|
|
- // The error for this is deferred until RefChecks.checkDefaultsInOverloaded
|
|
|
- && !sym.isErroneous && !sym1.isErroneous && !sym.hasDefault) {
|
|
|
- log("Double definition detected:\n " +
|
|
|
- ((sym.getClass, sym.info, sym.ownerChain)) + "\n " +
|
|
|
- ((sym1.getClass, sym1.info, sym1.ownerChain)))
|
|
|
-
|
|
|
- DefDefinedTwiceError(sym, sym1)
|
|
|
- scope.unlink(e1) // need to unlink to avoid later problems with lub; see #2779
|
|
|
- }
|
|
|
- e1 = scope.lookupNextEntry(e1)
|
|
|
- }
|
|
|
- e = e.next
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def addSynthetics(stats: List[Tree], scope: Scope): List[Tree] = {
|
|
|
- var newStats = new ListBuffer[Tree]
|
|
|
- var moreToAdd = true
|
|
|
- while (moreToAdd) {
|
|
|
- val initElems = scope.elems
|
|
|
- // scala/bug#5877 The decls of a package include decls of the package object. But we don't want to add
|
|
|
- // the corresponding synthetics to the package class, only to the package object class.
|
|
|
- // scala/bug#6734 Locality test below is meaningless if we're not even in the correct tree.
|
|
|
- // For modules that are synthetic case companions, check that case class is defined here.
|
|
|
- // scala/bug#10783 ditto for synthetic companions of derived value classes.
|
|
|
- def shouldAdd(sym: Symbol): Boolean = {
|
|
|
- def classDefinedHere(s: Symbol): Boolean = stats exists {
|
|
|
- case t: ClassDef => t.symbol eq s
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- def shouldAddAsModule: Boolean =
|
|
|
- classDefinedHere(companionSymbolOf(sym, context))
|
|
|
-
|
|
|
- (!sym.isModule || shouldAddAsModule) && (inBlock || !context.isInPackageObject(sym, context.owner))
|
|
|
- }
|
|
|
- for (sym <- scope) context.unit.synthetics.get(sym) match {
|
|
|
- // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop
|
|
|
- case Some(tree) if shouldAdd(sym) =>
|
|
|
- // if the completer set the IS_ERROR flag, retract the stat (currently only used by applyUnapplyMethodCompleter)
|
|
|
- if (!sym.initialize.hasFlag(IS_ERROR))
|
|
|
- newStats += typedStat(tree) // might add even more synthetics to the scope
|
|
|
- context.unit.synthetics -= sym
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- // the type completer of a synthetic might add more synthetics. example: if the
|
|
|
- // factory method of a case class (i.e. the constructor) has a default.
|
|
|
- moreToAdd = scope.elems ne initElems
|
|
|
- }
|
|
|
- if (newStats.isEmpty) stats
|
|
|
- else {
|
|
|
- // put default getters next to the method they belong to,
|
|
|
- // same for companion objects. fixes #2489 and #4036.
|
|
|
- // [Martin] This is pretty ugly. I think we could avoid
|
|
|
- // this code by associating defaults and companion objects
|
|
|
- // with the original tree instead of the new symbol.
|
|
|
- def matches(stat: Tree, synt: Tree) = (stat, synt) match {
|
|
|
- // synt is default arg for stat
|
|
|
- case (DefDef(_, statName, _, _, _, _), DefDef(mods, syntName, _, _, _, _)) =>
|
|
|
- mods.hasDefault && syntName.decodedName.startsWith(statName)
|
|
|
-
|
|
|
- // synt is companion module
|
|
|
- case (ClassDef(_, className, _, _), ModuleDef(_, moduleName, _)) =>
|
|
|
- className.toTermName == moduleName
|
|
|
-
|
|
|
- // synt is implicit def for implicit class (#6278)
|
|
|
- case (ClassDef(cmods, cname, _, _), DefDef(dmods, dname, _, _, _, _)) =>
|
|
|
- cmods.isImplicit && dmods.isImplicit && cname.toTermName == dname
|
|
|
-
|
|
|
- // ValDef and Accessor
|
|
|
- case (ValDef(_, cname, _, _), DefDef(_, dname, _, _, _, _)) =>
|
|
|
- cname.getterName == dname.getterName
|
|
|
-
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- def matching(stat: Tree): List[Tree] = {
|
|
|
- val (pos, neg) = newStats.partition(synt => matches(stat, synt))
|
|
|
- newStats = neg
|
|
|
- pos.toList
|
|
|
- }
|
|
|
-
|
|
|
- // sorting residual stats for stability (scala/bug#10343, synthetics generated by other synthetics)
|
|
|
- (stats foldRight List[Tree]())((stat, res) => {
|
|
|
- stat :: matching(stat) ::: res
|
|
|
- }) ::: newStats.sortBy(_.symbol.name).toList
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val stats1 = stats mapConserve typedStat
|
|
|
- if (phase.erasedTypes) stats1
|
|
|
- else {
|
|
|
- val scope = if (inBlock) context.scope else context.owner.info.decls
|
|
|
-
|
|
|
- // As packages are open, it doesn't make sense to check double definitions here. Furthermore,
|
|
|
- // it is expensive if the package is large. Instead, such double definitions are checked in `Namers.enterInScope`
|
|
|
- if (!context.owner.isPackageClass)
|
|
|
- checkNoDoubleDefs(scope)
|
|
|
-
|
|
|
- // Note that Java units don't have synthetics, but there's no point in making a special case (for performance or correctness),
|
|
|
- // as we only type check Java units when running Scaladoc on Java sources.
|
|
|
- addSynthetics(stats1, scope)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = {
|
|
|
- val typedMode = mode.onlySticky | newmode
|
|
|
- val t = withCondConstrTyper(mode.inSccMode)(_.typed(arg, typedMode, pt))
|
|
|
- checkDead.inMode(context, typedMode, t)
|
|
|
- }
|
|
|
-
|
|
|
- def typedArgs(args: List[Tree], mode: Mode) =
|
|
|
- args mapConserve (arg => typedArg(arg, mode, NOmode, WildcardType))
|
|
|
-
|
|
|
- /** Does function need to be instantiated, because a missing parameter
|
|
|
- * in an argument closure overlaps with an uninstantiated formal?
|
|
|
- */
|
|
|
- def needsInstantiation(tparams: List[Symbol], formals: List[Type], args: List[Tree]) = {
|
|
|
- def isLowerBounded(tparam: Symbol) = !tparam.info.lowerBound.typeSymbol.isBottomClass
|
|
|
-
|
|
|
- tparams.forall(isLowerBounded) && exists2(formals, args) {
|
|
|
- case (formal, Function(vparams, _)) if vparams.exists(_.tpt.isEmpty) =>
|
|
|
- val arity = vparams.length
|
|
|
- arity <= MaxFunctionArity && (formal.baseType(FunctionClass(arity)) match {
|
|
|
- case TypeRef(_, _, formalargs) =>
|
|
|
- exists2(formalargs, vparams) { (formal, vparam) =>
|
|
|
- vparam.tpt.isEmpty && tparams.exists(formal.contains)
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- false
|
|
|
- })
|
|
|
- case _ =>
|
|
|
- false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def callToCompanionConstr(context: Context, calledFun: Symbol) = {
|
|
|
- calledFun.isConstructor && {
|
|
|
- val methCtx = context.enclMethod
|
|
|
- (methCtx != NoContext) && {
|
|
|
- val contextFun = methCtx.tree.symbol
|
|
|
- contextFun.isPrimaryConstructor && contextFun.owner.isModuleClass &&
|
|
|
- companionSymbolOf(calledFun.owner, context).moduleClass == contextFun.owner
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
|
|
|
- // TODO_NMT: check the assumption that args nonEmpty
|
|
|
- def duplErrTree = setError(treeCopy.Apply(tree, fun0, args))
|
|
|
- def duplErrorTree(err: AbsTypeError) = { context.issue(err); duplErrTree }
|
|
|
-
|
|
|
- def preSelectOverloaded(fun: Tree): Tree = {
|
|
|
- if (fun.hasSymbolField && fun.symbol.isOverloaded) {
|
|
|
- // remove alternatives with wrong number of parameters without looking at types.
|
|
|
- // less expensive than including them in inferMethodAlternative (see below).
|
|
|
- def shapeType(arg: Tree): Type = arg match {
|
|
|
- case Function(vparams, body) =>
|
|
|
- // No need for phasedAppliedType, as we don't get here during erasure --
|
|
|
- // overloading resolution happens during type checking.
|
|
|
- // During erasure, the condition above (fun.symbol.isOverloaded) is false.
|
|
|
- functionType(vparams map (_ => AnyTpe), shapeType(body))
|
|
|
- case Match(EmptyTree, _) => // A partial function literal
|
|
|
- appliedType(PartialFunctionClass, AnyTpe :: NothingTpe :: Nil)
|
|
|
- case NamedArg(Ident(name), rhs) =>
|
|
|
- NamedType(name, shapeType(rhs))
|
|
|
- case _ =>
|
|
|
- NothingTpe
|
|
|
- }
|
|
|
- val argtypes = args map shapeType
|
|
|
- val pre = fun.symbol.tpe.prefix
|
|
|
- var sym = fun.symbol filter { alt =>
|
|
|
- // must use pt as expected type, not WildcardType (a tempting quick fix to #2665)
|
|
|
- // now fixed by using isWeaklyCompatible in exprTypeArgs
|
|
|
- // TODO: understand why exactly -- some types were not inferred anymore (`ant clean quick.bin` failed)
|
|
|
- // (I had expected inferMethodAlternative to pick up the slack introduced by using WildcardType here)
|
|
|
- //
|
|
|
- // @PP responds: I changed it to pass WildcardType instead of pt and only one line in
|
|
|
- // trunk (excluding scalacheck, which had another) failed to compile. It was this line in
|
|
|
- // Types: "refs = Array(Map(), Map())". I determined that inference fails if there are at
|
|
|
- // least two invariant type parameters. See the test case I checked in to help backstop:
|
|
|
- // pos/isApplicableSafe.scala.
|
|
|
- isApplicableSafe(context.undetparams, followApply(pre memberType alt), argtypes, pt)
|
|
|
- }
|
|
|
- if (sym.isOverloaded) {
|
|
|
- // retracted synthetic apply in favor of user-defined apply
|
|
|
- def isRetracted(alt: Symbol) = alt.isError && alt.isSynthetic
|
|
|
- // loose arity check: based on args, prefer no tupling, assume no args: _*,
|
|
|
- // but keep alt with repeated params or default args, this is a loose fitting
|
|
|
- def isLooseFit(alt: Symbol) =
|
|
|
- isApplicableBasedOnArity(pre memberType alt, argtypes.length, varargsStar = false, tuplingAllowed = false) || alt.tpe.params.exists(_.hasDefault)
|
|
|
- sym.filter(alt => !isRetracted(alt) && isLooseFit(alt)) match {
|
|
|
- case _: NoSymbol =>
|
|
|
- case sym1 => sym = sym1
|
|
|
- }
|
|
|
- }
|
|
|
- if (sym == NoSymbol) fun
|
|
|
- else adaptAfterOverloadResolution(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode)
|
|
|
- } else fun
|
|
|
- }
|
|
|
-
|
|
|
- val fun = preSelectOverloaded(fun0)
|
|
|
- val argslen = args.length
|
|
|
-
|
|
|
- fun.tpe match {
|
|
|
- case OverloadedType(pre, alts) =>
|
|
|
- def handleOverloaded = {
|
|
|
- val undetparams = context.undetparams
|
|
|
-
|
|
|
- val argTpes: ListBuffer[Type] = ListBuffer.empty[Type]
|
|
|
- val args1: List[Tree] = context.savingUndeterminedTypeParams() {
|
|
|
- val amode = forArgMode(fun, mode)
|
|
|
-
|
|
|
- mapWithIndex(args) { (arg, argIdx) =>
|
|
|
- def typedArg0(tree: Tree, argIdxOrName: Either[Int, Name] = Left(argIdx)) = {
|
|
|
- typedArg(tree, amode, BYVALmode, OverloadedArgProto(argIdxOrName, pre, alts)(undetparams))
|
|
|
- }
|
|
|
-
|
|
|
- arg match {
|
|
|
- // scala/bug#8197/scala/bug#4592 call for checking whether this named argument could be interpreted as an assign
|
|
|
- // infer.checkNames must not use UnitType: it may not be a valid assignment, or the setter may return another type from Unit
|
|
|
- // TODO: just make it an error to refer to a non-existent named arg, as it's far more likely to be
|
|
|
- // a typo than an assignment passed as an argument
|
|
|
- case NamedArg(lhs@Ident(name), rhs) =>
|
|
|
- // named args: only type the righthand sides ("unknown identifier" errors otherwise)
|
|
|
- // the assign is untyped; that's ok because we call doTypedApply
|
|
|
- val rhsTyped = typedArg0(rhs, Right(name))
|
|
|
- argTpes += NamedType(name, rhsTyped.tpe.deconst)
|
|
|
- treeCopy.NamedArg(arg, lhs, rhsTyped)
|
|
|
- case treeInfo.WildcardStarArg(_) =>
|
|
|
- val argTyped = typedArg0(arg)
|
|
|
- argTpes += RepeatedType(argTyped.tpe.deconst)
|
|
|
- argTyped
|
|
|
- case _ =>
|
|
|
- val argTyped = typedArg0(arg)
|
|
|
- argTpes += argTyped.tpe.deconst
|
|
|
- argTyped
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (context.reporter.hasErrors)
|
|
|
- setError(tree)
|
|
|
- else {
|
|
|
- // warn about conversions applied to blocks (#9386) in lieu of fixing
|
|
|
- def checkConversionsToBlockArgs(appl: Tree): Unit =
|
|
|
- if (settings.warnByNameImplicit) {
|
|
|
- val treeInfo.Applied(_, _, argss) = appl
|
|
|
- val needsAdjust =
|
|
|
- argss.find {
|
|
|
- case (aiv: ApplyImplicitView) :: Nil =>
|
|
|
- aiv.args match {
|
|
|
- case Block(_, _) :: Nil => true
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- needsAdjust.foreach(ts => context.warning(ts.head.pos, "Implicits applied to block expressions after overload resolution may have unexpected semantics", WarningCategory.LintBynameImplicit))
|
|
|
- }
|
|
|
- inferMethodAlternative(fun, undetparams, argTpes.toList, pt)
|
|
|
- doTypedApply(tree, adaptAfterOverloadResolution(fun, mode.forFunMode, WildcardType), args1, mode, pt).tap(checkConversionsToBlockArgs)
|
|
|
- }
|
|
|
- }
|
|
|
- handleOverloaded
|
|
|
-
|
|
|
- case _ if currentRun.runDefinitions.isPolymorphicSignature(fun.symbol) =>
|
|
|
- // Mimic's Java's treatment of polymorphic signatures as described in
|
|
|
- // https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.3
|
|
|
- //
|
|
|
- // One can think of these methods as being infinitely overloaded. We create
|
|
|
- // a fictitious new cloned method symbol for each call site that takes on a signature
|
|
|
- // governed by a) the argument types and b) the expected type
|
|
|
- val args1 = typedArgs(args, forArgMode(fun, mode))
|
|
|
- val clone = fun.symbol.cloneSymbol.withoutAnnotations
|
|
|
- val cloneParams = args1.map(arg => clone.newValueParameter(freshTermName()).setInfo(arg.tpe.deconst))
|
|
|
- val resultType =
|
|
|
- if (fun.symbol.tpe.resultType.typeSymbol != ObjectClass) fun.symbol.tpe.resultType
|
|
|
- else if (isFullyDefined(pt)) pt
|
|
|
- else ObjectTpe
|
|
|
- clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType))
|
|
|
- val fun1 = fun.setSymbol(clone).setType(clone.info)
|
|
|
- doTypedApply(tree, fun1, args1, mode, resultType).setType(resultType)
|
|
|
-
|
|
|
- case mt @ MethodType(params, _) =>
|
|
|
- val paramTypes = mt.paramTypes
|
|
|
- // repeat vararg as often as needed, remove by-name
|
|
|
- val formals = formalTypes(paramTypes, argslen)
|
|
|
-
|
|
|
- /* Try packing all arguments into a Tuple and apply `fun` to that.
|
|
|
- * This is the last thing which is tried (after default arguments).
|
|
|
- */
|
|
|
- def tryTupleApply: Tree =
|
|
|
- if (phase.erasedTypes || !eligibleForTupleConversion(paramTypes, argslen)) EmptyTree
|
|
|
- else {
|
|
|
- val tupleArgs = List(atPos(tree.pos.makeTransparent)(gen.mkTuple(args)))
|
|
|
- // expected one argument, but got 0 or >1 ==> try applying to tuple
|
|
|
- // the inner "doTypedApply" does "extractUndetparams" => restore when it fails
|
|
|
- val savedUndetparams = context.undetparams
|
|
|
- // May warn or error if a Unit or tuple was inserted.
|
|
|
- def validate(t: Tree): Tree = {
|
|
|
- // regardless of typer's mode
|
|
|
- val invalidAdaptation = t.symbol != null && !checkValidAdaptation(t, args)
|
|
|
- // only bail if we're typing an expression (and not inside another application)
|
|
|
- if (invalidAdaptation && mode.typingExprNotFun) EmptyTree
|
|
|
- else t.removeAttachment[MultiargInfixAttachment.type] // don't warn if we tupled
|
|
|
- }
|
|
|
- def reset(): Tree = {
|
|
|
- context.undetparams = savedUndetparams
|
|
|
- EmptyTree
|
|
|
- }
|
|
|
- silent(_.doTypedApply(tree, fun, tupleArgs, mode, pt)).fold(reset())(validate)
|
|
|
- }
|
|
|
-
|
|
|
- /* Treats an application which uses named or default arguments.
|
|
|
- * Also works if names + a vararg used: when names are used, the vararg
|
|
|
- * parameter has to be specified exactly once. Note that combining varargs
|
|
|
- * and defaults is ruled out by typedDefDef.
|
|
|
- */
|
|
|
- def tryNamesDefaults: Tree = {
|
|
|
- val lencmp = compareLengths(args, formals)
|
|
|
-
|
|
|
- def checkNotMacro() = {
|
|
|
- if (treeInfo.isMacroApplication(fun))
|
|
|
- tryTupleApply orElse duplErrorTree(NamedAndDefaultArgumentsNotSupportedForMacros(tree, fun))
|
|
|
- }
|
|
|
-
|
|
|
- if (mt.isErroneous) duplErrTree
|
|
|
- else if (mode.inPatternMode) {
|
|
|
- // #2064
|
|
|
- duplErrorTree(WrongNumberOfArgsError(tree, fun))
|
|
|
- } else if (lencmp > 0) {
|
|
|
- tryTupleApply orElse duplErrorTree {
|
|
|
- val (_, argPos) = removeNames(Typer.this)(args, params)
|
|
|
- TooManyArgsNamesDefaultsError(tree, fun, formals, args, argPos)
|
|
|
- }
|
|
|
- } else if (lencmp == 0) {
|
|
|
- // we don't need defaults. names were used, so this application is transformed
|
|
|
- // into a block (@see transformNamedApplication in NamesDefaults)
|
|
|
- val (namelessArgs, argPos) = removeNames(Typer.this)(args, params)
|
|
|
- if (namelessArgs exists (_.isErroneous)) {
|
|
|
- duplErrTree
|
|
|
- } else if (!allArgsArePositional(argPos) && !sameLength(formals, params))
|
|
|
- // !allArgsArePositional indicates that named arguments are used to re-order arguments
|
|
|
- duplErrorTree(MultipleVarargError(tree))
|
|
|
- else if (allArgsArePositional(argPos) && NamedApplyBlock.unapply(fun).isEmpty) {
|
|
|
- // if there's no re-ordering, and fun is not transformed, no need to transform
|
|
|
- // more than an optimization, e.g. important in "synchronized { x = update-x }"
|
|
|
- checkNotMacro()
|
|
|
- doTypedApply(tree, fun, namelessArgs, mode, pt)
|
|
|
- } else {
|
|
|
- checkNotMacro()
|
|
|
- transformNamedApplication(Typer.this, mode, pt)(
|
|
|
- treeCopy.Apply(tree, fun, namelessArgs), argPos)
|
|
|
- }
|
|
|
- } else {
|
|
|
- // defaults are needed. they are added to the argument list in named style as
|
|
|
- // calls to the default getters. Example:
|
|
|
- // foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
|
|
|
-
|
|
|
- // scala/bug#8111 transformNamedApplication eagerly shuffles around the application to preserve
|
|
|
- // evaluation order. During this process, it calls `changeOwner` on symbols that
|
|
|
- // are transplanted underneath synthetic temporary vals.
|
|
|
- //
|
|
|
- // Here, we keep track of the symbols owned by `context.owner` to enable us to
|
|
|
- // rollback, so that we don't end up with "orphaned" symbols.
|
|
|
- //
|
|
|
- // TODO: Find a better way!
|
|
|
- //
|
|
|
- // Note that duplicating trees would not be enough to fix this problem, we would also need to
|
|
|
- // clone local symbols in the duplicated tree to truly isolate things (in the spirit of BodyDuplicator),
|
|
|
- // or, better yet, disentangle the logic in `transformNamedApplication` so that we could
|
|
|
- // determine whether names/defaults is viable *before* transforming trees.
|
|
|
- def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner
|
|
|
- val symsOwnedByContextOwner = tree.collect {
|
|
|
- case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol
|
|
|
- }
|
|
|
- def rollbackNamesDefaultsOwnerChanges(): Unit = {
|
|
|
- symsOwnedByContextOwner foreach (_.owner = context.owner)
|
|
|
- }
|
|
|
-
|
|
|
- val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
|
|
|
- if (fun1.isErroneous) duplErrTree
|
|
|
- else {
|
|
|
- val NamedApplyBlock(NamedApplyInfo(qual, targs, previousArgss, _)) = fun1: @unchecked
|
|
|
- val blockIsEmpty = fun1 match {
|
|
|
- case Block(Nil, _) =>
|
|
|
- // if the block does not have any ValDef we can remove it. Note that the call to
|
|
|
- // "transformNamedApplication" is always needed in order to obtain targs/previousArgss
|
|
|
- fun1.attachments.remove[NamedApplyInfo]
|
|
|
- true
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- val (allArgs, missing) = addDefaults(args, qual, targs, previousArgss, params, fun.pos.focus, context)
|
|
|
- val funSym = fun1 match { case Block(_, expr) => expr.symbol case x => throw new MatchError(x) }
|
|
|
- val lencmp2 = compareLengths(allArgs, formals)
|
|
|
-
|
|
|
- if (!sameLength(allArgs, args) && callToCompanionConstr(context, funSym)) {
|
|
|
- duplErrorTree(ModuleUsingCompanionClassDefaultArgsError(tree))
|
|
|
- } else if (lencmp2 > 0) {
|
|
|
- removeNames(Typer.this)(allArgs, params) // #3818
|
|
|
- duplErrTree
|
|
|
- } else if (lencmp2 == 0) {
|
|
|
- // useful when a default doesn't match parameter type, e.g. def f[T](x:T="a"); f[Int]()
|
|
|
- checkNotMacro()
|
|
|
- context.set(ContextMode.DiagUsedDefaults)
|
|
|
- def checkRecursive(res: Tree): Unit =
|
|
|
- if (settings.warnRecurseWithDefault && !res.isErroneous && context.owner.hasTransOwner(funSym))
|
|
|
- context.warning(res.pos, "Recursive call used default arguments instead of passing current argument values.", WarningCategory.LintRecurseWithDefault)
|
|
|
-
|
|
|
- doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt).tap(checkRecursive)
|
|
|
- } else {
|
|
|
- rollbackNamesDefaultsOwnerChanges()
|
|
|
- tryTupleApply orElse duplErrorTree(NotEnoughArgsError(tree, fun, missing))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!sameLength(formals, args) || // wrong nb of arguments
|
|
|
- (args exists isNamedArg) || // uses a named argument
|
|
|
- NamedApplyBlock.unapply(fun).isDefined) { // fun was transformed to a named apply block =>
|
|
|
- // integrate this application into the block
|
|
|
- if (isApplyDynamicNamed(fun) && isDynamicRewrite(fun)) typedNamedApply(tree, fun, args, mode, pt)
|
|
|
- else tryNamesDefaults
|
|
|
- } else {
|
|
|
- val tparams = context.extractUndetparams()
|
|
|
- if (tparams.isEmpty) { // all type params are defined
|
|
|
- def handleMonomorphicCall: Tree = {
|
|
|
- // no expected type when jumping to a match label -- anything goes (this is ok since we're typing the translation of well-typed code)
|
|
|
- // ... except during erasure: we must take the expected type into account as it drives the insertion of casts!
|
|
|
- // I've exhausted all other semi-clean approaches I could think of in balancing GADT magic, scala/bug#6145, CPS type-driven transforms and other existential trickiness
|
|
|
- // (the right thing to do -- packing existential types -- runs into limitations in subtyping existential types,
|
|
|
- // casting breaks scala/bug#6145,
|
|
|
- // not casting breaks GADT typing as it requires sneaking ill-typed trees past typer)
|
|
|
- def noExpectedType = !phase.erasedTypes && fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol)
|
|
|
-
|
|
|
- val args1 =
|
|
|
- if (noExpectedType)
|
|
|
- typedArgs(args, forArgMode(fun, mode))
|
|
|
- else
|
|
|
- typedArgsForFormals(args, paramTypes, forArgMode(fun, mode))
|
|
|
-
|
|
|
- // instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case:
|
|
|
- // val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo)
|
|
|
- // precise(foo) : foo.type => foo.type
|
|
|
- val restpe = mt.resultType(mapList(args1)(arg => gen stableTypeFor arg orElse arg.tpe))
|
|
|
- def ifPatternSkipFormals(tp: Type) = tp match {
|
|
|
- case MethodType(_, rtp) if mode.inPatternMode => rtp
|
|
|
- case _ => tp
|
|
|
- }
|
|
|
-
|
|
|
- // Inline RHS of ValDef for right-associative by-value operator desugaring.
|
|
|
- // Remove the ValDef also if the argument is a constant-folded reference to it.
|
|
|
- var (args2, pos2) = (args1, tree.pos)
|
|
|
- args1 match {
|
|
|
- case List(lit: Literal) =>
|
|
|
- lit.attachments.get[OriginalTreeAttachment] match {
|
|
|
- case Some(OriginalTreeAttachment(id: Ident)) if rightAssocValDefs.contains(id.symbol) =>
|
|
|
- inlinedRightAssocValDefs += id.symbol
|
|
|
- rightAssocValDefs.remove(id.symbol)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
-
|
|
|
- case List(id: Ident) if rightAssocValDefs.contains(id.symbol) =>
|
|
|
- mt.params match {
|
|
|
- case List(p) if p.isByNameParam =>
|
|
|
- inlinedRightAssocValDefs += id.symbol
|
|
|
- val rhs = rightAssocValDefs.remove(id.symbol).get
|
|
|
- args2 = rhs.changeOwner(id.symbol -> context.owner) :: Nil
|
|
|
- pos2 = wrappingPos(tree :: rhs :: Nil)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- }
|
|
|
-
|
|
|
- if (!isPastTyper && args.isEmpty && canTranslateEmptyListToNil && currentRun.runDefinitions.isListApply(fun))
|
|
|
- atPos(tree.pos)(gen.mkNil.setType(restpe))
|
|
|
- else {
|
|
|
- // annoying issue with classOf that shouldn't be deconsted after typers (during fields phase)
|
|
|
- val resTp = ifPatternSkipFormals(if (isPastTyper) restpe else restpe.deconst)
|
|
|
- constfold(treeCopy.Apply(tree, fun, args2) setType resTp setPos pos2, context.owner)
|
|
|
- }
|
|
|
- }
|
|
|
- if (settings.warnDeadCode.value) {
|
|
|
- val sym = fun.symbol
|
|
|
- if (sym != null && sym.isMethod && !sym.isConstructor) {
|
|
|
- val suppress = sym == Object_synchronized || (sym.isLabel && treeInfo.isSynthCaseSymbol(sym))
|
|
|
- context.withSuppressDeadArgWarning(suppress) {
|
|
|
- handleMonomorphicCall
|
|
|
- }
|
|
|
- } else handleMonomorphicCall
|
|
|
- } else handleMonomorphicCall
|
|
|
- } else if (needsInstantiation(tparams, formals, args)) {
|
|
|
- //println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info)))
|
|
|
- inferExprInstance(fun, tparams)
|
|
|
- doTypedApply(tree, fun, args, mode, pt)
|
|
|
- } else {
|
|
|
- def handlePolymorphicCall = {
|
|
|
- assert(!mode.inPatternMode, mode) // this case cannot arise for patterns
|
|
|
- val lenientTargs = protoTypeArgs(tparams, formals, mt.resultApprox, pt)
|
|
|
- val strictTargs = map2(lenientTargs, tparams)((targ, tparam) =>
|
|
|
- if (targ == WildcardType) tparam.tpeHK else targ)
|
|
|
- var remainingParams = paramTypes
|
|
|
- def typedArgToPoly(arg: Tree, formal: Type): Tree = { //TR TODO: cleanup
|
|
|
- val lenientPt = formal.instantiateTypeParams(tparams, lenientTargs)
|
|
|
- val newmode =
|
|
|
- if (isByNameParamType(remainingParams.head)) POLYmode
|
|
|
- else POLYmode | BYVALmode
|
|
|
- if (remainingParams.tail.nonEmpty) remainingParams = remainingParams.tail
|
|
|
- val arg1 = typedArg(arg, forArgMode(fun, mode), newmode, lenientPt)
|
|
|
- val argtparams = context.extractUndetparams()
|
|
|
- if (!argtparams.isEmpty) {
|
|
|
- val strictPt = formal.instantiateTypeParams(tparams, strictTargs)
|
|
|
- inferArgumentInstance(arg1, argtparams, strictPt, lenientPt)
|
|
|
- arg1
|
|
|
- } else arg1
|
|
|
- }
|
|
|
- val args1 = map2(args, formals)(typedArgToPoly)
|
|
|
- if (args1 exists { _.isErrorTyped }) duplErrTree
|
|
|
- else {
|
|
|
- debuglog("infer method inst " + fun + ", tparams = " + tparams + ", args = " + args1.map(_.tpe) + ", pt = " + pt + ", lobounds = " + tparams.map(_.tpe.lowerBound) + ", parambounds = " + tparams.map(_.info)) //debug
|
|
|
- // define the undetparams which have been fixed by this param list, replace the corresponding symbols in "fun"
|
|
|
- // returns those undetparams which have not been instantiated.
|
|
|
- val undetparams = inferMethodInstance(fun, tparams, args1, pt)
|
|
|
- try doTypedApply(tree, fun, args1, mode, pt)
|
|
|
- finally context.undetparams = undetparams
|
|
|
- }
|
|
|
- }
|
|
|
- handlePolymorphicCall
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- case SingleType(_, _) =>
|
|
|
- doTypedApply(tree, fun setType fun.tpe.widen, args, mode, pt)
|
|
|
-
|
|
|
- case ErrorType =>
|
|
|
- if (!tree.isErrorTyped) setError(tree) else tree
|
|
|
- // @H change to setError(treeCopy.Apply(tree, fun, args))
|
|
|
-
|
|
|
- // scala/bug#7877 `isTerm` needed to exclude `class T[A] { def unapply(..) }; ... case T[X] =>`
|
|
|
- case HasUnapply(_) if mode.inPatternMode && fun.isTerm =>
|
|
|
- doTypedUnapply(tree, fun0, fun, args, mode, pt)
|
|
|
-
|
|
|
- case _ =>
|
|
|
- if (treeInfo.isMacroApplication(tree)) duplErrorTree(MacroTooManyArgumentListsError(tree, fun.symbol))
|
|
|
- else duplErrorTree(ApplyWithoutArgsError(tree, fun))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Convert an annotation constructor call into an AnnotationInfo.
|
|
|
- */
|
|
|
- def typedAnnotation(ann: Tree, annotee: Option[Tree], mode: Mode = EXPRmode): AnnotationInfo = {
|
|
|
- var hasError: Boolean = false
|
|
|
- var unmappable: Boolean = false
|
|
|
- val pending = ListBuffer[AbsTypeError]()
|
|
|
- def ErroneousAnnotation = new ErroneousAnnotation().setOriginal(ann)
|
|
|
-
|
|
|
- def rangeFinder(): (Int, Int) =
|
|
|
- if (settings.Yrangepos.value && annotee.get.pos.isDefined) {
|
|
|
- val p = annotee.get.pos
|
|
|
- (p.start, p.end)
|
|
|
- } else {
|
|
|
- // compute approximate range
|
|
|
- var s = unit.source.length
|
|
|
- var e = 0
|
|
|
- object setRange extends ForeachTreeTraverser({ child =>
|
|
|
- val pos = child.pos
|
|
|
- if (pos.isDefined) {
|
|
|
- s = s min pos.start
|
|
|
- e = e max pos.end
|
|
|
- }
|
|
|
- }) {
|
|
|
- // in `@nowarn @ann(deprecatedMethod) def foo`, the deprecation warning should show
|
|
|
- override def traverseModifiers(mods: Modifiers): Unit = ()
|
|
|
- }
|
|
|
- setRange(annotee.get)
|
|
|
- (s, e max s)
|
|
|
- }
|
|
|
- def registerNowarn(info: AnnotationInfo): Unit = {
|
|
|
- if (annotee.isDefined && NowarnClass.exists && info.matches(NowarnClass) && !runReporting.suppressionExists(info.pos)) {
|
|
|
- val filters = (info.assocs: @unchecked) match {
|
|
|
- case Nil => List(MessageFilter.Any)
|
|
|
- case (_, LiteralAnnotArg(s)) :: Nil =>
|
|
|
- if (s.stringValue.isEmpty) Nil
|
|
|
- else {
|
|
|
- val (ms, fs) = s.stringValue.split('&').map(WConf.parseFilter(_, runReporting.rootDirPrefix)).toList.partitionMap(identity)
|
|
|
- if (ms.nonEmpty)
|
|
|
- reporter.error(info.pos, s"Invalid message filter:\n${ms.mkString("\n")}")
|
|
|
- fs
|
|
|
- }
|
|
|
- }
|
|
|
- val (start, end) = rangeFinder()
|
|
|
- runReporting.addSuppression(Suppression(info.pos, filters, start, end))
|
|
|
- }
|
|
|
- }
|
|
|
- def registerDeprecationSuppression(info: AnnotationInfo): Unit =
|
|
|
- if (annotee.isDefined && info.matches(DeprecatedAttr) && !runReporting.suppressionExists(info.pos)) {
|
|
|
- val (start, end) = rangeFinder()
|
|
|
- runReporting.addSuppression(Suppression(info.pos, List(MessageFilter.Category(WarningCategory.Deprecation)), start, end, synthetic = true))
|
|
|
- }
|
|
|
-
|
|
|
- def finish(res: AnnotationInfo): AnnotationInfo = {
|
|
|
- if (hasError) {
|
|
|
- pending.foreach(ErrorUtils.issueTypeError)
|
|
|
- ErroneousAnnotation
|
|
|
- } else {
|
|
|
- registerNowarn(res)
|
|
|
- registerDeprecationSuppression(res)
|
|
|
- res
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def reportAnnotationError(err: AbsTypeError) = {
|
|
|
- pending += err
|
|
|
- hasError = true
|
|
|
- ErroneousAnnotation
|
|
|
- }
|
|
|
-
|
|
|
- // begin typedAnnotation
|
|
|
- val treeInfo.Applied(fun0, _, argss) = ann
|
|
|
- if (fun0.isErroneous) return finish(ErroneousAnnotation)
|
|
|
- val typedFun = context.withinAnnotation(typed(fun0, mode.forFunMode))
|
|
|
- if (typedFun.isErroneous) return finish(ErroneousAnnotation)
|
|
|
-
|
|
|
- val Select(New(annTpt), _) = typedFun: @unchecked
|
|
|
- val annType = annTpt.tpe // for a polymorphic annotation class, this type will have unbound type params (see context.undetparams)
|
|
|
- val annTypeSym = annType.typeSymbol
|
|
|
- val isJava = annTypeSym.isJavaDefined
|
|
|
-
|
|
|
- val isAnnotation = annTypeSym.isJavaAnnotation || annType <:< AnnotationClass.tpe
|
|
|
- if (!isAnnotation) {
|
|
|
- reportAnnotationError(DoesNotExtendAnnotation(typedFun, annTypeSym))
|
|
|
- return finish(ErroneousAnnotation)
|
|
|
- }
|
|
|
-
|
|
|
- /* Calling constfold right here is necessary because some trees (negated
|
|
|
- * floats and literals in particular) are not yet folded.
|
|
|
- */
|
|
|
- def tryConst(tr: Tree, pt: Type): Option[LiteralAnnotArg] = {
|
|
|
- // The typed tree may be relevantly different than the tree `tr`,
|
|
|
- // e.g. it may have encountered an implicit conversion.
|
|
|
- val ttree = if (isJava) typed(constfold(tr, context.owner), pt) else tr
|
|
|
- val const: Constant = ttree match {
|
|
|
- case l @ Literal(c) if !l.isErroneous => c
|
|
|
- case tree => tree.tpe match {
|
|
|
- case ConstantType(c) => c
|
|
|
- case _ => null
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def isDefaultArg(tree: Tree) = tree match {
|
|
|
- case treeInfo.Applied(fun, _, _) => fun.symbol != null && fun.symbol.isDefaultGetter
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- if (const == null) {
|
|
|
- if (unit.isJava) unmappable = true
|
|
|
- else if (!isDefaultArg(ttree)) reportAnnotationError(AnnotationNotAConstantError(ttree))
|
|
|
- None
|
|
|
- } else if (const.value == null) {
|
|
|
- reportAnnotationError(AnnotationArgNullError(tr)); None
|
|
|
- } else
|
|
|
- Some(LiteralAnnotArg(const))
|
|
|
- }
|
|
|
-
|
|
|
- /* Converts an untyped tree to a ClassfileAnnotArg. If the conversion fails,
|
|
|
- * an error message is reported and None is returned.
|
|
|
- */
|
|
|
- @tailrec
|
|
|
- def tree2ConstArg(tree: Tree, pt: Type): Option[ClassfileAnnotArg] = tree match {
|
|
|
- case Apply(Select(New(_), nme.CONSTRUCTOR), _) if pt.typeSymbol == ArrayClass && unit.isJava =>
|
|
|
- // In Java, a single value may be passed for array annotation parameters
|
|
|
- tree2ConstArg(Apply(Select(gen.mkAttributedRef(ArrayModule), nme.apply), List(tree)), pt)
|
|
|
-
|
|
|
- case Apply(Select(New(_), nme.CONSTRUCTOR), _) if pt.typeSymbol == ArrayClass =>
|
|
|
- reportAnnotationError(ArrayConstantsError(tree)); None
|
|
|
-
|
|
|
- case ann @ Apply(Select(New(tpt), nme.CONSTRUCTOR), _) if isJava =>
|
|
|
- val annInfo = typedAnnotation(ann, None, mode)
|
|
|
- val annType = annInfo.atp
|
|
|
-
|
|
|
- if (!annType.typeSymbol.isSubClass(pt.typeSymbol))
|
|
|
- reportAnnotationError(AnnotationTypeMismatchError(tpt, pt, annType))
|
|
|
- else if (!annType.typeSymbol.isJavaDefined)
|
|
|
- reportAnnotationError(NestedAnnotationError(ann, annType))
|
|
|
-
|
|
|
- if (annInfo.atp.isErroneous) { hasError = true; None }
|
|
|
- else Some(NestedAnnotArg(annInfo))
|
|
|
-
|
|
|
- // use of Array.apply[T: ClassTag](xs: T*): Array[T]
|
|
|
- // and Array.apply(x: Int, xs: Int*): Array[Int] (and similar)
|
|
|
- case treeInfo.Applied(fun, targs, args :: _) =>
|
|
|
- val typedFun = if (isJava) typed(fun, mode.forFunMode) else fun
|
|
|
- if (typedFun.symbol.owner == ArrayModule.moduleClass && typedFun.symbol.name == nme.apply)
|
|
|
- pt match {
|
|
|
- case _ if !isJava =>
|
|
|
- trees2ConstArg(args, targs.headOption.map(_.tpe).getOrElse(WildcardType))
|
|
|
- case TypeRef(_, ArrayClass, targ :: _) =>
|
|
|
- trees2ConstArg(args, targ)
|
|
|
- case _ =>
|
|
|
- // For classfile annotations, pt can only be T:
|
|
|
- // BT = Int, .., String, Class[_], JavaAnnotClass
|
|
|
- // T = BT | Array[BT]
|
|
|
- // So an array literal as argument can only be valid if pt is Array[_]
|
|
|
- reportAnnotationError(ArrayConstantsTypeMismatchError(tree, pt))
|
|
|
- None
|
|
|
- }
|
|
|
- else tryConst(tree, pt)
|
|
|
-
|
|
|
- case Typed(t, _) =>
|
|
|
- tree2ConstArg(t, pt)
|
|
|
-
|
|
|
- case tree if unit.isJava && pt.typeSymbol == ArrayClass =>
|
|
|
- /* If we get here, we have a Java array annotation argument which was passed
|
|
|
- * as a single value, and needs to be wrapped. */
|
|
|
- trees2ConstArg(tree :: Nil, pt.typeArgs.head)
|
|
|
-
|
|
|
- case tree =>
|
|
|
- tryConst(tree, pt)
|
|
|
- }
|
|
|
- def trees2ConstArg(trees: List[Tree], pt: Type): Option[ArrayAnnotArg] = {
|
|
|
- traverseOpt(trees)(tree2ConstArg(_, pt))
|
|
|
- .map(args => ArrayAnnotArg(args.toArray))
|
|
|
- }
|
|
|
-
|
|
|
- @inline def constantly = {
|
|
|
- // Arguments of Java annotations and ConstantAnnotations are checked to be constants and
|
|
|
- // stored in the `assocs` field of the resulting AnnotationInfo
|
|
|
- if (argss.lengthIs > 1) {
|
|
|
- reportAnnotationError(MultipleArgumentListForAnnotationError(ann))
|
|
|
- } else {
|
|
|
- val annScopeJava = annType.decls.filter(sym => sym.isMethod && !sym.isConstructor && sym.isJavaDefined)
|
|
|
-
|
|
|
- val names = mutable.Set[Symbol]()
|
|
|
- names ++= annScopeJava.iterator
|
|
|
-
|
|
|
- def hasValue = names exists (_.name == nme.value)
|
|
|
- val namedArgs = argss match {
|
|
|
- case List(List(arg)) if !isNamedArg(arg) && hasValue => gen.mkNamedArg(nme.value, arg) :: Nil
|
|
|
- case List(args) => args
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
-
|
|
|
- val nvPairs = namedArgs map {
|
|
|
- case arg @ NamedArg(Ident(name), rhs) =>
|
|
|
- val sym = annScopeJava.lookup(name)
|
|
|
- if (sym == NoSymbol) {
|
|
|
- reportAnnotationError(UnknownAnnotationNameError(arg, name))
|
|
|
- (nme.ERROR, None)
|
|
|
- } else if (!names.contains(sym)) {
|
|
|
- reportAnnotationError(DuplicateValueAnnotationError(arg, name))
|
|
|
- (nme.ERROR, None)
|
|
|
- } else {
|
|
|
- names -= sym
|
|
|
- sym.cookJavaRawInfo() // #3429
|
|
|
- val annArg = tree2ConstArg(rhs, sym.tpe.resultType)
|
|
|
- (sym.name, annArg)
|
|
|
- }
|
|
|
- case arg =>
|
|
|
- reportAnnotationError(ClassfileAnnotationsAsNamedArgsError(arg))
|
|
|
- (nme.ERROR, None)
|
|
|
- }
|
|
|
- for (sym <- names) {
|
|
|
- // make sure the flags are up to date before erroring (jvm/t3415 fails otherwise)
|
|
|
- sym.initialize
|
|
|
- if (!sym.hasAnnotation(AnnotationDefaultAttr) && !sym.hasDefault)
|
|
|
- reportAnnotationError(AnnotationMissingArgError(ann, annType, sym))
|
|
|
- }
|
|
|
-
|
|
|
- if (hasError) ErroneousAnnotation
|
|
|
- else if (unmappable) UnmappableAnnotation
|
|
|
- else AnnotationInfo(annType, Nil, nvPairs.map(p => (p._1, p._2.get))).setOriginal(Apply(typedFun, namedArgs).setPos(ann.pos))
|
|
|
- }
|
|
|
- }
|
|
|
- @inline def statically = {
|
|
|
- val typedAnn: Tree = {
|
|
|
- // local dummy fixes scala/bug#5544
|
|
|
- val localTyper = newTyper(context.make(ann, context.owner.newLocalDummy(ann.pos)))
|
|
|
- localTyper.typed(ann, mode)
|
|
|
- }
|
|
|
- @tailrec
|
|
|
- def annInfo(t: Tree): AnnotationInfo = t match {
|
|
|
- case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
|
|
|
- // `tpt.tpe` is more precise than `annType`, since it incorporates the types of `args`
|
|
|
- AnnotationInfo(tpt.tpe, args, Nil).setOriginal(typedAnn).setPos(t.pos)
|
|
|
-
|
|
|
- case Block(_, expr) =>
|
|
|
- if (!annTypeSym.isNonBottomSubClass(ConstantAnnotationClass))
|
|
|
- context.warning(t.pos, "Usage of named or default arguments transformed this annotation\n"+
|
|
|
- "constructor call into a block. The corresponding AnnotationInfo\n"+
|
|
|
- "will contain references to local values and default getters instead\n"+
|
|
|
- "of the actual argument trees", WarningCategory.Other)
|
|
|
- annInfo(expr)
|
|
|
-
|
|
|
- case Apply(fun, args) =>
|
|
|
- context.warning(t.pos, "Implementation limitation: multiple argument lists on annotations are\n"+
|
|
|
- "currently not supported; ignoring arguments "+ args, WarningCategory.Other)
|
|
|
- annInfo(fun)
|
|
|
-
|
|
|
- case _ =>
|
|
|
- reportAnnotationError(UnexpectedTreeAnnotationError(t, typedAnn))
|
|
|
- }
|
|
|
-
|
|
|
- if ((typedAnn.tpe == null) || typedAnn.tpe.isErroneous) ErroneousAnnotation
|
|
|
- else {
|
|
|
- val info = annInfo(typedAnn)
|
|
|
- // check message and since args to deprecated
|
|
|
- // when deprecated gets more params, those other args may also be defaults.
|
|
|
- def usesDefault = typedAnn match {
|
|
|
- case Block(vals, _) => info.args.exists {
|
|
|
- case Ident(n) => vals.exists {
|
|
|
- case ValDef(_, `n`, _, rhs) => treeInfo.isDefaultGetter(rhs)
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- false
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- info.args.exists(treeInfo.isDefaultGetter)
|
|
|
- }
|
|
|
- if (annTypeSym == DeprecatedAttr && settings.lintDeprecation && argss.head.lengthIs < 2 && usesDefault)
|
|
|
- context.warning(ann.pos, """Specify both message and version: @deprecated("message", since = "1.0")""", WarningCategory.LintDeprecation)
|
|
|
- info
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- finish {
|
|
|
- if (isJava)
|
|
|
- constantly
|
|
|
- else {
|
|
|
- val info = statically
|
|
|
- if (!info.isErroneous && annTypeSym.isNonBottomSubClass(ConstantAnnotationClass)) {
|
|
|
- var namedArgs: Map[Name, Tree] = Map.empty
|
|
|
- val treeInfo.Applied(constr, _, _) = info.original match {
|
|
|
- case Block(stats, call) =>
|
|
|
- // when named / default args are used
|
|
|
- namedArgs = Map.from(stats collect {
|
|
|
- case ValDef(_, name, _, rhs) => (name, rhs)
|
|
|
- })
|
|
|
- call
|
|
|
- case call => call
|
|
|
- }
|
|
|
- val params = constr.symbol.paramss.headOption.getOrElse(Nil)
|
|
|
- val assocs = info.args.zip(params) map {
|
|
|
- case (arg, param) =>
|
|
|
- val origArg = arg match {
|
|
|
- case Ident(n) => namedArgs.getOrElse(n, arg)
|
|
|
- case _ => arg
|
|
|
- }
|
|
|
- (param.name, tree2ConstArg(origArg, param.tpe.resultType))
|
|
|
- }
|
|
|
- if (hasError) ErroneousAnnotation
|
|
|
- else if (unmappable) UnmappableAnnotation
|
|
|
- else AnnotationInfo(info.atp, Nil, assocs.collect { case (n, Some(arg)) => (n, arg) }).setOriginal(info.original).setPos(info.pos)
|
|
|
- } else
|
|
|
- info
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedMacroAnnotation(cdef: ClassDef) = {
|
|
|
- val clazz = cdef.symbol
|
|
|
- if (!isPastTyper) {
|
|
|
- if (clazz != null && (clazz isNonBottomSubClass AnnotationClass)) {
|
|
|
- val macroTransform = clazz.info.member(nme.macroTransform)
|
|
|
- if (macroTransform != NoSymbol) {
|
|
|
- clazz.setFlag(MACRO)
|
|
|
- if (clazz.getAnnotation(CompileTimeOnlyAttr).isEmpty) clazz.addAnnotation(AnnotationInfo(CompileTimeOnlyAttr.tpe, List(Literal(Constant(MacroAnnotationNotExpandedMessage)) setType StringClass.tpe), Nil))
|
|
|
- def flavorOk = macroTransform.isMacro
|
|
|
- def paramssOk = mmap(macroTransform.paramss)(p => (p.name, p.info)) == List(List((nme.annottees, scalaRepeatedType(AnyTpe))))
|
|
|
- def tparamsOk = macroTransform.typeParams.isEmpty
|
|
|
- def everythingOk = flavorOk && paramssOk && tparamsOk
|
|
|
- if (!everythingOk) MacroAnnotationShapeError(clazz)
|
|
|
- if (!(clazz isNonBottomSubClass StaticAnnotationClass)) MacroAnnotationMustBeStaticError(clazz)
|
|
|
- // TODO: revisit the decision about @Inherited
|
|
|
- if (clazz.hasAnnotation(InheritedAttr)) MacroAnnotationCannotBeInheritedError(clazz)
|
|
|
- if (!clazz.isStatic) MacroAnnotationCannotBeMemberError(clazz)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- cdef
|
|
|
- }
|
|
|
-
|
|
|
- /** Compute an existential type from raw hidden symbols `syms` and type `tp`
|
|
|
- */
|
|
|
- def packSymbols(hidden: List[Symbol], tp: Type): Type = global.packSymbols(hidden, tp, context.owner)
|
|
|
-
|
|
|
- def isReferencedFrom(ctx: Context, sym: Symbol): Boolean =
|
|
|
- ctx.owner.isTerm && ctx.scope.exists(dcl => dcl.isInitialized && dcl.info.contains(sym)) || {
|
|
|
- var ctx1 = ctx.outer
|
|
|
- while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope))
|
|
|
- ctx1 = ctx1.outer
|
|
|
-
|
|
|
- (ctx1 != NoContext) && isReferencedFrom(ctx1, sym)
|
|
|
- }
|
|
|
-
|
|
|
- def isCapturedExistential(sym: Symbol) =
|
|
|
- (sym hasAllFlags EXISTENTIAL | CAPTURED) && {
|
|
|
- val start = if (settings.areStatisticsEnabled) statistics.startTimer(isReferencedNanos) else null
|
|
|
- try !isReferencedFrom(context, sym)
|
|
|
- finally if (settings.areStatisticsEnabled) statistics.stopTimer(isReferencedNanos, start)
|
|
|
- }
|
|
|
-
|
|
|
- def packCaptured(tpe: Type): Type = {
|
|
|
- val captured = mutable.Set[Symbol]()
|
|
|
- for (tp <- tpe)
|
|
|
- if (isCapturedExistential(tp.typeSymbol))
|
|
|
- captured += tp.typeSymbol
|
|
|
- existentialAbstraction(captured.toList, tpe)
|
|
|
- }
|
|
|
-
|
|
|
- /** convert local symbols and skolems to existentials */
|
|
|
- def packedType(tree: Tree, owner: Symbol): Type = {
|
|
|
- def defines(tree: Tree, sym: Symbol) = (
|
|
|
- sym.isExistentialSkolem && sym.unpackLocation == tree
|
|
|
- || tree.isDef && tree.symbol == sym
|
|
|
- )
|
|
|
- def isVisibleParameter(sym: Symbol) = (
|
|
|
- sym.isParameter
|
|
|
- && (sym.owner == owner)
|
|
|
- && (sym.isType || !owner.isAnonymousFunction)
|
|
|
- )
|
|
|
- def containsDef(owner: Symbol, sym: Symbol): Boolean =
|
|
|
- (!sym.hasPackageFlag) && {
|
|
|
- var o = sym.owner
|
|
|
- while (o != owner && o != NoSymbol && !o.hasPackageFlag) o = o.owner
|
|
|
- o == owner && !isVisibleParameter(sym)
|
|
|
- }
|
|
|
- val localSyms = mutable.LinkedHashSet[Symbol]()
|
|
|
- val boundSyms = mutable.LinkedHashSet[Symbol]()
|
|
|
- def isLocal(sym: Symbol): Boolean =
|
|
|
- if (sym == NoSymbol || sym.isRefinementClass || sym.isLocalDummy) false
|
|
|
- else if (owner == NoSymbol) tree exists (defines(_, sym))
|
|
|
- else containsDef(owner, sym) || isRawParameter(sym) || isCapturedExistential(sym)
|
|
|
- def containsLocal(tp: Type): Boolean =
|
|
|
- tp exists (t => isLocal(t.typeSymbol) || isLocal(t.termSymbol))
|
|
|
-
|
|
|
- val dealiasLocals = new TypeMap {
|
|
|
- @tailrec
|
|
|
- def apply(tp: Type): Type = tp match {
|
|
|
- case TypeRef(pre, sym, _) =>
|
|
|
- if (sym.isAliasType && containsLocal(tp) && (tp.dealias ne tp)) apply(tp.dealias)
|
|
|
- else {
|
|
|
- if (pre.isVolatile) pre match {
|
|
|
- case SingleType(_, sym) if sym.isSynthetic && isPastTyper =>
|
|
|
- debuglog(s"ignoring volatility of prefix in pattern matcher generated inferred type: $tp") // See pos/t7459c.scala
|
|
|
- case _ =>
|
|
|
- InferTypeWithVolatileTypeSelectionError(tree, pre)
|
|
|
- }
|
|
|
- mapOver(tp)
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- mapOver(tp)
|
|
|
- }
|
|
|
- }
|
|
|
- // add all local symbols of `tp` to `localSyms`
|
|
|
- // TODO: expand higher-kinded types into individual copies for each instance.
|
|
|
- def addLocals(tp: Type): Unit = {
|
|
|
- val remainingSyms = new ListBuffer[Symbol]
|
|
|
- def addIfLocal(sym: Symbol, tp: Type): Unit = {
|
|
|
- if (isLocal(sym) && !localSyms(sym) && !boundSyms(sym)) {
|
|
|
- if (sym.typeParams.isEmpty) {
|
|
|
- localSyms += sym
|
|
|
- remainingSyms += sym
|
|
|
- } else {
|
|
|
- AbstractExistentiallyOverParamerizedTpeError(tree, tp)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (t <- tp) {
|
|
|
- t match {
|
|
|
- case ExistentialType(tparams, _) =>
|
|
|
- boundSyms ++= tparams
|
|
|
- case AnnotatedType(annots, _) =>
|
|
|
- for (annot <- annots; arg <- annot.args) {
|
|
|
- arg match {
|
|
|
- case Ident(_) =>
|
|
|
- // Check the symbol of an Ident, unless the
|
|
|
- // Ident's type is already over an existential.
|
|
|
- // (If the type is already over an existential,
|
|
|
- // then remap the type, not the core symbol.)
|
|
|
- if (!arg.tpe.typeSymbol.hasFlag(EXISTENTIAL))
|
|
|
- addIfLocal(arg.symbol, arg.tpe)
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- addIfLocal(t.termSymbol, t)
|
|
|
- addIfLocal(t.typeSymbol, t)
|
|
|
- }
|
|
|
- for (sym <- remainingSyms) addLocals(sym.existentialBound)
|
|
|
- }
|
|
|
-
|
|
|
- val dealiasedType = dealiasLocals(tree.tpe)
|
|
|
- addLocals(dealiasedType)
|
|
|
- packSymbols(localSyms.toList, dealiasedType)
|
|
|
- }
|
|
|
-
|
|
|
- def typedClassOf(tree: Tree, tpt: Tree, noGen: Boolean = false) =
|
|
|
- if (!checkClassOrModuleType(tpt) && noGen) tpt
|
|
|
- else atPos(tree.pos)(gen.mkClassOf(tpt.tpe))
|
|
|
-
|
|
|
- protected def typedExistentialTypeTree(tree: ExistentialTypeTree, mode: Mode): Tree = {
|
|
|
- for (wc <- tree.whereClauses)
|
|
|
- if (wc.symbol == NoSymbol) { namer enterSym wc; wc.symbol setFlag EXISTENTIAL }
|
|
|
- else context.scope enter wc.symbol
|
|
|
- val whereClauses1 = typedStats(tree.whereClauses, context.owner)
|
|
|
- for (vd @ ValDef(_, _, _, _) <- whereClauses1)
|
|
|
- if (vd.symbol.tpe.isVolatile)
|
|
|
- AbstractionFromVolatileTypeError(vd)
|
|
|
- val tpt1 = typedType(tree.tpt, mode)
|
|
|
- existentialTransform(whereClauses1 map (_.symbol), tpt1.tpe)((tparams, tp) => {
|
|
|
- val original = tpt1 match {
|
|
|
- case tpt : TypeTree => atPos(tree.pos)(ExistentialTypeTree(tpt.original, tree.whereClauses))
|
|
|
- case _ =>
|
|
|
- debuglog(s"cannot reconstruct the original for $tree, because $tpt1 is not a TypeTree")
|
|
|
- tree
|
|
|
- }
|
|
|
- TypeTree(newExistentialType(tparams, tp)) setOriginal original
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- // lifted out of typed1 because it's needed in typedImplicit0
|
|
|
- protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match {
|
|
|
- case OverloadedType(_, _) =>
|
|
|
- inferPolyAlternatives(fun, mapList(args)(_.tpe))
|
|
|
-
|
|
|
- // scala/bug#8267 `memberType` can introduce existentials *around* a PolyType/MethodType, see AsSeenFromMap#captureThis.
|
|
|
- // If we had selected a non-overloaded symbol, `memberType` would have been called in `makeAccessible`
|
|
|
- // and the resulting existential type would have been skolemized in `adapt` *before* we typechecked
|
|
|
- // the enclosing type-/ value- application.
|
|
|
- //
|
|
|
- // However, if the selection is overloaded, we defer calling `memberType` until we can select a single
|
|
|
- // alternative here. It is therefore necessary to skolemize the existential here.
|
|
|
- //
|
|
|
- val fun1 = adaptAfterOverloadResolution(fun, mode.forFunMode | TAPPmode)
|
|
|
-
|
|
|
- val tparams = fun1.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
|
|
|
- val args1 = if (sameLength(args, tparams)) {
|
|
|
- //@M: in case TypeApply we can't check the kind-arities of the type arguments,
|
|
|
- // as we don't know which alternative to choose... here we do
|
|
|
- map2Conserve(args, tparams) {
|
|
|
- //@M! the polytype denotes the expected kind
|
|
|
- (arg, tparam) => typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams))
|
|
|
- }
|
|
|
- } else // @M: there's probably something wrong when args.length != tparams.length... (triggered by bug #320)
|
|
|
- // Martin, I'm using fake trees, because, if you use args or arg.map(typedType),
|
|
|
- // inferPolyAlternatives loops... -- I have no idea why :-(
|
|
|
- // ...actually this was looping anyway, see bug #278.
|
|
|
- return TypedApplyWrongNumberOfTpeParametersError(fun, fun)
|
|
|
-
|
|
|
- typedTypeApply(tree, mode, fun1, args1)
|
|
|
- case SingleType(_, _) =>
|
|
|
- typedTypeApply(tree, mode, fun setType fun.tpe.widen, args)
|
|
|
- case PolyType(tparams, restpe) if tparams.nonEmpty =>
|
|
|
- if (sameLength(tparams, args)) {
|
|
|
- val isJava = context.unit.isJava
|
|
|
- val isClassOf = fun.symbol.rawname == nme.classOf && currentRun.runDefinitions.isPredefClassOf(fun.symbol)
|
|
|
- if (isJava && fun.symbol.isTerm) args.foreach(_.modifyType(rawToExistential)) // e.g. List.class, parsed as classOf[List]
|
|
|
- val targs = mapList(args)(_.tpe)
|
|
|
- checkBounds(tree, NoPrefix, NoSymbol, tparams, targs, "")
|
|
|
- if (isClassOf)
|
|
|
- typedClassOf(tree, args.head, noGen = true)
|
|
|
- else {
|
|
|
- if (!isPastTyper && fun.symbol == Any_isInstanceOf && targs.nonEmpty) {
|
|
|
- val scrutineeType = fun match {
|
|
|
- case Select(qual, _) => qual.tpe
|
|
|
- case _ => AnyTpe
|
|
|
- }
|
|
|
- checkCheckable(tree, targs.head, scrutineeType, inPattern = false)
|
|
|
- }
|
|
|
- val resultpe = restpe.instantiateTypeParams(tparams, targs)
|
|
|
- //@M substitution in instantiateParams needs to be careful!
|
|
|
- //@M example: class Foo[a] { def foo[m[x]]: m[a] = error("") } (new Foo[Int]).foo[List] : List[Int]
|
|
|
- //@M --> first, m[a] gets changed to m[Int], then m gets substituted for List,
|
|
|
- // this must preserve m's type argument, so that we end up with List[Int], and not List[a]
|
|
|
- //@M related bug: #1438
|
|
|
- //println("instantiating type params "+restpe+" "+tparams+" "+targs+" = "+resultpe)
|
|
|
- treeCopy.TypeApply(tree, fun, args) setType resultpe
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- TypedApplyWrongNumberOfTpeParametersError(tree, fun)
|
|
|
- }
|
|
|
- case ErrorType =>
|
|
|
- setError(treeCopy.TypeApply(tree, fun, args))
|
|
|
- case _ =>
|
|
|
- fun match {
|
|
|
- // drop the application for an applyDynamic or selectDynamic call since it has been pushed down
|
|
|
- case treeInfo.DynamicApplication(_, _) => fun
|
|
|
- case _ => TypedApplyDoesNotTakeTpeParametersError(tree, fun)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //
|
|
|
- // START: applyDynamic support
|
|
|
- //
|
|
|
- import treeInfo.{isApplyDynamicName, DynamicUpdate, DynamicApplicationNamed}
|
|
|
-
|
|
|
- private def acceptsApplyDynamic(tp: Type) = tp.typeSymbol isNonBottomSubClass DynamicClass
|
|
|
-
|
|
|
- /** Returns `true` if `name` can be selected dynamically on `qual`, `false` if not. */
|
|
|
- private def acceptsApplyDynamicWithType(qual: Tree, name: Name): Boolean =
|
|
|
- // don't selectDynamic selectDynamic, do select dynamic at unknown type
|
|
|
- !isApplyDynamicName(name) && acceptsApplyDynamic(qual.tpe.widen)
|
|
|
-
|
|
|
- // if the qualifier is a Dynamic, that's all we need to know
|
|
|
- private def isDynamicallyUpdatable(tree: Tree) = tree match {
|
|
|
- case DynamicUpdate(qual, _) => acceptsApplyDynamic(qual.tpe)
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- private def isApplyDynamicNamed(fun: Tree): Boolean = fun match {
|
|
|
- case DynamicApplicationNamed(qual, _) => acceptsApplyDynamic(qual.tpe.widen)
|
|
|
- case _ => false
|
|
|
- // look deeper?
|
|
|
- // val treeInfo.Applied(methPart, _, _) = fun
|
|
|
- // println("methPart of "+ fun +" is "+ methPart)
|
|
|
- // if (methPart ne fun) isApplyDynamicNamed(methPart)
|
|
|
- // else false
|
|
|
- }
|
|
|
-
|
|
|
- private def typedNamedApply(orig: Tree, fun: Tree, args: List[Tree], mode: Mode, pt: Type): Tree = {
|
|
|
- def argToBinding(arg: Tree): Tree = arg match {
|
|
|
- case NamedArg(i @ Ident(name), rhs) =>
|
|
|
- atPos(i.pos.withEnd(rhs.pos.end)) {
|
|
|
- gen.mkTuple(List(atPos(i.pos)(CODE.LIT(name.toString)), rhs))
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- gen.mkTuple(List(CODE.LIT(""), arg))
|
|
|
- }
|
|
|
-
|
|
|
- val t = treeCopy.Apply(orig, unmarkDynamicRewrite(fun), args map argToBinding)
|
|
|
- wrapErrors(t, _.typed(t, mode, pt))
|
|
|
- }
|
|
|
-
|
|
|
- /** Translate selection that does not typecheck according to the normal rules into a selectDynamic/applyDynamic.
|
|
|
- *
|
|
|
- * foo.method("blah") ~~> foo.applyDynamic("method")("blah")
|
|
|
- * foo.method(x = "blah") ~~> foo.applyDynamicNamed("method")(("x", "blah"))
|
|
|
- * foo.varia = 10 ~~> foo.updateDynamic("varia")(10)
|
|
|
- * foo.field ~~> foo.selectDynamic("field")
|
|
|
- * foo.arr(10) = 13 ~~> foo.selectDynamic("arr").update(10, 13)
|
|
|
- *
|
|
|
- * what if we want foo.field == foo.selectDynamic("field") == 1, but `foo.field = 10` == `foo.selectDynamic("field").update(10)` == ()
|
|
|
- * what would the signature for selectDynamic be? (hint: it needs to depend on whether an update call is coming or not)
|
|
|
- *
|
|
|
- * need to distinguish selectDynamic and applyDynamic somehow: the former must return the selected value, the latter must accept an apply or an update
|
|
|
- * - could have only selectDynamic and pass it a boolean whether more is to come,
|
|
|
- * so that it can either return the bare value or something that can handle the apply/update
|
|
|
- * HOWEVER that makes it hard to return unrelated values for the two cases
|
|
|
- * --> selectDynamic's return type is now dependent on the boolean flag whether more is to come
|
|
|
- * - simplest solution: have two method calls
|
|
|
- *
|
|
|
- */
|
|
|
- private def mkInvoke(context: Context, tree: Tree, qual: Tree, name: Name): Option[Tree] = {
|
|
|
- val cxTree = context.enclosingNonImportContext.tree // scala/bug#8364
|
|
|
- debuglog(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)")
|
|
|
- val treeInfo.Applied(treeSelection, _, _) = tree
|
|
|
- def isDesugaredApply = {
|
|
|
- val protoQual = macroExpandee(qual) orElse qual
|
|
|
- treeSelection match {
|
|
|
- case Select(`protoQual`, nme.apply) => true
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Option.when(acceptsApplyDynamicWithType(qual, name)) {
|
|
|
- def matches(t: Tree) = isDesugaredApply || treeInfo.dissectCore(t) == treeSelection
|
|
|
-
|
|
|
- /* Note that the trees which arrive here are potentially some distance from
|
|
|
- * the trees of direct interest. `cxTree` is some enclosing expression which
|
|
|
- * may apparently be arbitrarily larger than `tree`; and `tree` itself is
|
|
|
- * too small, having at least in some cases lost its explicit type parameters.
|
|
|
- * This logic is designed to use `tree` to pinpoint the immediately surrounding
|
|
|
- * Apply/TypeApply/Select node, and only then creates the dynamic call.
|
|
|
- * See scala/bug#6731 among others.
|
|
|
- */
|
|
|
- def findSelection(t: Tree): Option[(TermName, Tree)] = t match {
|
|
|
- case Apply(fn, args) if matches(fn) =>
|
|
|
- val op = if(args.exists(_.isInstanceOf[NamedArg])) nme.applyDynamicNamed else nme.applyDynamic
|
|
|
- // not supported: foo.bar(a1,..., an: _*)
|
|
|
- val fn1 = if(treeInfo.isWildcardStarArgList(args)) DynamicVarArgUnsupported(fn, op) else fn
|
|
|
- Some((op, fn1))
|
|
|
- case Assign(lhs, _) if matches(lhs) => Some((nme.updateDynamic, lhs))
|
|
|
- case _ if matches(t) => Some((nme.selectDynamic, t))
|
|
|
- case _ => t.children.flatMap(findSelection).headOption
|
|
|
- }
|
|
|
- findSelection(cxTree) map { case (opName, treeInfo.Applied(_, targs, _)) =>
|
|
|
- val fun = atPos(wrappingPos(qual :: targs)) {
|
|
|
- gen.mkTypeApply(Select(qual, opName) setPos qual.pos, targs)
|
|
|
- }
|
|
|
- if (opName == nme.updateDynamic) suppressMacroExpansion(fun) // scala/bug#7617
|
|
|
- val nameStringLit = atPos(treeSelection.pos.withStart(treeSelection.pos.point).makeTransparent) {
|
|
|
- Literal(Constant(name.decode))
|
|
|
- }
|
|
|
- markDynamicRewrite {
|
|
|
- atPos(wrappingPos(qual :: fun :: nameStringLit :: Nil)) {
|
|
|
- Apply(fun, List(nameStringLit))
|
|
|
- }
|
|
|
- }
|
|
|
- } getOrElse {
|
|
|
- // While there may be an error in the found tree itself, it should not be possible to *not find* it at all.
|
|
|
- devWarning(s"Tree $tree not found in the context $cxTree while trying to do a dynamic application")
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- private def wrapErrors(tree: Tree, typeTree: Typer => Tree): Tree = silent(typeTree) orElse (err => DynamicRewriteError(tree, err.head))
|
|
|
- //
|
|
|
- // END: applyDynamic support
|
|
|
- //
|
|
|
-
|
|
|
- def typed1(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- // Lookup in the given class using the root mirror.
|
|
|
- def lookupInOwner(owner: Symbol, name: Name): Symbol =
|
|
|
- if (mode.inQualMode) rootMirror.missingHook(owner, name) else NoSymbol
|
|
|
-
|
|
|
- // Lookup in the given qualifier. Used in last-ditch efforts by typedIdent and typedSelect.
|
|
|
- def lookupInRoot(name: Name): Symbol = lookupInOwner(rootMirror.RootClass, name)
|
|
|
- def lookupInEmpty(name: Name): Symbol = rootMirror.EmptyPackageClass.info member name
|
|
|
-
|
|
|
- def lookupInQualifier(qual: Tree, name: Name): Symbol =
|
|
|
- if (name == nme.ERROR || qual.tpe.widen.isErroneous)
|
|
|
- NoSymbol
|
|
|
- else lookupInOwner(qual.tpe.typeSymbol, name) orElse {
|
|
|
- NotAMemberError(tree, qual, name, startingIdentContext)
|
|
|
- NoSymbol
|
|
|
- }
|
|
|
-
|
|
|
- def startingIdentContext =
|
|
|
- // ignore current variable scope in patterns to enforce linearity
|
|
|
- if (mode.inNone(PATTERNmode | TYPEPATmode)) context
|
|
|
- else context.outer
|
|
|
-
|
|
|
- def typedAnnotated(atd: Annotated): Tree = {
|
|
|
- val ann = atd.annot
|
|
|
- val arg1 = typed(atd.arg, mode, pt)
|
|
|
- /* mode for typing the annotation itself */
|
|
|
- val annotMode = (mode &~ TYPEmode) | EXPRmode
|
|
|
-
|
|
|
- def resultingTypeTree(tpe: Type) = {
|
|
|
- // we need symbol-ful originals for reification
|
|
|
- // hence we go the extra mile to hand-craft this guy
|
|
|
- val original = arg1 match {
|
|
|
- case tt @ TypeTree() if tt.original != null => Annotated(ann, tt.original)
|
|
|
- // this clause is needed to correctly compile stuff like "new C @D" or "@(inline @getter)"
|
|
|
- case _ => Annotated(ann, arg1)
|
|
|
- }
|
|
|
- original setType ann.tpe
|
|
|
- TypeTree(tpe) setOriginal original setPos tree.pos.focus
|
|
|
- }
|
|
|
-
|
|
|
- if (arg1.isType) {
|
|
|
- // make sure the annotation is only typechecked once
|
|
|
- if (ann.tpe == null) {
|
|
|
- val ainfo = typedAnnotation(ann, Some(atd), annotMode)
|
|
|
- val atype = arg1.tpe.withAnnotation(ainfo)
|
|
|
-
|
|
|
- if (ainfo.isErroneous)
|
|
|
- // Erroneous annotations were already reported in typedAnnotation
|
|
|
- arg1 // simply drop erroneous annotations
|
|
|
- else {
|
|
|
- ann setType atype
|
|
|
- resultingTypeTree(atype)
|
|
|
- }
|
|
|
- } else {
|
|
|
- // the annotation was typechecked before
|
|
|
- resultingTypeTree(ann.tpe)
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- if (ann.tpe == null) {
|
|
|
- val annotInfo = typedAnnotation(ann, Some(atd), annotMode)
|
|
|
- ann setType arg1.tpe.withAnnotation(annotInfo)
|
|
|
- }
|
|
|
- val atype = ann.tpe
|
|
|
- // For `f(): @inline/noinline` callsites, add the InlineAnnotatedAttachment. TypeApplys
|
|
|
- // are eliminated by erasure, so add it to the underlying function in this case.
|
|
|
- @tailrec
|
|
|
- def setInlineAttachment(t: Tree, att: InlineAnnotatedAttachment): Unit = t match {
|
|
|
- case TypeApply(fun, _) => setInlineAttachment(fun, att)
|
|
|
- case _ => t.updateAttachment(att)
|
|
|
- }
|
|
|
- if (atype.hasAnnotation(definitions.ScalaNoInlineClass)) setInlineAttachment(arg1, NoInlineCallsiteAttachment)
|
|
|
- else if (atype.hasAnnotation(definitions.ScalaInlineClass)) setInlineAttachment(arg1, InlineCallsiteAttachment)
|
|
|
- Typed(arg1, resultingTypeTree(atype)) setPos tree.pos setType atype
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedBind(tree: Bind) =
|
|
|
- tree match {
|
|
|
- case Bind(name: TypeName, body) =>
|
|
|
- assert(body.isEmpty, s"${context.unit} typedBind: ${name.debugString} ${body} ${body.getClass}")
|
|
|
- val sym =
|
|
|
- if (tree.symbol != NoSymbol) tree.symbol
|
|
|
- else {
|
|
|
- if (isFullyDefined(pt))
|
|
|
- context.owner.newAliasType(name, tree.pos) setInfo pt
|
|
|
- else
|
|
|
- context.owner.newAbstractType(name, tree.pos) setInfo TypeBounds.empty
|
|
|
- }
|
|
|
-
|
|
|
- if (name != tpnme.WILDCARD) namer.enterInScope(sym)
|
|
|
- else context.scope.enter(sym)
|
|
|
-
|
|
|
- tree setSymbol sym setType sym.tpeHK
|
|
|
-
|
|
|
- case Bind(name: TermName, body) =>
|
|
|
- val sym =
|
|
|
- if (tree.symbol != NoSymbol) tree.symbol
|
|
|
- else context.owner.newValue(name, tree.pos)
|
|
|
-
|
|
|
- if (name != nme.WILDCARD) {
|
|
|
- if (context.inPatAlternative)
|
|
|
- VariableInPatternAlternativeError(tree)
|
|
|
-
|
|
|
- namer.enterInScope(sym)
|
|
|
- }
|
|
|
-
|
|
|
- val body1 = typed(body, mode, pt)
|
|
|
- val impliedType = patmat.binderTypeImpliedByPattern(body1, pt) // scala/bug#1503, scala/bug#5204
|
|
|
- val symTp =
|
|
|
- if (treeInfo.isSequenceValued(body)) seqType(impliedType)
|
|
|
- else impliedType
|
|
|
- sym setInfo symTp
|
|
|
-
|
|
|
- // have to imperatively set the symbol for this bind to keep it in sync with the symbols used in the body of a case
|
|
|
- // when type checking a case we imperatively update the symbols in the body of the case
|
|
|
- // those symbols are bound by the symbols in the Binds in the pattern of the case,
|
|
|
- // so, if we set the symbols in the case body, but not in the patterns,
|
|
|
- // then re-type check the casedef (for a second try in typedApply for example -- scala/bug#1832),
|
|
|
- // we are no longer in sync: the body has symbols set that do not appear in the patterns
|
|
|
- // since body1 is not necessarily equal to body, we must return a copied tree,
|
|
|
- // but we must still mutate the original bind
|
|
|
- tree setSymbol sym
|
|
|
- treeCopy.Bind(tree, name, body1) setSymbol sym setType impliedType
|
|
|
- }
|
|
|
-
|
|
|
- def typedArrayValue(tree: ArrayValue) = {
|
|
|
- val elemtpt1 = typedType(tree.elemtpt, mode)
|
|
|
- val elems1 = tree.elems mapConserve (elem => typed(elem, mode, elemtpt1.tpe))
|
|
|
- // see run/t6126 for an example where `pt` does not suffice (tagged types)
|
|
|
- val tpe1 = if (isFullyDefined(pt) && !phase.erasedTypes) pt else arrayType(elemtpt1.tpe)
|
|
|
-
|
|
|
- treeCopy.ArrayValue(tree, elemtpt1, elems1) setType tpe1
|
|
|
- }
|
|
|
-
|
|
|
- def typedAssign(lhs: Tree, rhs: Tree): Tree = {
|
|
|
- // see scala/bug#7617 for an explanation of why macro expansion is suppressed
|
|
|
- def typedLhs(lhs: Tree) = typed(lhs, EXPRmode | LHSmode | POLYmode)
|
|
|
- val lhs1 = unsuppressMacroExpansion(typedLhs(suppressMacroExpansion(lhs)))
|
|
|
- val varsym = lhs1.symbol
|
|
|
-
|
|
|
- // see #2494 for double error message example
|
|
|
- def fail() =
|
|
|
- if (lhs1.isErrorTyped) lhs1
|
|
|
- else AssignmentError(tree, varsym)
|
|
|
-
|
|
|
- if (varsym == null)
|
|
|
- return fail()
|
|
|
-
|
|
|
- def shadowsSetter =
|
|
|
- lhs1 match {
|
|
|
- case treeInfo.Applied(Select(qual, _), _, _) if qual.isTyped =>
|
|
|
- qual.tpe.member(varsym.name.setterName).exists
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- def setterRewrite =
|
|
|
- lhs1 match {
|
|
|
- case treeInfo.Applied(Select(qual, _), _, _) =>
|
|
|
- val sel = Select(qual, varsym.name.setterName) setPos lhs.pos
|
|
|
- val app = Apply(sel, List(rhs)) setPos tree.pos
|
|
|
- typed(app, mode, pt)
|
|
|
- case _ => EmptyTree
|
|
|
- }
|
|
|
-
|
|
|
- if (treeInfo.mayBeVarGetter(varsym)) {
|
|
|
- val res = setterRewrite
|
|
|
- if (!res.isEmpty) return res
|
|
|
- }
|
|
|
-
|
|
|
- // if (varsym.isVariable ||
|
|
|
- // // setter-rewrite has been done above, so rule out methods here, but, wait a minute, why are we assigning to non-variables after erasure?!
|
|
|
- // (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) {
|
|
|
- if (varsym.isVariable || varsym.isValue && phase.assignsFields) {
|
|
|
- val rhs1 = typedByValueExpr(rhs, lhs1.tpe)
|
|
|
- treeCopy.Assign(tree, lhs1, checkDead(context, rhs1)) setType UnitTpe
|
|
|
- }
|
|
|
- else if (isDynamicallyUpdatable(lhs1)) {
|
|
|
- val t = atPos(lhs1.pos.withEnd(rhs.pos.end)) {
|
|
|
- Apply(lhs1, List(rhs))
|
|
|
- }
|
|
|
- wrapErrors(t, _.typed1(t, mode, pt))
|
|
|
- }
|
|
|
- else if (shadowsSetter) setterRewrite orElse fail()
|
|
|
- else fail()
|
|
|
- }
|
|
|
-
|
|
|
- def typedIf(tree: If): If = {
|
|
|
- val cond1 = checkDead(context, typedByValueExpr(tree.cond, BooleanTpe))
|
|
|
- // One-legged ifs don't need a lot of analysis
|
|
|
- if (tree.elsep.isEmpty)
|
|
|
- return treeCopy.If(tree, cond1, typed(tree.thenp, UnitTpe), tree.elsep) setType UnitTpe
|
|
|
-
|
|
|
- val thenp1 = typed(tree.thenp, pt)
|
|
|
- val elsep1 = typed(tree.elsep, pt)
|
|
|
-
|
|
|
- // in principle we should pack the types of each branch before lubbing, but lub doesn't really work for existentials anyway
|
|
|
- // in the special (though common) case where the types are equal, it pays to pack before comparing
|
|
|
- // especially virtpatmat needed more aggressive unification of skolemized types
|
|
|
- // this breaks src/library/scala/collection/immutable/TrieIterator.scala (which as of 2.13 doesn't actually exist anymore)
|
|
|
- // annotated types need to be lubbed regardless (at least, continuations break if you bypass them like this)
|
|
|
- def samePackedTypes = (
|
|
|
- !isPastTyper
|
|
|
- && thenp1.tpe.annotations.isEmpty
|
|
|
- && elsep1.tpe.annotations.isEmpty
|
|
|
- && packedType(thenp1, context.owner) =:= packedType(elsep1, context.owner)
|
|
|
- )
|
|
|
- def finish(ownType: Type) = treeCopy.If(tree, cond1, thenp1, elsep1) setType ownType
|
|
|
- // TODO: skolemize (lub of packed types) when that no longer crashes on files/pos/t4070b.scala
|
|
|
- // @PP: This was doing the samePackedTypes check BEFORE the isFullyDefined check,
|
|
|
- // which based on everything I see everywhere else was a bug. I reordered it.
|
|
|
- if (isFullyDefined(pt))
|
|
|
- finish(pt)
|
|
|
- // Important to deconst, otherwise `if (???) 0 else 0` evaluates to 0 (scala/bug#6331)
|
|
|
- else thenp1.tpe.deconst :: elsep1.tpe.deconst :: Nil match {
|
|
|
- case tp :: _ if samePackedTypes => finish(tp)
|
|
|
- case tpes if sameWeakLubAsLub(tpes) => finish(lub(tpes))
|
|
|
- case tpes =>
|
|
|
- val lub = weakLub(tpes)
|
|
|
- treeCopy.If(tree, cond1, adapt(thenp1, mode, lub), adapt(elsep1, mode, lub)) setType lub
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Type the Match and leave it until phase `patmat`
|
|
|
- // empty-selector matches are transformed into synthetic PartialFunction implementations when the expected type demands it
|
|
|
- def typedVirtualizedMatch(tree: Match): Tree = {
|
|
|
- val selector = tree.selector
|
|
|
- val cases = tree.cases
|
|
|
- if (selector == EmptyTree) {
|
|
|
- if (pt.typeSymbol == PartialFunctionClass)
|
|
|
- synthesizePartialFunction(newTermName(fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt)
|
|
|
- else {
|
|
|
- val arity = functionArityFromType(pt) match { case -1 => 1 case arity => arity } // scala/bug#8429: consider sam and function type equally in determining function arity
|
|
|
-
|
|
|
- val params = for (i <- List.range(0, arity)) yield
|
|
|
- atPos(tree.pos.focusStart) {
|
|
|
- ValDef(Modifiers(PARAM | SYNTHETIC),
|
|
|
- freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
|
|
|
- }
|
|
|
- val ids = for (p <- params) yield Ident(p.name)
|
|
|
- val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
|
|
|
- // scala/bug#8120 If we don't duplicate the cases, the original Match node will share trees with ones that
|
|
|
- // receive symbols owned by this function. However if, after a silent mode session, we discard
|
|
|
- // this Function and try a different approach (e.g. applying a view to the receiver) we end up
|
|
|
- // with orphaned symbols which blows up far down the pipeline (or can be detected with -Ycheck:typer).
|
|
|
- val body = treeCopy.Match(tree, selector1, (cases map duplicateAndKeepPositions).asInstanceOf[List[CaseDef]])
|
|
|
- typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
|
|
|
- }
|
|
|
- } else
|
|
|
- typedMatch(selector, cases, mode, pt, tree)
|
|
|
- }
|
|
|
-
|
|
|
- def typedReturn(tree: Return) = {
|
|
|
- val expr = tree.expr
|
|
|
- val enclMethod = context.enclMethod
|
|
|
- if (enclMethod == NoContext ||
|
|
|
- enclMethod.owner.isConstructor ||
|
|
|
- context.enclClass.enclMethod == enclMethod // i.e., we are in a constructor of a local class
|
|
|
- ) {
|
|
|
- ReturnOutsideOfDefError(tree)
|
|
|
- } else {
|
|
|
- val DefDef(_, name, _, _, restpt, _) = enclMethod.tree: @unchecked
|
|
|
- if (restpt.tpe eq null) {
|
|
|
- ReturnWithoutTypeError(tree, enclMethod.owner)
|
|
|
- }
|
|
|
- else {
|
|
|
- val expr1 = context withinReturnExpr typedByValueExpr(expr, restpt.tpe)
|
|
|
- // Warn about returning a value if no value can be returned.
|
|
|
- if (restpt.tpe.typeSymbol == UnitClass) {
|
|
|
- // The typing in expr1 says expr is Unit (it has already been coerced if
|
|
|
- // it is non-Unit) so we have to retype it. Fortunately it won't come up much
|
|
|
- // unless the warning is legitimate.
|
|
|
- val typedExpr = typed(expr)
|
|
|
- if (!isPastTyper && typedExpr.tpe.typeSymbol != UnitClass)
|
|
|
- context.warning(tree.pos, "enclosing method " + name + s" has result type Unit: return value of type ${typedExpr.tpe} discarded", WarningCategory.Other)
|
|
|
- }
|
|
|
- val res = treeCopy.Return(tree, checkDead(context, expr1)).setSymbol(enclMethod.owner)
|
|
|
- val tp = pluginsTypedReturn(NothingTpe, this, res, restpt.tpe)
|
|
|
- res.setType(tp)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedNew(tree: New) = {
|
|
|
- val tpt = tree.tpt
|
|
|
- val tpt1 = {
|
|
|
- // This way typedNew always returns a dealiased type. This used to happen by accident
|
|
|
- // for instantiations without type arguments due to ad hoc code in typedTypeConstructor,
|
|
|
- // and annotations depended on it (to the extent that they worked, which they did
|
|
|
- // not when given a parameterized type alias which dealiased to an annotation.)
|
|
|
- // typedTypeConstructor dealiases nothing now, but it makes sense for a "new" to always be
|
|
|
- // given a dealiased type.
|
|
|
- val tpt0 = typedTypeConstructor(tpt) modifyType (_.dealias)
|
|
|
-
|
|
|
- if (checkStablePrefixClassType(tpt0)) {
|
|
|
- tpt0.tpe.normalize match { // eta-expand
|
|
|
- case PolyType(undet, appliedToUndet) =>
|
|
|
- context.undetparams = undet // can reuse these type params, they're fresh
|
|
|
- notifyUndetparamsAdded(undet)
|
|
|
- TypeTree().setOriginal(tpt0).setType(appliedToUndet)
|
|
|
- case _ => tpt0
|
|
|
- }
|
|
|
- }
|
|
|
- else tpt0
|
|
|
- }
|
|
|
-
|
|
|
- /* If current tree <tree> appears in <val x(: T)? = <tree>>
|
|
|
- * return `tp with x.type' else return `tp`.
|
|
|
- */
|
|
|
- def narrowRhs(tp: Type) = { val sym = context.tree.symbol
|
|
|
- context.tree match {
|
|
|
- case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol =>
|
|
|
- val sym1 =
|
|
|
- if (sym.owner.isClass && sym.getterIn(sym.owner) != NoSymbol) sym.getterIn(sym.owner)
|
|
|
- else sym
|
|
|
- val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix
|
|
|
- intersectionType(List(tp, singleType(pre, sym1)))
|
|
|
- case _ => tp
|
|
|
- }}
|
|
|
-
|
|
|
- val tp = tpt1.tpe
|
|
|
- val sym = tp.typeSymbol.initialize
|
|
|
-
|
|
|
- if ((sym.isAbstractType || sym.hasAbstractFlag)
|
|
|
- && !(sym.isJavaAnnotation && context.inAnnotation))
|
|
|
- IsAbstractError(tree, sym)
|
|
|
- else if (isPrimitiveValueClass(sym)) {
|
|
|
- NotAMemberError(tpt, TypeTree(tp), nme.CONSTRUCTOR, startingIdentContext)
|
|
|
- setError(tpt)
|
|
|
- }
|
|
|
- else if (!( tp == sym.typeOfThis // when there's no explicit self type -- with (#3612) or without self variable
|
|
|
- // sym.thisSym.tpe == tp.typeOfThis (except for objects)
|
|
|
- || narrowRhs(tp) <:< tp.typeOfThis
|
|
|
- || phase.erasedTypes
|
|
|
- )) {
|
|
|
- DoesNotConformToSelfTypeError(tree, sym, tp.typeOfThis)
|
|
|
- } else
|
|
|
- treeCopy.New(tree, tpt1).setType(tp)
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /* Eta expand an expression like `m _`, where `m` denotes a method or a by-name argument
|
|
|
- *
|
|
|
- * The spec says:
|
|
|
- * The expression `$e$ _` is well-formed if $e$ is of method type or if $e$ is a call-by-name parameter.
|
|
|
- * (1) If $e$ is a method with parameters, `$e$ _` represents $e$ converted to a function type
|
|
|
- * by [eta expansion](#eta-expansion).
|
|
|
- * (2) If $e$ is a parameterless method or call-by-name parameter of type `=> $T$`, `$e$ _` represents
|
|
|
- * the function of type `() => $T$`, which evaluates $e$ when it is applied to the empty parameter list `()`.
|
|
|
- */
|
|
|
- def typedEta(methodValue: Tree): Tree = methodValue.tpe match {
|
|
|
- case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1)
|
|
|
- if (tp.params.lengthCompare(definitions.MaxFunctionArity) > 0) MaxFunctionArityError(methodValue, s"; method ${methodValue.symbol.name} cannot be eta-expanded because it takes ${tp.params.length} arguments")
|
|
|
- else {
|
|
|
- val etaPt =
|
|
|
- pt match {
|
|
|
- case pt: ProtoType =>
|
|
|
- pt.asFunctionType orElse functionType(WildcardType.fillList(tp.params.length), WildcardType) orElse WildcardType // arity overflow --> NoType
|
|
|
- case _ => pt
|
|
|
- }
|
|
|
-
|
|
|
- // We know syntactically methodValue can't refer to a constructor because you can't write `this _` for that (right???)
|
|
|
- typedEtaExpansion(methodValue, mode, etaPt)
|
|
|
- }
|
|
|
-
|
|
|
- case TypeRef(_, ByNameParamClass, _) | NullaryMethodType(_) => // (2)
|
|
|
- val pos = methodValue.pos
|
|
|
- // must create it here to change owner (normally done by typed's typedFunction)
|
|
|
- val funSym = context.owner.newAnonymousFunctionValue(pos)
|
|
|
- new ChangeOwnerTraverser(context.owner, funSym) traverse methodValue
|
|
|
-
|
|
|
- val result = typed(Function(Nil, methodValue) setSymbol funSym setPos pos, mode, pt)
|
|
|
-
|
|
|
- if (currentRun.isScala3) {
|
|
|
- UnderscoreNullaryEtaError(methodValue)
|
|
|
- } else {
|
|
|
- context.deprecationWarning(pos, NoSymbol, UnderscoreNullaryEtaWarnMsg, "2.13.2")
|
|
|
- result
|
|
|
- }
|
|
|
-
|
|
|
- case ErrorType =>
|
|
|
- methodValue
|
|
|
-
|
|
|
- case _ =>
|
|
|
- UnderscoreEtaError(methodValue)
|
|
|
- }
|
|
|
-
|
|
|
- def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = {
|
|
|
- val c = context.makeSilent(reportAmbiguousErrors = false)
|
|
|
- c.retyping = true
|
|
|
- try {
|
|
|
- val res = newTyper(c).typedArgs(args, mode)
|
|
|
- if (c.reporter.hasErrors) None else Some(res)
|
|
|
- } catch {
|
|
|
- case ex: CyclicReference =>
|
|
|
- throw ex
|
|
|
- case _: TypeError =>
|
|
|
- // @H some of typer errors can still leak,
|
|
|
- // for instance in continuations
|
|
|
- None
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Try to apply function to arguments; if it does not work, try to convert Java raw to existentials, or try to
|
|
|
- * insert an implicit conversion.
|
|
|
- */
|
|
|
- def tryTypedApply(fun: Tree, args: List[Tree]): Tree = {
|
|
|
- val start = if (settings.areStatisticsEnabled) statistics.startTimer(failedApplyNanos) else null
|
|
|
-
|
|
|
- def onError(typeErrors: Seq[AbsTypeError], warnings: Seq[(Position, String, WarningCategory, Symbol)]): Tree = {
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, start)
|
|
|
-
|
|
|
- // If the problem is with raw types, convert to existentials and try again.
|
|
|
- // See #4712 for a case where this situation arises,
|
|
|
- if ((fun.symbol ne null) && fun.symbol.isJavaDefined) {
|
|
|
- val newtpe = rawToExistential(fun.tpe)
|
|
|
- if (fun.tpe ne newtpe) {
|
|
|
- // println("late cooking: "+fun+":"+fun.tpe) // DEBUG
|
|
|
- return tryTypedApply(fun setType newtpe, args)
|
|
|
- }
|
|
|
- }
|
|
|
- // TODO: case to recurse into Function?
|
|
|
- /* Only retry if the error hails from a result expression of `tree`
|
|
|
- * (for instance, it makes no sense to retry on an error from a block statement)
|
|
|
- * compare with `samePointAs` since many synthetic trees are made with
|
|
|
- * offset positions even under -Yrangepos.
|
|
|
- */
|
|
|
- def errorInResult(tree: Tree): Boolean = {
|
|
|
- def pred(tree: Tree) = typeErrors.exists(_.errPos samePointAs tree.pos)
|
|
|
- def loop(tree: Tree): Boolean = pred(tree) || (tree match {
|
|
|
- case Block(_, r) => loop(r)
|
|
|
- case Match(_, cases) => cases.exists(pred)
|
|
|
- case CaseDef(_, _, r) => loop(r)
|
|
|
- case Annotated(_, r) => loop(r)
|
|
|
- case If(_, t, e) => loop(t) || loop(e)
|
|
|
- case Try(b, catches, _) => loop(b) || catches.exists(pred)
|
|
|
- case MethodValue(r) => loop(r)
|
|
|
- case Select(qual, _) => loop(qual)
|
|
|
- case Apply(fun, args) => loop(fun) || args.exists(loop)
|
|
|
- case TypeApply(fun, args) => loop(fun) || args.exists(loop)
|
|
|
- case _ => false
|
|
|
- })
|
|
|
- loop(tree)
|
|
|
- }
|
|
|
- val retry = typeErrors.forall(_.errPos != null) && (errorInResult(fun) || errorInResult(tree) || args.exists(errorInResult))
|
|
|
- typingStack.printTyping {
|
|
|
- val funStr = s"${ptTree(fun)} and ${args.map(ptTree).mkString(", ")}"
|
|
|
- if (retry) s"second try: $funStr"
|
|
|
- else s"no second try: $funStr because error not in result: ${typeErrors.head.errPos}!=${tree.pos}"
|
|
|
- }
|
|
|
- if (retry) {
|
|
|
- val Select(qual, name) = fun: @unchecked
|
|
|
- tryTypedArgs(args, forArgMode(fun, mode)) match {
|
|
|
- case Some(args1) if !args1.exists(arg => arg.exists(_.isErroneous)) =>
|
|
|
- val qual1 =
|
|
|
- if (!pt.isError) adaptToArguments(qual, name, args1, pt)
|
|
|
- else qual
|
|
|
- if (qual1 ne qual) {
|
|
|
- val tree1 = Apply(Select(qual1, name) setPos fun.pos, args1) setPos tree.pos
|
|
|
- return context withinSecondTry typed1(tree1, mode, pt)
|
|
|
- }
|
|
|
- case _ => ()
|
|
|
- }
|
|
|
- }
|
|
|
- def adjust(err: AbsTypeError) =
|
|
|
- if (tree.hasAttachment[InterpolatedString.type])
|
|
|
- tree match {
|
|
|
- case Apply(sc @ Ident(nme.StringContextName), _) =>
|
|
|
- if (sc.isErroneous) err
|
|
|
- else NormalTypeError(tree, s"${err.errMsg}; signature for interpolation must be `StringContext.apply(String*)`")
|
|
|
- case Apply(Select(_, nm), badargs) =>
|
|
|
- if (badargs.exists(arg => arg.isErroneous || arg.pos.includes(err.errPos) && arg.pos != err.errPos)) err
|
|
|
- else NormalTypeError(tree, s"${err.errMsg}; incompatible interpolation method $nm")
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- else err
|
|
|
- typeErrors.foreach(err => context.issue(adjust(err)))
|
|
|
- warnings.foreach { case (p, m, c, s) => context.warning(p, m, c, s) }
|
|
|
- setError(treeCopy.Apply(tree, fun, args))
|
|
|
- }
|
|
|
-
|
|
|
- silent(_.doTypedApply(tree, fun, args, mode, pt)) match {
|
|
|
- case SilentResultValue(value) => value
|
|
|
- case e: SilentTypeError => onError(e.errors, e.warnings)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def normalTypedApply(tree: Tree, fun: Tree, args: List[Tree]) = {
|
|
|
- // TODO: replace `fun.symbol.isStable` by `treeInfo.isStableIdentifierPattern(fun)`
|
|
|
- val stableApplication = (fun.symbol ne null) && fun.symbol.isMethod && fun.symbol.isStable
|
|
|
- val funpt = if (mode.inPatternMode) pt else WildcardType
|
|
|
- val appStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedApplyNanos) else null
|
|
|
- val opeqStart = if (settings.areStatisticsEnabled) statistics.startTimer(failedOpEqNanos) else null
|
|
|
-
|
|
|
- def isConversionCandidate(qual: Tree, name: Name): Boolean =
|
|
|
- !mode.inPatternMode && nme.isOpAssignmentName(TermName(name.decode)) && !qual.exists(_.isErroneous)
|
|
|
-
|
|
|
- def reportError(error: SilentTypeError): Tree = {
|
|
|
- error.reportableErrors.foreach(context.issue)
|
|
|
- error.warnings.foreach { case (p, m, c, s) => context.warning(p, m, c, s) }
|
|
|
- args.foreach(typed(_, mode, ErrorType))
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- def advice1(convo: Tree, errors: List[AbsTypeError], err: SilentTypeError): List[AbsTypeError] =
|
|
|
- errors.map { e =>
|
|
|
- if (e.errPos samePointAs tree.pos) {
|
|
|
- val header = f"${e.errMsg}%n Expression does not convert to assignment because:%n "
|
|
|
- val expansion = f"%n expansion: ${show(convo)}"
|
|
|
- NormalTypeError(tree, err.errors.flatMap(_.errMsg.linesIterator.toList).mkString(header, f"%n ", expansion))
|
|
|
- } else e
|
|
|
- }
|
|
|
- def advice2(errors: List[AbsTypeError]): List[AbsTypeError] =
|
|
|
- errors.map { e =>
|
|
|
- if (e.errPos samePointAs tree.pos) {
|
|
|
- val msg = f"${e.errMsg}%n Expression does not convert to assignment because receiver is not assignable."
|
|
|
- NormalTypeError(tree, msg)
|
|
|
- } else e
|
|
|
- }
|
|
|
- def onError(error: SilentTypeError): Tree = fun match {
|
|
|
- case Select(qual, name) if isConversionCandidate(qual, name) =>
|
|
|
- val qual1 = typedQualifier(qual)
|
|
|
- if (treeInfo.isVariableOrGetter(qual1)) {
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopTimer(failedOpEqNanos, opeqStart)
|
|
|
- val erred = qual1.exists(_.isErroneous) || args.exists(_.isErroneous)
|
|
|
- if (erred) reportError(error) else {
|
|
|
- val convo = convertToAssignment(fun, qual1, name, args)
|
|
|
- if (settings.multiargInfix && tree.hasAttachment[MultiargInfixAttachment.type] && args.lengthCompare(1) > 0)
|
|
|
- warnMultiargInfix(tree)
|
|
|
- silent(op = _.typed1(convo, mode, pt)) match {
|
|
|
- case SilentResultValue(t) => t
|
|
|
- case err: SilentTypeError => reportError(
|
|
|
- SilentTypeError(advice1(convo, error.errors, err), error.warnings)
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, appStart)
|
|
|
- val Apply(Select(qual2, _), args2) = tree: @unchecked
|
|
|
- val erred = qual2.exists(_.isErroneous) || args2.exists(_.isErroneous)
|
|
|
- reportError {
|
|
|
- if (erred) error else SilentTypeError(advice2(error.errors), error.warnings)
|
|
|
- }
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- if (settings.areStatisticsEnabled) statistics.stopTimer(failedApplyNanos, appStart)
|
|
|
- reportError(error)
|
|
|
- }
|
|
|
- val silentResult = silent(
|
|
|
- op = _.typed(fun, mode.forFunMode, funpt),
|
|
|
- reportAmbiguousErrors = !mode.inExprMode && context.ambiguousErrors,
|
|
|
- newtree = if (mode.inExprMode) tree else context.tree
|
|
|
- )
|
|
|
- silentResult match {
|
|
|
- case SilentResultValue(fun1) =>
|
|
|
- val fun2 = if (stableApplication) stabilizeFun(fun1, mode, pt) else fun1
|
|
|
- if (settings.areStatisticsEnabled) statistics.incCounter(typedApplyCount)
|
|
|
- val isFirstTry = fun2 match {
|
|
|
- case Select(_, _) => mode.inExprMode && {
|
|
|
- val noSecondTry = (
|
|
|
- isPastTyper
|
|
|
- || context.inSecondTry
|
|
|
- || (fun2.symbol ne null) && fun2.symbol.isConstructor
|
|
|
- || isImplicitMethodType(fun2.tpe)
|
|
|
- )
|
|
|
- !noSecondTry
|
|
|
- }
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- if (isFirstTry)
|
|
|
- tryTypedApply(fun2, args)
|
|
|
- else
|
|
|
- doTypedApply(tree, fun2, args, mode, pt)
|
|
|
- case err: SilentTypeError => onError(err)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedApply(tree: Apply) = tree match {
|
|
|
- case Apply(Block(stats, expr), args) =>
|
|
|
- typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt)
|
|
|
- case Apply(fun, args) =>
|
|
|
- normalTypedApply(tree, fun, args) match {
|
|
|
- case treeInfo.ArrayInstantiation(level, componentType, arg) =>
|
|
|
- // convert new Array[T](len) to evidence[ClassTag[T]].newArray(len)
|
|
|
- // convert new Array^N[T](len) for N > 1 to evidence[ClassTag[Array[...Array[T]...]]].newArray(len)
|
|
|
- // where Array HK gets applied (N-1) times
|
|
|
- val tagType = (1 until level).foldLeft(componentType)((res, _) => arrayType(res))
|
|
|
-
|
|
|
- val tree1: Tree = resolveClassTag(tree.pos, tagType) match {
|
|
|
- case EmptyTree => MissingClassTagError(tree, tagType)
|
|
|
- case tag => atPos(tree.pos)(new ApplyToImplicitArgs(Select(tag, nme.newArray), arg :: Nil))
|
|
|
- }
|
|
|
- if (tree1.isErrorTyped) tree1 else typed(tree1, mode, pt)
|
|
|
- case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => TooManyArgumentListsForConstructor(tree) //scala/bug#5696
|
|
|
- case tree1 if mode.inPatternMode && tree1.tpe.paramSectionCount > 0 =>
|
|
|
- // For a case class C with more than two parameter lists,
|
|
|
- // C(_) is typed as C(_)() which is a method type like ()C.
|
|
|
- // In a pattern, just use the final result type, C in this case.
|
|
|
- // The enclosing context may be case c @ C(_) => or val c @ C(_) = v.
|
|
|
- tree1 modifyType (_.finalResultType)
|
|
|
- tree1
|
|
|
- case tree1 @ Apply(_, args1) if settings.multiargInfix && tree.hasAttachment[MultiargInfixAttachment.type] && args1.lengthCompare(1) > 0 =>
|
|
|
- warnMultiargInfix(tree1)
|
|
|
- tree1
|
|
|
- case tree1 => tree1
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def convertToAssignment(fun: Tree, qual: Tree, name: Name, args: List[Tree]): Tree = {
|
|
|
- val prefix = name.toTermName stripSuffix nme.EQL
|
|
|
- def mkAssign(vble: Tree): Tree =
|
|
|
- Assign(
|
|
|
- vble,
|
|
|
- Apply(
|
|
|
- Select(vble.duplicate, prefix) setPos fun.pos.focus, args) setPos tree.pos.makeTransparent
|
|
|
- ) setPos tree.pos
|
|
|
-
|
|
|
- def mkUpdate(table: Tree, indices: List[Tree], args_? : Option[List[Tree]]) =
|
|
|
- gen.evalOnceAll(table :: indices, context.owner, fresh) {
|
|
|
- case tab :: is =>
|
|
|
- def mkCall(name: Name, extraArgs: Tree*) =
|
|
|
- Apply(
|
|
|
- Select(tab(), name) setPos table.pos,
|
|
|
- is.map(i => i()) ++ extraArgs
|
|
|
- ) setPos tree.pos
|
|
|
-
|
|
|
- def mkApplies(core: Tree) = args_?.fold(core) { args =>
|
|
|
- Apply(core, args) setPos wrappingPos(core :: args)
|
|
|
- }
|
|
|
- mkCall(
|
|
|
- nme.update,
|
|
|
- Apply(Select(mkApplies(mkCall(nme.apply)), prefix) setPos fun.pos, args) setPos tree.pos
|
|
|
- )
|
|
|
- case _ => EmptyTree
|
|
|
- }
|
|
|
-
|
|
|
- val assignment = qual match {
|
|
|
- case Ident(_) =>
|
|
|
- mkAssign(qual)
|
|
|
-
|
|
|
- case Select(qualqual, _) =>
|
|
|
- gen.evalOnce(qualqual, context.owner, fresh) { qq =>
|
|
|
- val qq1 = qq()
|
|
|
- mkAssign(Select(qq1, qual.symbol) setPos qual.pos)
|
|
|
- }
|
|
|
-
|
|
|
- case Apply(fn, extra) if qual.isInstanceOf[ApplyToImplicitArgs] =>
|
|
|
- fn match {
|
|
|
- case treeInfo.Applied(Select(table, nme.apply), _, indices :: Nil) =>
|
|
|
- // table(indices)(implicits)
|
|
|
- mkUpdate(table, indices, Some(extra))
|
|
|
- case _ => UnexpectedTreeAssignmentConversionError(qual)
|
|
|
- }
|
|
|
-
|
|
|
- case Apply(fn, indices) =>
|
|
|
- fn match {
|
|
|
- case treeInfo.Applied(Select(table, nme.apply), _, Nil) =>
|
|
|
- mkUpdate(table, indices, None)
|
|
|
- case _ => UnexpectedTreeAssignmentConversionError(qual)
|
|
|
- }
|
|
|
-
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- assignment
|
|
|
- }
|
|
|
-
|
|
|
- def typedSuper(tree: Super) = {
|
|
|
- val mix = tree.mix
|
|
|
- val qual1 = typed(tree.qual)
|
|
|
-
|
|
|
- val clazz = qual1 match {
|
|
|
- case This(_) => qual1.symbol
|
|
|
- case _ => qual1.tpe.typeSymbol
|
|
|
- }
|
|
|
- def findMixinSuper(site: Type): Type = {
|
|
|
- var ps = site.parents filter (_.typeSymbol.name == mix)
|
|
|
- if (ps.isEmpty)
|
|
|
- ps = site.parents filter (_.typeSymbol.name == mix)
|
|
|
- if (ps.isEmpty) {
|
|
|
- debuglog("Fatal: couldn't find site " + site + " in " + site.parents.map(_.typeSymbol.name))
|
|
|
- if (phase.erasedTypes && context.enclClass.owner.isTrait) {
|
|
|
- // the reference to super class got lost during erasure
|
|
|
- restrictionError(tree.pos, unit, "traits may not select fields or methods from super[C] where C is a class")
|
|
|
- ErrorType
|
|
|
- } else {
|
|
|
- MixinMissingParentClassNameError(tree, mix, clazz)
|
|
|
- ErrorType
|
|
|
- }
|
|
|
- } else if (!ps.tail.isEmpty) {
|
|
|
- AmbiguousParentClassError(tree)
|
|
|
- ErrorType
|
|
|
- } else {
|
|
|
- ps.head
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val owntype =
|
|
|
- if (!mix.isEmpty) findMixinSuper(clazz.tpe)
|
|
|
- else if (context.inSuperInit) clazz.info.firstParent
|
|
|
- else intersectionType(clazz.info.parents)
|
|
|
-
|
|
|
- treeCopy.Super(tree, qual1, mix) setType SuperType(clazz.thisType, owntype)
|
|
|
- }
|
|
|
-
|
|
|
- def typedThis(tree: This) =
|
|
|
- tree.symbol orElse qualifyingClass(tree, tree.qual, packageOK = false, immediate = true) match {
|
|
|
- case NoSymbol => tree
|
|
|
- case clazz =>
|
|
|
- tree setSymbol clazz setType clazz.thisType.underlying
|
|
|
- if (isStableContext(tree, mode, pt)) tree setType clazz.thisType else tree
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // For Java, instance and static members are in the same scope, but we put the static ones in the companion object
|
|
|
- // so, when we can't find a member in the class scope, check the companion
|
|
|
- def inCompanionForJavaStatic(cls: Symbol, name: Name): Symbol =
|
|
|
- if (!(context.unit.isJava && cls.isClass)) NoSymbol else {
|
|
|
- context.javaFindMember(cls.typeOfThis, name, _.isStaticMember)._2
|
|
|
- }
|
|
|
-
|
|
|
- /* Attribute a selection where `tree` is `qual.name`.
|
|
|
- * `qual` is already attributed.
|
|
|
- */
|
|
|
- def typedSelect(tree: Tree, qual: Tree, name: Name): Tree = {
|
|
|
- // note: on error, we discard the work we did in type checking tree.qualifier into qual
|
|
|
- // (tree is either Select or SelectFromTypeTree, and qual may be different from tree.qualifier because it has been type checked)
|
|
|
- val qualTp = qual.tpe
|
|
|
- if ((qualTp eq null) || qualTp.isError) setError(tree)
|
|
|
- else if (name.isTypeName && qualTp.isVolatile) // TODO: use same error message for volatileType#T and volatilePath.T?
|
|
|
- if (tree.isInstanceOf[SelectFromTypeTree]) TypeSelectionFromVolatileTypeError(tree, qual)
|
|
|
- else UnstableTreeError(qual)
|
|
|
- else {
|
|
|
- def asDynamicCall = mkInvoke(context, tree, qual, name) map { t =>
|
|
|
- wrapErrors(t, _.typed1(t, mode, pt))
|
|
|
- }
|
|
|
-
|
|
|
- val sym = tree.symbol orElse member(qual.tpe, name) orElse inCompanionForJavaStatic(qual.tpe.typeSymbol, name)
|
|
|
- if ((sym eq NoSymbol) && name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) {
|
|
|
- // symbol not found? --> try to convert implicitly to a type that does have the required
|
|
|
- // member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an
|
|
|
- // xml member to StringContext, which in turn has an unapply[Seq] method)
|
|
|
-
|
|
|
- val qual1 = adaptToMemberWithArgs(tree, qual, name, mode)
|
|
|
- if ((qual1 ne qual) && !qual1.isErrorTyped)
|
|
|
- return typed(treeCopy.Select(tree, qual1, name), mode, pt)
|
|
|
- }
|
|
|
-
|
|
|
- // This special-case complements the logic in `adaptMember` in erasure, it handles selections
|
|
|
- // from `Super`. In `adaptMember`, if the erased type of a qualifier doesn't conform to the
|
|
|
- // owner of the selected member, a cast is inserted, e.g., (foo: Option[String]).get.trim).
|
|
|
- // Similarly, for `super.m`, typing `super` during erasure assigns the superclass. If `m`
|
|
|
- // is defined in a trait, this is incorrect, we need to assign a type to `super` that conforms
|
|
|
- // to the owner of `m`. Adding a cast (as in `adaptMember`) would not work, `super.asInstanceOf`
|
|
|
- // is not a valid tree.
|
|
|
- if (phase.erasedTypes && qual.isInstanceOf[Super]) {
|
|
|
- // See the comment in `preErase` why we use the attachment (scala/bug#7936)
|
|
|
- val qualSym = tree.getAndRemoveAttachment[QualTypeSymAttachment] match {
|
|
|
- case Some(a) => a.sym
|
|
|
- case None => sym.owner
|
|
|
- }
|
|
|
- qual.setType(qualSym.tpe)
|
|
|
- }
|
|
|
-
|
|
|
- if (!reallyExists(sym)) {
|
|
|
- def handleMissing: Tree = {
|
|
|
- def errorTree = missingSelectErrorTree(tree, qual, name)
|
|
|
- def asTypeSelection =
|
|
|
- if (context.unit.isJava && name.isTypeName) {
|
|
|
- // scala/bug#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.
|
|
|
- atPos(tree.pos)(gen.convertToSelectFromType(qual, name)) match {
|
|
|
- case EmptyTree => None
|
|
|
- case tree1 => Some(typed1(tree1, mode, pt))
|
|
|
- }
|
|
|
- } else None
|
|
|
-
|
|
|
- debuglog(s"""
|
|
|
- |qual=$qual:${qual.tpe}
|
|
|
- |symbol=${qual.tpe.termSymbol.defString}
|
|
|
- |scope-id=${qual.tpe.termSymbol.info.decls.hashCode}
|
|
|
- |members=${qual.tpe.members mkString ", "}
|
|
|
- |name=$name
|
|
|
- |found=$sym
|
|
|
- |owner=${context.enclClass.owner}
|
|
|
- """.stripMargin)
|
|
|
-
|
|
|
- // 1) Try converting a term selection on a java class into a type selection.
|
|
|
- // 2) Try expanding according to Dynamic rules.
|
|
|
- // 3) Try looking up the name in the qualifier.
|
|
|
- asTypeSelection orElse asDynamicCall getOrElse (lookupInQualifier(qual, name) match {
|
|
|
- case NoSymbol => setError(errorTree)
|
|
|
- case found => typed1(tree setSymbol found, mode, pt)
|
|
|
- })
|
|
|
- }
|
|
|
- handleMissing
|
|
|
- }
|
|
|
- else {
|
|
|
- if ((sym ne NoSymbol) && !qual.tpe.isStable && argsDependOnPrefix(sym)) {
|
|
|
- // Rewrites "qual.name ..." to "{ val lhs = qual ; lhs.name ... }" in cases where
|
|
|
- // qual is not stable and name has a method type which depends on its prefix. If
|
|
|
- // this is the case then hoisting qual out as a stable val means that members of
|
|
|
- // implicit scopes which are accessible via lhs can be candidates for satisfying
|
|
|
- // implicit (conversions to) arguments of name.
|
|
|
-
|
|
|
- val vsym = context.owner.newValue(freshTermName(nme.STABILIZER_PREFIX), qual.pos.focus, SYNTHETIC | ARTIFACT | STABLE)
|
|
|
- vsym.setInfo(uncheckedBounds(qual.tpe))
|
|
|
- val vdef = atPos(vsym.pos)(ValDef(vsym, focusInPlace(qual)) setType NoType)
|
|
|
- context.pendingStabilizers ::= vdef
|
|
|
- qual.changeOwner(context.owner -> vsym)
|
|
|
- val newQual = Ident(vsym) setType singleType(NoPrefix, vsym) setPos qual.pos.focus
|
|
|
- return typedSelect(tree, newQual, name)
|
|
|
- }
|
|
|
-
|
|
|
- val tree1 = tree match {
|
|
|
- case Select(_, _) => treeCopy.Select(tree, qual, name)
|
|
|
- case SelectFromTypeTree(_, _) => treeCopy.SelectFromTypeTree(tree, qual, name)
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- val pre = qual.tpe
|
|
|
- var accessibleError: AccessTypeError = null
|
|
|
- val result = silent(_.makeAccessible(tree1, sym, pre, qual)) match {
|
|
|
- case SilentTypeError(err) => err match {
|
|
|
- case err: AccessTypeError =>
|
|
|
- accessibleError = err
|
|
|
- tree1
|
|
|
- case _ =>
|
|
|
- SelectWithUnderlyingError(tree, err)
|
|
|
- return tree
|
|
|
- }
|
|
|
- case SilentResultValue(value) => value match {
|
|
|
- case qual: Tree => stabilize(qual, pre, mode, pt)
|
|
|
- case (qual: Tree, pre1: Type) => stabilize(qual, pre1, mode, pt)
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
-
|
|
|
- result match {
|
|
|
- case _ if accessibleError != null =>
|
|
|
- // don't adapt constructor, scala/bug#6074
|
|
|
- val qual1 = if (name == nme.CONSTRUCTOR) qual
|
|
|
- else adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = false, saveErrors = false)
|
|
|
- if (!qual1.isErrorTyped && (qual1 ne qual))
|
|
|
- typed(Select(qual1, name) setPos tree.pos, mode, pt)
|
|
|
- else
|
|
|
- // before failing due to access, try a dynamic call.
|
|
|
- asDynamicCall getOrElse {
|
|
|
- context.issue(accessibleError)
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- // could checkAccessible (called by makeAccessible) potentially have skipped checking a type application in qual?
|
|
|
- case SelectFromTypeTree(qual@TypeTree(), name) if qual.tpe.typeArgs.nonEmpty => // TODO: somehow the new qual is not checked in refchecks
|
|
|
- treeCopy.SelectFromTypeTree(
|
|
|
- result,
|
|
|
- TypeTreeWithDeferredRefCheck() { () => val tp = qual.tpe; val sym = tp.typeSymbolDirect
|
|
|
- // will execute during refchecks -- TODO: make private checkTypeRef in refchecks public and call that one?
|
|
|
- checkBounds(qual, tp.prefix, sym.owner, sym.typeParams, tp.typeArgs, "")
|
|
|
- qual // you only get to see the wrapped tree after running this check :-p
|
|
|
- }.setType(qual.tpe).setPos(qual.pos),
|
|
|
- name
|
|
|
- )
|
|
|
- case _ =>
|
|
|
- result
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeSelectionQualifier(tree: Tree, pt: Type) =
|
|
|
- context.withImplicitsDisabled {
|
|
|
- val mode1 = MonoQualifierModes | mode.onlyTypePat
|
|
|
- typed(checkRootOfQualifier(tree, mode1), mode1, pt)
|
|
|
- }
|
|
|
-
|
|
|
- def typedSelectOrSuperCall(tree: Select) = tree match {
|
|
|
- case Select(qual @ Super(_, _), nme.CONSTRUCTOR) =>
|
|
|
- // the qualifier type of a supercall constructor is its first parent class
|
|
|
- typedSelect(tree, typedSelectOrSuperQualifier(qual), nme.CONSTRUCTOR)
|
|
|
- case Select(qual, name) =>
|
|
|
- if (name.isTypeName) {
|
|
|
- val qualTyped = typedTypeSelectionQualifier(tree.qualifier, WildcardType)
|
|
|
- val qualStableOrError =
|
|
|
- if (qualTyped.isErrorTyped || unit.isJava || treeInfo.admitsTypeSelection(qualTyped)) qualTyped
|
|
|
- else UnstableTreeError(qualTyped)
|
|
|
- typedSelect(tree, qualStableOrError, name)
|
|
|
- } else {
|
|
|
- if (settings.areStatisticsEnabled) statistics.incCounter(typedSelectCount)
|
|
|
- val qualTyped = checkDead(context, typedQualifier(qual, mode))
|
|
|
- val tree1 = typedSelect(tree, qualTyped, name)
|
|
|
-
|
|
|
- if (tree.hasAttachment[PostfixAttachment.type])
|
|
|
- checkFeature(tree.pos, currentRun.runDefinitions.PostfixOpsFeature, name.decode)
|
|
|
- val sym = tree1.symbol
|
|
|
- if (sym != null && sym.isOnlyRefinementMember && !sym.isMacro)
|
|
|
- checkFeature(tree1.pos, currentRun.runDefinitions.ReflectiveCallsFeature, sym.toString)
|
|
|
-
|
|
|
- qualTyped.symbol match {
|
|
|
- case s: Symbol if s.isRootPackage => treeCopy.Ident(tree1, name)
|
|
|
- case _ => tree1
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* A symbol qualifies if:
|
|
|
- * - it exists
|
|
|
- * - it is not stale (stale symbols are made to disappear here)
|
|
|
- * - if we are in a constructor pattern, method definitions do not qualify
|
|
|
- * unless they are stable. Otherwise, 'case x :: xs' would find the :: method.
|
|
|
- */
|
|
|
- def qualifies(sym: Symbol) = (
|
|
|
- sym.hasRawInfo
|
|
|
- && reallyExists(sym)
|
|
|
- && !(mode.typingConstructorPattern && sym.isMethod && !sym.isStable)
|
|
|
- )
|
|
|
-
|
|
|
- /* Attribute an identifier consisting of a simple name or an outer reference.
|
|
|
- *
|
|
|
- * @param tree The tree representing the identifier.
|
|
|
- * @param name The name of the identifier.
|
|
|
- * Transformations: (1) Prefix class members with this.
|
|
|
- * (2) Change imported symbols to selections
|
|
|
- */
|
|
|
- def typedIdent(tree: Tree, name: Name): Tree = {
|
|
|
- // setting to enable unqualified idents in empty package (used by the repl)
|
|
|
- def inEmptyPackage = if (settings.exposeEmptyPackage.value) lookupInEmpty(name) else NoSymbol
|
|
|
-
|
|
|
- def issue(err: AbsTypeError) = {
|
|
|
- // Avoiding some spurious error messages: see scala/bug#2388.
|
|
|
- val suppress = reporter.hasErrors && (name startsWith tpnme.ANON_CLASS_NAME)
|
|
|
- if (!suppress)
|
|
|
- ErrorUtils.issueTypeError(err)
|
|
|
-
|
|
|
- setError(tree)
|
|
|
- }
|
|
|
- // ignore current variable scope in patterns to enforce linearity
|
|
|
- val startContext = if (mode.typingPatternOrTypePat) context.outer else context
|
|
|
-
|
|
|
- def asTypeName = if (mode.inAll(MonoQualifierModes) && unit.isJava && name.isTermName) {
|
|
|
- startContext.lookupSymbol(name.toTypeName, qualifies).symbol
|
|
|
- } else NoSymbol
|
|
|
-
|
|
|
- // in Java, only pick a package if it is rooted
|
|
|
- def termQualifies(sym: Symbol) = qualifies(sym) && (
|
|
|
- !startContext.unit.isJava || !sym.hasPackageFlag
|
|
|
- || sym.owner.isEffectiveRoot || sym.owner.isRootPackage || sym.isRootPackage
|
|
|
- )
|
|
|
- val nameLookup = tree.symbol match {
|
|
|
- case NoSymbol => startContext.lookupSymbol(name, termQualifies)
|
|
|
- case sym => LookupSucceeded(EmptyTree, sym)
|
|
|
- }
|
|
|
- import InferErrorGen._
|
|
|
- nameLookup match {
|
|
|
- case LookupAmbiguous(msg) => issue(AmbiguousIdentError(tree, name, msg))
|
|
|
- case LookupInaccessible(sym, msg) => issue(AccessError(tree, sym, context, msg))
|
|
|
- case LookupNotFound =>
|
|
|
- asTypeName orElse inEmptyPackage orElse lookupInRoot(name) match {
|
|
|
- case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext, mode.in(all = PATTERNmode, none = APPSELmode | TYPEPATmode)))
|
|
|
- case sym => typed1(tree setSymbol sym, mode, pt)
|
|
|
- }
|
|
|
- case LookupSucceeded(qual, sym) =>
|
|
|
- (// this -> Foo.this
|
|
|
- if (sym.isThisSym)
|
|
|
- typed1(This(sym.owner) setPos tree.pos, mode, pt)
|
|
|
- else if (sym.rawname == nme.classOf && currentRun.runDefinitions.isPredefClassOf(sym) && pt.typeSymbol == ClassClass && pt.typeArgs.nonEmpty) {
|
|
|
- // Inferring classOf type parameter from expected type. Otherwise an
|
|
|
- // actual call to the stubbed classOf method is generated, returning null.
|
|
|
- typedClassOf(tree, TypeTree(pt.typeArgs.head).setPos(tree.pos.focus))
|
|
|
- }
|
|
|
- else {
|
|
|
- val pre1 = if (sym.isTopLevel) sym.owner.thisType else if (qual == EmptyTree) NoPrefix else qual.tpe
|
|
|
- val tree1 = if (qual == EmptyTree) tree else {
|
|
|
- val pos = tree.pos
|
|
|
- Select(atPos(pos.focusStart)(qual), name).setPos(pos)
|
|
|
- }
|
|
|
- var tree2: Tree = null
|
|
|
- var pre2: Type = pre1
|
|
|
- makeAccessible(tree1, sym, pre1, qual) match {
|
|
|
- case (t: Tree, tp: Type) =>
|
|
|
- tree2 = t
|
|
|
- pre2 = tp
|
|
|
- case t: Tree =>
|
|
|
- tree2 = t
|
|
|
- case x => throw new MatchError(x)
|
|
|
- }
|
|
|
- // scala/bug#5967 Important to replace param type A* with Seq[A] when seen from from a reference, to avoid
|
|
|
- // inference errors in pattern matching.
|
|
|
- stabilize(tree2, pre2, mode, pt) modifyType dropIllegalStarTypes
|
|
|
- }) setAttachments tree.attachments
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedIdentOrWildcard(tree: Ident) = {
|
|
|
- val name = tree.name
|
|
|
- if (settings.areStatisticsEnabled) statistics.incCounter(typedIdentCount)
|
|
|
- if (!tree.isBackquoted &&
|
|
|
- ((name == nme.WILDCARD && mode.typingPatternNotConstructor) ||
|
|
|
- (name == tpnme.WILDCARD && mode.inTypeMode)))
|
|
|
- tree setType makeFullyDefined(pt)
|
|
|
- else
|
|
|
- typedIdent(tree, name)
|
|
|
- }
|
|
|
-
|
|
|
- def typedCompoundTypeTree(tree: CompoundTypeTree) = {
|
|
|
- val templ = tree.templ
|
|
|
- val parents1 = templ.parents mapConserve (typedType(_, mode))
|
|
|
-
|
|
|
- // This is also checked later in typedStats, but that is too late for scala/bug#5361, so
|
|
|
- // we eagerly check this here.
|
|
|
- for (stat <- templ.body if !treeInfo.isDeclarationOrTypeDef(stat))
|
|
|
- OnlyDeclarationsError(stat)
|
|
|
-
|
|
|
- if ((parents1 ++ templ.body) exists (_.isErrorTyped)) tree setType ErrorType
|
|
|
- else {
|
|
|
- val decls = newScope
|
|
|
- //Console.println("Owner: " + context.enclClass.owner + " " + context.enclClass.owner.id)
|
|
|
- val self = refinedType(parents1 map (_.tpe), context.enclClass.owner, decls, templ.pos)
|
|
|
- newTyper(context.make(templ, self.typeSymbol, decls)).typedRefinement(templ)
|
|
|
- templ updateAttachment CompoundTypeTreeOriginalAttachment(parents1, Nil) // stats are set elsewhere
|
|
|
- tree setType (if (templ.exists(_.isErroneous)) ErrorType else self) // Being conservative to avoid scala/bug#5361
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedAppliedTypeTree(tree: AppliedTypeTree) = {
|
|
|
- val tpt = tree.tpt
|
|
|
- val args = tree.args
|
|
|
- val tpt1 = typed1(tpt, mode | FUNmode | TAPPmode, WildcardType)
|
|
|
-
|
|
|
- if (tpt1.isErrorTyped) {
|
|
|
- tpt1
|
|
|
- } else if (!tpt1.hasSymbolField) {
|
|
|
- AppliedTypeNoParametersError(tree, tpt1.tpe)
|
|
|
- } else {
|
|
|
- val tparams = tpt1.symbol.typeParams
|
|
|
- val isComplete = tpt1.symbol.rawInfo.isComplete
|
|
|
-
|
|
|
- if (sameLength(tparams, args)) {
|
|
|
- // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
|
|
|
- val args1 = map2Conserve(args, tparams) { (arg, tparam) =>
|
|
|
- def ptParams = Kind.FromParams(tparam.typeParams)
|
|
|
-
|
|
|
- // if symbol hasn't been fully loaded, can't check kind-arity except when we're in a pattern,
|
|
|
- // where we can (we can't take part in F-Bounds) and must (scala/bug#8023)
|
|
|
- val pt = if (mode.typingPatternOrTypePat) {
|
|
|
- tparam.initialize; ptParams
|
|
|
- }
|
|
|
- else if (isComplete) ptParams
|
|
|
- else Kind.Wildcard
|
|
|
-
|
|
|
- typedHigherKindedType(arg, mode, pt)
|
|
|
- }
|
|
|
- val argtypes = mapList(args1)(_.tpe)
|
|
|
-
|
|
|
- foreach2(args, tparams) { (arg, tparam) =>
|
|
|
- // note: can't use args1 in selector, because Binds got replaced
|
|
|
- val asym = arg.symbol
|
|
|
- def abounds = asym.info.bounds
|
|
|
- def tbounds = tparam.info.bounds
|
|
|
- // TODO investigate whether this should be merged with the near duplicate in Inferencer
|
|
|
- // and whether or not we should avoid using setInfo here as well to avoid potentially
|
|
|
- // trampling on type history.
|
|
|
- def enhanceBounds(): Unit = {
|
|
|
- val TypeBounds(lo0, hi0) = abounds: @unchecked
|
|
|
- val TypeBounds(lo1, hi1) = tbounds.subst(tparams, argtypes): @unchecked
|
|
|
- val lo = lub(List(lo0, lo1))
|
|
|
- val hi = glb(List(hi0, hi1))
|
|
|
- if (!(lo =:= lo0 && hi =:= hi0))
|
|
|
- asym setInfo logResult(s"Updating bounds of ${asym.fullLocationString} in $tree from '$abounds' to")(TypeBounds(lo, hi))
|
|
|
- }
|
|
|
- if (asym != null && asym.isAbstractType) {
|
|
|
- arg match {
|
|
|
- // I removed the Ident() case that partially fixed scala/bug#1786,
|
|
|
- // because the stricter bounds being inferred broke e.g., slick
|
|
|
- // worse, the fix was compilation order-dependent
|
|
|
- // sharpenQuantifierBounds (used in skolemizeExistential) has an alternative fix (scala/bug#6169) that's less invasive
|
|
|
- case Bind(_, _) => enhanceBounds()
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- val original = treeCopy.AppliedTypeTree(tree, tpt1, args1)
|
|
|
- val result = TypeTree(appliedType(tpt1.tpe, argtypes)) setOriginal original
|
|
|
- val isPoly = tpt1.tpe.isInstanceOf[PolyType]
|
|
|
- if (isPoly) // did the type application (performed by appliedType) involve an unchecked beta-reduction?
|
|
|
- TypeTreeWithDeferredRefCheck() { () =>
|
|
|
- // wrap the tree and include the bounds check -- refchecks will perform this check (that the beta reduction was indeed allowed) and unwrap
|
|
|
- // we can't simply use original in refchecks because it does not contains types
|
|
|
- // (and the only typed trees we have been mangled so they're not quite the original tree anymore)
|
|
|
- checkBounds(result, tpt1.tpe.prefix, tpt1.symbol.owner, tpt1.symbol.typeParams, argtypes, "")
|
|
|
- result // you only get to see the wrapped tree after running this check :-p
|
|
|
- }.setType(result.tpe).setPos(result.pos)
|
|
|
- else result
|
|
|
- } else if (tparams.isEmpty) {
|
|
|
- AppliedTypeNoParametersError(tree, tpt1.tpe)
|
|
|
- } else {
|
|
|
- //Console.println("\{tpt1}:\{tpt1.symbol}:\{tpt1.symbol.info}")
|
|
|
- if (settings.isDebug) Console.println(s"$tpt1:${tpt1.symbol}:${tpt1.symbol.info}")//debug
|
|
|
- AppliedTypeWrongNumberOfArgsError(tree, tpt1, tparams)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- val sym: Symbol = tree.symbol
|
|
|
- if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
|
|
|
-
|
|
|
- def typedPackageDef(pdef0: PackageDef) = {
|
|
|
- val pdef = treeCopy.PackageDef(pdef0, pdef0.pid, pluginsEnterStats(this, namer.expandMacroAnnotations(pdef0.stats)))
|
|
|
- val pid1 = typedPackageQualifier(pdef.pid).asInstanceOf[RefTree]
|
|
|
- assert(sym.moduleClass ne NoSymbol, sym)
|
|
|
- if (pid1.symbol.ne(NoSymbol) && !(pid1.symbol.hasPackageFlag || pid1.symbol.isModule))
|
|
|
- reporter.error(pdef.pos, s"There is name conflict between the ${pid1.symbol.fullName} and the package ${sym.fullName}.")
|
|
|
- val stats1 = newTyper(context.make(tree, sym.moduleClass, sym.info.decls))
|
|
|
- .typedStats(pdef.stats, NoSymbol)
|
|
|
- treeCopy.PackageDef(tree, pid1, stats1) setType NoType
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * The typer with the correct context for a method definition. If the method is a default getter for
|
|
|
- * a constructor default, the resulting typer has a constructor context (fixes scala/bug#5543).
|
|
|
- */
|
|
|
- def defDefTyper(ddef: DefDef) = {
|
|
|
- val isConstrDefaultGetter = ddef.mods.hasDefault && sym.owner.isModuleClass &&
|
|
|
- nme.defaultGetterToMethod(sym.name) == nme.CONSTRUCTOR
|
|
|
- newTyper(context.makeNewScope(ddef, sym)).constrTyperIf(isConstrDefaultGetter)
|
|
|
- }
|
|
|
-
|
|
|
- def typedAlternative(alt: Alternative) = {
|
|
|
- context withinPatAlternative (
|
|
|
- treeCopy.Alternative(tree, alt.trees mapConserve (alt => typed(alt, mode, pt))) setType pt
|
|
|
- )
|
|
|
- }
|
|
|
- def typedStar(tree: Star) = {
|
|
|
- if (!context.starPatterns && !isPastTyper)
|
|
|
- StarPatternWithVarargParametersError(tree)
|
|
|
-
|
|
|
- treeCopy.Star(tree, typed(tree.elem, mode, pt)) setType makeFullyDefined(pt)
|
|
|
- }
|
|
|
-
|
|
|
- def typedTry(tree: Try) = {
|
|
|
- def warn(pos: Position, name: Name) = {
|
|
|
- val msg = s"This catches all Throwables. If this is really intended, use `case ${name.decoded} : Throwable` to clear this warning."
|
|
|
- context.warning(pos, msg, WarningCategory.Other)
|
|
|
- }
|
|
|
- def issueTryWarnings(tree: Try): Try = {
|
|
|
- def checkForCatchAll(cdef: CaseDef): Unit = {
|
|
|
- def unbound(t: Tree) = t.symbol == null || t.symbol == NoSymbol
|
|
|
- if (cdef.guard.isEmpty) cdef.pat match {
|
|
|
- case Bind(name, i @ Ident(_)) if unbound(i) => warn(cdef.pat.pos, name)
|
|
|
- case i @ Ident(name) if unbound(i) => warn(cdef.pat.pos, name)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
- if (!isPastTyper) tree match {
|
|
|
- case Try(_, Nil, fin) =>
|
|
|
- if (fin eq EmptyTree)
|
|
|
- context.warning(tree.pos, "A try without a catch or finally is equivalent to putting its body in a block; no exceptions are handled.", WarningCategory.Other)
|
|
|
- case Try(_, catches, _) =>
|
|
|
- catches foreach checkForCatchAll
|
|
|
- }
|
|
|
- tree
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- val Try(block, catches, fin) = tree
|
|
|
- val block1 = typed(block, pt)
|
|
|
- val cases = catches match {
|
|
|
- case CaseDef(EmptyTree, EmptyTree, catchExpr) :: Nil =>
|
|
|
- val e = typed(catchExpr, functionType(List(ThrowableTpe), pt))
|
|
|
- val catcher =
|
|
|
- if (isPartialFunctionType(e.tpe)) treeBuilder.makeCatchFromExpr(e)
|
|
|
- else {
|
|
|
- warn(e.pos, nme.WILDCARD)
|
|
|
- treeBuilder.makeCatchFromFunc(e)
|
|
|
- }
|
|
|
- catcher :: Nil
|
|
|
- case _ => catches
|
|
|
- }
|
|
|
- val catches1 = typedCases(cases, ThrowableTpe, pt)
|
|
|
- val fin1 = if (fin.isEmpty) fin else typed(fin, UnitTpe)
|
|
|
-
|
|
|
- def finish(ownType: Type) = treeCopy.Try(tree, block1, catches1, fin1) setType ownType
|
|
|
-
|
|
|
- issueTryWarnings(
|
|
|
- if (isFullyDefined(pt))
|
|
|
- finish(pt)
|
|
|
- else block1 :: catches1 map (_.tpe.deconst) match {
|
|
|
- case tpes if sameWeakLubAsLub(tpes) => finish(lub(tpes))
|
|
|
- case tpes =>
|
|
|
- val lub = weakLub(tpes)
|
|
|
- val block2 = adapt(block1, mode, lub)
|
|
|
- val catches2 = catches1 map (adaptCase(_, mode, lub))
|
|
|
- treeCopy.Try(tree, block2, catches2, fin1) setType lub
|
|
|
- }
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- def typedThrow(tree: Throw) = {
|
|
|
- val expr1 = typedByValueExpr(tree.expr, ThrowableTpe)
|
|
|
- treeCopy.Throw(tree, expr1) setType NothingTpe
|
|
|
- }
|
|
|
-
|
|
|
- def typedTyped(tree: Typed) = {
|
|
|
- if (treeInfo isWildcardStarType tree.tpt)
|
|
|
- typedStarInPattern(tree, mode.onlySticky, pt)
|
|
|
- else if (mode.inPatternMode)
|
|
|
- typedInPattern(tree, mode.onlySticky, pt)
|
|
|
- else tree match {
|
|
|
- // find out whether the programmer is trying to eta-expand a macro def
|
|
|
- // to do that we need to typecheck the tree first (we need a symbol of the eta-expandee)
|
|
|
- // that typecheck must not trigger macro expansions, so we explicitly prohibit them
|
|
|
- // however we cannot do `context.withMacrosDisabled`
|
|
|
- // because `expr` might contain nested macro calls (see scala/bug#6673).
|
|
|
- // Otherwise, (check for dead code, and) eta-expand.
|
|
|
- case MethodValue(expr) =>
|
|
|
- // Need to type in FUNmode so that we accept a method type (which also means we can't use our pt),
|
|
|
- // this does mean no overloading is performed. The main reason to ignore pt and move to FUNmode is that
|
|
|
- // the `m` in `m _` could involve an implicit conversion, which will go through adapt after converting,
|
|
|
- // which will run afoul of the restriction that a method-typed tree is only allowed when a function type is expected.
|
|
|
- // We peeled off the `_` marker for the typed1 call, so we don't know that the user has requested eta-expansion.
|
|
|
- // See scala/bug#8299.
|
|
|
- val funTyped = typed1(suppressMacroExpansion(expr), mode | FUNmode, WildcardType)
|
|
|
- if (funTyped.tpe.isInstanceOf[OverloadedType]) inferExprAlternative(funTyped, pt)
|
|
|
- funTyped match {
|
|
|
- case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef)
|
|
|
- case methodValue => typedEta(checkDead(context, methodValue))
|
|
|
- }
|
|
|
- case Typed(expr, tpt) =>
|
|
|
- val tpt1 = typedType(tpt, mode) // type the ascribed type first
|
|
|
- val exprWithAttachment =
|
|
|
- if (definitions.isUnitType(tpt1.tpe)) expr.updateAttachment(TypedExpectingUnitAttachment)
|
|
|
- else expr
|
|
|
- val expr1 = typed(exprWithAttachment, mode.onlySticky, tpt1.tpe.deconst) // then type the expression with tpt1 as the expected type
|
|
|
- treeCopy.Typed(tree, expr1, tpt1) setType tpt1.tpe
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeApply(tree: TypeApply) = {
|
|
|
- val fun = tree.fun
|
|
|
- val args = tree.args
|
|
|
- // @M: kind-arity checking is done here and in adapt, full kind-checking is in checkKindBounds (in Infer)
|
|
|
- //@M! we must type fun in order to type the args, as that requires the kinds of fun's type parameters.
|
|
|
- // However, args should apparently be done first, to save context.undetparams. Unfortunately, the args
|
|
|
- // *really* have to be typed *after* fun. We escape from this classic Catch-22 by simply saving&restoring undetparams.
|
|
|
-
|
|
|
- // @M TODO: the compiler still bootstraps&all tests pass when this is commented out..
|
|
|
- //val undets = context.undetparams
|
|
|
-
|
|
|
- // @M: fun is typed in TAPPmode because it is being applied to its actual type parameters
|
|
|
- val fun1 = typed(fun, mode.forFunMode | TAPPmode)
|
|
|
- val tparams = if (fun1.symbol == null) Nil else fun1.symbol.typeParams
|
|
|
-
|
|
|
- //@M TODO: val undets_fun = context.undetparams ?
|
|
|
- // "do args first" (by restoring the context.undetparams) in order to maintain context.undetparams on the function side.
|
|
|
-
|
|
|
- // @M TODO: the compiler still bootstraps when this is commented out.. TODO: run tests
|
|
|
- //context.undetparams = undets
|
|
|
-
|
|
|
- // @M maybe the well-kindedness check should be done when checking the type arguments conform to the type parameters' bounds?
|
|
|
- val args1 = if (sameLength(args, tparams)) map2Conserve(args, tparams) {
|
|
|
- (arg, tparam) => typedHigherKindedType(arg, mode, Kind.FromParams(tparam.typeParams))
|
|
|
- }
|
|
|
- else {
|
|
|
- //@M this branch is correctly hit for an overloaded polymorphic type. It also has to handle erroneous cases.
|
|
|
- // Until the right alternative for an overloaded method is known, be very liberal,
|
|
|
- // typedTypeApply will find the right alternative and then do the same check as
|
|
|
- // in the then-branch above. (see pos/tcpoly_overloaded.scala)
|
|
|
- // this assert is too strict: be tolerant for errors like trait A { def foo[m[x], g]=error(""); def x[g] = foo[g/*ERR: missing argument type*/] }
|
|
|
- //assert(fun1.symbol.info.isInstanceOf[OverloadedType] || fun1.symbol.isError) //, (fun1.symbol,fun1.symbol.info,fun1.symbol.info.getClass,args,tparams))
|
|
|
- args mapConserve (typedHigherKindedType(_, mode))
|
|
|
- }
|
|
|
-
|
|
|
- //@M TODO: context.undetparams = undets_fun ?
|
|
|
- Typer.this.typedTypeApply(tree, mode, fun1, args1)
|
|
|
- }
|
|
|
-
|
|
|
- def typedApplyDynamic(tree: ApplyDynamic) = {
|
|
|
- assert(phase.erasedTypes, "typedApplyDynamic called before erasure")
|
|
|
- val qual1 = typed(tree.qual, AnyRefTpe)
|
|
|
- val args1 = tree.args mapConserve (arg => typed(arg, AnyRefTpe))
|
|
|
- treeCopy.ApplyDynamic(tree, qual1, args1) setType AnyRefTpe
|
|
|
- }
|
|
|
-
|
|
|
- def typedReferenceToBoxed(tree: ReferenceToBoxed) = {
|
|
|
- val id = tree.ident
|
|
|
- val id1 = typed1(id, mode, pt) match { case id: Ident => id case x => throw new MatchError(x) }
|
|
|
- // [Eugene] am I doing it right?
|
|
|
- val erasedTypes = phaseId(currentPeriod) >= currentRun.erasurePhase.id
|
|
|
- val tpe = capturedVariableType(id.symbol, erasedTypes = erasedTypes)
|
|
|
- treeCopy.ReferenceToBoxed(tree, id1) setType tpe
|
|
|
- }
|
|
|
-
|
|
|
- // Warn about likely interpolated strings which are missing their interpolators
|
|
|
- def warnMissingInterpolator(lit: Literal): Unit = if (!isPastTyper) {
|
|
|
- // attempt to avoid warning about trees munged by macros
|
|
|
- def isMacroExpansion = {
|
|
|
- // context.tree is not the expandee; it is plain new SC(ps).m(args)
|
|
|
- //context.tree exists (t => (t.pos includes lit.pos) && hasMacroExpansionAttachment(t))
|
|
|
- // testing pos works and may suffice
|
|
|
- //openMacros exists (_.macroApplication.pos includes lit.pos)
|
|
|
- // tests whether the lit belongs to the expandee of an open macro
|
|
|
- openMacros exists (_.macroApplication.attachments.get[MacroExpansionAttachment] match {
|
|
|
- case Some(MacroExpansionAttachment(_, t: Tree)) => t exists (_ == lit)
|
|
|
- case _ => false
|
|
|
- })
|
|
|
- }
|
|
|
- // An interpolation desugars to `StringContext(parts).m(args)`, so obviously not missing.
|
|
|
- // `implicitNotFound` annotations have strings with `${A}`, so never warn for that.
|
|
|
- // Also don't warn for macro expansion.
|
|
|
- def mightBeMissingInterpolation: Boolean = context.enclosingApply.tree match {
|
|
|
- case Apply(Select(Apply(RefTree(_, nme.StringContextName), _), _), _) => false
|
|
|
- case Apply(Select(New(RefTree(_, tpnme.implicitNotFound)), _), _) => false
|
|
|
- case _ => !isMacroExpansion
|
|
|
- }
|
|
|
- def maybeWarn(s: String): Unit = {
|
|
|
- def warn(message: String) = context.warning(lit.pos, s"possible missing interpolator: $message", WarningCategory.LintMissingInterpolator)
|
|
|
- def isPlausible(id: String): Boolean = {
|
|
|
- def requiresNoArgs(tp: Type): Boolean = tp match {
|
|
|
- case PolyType(_, restpe) => requiresNoArgs(restpe)
|
|
|
- case MethodType(Nil, restpe) => requiresNoArgs(restpe) // may be a curried method - can't tell yet
|
|
|
- case MethodType(p :: _, _) => p.isImplicit // implicit method requires no args
|
|
|
- case _ => true // catches all others including NullaryMethodType
|
|
|
- }
|
|
|
- def isNullaryTerm: Boolean = {
|
|
|
- val maybe = context.lookupSymbol(TermName(id), _ => true).symbol
|
|
|
- maybe != NoSymbol && !maybe.hasPackageFlag && !maybe.isModule && maybe.alternatives.exists(x => requiresNoArgs(x.info))
|
|
|
- }
|
|
|
- id == "this" || isNullaryTerm
|
|
|
- }
|
|
|
- val suspiciousExprs = InterpolatorCodeRegex.findAllMatchIn(s)
|
|
|
- def suspiciousIdents = InterpolatorIdentRegex.findAllIn(s)
|
|
|
-
|
|
|
- if (suspiciousExprs.hasNext) {
|
|
|
- def isCheapIdent(expr: String) = Character.isJavaIdentifierStart(expr.charAt(0)) && expr.tail.forall(Character.isJavaIdentifierPart)
|
|
|
- def warnableExpr(expr: String) = !expr.isEmpty && (!isCheapIdent(expr) || isPlausible(expr))
|
|
|
- val exprs = suspiciousExprs.map(_ group 1).toList
|
|
|
- // short-circuit on leading ${}
|
|
|
- if (!exprs.head.isEmpty && exprs.exists(warnableExpr))
|
|
|
- warn("detected an interpolated expression") // "${...}"
|
|
|
- } else
|
|
|
- suspiciousIdents.find(id => isPlausible(id.substring(1))).foreach(id => warn(s"detected interpolated identifier `$id`"))
|
|
|
- }
|
|
|
- lit match {
|
|
|
- case Literal(Constant(s: String)) if mightBeMissingInterpolation => maybeWarn(s)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedLiteral(tree: Literal) = {
|
|
|
- if (settings.warnMissingInterpolator) warnMissingInterpolator(tree)
|
|
|
-
|
|
|
- tree setType (if (tree.value.tag == UnitTag) UnitTpe else ConstantType(tree.value))
|
|
|
- }
|
|
|
-
|
|
|
- def typedSingletonTypeTree(tree: SingletonTypeTree) = {
|
|
|
- val refTyped = typedTypeSelectionQualifier(tree.ref, WildcardType)
|
|
|
-
|
|
|
- if (refTyped.isErrorTyped) setError(tree)
|
|
|
- else {
|
|
|
- // .resultType unwraps NullaryMethodType (accessor of a path)
|
|
|
- // .deconst unwraps the ConstantType to a LiteralType (for literal-based singleton types)
|
|
|
- if (!treeInfo.admitsTypeSelection(refTyped)) UnstableTreeError(tree)
|
|
|
- else treeCopy.SingletonTypeTree(tree, refTyped).setType(refTyped.tpe.resultType.deconst)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeBoundsTree(tree: TypeBoundsTree) = {
|
|
|
- val lo1 = if (tree.lo.isEmpty) TypeTree(NothingTpe) else typedType(tree.lo, mode)
|
|
|
- val hi1 = if (tree.hi.isEmpty) TypeTree(AnyTpe) else typedType(tree.hi, mode)
|
|
|
- treeCopy.TypeBoundsTree(tree, lo1, hi1) setType TypeBounds(lo1.tpe, hi1.tpe)
|
|
|
- }
|
|
|
-
|
|
|
- def typedExistentialTypeTree(tree: ExistentialTypeTree) = {
|
|
|
- val tree1 = typerWithLocalContext(context.makeNewScope(tree, context.owner)){
|
|
|
- typer =>
|
|
|
- if (context.inTypeConstructorAllowed)
|
|
|
- typer.context.withinTypeConstructorAllowed(typer.typedExistentialTypeTree(tree, mode))
|
|
|
- else
|
|
|
- typer.typedExistentialTypeTree(tree, mode)
|
|
|
- }
|
|
|
- checkExistentialsFeature(tree1.pos, tree1.tpe, "the existential type")
|
|
|
- tree1
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeTree(tree: TypeTree) = {
|
|
|
- if (tree.original != null) {
|
|
|
- val newTpt = typedType(tree.original, mode)
|
|
|
- tree setType newTpt.tpe
|
|
|
- newTpt match {
|
|
|
- case tt @ TypeTree() => tree setOriginal tt.original
|
|
|
- case _ => tree
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- // we should get here only when something before failed
|
|
|
- // and we try again (@see tryTypedApply). In that case we can assign
|
|
|
- // whatever type to tree; we just have to survive until a real error message is issued.
|
|
|
- devWarning(tree.pos, s"Assigning Any type to TypeTree because tree.original is null: tree is $tree/${System.identityHashCode(tree)}, sym=${tree.symbol}, tpe=${tree.tpe}")
|
|
|
- tree setType AnyTpe
|
|
|
- }
|
|
|
- }
|
|
|
- def typedFunction(fun: Function) = {
|
|
|
- if (fun.symbol == NoSymbol)
|
|
|
- fun.symbol = context.owner.newAnonymousFunctionValue(fun.pos)
|
|
|
-
|
|
|
- typerWithLocalContext(context.makeNewScope(fun, fun.symbol))(_.typedFunction(fun, mode, pt))
|
|
|
- }
|
|
|
-
|
|
|
- // Trees only allowed during pattern mode.
|
|
|
- def typedInPatternMode(tree: Tree): Tree = tree match {
|
|
|
- case tree: Alternative => typedAlternative(tree)
|
|
|
- case tree: Star => typedStar(tree)
|
|
|
- case _ => abort(s"unexpected tree in pattern mode: ${tree.getClass}\n$tree")
|
|
|
- }
|
|
|
-
|
|
|
- @inline def typedTypTree(tree: TypTree): Tree = tree match {
|
|
|
- case tree: TypeTree => typedTypeTree(tree)
|
|
|
- case tree: AppliedTypeTree => typedAppliedTypeTree(tree)
|
|
|
- case tree: TypeBoundsTree => typedTypeBoundsTree(tree)
|
|
|
- case tree: SingletonTypeTree => typedSingletonTypeTree(tree)
|
|
|
- case tree: SelectFromTypeTree => typedSelect(tree, typedType(tree.qualifier, mode), tree.name)
|
|
|
- case tree: CompoundTypeTree => typedCompoundTypeTree(tree)
|
|
|
- case tree: ExistentialTypeTree => typedExistentialTypeTree(tree)
|
|
|
- case tree: TypeTreeWithDeferredRefCheck => tree // TODO: retype the wrapped tree? TTWDRC would have to change to hold the wrapped tree (not a closure)
|
|
|
- case _ => abort(s"unexpected type-representing tree: ${tree.getClass}\n$tree")
|
|
|
- }
|
|
|
-
|
|
|
- @inline def typedMemberDef(tree: MemberDef): Tree = tree match {
|
|
|
- case tree: ValDef => typedValDef(tree)
|
|
|
- case tree: DefDef => defDefTyper(tree).typedDefDef(tree)
|
|
|
- case tree: ClassDef => newTyper(context.makeNewScope(tree, sym)).typedClassDef(tree)
|
|
|
- case tree: ModuleDef => newTyper(context.makeNewScope(tree, sym.moduleClass)).typedModuleDef(tree)
|
|
|
- case tree: TypeDef => typedTypeDef(tree)
|
|
|
- case tree: PackageDef => typedPackageDef(tree)
|
|
|
- case _ => abort(s"unexpected member def: ${tree.getClass}\n$tree")
|
|
|
- }
|
|
|
-
|
|
|
- // Trees not allowed during pattern mode.
|
|
|
- def typedOutsidePatternMode(tree: Tree): Tree = tree match {
|
|
|
- case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt))
|
|
|
- case tree: If => typedIf(tree)
|
|
|
- case tree: TypeApply => typedTypeApply(tree)
|
|
|
- case tree: Function => typedFunction(tree)
|
|
|
- case tree: Match => typedVirtualizedMatch(tree)
|
|
|
- case tree: New => typedNew(tree)
|
|
|
- case tree: Assign => typedAssign(tree.lhs, tree.rhs)
|
|
|
- case tree: NamedArg => typedAssign(tree.lhs, tree.rhs)
|
|
|
- case tree: Super => typedSuper(tree)
|
|
|
- case tree: Annotated => typedAnnotated(tree)
|
|
|
- case tree: Return => typedReturn(tree)
|
|
|
- case tree: Try => typedTry(tree)
|
|
|
- case tree: Throw => typedThrow(tree)
|
|
|
- case tree: ArrayValue => typedArrayValue(tree)
|
|
|
- case tree: ApplyDynamic => typedApplyDynamic(tree)
|
|
|
- case tree: ReferenceToBoxed => typedReferenceToBoxed(tree)
|
|
|
- case tree: LabelDef => labelTyper(tree).typedLabelDef(tree)
|
|
|
- case tree: DocDef => typedDocDef(tree, mode, pt)
|
|
|
- case _ => abort(s"unexpected tree: ${tree.getClass}\n$tree")
|
|
|
- }
|
|
|
-
|
|
|
- // Trees allowed in or out of pattern mode.
|
|
|
- @inline def typedInAnyMode(tree: Tree): Tree = tree match {
|
|
|
- case tree: Ident => typedIdentOrWildcard(tree)
|
|
|
- case tree: Bind => typedBind(tree)
|
|
|
- case tree: Apply => typedApply(tree)
|
|
|
- case tree: Select => typedSelectOrSuperCall(tree)
|
|
|
- case tree: Literal => typedLiteral(tree)
|
|
|
- case tree: Typed => typedTyped(tree)
|
|
|
- case tree: This => typedThis(tree) // scala/bug#6104
|
|
|
- case tree: UnApply => abort(s"unexpected UnApply $tree") // turns out UnApply never reaches here
|
|
|
- case _ =>
|
|
|
- if (mode.inPatternMode)
|
|
|
- typedInPatternMode(tree)
|
|
|
- else
|
|
|
- typedOutsidePatternMode(tree)
|
|
|
- }
|
|
|
-
|
|
|
- // begin typed1
|
|
|
- tree match {
|
|
|
- case tree: TypTree => typedTypTree(tree)
|
|
|
- case tree: MemberDef => typedMemberDef(tree)
|
|
|
- case _ => typedInAnyMode(tree)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typed(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- lastTreeToTyper = tree
|
|
|
- val startByType = if (settings.areHotStatisticsEnabled) statistics.pushTimer(byTypeStack, byTypeNanos(tree.getClass)) else null
|
|
|
- if (settings.areHotStatisticsEnabled) statistics.incCounter(visitsByType, tree.getClass)
|
|
|
- val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree)
|
|
|
- val shouldPopTypingStack = shouldPrintTyping && typingStack.beforeNextTyped(tree, mode, pt, context)
|
|
|
-
|
|
|
- def shouldInsertStabilizersImpl = tree match {
|
|
|
- case _ if phase.erasedTypes || mode.in(APPSELmode) || isMacroImplRef(tree) => false
|
|
|
- case _: Select | _: Apply | _: TypeApply => true
|
|
|
- case _ => false
|
|
|
- }
|
|
|
-
|
|
|
- val shouldInsertStabilizers = shouldInsertStabilizersImpl
|
|
|
- val mode1: Mode = if (shouldInsertStabilizers) mode | APPSELmode else mode
|
|
|
- val savedPendingStabilizer = context.pendingStabilizers
|
|
|
- if (shouldInsertStabilizers) context.pendingStabilizers = Nil
|
|
|
-
|
|
|
- try {
|
|
|
- val ptPlugins = pluginsPt(pt, this, tree, mode1)
|
|
|
- def retypingOk = (
|
|
|
- context.retyping
|
|
|
- && (tree.tpe ne null)
|
|
|
- && (tree.tpe.isErroneous || !(tree.tpe <:< ptPlugins))
|
|
|
- )
|
|
|
- if (retypingOk) {
|
|
|
- tree.setType(null)
|
|
|
- if (tree.hasSymbolField) tree.symbol = NoSymbol
|
|
|
- }
|
|
|
- val alreadyTyped = tree.tpe ne null
|
|
|
- val shouldPrint = !alreadyTyped && !phase.erasedTypes
|
|
|
- val ptWild = if (mode1.inPatternMode)
|
|
|
- ptPlugins // scala/bug#5022 don't widen pt for patterns as types flow from it to the case body.
|
|
|
- else
|
|
|
- dropExistential(ptPlugins) // FIXME: document why this is done.
|
|
|
- val tree1: Tree = if (alreadyTyped) tree else typed1(tree, mode1, ptWild)
|
|
|
-
|
|
|
- if (shouldPrint)
|
|
|
- typingStack.showTyped(tree1)
|
|
|
-
|
|
|
- // Can happen during erroneous compilation - error(s) have been
|
|
|
- // reported, but we need to avoid causing an NPE with this tree
|
|
|
- if (tree1.tpe eq null)
|
|
|
- return setError(tree)
|
|
|
-
|
|
|
- tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode1, ptPlugins)
|
|
|
-
|
|
|
- val adapted =
|
|
|
- if (tree1.isEmpty) tree1
|
|
|
- else {
|
|
|
- val result = adapt(tree1, mode1, ptPlugins, tree)
|
|
|
- if (typerShouldExpandDeferredMacros) {
|
|
|
- macroExpandAll(this, result)
|
|
|
- } else result
|
|
|
- }
|
|
|
-
|
|
|
- val result =
|
|
|
- if (shouldInsertStabilizers) addStabilizers(context.pendingStabilizers, adapted)
|
|
|
- else adapted
|
|
|
-
|
|
|
- if (shouldPrint)
|
|
|
- typingStack.showAdapt(tree1, result, ptPlugins, context)
|
|
|
-
|
|
|
- if (!isPastTyper)
|
|
|
- signalDone(context.asInstanceOf[analyzer.Context], tree, result)
|
|
|
-
|
|
|
- if (mode1.inPatternMode && !mode1.inPolyMode && result.isType)
|
|
|
- PatternMustBeValue(result, pt)
|
|
|
-
|
|
|
- if (shouldPopTypingStack) typingStack.showPop(result)
|
|
|
-
|
|
|
- result
|
|
|
- } catch {
|
|
|
- case ex: CyclicReference if global.propagateCyclicReferences =>
|
|
|
- throw ex
|
|
|
- case ex: TypeError =>
|
|
|
- tree.clearType()
|
|
|
- // The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere.
|
|
|
- typingStack.printTyping(tree, "caught %s: while typing %s".format(ex, tree)) //DEBUG
|
|
|
- reportTypeError(context, tree.pos, ex)
|
|
|
- setError(tree)
|
|
|
- case ex: Exception =>
|
|
|
- // @M causes cyclic reference error
|
|
|
- devWarning(s"exception when typing $tree, pt=$pt")
|
|
|
- if (context != null && context.unit.exists && tree != null)
|
|
|
- logError("AT: " + tree.pos, ex)
|
|
|
- throw ex
|
|
|
- } finally {
|
|
|
- if (shouldPopTypingStack) typingStack.pop(tree)
|
|
|
- if (settings.areHotStatisticsEnabled) statistics.popTimer(byTypeStack, startByType)
|
|
|
- if (shouldInsertStabilizers) context.pendingStabilizers = savedPendingStabilizer
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private def addStabilizers(newStabilizers: List[Tree], expr: Tree): Tree = {
|
|
|
- if (newStabilizers.isEmpty) expr else {
|
|
|
- devWarningIf(newStabilizers.forall(_.symbol.owner == context.owner))(s"${context.owner} - ${(newStabilizers.map(vd => (vd.symbol, vd.symbol.owner.fullNameString)), context.owner)}")
|
|
|
- // Insert stabilizing ValDefs (if any) which might have been introduced during the typing of the original expression.
|
|
|
- Block(newStabilizers.reverse, expr).setPos(expr.pos).setType(expr.tpe)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def atOwner(owner: Symbol): Typer =
|
|
|
- newTyper(context.make(owner = owner))
|
|
|
-
|
|
|
- def atOwner(tree: Tree, owner: Symbol): Typer =
|
|
|
- newTyper(context.make(tree, owner))
|
|
|
-
|
|
|
- /** Types expression or definition `tree`.
|
|
|
- */
|
|
|
- @inline final def typed(tree: Tree): Tree =
|
|
|
- typed(tree, context.defaultModeForTyped, WildcardType)
|
|
|
-
|
|
|
- @inline final def typedByValueExpr(tree: Tree, pt: Type = WildcardType): Tree = typed(tree, EXPRmode | BYVALmode, pt)
|
|
|
-
|
|
|
- def typedPos(pos: Position, mode: Mode, pt: Type)(tree: Tree) = typed(atPos(pos)(tree), mode, pt)
|
|
|
- def typedPos(pos: Position)(tree: Tree) = typed(atPos(pos)(tree))
|
|
|
- // TODO: see if this formulation would impose any penalty, since
|
|
|
- // it makes for a lot less casting.
|
|
|
- // def typedPos[T <: Tree](pos: Position)(tree: T): T = typed(atPos(pos)(tree)).asInstanceOf[T]
|
|
|
-
|
|
|
- /** Types expression `tree` with given prototype `pt`.
|
|
|
- */
|
|
|
- @inline final def typed(tree: Tree, pt: Type): Tree =
|
|
|
- typed(tree, context.defaultModeForTyped, pt)
|
|
|
-
|
|
|
- @inline final def typed(tree: Tree, mode: Mode): Tree =
|
|
|
- typed(tree, mode, WildcardType)
|
|
|
-
|
|
|
- /** Types qualifier `tree` of a select node.
|
|
|
- * E.g. is tree occurs in a context like `tree.m`.
|
|
|
- */
|
|
|
- @inline final def typedQualifier(tree: Tree, mode: Mode, pt: Type): Tree =
|
|
|
- typed(checkRootOfQualifier(tree, mode), PolyQualifierModes | mode.onlyTypePat, pt) // TR: don't set BYVALmode, since qualifier might end up as by-name param to an implicit
|
|
|
-
|
|
|
- /** Types qualifier `tree` of a select node.
|
|
|
- * E.g. is tree occurs in a context like `tree.m`.
|
|
|
- */
|
|
|
- @inline final def typedQualifier(tree: Tree, mode: Mode): Tree =
|
|
|
- typedQualifier(tree, mode, WildcardType)
|
|
|
-
|
|
|
- @inline final def typedQualifier(tree: Tree): Tree = typedQualifier(tree, NOmode, WildcardType)
|
|
|
-
|
|
|
- // if a package id is a selection from _root_ in scope, warn about semantics and set symbol for typedQualifier
|
|
|
- @inline final def typedPackageQualifier(tree: Tree): Tree = typedQualifier(checkRootOfPackageQualifier(tree))
|
|
|
-
|
|
|
- def checkRootOfPackageQualifier(q: Tree): Tree = {
|
|
|
- q match {
|
|
|
- case Select(id @ Ident(nme.ROOTPKG), _) if !id.hasExistingSymbol && id.hasAttachment[RootSelection.type] =>
|
|
|
- context.lookupSymbol(nme.ROOTPKG, p => p.hasPackageFlag && !p.isRootPackage) match {
|
|
|
- case LookupSucceeded(_, sym) =>
|
|
|
- runReporting.warning(
|
|
|
- id.pos,
|
|
|
- s"${nme.ROOTPKG} in root position in package definition does not refer to the root package, but to ${sym.fullLocationString}, which is in scope",
|
|
|
- WarningCategory.Other,
|
|
|
- context.owner)
|
|
|
- id.removeAttachment[RootSelection.type]
|
|
|
- id.setSymbol(sym)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- q
|
|
|
- }
|
|
|
-
|
|
|
- /** If import from path starting with _root_, warn if there is a _root_ value in scope,
|
|
|
- * and ensure _root_ can only be the root package in that position.
|
|
|
- */
|
|
|
- def checkRootOfQualifier(q: Tree, mode: Mode): Tree = {
|
|
|
- q match {
|
|
|
- case Ident(nme.ROOTPKG) if !q.hasExistingSymbol && q.hasAttachment[RootSelection.type] =>
|
|
|
- val startContext = if (mode.typingPatternOrTypePat) context.outer else context
|
|
|
- startContext.lookupSymbol(nme.ROOTPKG, !_.isRootPackage) match {
|
|
|
- case LookupSucceeded(_, sym) =>
|
|
|
- runReporting.warning(
|
|
|
- q.pos,
|
|
|
- s"${nme.ROOTPKG} in root position of qualifier refers to the root package, not ${sym.fullLocationString}, which is in scope",
|
|
|
- WarningCategory.Other,
|
|
|
- context.owner)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- q.setSymbol(rootMirror.RootPackage)
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- q
|
|
|
- }
|
|
|
-
|
|
|
- /** Types function part of an application */
|
|
|
- @inline final def typedOperator(tree: Tree): Tree = typed(tree, OperatorModes)
|
|
|
-
|
|
|
- // the qualifier type of a supercall constructor is its first parent class
|
|
|
- private def typedSelectOrSuperQualifier(qual: Tree) =
|
|
|
- context withinSuperInit typed(qual, PolyQualifierModes)
|
|
|
-
|
|
|
- /** Types a pattern with prototype `pt` */
|
|
|
- def typedPattern(tree: Tree, pt: Type): Tree = {
|
|
|
- // We disable implicits because otherwise some constructs will
|
|
|
- // type check which should not. The pattern matcher does not
|
|
|
- // perform implicit conversions in an attempt to consummate a match.
|
|
|
-
|
|
|
- // on the one hand,
|
|
|
- // "abc" match { case Seq('a', 'b', 'c') => true }
|
|
|
- // should be ruled out statically, otherwise this is a runtime
|
|
|
- // error both because there is an implicit from String to Seq
|
|
|
- // (even though such implicits are not used by the matcher) and
|
|
|
- // because the typer is fine with concluding that "abc" might
|
|
|
- // be of type "String with Seq[T]" and thus eligible for a call
|
|
|
- // to unapplySeq.
|
|
|
-
|
|
|
- // on the other hand, we want to be able to use implicits to add members retro-actively (e.g., add xml to StringContext)
|
|
|
-
|
|
|
- // as a compromise, context.enrichmentEnabled tells adaptToMember to go ahead and enrich,
|
|
|
- // but arbitrary conversions (in adapt) are disabled
|
|
|
- // TODO: can we achieve the pattern matching bit of the string interpolation SIP without this?
|
|
|
- typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt)))
|
|
|
- }
|
|
|
-
|
|
|
- /** Types a (fully parameterized) type tree */
|
|
|
- def typedType(tree: Tree, mode: Mode): Tree =
|
|
|
- typed(tree, mode.forTypeMode, WildcardType)
|
|
|
-
|
|
|
- /** Types a (fully parameterized) type tree */
|
|
|
- def typedType(tree: Tree): Tree = typedType(tree, NOmode)
|
|
|
-
|
|
|
- /** Types a higher-kinded type tree -- pt denotes the expected kind and must be one of `Kind.WildCard` and `Kind.FromParams` */
|
|
|
- def typedHigherKindedType(tree: Tree, mode: Mode, pt: Type): Tree =
|
|
|
- if (pt != Kind.Wildcard && pt.typeParams.isEmpty) typedType(tree, mode) // kind is known and it's *
|
|
|
- else context withinTypeConstructorAllowed typed(tree, NOmode, pt)
|
|
|
-
|
|
|
- def typedHigherKindedType(tree: Tree, mode: Mode): Tree =
|
|
|
- context withinTypeConstructorAllowed typed(tree)
|
|
|
-
|
|
|
- /** Types a type constructor tree used in a new or supertype */
|
|
|
- def typedTypeConstructor(tree: Tree, mode: Mode): Tree = {
|
|
|
- val result = typed(tree, mode.forTypeMode | FUNmode, WildcardType)
|
|
|
-
|
|
|
- // get rid of type aliases for the following check (#1241)
|
|
|
- result.tpe.dealias match {
|
|
|
- case restpe @ TypeRef(pre, _, _) if !phase.erasedTypes && !pre.isStable && !context.unit.isJava =>
|
|
|
- // The isJava exception if OK only because the only type constructors scalac gets
|
|
|
- // to see are those in the signatures. These do not need a unique object as a prefix.
|
|
|
- // The situation is different for new's and super's, but scalac does not look deep
|
|
|
- // enough to see those. See #3938
|
|
|
- ConstructorPrefixError(tree, restpe)
|
|
|
- case _ =>
|
|
|
- // must not normalize: type application must be (bounds-)checked (during RefChecks), see #2208
|
|
|
- // during uncurry (after refchecks), all types are normalized
|
|
|
- result
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- def typedTypeConstructor(tree: Tree): Tree = typedTypeConstructor(tree, NOmode)
|
|
|
-
|
|
|
- def computeType(tree: Tree, pt: Type): Type = {
|
|
|
- // macros employ different logic of `computeType`
|
|
|
- assert(!context.owner.isMacro, context.owner)
|
|
|
- val tree1 = typed(tree, pt)
|
|
|
- transformed(tree) = tree1
|
|
|
- val tpe = packedType(tree1, context.owner)
|
|
|
- checkExistentialsFeature(tree.pos, tpe, "inferred existential type")
|
|
|
- tpe
|
|
|
- }
|
|
|
-
|
|
|
- def computeMacroDefType(ddef: DefDef, pt: Type): Type = {
|
|
|
- assert(context.owner.isMacro, context.owner)
|
|
|
- assert(ddef.symbol.isMacro, ddef.symbol)
|
|
|
-
|
|
|
- // macro defs are typechecked in `methodSig` (by calling this method) in order to establish their link to macro implementation asap
|
|
|
- // if a macro def doesn't have explicitly specified return type, this method will be called again by `assignTypeToTree`
|
|
|
- // here we guard against this case
|
|
|
- val rhs1 =
|
|
|
- if (transformed contains ddef.rhs) {
|
|
|
- transformed(ddef.rhs)
|
|
|
- } else {
|
|
|
- val rhs1 = typedMacroBody(this, ddef)
|
|
|
- transformed(ddef.rhs) = rhs1
|
|
|
- rhs1
|
|
|
- }
|
|
|
-
|
|
|
- val isMacroBodyOkay = !ddef.symbol.isErroneous && !(rhs1 exists (_.isErroneous)) && rhs1 != EmptyTree
|
|
|
- val shouldInheritMacroImplReturnType = ddef.tpt.isEmpty
|
|
|
- if (isMacroBodyOkay && shouldInheritMacroImplReturnType) {
|
|
|
- val commonMessage = "macro defs must have explicitly specified return types"
|
|
|
- def reportFailure() = {
|
|
|
- ddef.symbol.setFlag(IS_ERROR)
|
|
|
- context.error(ddef.pos, commonMessage)
|
|
|
- }
|
|
|
- reportFailure()
|
|
|
- }
|
|
|
- AnyTpe
|
|
|
- }
|
|
|
-
|
|
|
- @inline final def transformedOr(tree: Tree, op: => Tree): Tree = lookupTransformed(tree) match {
|
|
|
- case Some(tree1) => tree1
|
|
|
- case _ => op
|
|
|
- }
|
|
|
-
|
|
|
- @inline
|
|
|
- final def transformedOrTyped(tree: Tree, mode: Mode, pt: Type): Tree = {
|
|
|
- lookupTransformed(tree) match {
|
|
|
- case Some(tree1) => tree1
|
|
|
- case _ => if (canSkipRhs(tree)) EmptyTree else typed(tree, mode, pt)
|
|
|
- }
|
|
|
- }
|
|
|
- final def lookupTransformed(tree: Tree): Option[Tree] =
|
|
|
- if (phase.erasedTypes) None // OPT save the hashmap lookup in erasure type and beyond
|
|
|
- else transformed remove tree
|
|
|
-
|
|
|
- private final def canSkipRhs(tree: Tree) = settings.Youtline.value && !tree.exists {
|
|
|
- case Super(_, mix) =>
|
|
|
- // conservative approximation of method bodies that may give rise to super accessors which must be
|
|
|
- // stored in pickle.
|
|
|
- context.owner.enclClass.isTrait || mix != tpnme.EMPTY
|
|
|
- case _ => false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /** Finish computation of param aliases after typechecking is completed */
|
|
|
- final def finishComputeParamAlias(): Unit = {
|
|
|
- val classes = superConstructorCalls.keys.toArray
|
|
|
- // superclasses before subclasses to avoid a data race between `superAcc.alias` and `acc.setAlias` below.
|
|
|
- scala.util.Sorting.quickSort(classes)(Ordering.fromLessThan((a, b) => b.isLess(a)))
|
|
|
-
|
|
|
- for (sym <- classes) {
|
|
|
- for ((ownAcc, superAcc) <- superConstructorCalls.getOrElse(sym, Nil)) {
|
|
|
- // We have a corresponding parameter in the super class.
|
|
|
- val superClazz = sym.superClass
|
|
|
- val alias = (
|
|
|
- superAcc.initialize.alias // Is the param accessor is an alias for a field further up the class hierarchy?
|
|
|
- orElse (superAcc getterIn superAcc.owner) // otherwise, lookup the accessor for the super
|
|
|
- filter (alias => superClazz.info.nonPrivateMember(alias.name) == alias) // the accessor must be public
|
|
|
- )
|
|
|
- if (alias.exists && !alias.accessed.isVariable && !isRepeatedParamType(alias.accessed.info)) {
|
|
|
- ownAcc match {
|
|
|
- case acc: TermSymbol if !acc.isVariable && !isByNameParamType(acc.info) =>
|
|
|
- debuglog(s"$acc has alias ${alias.fullLocationString}")
|
|
|
- acc setAlias alias
|
|
|
- case _ =>
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- superConstructorCalls.clear()
|
|
|
}
|
|
|
|
|
|
}
|
|
|
-
|
|
|
-trait TypersStats {
|
|
|
- self: TypesStats with Statistics =>
|
|
|
- val typedIdentCount = newCounter("#typechecked identifiers")
|
|
|
- val typedSelectCount = newCounter("#typechecked selections")
|
|
|
- val typedApplyCount = newCounter("#typechecked applications")
|
|
|
- val subtypeFailed = newSubCounter(" of which in failed", subtypeCount)
|
|
|
- val findMemberFailed = newSubCounter(" of which in failed", findMemberCount)
|
|
|
- val failedSilentNanos = newSubTimer("time spent in failed", typerNanos)
|
|
|
- val failedApplyNanos = newSubTimer(" failed apply", typerNanos)
|
|
|
- val failedOpEqNanos = newSubTimer(" failed op=", typerNanos)
|
|
|
- val isReferencedNanos = newSubTimer("time spent ref scanning", typerNanos)
|
|
|
- val visitsByType = newByClass("#visits by tree node", "typer")(newCounter(""))
|
|
|
- val byTypeNanos = newByClass("time spent by tree node", "typer")(newStackableTimer("", typerNanos))
|
|
|
- val byTypeStack = newTimerStack()
|
|
|
-}
|