diff options
author | Jason Zaugg <jzaugg@gmail.com> | 2015-01-29 09:59:54 +1000 |
---|---|---|
committer | Jason Zaugg <jzaugg@gmail.com> | 2015-01-29 09:59:54 +1000 |
commit | 1db62995b52b06f5037331172b3f54739d720d62 (patch) | |
tree | 0b684c4adfcb800fa195947b37fdb5dc89731552 /src/compiler/scala | |
parent | 8b5f2b435b4b14089806406c8923f7e845d10ef6 (diff) | |
parent | eb15950e697eb77e52733f81c65e2d51951ad881 (diff) | |
download | scala-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')
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 => |