summaryrefslogtreecommitdiff
path: root/src/compiler/scala
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-01-29 09:59:54 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-01-29 09:59:54 +1000
commit1db62995b52b06f5037331172b3f54739d720d62 (patch)
tree0b684c4adfcb800fa195947b37fdb5dc89731552 /src/compiler/scala
parent8b5f2b435b4b14089806406c8923f7e845d10ef6 (diff)
parenteb15950e697eb77e52733f81c65e2d51951ad881 (diff)
downloadscala-1db62995b52b06f5037331172b3f54739d720d62.tar.gz
scala-1db62995b52b06f5037331172b3f54739d720d62.tar.bz2
scala-1db62995b52b06f5037331172b3f54739d720d62.zip
Merge commit 'eb15950' into merge/2.11.x-to-2.12.x-20150129
Diffstat (limited to 'src/compiler/scala')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala32
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala29
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala82
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala83
-rw-r--r--src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/AbsSettings.scala6
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala25
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Delambdafy.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala7
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Logic.scala17
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala56
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala18
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/Solving.scala96
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala41
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala32
19 files changed, 326 insertions, 211 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index 0c0d726630..4285858bf8 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -14,6 +14,13 @@ import PartialFunction._
*/
final class BCodeAsmCommon[G <: Global](val global: G) {
import global._
+ import definitions._
+
+ val ExcludedForwarderFlags = {
+ import scala.tools.nsc.symtab.Flags._
+ // Should include DEFERRED but this breaks findMember.
+ ( SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO )
+ }
/**
* True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
@@ -124,4 +131,29 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
assert(r != NoSymbol, sym.fullLocationString)
r
})(collection.breakOut)
+
+ lazy val AnnotationRetentionPolicyModule = AnnotationRetentionPolicyAttr.companionModule
+ lazy val AnnotationRetentionPolicySourceValue = AnnotationRetentionPolicyModule.tpe.member(TermName("SOURCE"))
+ lazy val AnnotationRetentionPolicyClassValue = AnnotationRetentionPolicyModule.tpe.member(TermName("CLASS"))
+ lazy val AnnotationRetentionPolicyRuntimeValue = AnnotationRetentionPolicyModule.tpe.member(TermName("RUNTIME"))
+
+ /** Whether an annotation should be emitted as a Java annotation
+ * .initialize: if 'annot' is read from pickle, atp might be un-initialized
+ */
+ def shouldEmitAnnotation(annot: AnnotationInfo) = {
+ annot.symbol.initialize.isJavaDefined &&
+ annot.matches(ClassfileAnnotationClass) &&
+ retentionPolicyOf(annot) != AnnotationRetentionPolicySourceValue &&
+ annot.args.isEmpty
+ }
+
+ def isRuntimeVisible(annot: AnnotationInfo): Boolean =
+ annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr)
+ .exists(_.assocs.contains((nme.value -> LiteralAnnotArg(Constant(AnnotationRetentionPolicyRuntimeValue)))))
+
+ private def retentionPolicyOf(annot: AnnotationInfo): Symbol =
+ annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).map(_.assocs).map(assoc =>
+ assoc.collectFirst {
+ case (`nme`.value, LiteralAnnotArg(Constant(value: Symbol))) => value
+ }).flatten.getOrElse(AnnotationRetentionPolicyClassValue)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 14bffd67ab..806d4b277c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -469,6 +469,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
trait BCAnnotGen extends BCInnerClassGen {
import genASM.{ubytesToCharArray, arrEncode}
+ import bCodeAsmCommon.{shouldEmitAnnotation, isRuntimeVisible}
/*
* can-multi-thread
@@ -533,17 +534,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
}
- /* Whether an annotation should be emitted as a Java annotation
- * .initialize: if 'annot' is read from pickle, atp might be un-initialized
- *
- * must-single-thread
- */
- private def shouldEmitAnnotation(annot: AnnotationInfo) =
- annot.symbol.initialize.isJavaDefined &&
- annot.matches(definitions.ClassfileAnnotationClass) &&
- annot.args.isEmpty &&
- !annot.matches(definitions.DeprecatedAttr)
-
/*
* In general,
* must-single-thread
@@ -563,7 +553,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = cw.visitAnnotation(descriptor(typ), true)
+ val av = cw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -575,7 +565,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = mw.visitAnnotation(descriptor(typ), true)
+ val av = mw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -587,7 +577,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = fw.visitAnnotation(descriptor(typ), true)
+ val av = fw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -602,7 +592,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
annot <- annots) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), true)
+ val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), isRuntimeVisible(annot))
emitAssocs(pannVisitor, assocs)
}
}
@@ -625,13 +615,6 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
trait BCForwardersGen extends BCAnnotGen with BCJGenSigGen {
- // -----------------------------------------------------------------------------------------
- // Static forwarders (related to mirror classes but also present in
- // a plain class lacking companion module, for details see `isCandidateForForwarders`).
- // -----------------------------------------------------------------------------------------
-
- val ExcludedForwarderFlags = genASM.ExcludedForwarderFlags
-
/* Adds a @remote annotation, actual use unknown.
*
* Invoked from genMethod() and addForwarder().
@@ -727,7 +710,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
}
debuglog(s"Potentially conflicting names for forwarders: $conflictingNames")
- for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, symtab.Flags.METHOD)) {
+ for (m <- moduleClass.info.membersBasedOnFlags(bCodeAsmCommon.ExcludedForwarderFlags, symtab.Flags.METHOD)) {
if (m.isType || m.isDeferred || (m.owner eq definitions.ObjectClass) || m.isConstructor)
debuglog(s"No forwarder for '$m' from $jclassName to '$moduleClass'")
else if (conflictingNames(m.name))
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 2593903b9d..d0a12c32e5 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -20,7 +20,7 @@ import scala.annotation.tailrec
*
* Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2012Q2/GenASM.pdf
*/
-abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { self =>
+abstract class GenASM extends SubComponent with BytecodeWriters { self =>
import global._
import icodes._
import icodes.opcodes._
@@ -99,17 +99,76 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
}
+ private def isJavaEntryPoint(icls: IClass) = {
+ val sym = icls.symbol
+ def fail(msg: String, pos: Position = sym.pos) = {
+ reporter.warning(sym.pos,
+ sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" +
+ " Reason: " + msg
+ // TODO: make this next claim true, if possible
+ // by generating valid main methods as static in module classes
+ // not sure what the jvm allows here
+ // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead."
+ )
+ false
+ }
+ def failNoForwarder(msg: String) = {
+ fail(msg + ", which means no static forwarder can be generated.\n")
+ }
+ val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil
+ val hasApproximate = possibles exists { m =>
+ m.info match {
+ case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass
+ case _ => false
+ }
+ }
+ // At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
+ hasApproximate && {
+ // Before erasure so we can identify generic mains.
+ enteringErasure {
+ val companion = sym.linkedClassOfClass
+
+ if (hasJavaMainMethod(companion))
+ failNoForwarder("companion contains its own main method")
+ else if (companion.tpe.member(nme.main) != NoSymbol)
+ // this is only because forwarders aren't smart enough yet
+ failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
+ else if (companion.isTrait)
+ failNoForwarder("companion is a trait")
+ // Now either succeeed, or issue some additional warnings for things which look like
+ // attempts to be java main methods.
+ else (possibles exists isJavaMainMethod) || {
+ possibles exists { m =>
+ m.info match {
+ case PolyType(_, _) =>
+ fail("main methods cannot be generic.")
+ case MethodType(params, res) =>
+ if (res.typeSymbol :: params exists (_.isAbstractType))
+ fail("main methods cannot refer to type parameters or abstract types.", m.pos)
+ else
+ isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos)
+ case tp =>
+ fail("don't know what this is: " + tp, m.pos)
+ }
+ }
+ }
+ }
+ }
+ }
+
override def run() {
if (settings.debug)
inform("[running phase " + name + " on icode]")
- if (settings.Xdce)
- for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) {
+ if (settings.Xdce) {
+ val classes = icodes.classes.keys.toList // copy to avoid mutating the map while iterating
+ for (sym <- classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) {
log(s"Optimizer eliminated ${sym.fullNameString}")
deadCode.elidedClosures += sym
icodes.classes -= sym
}
+ }
// For predictably ordered error messages.
var sortedClasses = classes.values.toList sortBy (_.symbol.fullName)
@@ -805,15 +864,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for (ThrownException(exc) <- excs.distinct)
yield javaName(exc)
- /** Whether an annotation should be emitted as a Java annotation
- * .initialize: if 'annot' is read from pickle, atp might be un-initialized
- */
- private def shouldEmitAnnotation(annot: AnnotationInfo) =
- annot.symbol.initialize.isJavaDefined &&
- annot.matches(ClassfileAnnotationClass) &&
- annot.args.isEmpty &&
- !annot.matches(DeprecatedAttr)
-
def getCurrentCUnit(): CompilationUnit
def getGenericSignature(sym: Symbol, owner: Symbol) = self.getGenericSignature(sym, owner, getCurrentCUnit())
@@ -875,7 +925,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = cw.visitAnnotation(descriptor(typ), true)
+ val av = cw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -884,7 +934,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = mw.visitAnnotation(descriptor(typ), true)
+ val av = mw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -893,7 +943,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
for(annot <- annotations; if shouldEmitAnnotation(annot)) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val av = fw.visitAnnotation(descriptor(typ), true)
+ val av = fw.visitAnnotation(descriptor(typ), isRuntimeVisible(annot))
emitAssocs(av, assocs)
}
}
@@ -905,7 +955,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
annot <- annots) {
val AnnotationInfo(typ, args, assocs) = annot
assert(args.isEmpty, args)
- val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), true)
+ val pannVisitor: asm.AnnotationVisitor = jmethod.visitParameterAnnotation(idx, descriptor(typ), isRuntimeVisible(annot))
emitAssocs(pannVisitor, assocs)
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala
deleted file mode 100644
index 2bcde7f7b9..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVMASM.scala
+++ /dev/null
@@ -1,83 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2013 LAMP/EPFL
- * @author Jason Zaugg
- */
-
-package scala.tools.nsc
-package backend.jvm
-import scala.tools.nsc.symtab._
-
-/** Code shared between the erstwhile legacy backend (aka GenJVM)
- * and the new backend [[scala.tools.nsc.backend.jvm.GenASM]]. There should be
- * more here, but for now I'm starting with the refactorings that are either
- * straightforward to review or necessary for maintenance.
- */
-trait GenJVMASM {
- val global: Global
- import global._
- import icodes._
- import definitions._
-
- val ExcludedForwarderFlags = {
- import Flags._
- // Should include DEFERRED but this breaks findMember.
- ( SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO )
- }
-
- protected def isJavaEntryPoint(icls: IClass) = {
- val sym = icls.symbol
- def fail(msg: String, pos: Position = sym.pos) = {
- reporter.warning(sym.pos,
- sym.name + " has a main method with parameter type Array[String], but " + sym.fullName('.') + " will not be a runnable program.\n" +
- " Reason: " + msg
- // TODO: make this next claim true, if possible
- // by generating valid main methods as static in module classes
- // not sure what the jvm allows here
- // + " You can still run the program by calling it as " + sym.javaSimpleName + " instead."
- )
- false
- }
- def failNoForwarder(msg: String) = {
- fail(msg + ", which means no static forwarder can be generated.\n")
- }
- val possibles = if (sym.hasModuleFlag) (sym.tpe nonPrivateMember nme.main).alternatives else Nil
- val hasApproximate = possibles exists { m =>
- m.info match {
- case MethodType(p :: Nil, _) => p.tpe.typeSymbol == ArrayClass
- case _ => false
- }
- }
- // At this point it's a module with a main-looking method, so either succeed or warn that it isn't.
- hasApproximate && {
- // Before erasure so we can identify generic mains.
- enteringErasure {
- val companion = sym.linkedClassOfClass
-
- if (hasJavaMainMethod(companion))
- failNoForwarder("companion contains its own main method")
- else if (companion.tpe.member(nme.main) != NoSymbol)
- // this is only because forwarders aren't smart enough yet
- failNoForwarder("companion contains its own main method (implementation restriction: no main is allowed, regardless of signature)")
- else if (companion.isTrait)
- failNoForwarder("companion is a trait")
- // Now either succeeed, or issue some additional warnings for things which look like
- // attempts to be java main methods.
- else (possibles exists isJavaMainMethod) || {
- possibles exists { m =>
- m.info match {
- case PolyType(_, _) =>
- fail("main methods cannot be generic.")
- case MethodType(params, res) =>
- if (res.typeSymbol :: params exists (_.isAbstractType))
- fail("main methods cannot refer to type parameters or abstract types.", m.pos)
- else
- isJavaMainMethod(m) || fail("main method must have exact signature (Array[String])Unit", m.pos)
- case tp =>
- fail("don't know what this is: " + tp, m.pos)
- }
- }
- }
- }
- }
- }
-}
diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
index 0b218b711c..5bf611a7b0 100644
--- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
+++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala
@@ -29,7 +29,7 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr
case INFO => null
}
- private def clabel(severity: Severity): String = {
+ protected def clabel(severity: Severity): String = {
val label0 = label(severity)
if (label0 eq null) "" else label0 + ": "
}
diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
index 4727e6d867..060a24d8d4 100644
--- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
@@ -35,7 +35,11 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings {
case s: AbsSettings => this.userSetSettings == s.userSetSettings
case _ => false
}
- override def toString() = "Settings {\n%s}\n" format (userSetSettings map (" " + _ + "\n")).mkString
+ override def toString() = {
+ val uss = userSetSettings
+ val indent = if (uss.nonEmpty) " " * 2 else ""
+ uss.mkString(f"Settings {%n$indent", f"%n$indent", f"%n}%n")
+ }
def toConciseString = userSetSettings.mkString("(", " ", ")")
def checkDependencies =
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index bbe21477cb..23611bb629 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -125,14 +125,26 @@ class MutableSettings(val errorFn: String => Unit)
case Some(cmd) => setter(cmd)(args)
}
- // if arg is of form -Xfoo:bar,baz,quux
- def parseColonArg(s: String): Option[List[String]] = {
- val (p, args) = StringOps.splitWhere(s, _ == ':', doDropIndex = true) getOrElse (return None)
-
- // any non-Nil return value means failure and we return s unmodified
- tryToSetIfExists(p, (args split ",").toList, (s: Setting) => s.tryToSetColon _)
+ // -Xfoo: clears Clearables
+ def clearIfExists(cmd: String): Option[List[String]] = lookupSetting(cmd) match {
+ case Some(c: Clearable) => c.clear() ; Some(Nil)
+ case Some(s) => s.errorAndValue(s"Missing argument to $cmd", None)
+ case None => None
}
+ // if arg is of form -Xfoo:bar,baz,quux
+ // the entire arg is consumed, so return None for failure
+ // any non-Nil return value means failure and we return s unmodified
+ def parseColonArg(s: String): Option[List[String]] =
+ if (s endsWith ":") {
+ clearIfExists(s.init)
+ } else {
+ for {
+ (p, args) <- StringOps.splitWhere(s, _ == ':', doDropIndex = true)
+ rest <- tryToSetIfExists(p, (args split ",").toList, (s: Setting) => s.tryToSetColon _)
+ } yield rest
+ }
+
// if arg is of form -Xfoo or -Xfoo bar (name = "-Xfoo")
def parseNormalArg(p: String, args: List[String]): Option[List[String]] =
tryToSetIfExists(p, args, (s: Setting) => s.tryToSet _)
@@ -532,6 +544,7 @@ class MutableSettings(val errorFn: String => Unit)
def prepend(s: String) = prependPath.value = join(s, prependPath.value)
def append(s: String) = appendPath.value = join(appendPath.value, s)
+ override def isDefault = super.isDefault && prependPath.isDefault && appendPath.isDefault
override def value = join(
prependPath.value,
super.value,
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 466e397dd7..850534f2cc 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -262,6 +262,8 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.")
val Ytyperdebug = BooleanSetting("-Ytyper-debug", "Trace all type assignments.")
val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.")
+ val YpatmatExhaustdepth = IntSetting("-Ypatmat-exhaust-depth", "off", 20, Some((10, Int.MaxValue)),
+ str => Some(if(str.equalsIgnoreCase("off")) Int.MaxValue else str.toInt))
val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.")
// TODO 2.12 Remove
diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
index 12e7b23f48..835d338ab3 100644
--- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
+++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala
@@ -260,7 +260,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre
val captureProxies2 = new LinkedHashMap[Symbol, TermSymbol]
captures foreach {capture =>
- val sym = lambdaClass.newVariable(capture.name.toTermName, capture.pos, SYNTHETIC)
+ val sym = lambdaClass.newVariable(unit.freshTermName(capture.name.toString + "$"), capture.pos, SYNTHETIC)
sym setInfo capture.info
captureProxies2 += ((capture, sym))
}
diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala
index fa53ef48b5..4662ef6224 100644
--- a/src/compiler/scala/tools/nsc/transform/Flatten.scala
+++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala
@@ -77,8 +77,11 @@ abstract class Flatten extends InfoTransform {
if (sym.isTerm && !sym.isStaticModule) {
decls1 enter sym
if (sym.isModule) {
- // Nested, non-static moduls are transformed to methods.
- assert(sym.isMethod, s"Non-static $sym should have the lateMETHOD flag from RefChecks")
+ // In theory, we could assert(sym.isMethod), because nested, non-static moduls are
+ // transformed to methods (lateMETHOD flag added in RefChecks). But this requires
+ // forcing sym.info (see comment on isModuleNotMethod), which forces stub symbols
+ // too eagerly (SI-8907).
+
// Note that module classes are not entered into the 'decls' of the ClassInfoType
// of the outer class, only the module symbols are. So the current loop does
// not visit module classes. Therefore we set the LIFTED flag here for module
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
index 0899507bab..e6ddf8b758 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala
@@ -10,7 +10,6 @@ package tools.nsc.transform.patmat
import scala.language.postfixOps
import scala.collection.mutable
import scala.reflect.internal.util.Statistics
-import scala.reflect.internal.util.Position
import scala.reflect.internal.util.HashSet
trait Logic extends Debugging {
@@ -72,6 +71,8 @@ trait Logic extends Debugging {
def unapply(v: Var): Some[Tree]
}
+ def reportWarning(message: String): Unit
+
// resets hash consing -- only supposed to be called by TreeMakersToProps
def prepareNewAnalysis(): Unit
@@ -86,7 +87,7 @@ trait Logic extends Debugging {
def mayBeNull: Boolean
// compute the domain and return it (call registerNull first!)
- def domainSyms: Option[mutable.LinkedHashSet[Sym]]
+ def domainSyms: Option[Set[Sym]]
// the symbol for this variable being equal to its statically known type
// (only available if registerEquality has been called for that type before)
@@ -204,7 +205,7 @@ trait Logic extends Debugging {
def removeVarEq(props: List[Prop], modelNull: Boolean = false): (Formula, List[Formula]) = {
val start = if (Statistics.canEnable) Statistics.startTimer(patmatAnaVarEq) else null
- val vars = mutable.LinkedHashSet[Var]()
+ val vars = new mutable.HashSet[Var]
object gatherEqualities extends PropTraverser {
override def apply(p: Prop) = p match {
@@ -291,7 +292,7 @@ trait Logic extends Debugging {
def eqFreePropToSolvable(p: Prop): Formula
def cnfString(f: Formula): String
- type Model = collection.immutable.SortedMap[Sym, Boolean]
+ type Model = Map[Sym, Boolean]
val EmptyModel: Model
val NoModel: Model
@@ -341,9 +342,9 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
// we enumerate the subtypes of the full type, as that allows us to filter out more types statically,
// once we go to run-time checks (on Const's), convert them to checkable types
// TODO: there seems to be bug for singleton domains (variable does not show up in model)
- lazy val domain: Option[mutable.LinkedHashSet[Const]] = {
- val subConsts: Option[mutable.LinkedHashSet[Const]] = enumerateSubtypes(staticTp).map { tps =>
- mutable.LinkedHashSet(tps: _*).map{ tp =>
+ lazy val domain: Option[Set[Const]] = {
+ val subConsts = enumerateSubtypes(staticTp).map{ tps =>
+ tps.toSet[Type].map{ tp =>
val domainC = TypeConst(tp)
registerEquality(domainC)
domainC
@@ -486,7 +487,7 @@ trait ScalaLogic extends Interface with Logic with TreeAndTypeAnalysis {
}
// accessing after calling registerNull will result in inconsistencies
- lazy val domainSyms: Option[collection.mutable.LinkedHashSet[Sym]] = domain map { _ map symForEqualsTo }
+ lazy val domainSyms: Option[Set[Sym]] = domain map { _ map symForEqualsTo }
lazy val symForStaticTp: Option[Sym] = symForEqualsTo.get(TypeConst(staticTpCheckable))
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
index b2dc6e4e52..21e90b1d78 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
@@ -9,7 +9,6 @@ package scala.tools.nsc.transform.patmat
import scala.language.postfixOps
import scala.collection.mutable
import scala.reflect.internal.util.Statistics
-import scala.reflect.internal.util.Position
trait TreeAndTypeAnalysis extends Debugging {
import global._
@@ -173,7 +172,6 @@ trait TreeAndTypeAnalysis extends Debugging {
// a type is "uncheckable" (for exhaustivity) if we don't statically know its subtypes (i.e., it's unsealed)
// we consider tuple types with at least one component of a checkable type as a checkable type
def uncheckableType(tp: Type): Boolean = {
- def tupleComponents(tp: Type) = tp.normalize.typeArgs
val checkable = (
(isTupleType(tp) && tupleComponents(tp).exists(tp => !uncheckableType(tp)))
|| enumerateSubtypes(tp).nonEmpty)
@@ -400,6 +398,7 @@ trait MatchAnalysis extends MatchApproximation {
trait MatchAnalyzer extends MatchApproximator {
def uncheckedWarning(pos: Position, msg: String) = currentRun.reporting.uncheckedWarning(pos, msg)
def warn(pos: Position, ex: AnalysisBudget.Exception, kind: String) = uncheckedWarning(pos, s"Cannot check match for $kind.\n${ex.advice}")
+ def reportWarning(message: String) = global.reporter.warning(typer.context.tree.pos, message)
// TODO: model dependencies between variables: if V1 corresponds to (x: List[_]) and V2 is (x.hd), V2 cannot be assigned when V1 = null or V1 = Nil
// right now hackily implement this by pruning counter-examples
@@ -524,9 +523,11 @@ trait MatchAnalysis extends MatchApproximation {
val matchFailModels = findAllModelsFor(propToSolvable(matchFails))
val scrutVar = Var(prevBinderTree)
- val counterExamples = matchFailModels.map(modelToCounterExample(scrutVar))
-
- val pruned = CounterExample.prune(counterExamples).map(_.toString).sorted
+ val counterExamples = matchFailModels.flatMap(modelToCounterExample(scrutVar))
+ // sorting before pruning is important here in order to
+ // keep neg/t7020.scala stable
+ // since e.g. List(_, _) would cover List(1, _)
+ val pruned = CounterExample.prune(counterExamples.sortBy(_.toString)).map(_.toString)
if (Statistics.canEnable) Statistics.stopTimer(patmatAnaExhaust, start)
pruned
@@ -616,7 +617,7 @@ trait MatchAnalysis extends MatchApproximation {
// (the variables don't take into account type information derived from other variables,
// so, naively, you might try to construct a counter example like _ :: Nil(_ :: _, _ :: _),
// since we didn't realize the tail of the outer cons was a Nil)
- def modelToCounterExample(scrutVar: Var)(model: Model): CounterExample = {
+ def modelToCounterExample(scrutVar: Var)(model: Model): Option[CounterExample] = {
// x1 = ...
// x1.hd = ...
// x1.tl = ...
@@ -674,6 +675,7 @@ trait MatchAnalysis extends MatchApproximation {
private val fields: mutable.Map[Symbol, VariableAssignment] = mutable.HashMap.empty
// need to prune since the model now incorporates all super types of a constant (needed for reachability)
private lazy val uniqueEqualTo = equalTo filterNot (subsumed => equalTo.exists(better => (better ne subsumed) && instanceOfTpImplies(better.tp, subsumed.tp)))
+ private lazy val inSameDomain = uniqueEqualTo forall (const => variable.domainSyms.exists(_.exists(_.const.tp =:= const.tp)))
private lazy val prunedEqualTo = uniqueEqualTo filterNot (subsumed => variable.staticTpCheckable <:< subsumed.tp)
private lazy val ctor = (prunedEqualTo match { case List(TypeConst(tp)) => tp case _ => variable.staticTpCheckable }).typeSymbol.primaryConstructor
private lazy val ctorParams = if (ctor.paramss.isEmpty) Nil else ctor.paramss.head
@@ -694,13 +696,13 @@ trait MatchAnalysis extends MatchApproximation {
// NoExample if the constructor call is ill-typed
// (thus statically impossible -- can we incorporate this into the formula?)
// beBrief is used to suppress negative information nested in tuples -- it tends to get too noisy
- def toCounterExample(beBrief: Boolean = false): CounterExample =
- if (!allFieldAssignmentsLegal) NoExample
+ def toCounterExample(beBrief: Boolean = false): Option[CounterExample] =
+ if (!allFieldAssignmentsLegal) Some(NoExample)
else {
debug.patmat("describing "+ ((variable, equalTo, notEqualTo, fields, cls, allFieldAssignmentsLegal)))
val res = prunedEqualTo match {
// a definite assignment to a value
- case List(eq: ValueConst) if fields.isEmpty => ValueExample(eq)
+ case List(eq: ValueConst) if fields.isEmpty => Some(ValueExample(eq))
// constructor call
// or we did not gather any information about equality but we have information about the fields
@@ -713,30 +715,50 @@ trait MatchAnalysis extends MatchApproximation {
// figure out the constructor arguments from the field assignment
val argLen = (caseFieldAccs.length min ctorParams.length)
- (0 until argLen).map(i => fields.get(caseFieldAccs(i)).map(_.toCounterExample(brevity)) getOrElse WildcardExample).toList
+ val examples = (0 until argLen).map(i => fields.get(caseFieldAccs(i)).map(_.toCounterExample(brevity)) getOrElse Some(WildcardExample)).toList
+ sequence(examples)
}
cls match {
- case ConsClass => ListExample(args())
- case _ if isTupleSymbol(cls) => TupleExample(args(brevity = true))
- case _ => ConstructorExample(cls, args())
+ case ConsClass =>
+ args().map {
+ case List(NoExample, l: ListExample) =>
+ // special case for neg/t7020.scala:
+ // if we find a counter example `??::*` we report `*::*` instead
+ // since the `??` originates from uniqueEqualTo containing several instanced of the same type
+ List(WildcardExample, l)
+ case args => args
+ }.map(ListExample)
+ case _ if isTupleSymbol(cls) => args(brevity = true).map(TupleExample)
+ case _ if cls.isSealed && cls.isAbstractClass =>
+ // don't report sealed abstract classes, since
+ // 1) they can't be instantiated
+ // 2) we are already reporting any missing subclass (since we know the full domain)
+ // (see patmatexhaust.scala)
+ None
+ case _ => args().map(ConstructorExample(cls, _))
}
// a definite assignment to a type
- case List(eq) if fields.isEmpty => TypeExample(eq)
+ case List(eq) if fields.isEmpty => Some(TypeExample(eq))
// negative information
case Nil if nonTrivialNonEqualTo.nonEmpty =>
// negation tends to get pretty verbose
- if (beBrief) WildcardExample
+ if (beBrief) Some(WildcardExample)
else {
val eqTo = equalTo.headOption getOrElse TypeConst(variable.staticTpCheckable)
- NegativeExample(eqTo, nonTrivialNonEqualTo)
+ Some(NegativeExample(eqTo, nonTrivialNonEqualTo))
}
+ // if uniqueEqualTo contains more than one symbol of the same domain
+ // then we can safely ignore these counter examples since we will eventually encounter
+ // both counter examples separately
+ case _ if inSameDomain => None
+
// not a valid counter-example, possibly since we have a definite type but there was a field mismatch
// TODO: improve reasoning -- in the mean time, a false negative is better than an annoying false positive
- case _ => NoExample
+ case _ => Some(NoExample)
}
debug.patmatResult("described as")(res)
}
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
index 1974befb45..3abec521df 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchTreeMaking.scala
@@ -550,8 +550,24 @@ trait MatchTreeMaking extends MatchCodeGen with Debugging {
case _ => false
}
val suppression = Suppression(suppressExhaustive, supressUnreachable)
+ val hasSwitchAnnotation = treeInfo.isSwitchAnnotation(tpt.tpe)
// matches with two or fewer cases need not apply for switchiness (if-then-else will do)
- val requireSwitch = treeInfo.isSwitchAnnotation(tpt.tpe) && casesNoSubstOnly.lengthCompare(2) > 0
+ // `case 1 | 2` is considered as two cases.
+ def exceedsTwoCasesOrAlts = {
+ // avoids traversing the entire list if there are more than 3 elements
+ def lengthMax3[T](l: List[T]): Int = l match {
+ case a :: b :: c :: _ => 3
+ case cases =>
+ cases.map({
+ case AlternativesTreeMaker(_, alts, _) :: _ => lengthMax3(alts)
+ case c => 1
+ }).sum
+ }
+ lengthMax3(casesNoSubstOnly) > 2
+ }
+ val requireSwitch = hasSwitchAnnotation && exceedsTwoCasesOrAlts
+ if (hasSwitchAnnotation && !requireSwitch)
+ reporter.warning(scrut.pos, "matches with two cases or fewer are emitted using if-then-else instead of switch")
(suppression, requireSwitch)
case _ =>
(Suppression.NoSuppression, false)
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala
index 31b1ffa912..4330781013 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/Solving.scala
@@ -26,16 +26,9 @@ trait Solving extends Logic {
type Formula = FormulaBuilder
def formula(c: Clause*): Formula = ArrayBuffer(c: _*)
- type Clause = collection.Set[Lit]
+ type Clause = Set[Lit]
// a clause is a disjunction of distinct literals
- def clause(l: Lit*): Clause = (
- if (l.lengthCompare(1) <= 0) {
- l.toSet // SI-8531 Avoid LinkedHashSet's bulk for 0 and 1 element clauses
- } else {
- // neg/t7020.scala changes output 1% of the time, the non-determinism is quelled with this linked set
- mutable.LinkedHashSet(l: _*)
- }
- )
+ def clause(l: Lit*): Clause = l.toSet
type Lit
def Lit(sym: Sym, pos: Boolean = true): Lit
@@ -115,7 +108,6 @@ trait Solving extends Logic {
if (Statistics.canEnable) Statistics.stopTimer(patmatCNF, start)
- //
if (Statistics.canEnable) patmatCNFSizes(res.size).value += 1
// debug.patmat("cnf for\n"+ p +"\nis:\n"+cnfString(res))
@@ -141,36 +133,83 @@ trait Solving extends Logic {
def cnfString(f: Formula) = alignAcrossRows(f map (_.toList) toList, "\\/", " /\\\n")
// adapted from http://lara.epfl.ch/w/sav10:simple_sat_solver (original by Hossein Hojjat)
- val EmptyModel = collection.immutable.SortedMap.empty[Sym, Boolean]
+ val EmptyModel = Map.empty[Sym, Boolean]
val NoModel: Model = null
// returns all solutions, if any (TODO: better infinite recursion backstop -- detect fixpoint??)
def findAllModelsFor(f: Formula): List[Model] = {
+
+ debug.patmat("find all models for\n"+ cnfString(f))
+
val vars: Set[Sym] = f.flatMap(_ collect {case l: Lit => l.sym}).toSet
// debug.patmat("vars "+ vars)
// the negation of a model -(S1=True/False /\ ... /\ SN=True/False) = clause(S1=False/True, ...., SN=False/True)
def negateModel(m: Model) = clause(m.toSeq.map{ case (sym, pos) => Lit(sym, !pos) } : _*)
- def findAllModels(f: Formula, models: List[Model], recursionDepthAllowed: Int = 10): List[Model]=
- if (recursionDepthAllowed == 0) models
- else {
- debug.patmat("find all models for\n"+ cnfString(f))
+ /**
+ * The DPLL procedure only returns a minimal mapping from literal to value
+ * such that the CNF formula is satisfied.
+ * E.g. for:
+ * `(a \/ b)`
+ * The DPLL procedure will find either {a = true} or {b = true}
+ * as solution.
+ *
+ * The expansion step will amend both solutions with the unassigned variable
+ * i.e., {a = true} will be expanded to {a = true, b = true} and {a = true, b = false}.
+ */
+ def expandUnassigned(unassigned: List[Sym], model: Model): List[Model] = {
+ // the number of solutions is doubled for every unassigned variable
+ val expandedModels = 1 << unassigned.size
+ var current = mutable.ArrayBuffer[Model]()
+ var next = mutable.ArrayBuffer[Model]()
+ current.sizeHint(expandedModels)
+ next.sizeHint(expandedModels)
+
+ current += model
+
+ // we use double buffering:
+ // read from `current` and create a two models for each model in `next`
+ for {
+ s <- unassigned
+ } {
+ for {
+ model <- current
+ } {
+ def force(l: Lit) = model + (l.sym -> l.pos)
+
+ next += force(Lit(s, pos = true))
+ next += force(Lit(s, pos = false))
+ }
+
+ val tmp = current
+ current = next
+ next = tmp
+
+ next.clear()
+ }
+
+ current.toList
+ }
+
+ def findAllModels(f: Formula,
+ models: List[Model],
+ recursionDepthAllowed: Int = global.settings.YpatmatExhaustdepth.value): List[Model]=
+ if (recursionDepthAllowed == 0) {
+ val maxDPLLdepth = global.settings.YpatmatExhaustdepth.value
+ reportWarning("(Exhaustivity analysis reached max recursion depth, not all missing cases are reported. " +
+ s"Please try with scalac -Ypatmat-exhaust-depth ${maxDPLLdepth * 2} or -Ypatmat-exhaust-depth off.)")
+ models
+ } else {
val model = findModelFor(f)
// if we found a solution, conjunct the formula with the model's negation and recurse
if (model ne NoModel) {
val unassigned = (vars -- model.keySet).toList
debug.patmat("unassigned "+ unassigned +" in "+ model)
- def force(lit: Lit) = {
- val model = withLit(findModelFor(dropUnit(f, lit)), lit)
- if (model ne NoModel) List(model)
- else Nil
- }
- val forced = unassigned flatMap { s =>
- force(Lit(s, pos = true)) ++ force(Lit(s, pos = false))
- }
+
+ val forced = expandUnassigned(unassigned, model)
debug.patmat("forced "+ forced)
val negated = negateModel(model)
- findAllModels(f :+ negated, model :: (forced ++ models), recursionDepthAllowed - 1)
+ findAllModels(f :+ negated, forced ++ models, recursionDepthAllowed - 1)
}
else models
}
@@ -210,15 +249,14 @@ trait Solving extends Logic {
withLit(findModelFor(dropUnit(f, unitLit)), unitLit)
case _ =>
// partition symbols according to whether they appear in positive and/or negative literals
- // SI-7020 Linked- for deterministic counter examples.
- val pos = new mutable.LinkedHashSet[Sym]()
- val neg = new mutable.LinkedHashSet[Sym]()
+ val pos = new mutable.HashSet[Sym]()
+ val neg = new mutable.HashSet[Sym]()
mforeach(f)(lit => if (lit.pos) pos += lit.sym else neg += lit.sym)
// appearing in both positive and negative
- val impures: mutable.LinkedHashSet[Sym] = pos intersect neg
+ val impures = pos intersect neg
// appearing only in either positive/negative positions
- val pures: mutable.LinkedHashSet[Sym] = (pos ++ neg) -- impures
+ val pures = (pos ++ neg) -- impures
if (pures nonEmpty) {
val pureSym = pures.head
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 20e462bbce..866ca37303 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -885,22 +885,31 @@ trait ContextErrors {
val WrongNumber, NoParams, ArgsDoNotConform = Value
}
- private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit =
- if (!(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)) {
- if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
- val methodName = nme.defaultGetterToMethod(sym1.name)
- context.issueAmbiguousError(AmbiguousTypeError(sym1.enclClass.pos,
- "in "+ sym1.enclClass +", multiple overloaded alternatives of " + methodName +
- " define default arguments"))
- } else {
- context.issueAmbiguousError(AmbiguousTypeError(pos,
- ("ambiguous reference to overloaded definition,\n" +
- "both " + sym1 + sym1.locationString + " of type " + pre.memberType(sym1) +
- "\nand " + sym2 + sym2.locationString + " of type " + pre.memberType(sym2) +
- "\nmatch " + rest)
- ))
- }
- }
+ private def issueAmbiguousTypeErrorUnlessErroneous(pos: Position, pre: Type, sym1: Symbol, sym2: Symbol, rest: String): Unit = {
+ // To avoid stack overflows (SI-8890), we MUST (at least) report when either `validTargets` OR `ambiguousSuppressed`
+ // More details:
+ // If `!context.ambiguousErrors`, `reporter.issueAmbiguousError` (which `context.issueAmbiguousError` forwards to)
+ // buffers ambiguous errors. In this case, to avoid looping, we must issue even if `!validTargets`. (TODO: why?)
+ // When not buffering (and thus reporting to the user), we shouldn't issue unless `validTargets`,
+ // otherwise we report two different errors that trace back to the same root cause,
+ // and unless `validTargets`, we don't know for sure the ambiguity is real anyway.
+ val validTargets = !(pre.isErroneous || sym1.isErroneous || sym2.isErroneous)
+ val ambiguousBuffered = !context.ambiguousErrors
+ if (validTargets || ambiguousBuffered)
+ context.issueAmbiguousError(
+ if (sym1.hasDefault && sym2.hasDefault && sym1.enclClass == sym2.enclClass) {
+ val methodName = nme.defaultGetterToMethod(sym1.name)
+ AmbiguousTypeError(sym1.enclClass.pos,
+ s"in ${sym1.enclClass}, multiple overloaded alternatives of $methodName define default arguments")
+
+ } else {
+ AmbiguousTypeError(pos,
+ "ambiguous reference to overloaded definition,\n" +
+ s"both ${sym1.fullLocationString} of type ${pre.memberType(sym1)}\n" +
+ s"and ${sym2.fullLocationString} of type ${pre.memberType(sym2)}\n" +
+ s"match $rest")
+ })
+ }
def AccessError(tree: Tree, sym: Symbol, ctx: Context, explanation: String): AbsTypeError =
AccessError(tree, sym, ctx.enclClass.owner.thisType, ctx.enclClass.owner, explanation)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index c86eaffccf..da0ae4ee79 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -480,6 +480,8 @@ trait Contexts { self: Analyzer =>
// SI-8245 `isLazy` need to skip lazy getters to ensure `return` binds to the right place
c.enclMethod = if (isDefDef && !owner.isLazy) c else enclMethod
+ if (tree != outer.tree) c(TypeConstructorAllowed) = false
+
registerContext(c.asInstanceOf[analyzer.Context])
debuglog("[context] ++ " + c.unit + " / " + tree.summaryString)
c
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index e2ad578252..7ed4fe1f88 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -1475,8 +1475,10 @@ trait Implicits {
})
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(paramTp.typeArgs 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?
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index fdff2f3076..e876d4a6af 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -1643,6 +1643,7 @@ trait Namers extends MethodSynthesis {
def symbolAllowsDeferred = (
sym.isValueParameter
|| sym.isTypeParameterOrSkolem
+ || (sym.isAbstractType && sym.owner.isClass)
|| context.tree.isInstanceOf[ExistentialTypeTree]
)
// Does the symbol owner require no undefined members?
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index fe6038bc00..20db85e665 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1104,7 +1104,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
adaptConstant(value)
case OverloadedType(pre, alts) if !mode.inFunMode => // (1)
inferExprAlternative(tree, pt)
- adapt(tree, mode, pt, original)
+ adaptAfterOverloadResolution(tree, mode, pt, original)
case NullaryMethodType(restpe) => // (2)
adapt(tree setType restpe, mode, pt, original)
case TypeRef(_, ByNameParamClass, arg :: Nil) if mode.inExprMode => // (2)
@@ -1137,6 +1137,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
+ // This just exists to help keep track of the spots where we have to adapt a tree after
+ // overload resolution. These proved hard to find during the fix for SI-8267.
+ def adaptAfterOverloadResolution(tree: Tree, mode: Mode, pt: Type = WildcardType, original: Tree = EmptyTree): Tree = {
+ adapt(tree, mode, pt, original)
+ }
+
def instantiate(tree: Tree, mode: Mode, pt: Type): Tree = {
inferExprInstance(tree, context.extractUndetparams(), pt)
adapt(tree, mode, pt)
@@ -1724,7 +1730,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if ((clazz isNonBottomSubClass ClassfileAnnotationClass) && (clazz != ClassfileAnnotationClass)) {
if (!clazz.owner.isPackageClass)
context.error(clazz.pos, "inner classes cannot be classfile annotations")
- else restrictionWarning(cdef.pos, unit,
+ // Ignore @SerialVersionUID, because it is special-cased and handled completely differently.
+ // It only extends ClassfileAnnotationClass instead of StaticAnnotation to get the enforcement
+ // of constant argument values "for free". Related to SI-7041.
+ else if (clazz != SerialVersionUIDAttr) restrictionWarning(cdef.pos, unit,
"""|subclassing Classfile does not
|make your annotation visible at runtime. If that is what
|you want, you must write the annotation class in Java.""".stripMargin)
@@ -3175,7 +3184,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (sym1 != NoSymbol) sym = sym1
}
if (sym == NoSymbol) fun
- else adapt(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode, WildcardType)
+ else adaptAfterOverloadResolution(fun setSymbol sym setType pre.memberType(sym), mode.forFunMode)
} else fun
}
@@ -3220,7 +3229,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
setError(tree)
else {
inferMethodAlternative(fun, undetparams, argTpes, pt)
- doTypedApply(tree, adapt(fun, mode.forFunMode, WildcardType), args1, mode, pt)
+ doTypedApply(tree, adaptAfterOverloadResolution(fun, mode.forFunMode, WildcardType), args1, mode, pt)
}
}
handleOverloaded
@@ -3803,7 +3812,18 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
protected def typedTypeApply(tree: Tree, mode: Mode, fun: Tree, args: List[Tree]): Tree = fun.tpe match {
case OverloadedType(pre, alts) =>
inferPolyAlternatives(fun, mapList(args)(treeTpe))
- val tparams = fun.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
+
+ // SI-8267 `memberType` can introduce existentials *around* a PolyType/MethodType, see AsSeenFromMap#captureThis.
+ // If we had selected a non-overloaded symbol, `memberType` would have been called in `makeAccessible`
+ // and the resulting existential type would have been skolemized in `adapt` *before* we typechecked
+ // the enclosing type-/ value- application.
+ //
+ // However, if the selection is overloaded, we defer calling `memberType` until we can select a single
+ // alternative here. It is therefore necessary to skolemize the existential here.
+ //
+ val fun1 = adaptAfterOverloadResolution(fun, mode.forFunMode | TAPPmode)
+
+ val tparams = fun1.symbol.typeParams //@M TODO: fun.symbol.info.typeParams ? (as in typedAppliedTypeTree)
val args1 = if (sameLength(args, tparams)) {
//@M: in case TypeApply we can't check the kind-arities of the type arguments,
// as we don't know which alternative to choose... here we do
@@ -3817,7 +3837,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// ...actually this was looping anyway, see bug #278.
return TypedApplyWrongNumberOfTpeParametersError(fun, fun)
- typedTypeApply(tree, mode, fun, args1)
+ typedTypeApply(tree, mode, fun1, args1)
case SingleType(_, _) =>
typedTypeApply(tree, mode, fun setType fun.tpe.widen, args)
case PolyType(tparams, restpe) if tparams.nonEmpty =>