summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Checkable.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala30
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala28
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala109
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala52
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala129
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Macros.scala19
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala210
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala72
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala107
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala121
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala64
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Tags.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala845
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala3
19 files changed, 923 insertions, 904 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
index 309b80f9ba..2b6a4c763a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Checkable.scala
@@ -212,8 +212,8 @@ trait Checkable {
)
/** Are all children of these symbols pairwise irreconcilable? */
def allChildrenAreIrreconcilable(sym1: Symbol, sym2: Symbol) = (
- sym1.children.toList forall (c1 =>
- sym2.children.toList forall (c2 =>
+ sym1.sealedChildren.toList forall (c1 =>
+ sym2.sealedChildren.toList forall (c2 =>
areIrreconcilableAsParents(c1, c2)
)
)
diff --git a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
index 56ed0ee16c..2cd4785fbf 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ConstantFolder.scala
@@ -40,9 +40,10 @@ abstract class ConstantFolder {
if ((x ne null) && x.tag != UnitTag) tree setType ConstantType(x)
else tree
} catch {
- case _: ArithmeticException => tree // the code will crash at runtime,
- // but that is better than the
- // compiler itself crashing
+ case e: ArithmeticException =>
+ if (settings.warnConstant)
+ warning(tree.pos, s"Evaluation of a constant expression results in an arithmetic error: ${e.getMessage}")
+ tree
}
private def foldUnop(op: Name, x: Constant): Constant = (op, x.tag) match {
@@ -75,7 +76,7 @@ abstract class ConstantFolder {
case nme.AND => Constant(x.booleanValue & y.booleanValue)
case nme.EQ => Constant(x.booleanValue == y.booleanValue)
case nme.NE => Constant(x.booleanValue != y.booleanValue)
- case _ => null
+ case _ => null
}
private def foldSubrangeOp(op: Name, x: Constant, y: Constant): Constant = op match {
case nme.OR => Constant(x.intValue | y.intValue)
@@ -95,14 +96,20 @@ abstract class ConstantFolder {
case nme.MUL => Constant(x.intValue * y.intValue)
case nme.DIV => Constant(x.intValue / y.intValue)
case nme.MOD => Constant(x.intValue % y.intValue)
- case _ => null
+ case _ => null
}
private def foldLongOp(op: Name, x: Constant, y: Constant): Constant = op match {
case nme.OR => Constant(x.longValue | y.longValue)
case nme.XOR => Constant(x.longValue ^ y.longValue)
case nme.AND => Constant(x.longValue & y.longValue)
- case nme.LSL => Constant(x.longValue << y.longValue)
+ case nme.LSL if x.tag <= IntTag
+ => Constant(x.intValue << y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5)
+ case nme.LSL => Constant(x.longValue << y.longValue)
+ case nme.LSR if x.tag <= IntTag
+ => Constant(x.intValue >>> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5)
case nme.LSR => Constant(x.longValue >>> y.longValue)
+ case nme.ASR if x.tag <= IntTag
+ => Constant(x.intValue >> y.longValue.toInt) // TODO: remove .toInt once starr includes the fix for SI-9516 (2.12.0-M5)
case nme.ASR => Constant(x.longValue >> y.longValue)
case nme.EQ => Constant(x.longValue == y.longValue)
case nme.NE => Constant(x.longValue != y.longValue)
@@ -115,7 +122,7 @@ abstract class ConstantFolder {
case nme.MUL => Constant(x.longValue * y.longValue)
case nme.DIV => Constant(x.longValue / y.longValue)
case nme.MOD => Constant(x.longValue % y.longValue)
- case _ => null
+ case _ => null
}
private def foldFloatOp(op: Name, x: Constant, y: Constant): Constant = op match {
case nme.EQ => Constant(x.floatValue == y.floatValue)
@@ -129,7 +136,7 @@ abstract class ConstantFolder {
case nme.MUL => Constant(x.floatValue * y.floatValue)
case nme.DIV => Constant(x.floatValue / y.floatValue)
case nme.MOD => Constant(x.floatValue % y.floatValue)
- case _ => null
+ case _ => null
}
private def foldDoubleOp(op: Name, x: Constant, y: Constant): Constant = op match {
case nme.EQ => Constant(x.doubleValue == y.doubleValue)
@@ -143,7 +150,7 @@ abstract class ConstantFolder {
case nme.MUL => Constant(x.doubleValue * y.doubleValue)
case nme.DIV => Constant(x.doubleValue / y.doubleValue)
case nme.MOD => Constant(x.doubleValue % y.doubleValue)
- case _ => null
+ case _ => null
}
private def foldBinop(op: Name, x: Constant, y: Constant): Constant = {
@@ -152,7 +159,7 @@ abstract class ConstantFolder {
else if (x.isNumeric && y.isNumeric) math.max(x.tag, y.tag)
else NoTag
- try optag match {
+ optag match {
case BooleanTag => foldBooleanOp(op, x, y)
case ByteTag | ShortTag | CharTag | IntTag => foldSubrangeOp(op, x, y)
case LongTag => foldLongOp(op, x, y)
@@ -161,8 +168,5 @@ abstract class ConstantFolder {
case StringTag if op == nme.ADD => Constant(x.stringValue + y.stringValue)
case _ => null
}
- catch {
- case ex: ArithmeticException => null
- }
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 727f09290a..e190b57017 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -7,7 +7,6 @@ package scala.tools.nsc
package typechecker
import scala.reflect.internal.util.StringOps.{ countElementsAsString, countAsString }
-import symtab.Flags.IS_ERROR
import scala.compat.Platform.EOL
import scala.reflect.runtime.ReflectionUtils
import scala.reflect.macros.runtime.AbortMacroException
@@ -470,6 +469,11 @@ trait ContextErrors {
setError(tree)
}
+ def ConstructorRecursesError(tree: Tree) = {
+ issueNormalTypeError(tree, "constructor invokes itself")
+ setError(tree)
+ }
+
def OnlyDeclarationsError(tree: Tree) = {
issueNormalTypeError(tree, "only declarations allowed here")
setError(tree)
@@ -1105,7 +1109,7 @@ trait ContextErrors {
def GetterDefinedTwiceError(getter: Symbol) =
issueSymbolTypeError(getter, getter+" is defined twice")
- def ValOrValWithSetterSuffixError(tree: Tree) =
+ def ValOrVarWithSetterSuffixError(tree: Tree) =
issueNormalTypeError(tree, "Names of vals or vars may not end in `_='")
def PrivateThisCaseClassParameterError(tree: Tree) =
@@ -1212,7 +1216,8 @@ trait ContextErrors {
import definitions._
- def AmbiguousImplicitError(info1: ImplicitInfo, info2: ImplicitInfo,
+ def AmbiguousImplicitError(info1: ImplicitInfo, tree1: Tree,
+ info2: ImplicitInfo, tree2: Tree,
pre1: String, pre2: String, trailer: String)
(isView: Boolean, pt: Type, tree: Tree)(implicit context0: Context) = {
if (!info1.tpe.isErroneous && !info2.tpe.isErroneous) {
@@ -1248,10 +1253,21 @@ trait ContextErrors {
if (explanation == "") "" else "\n" + explanation
)
}
+
+ def treeTypeArgs(annotatedTree: Tree): List[String] = annotatedTree match {
+ case TypeApply(_, args) => args.map(_.toString)
+ case Block(_, Function(_, treeInfo.Applied(_, targs, _))) => targs.map(_.toString) // eta expansion, see neg/t9527b.scala
+ case _ => Nil
+ }
+
context.issueAmbiguousError(AmbiguousImplicitTypeError(tree,
- if (isView) viewMsg
- else s"ambiguous implicit values:\n${coreMsg}match expected type $pt")
- )
+ (info1.sym, info2.sym) match {
+ case (ImplicitAmbiguousMsg(msg), _) => msg.format(treeTypeArgs(tree1))
+ case (_, ImplicitAmbiguousMsg(msg)) => msg.format(treeTypeArgs(tree2))
+ case (_, _) if isView => viewMsg
+ case (_, _) => s"ambiguous implicit values:\n${coreMsg}match expected type $pt"
+ }
+ ))
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index 5ec16e84bb..bcc1ed3e64 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -24,7 +24,8 @@ trait Contexts { self: Analyzer =>
object NoContext
extends Context(EmptyTree, NoSymbol, EmptyScope, NoCompilationUnit,
- null) { // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer`
+ // We can't pass the uninitialized `this`. Instead, we treat null specially in `Context#outer`
+ null) {
enclClass = this
enclMethod = this
@@ -48,12 +49,11 @@ trait Contexts { self: Analyzer =>
def ambiguousDefnAndImport(owner: Symbol, imp: ImportInfo) =
LookupAmbiguous(s"it is both defined in $owner and imported subsequently by \n$imp")
- private lazy val startContext = {
- NoContext.make(
+ private lazy val startContext = NoContext.make(
Template(List(), noSelfType, List()) setSymbol global.NoSymbol setType global.NoType,
rootMirror.RootClass,
- rootMirror.RootClass.info.decls)
- }
+ rootMirror.RootClass.info.decls
+ )
private lazy val allUsedSelectors =
mutable.Map[ImportInfo, Set[ImportSelector]]() withDefaultValue Set()
@@ -387,8 +387,10 @@ trait Contexts { self: Analyzer =>
@inline final def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op)
@inline final def withImplicitsDisabled[T](op: => T): T = withMode(disabled = ImplicitsEnabled | EnrichmentEnabled)(op)
@inline final def withImplicitsDisabledAllowEnrichment[T](op: => T): T = withMode(enabled = EnrichmentEnabled, disabled = ImplicitsEnabled)(op)
+ @inline final def withImplicits[T](enabled: Boolean)(op: => T): T = if (enabled) withImplicitsEnabled(op) else withImplicitsDisabled(op)
@inline final def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op)
@inline final def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op)
+ @inline final def withMacros[T](enabled: Boolean)(op: => T): T = if (enabled) withMacrosEnabled(op) else withMacrosDisabled(op)
@inline final def withinStarPatterns[T](op: => T): T = withMode(enabled = StarPatterns)(op)
@inline final def withinSuperInit[T](op: => T): T = withMode(enabled = SuperInit)(op)
@inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op)
@@ -723,7 +725,6 @@ trait Contexts { self: Analyzer =>
( (ab.isTerm || ab == rootMirror.RootClass)
|| (accessWithin(ab) || accessWithinLinked(ab)) &&
( !sym.isLocalToThis
- || sym.owner.isImplClass // allow private local accesses to impl classes
|| sym.isProtected && isSubThisType(pre, sym.owner)
|| pre =:= sym.owner.thisType
)
@@ -806,11 +807,13 @@ trait Contexts { self: Analyzer =>
(e ne null) && (e.owner == scope) && (!settings.isScala212 || e.sym.exists)
})
- private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) =
- for {
- sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives
- if isQualifyingImplicit(name, sym, pre, imported = true)
- } f(sym)
+ /** Do something with the symbols with name `name` imported via the import in `imp`,
+ * if any such symbol is accessible from this context and is a qualifying implicit.
+ */
+ private def withQualifyingImplicitAlternatives(imp: ImportInfo, name: Name, pre: Type)(f: Symbol => Unit) = for {
+ sym <- importedAccessibleSymbol(imp, name, requireExplicit = false, record = false).alternatives
+ if isQualifyingImplicit(name, sym, pre, imported = true)
+ } f(sym)
private def collectImplicits(syms: Scope, pre: Type, imported: Boolean = false): List[ImplicitInfo] =
for (sym <- syms.toList if isQualifyingImplicit(sym.name, sym, pre, imported)) yield
@@ -819,12 +822,8 @@ trait Contexts { self: Analyzer =>
private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = {
val qual = imp.qual
- val pre =
- if (qual.tpe.typeSymbol.isPackageClass)
- // SI-6225 important if the imported symbol is inherited by the package object.
- singleType(qual.tpe, qual.tpe member nme.PACKAGE)
- else
- qual.tpe
+ val qualSym = qual.tpe.typeSymbol
+ val pre = qual.tpe
def collect(sels: List[ImportSelector]): List[ImplicitInfo] = sels match {
case List() =>
List()
@@ -900,7 +899,8 @@ trait Contexts { self: Analyzer =>
Some(collectImplicitImports(imports.head))
} else if (owner.isPackageClass) {
// the corresponding package object may contain implicit members.
- Some(collectImplicits(owner.tpe.implicitMembers, owner.tpe))
+ val pre = owner.packageObject.typeOfThis
+ Some(collectImplicits(pre.implicitMembers, pre))
} else Some(Nil)
}
@@ -967,51 +967,19 @@ trait Contexts { self: Analyzer =>
private def importedAccessibleSymbol(imp: ImportInfo, name: Name, requireExplicit: Boolean, record: Boolean): Symbol =
imp.importedSymbol(name, requireExplicit, record) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false))
- /** Is `sym` defined in package object of package `pkg`?
- * Since sym may be defined in some parent of the package object,
- * we cannot inspect its owner only; we have to go through the
- * info of the package object. However to avoid cycles we'll check
- * what other ways we can before pushing that way.
+ private def requiresQualifier(s: Symbol): Boolean = (
+ s.owner.isClass
+ && !s.owner.isPackageClass
+ && !s.isTypeParameterOrSkolem
+ && !s.isExistentiallyBound
+ )
+
+ /** Must `sym` defined in package object of package `pkg`, if
+ * it selected from a prefix with `pkg` as its type symbol?
*/
def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = {
- def uninitialized(what: String) = {
- log(s"Cannot look for $sym in package object of $pkg; $what is not initialized.")
- false
- }
- def pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg
- def matchesInfo = (
- // need to be careful here to not get a cyclic reference during bootstrap
- if (pkg.isInitialized) {
- val module = pkg.info member nme.PACKAGEkw
- if (module.isInitialized)
- module.info.member(sym.name).alternatives contains sym
- else
- uninitialized("" + module)
- }
- else uninitialized("" + pkg)
- )
- def inPackageObject(sym: Symbol) = (
- // To be in the package object, one of these must be true:
- // 1) sym.owner is a package object class, and sym.owner.owner is the package class for `pkg`
- // 2) sym.owner is inherited by the correct package object class
- // We try to establish 1) by inspecting the owners directly, and then we try
- // to rule out 2), and only if both those fail do we resort to looking in the info.
- !sym.hasPackageFlag && sym.owner.exists && (
- if (sym.owner.isPackageObjectClass)
- sym.owner.owner == pkgClass
- else
- !sym.owner.isPackageClass && matchesInfo
- )
- )
-
- // An overloaded symbol might not have the expected owner!
- // The alternatives must be inspected directly.
- pkgClass.isPackageClass && (
- if (sym.isOverloaded)
- sym.alternatives forall (isInPackageObject(_, pkg))
- else
- inPackageObject(sym)
- )
+ if (sym.isOverloaded) sym.alternatives.exists(alt => isInPackageObject(alt, pkg))
+ else pkg.hasPackageFlag && sym.owner != pkg && requiresQualifier(sym)
}
def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess
@@ -1048,11 +1016,6 @@ trait Contexts { self: Analyzer =>
|| unit.exists && s.sourceFile != unit.source.file
)
)
- def requiresQualifier(s: Symbol) = (
- s.owner.isClass
- && !s.owner.isPackageClass
- && !s.isTypeParameterOrSkolem
- )
def lookupInPrefix(name: Name) = pre member name filter qualifies
def accessibleInPrefix(s: Symbol) = isAccessible(s, pre, superAccess = false)
@@ -1407,7 +1370,6 @@ trait Contexts { self: Analyzer =>
protected def handleError(pos: Position, msg: String): Unit = onTreeCheckerError(pos, msg)
}
-
class ImportInfo(val tree: Import, val depth: Int) {
def pos = tree.pos
def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos
@@ -1423,14 +1385,15 @@ trait Contexts { self: Analyzer =>
def isExplicitImport(name: Name): Boolean =
tree.selectors exists (_.rename == name.toTermName)
- /** The symbol with name `name` imported from import clause `tree`.
- */
+ /** The symbol with name `name` imported from import clause `tree`. */
def importedSymbol(name: Name): Symbol = importedSymbol(name, requireExplicit = false, record = true)
- private def recordUsage(sel: ImportSelector, result: Symbol) {
- def posstr = pos.source.file.name + ":" + posOf(sel).line
- def resstr = if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)" else s"(expr=${tree.expr}, ${result.fullLocationString})"
- debuglog(s"In $this at $posstr, selector '${selectorString(sel)}' resolved to $resstr")
+ private def recordUsage(sel: ImportSelector, result: Symbol): Unit = {
+ debuglog(s"In $this at ${ pos.source.file.name }:${ posOf(sel).line }, selector '${ selectorString(sel)
+ }' resolved to ${
+ if (tree.symbol.hasCompleteInfo) s"(qual=$qual, $result)"
+ else s"(expr=${tree.expr}, ${result.fullLocationString})"
+ }")
allUsedSelectors(this) += sel
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
index 69ae6ec0c8..0c10242950 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -7,7 +7,7 @@ package scala.tools.nsc
package typechecker
import scala.tools.nsc.symtab.Flags
-import scala.collection.{ mutable, immutable }
+import scala.collection.mutable
/** Duplicate trees and re-type check them, taking care to replace
* and create fresh symbols for new local definitions.
diff --git a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
index 7092f00bff..97de2b6c85 100644
--- a/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/EtaExpansion.scala
@@ -15,39 +15,29 @@ import symtab.Flags._
* @version 1.0
*/
trait EtaExpansion { self: Analyzer =>
-
import global._
- object etaExpansion {
- private def isMatch(vparam: ValDef, arg: Tree) = arg match {
- case Ident(name) => vparam.name == name
- case _ => false
- }
-
- def unapply(tree: Tree): Option[(List[ValDef], Tree, List[Tree])] = tree match {
- case Function(vparams, Apply(fn, args)) if (vparams corresponds args)(isMatch) =>
- Some((vparams, fn, args))
- case _ =>
- None
- }
- }
-
- /** <p>
- * Expand partial function applications of type `type`.
- * </p><pre>
- * p.f(es_1)...(es_n)
- * ==> {
- * <b>private synthetic val</b> eta$f = p.f // if p is not stable
- * ...
- * <b>private synthetic val</b> eta$e_i = e_i // if e_i is not stable
- * ...
- * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
- * }</pre>
- * <p>
- * tree is already attributed
- * </p>
- */
- def etaExpand(unit : CompilationUnit, tree: Tree, typer: Typer): Tree = {
+ /** Expand partial method application `p.f(es_1)...(es_n)`.
+ *
+ * We expand this to the following block, which evaluates
+ * the target of the application and its supplied arguments if needed (they are not stable),
+ * and then wraps a Function that abstracts over the missing arguments.
+ *
+ * ```
+ * {
+ * private synthetic val eta$f = p.f // if p is not stable
+ * ...
+ * private synthetic val eta$e_i = e_i // if e_i is not stable
+ * ...
+ * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
+ * }
+ * ```
+ *
+ * This is called from instantiateToMethodType after type checking `tree`,
+ * and we realize we have a method type, where a function type (builtin or SAM) is expected.
+ *
+ **/
+ def etaExpand(unit: CompilationUnit, tree: Tree, typer: Typer): Tree = {
val tpe = tree.tpe
var cnt = 0 // for NoPosition
def freshName() = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 509ce59104..bee2ae8e99 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -13,7 +13,7 @@ package tools.nsc
package typechecker
import scala.annotation.tailrec
-import scala.collection.{ mutable, immutable }
+import scala.collection.mutable
import mutable.{ LinkedHashMap, ListBuffer }
import scala.util.matching.Regex
import symtab.Flags._
@@ -34,14 +34,33 @@ trait Implicits {
import typingStack.{ printTyping }
import typeDebug._
+ // standard usage
+ def inferImplicitFor(pt: Type, tree: Tree, context: Context, reportAmbiguous: Boolean = true): SearchResult =
+ inferImplicit(tree, pt, reportAmbiguous, isView = false, context, saveAmbiguousDivergent = true, tree.pos)
+
+ // used by typer to find an implicit coercion
+ def inferImplicitView(from: Type, to: Type, tree: Tree, context: Context, reportAmbiguous: Boolean, saveAmbiguousDivergent: Boolean) =
+ inferImplicit(tree, Function1(from, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent, tree.pos)
+
+ // used for manifests, typetags, checking language features, scaladoc
+ def inferImplicitByType(pt: Type, context: Context, pos: Position = NoPosition): SearchResult =
+ inferImplicit(EmptyTree, pt, reportAmbiguous = true, isView = false, context, saveAmbiguousDivergent = true, pos)
+
+ def inferImplicitByTypeSilent(pt: Type, context: Context, pos: Position = NoPosition): SearchResult =
+ inferImplicit(EmptyTree, pt, reportAmbiguous = false, isView = false, context, saveAmbiguousDivergent = false, pos)
+
+ @deprecated("Unused in scalac", "2.12.0-M4")
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context): SearchResult =
inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent = true, tree.pos)
+ @deprecated("Unused in scalac", "2.12.0-M4")
def inferImplicit(tree: Tree, pt: Type, reportAmbiguous: Boolean, isView: Boolean, context: Context, saveAmbiguousDivergent: Boolean): SearchResult =
inferImplicit(tree, pt, reportAmbiguous, isView, context, saveAmbiguousDivergent, tree.pos)
- /** Search for an implicit value. See the comment on `result` at the end of class `ImplicitSearch`
- * for more info how the search is conducted.
+ /** Search for an implicit value. Consider using one of the convenience methods above. This one has many boolean levers.
+ *
+ * See the comment on `result` at the end of class `ImplicitSearch` for more info how the search is conducted.
+ *
* @param tree The tree for which the implicit needs to be inserted.
* (the inference might instantiate some of the undetermined
* type parameters of that tree.
@@ -92,9 +111,10 @@ trait Implicits {
/** A friendly wrapper over inferImplicit to be used in macro contexts and toolboxes.
*/
def inferImplicit(tree: Tree, pt: Type, isView: Boolean, context: Context, silent: Boolean, withMacrosDisabled: Boolean, pos: Position, onError: (Position, String) => Unit): Tree = {
- val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _)
- def wrapper(inference: => SearchResult) = wrapper1(inference)
- val result = wrapper(inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos))
+ val result = context.withMacros(enabled = !withMacrosDisabled) {
+ inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context, saveAmbiguousDivergent = !silent, pos)
+ }
+
if (result.isFailure && !silent) {
val err = context.reporter.firstError
val errPos = err.map(_.errPos).getOrElse(pos)
@@ -304,6 +324,10 @@ trait Implicits {
*/
object Function1 {
val Sym = FunctionClass(1)
+ val Pre = Sym.typeConstructor.prefix
+
+ def apply(from: Type, to: Type) = TypeRef(Pre, Sym, List(from, to))
+
// It is tempting to think that this should be inspecting "tp baseType Sym"
// rather than tp. See test case run/t8280 and the commit message which
// accompanies it for explanation why that isn't done.
@@ -887,7 +911,7 @@ trait Implicits {
* - find the most likely one
* - if it matches, forget about all others it improves upon
*/
- @tailrec private def rankImplicits(pending: Infos, acc: Infos): Infos = pending match {
+ @tailrec private def rankImplicits(pending: Infos, acc: List[(SearchResult, ImplicitInfo)]): List[(SearchResult, ImplicitInfo)] = pending match {
case Nil => acc
case firstPending :: otherPending =>
def firstPendingImproves(alt: ImplicitInfo) =
@@ -914,7 +938,7 @@ trait Implicits {
val pendingImprovingBest = undoLog undo {
otherPending filterNot firstPendingImproves
}
- rankImplicits(pendingImprovingBest, firstPending :: acc)
+ rankImplicits(pendingImprovingBest, (newBest, firstPending) :: acc)
}
}
@@ -930,14 +954,14 @@ trait Implicits {
// So if there is any element not improved upon by the first it is an error.
rankImplicits(eligible, Nil) match {
case Nil => ()
- case chosen :: rest =>
- rest find (alt => !improves(chosen, alt)) match {
- case Some(competing) =>
- AmbiguousImplicitError(chosen, competing, "both", "and", "")(isView, pt, tree)(context)
+ case (chosenResult, chosenInfo) :: rest =>
+ rest find { case (_, alt) => !improves(chosenInfo, alt) } match {
+ case Some((competingResult, competingInfo)) =>
+ AmbiguousImplicitError(chosenInfo, chosenResult.tree, competingInfo, competingResult.tree, "both", "and", "")(isView, pt, tree)(context)
return AmbiguousSearchFailure // Stop the search once ambiguity is encountered, see t4457_2.scala
case _ =>
- if (isView) chosen.useCountView += 1
- else chosen.useCountArg += 1
+ if (isView) chosenInfo.useCountView += 1
+ else chosenInfo.useCountArg += 1
}
}
@@ -1014,15 +1038,12 @@ trait Implicits {
}
case None =>
if (pre.isStable && !pre.typeSymbol.isExistentiallyBound) {
- val companion = companionSymbolOf(sym, context)
- companion.moduleClass match {
- case mc: ModuleClassSymbol =>
- val infos =
- for (im <- mc.implicitMembers.toList) yield new ImplicitInfo(im.name, singleType(pre, companion), im)
- if (infos.nonEmpty)
- infoMap += (sym -> infos)
- case _ =>
- }
+ val pre1 =
+ if (sym.isPackageClass) sym.packageObject.typeOfThis
+ else singleType(pre, companionSymbolOf(sym, context))
+ val infos = pre1.implicitMembers.iterator.map(mem => new ImplicitInfo(mem.name, pre1, mem)).toList
+ if (infos.nonEmpty)
+ infoMap += (sym -> infos)
}
val bts = tp.baseTypeSeq
var i = 1
@@ -1210,7 +1231,7 @@ trait Implicits {
/* Re-wraps a type in a manifest before calling inferImplicit on the result */
def findManifest(tp: Type, manifestClass: Symbol = if (full) FullManifestClass else PartialManifestClass) =
- inferImplicit(tree, appliedType(manifestClass, tp), reportAmbiguous = true, isView = false, context).tree
+ inferImplicitFor(appliedType(manifestClass, tp), tree, context).tree
def findSubManifest(tp: Type) = findManifest(tp, if (full) FullManifestClass else OptManifestClass)
def mot(tp0: Type, from: List[Symbol], to: List[Type]): SearchResult = {
@@ -1413,7 +1434,7 @@ trait Implicits {
}
if (result.isFailure && settings.debug) // debuglog is not inlined for some reason
- log("no implicits found for "+pt+" "+pt.typeSymbol.info.baseClasses+" "+implicitsOfExpectedType)
+ log(s"no implicits found for ${pt} ${pt.typeSymbol.info.baseClasses} ${implicitsOfExpectedType}")
result
}
@@ -1450,9 +1471,9 @@ trait Implicits {
}
}
- object ImplicitNotFoundMsg {
- def unapply(sym: Symbol): Option[(Message)] = sym.implicitNotFoundMsg match {
- case Some(m) => Some(new Message(sym, m))
+ class ImplicitAnnotationMsg(f: Symbol => Option[String], clazz: Symbol, annotationName: String) {
+ def unapply(sym: Symbol): Option[(Message)] = f(sym) match {
+ case Some(m) => Some(new Message(sym, m, annotationName))
case None if sym.isAliasType =>
// perform exactly one step of dealiasing
// this is necessary because ClassManifests are now aliased to ClassTags
@@ -1464,41 +1485,45 @@ trait Implicits {
// check the message's syntax: should be a string literal that may contain occurrences of the string "${X}",
// where `X` refers to a type parameter of `sym`
def check(sym: Symbol): Option[String] =
- sym.getAnnotation(ImplicitNotFoundClass).flatMap(_.stringArg(0) match {
- case Some(m) => new Message(sym, m).validate
- case None => Some("Missing argument `msg` on implicitNotFound annotation.")
+ sym.getAnnotation(clazz).flatMap(_.stringArg(0) match {
+ case Some(m) => new Message(sym, m, annotationName).validate
+ case None => Some(s"Missing argument `msg` on $annotationName annotation.")
})
+ }
+ object ImplicitNotFoundMsg extends ImplicitAnnotationMsg(_.implicitNotFoundMsg, ImplicitNotFoundClass, "implicitNotFound")
+
+ object ImplicitAmbiguousMsg extends ImplicitAnnotationMsg(_.implicitAmbiguousMsg, ImplicitAmbiguousClass, "implicitAmbiguous")
+
+ class Message(sym: Symbol, msg: String, annotationName: String) {
// http://dcsobral.blogspot.com/2010/01/string-interpolation-in-scala-with.html
private val Intersobralator = """\$\{\s*([^}\s]+)\s*\}""".r
- class Message(sym: Symbol, msg: String) {
- private def interpolate(text: String, vars: Map[String, String]) =
- Intersobralator.replaceAllIn(text, (_: Regex.Match) match {
- case Regex.Groups(v) => Regex quoteReplacement vars.getOrElse(v, "")
+ private def interpolate(text: String, vars: Map[String, String]) =
+ Intersobralator.replaceAllIn(text, (_: Regex.Match) match {
+ case Regex.Groups(v) => Regex quoteReplacement vars.getOrElse(v, "")
// #3915: need to quote replacement string since it may include $'s (such as the interpreter's $iw)
- })
+ })
- private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName)
- private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs
+ private lazy val typeParamNames: List[String] = sym.typeParams.map(_.decodedName)
+ private def typeArgsAtSym(paramTp: Type) = paramTp.baseType(sym).typeArgs
- def format(paramName: Name, paramTp: Type): String = format(typeArgsAtSym(paramTp) map (_.toString))
+ def format(paramName: Name, paramTp: Type): String = format(typeArgsAtSym(paramTp) map (_.toString))
- def format(typeArgs: List[String]): String =
- interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc?
+ def format(typeArgs: List[String]): String =
+ interpolate(msg, Map((typeParamNames zip typeArgs): _*)) // TODO: give access to the name and type of the implicit argument, etc?
- def validate: Option[String] = {
- val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSet
- val decls = typeParamNames.toSet
+ def validate: Option[String] = {
+ val refs = Intersobralator.findAllMatchIn(msg).map(_ group 1).toSet
+ val decls = typeParamNames.toSet
- (refs &~ decls) match {
- case s if s.isEmpty => None
- case unboundNames =>
- val singular = unboundNames.size == 1
- val ess = if (singular) "" else "s"
- val bee = if (singular) "is" else "are"
- Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @implicitNotFound annotation $bee not defined by $sym.")
- }
+ (refs &~ decls) match {
+ case s if s.isEmpty => None
+ case unboundNames =>
+ val singular = unboundNames.size == 1
+ val ess = if (singular) "" else "s"
+ val bee = if (singular) "is" else "are"
+ Some(s"The type parameter$ess ${unboundNames mkString ", "} referenced in the message of the @$annotationName annotation $bee not defined by $sym.")
}
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 9f7bdf7aff..dc91d23011 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -164,7 +164,9 @@ trait Infer extends Checkable {
| was: $restpe
| now""")(normalize(restpe))
case mt @ MethodType(_, restpe) if mt.isImplicit => normalize(restpe)
- case mt @ MethodType(_, restpe) if !mt.isDependentMethodType => functionType(mt.paramTypes, normalize(restpe))
+ case mt @ MethodType(_, restpe) if !mt.isDependentMethodType =>
+ if (phase.erasedTypes) FunctionClass(mt.params.length).tpe
+ else functionType(mt.paramTypes, normalize(restpe))
case NullaryMethodType(restpe) => normalize(restpe)
case ExistentialType(tparams, qtpe) => newExistentialType(tparams, normalize(qtpe))
case _ => tp // @MAT aliases already handled by subtyping
@@ -295,7 +297,7 @@ trait Infer extends Checkable {
&& !isByNameParamType(tp)
&& isCompatible(tp, dropByName(pt))
)
- def isCompatibleSam(tp: Type, pt: Type): Boolean = {
+ def isCompatibleSam(tp: Type, pt: Type): Boolean = (definitions.isFunctionType(tp) || tp.isInstanceOf[MethodType] || tp.isInstanceOf[PolyType]) && {
val samFun = typer.samToFunctionType(pt)
(samFun ne NoType) && isCompatible(tp, samFun)
}
@@ -1207,6 +1209,7 @@ trait Infer extends Checkable {
}
}
tvars foreach instantiateTypeVar
+ invalidateTreeTpeCaches(tree0, tvars.map(_.origin.typeSymbol))
}
/* If the scrutinee has free type parameters but the pattern does not,
* we have to flip the arguments so the expected type is treated as more
@@ -1217,7 +1220,7 @@ trait Infer extends Checkable {
}
def inferModulePattern(pat: Tree, pt: Type) =
- if (!(pat.tpe <:< pt)) {
+ if ((pat.symbol ne null) && pat.symbol.isModule && !(pat.tpe <:< pt)) {
val ptparams = freeTypeParamsOfTerms(pt)
debuglog("free type params (2) = " + ptparams)
val ptvars = ptparams map freshVar
@@ -1446,7 +1449,7 @@ trait Infer extends Checkable {
log(s"Attaching AntiPolyType-carrying overloaded type to $sym")
// Multiple alternatives which are within bounds; spin up an
// overloaded type which carries an "AntiPolyType" as a prefix.
- val tparams = newAsSeenFromMap(pre, hd.owner) mapOver hd.typeParams
+ val tparams = new AsSeenFromMap(pre, hd.owner) mapOver hd.typeParams
val bounds = tparams map (_.tpeHK) // see e.g., #1236
val tpe = PolyType(tparams, OverloadedType(AntiPolyType(pre, bounds), alts))
finish(sym setInfo tpe, tpe)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
index 3ed128cbc5..d7c53ed3c4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala
@@ -5,17 +5,13 @@ import java.lang.Math.min
import symtab.Flags._
import scala.reflect.internal.util.ScalaClassLoader
import scala.reflect.runtime.ReflectionUtils
-import scala.collection.mutable.ListBuffer
-import scala.reflect.ClassTag
import scala.reflect.internal.util.Statistics
import scala.reflect.macros.util._
import scala.util.control.ControlThrowable
import scala.reflect.internal.util.ListOfNil
import scala.reflect.macros.runtime.{AbortMacroException, MacroRuntimes}
-import scala.reflect.runtime.{universe => ru}
import scala.reflect.macros.compiler.DefaultMacroCompiler
import scala.tools.reflect.FastTrack
-import scala.runtime.ScalaRunTime
import Fingerprint._
/**
@@ -242,7 +238,7 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
if (!payload.contains(field)) failField("is supposed to be there")
val raw: Any = payload(field)
if (raw == null) failField(s"is not supposed to be null")
- val expected = ScalaRunTime.box(clazz)
+ val expected = box(clazz)
val actual = raw.getClass
if (!expected.isAssignableFrom(actual)) failField(s"has wrong type: expected $expected, actual $actual")
raw.asInstanceOf[T]
@@ -259,6 +255,19 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
val signature = unpickle("signature", classOf[List[List[Fingerprint]]])
MacroImplBinding(isBundle, isBlackbox, className, methodName, signature, targs)
}
+
+ private def box[T](clazz: Class[T]): Class[_] = clazz match {
+ case java.lang.Byte.TYPE => classOf[java.lang.Byte]
+ case java.lang.Short.TYPE => classOf[java.lang.Short]
+ case java.lang.Character.TYPE => classOf[java.lang.Character]
+ case java.lang.Integer.TYPE => classOf[java.lang.Integer]
+ case java.lang.Long.TYPE => classOf[java.lang.Long]
+ case java.lang.Float.TYPE => classOf[java.lang.Float]
+ case java.lang.Double.TYPE => classOf[java.lang.Double]
+ case java.lang.Void.TYPE => classOf[scala.runtime.BoxedUnit]
+ case java.lang.Boolean.TYPE => classOf[java.lang.Boolean]
+ case _ => clazz
+ }
}
def bindMacroImpl(macroDef: Symbol, macroImplRef: Tree): Unit = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index f3856db552..c03094bc6a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -6,11 +6,8 @@ package scala.tools.nsc
package typechecker
import symtab.Flags._
-import scala.reflect.internal.util.StringOps.{ ojoin }
-import scala.reflect.ClassTag
+import scala.reflect.internal.util.StringOps.ojoin
import scala.reflect.internal.util.ListOfNil
-import scala.reflect.runtime.{ universe => ru }
-import scala.language.higherKinds
/** Logic related to method synthesis which involves cooperation between
* Namer and Typer.
@@ -22,17 +19,6 @@ trait MethodSynthesis {
import definitions._
import CODE._
- /** The annotations amongst those found on the original symbol which
- * should be propagated to this kind of accessor.
- */
- def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = {
- def annotationFilter(ann: AnnotationInfo) = ann.metaAnnotations match {
- case Nil if ann.defaultTargets.isEmpty => keepClean // no meta-annotations or default targets
- case Nil => ann.defaultTargets contains category // default targets exist for ann
- case metas => metas exists (_ matches category) // meta-annotations attached to ann
- }
- initial filter annotationFilter
- }
class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
def mkThis = This(clazz) setPos clazz.pos.focus
@@ -134,39 +120,41 @@ trait MethodSynthesis {
ImplicitClassWrapper(tree).createAndEnterSymbol()
}
- def enterGetterSetter(tree: ValDef) {
- val ValDef(mods, name, _, _) = tree
- if (nme.isSetterName(name))
- ValOrValWithSetterSuffixError(tree)
-
- tree.symbol = (
- if (mods.isLazy) {
+ // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories
+ def enterGetterSetter(tree: ValDef): Unit = {
+ tree.symbol =
+ if (tree.mods.isLazy) {
val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol()
enterLazyVal(tree, lazyValGetter)
} else {
- if (mods.isPrivateLocal)
- PrivateThisCaseClassParameterError(tree)
- val getter = Getter(tree).createAndEnterSymbol()
+ val getter = Getter(tree)
+ val getterSym = getter.createAndEnterSymbol()
+
// Create the setter if necessary.
- if (mods.isMutable)
+ if (getter.needsSetter)
Setter(tree).createAndEnterSymbol()
- // If abstract, the tree gets the getter's symbol. Otherwise, create a field.
- if (mods.isDeferred) getter setPos tree.pos
+ // If the getter's abstract the tree gets the getter's symbol,
+ // otherwise, create a field (assume the getter requires storage).
+ // NOTE: we cannot look at symbol info, since we're in the process of deriving them
+ // (luckily, they only matter for lazy vals, which we've ruled out in this else branch,
+ // and `doNotDeriveField` will skip them if `!mods.isLazy`)
+ if (Field.noFieldFor(tree)) getterSym setPos tree.pos
else enterStrictVal(tree)
}
- )
enterBeans(tree)
}
+ import AnnotationInfo.{mkFilter => annotationFilter}
+
/** This is called for those ValDefs which addDerivedTrees ignores, but
* which might have a warnable annotation situation.
*/
private def warnForDroppedAnnotations(tree: Tree) {
val annotations = tree.symbol.initialize.annotations
val targetClass = defaultAnnotationTarget(tree)
- val retained = deriveAnnotations(annotations, targetClass, keepClean = true)
+ val retained = annotations filter annotationFilter(targetClass, defaultRetention = true)
annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass))
}
@@ -177,11 +165,11 @@ trait MethodSynthesis {
}
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
- case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) =>
+ case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar =>
// If we don't save the annotations, they seem to wander off.
val annotations = stat.symbol.initialize.annotations
val trees = (
- allValDefDerived(vd)
+ (field(vd) ::: standardAccessors(vd) ::: beanAccessors(vd))
map (acc => atPos(vd.pos.focus)(acc derive annotations))
filterNot (_ eq EmptyTree)
)
@@ -208,8 +196,8 @@ trait MethodSynthesis {
context.unit.synthetics get meth match {
case Some(mdef) =>
context.unit.synthetics -= meth
- meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false)
- cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true)
+ meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false))
+ cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true))
List(cd, mdef)
case _ =>
// Shouldn't happen, but let's give ourselves a reasonable error when it does
@@ -221,11 +209,14 @@ trait MethodSynthesis {
stat :: Nil
}
- def standardAccessors(vd: ValDef): List[DerivedFromValDef] = (
- if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
- else if (vd.mods.isLazy) List(LazyValGetter(vd))
- else List(Getter(vd))
- )
+ def standardAccessors(vd: ValDef): List[DerivedFromValDef] =
+ if (vd.mods.isLazy) List(LazyValGetter(vd))
+ else {
+ val getter = Getter(vd)
+ if (getter.needsSetter) List(getter, Setter(vd))
+ else List(getter)
+ }
+
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil
if (vd.symbol hasAnnotation BeanPropertyAttr)
@@ -234,15 +225,8 @@ trait MethodSynthesis {
BooleanBeanGetter(vd) :: setter
else Nil
}
- def allValDefDerived(vd: ValDef) = {
- val field = if (vd.mods.isDeferred || (vd.mods.isLazy && hasUnitType(vd.symbol))) Nil
- else List(Field(vd))
- field ::: standardAccessors(vd) ::: beanAccessors(vd)
- }
- // Take into account annotations so that we keep annotated unit lazy val
- // to get better error message already from the cps plugin itself
- def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
+ def field(vd: ValDef): List[Field] = if (Field.noFieldFor(vd)) Nil else List(Field(vd))
/** This trait assembles what's needed for synthesizing derived methods.
* Important: Typically, instances of this trait are created TWICE for each derived
@@ -260,7 +244,6 @@ trait MethodSynthesis {
def name: TermName
/** The flags that are retained from the original symbol */
-
def flagsMask: Long
/** The flags that the derived symbol has in addition to those retained from
@@ -284,8 +267,9 @@ trait MethodSynthesis {
def enclClass: Symbol
// Final methods to make the rest easier to reason about.
- final def mods = tree.mods
- final def basisSym = tree.symbol
+ final def mods = tree.mods
+ final def basisSym = tree.symbol
+ final def derivedMods = mods & flagsMask | flagsExtra
}
sealed trait DerivedFromClassDef extends DerivedFromMemberDef {
@@ -297,25 +281,19 @@ trait MethodSynthesis {
def tree: ValDef
final def enclClass = basisSym.enclClass
- /** Which meta-annotation is associated with this kind of entity.
- * Presently one of: field, getter, setter, beanGetter, beanSetter, param.
- */
- def category: Symbol
/* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
final def fieldSelection = Select(This(enclClass), basisSym)
- final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil)
def derivedSym: Symbol = tree.symbol
def derivedTree: Tree = EmptyTree
def isSetter = false
def isDeferred = mods.isDeferred
- def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept.
def validate() { }
- def createAndEnterSymbol(): Symbol = {
- val sym = owner.newMethod(name, tree.pos.focus, (tree.mods.flags & flagsMask) | flagsExtra)
+ def createAndEnterSymbol(): MethodSymbol = {
+ val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
setPrivateWithin(tree, sym)
enterInScope(sym)
sym setInfo completer(sym)
@@ -328,12 +306,35 @@ trait MethodSynthesis {
}
final def derive(initial: List[AnnotationInfo]): Tree = {
validate()
- derivedSym setAnnotations deriveAnnotations(initial, category, keepClean)
+
+ // see scala.annotation.meta's package class for more info
+ // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param.
+ // The defaults are:
+ // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity.
+ // - val/var member annotations solely end up on the underlying field.
+ //
+ // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit?
+ // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter)
+ val annotFilter: AnnotationInfo => Boolean = this match {
+ case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true)
+ // By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR).
+ case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor)
+ case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false)
+ case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false)
+ case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false)
+ case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false)
+ }
+
+ // The annotations amongst those found on the original symbol which
+ // should be propagated to this kind of accessor.
+ derivedSym setAnnotations (initial filter annotFilter)
+
logDerived(derivedTree)
}
}
sealed trait DerivedGetter extends DerivedFromValDef {
- // TODO
+ // A getter must be accompanied by a setter if the ValDef is mutable.
+ def needsSetter = mods.isMutable
}
sealed trait DerivedSetter extends DerivedFromValDef {
override def isSetter = true
@@ -341,10 +342,13 @@ trait MethodSynthesis {
case (p :: Nil) :: _ => p
case _ => NoSymbol
}
- private def setterRhs = (
- if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree
+
+ private def setterRhs = {
+ assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for $basisSym in $enclClass")
+ if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree
else Assign(fieldSelection, Ident(setterParam))
- )
+ }
+
private def setterDef = DefDef(derivedSym, setterRhs)
override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef
}
@@ -363,8 +367,7 @@ trait MethodSynthesis {
context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $name in $enclClass / ${enclClass.info.decls}")
result
}
- def derivedTree: DefDef =
- factoryMeth(mods & flagsMask | flagsExtra, name, tree)
+ def derivedTree: DefDef = factoryMeth(derivedMods, name, tree)
def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC
def flagsMask: Long = AccessFlags
def name: TermName = tree.name.toTermName
@@ -372,7 +375,6 @@ trait MethodSynthesis {
sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
def name = tree.name
- def category = GetterTargetClass
def flagsMask = GetterFlags
def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE )
@@ -385,8 +387,9 @@ trait MethodSynthesis {
}
}
case class Getter(tree: ValDef) extends BaseGetter(tree) {
- override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getterIn(enclClass)
- private def derivedRhs = if (mods.isDeferred) EmptyTree else fieldSelection
+ override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass)
+ private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection
+
private def derivedTpt = {
// For existentials, don't specify a type for the getter, even one derived
// from the symbol! This leads to incompatible existentials for the field and
@@ -396,23 +399,26 @@ trait MethodSynthesis {
// starts compiling (instead of failing like it's supposed to) because the typer
// expects to be able to identify escaping locals in typedDefDef, and fails to
// spot that brand of them. In other words it's an artifact of the implementation.
- val tpt = derivedSym.tpe_*.finalResultType.widen match {
+ val getterTp = derivedSym.tpe_*.finalResultType
+ val tpt = getterTp.widen match {
// Range position errors ensue if we don't duplicate this in some
// circumstances (at least: concrete vals with existential types.)
- case ExistentialType(_, _) => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
- case _ if mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
- case tp => TypeTree(tp)
+ case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
+ case _ if isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
+ case _ => TypeTree(getterTp)
}
tpt setPos tree.tpt.pos.focus
}
override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt)
}
+
/** Implements lazy value accessors:
- * - for lazy values of type Unit and all lazy fields inside traits,
- * the rhs is the initializer itself
- * - for all other lazy values z the accessor is a block of this form:
- * { z = <rhs>; z } where z can be an identifier or a field.
- */
+ * - for lazy values of type Unit and all lazy fields inside traits,
+ * the rhs is the initializer itself, because we'll just "compute" the result on every access
+ * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap)
+ * - for all other lazy values z the accessor is a block of this form:
+ * { z = <rhs>; z } where z can be an identifier or a field.
+ */
case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) {
class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol)
extends ChangeOwnerTraverser(oldowner, newowner) {
@@ -432,10 +438,10 @@ trait MethodSynthesis {
override def derivedTree: DefDef = {
val ValDef(_, _, tpt0, rhs0) = tree
val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0)
- val body = (
- if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1
+ val body =
+ if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor
else gen.mkAssignAndReturn(basisSym, rhs1)
- )
+
derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition
val ddefRes = DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body))
// ValDef will have its position focused whereas DefDef will have original correct rangepos
@@ -448,32 +454,45 @@ trait MethodSynthesis {
}
case class Setter(tree: ValDef) extends DerivedSetter {
def name = tree.setterName
- def category = SetterTargetClass
def flagsMask = SetterFlags
def flagsExtra = ACCESSOR
override def derivedSym = basisSym.setterIn(enclClass)
}
+
+ object Field {
+ // No field for these vals (either never emitted or eliminated later on):
+ // - abstract vals have no value we could store (until they become concrete, potentially)
+ // - lazy vals of type Unit
+ // - [Emitted, later removed during AddInterfaces/Mixins] concrete vals in traits can't have a field
+ // - [Emitted, later removed during Constructors] a concrete val with a statically known value (Unit / ConstantType)
+ // performs its side effect according to lazy/strict semantics, but doesn't need to store its value
+ // each access will "evaluate" the RHS (a literal) again
+ // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer.
+ // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does.
+ // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`),
+ // as the symbol info is in the process of being created then.
+ // TODO: harmonize tree & symbol creation
+ // TODO: the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite)
+ def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info))
+ }
+
case class Field(tree: ValDef) extends DerivedFromValDef {
def name = tree.localName
- def category = FieldTargetClass
def flagsMask = FieldFlags
def flagsExtra = PrivateLocal
- // By default annotations go to the field, except if the field is
- // generated for a class parameter (PARAMACCESSOR).
- override def keepClean = !mods.isParamAccessor
- override def derivedTree = (
- if (mods.isDeferred) EmptyTree
- else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
+
+ // handle lazy val first for now (we emit a Field even though we probably shouldn't...)
+ override def derivedTree =
+ if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
+ else if (Field.noFieldFor(tree)) EmptyTree
else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
- )
+
}
case class Param(tree: ValDef) extends DerivedFromValDef {
def name = tree.name
- def category = ParamTargetClass
def flagsMask = -1L
def flagsExtra = 0L
- override def keepClean = true
override def derivedTree = EmptyTree
}
def validateParam(tree: ValDef) {
@@ -487,7 +506,6 @@ trait MethodSynthesis {
override def derivedSym = enclClass.info decl name
}
sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter {
- def category = BeanGetterTargetClass
override def validate() {
if (derivedSym == NoSymbol) {
// the namer decides whether to generate these symbols or not. at that point, we don't
@@ -501,18 +519,16 @@ trait MethodSynthesis {
// Derives a tree without attempting to use the original tree's symbol.
override def derivedTree = {
atPos(tree.pos.focus) {
- DefDef(derivedMods, name, Nil, ListOfNil, tree.tpt.duplicate,
+ DefDef(derivedMods mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate,
if (isDeferred) EmptyTree else Select(This(owner), tree.name)
)
}
}
- override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
+ override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol]
}
case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }
- case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter {
- def category = BeanSetterTargetClass
- }
+ case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter
// No Symbols available.
private def beanAccessorsFromNames(tree: ValDef) = {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 4ad81b60ae..8943ec810d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -7,7 +7,6 @@ package scala.tools.nsc
package typechecker
import scala.collection.mutable
-import scala.annotation.tailrec
import symtab.Flags._
import scala.language.postfixOps
import scala.reflect.internal.util.ListOfNil
@@ -115,21 +114,14 @@ trait Namers extends MethodSynthesis {
TypeSigError(tree, ex)
alt
}
- // PRIVATE | LOCAL are fields generated for primary constructor arguments
- // @PP: ...or fields declared as private[this]. PARAMACCESSOR marks constructor arguments.
- // Neither gets accessors so the code is as far as I know still correct.
- def noEnterGetterSetter(vd: ValDef) = !vd.mods.isLazy && (
- !owner.isClass
- || (vd.mods.isPrivateLocal && !vd.mods.isCaseAccessor)
- || (vd.name startsWith nme.OUTER)
- || (context.unit.isJava)
- || isEnumConstant(vd)
- )
- def noFinishGetterSetter(vd: ValDef) = (
- (vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this]
- || vd.symbol.isModuleVar
- || isEnumConstant(vd))
+ // All lazy vals need accessors, including those owned by terms (e.g., in method) or private[this] in a class
+ def deriveAccessors(vd: ValDef) = vd.mods.isLazy || (owner.isClass && deriveAccessorsInClass(vd))
+
+ private def deriveAccessorsInClass(vd: ValDef) =
+ !vd.mods.isPrivateLocal && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors
+ !(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter
+ !isEnumConstant(vd) // enums can only occur in classes, so only check here
/** Determines whether this field holds an enum constant.
* To qualify, the following conditions must be met:
@@ -170,13 +162,9 @@ trait Namers extends MethodSynthesis {
def updatePosFlags(sym: Symbol, pos: Position, flags: Long): Symbol = {
debuglog("[overwrite] " + sym)
val newFlags = (sym.flags & LOCKED) | flags
- sym.rawInfo match {
- case tr: TypeRef =>
- // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef
- // over this mutated symbol, and we witness a stale cache for `parents`.
- tr.invalidateCaches()
- case _ =>
- }
+ // !!! needed for: pos/t5954d; the uniques type cache will happily serve up the same TypeRef
+ // over this mutated symbol, and we witness a stale cache for `parents`.
+ invalidateCaches(sym.rawInfo, sym :: sym.moduleClass :: Nil)
sym reset NoType setFlag newFlags setPos pos
sym.moduleClass andAlso (updatePosFlags(_, pos, moduleClassFlags(flags)))
@@ -655,14 +643,20 @@ trait Namers extends MethodSynthesis {
}
}
- def enterValDef(tree: ValDef) {
- if (noEnterGetterSetter(tree))
- assignAndEnterFinishedSymbol(tree)
- else
- enterGetterSetter(tree)
+ def enterValDef(tree: ValDef): Unit = {
+ val isScala = !context.unit.isJava
+ if (isScala) {
+ if (nme.isSetterName(tree.name)) ValOrVarWithSetterSuffixError(tree)
+ if (tree.mods.isPrivateLocal && tree.mods.isCaseAccessor) PrivateThisCaseClassParameterError(tree)
+ }
- if (isEnumConstant(tree))
+ if (isScala && deriveAccessors(tree)) enterGetterSetter(tree)
+ else assignAndEnterFinishedSymbol(tree)
+
+ if (isEnumConstant(tree)) {
tree.symbol setInfo ConstantType(Constant(tree.symbol))
+ tree.symbol.owner.linkedClassOfClass addChild tree.symbol
+ }
}
def enterLazyVal(tree: ValDef, lazyAccessor: Symbol): TermSymbol = {
@@ -1600,11 +1594,7 @@ trait Namers extends MethodSynthesis {
import SymValidateErrors._
def fail(kind: SymValidateErrors.Value) = SymbolValidationError(sym, kind)
- def checkWithDeferred(flag: Int) {
- if (sym hasFlag flag)
- AbstractMemberWithModiferError(sym, flag)
- }
- def checkNoConflict(flag1: Int, flag2: Int) {
+ def checkNoConflict(flag1: Int, flag2: Int) = {
if (sym hasAllFlags flag1.toLong | flag2)
IllegalModifierCombination(sym, flag1, flag2)
}
@@ -1643,6 +1633,10 @@ trait Namers extends MethodSynthesis {
checkNoConflict(ABSTRACT, FINAL)
if (sym.isDeferred) {
+ def checkWithDeferred(flag: Int) = {
+ if (sym hasFlag flag)
+ AbstractMemberWithModiferError(sym, flag)
+ }
// Is this symbol type always allowed the deferred flag?
def symbolAllowsDeferred = (
sym.isValueParameter
@@ -1658,14 +1652,16 @@ trait Namers extends MethodSynthesis {
)
if (sym hasAnnotation NativeAttr)
sym resetFlag DEFERRED
- else if (!symbolAllowsDeferred && ownerRequiresConcrete)
- fail(AbstractVar)
+ else {
+ if (!symbolAllowsDeferred && ownerRequiresConcrete) fail(AbstractVar)
- checkWithDeferred(PRIVATE)
- checkWithDeferred(FINAL)
+ checkWithDeferred(PRIVATE)
+ checkWithDeferred(FINAL)
+ }
}
- checkNoConflict(FINAL, SEALED)
+ if (!sym.isJavaEnum)
+ checkNoConflict(FINAL, SEALED)
checkNoConflict(PRIVATE, PROTECTED)
// checkNoConflict(PRIVATE, OVERRIDE) // this one leads to bad error messages like #4174, so catch in refchecks
// checkNoConflict(PRIVATE, FINAL) // can't do this because FINAL also means compile-time constant
diff --git a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
index 39cd610b1c..5062289ed1 100644
--- a/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
@@ -9,6 +9,7 @@ package typechecker
import symtab.Flags._
import scala.collection.mutable
import scala.reflect.ClassTag
+import PartialFunction.{ cond => when }
/**
* @author Lukas Rytz
@@ -468,8 +469,7 @@ trait NamesDefaults { self: Analyzer =>
else {
// isClass also works for methods in objects, owner is the ModuleClassSymbol
if (param.owner.owner.isClass) {
- // .toInterface: otherwise we get the method symbol of the impl class
- param.owner.owner.toInterface.info.member(defGetterName)
+ param.owner.owner.info.member(defGetterName)
} else {
// the owner of the method is another method. find the default
// getter in the context.
@@ -551,64 +551,73 @@ trait NamesDefaults { self: Analyzer =>
}
}
- /**
- * Removes name assignments from args. Additionally, returns an array mapping
- * argument indices from call-site-order to definition-site-order.
+ /** Removes name assignments from args. Additionally, returns an array mapping
+ * argument indices from call-site-order to definition-site-order.
*
- * Verifies that names are not specified twice, positional args don't appear
- * after named ones.
+ * Verifies that names are not specified twice, and positional args don't appear after named ones.
*/
def removeNames(typer: Typer)(args: List[Tree], params: List[Symbol]): (List[Tree], Array[Int]) = {
implicit val context0 = typer.context
- // maps indices from (order written by user) to (order of definition)
- val argPos = Array.fill(args.length)(-1)
- var positionalAllowed = true
- val namelessArgs = mapWithIndex(args) { (arg, argIndex) =>
- arg match {
- case arg @ AssignOrNamedArg(Ident(name), rhs) =>
- def matchesName(param: Symbol) = !param.isSynthetic && (
- (param.name == name) || (param.deprecatedParamName match {
- case Some(`name`) =>
- context0.deprecationWarning(arg.pos, param,
- s"the parameter name $name has been deprecated. Use ${param.name} instead.")
- true
- case _ => false
- })
- )
- val paramPos = params indexWhere matchesName
- if (paramPos == -1) {
- if (positionalAllowed) {
- argPos(argIndex) = argIndex
- // prevent isNamed from being true when calling doTypedApply recursively,
- // treat the arg as an assignment of type Unit
- Assign(arg.lhs, rhs) setPos arg.pos
- }
- else UnknownParameterNameNamesDefaultError(arg, name)
- }
- else if (argPos contains paramPos) {
+ def matchesName(param: Symbol, name: Name, argIndex: Int) = {
+ def warn(w: String) = context0.deprecationWarning(args(argIndex).pos, param, w)
+ def checkDeprecation(anonOK: Boolean) =
+ when (param.deprecatedParamName) {
+ case Some(`name`) => true
+ case Some(nme.NO_NAME) => anonOK
+ }
+ def checkName = {
+ val res = param.name == name
+ if (res && checkDeprecation(true)) warn(s"naming parameter $name has been deprecated.")
+ res
+ }
+ def checkAltName = {
+ val res = checkDeprecation(false)
+ if (res) warn(s"the parameter name $name has been deprecated. Use ${param.name} instead.")
+ res
+ }
+ !param.isSynthetic && (checkName || checkAltName)
+ }
+ // argPos maps indices from (order written by user) to (order of definition)
+ val argPos = Array.fill(args.length)(-1)
+ val namelessArgs = {
+ var positionalAllowed = true
+ def stripNamedArg(arg: AssignOrNamedArg, argIndex: Int): Tree = {
+ val AssignOrNamedArg(Ident(name), rhs) = arg
+ params indexWhere (p => matchesName(p, name, argIndex)) match {
+ case -1 if positionalAllowed =>
+ // prevent isNamed from being true when calling doTypedApply recursively,
+ // treat the arg as an assignment of type Unit
+ Assign(arg.lhs, rhs) setPos arg.pos
+ case -1 =>
+ UnknownParameterNameNamesDefaultError(arg, name)
+ case paramPos if argPos contains paramPos =>
val existingArgIndex = argPos.indexWhere(_ == paramPos)
- val otherName = args(paramPos) match {
- case AssignOrNamedArg(Ident(oName), rhs) if oName != name => Some(oName)
- case _ => None
+ val otherName = Some(args(paramPos)) collect {
+ case AssignOrNamedArg(Ident(oName), _) if oName != name => oName
}
DoubleParamNamesDefaultError(arg, name, existingArgIndex+1, otherName)
- } else if (isAmbiguousAssignment(typer, params(paramPos), arg))
+ case paramPos if isAmbiguousAssignment(typer, params(paramPos), arg) =>
AmbiguousReferenceInNamesDefaultError(arg, name)
- else {
- // if the named argument is on the original parameter
- // position, positional after named is allowed.
- if (argIndex != paramPos)
- positionalAllowed = false
- argPos(argIndex) = paramPos
+ case paramPos if paramPos != argIndex =>
+ positionalAllowed = false // named arg is not in original parameter order: require names after this
+ argPos(argIndex) = paramPos // fix up the arg position
rhs
- }
- case _ =>
- argPos(argIndex) = argIndex
- if (positionalAllowed) arg
- else PositionalAfterNamedNamesDefaultError(arg)
+ case _ => rhs
+ }
+ }
+ mapWithIndex(args) {
+ case (arg: AssignOrNamedArg, argIndex) =>
+ val t = stripNamedArg(arg, argIndex)
+ if (!t.isErroneous && argPos(argIndex) < 0) argPos(argIndex) = argIndex
+ t
+ case (arg, argIndex) =>
+ if (positionalAllowed) {
+ argPos(argIndex) = argIndex
+ arg
+ } else
+ PositionalAfterNamedNamesDefaultError(arg)
}
}
-
(namelessArgs, argPos)
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 3b2e07bdbd..da269168ec 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -6,15 +6,17 @@
package scala.tools.nsc
package typechecker
-import symtab.Flags._
-import scala.collection.{ mutable, immutable }
-import transform.InfoTransform
-import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
+
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
import scala.tools.nsc.settings.ScalaVersion
-import scala.tools.nsc.settings.AnyScalaVersion
import scala.tools.nsc.settings.NoScalaVersion
+import symtab.Flags._
+import transform.InfoTransform
+
+
/** <p>
* Post-attribution checking and transformation.
* </p>
@@ -48,7 +50,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
import global._
import definitions._
- import typer.{typed, typedOperator, atOwner}
+ import typer.typed
/** the following two members override abstract members in Transform */
val phaseName: String = "refchecks"
@@ -86,17 +88,19 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
)
- def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match {
+ def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type, isModuleOverride: Boolean): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match {
case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
rtp1 <:< rtp2
case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
rtp1 <:< rtp2
case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
- overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix)
+ overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix, isModuleOverride)
case _ =>
def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner)
-
- (tp1 <:< tp2) || ( // object override check
+ (tp1 <:< tp2) || isModuleOverride && (
+ // Object override check. This requires that both the overridden and the overriding member are object
+ // definitions. The overriding module type is allowed to replace the original one with the same name
+ // as long as it conform to the original non-singleton type.
tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
val cb1 = classBoundAsSeen(tp1)
val cb2 = classBoundAsSeen(tp2)
@@ -518,7 +522,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
def checkOverrideTerm() {
other.cookJavaRawInfo() // #2454
- if (!overridesTypeInPrefix(lowType, highType, rootType)) { // 8
+ if (!overridesTypeInPrefix(lowType, highType, rootType, low.isModuleOrModuleClass && high.isModuleOrModuleClass)) { // 8
overrideTypeError()
explainTypes(lowType, highType)
}
@@ -1150,11 +1154,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
def toConstructor(pos: Position, tpe: Type): Tree = {
val rtpe = tpe.finalResultType
assert(rtpe.typeSymbol hasFlag CASE, tpe)
- localTyper.typedOperator {
+ val tree = localTyper.typedOperator {
atPos(pos) {
Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor)
}
}
+ checkUndesiredProperties(rtpe.typeSymbol, tree.pos)
+ tree
}
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
@@ -1182,59 +1188,54 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
private def eliminateModuleDefs(moduleDef: Tree): List[Tree] = exitingRefchecks {
val ModuleDef(_, _, impl) = moduleDef
val module = moduleDef.symbol
- val moduleClass = module.moduleClass
val site = module.owner
val moduleName = module.name.toTermName
// The typer doesn't take kindly to seeing this ClassDef; we have to
// set NoType so it will be ignored.
- val cdef = ClassDef(moduleClass, impl) setType NoType
-
- // This code is related to the fix of SI-9375, which stops adding `readResolve` methods to
- // non-static (nested) modules. Before the fix, the method would cause the module accessor
- // to become notPrivate. To prevent binary changes in the 2.11.x branch, we mimic that behavior.
- // There is a bit of code duplication between here and SyntheticMethods. We cannot call
- // makeNotPrivate already in SyntheticMethod: that is during type checking, and not all references
- // are resolved yet, so we cannot rename a definition. This code doesn't exist in the 2.12.x branch.
- def hasConcreteImpl(name: Name) = moduleClass.info.member(name).alternatives exists (m => !m.isDeferred)
- val hadReadResolveBeforeSI9375 = moduleClass.isSerializable && !hasConcreteImpl(nme.readResolve)
- if (hadReadResolveBeforeSI9375)
- moduleClass.sourceModule.makeNotPrivate(moduleClass.sourceModule.owner)
-
- // Create the module var unless the immediate owner is a class and
- // the module var already exists there. See SI-5012, SI-6712.
- def findOrCreateModuleVar() = {
- val vsym = (
- if (site.isTerm) NoSymbol
- else site.info decl nme.moduleVarName(moduleName)
- )
- vsym orElse (site newModuleVarSymbol module)
- }
- def newInnerObject() = {
- // Create the module var unless it is already in the module owner's scope.
- // The lookup is on module.enclClass and not module.owner lest there be a
- // nullary method between us and the class; see SI-5012.
- val moduleVar = findOrCreateModuleVar()
- val rhs = gen.newModule(module, moduleVar.tpe)
- val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs)
- val accessor = DefDef(module, body.changeOwner(moduleVar -> module))
-
- ValDef(moduleVar) :: accessor :: Nil
- }
+ val cdef = ClassDef(module.moduleClass, impl) setType NoType
+
def matchingInnerObject() = {
val newFlags = (module.flags | STABLE) & ~MODULE
- val newInfo = NullaryMethodType(moduleClass.tpe)
+ val newInfo = NullaryMethodType(module.moduleClass.tpe)
val accessor = site.newMethod(moduleName, module.pos, newFlags) setInfoAndEnter newInfo
DefDef(accessor, Select(This(site), module)) :: Nil
}
val newTrees = cdef :: (
if (module.isStatic)
+ // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O.
if (module.isOverridingSymbol) matchingInnerObject() else Nil
else
- newInnerObject()
+ newInnerObject(site, module)
)
transformTrees(newTrees map localTyper.typedPos(moduleDef.pos))
}
+ def newInnerObject(site: Symbol, module: Symbol): List[Tree] = {
+ if (site.isTrait)
+ DefDef(module, EmptyTree) :: Nil
+ else {
+ val moduleVar = site newModuleVarSymbol module
+ // used for the mixin case: need a new symbol owned by the subclass for the accessor, rather than repurposing the module symbol
+ def mkAccessorSymbol =
+ site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | MIXEDIN)
+ .setInfo(moduleVar.tpe)
+ .andAlso(self => if (module.isPrivate) self.expandName(module.owner))
+
+ val accessor = if (module.owner == site) module else mkAccessorSymbol
+ val accessorDef = DefDef(accessor, gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.tpe)).changeOwner(moduleVar -> accessor))
+
+ ValDef(moduleVar) :: accessorDef :: Nil
+ }
+ }
+
+ def mixinModuleDefs(clazz: Symbol): List[Tree] = {
+ val res = for {
+ mixinClass <- clazz.mixinClasses.iterator
+ module <- mixinClass.info.decls.iterator.filter(_.isModule)
+ newMember <- newInnerObject(clazz, module)
+ } yield transform(localTyper.typedPos(clazz.pos)(newMember))
+ res.toList
+ }
def transformStat(tree: Tree, index: Int): List[Tree] = tree match {
case t if treeInfo.isSelfConstrCall(t) =>
@@ -1480,10 +1481,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
case m: MemberDef =>
val sym = m.symbol
applyChecks(sym.annotations)
- // validate implicitNotFoundMessage
- analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
- reporter.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn")
- }
+
+ def messageWarning(name: String)(warn: String) =
+ reporter.warning(tree.pos, f"Invalid $name message for ${sym}%s${sym.locationString}%s:%n$warn")
+
+ // validate implicitNotFoundMessage and implicitAmbiguousMessage
+ analyzer.ImplicitNotFoundMsg.check(sym) foreach messageWarning("implicitNotFound")
+ analyzer.ImplicitAmbiguousMsg.check(sym) foreach messageWarning("implicitAmbiguous")
case tpt@TypeTree() =>
if(tpt.original != null) {
@@ -1526,11 +1530,21 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
}
private def transformCaseApply(tree: Tree) = {
+ def loop(t: Tree): Unit = t match {
+ case Ident(_) =>
+ checkUndesiredProperties(t.symbol, t.pos)
+ case Select(qual, _) =>
+ checkUndesiredProperties(t.symbol, t.pos)
+ loop(qual)
+ case _ =>
+ }
+
tree foreach {
case i@Ident(_) =>
enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()`
case _ =>
}
+ loop(tree)
toConstructor(tree.pos, tree.tpe)
}
@@ -1669,11 +1683,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// SI-7870 default getters for constructors live in the companion module
checkOverloadedRestrictions(currentOwner, currentOwner.companionModule)
val bridges = addVarargBridges(currentOwner)
+ val moduleDesugared = if (currentOwner.isTrait) Nil else mixinModuleDefs(currentOwner)
checkAllOverrides(currentOwner)
checkAnyValSubclass(currentOwner)
if (currentOwner.isDerivedValueClass)
currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler!
- if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree
+ if (bridges.nonEmpty || moduleDesugared.nonEmpty) deriveTemplate(tree)(_ ::: bridges ::: moduleDesugared) else tree
case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc")
case tpt@TypeTree() =>
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index e0d96df062..a1bec13999 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -287,7 +287,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val result = (localTyper.typedPos(tree.pos) {
Select(Super(qual, tpnme.EMPTY) setPos qual.pos, sym.alias)
}).asInstanceOf[Select]
- debuglog("alias replacement: " + tree + " ==> " + result); //debug
+ debuglog(s"alias replacement: $sym --> ${sym.alias} / $tree ==> $result"); //debug
localTyper.typed(gen.maybeMkAsInstanceOf(transformSuperSelect(result), sym.tpe, sym.alias.tpe, beforeRefChecks = true))
} else {
/*
@@ -387,7 +387,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
val savedValid = validCurrentOwner
if (owner.isClass) validCurrentOwner = true
val savedLocalTyper = localTyper
- localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner)
+ localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner)
typers = typers updated (owner, localTyper)
val result = super.atOwner(tree, owner)(trans)
localTyper = savedLocalTyper
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index 1b3f066fc1..6d883aee3d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -6,11 +6,13 @@
package scala.tools.nsc
package typechecker
-import scala.collection.{ mutable, immutable }
-import symtab.Flags._
-import scala.collection.mutable.ListBuffer
import scala.language.postfixOps
+import scala.collection.mutable
+import scala.collection.mutable.ListBuffer
+
+import symtab.Flags._
+
/** Synthetic method implementations for case classes and case objects.
*
* Added to all case classes/objects:
@@ -87,33 +89,18 @@ trait SyntheticMethods extends ast.TreeDSL {
def accessors = clazz.caseFieldAccessors
val arity = accessors.size
- // If this is ProductN[T1, T2, ...], accessorLub is the lub of T1, T2, ..., .
- // !!! Hidden behind -Xexperimental due to bummer type inference bugs.
- // Refining from Iterator[Any] leads to types like
- //
- // Option[Int] { def productIterator: Iterator[String] }
- //
- // appearing legitimately, but this breaks invariant places
- // like Tags and Arrays which are not robust and infer things
- // which they shouldn't.
- val accessorLub = (
- if (settings.Xexperimental) {
- global.lub(accessors map (_.tpe.finalResultType)) match {
- case RefinedType(parents, decls) if !decls.isEmpty => intersectionType(parents)
- case tp => tp
- }
- }
- else AnyTpe
- )
def forwardToRuntime(method: Symbol): Tree =
forwardMethod(method, getMember(ScalaRunTimeModule, (method.name prepend "_")))(mkThis :: _)
- def callStaticsMethod(name: String)(args: Tree*): Tree = {
- val method = termMember(RuntimeStaticsModule, name)
+ def callStaticsMethodName(name: TermName)(args: Tree*): Tree = {
+ val method = RuntimeStaticsModule.info.member(name)
Apply(gen.mkAttributedRef(method), args.toList)
}
+ def callStaticsMethod(name: String)(args: Tree*): Tree =
+ callStaticsMethodName(newTermName(name))(args: _*)
+
// Any concrete member, including private
def hasConcreteImpl(name: Name) =
clazz.info.member(name).alternatives exists (m => !m.isDeferred)
@@ -125,8 +112,8 @@ trait SyntheticMethods extends ast.TreeDSL {
}
}
def productIteratorMethod = {
- createMethod(nme.productIterator, iteratorOfType(accessorLub))(_ =>
- gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(accessorLub), List(mkThis))
+ createMethod(nme.productIterator, iteratorOfType(AnyTpe))(_ =>
+ gen.mkMethodCall(ScalaRunTimeModule, nme.typedProductIterator, List(AnyTpe), List(mkThis))
)
}
@@ -246,7 +233,7 @@ trait SyntheticMethods extends ast.TreeDSL {
List(
Product_productPrefix -> (() => constantNullary(nme.productPrefix, clazz.name.decode)),
Product_productArity -> (() => constantNullary(nme.productArity, arity)),
- Product_productElement -> (() => perElementMethod(nme.productElement, accessorLub)(mkThisSelect)),
+ Product_productElement -> (() => perElementMethod(nme.productElement, AnyTpe)(mkThisSelect)),
Product_iterator -> (() => productIteratorMethod),
Product_canEqual -> (() => canEqualMethod)
// This is disabled pending a reimplementation which doesn't add any
@@ -261,10 +248,10 @@ trait SyntheticMethods extends ast.TreeDSL {
case BooleanClass => If(Ident(sym), Literal(Constant(1231)), Literal(Constant(1237)))
case IntClass => Ident(sym)
case ShortClass | ByteClass | CharClass => Select(Ident(sym), nme.toInt)
- case LongClass => callStaticsMethod("longHash")(Ident(sym))
- case DoubleClass => callStaticsMethod("doubleHash")(Ident(sym))
- case FloatClass => callStaticsMethod("floatHash")(Ident(sym))
- case _ => callStaticsMethod("anyHash")(Ident(sym))
+ case LongClass => callStaticsMethodName(nme.longHash)(Ident(sym))
+ case DoubleClass => callStaticsMethodName(nme.doubleHash)(Ident(sym))
+ case FloatClass => callStaticsMethodName(nme.floatHash)(Ident(sym))
+ case _ => callStaticsMethodName(nme.anyHash)(Ident(sym))
}
}
@@ -354,16 +341,18 @@ trait SyntheticMethods extends ast.TreeDSL {
}
for ((m, impl) <- methods ; if shouldGenerate(m)) yield impl()
}
- def extras = (
+ def extras = {
if (needsReadResolve) {
// Aha, I finally decoded the original comment.
// This method should be generated as private, but apparently if it is, then
// it is name mangled afterward. (Wonder why that is.) So it's only protected.
// For sure special methods like "readResolve" should not be mangled.
- List(createMethod(nme.readResolve, Nil, ObjectTpe)(m => { m setFlag PRIVATE ; REF(clazz.sourceModule) }))
+ List(createMethod(nme.readResolve, Nil, ObjectTpe)(m => {
+ m setFlag PRIVATE; REF(clazz.sourceModule)
+ }))
}
else Nil
- )
+ }
try impls ++ extras
catch { case _: TypeError if reporter.hasErrors => Nil }
@@ -381,7 +370,14 @@ trait SyntheticMethods extends ast.TreeDSL {
for (ddef @ DefDef(_, _, _, _, _, _) <- templ.body ; if isRewrite(ddef.symbol)) {
val original = ddef.symbol
- val newAcc = deriveMethod(ddef.symbol, name => context.unit.freshTermName(name + "$")) { newAcc =>
+ val i = original.owner.caseFieldAccessors.indexOf(original)
+ def freshAccessorName = {
+ devWarning(s"Unable to find $original among case accessors of ${original.owner}: ${original.owner.caseFieldAccessors}")
+ context.unit.freshTermName(original.name + "$")
+ }
+ def nameSuffixedByParamIndex = original.name.append(nme.CASE_ACCESSOR + "$" + i).toTermName
+ val newName = if (i < 0) freshAccessorName else nameSuffixedByParamIndex
+ val newAcc = deriveMethod(ddef.symbol, name => newName) { newAcc =>
newAcc.makePublic
newAcc resetFlag (ACCESSOR | PARAMACCESSOR | OVERRIDE)
ddef.rhs.duplicate
diff --git a/src/compiler/scala/tools/nsc/typechecker/Tags.scala b/src/compiler/scala/tools/nsc/typechecker/Tags.scala
index 56127f4026..e29451f379 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Tags.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Tags.scala
@@ -13,16 +13,7 @@ trait Tags {
private val runDefinitions = currentRun.runDefinitions
private def resolveTag(pos: Position, taggedTp: Type, allowMaterialization: Boolean) = enteringTyper {
- def wrapper (tree: => Tree): Tree = if (allowMaterialization) (context.withMacrosEnabled[Tree](tree)) else (context.withMacrosDisabled[Tree](tree))
- wrapper(inferImplicit(
- EmptyTree,
- taggedTp,
- reportAmbiguous = true,
- isView = false,
- context,
- saveAmbiguousDivergent = true,
- pos
- ).tree)
+ context.withMacros(enabled = allowMaterialization) { inferImplicitByType(taggedTp, context, pos).tree }
}
/** Finds in scope or materializes a ClassTag.
diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
index e8db8309f1..990edcd86d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
@@ -7,8 +7,6 @@ package scala.tools.nsc
package typechecker
import scala.collection.mutable
-import mutable.ListBuffer
-import util.returning
import scala.reflect.internal.util.shortClassOfInstance
import scala.reflect.internal.util.StringOps._
@@ -94,7 +92,7 @@ abstract class TreeCheckers extends Analyzer {
def latest = maps.head._2
val defSyms = symbolTreeMap[DefTree]()
val newSyms = mutable.HashSet[Symbol]()
- val movedMsgs = new ListBuffer[String]
+ val movedMsgs = mutable.ListBuffer[String]()
def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString)
def record(tree: Tree) {
@@ -113,8 +111,6 @@ abstract class TreeCheckers extends Analyzer {
newSyms += sym
else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym)))
()
- else if (prevTrees exists (_.symbol.owner == sym.owner.implClass))
- errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.")
else {
val s1 = (prevTrees map wholetreestr).sorted.distinct
val s2 = wholetreestr(tree)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 6b73a538df..329ce8c23b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -14,7 +14,7 @@ package tools.nsc
package typechecker
import scala.collection.{mutable, immutable}
-import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance, ListOfNil }
+import scala.reflect.internal.util.{ Statistics, ListOfNil }
import mutable.ListBuffer
import symtab.Flags._
import Mode._
@@ -105,12 +105,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// that are turned private by typedBlock
private final val SYNTHETIC_PRIVATE = TRANS_FLAG
- private final val InterpolatorCodeRegex = """\$\{.*?\}""".r
+ private final val InterpolatorCodeRegex = """\$\{\s*(.*?)\s*\}""".r
private final val InterpolatorIdentRegex = """\$[$\w]+""".r // note that \w doesn't include $
abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors {
import context0.unit
- import typeDebug.{ ptTree, ptBlock, ptLine, inGreen, inRed }
+ import typeDebug.ptTree
import TyperErrorGen._
val runDefinitions = currentRun.runDefinitions
import runDefinitions._
@@ -128,6 +128,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def canTranslateEmptyListToNil = true
def missingSelectErrorTree(tree: Tree, qual: Tree, name: Name): Tree = tree
+ // 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.specialScalaErasure(tp) else tp
+ }
+
def typedDocDef(docDef: DocDef, mode: Mode, pt: Type): Tree =
typed(docDef.definition, mode, pt)
@@ -151,7 +158,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
for(ar <- argResultsBuff)
paramTp = paramTp.subst(ar.subst.from, ar.subst.to)
- val res = if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure else inferImplicit(fun, paramTp, context.reportErrors, isView = false, context)
+ val res =
+ if (paramFailed || (paramTp.isErroneous && {paramFailed = true; true})) SearchFailure
+ else inferImplicitFor(paramTp, fun, context, reportAmbiguous = context.reportErrors)
argResultsBuff += res
if (res.isSuccess) {
@@ -187,14 +196,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
!from.isError
&& !to.isError
&& context.implicitsEnabled
- && (inferView(context.tree, from, to, reportAmbiguous = false, saveErrors = true) != EmptyTree)
+ && (inferView(context.tree, from, to, reportAmbiguous = false) != EmptyTree)
// SI-8230 / SI-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.
)
- def inferView(tree: Tree, from: Type, to: Type, reportAmbiguous: Boolean): Tree =
- inferView(tree, from, to, reportAmbiguous, saveErrors = true)
/** Infer an implicit conversion (`view`) between two types.
* @param tree The tree which needs to be converted.
@@ -207,25 +214,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* 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, saveErrors: Boolean): Tree = {
- debuglog("infer view from "+from+" to "+to)//debug
- if (isPastTyper) EmptyTree
- else from match {
- case MethodType(_, _) => EmptyTree
- case OverloadedType(_, _) => EmptyTree
- case PolyType(_, _) => EmptyTree
- case _ =>
- def wrapImplicit(from: Type): Tree = {
- val result = inferImplicit(tree, functionType(from.withoutAnnotations :: Nil, to), reportAmbiguous, isView = true, context, saveAmbiguousDivergent = saveErrors)
- if (result.subst != EmptyTreeTypeSubstituter) {
- result.subst traverse tree
- notifyUndetparamsInferred(result.subst.from, result.subst.to)
- }
- result.tree
- }
- wrapImplicit(from) orElse wrapImplicit(byNameType(from))
+ 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 result = inferImplicitView(fromNoAnnot, to, tree, context, reportAmbiguous, saveErrors) match {
+ case fail if fail.isFailure => inferImplicitView(byNameType(fromNoAnnot), to, 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._
@@ -239,6 +244,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
var context = context0
def context1 = context
+ // for use with silent type checking to when we can't have results with undetermined type params
+ // note that this captures the context var
+ val isMonoContext = (_: Any) => context.undetparams.isEmpty
+
def dropExistential(tp: Type): Type = tp match {
case ExistentialType(tparams, tpe) =>
new SubstWildcardMap(tparams).apply(tp)
@@ -542,7 +551,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
val qual = typedQualifier { atPos(tree.pos.makeTransparent) {
tree match {
- case Ident(_) => Ident(rootMirror.getPackageObjectWithMember(pre, sym))
+ 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)
}
@@ -721,7 +734,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
featureTrait.owner.ownerChain.takeWhile(_ != languageFeatureModule.moduleClass).reverse
val featureName = (nestedOwners map (_.name + ".")).mkString + featureTrait.name
def action(): Boolean = {
- def hasImport = inferImplicit(EmptyTree: Tree, featureTrait.tpe, reportAmbiguous = true, isView = false, context).isSuccess
+ def hasImport = inferImplicitByType(featureTrait.tpe, context).isSuccess
def hasOption = settings.language contains featureName
val OK = hasImport || hasOption
if (!OK) {
@@ -798,7 +811,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* (11) Widen numeric literals to their expected type, if necessary
* (12) When in mode EXPRmode, convert E to { E; () } if expected type is scala.Unit.
* (13) When in mode EXPRmode, apply AnnotationChecker conversion if expected type is annotated.
- * (14) When in mode EXPRmode, apply a view
+ * (14) When in mode EXPRmode, do SAM conversion
+ * (15) When in mode EXPRmode, apply a view
* If all this fails, error
*/
protected def adapt(tree: Tree, mode: Mode, pt: Type, original: Tree = EmptyTree): Tree = {
@@ -858,7 +872,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case Block(_, tree1) => tree1.symbol
case _ => tree.symbol
}
- if (!meth.isConstructor && (isFunctionType(pt) || samOf(pt).exists)) { // (4.2)
+ def shouldEtaExpandToSam: Boolean = {
+ // SI-9536 don't adapt parameterless method types to a to SAM's, fall through to empty application
+ // instead for backwards compatiblity with 2.11. See comments of that ticket and SI-7187
+ // for analogous trouble with non-SAM eta expansion. Suggestions there are: a) deprecate eta expansion to Function0,
+ // or b) switch the order of eta-expansion and empty application in this adaptation.
+ !mt.params.isEmpty && samOf(pt).exists
+ }
+ if (!meth.isConstructor && (isFunctionType(pt) || shouldEtaExpandToSam)) { // (4.2)
debuglog(s"eta-expanding $tree: ${tree.tpe} to $pt")
checkParamsConvertible(tree, tree.tpe)
val tree0 = etaExpand(context.unit, tree, this)
@@ -928,24 +949,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def insertApply(): Tree = {
assert(!context.inTypeConstructorAllowed, mode) //@M
val adapted = adaptToName(tree, nme.apply)
- def stabilize0(pre: Type): Tree = stabilize(adapted, pre, MonoQualifierModes, WildcardType)
-
- // TODO reconcile the overlap between Typers#stablize and TreeGen.stabilize
- val qual = adapted match {
- case This(_) =>
- gen.stabilize(adapted)
- case Ident(_) =>
- val owner = adapted.symbol.owner
- val pre =
- if (owner.isPackageClass) owner.thisType
- else if (owner.isClass) context.enclosingSubClassContext(owner).prefix
- else NoPrefix
- stabilize0(pre)
- case Select(qualqual, _) =>
- stabilize0(qualqual.tpe)
- case other =>
- other
- }
+ val qual = gen.stabilize(adapted)
typedPos(tree.pos, mode, pt) {
Select(qual setPos tree.pos.makeTransparent, nme.apply)
}
@@ -1020,72 +1024,70 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- def fallbackAfterVanillaAdapt(): Tree = {
- def isPopulatedPattern = {
- if ((tree.symbol ne null) && tree.symbol.isModule)
- inferModulePattern(tree, pt)
-
- isPopulated(tree.tpe, approximateAbstracts(pt))
+ def adaptExprNotFunMode(): Tree = {
+ def lastTry(err: AbsTypeError = null): Tree = {
+ debuglog("error tree = " + tree)
+ if (settings.debug && settings.explaintypes) explainTypes(tree.tpe, pt)
+ if (err ne null) context.issue(err)
+ if (tree.tpe.isErroneous || pt.isErroneous) setError(tree)
+ else adaptMismatchedSkolems()
}
- if (mode.inPatternMode && isPopulatedPattern)
- return tree
- val tree1 = constfold(tree, pt) // (10) (11)
- if (tree1.tpe <:< pt)
- return adapt(tree1, mode, pt, original)
+ // 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)
- if (mode.typingExprNotFun) {
- // The <: Any requirement inhibits attempts to adapt continuation types
- // to non-continuation types.
- if (tree.tpe <:< AnyTpe) pt.dealias match {
- case TypeRef(_, UnitClass, _) => // (12)
- if (!isPastTyper && settings.warnValueDiscard)
- context.warning(tree.pos, "discarded non-Unit value")
- return typedPos(tree.pos, mode, pt)(Block(List(tree), Literal(Constant(()))))
- case TypeRef(_, sym, _) if isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt) =>
- if (!isPastTyper && settings.warnNumericWiden)
- context.warning(tree.pos, "implicit numeric widening")
- return typedPos(tree.pos, mode, pt)(Select(tree, "to" + sym.name))
- case _ =>
- }
- if (pt.dealias.annotations.nonEmpty && canAdaptAnnotations(tree, this, mode, pt)) // (13)
- return typed(adaptAnnotations(tree, this, mode, pt), mode, pt)
+ @inline def warnValueDiscard(): Unit =
+ if (!isPastTyper && settings.warnValueDiscard) context.warning(tree.pos, "discarded non-Unit value")
+ @inline def warnNumericWiden(): Unit =
+ if (!isPastTyper && settings.warnNumericWiden) context.warning(tree.pos, "implicit numeric widening")
- if (hasUndets)
- return instantiate(tree, mode, pt)
-
- if (context.implicitsEnabled && !pt.isError && !tree.isErrorTyped) {
- // (14); the condition prevents chains of views
- debuglog("inferring view from " + tree.tpe + " to " + pt)
- inferView(tree, tree.tpe, pt, reportAmbiguous = true) match {
- case EmptyTree =>
- case coercion =>
- def msg = "inferred view from " + tree.tpe + " to " + pt + " = " + coercion + ":" + coercion.tpe
- if (settings.logImplicitConv)
- context.echo(tree.pos, msg)
-
- debuglog(msg)
- val silentContext = context.makeImplicit(context.ambiguousErrors)
- val res = newTyper(silentContext).typed(
- new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt)
- silentContext.reporter.firstError match {
- case Some(err) => context.issue(err)
- case None => return res
+ // 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() ; 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 samAttach = inferSamType(tree, pt, mode)
+
+ if (samAttach.samTp ne NoType) tree.setType(samAttach.samTp).updateAttachment(samAttach)
+ 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) 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()
}
- }
+ }
}
- }
-
- debuglog("error tree = " + tree)
- if (settings.debug && settings.explaintypes)
- explainTypes(tree.tpe, pt)
-
- if (tree.tpe.isErroneous || pt.isErroneous)
- setError(tree)
- else
- adaptMismatchedSkolems()
+ } else lastTry()
}
+
def vanillaAdapt(tree: Tree) = {
def applyPossible = {
def applyMeth = member(adaptToName(tree, nme.apply), nme.apply)
@@ -1119,8 +1121,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
else if (tree.tpe <:< pt)
tree
- else
- fallbackAfterVanillaAdapt()
+ else if (mode.inPatternMode && { inferModulePattern(tree, pt); isPopulated(tree.tpe, approximateAbstracts(pt)) })
+ tree
+ else {
+ val constFolded = constfold(tree, pt)
+ if (constFolded.tpe <:< pt) adapt(constFolded, mode, pt, original) // set stage for (0)
+ else adaptExprNotFunMode() // (10) -- (15)
+ }
}
// begin adapt
@@ -1185,7 +1192,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val savedUndetparams = context.undetparams
silent(_.instantiate(tree, mode, UnitTpe)) orElse { _ =>
context.undetparams = savedUndetparams
- val valueDiscard = atPos(tree.pos)(Block(List(instantiate(tree, mode, WildcardType)), Literal(Constant(()))))
+ val valueDiscard = atPos(tree.pos)(gen.mkUnitBlock(instantiate(tree, mode, WildcardType)))
typed(valueDiscard, mode, UnitTpe)
}
}
@@ -1246,7 +1253,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* If no conversion is found, return `qual` unchanged.
*
*/
- def adaptToArguments(qual: Tree, name: Name, args: List[Tree], pt: Type, reportAmbiguous: Boolean, saveErrors: Boolean): Tree = {
+ 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), restpe), reportAmbiguous, saveErrors)
@@ -1262,7 +1269,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* 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, saveErrors: Boolean): Tree = {
+ 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))
@@ -1707,6 +1714,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
psym addChild context.owner
else
pending += ParentSealedInheritanceError(parent, psym)
+ if (psym.isLocalToBlock && !phase.erasedTypes)
+ psym addChild context.owner
val parentTypeOfThis = parent.tpe.dealias.typeOfThis
if (!(selfType <:< parentTypeOfThis) &&
@@ -1742,17 +1751,21 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
classinfo.parents map (_.instantiateTypeParams(List(tparam), List(AnyRefTpe))),
classinfo.decls,
clazz)
- clazz.setInfo {
- clazz.info match {
- case PolyType(tparams, _) => PolyType(tparams, newinfo)
- case _ => newinfo
- }
- }
+ 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
val typedMods = typedModifiers(cdef.mods)
@@ -1861,10 +1874,30 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// 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.map(_.completeInfo())
if (templ.symbol == NoSymbol)
templ setSymbol clazz.newLocalDummy(templ.pos)
- val self1 = templ.self match {
+ val self1 = (templ.self: @unchecked) match {
case vd @ ValDef(_, _, tpt, EmptyTree) =>
val tpt1 = checkNoEscaping.privates(
clazz.thisSym,
@@ -2228,7 +2261,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val allParams = meth.paramss.flatten
for (p <- allParams) {
for (n <- p.deprecatedParamName) {
- if (allParams.exists(p1 => p1.name == n || (p != p1 && p1.deprecatedParamName.exists(_ == n))))
+ if (allParams.exists(p1 => p != p1 && (p1.name == n || p1.deprecatedParamName.exists(_ == n))))
DeprecatedParamNameError(p, n)
}
}
@@ -2581,8 +2614,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// the default uses applyOrElse's first parameter since the scrut's type has been widened
val match_ = {
- val defaultCase = methodBodyTyper.typedCase(
- mkDefaultCase(methodBodyTyper.typed1(REF(default) APPLY (REF(x)), mode, B1.tpe).setType(B1.tpe)), argTp, B1.tpe)
+ 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)
treeCopy.Match(match0, match0.selector, match0.cases :+ defaultCase)
}
match_ setType B1.tpe
@@ -2710,187 +2743,99 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
- /** Synthesize and type check the implementation of a type with a Single Abstract Method
- *
- * `{ (p1: T1, ..., pN: TN) => body } : S`
- *
- * expands to (where `S` is the expected type that defines a single abstract method named `apply`)
- *
- * `{
- * def apply$body(p1: T1, ..., pN: TN): T = body
- * new S {
- * def apply(p1: T1', ..., pN: TN'): T' = apply$body(p1,..., pN)
- * }
- * }`
- *
- * If 'T' is not fully defined, it is inferred by type checking
- * `apply$body` without a result type before type checking the block.
- * The method's inferred result type is used instead of `T`. [See test/files/pos/sammy_poly.scala]
- *
- * The `apply` method is identified by the argument `sam`; `S` corresponds to the argument `samClassTp`,
- * and `resPt` is derived from `samClassTp` -- it may be fully defined, or not...
- * If it 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 method outside of the class definition to enforce scoping.
- * S's members should not be in scope in `body`.
- *
- * The restriction on implicit arguments (neither S's constructor, nor sam may take an implicit argument list),
- * is largely to keep the implementation of type inference (the computation of `samClassTpFullyDefined`) simple.
- *
- * NOTE: it would be nicer to not have to type check `apply$body` separately when `T` is not fully defined.
- * However T must be fully defined before we type the instantiation, as it'll end up as a parent type,
- * which must be fully defined. Would be nice to have some kind of mechanism to insert type vars in a block of code,
- * and have the instantiation of the first occurrence propagate to the rest of the block.
- *
- * TODO: by-name params
- * scala> trait LazySink { def accept(a: => Any): Unit }
- * defined trait LazySink
- *
- * scala> val f: LazySink = (a) => (a, a)
- * f: LazySink = $anonfun$1@1fb26910
- *
- * scala> f(println("!"))
- * <console>:10: error: LazySink does not take parameters
- * f(println("!"))
- * ^
- *
- * scala> f.accept(println("!"))
- * !
- * !
- */
- def synthesizeSAMFunction(sam: Symbol, fun: Function, resPt: Type, samClassTp: Type, mode: Mode): Tree = {
- // assert(fun.vparams forall (vp => isFullyDefined(vp.tpt.tpe))) -- by construction, as we take them from sam's info
- val sampos = fun.pos
-
- // if the expected sam type is fully defined, use it for the method's result type
- // otherwise, NoType, so that type inference will determine the method's result type
- // resPt is syntactically contained in samClassTp, so if the latter is fully defined, so is the former
- // ultimately, we want to fully define samClassTp as it is used as the superclass of our anonymous class
- val samDefTp = if (isFullyDefined(resPt)) resPt else NoType
- val bodyName = newTermName(sam.name + "$body")
-
- // `def '${sam.name}\$body'($p1: $T1, ..., $pN: $TN): $resPt = $body`
- val samBodyDef =
- DefDef(NoMods,
- bodyName,
- Nil,
- List(fun.vparams.map(_.duplicate)), // must duplicate as we're also using them for `samDef`
- TypeTree(samDefTp) setPos sampos.focus,
- fun.body)
-
- // If we need to enter the sym for the body def before type checking the block,
- // we'll create a nested context, as explained below.
- var nestedTyper = this
-
- // Type check body def before classdef to fully determine samClassTp (if necessary).
- // As `samClassTp` determines a parent type for the class,
- // we can't type check `block` in one go unless `samClassTp` is fully defined.
- val samClassTpFullyDefined =
- if (isFullyDefined(samClassTp)) samClassTp
+ /** 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): SAMFunction = {
+ val sam =
+ if (fun.isInstanceOf[Function] && !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 = samOf(pt)
+ if (samMatchesFunctionBasedOnArity(sam, fun.asInstanceOf[Function].vparams)) sam
+ else NoSymbol
+ } else NoSymbol
+
+ def fullyDefinedMeetsExpectedFunTp(pt: Type): Boolean = isFullyDefined(pt) && {
+ val samMethType = pt memberInfo sam
+ fun.tpe <:< functionType(samMethType.paramTypes, samMethType.resultType)
+ }
+
+ SAMFunction(
+ if (!sam.exists) NoType
+ else if (fullyDefinedMeetsExpectedFunTp(pt)) pt
else try {
- // This creates a symbol for samBodyDef with a type completer that'll be triggered immediately below.
- // The symbol is entered in the same scope used for the block below, and won't thus be reentered later.
- // It has to be a new scope, though, or we'll "get ambiguous reference to overloaded definition" [pos/sammy_twice.scala]
- // makeSilent: [pos/nonlocal-unchecked.scala -- when translation all functions to sams]
- val nestedCtx = enterSym(context.makeNewScope(context.tree, context.owner).makeSilent(), samBodyDef)
- nestedTyper = newTyper(nestedCtx)
-
- // NOTE: this `samBodyDef.symbol.info` runs the type completer set up by the enterSym above
- val actualSamType = samBodyDef.symbol.info
+ val samClassSym = pt.typeSymbol
// we're trying to fully define the type arguments for this type constructor
- val samTyCon = samClassTp.typeSymbol.typeConstructor
+ val samTyCon = samClassSym.typeConstructor
// the unknowns
- val tparams = samClassTp.typeSymbol.typeParams
+ val tparams = samClassSym.typeParams
// ... as typevars
- val tvars = tparams map freshVar
-
- // 1. Recover partial information:
- // - derive a type from samClassTp that has the corresponding tparams for type arguments that aren't fully defined
- // - constrain typevars to be equal to type args that are fully defined
- val samClassTpMoreDefined = appliedType(samTyCon,
- (samClassTp.typeArgs, tparams, tvars).zipped map {
- case (a, _, tv) if isFullyDefined(a) => tv =:= a; a
- case (_, p, _) => p.typeConstructor
- })
+ val tvars = tparams map freshVar
- // the method type we're expecting the synthesized sam to have, based on the expected sam type,
- // where fully defined type args to samClassTp have been preserved,
- // with the unknown args replaced by their corresponding type param
- val expectedSamType = samClassTpMoreDefined.memberInfo(sam)
+ val ptVars = appliedType(samTyCon, tvars)
- // 2. make sure the body def's actual type (formals and result) conforms to
- // sam's expected type (in terms of the typevars that represent the sam's class's type params)
- actualSamType <:< expectedSamType.substituteTypes(tparams, tvars)
+ // carry over info from pt
+ ptVars <:< pt
- // solve constraints tracked by tvars
- val targs = solvedTypes(tvars, tparams, tparams map varianceInType(sam.info), upper = false, lubDepth(sam.info :: Nil))
+ val samInfoWithTVars = ptVars.memberInfo(sam)
- debuglog(s"sam infer: $samClassTp --> ${appliedType(samTyCon, targs)} by $actualSamType <:< $expectedSamType --> $targs for $tparams")
+ // use function type subtyping, not method type subtyping (the latter is invariant in argument types)
+ fun.tpe <:< functionType(samInfoWithTVars.paramTypes, samInfoWithTVars.finalResultType)
- // a fully defined samClassTp
- appliedType(samTyCon, targs)
- } catch {
- case _: NoInstance | _: TypeError =>
- devWarning(sampos, s"Could not define type $samClassTp using ${samBodyDef.symbol.rawInfo} <:< ${samClassTp memberInfo sam} (for $sam)")
- samClassTp
- }
-
- // what's the signature of the method that we should actually be overriding?
- val samMethTp = samClassTpFullyDefined memberInfo sam
- // Before the mutation, `tp <:< vpar.tpt.tpe` should hold.
- // TODO: error message when this is not the case, as the expansion won't type check
- // - Ti' <:< Ti and T <: T' must hold for the samDef body to type check
- val funArgTps = foreach2(samMethTp.paramTypes, fun.vparams)((tp, vpar) => vpar.tpt setType tp)
-
- // `final override def ${sam.name}($p1: $T1', ..., $pN: $TN'): ${samMethTp.finalResultType} = ${sam.name}\$body'($p1, ..., $pN)`
- val samDef =
- DefDef(Modifiers(FINAL | OVERRIDE | SYNTHETIC),
- sam.name.toTermName,
- Nil,
- List(fun.vparams),
- TypeTree(samMethTp.finalResultType) setPos sampos.focus,
- Apply(Ident(bodyName), fun.vparams map gen.paramToArg)
- )
+ val variances = tparams map varianceInType(sam.info)
- val serializableParentAddendum =
- if (typeIsSubTypeOfSerializable(samClassTp)) Nil
- else List(TypeTree(SerializableTpe))
-
- val classDef =
- ClassDef(Modifiers(FINAL), tpnme.ANON_FUN_NAME, tparams = Nil,
- gen.mkTemplate(
- parents = TypeTree(samClassTpFullyDefined) :: serializableParentAddendum,
- self = noSelfType,
- constrMods = NoMods,
- vparamss = ListOfNil,
- body = List(samDef),
- superPos = sampos.focus
- )
- )
-
- // type checking the whole block, so that everything is packaged together nicely
- // and we don't have to create any symbols by hand
- val block =
- nestedTyper.typedPos(sampos, mode, samClassTpFullyDefined) {
- Block(
- samBodyDef,
- classDef,
- Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), Nil)
- )
- }
+ // solve constraints tracked by tvars
+ val targs = solvedTypes(tvars, tparams, variances, upper = false, lubDepth(sam.info :: Nil))
- // TODO: improve error reporting -- when we're in silent mode (from `silent(_.doTypedApply(tree, fun, args, mode, pt)) orElse onError`)
- // the errors in the function don't get out...
- if (block exists (_.isErroneous))
- context.error(fun.pos, s"Could not derive subclass of $samClassTp\n (with SAM `def $sam$samMethTp`)\n based on: $fun.")
+ debuglog(s"sam infer: $pt --> ${appliedType(samTyCon, targs)} by ${fun.tpe} <:< $samInfoWithTVars --> $targs for $tparams")
- classDef.symbol addAnnotation SerialVersionUIDAnnotation
- block
+ val ptFullyDefined = appliedType(samTyCon, targs)
+ 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
+ }, sam)
}
/** Type check a function literal.
@@ -2900,16 +2845,19 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* - a type with a Single Abstract Method (under -Xexperimental for now).
*/
private def typedFunction(fun: Function, mode: Mode, pt: Type): Tree = {
- val numVparams = fun.vparams.length
+ val vparams = fun.vparams
+ val numVparams = vparams.length
val FunctionSymbol =
if (numVparams > definitions.MaxFunctionArity) NoSymbol
else FunctionClass(numVparams)
+ val ptSym = pt.typeSymbol
+
/* The Single Abstract Member of pt, unless pt is the built-in function type of the expected arity,
* as `(a => a): Int => Int` should not (yet) get the sam treatment.
*/
val sam =
- if (pt.typeSymbol == FunctionSymbol) NoSymbol
+ if (ptSym == NoSymbol || ptSym == FunctionSymbol || ptSym == PartialFunctionClass) NoSymbol
else samOf(pt)
/* The SAM case comes first so that this works:
@@ -2917,79 +2865,101 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
* (a => a): MyFun
*
* Note that the arity of the sam must correspond to the arity of the function.
+ * TODO: handle vararg sams?
*/
- val samViable = sam.exists && sameLength(sam.info.params, fun.vparams)
- val ptNorm = if (samViable) samToFunctionType(pt, sam) else pt
+ val ptNorm =
+ if (samMatchesFunctionBasedOnArity(sam, vparams)) samToFunctionType(pt, sam)
+ else pt
val (argpts, respt) =
ptNorm baseType FunctionSymbol match {
case TypeRef(_, FunctionSymbol, args :+ res) => (args, res)
- case _ => (fun.vparams map (_ => if (pt == ErrorType) ErrorType else NoType), WildcardType)
+ case _ => (vparams map (if (pt == ErrorType) (_ => ErrorType) else (_ => NoType)), WildcardType)
}
- if (!FunctionSymbol.exists)
- MaxFunctionArityError(fun)
- else if (argpts.lengthCompare(numVparams) != 0)
- WrongNumberOfParametersError(fun, argpts)
+ if (!FunctionSymbol.exists) MaxFunctionArityError(fun)
+ else if (argpts.lengthCompare(numVparams) != 0) WrongNumberOfParametersError(fun, argpts)
else {
- var issuedMissingParameterTypeError = false
- foreach2(fun.vparams, argpts) { (vparam, argpt) =>
+ 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, argpts) { (vparam, argpt) =>
if (vparam.tpt.isEmpty) {
- val vparamType =
- if (isFullyDefined(argpt)) argpt
- else {
- fun match {
- case etaExpansion(vparams, fn, args) =>
- silent(_.typed(fn, mode.forFunMode, pt)) filter (_ => context.undetparams.isEmpty) map { fn1 =>
- // if context.undetparams is not empty, the function was polymorphic,
- // so we need the missing arguments to infer its type. See #871
- //println("typing eta "+fun+":"+fn1.tpe+"/"+context.undetparams)
- val ftpe = normalize(fn1.tpe) baseType FunctionClass(numVparams)
- if (isFunctionType(ftpe) && isFullyDefined(ftpe))
- return typedFunction(fun, mode, ftpe)
- }
- case _ =>
- }
- MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError)
- issuedMissingParameterTypeError = true
- ErrorType
- }
- vparam.tpt.setType(vparamType)
+ if (isFullyDefined(argpt)) vparam.tpt setType argpt
+ else paramsMissingType += vparam
+
if (!vparam.tpt.pos.isDefined) vparam.tpt setPos vparam.pos.focus
}
}
- fun.body match {
- // translate `x => x match { <cases> }` : PartialFunction to
- // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }`
- case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) =>
- // 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 = fun.vparams.head
- if (p.tpt.tpe == null) p.tpt setType outerTyper.typedType(p.tpt).tpe
+ // If we're 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.
+ // This tree is the result from one of:
+ // - manual eta-expansion with named arguments (x => f(x));
+ // - wildcard-style eta expansion (`m(_, _,)`);
+ // - instantiateToMethodType adapting a tree of method type to a function type using etaExpand.
+ //
+ // Note that method values are a separate thing (`m _`): they have the idiosyncratic shape
+ // of `Typed(expr, Function(Nil, EmptyTree))`
+ val ptUnrollingEtaExpansion =
+ if (paramsMissingType.nonEmpty && pt != ErrorType) 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
+ case Apply(meth, args) if (vparams corresponds args) { case (p, Ident(name)) => p.name == name case _ => false } =>
+ // 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
+ val methArgs = NoSymbol.newSyntheticValueParams(argpts map { case NoType => WildcardType case tp => tp })
+ silent(_.typed(meth, mode.forFunMode, MethodType(methArgs, respt))) filter (isMonoContext) map { methTyped =>
+ // if context.undetparams is not empty, the method was polymorphic,
+ // so we need the missing arguments to infer its type. See #871
+ val funPt = normalize(methTyped.tpe) baseType FunctionClass(numVparams)
+ // println(s"typeUnEtaExpanded $meth : ${methTyped.tpe} --> normalized: $funPt")
+
+ // If we are sure this function type provides all the necesarry info, so that we won't have
+ // any undetermined argument types, go ahead an recurse below (`typedFunction(fun, mode, ptUnrollingEtaExpansion)`)
+ // and rest assured we won't end up right back here (and keep recursing)
+ if (isFunctionType(funPt) && funPt.typeArgs.iterator.take(numVparams).forall(isFullyDefined)) funPt
+ else null
+ } orElse { _ => null }
+ case _ => null
+ } else null
+
+
+ if (ptUnrollingEtaExpansion ne null) typedFunction(fun, mode, ptUnrollingEtaExpansion)
+ else {
+ // we ran out of things to try, missing parameter types are an irrevocable error
+ var issuedMissingParameterTypeError = false
+ paramsMissingType.foreach { vparam =>
+ vparam.tpt setType ErrorType
+ MissingParameterTypeError(fun, vparam, pt, withTupleAddendum = !issuedMissingParameterTypeError)
+ issuedMissingParameterTypeError = true
+ }
- outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt)
+ fun.body match {
+ // translate `x => x match { <cases> }` : PartialFunction to
+ // `new PartialFunction { def applyOrElse(x, default) = x match { <cases> } def isDefinedAt(x) = ... }`
+ case Match(sel, cases) if (sel ne EmptyTree) && (pt.typeSymbol == PartialFunctionClass) =>
+ // 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
- // Use synthesizeSAMFunction to expand `(p1: T1, ..., pN: TN) => body`
- // to an instance of the corresponding anonymous subclass of `pt`.
- case _ if samViable =>
- newTyper(context.outer).synthesizeSAMFunction(sam, fun, respt, pt, mode)
+ outerTyper.synthesizePartialFunction(p.name, p.pos, paramSynthetic = false, fun.body, mode, pt)
- // regular Function
- case _ =>
- val vparamSyms = fun.vparams map { vparam =>
- enterSym(context, vparam)
- if (context.retyping) context.scope enter vparam.symbol
- vparam.symbol
- }
- val vparams = fun.vparams mapConserve typedValDef
- val formals = vparamSyms map (_.tpe)
- val body1 = typed(fun.body, respt)
- val restpe = packedType(body1, fun.symbol).deconst.resultType
- val funtpe = appliedType(FunctionSymbol, formals :+ restpe: _*)
+ case _ =>
+ val vparamSyms = vparams map { vparam =>
+ enterSym(context, vparam)
+ if (context.retyping) context.scope enter vparam.symbol
+ vparam.symbol
+ }
+ val vparamsTyped = vparams mapConserve typedValDef
+ val formals = vparamSyms map (_.tpe)
+ val body1 = typed(fun.body, respt)
+ val restpe = packedType(body1, fun.symbol).deconst.resultType
+ val funtpe = phasedAppliedType(FunctionSymbol, formals :+ restpe)
- treeCopy.Function(fun, vparams, body1) setType funtpe
+ treeCopy.Function(fun, vparamsTyped, body1) setType funtpe
+ }
}
}
}
@@ -3022,43 +2992,36 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
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 = {
- if (context.owner.isRefinementClass && !treeInfo.isDeclarationOrTypeDef(stat))
- OnlyDeclarationsError(stat)
- else
- stat match {
- case imp @ Import(_, _) =>
- imp.symbol.initialize
- if (!imp.symbol.isError) {
- context = context.make(imp)
- typedImport(imp)
- } else EmptyTree
- case _ =>
- if (localTarget && !includesTargetPos(stat)) {
- // skip typechecking of statements in a sequence where some other statement includes
- // the targetposition
- stat
- } else {
- 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(localTyper.typedByValueExpr(stat))
-
- if (treeInfo.isSelfOrSuperConstrCall(result)) {
- context.inConstructorSuffix = true
- if (treeInfo.isSelfConstrCall(result) && result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0))
- ConstructorsOrderError(stat)
- }
-
- if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
- "a pure expression does nothing in statement position; " +
- "you may be omitting necessary parentheses"
- )
- result
- }
+ 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(localTyper.typedByValueExpr(stat))
+
+ if (treeInfo.isSelfOrSuperConstrCall(result)) {
+ context.inConstructorSuffix = true
+ if (treeInfo.isSelfConstrCall(result)) {
+ if (result.symbol == exprOwner.enclMethod)
+ ConstructorRecursesError(stat)
+ else if (result.symbol.pos.pointOrElse(0) >= exprOwner.enclMethod.pos.pointOrElse(0))
+ ConstructorsOrderError(stat)
+ }
}
+ if (!isPastTyper && treeInfo.isPureExprForWarningPurposes(result)) context.warning(stat.pos,
+ "a pure expression does nothing in statement position; you may be omitting necessary parentheses"
+ )
+ result
}
/* 'accessor' and 'accessed' are so similar it becomes very difficult to
@@ -3152,7 +3115,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (phase.erasedTypes) stats1
else {
// 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 defininitions are checked in `Namers.enterInScope`
+ // it is expensive if the package is large. Instead, such double definitions are checked in `Namers.enterInScope`
if (!context.owner.isPackageClass)
checkNoDoubleDefs
addSynthetics(stats1)
@@ -3219,6 +3182,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// 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 AssignOrNamedArg(Ident(name), rhs) =>
NamedType(name, shapeType(rhs))
@@ -3664,7 +3630,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val annType = annTpt.tpe
finish(
- if (typedFun.isErroneous)
+ if (typedFun.isErroneous || annType == null)
ErroneousAnnotation
else if (annType.typeSymbol isNonBottomSubClass ClassfileAnnotationClass) {
// annotation to be saved as java classfile annotation
@@ -4141,6 +4107,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
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.
+ 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
}
}
@@ -4292,7 +4266,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (pt.typeSymbol == PartialFunctionClass)
synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt)
else {
- val arity = if (isFunctionType(pt)) pt.dealiasWiden.typeArgs.length - 1 else 1
+ val arity = functionArityFromType(pt) match { case -1 => 1 case arity => arity } // SI-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),
@@ -4392,31 +4367,43 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
treeCopy.New(tree, tpt1).setType(tp)
}
- def functionTypeWildcard(tree: Tree, arity: Int): Type = {
- val tp = functionType(List.fill(arity)(WildcardType), WildcardType)
- if (tp == NoType) MaxFunctionArityError(tree)
- tp
- }
-
- def typedEta(expr1: Tree): Tree = expr1.tpe match {
- case TypeRef(_, ByNameParamClass, _) =>
- val expr2 = Function(List(), expr1) setPos expr1.pos
- new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
- typed1(expr2, mode, pt)
- case NullaryMethodType(restpe) =>
- val expr2 = Function(List(), expr1) setPos expr1.pos
- new ChangeOwnerTraverser(context.owner, expr2.symbol).traverse(expr2)
- typed1(expr2, mode, pt)
- case PolyType(_, MethodType(formals, _)) =>
- if (isFunctionType(pt)) expr1
- else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length))
- case MethodType(formals, _) =>
- if (isFunctionType(pt)) expr1
- else adapt(expr1, mode, functionTypeWildcard(expr1, formals.length))
+ def functionTypeWildcard(arity: Int): Type =
+ functionType(List.fill(arity)(WildcardType), WildcardType)
+
+ def checkArity(tree: Tree)(tp: Type): tp.type = tp match {
+ case NoType => MaxFunctionArityError(tree); tp
+ case _ => 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 parameterlist `()`.
+ */
+ def typedEta(methodValue: Tree): Tree = methodValue.tpe match {
+ case tp@(MethodType(_, _) | PolyType(_, MethodType(_, _))) => // (1)
+ val formals = tp.params
+ if (isFunctionType(pt) || samMatchesFunctionBasedOnArity(samOf(pt), formals)) methodValue
+ else adapt(methodValue, mode, checkArity(methodValue)(functionTypeWildcard(formals.length)))
+
+ 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
+
+ typed(Function(List(), methodValue) setSymbol funSym setPos pos, mode, pt)
+
case ErrorType =>
- expr1
+ methodValue
+
case _ =>
- UnderscoreEtaError(expr1)
+ UnderscoreEtaError(methodValue)
}
def tryTypedArgs(args: List[Tree], mode: Mode): Option[List[Tree]] = {
@@ -4460,7 +4447,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case Annotated(_, r) => treesInResult(r)
case If(_, t, e) => treesInResult(t) ++ treesInResult(e)
case Try(b, catches, _) => treesInResult(b) ++ catches
- case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r)
+ case Typed(r, Function(Nil, EmptyTree)) => treesInResult(r) // a method value
case Select(qual, name) => treesInResult(qual)
case Apply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult)
case TypeApply(fun, args) => treesInResult(fun) ++ args.flatMap(treesInResult)
@@ -4479,7 +4466,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
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, reportAmbiguous = true, saveErrors = true)
+ 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
@@ -4640,10 +4627,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
def findMixinSuper(site: Type): Type = {
var ps = site.parents filter (_.typeSymbol.name == mix)
if (ps.isEmpty)
- ps = site.parents filter (_.typeSymbol.toInterface.name == mix)
+ 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.isImplClass) {
+ 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
@@ -4706,7 +4693,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// 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)
if (name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) {
- val qual1 = adaptToMemberWithArgs(tree, qual, name, mode, reportAmbiguous = true, saveErrors = true)
+ val qual1 = adaptToMemberWithArgs(tree, qual, name, mode)
if ((qual1 ne qual) && !qual1.isErrorTyped)
return typed(treeCopy.Select(tree, qual1, name), mode, pt)
}
@@ -5100,11 +5087,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// because `expr` might contain nested macro calls (see SI-6673)
//
// Note: apparently `Function(Nil, EmptyTree)` is the secret parser marker
- // which means trailing underscore.
+ // which means trailing underscore -- denoting a method value. See makeMethodValue in TreeBuilder.
case Typed(expr, Function(Nil, EmptyTree)) =>
typed1(suppressMacroExpansion(expr), mode, pt) match {
case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef)
- case exprTyped => typedEta(checkDead(exprTyped))
+ case methodValue => typedEta(checkDead(methodValue))
}
case Typed(expr, tpt) =>
val tpt1 = typedType(tpt, mode) // type the ascribed type first
@@ -5195,17 +5182,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case MethodType(p :: _, _) => p.isImplicit // implicit method requires no args
case _ => true // catches all others including NullaryMethodType
}
- def isPlausible(m: Symbol) = m.alternatives exists (m => requiresNoArgs(m.info))
+ def isPlausible(m: Symbol) = !m.isPackage && m.alternatives.exists(x => requiresNoArgs(x.info))
def maybeWarn(s: String): Unit = {
def warn(message: String) = context.warning(lit.pos, s"possible missing interpolator: $message")
def suspiciousSym(name: TermName) = context.lookupSymbol(name, _ => true).symbol
- def suspiciousExpr = InterpolatorCodeRegex findFirstIn s
+ val suspiciousExprs = InterpolatorCodeRegex findAllMatchIn s
def suspiciousIdents = InterpolatorIdentRegex findAllIn s map (s => suspiciousSym(TermName(s drop 1)))
-
- if (suspiciousExpr.nonEmpty)
- warn("detected an interpolated expression") // "${...}"
- else
+ def isCheapIdent(expr: String) = (Character.isJavaIdentifierStart(expr.charAt(0)) &&
+ expr.tail.forall(Character.isJavaIdentifierPart))
+ def warnableExpr(expr: String) = !expr.isEmpty && (!isCheapIdent(expr) || isPlausible(suspiciousSym(TermName(expr))))
+
+ if (suspiciousExprs.nonEmpty) {
+ 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 isPlausible foreach (sym => warn(s"detected interpolated identifier `$$${sym.name}`")) // "$id"
}
lit match {
@@ -5229,7 +5222,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (refTyped.isErrorTyped) {
setError(tree)
} else {
- tree setType refTyped.tpe.resultType
+ tree setType refTyped.tpe.resultType.deconst
if (refTyped.isErrorTyped || treeInfo.admitsTypeSelection(refTyped)) tree
else UnstableTreeError(tree)
}
@@ -5417,6 +5410,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (!isPastTyper)
signalDone(context.asInstanceOf[analyzer.Context], tree, result)
+ if (mode.inPatternMode && !mode.inPolyMode && result.isType)
+ PatternMustBeValue(result, pt)
+
result
}
@@ -5506,10 +5502,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// 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))) match {
- case tpt if tpt.isType => PatternMustBeValue(tpt, pt); tpt
- case pat => pat
- }
+ typingInPattern(context.withImplicitsDisabledAllowEnrichment(typed(tree, PATTERNmode, pt)))
}
/** Types a (fully parameterized) type tree */
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala
index 37fbb73b85..f2911fb98b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypersTracking.scala
@@ -6,9 +6,6 @@
package scala.tools.nsc
package typechecker
-import scala.collection.mutable
-import scala.reflect.internal.util.{ BatchSourceFile, Statistics }
-import mutable.ListBuffer
import Mode._
trait TypersTracking {