summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bincompat-forward.whitelist.conf4
-rw-r--r--build.sbt5
-rw-r--r--project/ScalaOptionParser.scala2
-rw-r--r--project/build.properties2
-rw-r--r--scripts/common2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala13
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala4
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala18
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala31
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala9
-rw-r--r--src/library/scala/collection/Iterator.scala23
-rw-r--r--src/library/scala/collection/immutable/StringLike.scala1
-rw-r--r--src/reflect/scala/reflect/internal/StdAttachments.scala6
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala17
-rw-r--r--src/reflect/scala/reflect/internal/TypeDebugging.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala45
-rw-r--r--src/reflect/scala/reflect/internal/settings/MutableSettings.scala1
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala1
-rw-r--r--src/reflect/scala/reflect/runtime/Settings.scala1
-rw-r--r--src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala7
-rw-r--r--test/files/neg/t2712-1.check13
-rw-r--r--test/files/neg/t2712-1.scala8
-rw-r--r--test/files/neg/t2712-2.check13
-rw-r--r--test/files/neg/t2712-2.flags1
-rw-r--r--test/files/neg/t2712-2.scala18
-rw-r--r--test/files/neg/t2712-3.check6
-rw-r--r--test/files/neg/t2712-3.scala18
-rw-r--r--test/files/neg/t2712.flags1
-rw-r--r--test/files/neg/t7046-2.check3
-rw-r--r--test/files/neg/t7046-2/Macros_1.scala15
-rw-r--r--test/files/neg/t7046-2/Test_2.scala14
-rw-r--r--test/files/neg/t7046.check3
-rw-r--r--test/files/neg/t7046/Macros_1.scala15
-rw-r--r--test/files/neg/t7046/Test_2.scala35
-rw-r--r--test/files/pos/hkgadt.scala18
-rw-r--r--test/files/pos/t2712-1.flags1
-rw-r--r--test/files/pos/t2712-1.scala9
-rw-r--r--test/files/pos/t2712-2.flags2
-rw-r--r--test/files/pos/t2712-2.scala25
-rw-r--r--test/files/pos/t2712-3.flags2
-rw-r--r--test/files/pos/t2712-3.scala24
-rw-r--r--test/files/pos/t2712-4.flags2
-rw-r--r--test/files/pos/t2712-4.scala17
-rw-r--r--test/files/pos/t2712-5.flags1
-rw-r--r--test/files/pos/t2712-5.scala29
-rw-r--r--test/files/pos/t2712-6.flags1
-rw-r--r--test/files/pos/t2712-6.scala12
-rw-r--r--test/files/pos/t2712-7.flags1
-rw-r--r--test/files/pos/t2712-7.scala15
-rw-r--r--test/files/pos/t5683.flags1
-rw-r--r--test/files/pos/t5683.scala23
-rw-r--r--test/files/pos/t6895b.flags2
-rw-r--r--test/files/pos/t6895b.scala39
-rw-r--r--test/files/pos/t7046-2/Macros_1.scala14
-rw-r--r--test/files/pos/t7046-2/Test_2.scala9
-rw-r--r--test/files/presentation/t8085.check1
-rw-r--r--test/files/presentation/t8085b.check1
-rw-r--r--test/files/run/inferred-type-constructors-hou.check56
-rw-r--r--test/files/run/inferred-type-constructors-hou.flags1
-rw-r--r--test/files/run/inferred-type-constructors-hou.scala125
-rw-r--r--test/files/run/reflection-mem-typecheck.scala2
-rw-r--r--test/files/run/t7046-1/Macros_1.scala15
-rw-r--r--test/files/run/t7046-1/Test_2.scala23
-rw-r--r--test/files/run/t7046-2/Macros_1.scala15
-rw-r--r--test/files/run/t7046-2/Test_2.scala14
-rw-r--r--test/files/run/t9806.scala18
-rw-r--r--test/files/run/t9841.scala24
-rw-r--r--test/junit/scala/collection/IteratorTest.scala6
-rw-r--r--test/junit/scala/tools/nsc/settings/SettingsTest.scala67
73 files changed, 937 insertions, 51 deletions
diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf
index c6cffee846..411989fd4b 100644
--- a/bincompat-forward.whitelist.conf
+++ b/bincompat-forward.whitelist.conf
@@ -387,6 +387,10 @@ filter {
{
matchName="scala.concurrent.impl.Promise.toString"
problemName=MissingMethodProblem
+ },
+ {
+ matchName="scala.reflect.runtime.Settings.YpartialUnification"
+ problemName=MissingMethodProblem
}
]
}
diff --git a/build.sbt b/build.sbt
index 1ae5edb4d5..c23e5bf24b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -136,6 +136,11 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ publishSettings +
}
},
scalaVersion := (scalaVersion in bootstrap).value,
+ // As of sbt 0.13.12 (sbt/sbt#2634) sbt endeavours to align both scalaOrganization and scalaVersion
+ // in the Scala artefacts, for example scala-library and scala-compiler.
+ // This doesn't work in the scala/scala build because the version of scala-library and the scalaVersion of
+ // scala-library are correct to be different. So disable overriding.
+ ivyScala ~= (_ map (_ copy (overrideScalaVersion = false))),
// we always assume that Java classes are standalone and do not have any dependency
// on Scala classes
compileOrder := CompileOrder.JavaThenScala,
diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala
index da8a3bf460..b907045cb4 100644
--- a/project/ScalaOptionParser.scala
+++ b/project/ScalaOptionParser.scala
@@ -86,7 +86,7 @@ object ScalaOptionParser {
"-Yeta-expand-keeps-star", "-Yide-debug", "-Yinfer-argument-types", "-Yinfer-by-name", "-Yinfer-debug", "-Yinline", "-Yinline-handlers",
"-Yinline-warnings", "-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
"-Yno-completion", "-Yno-generic-signatures", "-Yno-imports", "-Yno-load-impl-class", "-Yno-predef", "-Ynooptimise",
- "-Yoverride-objects", "-Yoverride-vars", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypos-debug", "-Ypresentation-debug",
+ "-Yoverride-objects", "-Yoverride-vars", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypartial-unification", "-Ypos-debug", "-Ypresentation-debug",
"-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based",
"-Yrepl-sync", "-Yshow-member-pos", "-Yshow-symkinds", "-Yshow-symowners", "-Yshow-syms", "-Yshow-trees", "-Yshow-trees-compact", "-Yshow-trees-stringified", "-Ytyper-debug",
"-Ywarn-adapted-args", "-Ywarn-dead-code", "-Ywarn-inaccessible", "-Ywarn-infer-any", "-Ywarn-nullary-override", "-Ywarn-nullary-unit", "-Ywarn-numeric-widen", "-Ywarn-unused", "-Ywarn-unused-import", "-Ywarn-value-discard",
diff --git a/project/build.properties b/project/build.properties
index 43b8278c68..35c88bab7d 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.11
+sbt.version=0.13.12
diff --git a/scripts/common b/scripts/common
index bfddf3d149..f2202b9165 100644
--- a/scripts/common
+++ b/scripts/common
@@ -19,7 +19,7 @@ mkdir -p $IVY_CACHE
rm -rf $IVY_CACHE/cache/org.scala-lang
SBT_CMD=${sbtCmd-sbt}
-SBT_CMD="$SBT_CMD -sbt-version 0.13.11"
+SBT_CMD="$SBT_CMD -sbt-version 0.13.12"
# temp dir where all 'non-build' operation are performed
TMP_ROOT_DIR=$(mktemp -d -t pr-scala.XXXX)
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index a618b080c8..8d72fd76bd 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -214,6 +214,19 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
}
}
+ private var propCnt = 0
+ @inline final def withPropagateCyclicReferences[T](t: => T): T = {
+ try {
+ propCnt = propCnt+1
+ t
+ } finally {
+ propCnt = propCnt-1
+ assert(propCnt >= 0)
+ }
+ }
+
+ def propagateCyclicReferences: Boolean = propCnt > 0
+
/** Representing ASTs as graphs */
object treeBrowsers extends {
val global: Global.this.type = Global.this
diff --git a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala
index a7ce7dfa04..eafaf41932 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/ConstantOptimization.scala
@@ -539,14 +539,14 @@ abstract class ConstantOptimization extends SubComponent {
// number of instructions excluding the last one
val normalCount = block.size - 1
- val exceptionState = in.cleanStack
+ var exceptionState = in.cleanStack
var normalExitState = in
var idx = 0
while (idx < normalCount) {
val inst = block(idx)
normalExitState = interpretInst(normalExitState, inst)
if (normalExitState.locals ne exceptionState.locals)
- exceptionState.copy(locals = exceptionState mergeLocals normalExitState.locals)
+ exceptionState = exceptionState.copy(locals = exceptionState mergeLocals normalExitState.locals)
idx += 1
}
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index b4987e1240..11cde935f2 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -744,11 +744,19 @@ class MutableSettings(val errorFn: String => Unit)
def isHelping: Boolean = sawHelp
def help: String = {
- val choiceLength = choices.map(_.length).max + 1
- val formatStr = s" %-${choiceLength}s %s"
- choices.zipAll(descriptions, "", "").map {
- case (arg, descr) => formatStr.format(arg, descr)
- } mkString (f"$descr%n", f"%n", "")
+ val describe: ((String, String)) => String = {
+ val choiceWidth = choices.map(_.length).max + 1
+ val formatStr = s" %-${choiceWidth}s %s"
+ locally {
+ case (choice, description) => formatStr.format(choice, description)
+ }
+ }
+ val verboseDefault = default match {
+ case Some("_" :: Nil) => Some("All choices are enabled by default." :: Nil)
+ case _ => default
+ }
+ val orelse = verboseDefault.map(_.mkString(f"%nDefault: ", ", ", f"%n")).getOrElse("")
+ choices.zipAll(descriptions, "", "").map(describe).mkString(f"${descr}%n", f"%n", orelse)
}
def clear(): Unit = {
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index 1817cfa25a..8e5c064e1f 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -218,6 +218,7 @@ trait ScalaSettings extends AbsScalaSettings
val inferByName = BooleanSetting ("-Yinfer-by-name", "Allow inference of by-name types. This is a temporary option to ease transition. See SI-7899.").withDeprecationMessage(removalIn212)
val YclasspathImpl = ChoiceSetting ("-YclasspathImpl", "implementation", "Choose classpath scanning method.", List(ClassPathRepresentationType.Recursive, ClassPathRepresentationType.Flat), ClassPathRepresentationType.Recursive)
val YdisableFlatCpCaching = BooleanSetting ("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
+ val YpartialUnification = BooleanSetting ("-Ypartial-unification", "Enable partial unification in type constructor inference")
val YvirtClasses = false // too embryonic to even expose as a -Y //BooleanSetting ("-Yvirtual-classes", "Support virtual classes")
val YdisableUnreachablePrevention = BooleanSetting("-Ydisable-unreachable-prevention", "Disable the prevention of unreachable blocks in code generation.")
diff --git a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala
index c2d0f5ccec..d3c7ba4d76 100644
--- a/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala
+++ b/src/compiler/scala/tools/nsc/symtab/BrowsingLoaders.scala
@@ -87,16 +87,16 @@ abstract class BrowsingLoaders extends GlobalSymbolLoaders {
if (packagePrefix == root.fullName) {
enterClass(root, name.toString, new SourcefileLoader(src))
entered += 1
- } else println("prefixes differ: "+packagePrefix+","+root.fullName)
+ } else log("prefixes differ: "+packagePrefix+","+root.fullName)
case ModuleDef(_, name, _) =>
if (packagePrefix == root.fullName) {
val module = enterModule(root, name.toString, new SourcefileLoader(src))
entered += 1
if (name == nme.PACKAGEkw) {
- println("open package module: "+module)
+ log("open package module: "+module)
openPackageModule(module, root)
}
- } else println("prefixes differ: "+packagePrefix+","+root.fullName)
+ } else log("prefixes differ: "+packagePrefix+","+root.fullName)
case _ =>
}
}
@@ -121,7 +121,7 @@ abstract class BrowsingLoaders extends GlobalSymbolLoaders {
browseTopLevel(root, src)
} catch {
case ex: syntaxAnalyzer.MalformedInput =>
- println("[%s] caught malformed input exception at offset %d: %s".format(src, ex.offset, ex.msg))
+ log(s"[$src] caught malformed input exception at offset ${ex.offset}: ${ex.msg}")
super.enterToplevelsFromSource(root, name, src)
}
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
index 727f09290a..80cccaf2ae 100644
--- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -614,9 +614,6 @@ trait ContextErrors {
def ParentFinalInheritanceError(parent: Tree, mixin: Symbol) =
NormalTypeError(parent, "illegal inheritance from final "+mixin)
- def ParentSealedInheritanceError(parent: Tree, psym: Symbol) =
- NormalTypeError(parent, "illegal inheritance from sealed " + psym )
-
def ParentSelfTypeConformanceError(parent: Tree, selfType: Type) =
NormalTypeError(parent,
"illegal inheritance;\n self-type "+selfType+" does not conform to "+
@@ -1135,6 +1132,9 @@ trait ContextErrors {
def MissingParameterOrValTypeError(vparam: Tree) =
issueNormalTypeError(vparam, "missing parameter type")
+ def ParentSealedInheritanceError(parent: Tree, psym: Symbol) =
+ NormalTypeError(parent, "illegal inheritance from sealed " + psym )
+
def RootImportError(tree: Tree) =
issueNormalTypeError(tree, "_root_ cannot be imported")
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 9f7bdf7aff..c188c326c3 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1254,7 +1254,6 @@ trait Infer extends Checkable {
def isFreeTypeParamOfTerm(sym: Symbol) = (
sym.isAbstractType
&& sym.owner.isTerm
- && !sym.info.bounds.exists(_.typeParams.nonEmpty)
)
// Intentionally *not* using `Type#typeSymbol` here, which would normalize `tp`
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 4ad81b60ae..ee64a6646f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -110,7 +110,7 @@ trait Namers extends MethodSynthesis {
protected def owner = context.owner
def contextFile = context.unit.source.file
def typeErrorHandler[T](tree: Tree, alt: T): PartialFunction[Throwable, T] = {
- case ex: TypeError =>
+ case ex: TypeError if !global.propagateCyclicReferences =>
// H@ need to ensure that we handle only cyclic references
TypeSigError(tree, ex)
alt
@@ -912,12 +912,33 @@ trait Namers extends MethodSynthesis {
private def templateSig(templ: Template): Type = {
val clazz = context.owner
+
+ val parentTrees = typer.typedParentTypes(templ)
+
+ val pending = mutable.ListBuffer[AbsTypeError]()
+ parentTrees foreach { tpt =>
+ val ptpe = tpt.tpe
+ if(!ptpe.isError) {
+ val psym = ptpe.typeSymbol
+ val sameSourceFile = context.unit.source.file == psym.sourceFile
+
+ if (psym.isSealed && !phase.erasedTypes)
+ if (sameSourceFile)
+ psym addChild context.owner
+ else
+ pending += ParentSealedInheritanceError(tpt, psym)
+ if (psym.isLocalToBlock && !phase.erasedTypes)
+ psym addChild context.owner
+ }
+ }
+ pending.foreach(ErrorUtils.issueTypeError)
+
def checkParent(tpt: Tree): Type = {
if (tpt.tpe.isError) AnyRefTpe
else tpt.tpe
}
- val parents = typer.typedParentTypes(templ) map checkParent
+ val parents = parentTrees map checkParent
enterSelf(templ.self)
@@ -1678,6 +1699,12 @@ trait Namers extends MethodSynthesis {
abstract class TypeCompleter extends LazyType {
val tree: Tree
+ override def forceDirectSuperclasses: Unit = {
+ tree.foreach {
+ case dt: DefTree => global.withPropagateCyclicReferences(Option(dt.symbol).map(_.maybeInitialize))
+ case _ =>
+ }
+ }
}
def mkTypeCompleter(t: Tree)(c: Symbol => Unit) = new LockingTypeCompleter with FlagAgnosticCompleter {
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 6b73a538df..508d205424 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1643,7 +1643,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
supertpts mapConserve (tpt => checkNoEscaping.privates(context.owner, tpt))
}
catch {
- case ex: TypeError =>
+ case ex: TypeError if !global.propagateCyclicReferences =>
// fallback in case of cyclic errors
// @H none of the tests enter here but I couldn't rule it out
// upd. @E when a definition inherits itself, we end up here
@@ -1702,11 +1702,6 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
context.deprecationWarning(parent.pos, psym, msg)
}
- if (psym.isSealed && !phase.erasedTypes)
- if (sameSourceFile)
- psym addChild context.owner
- else
- pending += ParentSealedInheritanceError(parent, psym)
val parentTypeOfThis = parent.tpe.dealias.typeOfThis
if (!(selfType <:< parentTypeOfThis) &&
@@ -5421,6 +5416,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
try runTyper() catch {
+ case ex: CyclicReference if global.propagateCyclicReferences =>
+ throw ex
case ex: TypeError =>
tree.clearType()
// The only problematic case are (recoverable) cyclic reference errors which can pop up almost anywhere.
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala
index 9ba16976bd..03b9fbff26 100644
--- a/src/library/scala/collection/Iterator.scala
+++ b/src/library/scala/collection/Iterator.scala
@@ -648,15 +648,15 @@ trait Iterator[+A] extends TraversableOnce[A] {
* handling of structural calls. It's not what's intended here.
*/
class Leading extends AbstractIterator[A] {
- var lookahead: mutable.Queue[A] = null
- var hd: A = _
+ private[this] var lookahead: mutable.Queue[A] = null
+ private[this] var hd: A = _
/* Status is kept with magic numbers
* 1 means next element is in hd and we're still reading into this iterator
* 0 means we're still reading but haven't found a next element
* -1 means we are done reading into the iterator, so we must rely on lookahead
* -2 means we are done but have saved hd for the other iterator to use as its first element
*/
- var status = 0
+ private[this] var status = 0
private def store(a: A) {
if (lookahead == null) lookahead = new mutable.Queue[A]
lookahead += a
@@ -680,26 +680,23 @@ trait Iterator[+A] extends TraversableOnce[A] {
}
else empty.next()
}
- def finish(): Boolean = {
- if (status == -1) false
- else if (status == -2) {
+ def finish(): Boolean = status match {
+ case -2 => status = -1 ; true
+ case -1 => false
+ case 1 => store(hd) ; status = 0 ; finish()
+ case 0 =>
status = -1
- true
- }
- else {
- if (status == 1) store(hd)
while (self.hasNext) {
val a = self.next()
if (p(a)) store(a)
else {
hd = a
- status = -1
return true
}
}
false
- }
}
+ def trailer: A = hd
}
val leading = new Leading
@@ -732,7 +729,7 @@ trait Iterator[+A] extends TraversableOnce[A] {
if (status > 0) self.next()
else {
status = 1
- val ans = myLeading.hd
+ val ans = myLeading.trailer
myLeading = null
ans
}
diff --git a/src/library/scala/collection/immutable/StringLike.scala b/src/library/scala/collection/immutable/StringLike.scala
index 1b52e40b72..232d67df4f 100644
--- a/src/library/scala/collection/immutable/StringLike.scala
+++ b/src/library/scala/collection/immutable/StringLike.scala
@@ -137,6 +137,7 @@ self =>
/** Returns this string with first character converted to upper case.
* If the first character of the string is capitalized, it is returned unchanged.
+ * This method does not convert characters outside the Basic Multilingual Plane (BMP).
*/
def capitalize: String =
if (toString == null) null
diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala
index cca33253be..3df31b538c 100644
--- a/src/reflect/scala/reflect/internal/StdAttachments.scala
+++ b/src/reflect/scala/reflect/internal/StdAttachments.scala
@@ -52,4 +52,10 @@ trait StdAttachments {
/** Untyped list of subpatterns attached to selector dummy. */
case class SubpatternsAttachment(patterns: List[Tree])
+
+ /** Attached to a class symbol to indicate that its children have been observed
+ * via knownDirectSubclasses. Children added subsequently will trigger an
+ * error to indicate that the earlier observation was incomplete.
+ */
+ case object KnownDirectSubclassesCalled extends PlainAttachment
}
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 3b9ee9048a..6116952c70 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -110,6 +110,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def knownDirectSubclasses = {
// See `getFlag` to learn more about the `isThreadsafe` call in the body of this method.
if (!isCompilerUniverse && !isThreadsafe(purpose = AllOps)) initialize
+
+ enclosingPackage.info.decls.foreach { sym =>
+ if(sourceFile == sym.sourceFile) {
+ sym.rawInfo.forceDirectSuperclasses
+ }
+ }
+
+ if(!isPastTyper)
+ updateAttachment(KnownDirectSubclassesCalled)
+
children
}
@@ -3351,7 +3361,12 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
private[this] var childSet: Set[Symbol] = Set()
override def children = childSet
- override def addChild(sym: Symbol) { childSet = childSet + sym }
+ override def addChild(sym: Symbol) {
+ if(!isPastTyper && hasAttachment[KnownDirectSubclassesCalled.type] && !childSet.contains(sym))
+ globalError(s"knownDirectSubclasses of ${this.name} observed before subclass ${sym.name} registered")
+
+ childSet = childSet + sym
+ }
def anonOrRefinementString = {
if (hasCompleteInfo) {
diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala
index 63f897cd32..4a5128feeb 100644
--- a/src/reflect/scala/reflect/internal/TypeDebugging.scala
+++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala
@@ -110,7 +110,7 @@ trait TypeDebugging {
val hi_s = if (noPrint(hi)) "" else " <: " + ptTree(hi)
lo_s + hi_s
case _ if (t.symbol eq null) || (t.symbol eq NoSymbol) => to_s(t)
- case _ => "" + t.symbol.tpe
+ case _ => if (t.symbol.hasCompleteInfo) "" + t.symbol.tpe else "<?>"
}
def ptTypeParam(td: TypeDef): String = {
val TypeDef(_, name, tparams, rhs) = td
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 9697e16da7..92df4ba3af 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -310,6 +310,11 @@ trait Types
/** If this is a lazy type, assign a new type to `sym`. */
def complete(sym: Symbol) {}
+ /** If this is a lazy type corresponding to a subclass add it to its
+ * parents children
+ */
+ def forceDirectSuperclasses: Unit = ()
+
/** The term symbol associated with the type
* Note that the symbol of the normalized type is returned (@see normalize)
*/
@@ -3076,13 +3081,43 @@ trait Types
*/
def unifyFull(tpe: Type): Boolean = {
def unifySpecific(tp: Type) = {
- sameLength(typeArgs, tp.typeArgs) && {
- val lhs = if (isLowerBound) tp.typeArgs else typeArgs
- val rhs = if (isLowerBound) typeArgs else tp.typeArgs
+ val tpTypeArgs = tp.typeArgs
+ val arityDelta = compareLengths(typeArgs, tpTypeArgs)
+ if (arityDelta == 0) {
+ val lhs = if (isLowerBound) tpTypeArgs else typeArgs
+ val rhs = if (isLowerBound) typeArgs else tpTypeArgs
// This is a higher-kinded type var with same arity as tp.
// If so (see SI-7517), side effect: adds the type constructor itself as a bound.
- isSubArgs(lhs, rhs, params, AnyDepth) && { addBound(tp.typeConstructor); true }
- }
+ isSubArgs(lhs, rhs, params, AnyDepth) && {addBound(tp.typeConstructor); true}
+ } else if (settings.YpartialUnification && arityDelta < 0 && typeArgs.nonEmpty) {
+ // Simple algorithm as suggested by Paul Chiusano in the comments on SI-2712
+ //
+ // https://issues.scala-lang.org/browse/SI-2712?focusedCommentId=61270
+ //
+ // Treat the type constructor as curried and partially applied, we treat a prefix
+ // as constants and solve for the suffix. For the example in the ticket, unifying
+ // M[A] with Int => Int this unifies as,
+ //
+ // M[t] = [t][Int => t] --> abstract on the right to match the expected arity
+ // A = Int --> capture the remainder on the left
+ //
+ // A more "natural" unifier might be M[t] = [t][t => t]. There's lots of scope for
+ // experimenting with alternatives here.
+ val numCaptured = tpTypeArgs.length - typeArgs.length
+ val (captured, abstractedArgs) = tpTypeArgs.splitAt(numCaptured)
+
+ val (lhs, rhs) =
+ if (isLowerBound) (abstractedArgs, typeArgs)
+ else (typeArgs, abstractedArgs)
+
+ isSubArgs(lhs, rhs, params, AnyDepth) && {
+ val tpSym = tp.typeSymbolDirect
+ val abstractedTypeParams = tpSym.typeParams.drop(numCaptured).map(_.cloneSymbol(tpSym))
+
+ addBound(PolyType(abstractedTypeParams, appliedType(tp.typeConstructor, captured ++ abstractedTypeParams.map(_.tpeHK))))
+ true
+ }
+ } else false
}
// The type with which we can successfully unify can be hidden
// behind singleton types and type aliases.
diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
index 38893d8db3..3de720da11 100644
--- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
+++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala
@@ -53,6 +53,7 @@ abstract class MutableSettings extends AbsSettings {
def printtypes: BooleanSetting
def uniqid: BooleanSetting
def verbose: BooleanSetting
+ def YpartialUnification: BooleanSetting
def Yrecursion: IntSetting
def maxClassfileName: IntSetting
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index 7725e4a2f0..8481cd8996 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -41,6 +41,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
this.ForAttachment
this.SyntheticUnitAttachment
this.SubpatternsAttachment
+ this.KnownDirectSubclassesCalled
this.noPrint
this.typeDebug
this.Range
diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala
index 27d574b1de..1081218a70 100644
--- a/src/reflect/scala/reflect/runtime/Settings.scala
+++ b/src/reflect/scala/reflect/runtime/Settings.scala
@@ -47,6 +47,7 @@ private[reflect] class Settings extends MutableSettings {
val printtypes = new BooleanSetting(false)
val uniqid = new BooleanSetting(false)
val verbose = new BooleanSetting(false)
+ val YpartialUnification = new BooleanSetting(false)
val Yrecursion = new IntSetting(0)
val maxClassfileName = new IntSetting(255)
diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala
index b5db4c2098..0983f24fbb 100644
--- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala
+++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala
@@ -33,11 +33,14 @@ class InteractiveReader(completer: () => Completion) extends interpreter.Interac
private val consoleReader = {
val reader = new JLineConsoleReader()
- reader setPaginationEnabled interpreter.`package`.isPaged
+ reader setPaginationEnabled interpreter.isPaged
- // ASAP
+ // turn off magic !
reader setExpandEvents false
+ // enable detecting pasted tab char (when next char is immediately available) which is taken raw, not completion
+ reader setCopyPasteDetection true
+
reader setHistory history.asInstanceOf[JHistory]
reader
diff --git a/test/files/neg/t2712-1.check b/test/files/neg/t2712-1.check
new file mode 100644
index 0000000000..61e4b6b149
--- /dev/null
+++ b/test/files/neg/t2712-1.check
@@ -0,0 +1,13 @@
+t2712-1.scala:7: error: no type parameters for method foo: (m: M[A])Unit exist so that it can be applied to arguments (test.Two[Int,String])
+ --- because ---
+argument expression's type is not compatible with formal parameter type;
+ found : test.Two[Int,String]
+ required: ?M[?A]
+ def test(ma: Two[Int, String]) = foo(ma) // should fail with -Ypartial-unification *disabled*
+ ^
+t2712-1.scala:7: error: type mismatch;
+ found : test.Two[Int,String]
+ required: M[A]
+ def test(ma: Two[Int, String]) = foo(ma) // should fail with -Ypartial-unification *disabled*
+ ^
+two errors found
diff --git a/test/files/neg/t2712-1.scala b/test/files/neg/t2712-1.scala
new file mode 100644
index 0000000000..f7967d71b6
--- /dev/null
+++ b/test/files/neg/t2712-1.scala
@@ -0,0 +1,8 @@
+package test
+
+trait Two[A, B]
+
+object Test {
+ def foo[M[_], A](m: M[A]) = ()
+ def test(ma: Two[Int, String]) = foo(ma) // should fail with -Ypartial-unification *disabled*
+}
diff --git a/test/files/neg/t2712-2.check b/test/files/neg/t2712-2.check
new file mode 100644
index 0000000000..ea19e33e2c
--- /dev/null
+++ b/test/files/neg/t2712-2.check
@@ -0,0 +1,13 @@
+t2712-2.scala:16: error: type mismatch;
+ found : test.Foo
+ required: test.Two[test.X1,Object]
+Note: test.X2 <: Object (and test.Foo <: test.Two[test.X1,test.X2]), but trait Two is invariant in type B.
+You may wish to define B as +B instead. (SLS 4.5)
+ test1(foo): One[X3] // fails with -Ypartial-unification enabled
+ ^
+t2712-2.scala:16: error: type mismatch;
+ found : test.Two[test.X1,Object]
+ required: test.One[test.X3]
+ test1(foo): One[X3] // fails with -Ypartial-unification enabled
+ ^
+two errors found
diff --git a/test/files/neg/t2712-2.flags b/test/files/neg/t2712-2.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/neg/t2712-2.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/neg/t2712-2.scala b/test/files/neg/t2712-2.scala
new file mode 100644
index 0000000000..85ed523489
--- /dev/null
+++ b/test/files/neg/t2712-2.scala
@@ -0,0 +1,18 @@
+package test
+
+class X1
+class X2
+class X3
+
+trait One[A]
+trait Two[A, B]
+
+class Foo extends Two[X1, X2] with One[X3]
+object Test {
+ def test1[M[_], A](x: M[A]): M[A] = x
+
+ val foo = new Foo
+
+ test1(foo): One[X3] // fails with -Ypartial-unification enabled
+ test1(foo): Two[X1, X2] // fails without -Ypartial-unification
+}
diff --git a/test/files/neg/t2712-3.check b/test/files/neg/t2712-3.check
new file mode 100644
index 0000000000..a84d96bf09
--- /dev/null
+++ b/test/files/neg/t2712-3.check
@@ -0,0 +1,6 @@
+t2712-3.scala:17: error: type mismatch;
+ found : test.One[test.X3]
+ required: test.Two[test.X1,test.X2]
+ test1(foo): Two[X1, X2] // fails without -Ypartial-unification
+ ^
+one error found
diff --git a/test/files/neg/t2712-3.scala b/test/files/neg/t2712-3.scala
new file mode 100644
index 0000000000..85ed523489
--- /dev/null
+++ b/test/files/neg/t2712-3.scala
@@ -0,0 +1,18 @@
+package test
+
+class X1
+class X2
+class X3
+
+trait One[A]
+trait Two[A, B]
+
+class Foo extends Two[X1, X2] with One[X3]
+object Test {
+ def test1[M[_], A](x: M[A]): M[A] = x
+
+ val foo = new Foo
+
+ test1(foo): One[X3] // fails with -Ypartial-unification enabled
+ test1(foo): Two[X1, X2] // fails without -Ypartial-unification
+}
diff --git a/test/files/neg/t2712.flags b/test/files/neg/t2712.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/neg/t2712.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/neg/t7046-2.check b/test/files/neg/t7046-2.check
new file mode 100644
index 0000000000..b4efd8b5e9
--- /dev/null
+++ b/test/files/neg/t7046-2.check
@@ -0,0 +1,3 @@
+error: knownDirectSubclasses of Foo observed before subclass Bar registered
+error: knownDirectSubclasses of Foo observed before subclass Baz registered
+two errors found
diff --git a/test/files/neg/t7046-2/Macros_1.scala b/test/files/neg/t7046-2/Macros_1.scala
new file mode 100644
index 0000000000..2a5bf82f62
--- /dev/null
+++ b/test/files/neg/t7046-2/Macros_1.scala
@@ -0,0 +1,15 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+
+object Macros {
+ def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = {
+ import c.universe._;
+ val ttpe = ttag.tpe
+ val tsym = ttpe.typeSymbol.asClass
+ val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString)
+
+ c.Expr[List[String]](q"$subclasses")
+ }
+
+ def knownDirectSubclasses[T]: List[String] = macro impl[T]
+}
diff --git a/test/files/neg/t7046-2/Test_2.scala b/test/files/neg/t7046-2/Test_2.scala
new file mode 100644
index 0000000000..18a2ebcbc2
--- /dev/null
+++ b/test/files/neg/t7046-2/Test_2.scala
@@ -0,0 +1,14 @@
+object Test extends App {
+ def nested: Unit = {
+ val subs = Macros.knownDirectSubclasses[Foo]
+ assert(subs == List("Bar", "Baz"))
+
+ sealed trait Foo
+ object Foo {
+ trait Bar extends Foo
+ trait Baz extends Foo
+ }
+ }
+
+ nested
+}
diff --git a/test/files/neg/t7046.check b/test/files/neg/t7046.check
new file mode 100644
index 0000000000..689520a0aa
--- /dev/null
+++ b/test/files/neg/t7046.check
@@ -0,0 +1,3 @@
+error: knownDirectSubclasses of Foo observed before subclass Local registered
+error: knownDirectSubclasses of Foo observed before subclass Riddle registered
+two errors found
diff --git a/test/files/neg/t7046/Macros_1.scala b/test/files/neg/t7046/Macros_1.scala
new file mode 100644
index 0000000000..2a5bf82f62
--- /dev/null
+++ b/test/files/neg/t7046/Macros_1.scala
@@ -0,0 +1,15 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+
+object Macros {
+ def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = {
+ import c.universe._;
+ val ttpe = ttag.tpe
+ val tsym = ttpe.typeSymbol.asClass
+ val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString)
+
+ c.Expr[List[String]](q"$subclasses")
+ }
+
+ def knownDirectSubclasses[T]: List[String] = macro impl[T]
+}
diff --git a/test/files/neg/t7046/Test_2.scala b/test/files/neg/t7046/Test_2.scala
new file mode 100644
index 0000000000..fcb3e46a0f
--- /dev/null
+++ b/test/files/neg/t7046/Test_2.scala
@@ -0,0 +1,35 @@
+object Test extends App {
+ val subs = Macros.knownDirectSubclasses[Foo]
+ assert(subs == List("Wibble", "Wobble", "Bar", "Baz"))
+}
+
+sealed trait Foo
+object Foo {
+ trait Wibble extends Foo
+ case object Wobble extends Foo
+}
+
+trait Bar extends Foo
+
+object Blah {
+ type Quux = Foo
+}
+
+import Blah._
+
+trait Baz extends Quux
+
+class Boz[T](t: T)
+class Unrelated extends Boz(Test.subs)
+
+object Enigma {
+ locally {
+ // local class not seen
+ class Local extends Foo
+ }
+
+ def foo: Unit = {
+ // local class not seen
+ class Riddle extends Foo
+ }
+}
diff --git a/test/files/pos/hkgadt.scala b/test/files/pos/hkgadt.scala
new file mode 100644
index 0000000000..efd7d3df21
--- /dev/null
+++ b/test/files/pos/hkgadt.scala
@@ -0,0 +1,18 @@
+package test
+
+object HKGADT {
+ sealed trait Foo[F[_]]
+ final case class Bar() extends Foo[List]
+
+ def frob[F[_]](foo: Foo[F]): F[Int] =
+ foo match {
+ case Bar() =>
+ List(1)
+ }
+
+ sealed trait Foo1[F]
+ final case class Bar1() extends Foo1[Int]
+ def frob1[A](foo: Foo1[A]) = foo match {
+ case Bar1() => 1
+ }
+}
diff --git a/test/files/pos/t2712-1.flags b/test/files/pos/t2712-1.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/pos/t2712-1.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/pos/t2712-1.scala b/test/files/pos/t2712-1.scala
new file mode 100644
index 0000000000..4f84c9df5e
--- /dev/null
+++ b/test/files/pos/t2712-1.scala
@@ -0,0 +1,9 @@
+package test
+
+// Original test case from,
+//
+// https://issues.scala-lang.org/browse/SI-2712
+object Test {
+ def meh[M[_], A](x: M[A]): M[A] = x
+ meh{(x: Int) => x} // solves ?M = [X] Int => X and ?A = Int ...
+}
diff --git a/test/files/pos/t2712-2.flags b/test/files/pos/t2712-2.flags
new file mode 100644
index 0000000000..7d49efbb8e
--- /dev/null
+++ b/test/files/pos/t2712-2.flags
@@ -0,0 +1,2 @@
+-Ypartial-unification
+
diff --git a/test/files/pos/t2712-2.scala b/test/files/pos/t2712-2.scala
new file mode 100644
index 0000000000..39f22dd92a
--- /dev/null
+++ b/test/files/pos/t2712-2.scala
@@ -0,0 +1,25 @@
+package test
+
+// See: https://github.com/milessabin/si2712fix-demo/issues/3
+object Test {
+ trait A[T1, T2] { }
+ trait B[T1, T2] { }
+ class C[T] extends A[T, Long] with B[T, Double]
+ class CB extends A[Boolean, Long] with B[Boolean, Double]
+
+ trait A2[T]
+ trait B2[T]
+ class C2[T] extends A2[T] with B2[T]
+ class CB2 extends A2[Boolean] with B2[Boolean]
+
+ def meh[M[_], A](x: M[A]): M[A] = x
+
+ val m0 = meh(new C[Boolean])
+ m0: C[Boolean]
+ val m1 = meh(new CB)
+ m1: A[Boolean, Long]
+ val m2 = meh(new C2[Boolean])
+ m2: C2[Boolean]
+ val m3 = meh(new CB2)
+ m3: A2[Boolean]
+}
diff --git a/test/files/pos/t2712-3.flags b/test/files/pos/t2712-3.flags
new file mode 100644
index 0000000000..7d49efbb8e
--- /dev/null
+++ b/test/files/pos/t2712-3.flags
@@ -0,0 +1,2 @@
+-Ypartial-unification
+
diff --git a/test/files/pos/t2712-3.scala b/test/files/pos/t2712-3.scala
new file mode 100644
index 0000000000..46445f9289
--- /dev/null
+++ b/test/files/pos/t2712-3.scala
@@ -0,0 +1,24 @@
+package test
+
+object Test1 {
+ class Foo[T, F[_]]
+ def meh[M[_[_]], F[_]](x: M[F]): M[F] = x
+ meh(new Foo[Int, List]) // solves ?M = [X[_]]Foo[Int, X[_]] ?A = List ...
+}
+
+object Test2 {
+ trait TC[T]
+ class Foo[F[_], G[_]]
+ def meh[G[_[_]]](g: G[TC]) = ???
+ meh(new Foo[TC, TC]) // solves ?G = [X[_]]Foo[TC, X]
+}
+
+object Test3 {
+ trait TC[F[_]]
+ trait TC2[F[_]]
+ class Foo[F[_[_]], G[_[_]]]
+ new Foo[TC, TC2]
+
+ def meh[G[_[_[_]]]](g: G[TC2]) = ???
+ meh(new Foo[TC, TC2]) // solves ?G = [X[_[_]]]Foo[TC, X]
+}
diff --git a/test/files/pos/t2712-4.flags b/test/files/pos/t2712-4.flags
new file mode 100644
index 0000000000..7d49efbb8e
--- /dev/null
+++ b/test/files/pos/t2712-4.flags
@@ -0,0 +1,2 @@
+-Ypartial-unification
+
diff --git a/test/files/pos/t2712-4.scala b/test/files/pos/t2712-4.scala
new file mode 100644
index 0000000000..3e2e5cddae
--- /dev/null
+++ b/test/files/pos/t2712-4.scala
@@ -0,0 +1,17 @@
+package test
+
+object Test1 {
+ trait X
+ trait Y extends X
+ class Foo[T, U <: X]
+ def meh[M[_ <: A], A](x: M[A]): M[A] = x
+ meh(new Foo[Int, Y])
+}
+
+object Test2 {
+ trait X
+ trait Y extends X
+ class Foo[T, U >: Y]
+ def meh[M[_ >: A], A](x: M[A]): M[A] = x
+ meh(new Foo[Int, X])
+}
diff --git a/test/files/pos/t2712-5.flags b/test/files/pos/t2712-5.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/pos/t2712-5.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/pos/t2712-5.scala b/test/files/pos/t2712-5.scala
new file mode 100644
index 0000000000..ed96d4c06f
--- /dev/null
+++ b/test/files/pos/t2712-5.scala
@@ -0,0 +1,29 @@
+package test
+
+import scala.language.higherKinds
+
+trait Functor[F[_]] {
+ def map[A, B](f: A => B, fa: F[A]): F[B]
+}
+
+object Functor {
+ implicit def function[A]: Functor[({ type l[B] = A => B })#l] =
+ new Functor[({ type l[B] = A => B })#l] {
+ def map[C, B](cb: C => B, ac: A => C): A => B = cb compose ac
+ }
+}
+
+object FunctorSyntax {
+ implicit class FunctorOps[F[_], A](fa: F[A])(implicit F: Functor[F]) {
+ def map[B](f: A => B): F[B] = F.map(f, fa)
+ }
+}
+
+object Test {
+
+ val f: Int => String = _.toString
+
+ import FunctorSyntax._
+
+ f.map((s: String) => s.reverse)
+}
diff --git a/test/files/pos/t2712-6.flags b/test/files/pos/t2712-6.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/pos/t2712-6.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/pos/t2712-6.scala b/test/files/pos/t2712-6.scala
new file mode 100644
index 0000000000..eefe769ad6
--- /dev/null
+++ b/test/files/pos/t2712-6.scala
@@ -0,0 +1,12 @@
+package test
+
+object Tags {
+ type Tagged[A, T] = {type Tag = T; type Self = A}
+
+ type @@[T, Tag] = Tagged[T, Tag]
+
+ trait Disjunction
+
+ def meh[M[_], A](ma: M[A]): M[A] = ma
+ meh(null.asInstanceOf[Int @@ Disjunction])
+}
diff --git a/test/files/pos/t2712-7.flags b/test/files/pos/t2712-7.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/pos/t2712-7.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/pos/t2712-7.scala b/test/files/pos/t2712-7.scala
new file mode 100644
index 0000000000..d9c5243f13
--- /dev/null
+++ b/test/files/pos/t2712-7.scala
@@ -0,0 +1,15 @@
+package test
+
+// Cats Xor, Scalaz \/, scala.util.Either
+sealed abstract class Xor[+A, +B] extends Product with Serializable
+object Xor {
+ final case class Left[+A](a: A) extends (A Xor Nothing)
+ final case class Right[+B](b: B) extends (Nothing Xor B)
+}
+
+object TestXor {
+ import Xor._
+ def meh[F[_], A, B](fa: F[A])(f: A => B): F[B] = ???
+ meh(new Right(23): Xor[Boolean, Int])(_ < 13)
+ meh(new Left(true): Xor[Boolean, Int])(_ < 13)
+}
diff --git a/test/files/pos/t5683.flags b/test/files/pos/t5683.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/pos/t5683.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/pos/t5683.scala b/test/files/pos/t5683.scala
new file mode 100644
index 0000000000..05ab035792
--- /dev/null
+++ b/test/files/pos/t5683.scala
@@ -0,0 +1,23 @@
+object Test {
+ trait NT[X]
+ trait W[W, A] extends NT[Int]
+ type StringW[T] = W[String, T]
+ trait K[M[_], A, B]
+
+ def k[M[_], B](f: Int => M[B]): K[M, Int, B] = null
+
+ val okay1: K[StringW,Int,Int] = k{ (y: Int) => null: StringW[Int] }
+ val okay2 = k[StringW,Int]{ (y: Int) => null: W[String, Int] }
+
+ val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] }
+
+ // remove `extends NT[Int]`, and the last line gives an inference error
+ // rather than a crash.
+ // test/files/pos/t5683.scala:12: error: no type parameters for method k: (f: Int => M[B])Test.K[M,Int,B] exist so that it can be applied to arguments (Int => Test.W[String,Int])
+ // --- because ---
+ // argument expression's type is not compatible with formal parameter type;
+ // found : Int => Test.W[String,Int]
+ // required: Int => ?M[?B]
+ // val crash: K[StringW,Int,Int] = k{ (y: Int) => null: W[String, Int] }
+ // ^
+}
diff --git a/test/files/pos/t6895b.flags b/test/files/pos/t6895b.flags
new file mode 100644
index 0000000000..7d49efbb8e
--- /dev/null
+++ b/test/files/pos/t6895b.flags
@@ -0,0 +1,2 @@
+-Ypartial-unification
+
diff --git a/test/files/pos/t6895b.scala b/test/files/pos/t6895b.scala
new file mode 100644
index 0000000000..c465065011
--- /dev/null
+++ b/test/files/pos/t6895b.scala
@@ -0,0 +1,39 @@
+trait Foo[F[_]]
+trait Bar[F[_], A]
+
+trait Or[A, B]
+
+class Test {
+ implicit def orFoo[A]: Foo[({type L[X] = Or[A, X]})#L] = ???
+ implicit def barFoo[F[_]](implicit f: Foo[F]): Foo[({type L[X] = Bar[F, X]})#L] = ???
+
+ // Now we can define a couple of type aliases:
+ type StringOr[X] = Or[String, X]
+ type BarStringOr[X] = Bar[StringOr, X]
+
+ // ok
+ implicitly[Foo[BarStringOr]]
+ barFoo[StringOr](null) : Foo[BarStringOr]
+ barFoo(null) : Foo[BarStringOr]
+
+ // nok
+ implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]]
+ // Let's write the application explicitly, and then
+ // compile with just this line enabled and -explaintypes.
+ barFoo(null) : Foo[({type L[X] = Bar[StringOr, X]})#L]
+
+ // Foo[[X]Bar[F,X]] <: Foo[[X]Bar[[X]Or[String,X],X]]?
+ // Bar[[X]Or[String,X],X] <: Bar[F,X]?
+ // F[_] <: Or[String,_]?
+ // false
+ // false
+ // false
+
+ // Note that the type annotation above is typechecked as
+ // Foo[[X]Bar[[X]Or[String,X],X]], ie the type alias `L`
+ // is eta expanded.
+ //
+ // This is done so that it does not escape its defining scope.
+ // However, one this is done, higher kinded inference
+ // no longer is able to unify F with `StringOr` (SI-2712)
+}
diff --git a/test/files/pos/t7046-2/Macros_1.scala b/test/files/pos/t7046-2/Macros_1.scala
new file mode 100644
index 0000000000..07c0c61281
--- /dev/null
+++ b/test/files/pos/t7046-2/Macros_1.scala
@@ -0,0 +1,14 @@
+package p1
+
+import scala.reflect.macros.blackbox._
+import language.experimental._
+
+object Macro {
+ def impl(c: Context): c.Tree = {
+ import c.universe._
+ val tsym = rootMirror.staticClass("p1.Base")
+ val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString)
+ q"$subclasses"
+ }
+ def p1_Base_knownDirectSubclasses: List[String] = macro impl
+}
diff --git a/test/files/pos/t7046-2/Test_2.scala b/test/files/pos/t7046-2/Test_2.scala
new file mode 100644
index 0000000000..74e30a863d
--- /dev/null
+++ b/test/files/pos/t7046-2/Test_2.scala
@@ -0,0 +1,9 @@
+package p1
+
+sealed trait Base
+
+object Test {
+ val x = Macro.p1_Base_knownDirectSubclasses
+}
+
+case class B(val b: Test.x.type)
diff --git a/test/files/presentation/t8085.check b/test/files/presentation/t8085.check
index 921ca7528b..0e85de45f9 100644
--- a/test/files/presentation/t8085.check
+++ b/test/files/presentation/t8085.check
@@ -1,3 +1,2 @@
reload: NodeScalaSuite.scala
-open package module: package object nodescala
Test OK
diff --git a/test/files/presentation/t8085b.check b/test/files/presentation/t8085b.check
index 921ca7528b..0e85de45f9 100644
--- a/test/files/presentation/t8085b.check
+++ b/test/files/presentation/t8085b.check
@@ -1,3 +1,2 @@
reload: NodeScalaSuite.scala
-open package module: package object nodescala
Test OK
diff --git a/test/files/run/inferred-type-constructors-hou.check b/test/files/run/inferred-type-constructors-hou.check
new file mode 100644
index 0000000000..6b09823341
--- /dev/null
+++ b/test/files/run/inferred-type-constructors-hou.check
@@ -0,0 +1,56 @@
+warning: there were two feature warnings; re-run with -feature for details
+ p.Iterable[Int]
+ p.Set[Int]
+ p.Seq[Int]
+ p.m.Set[Int]
+ p.m.Seq[Int]
+ private[m] p.m.ASet[Int]
+ p.i.Seq[Int]
+ private[i] p.i.ASet[Int]
+ private[i] p.i.ASeq[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Set[Int]
+ p.Iterable[Int]
+ p.Set[Int]
+ p.Iterable[Int]
+ p.Set[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Seq[Int]
+ p.Iterable[Int]
+ p.Seq[Int]
+ p.Iterable[Int]
+ p.Seq[Int]
+ p.Iterable[Int]
+ p.m.Set[Int]
+ p.Iterable[Int]
+ p.Set[Int]
+ p.Iterable[Int]
+ p.Iterable[Int]
+ p.Seq[Int]
+ p.Iterable[Int]
+ p.Seq[Int]
+ p.Iterable[Int]
+ private[p] p.ASet[Int]
+ private[p] p.AIterable[Int]
+ p.Iterable[Int]
+ p.i.Seq[Int]
+ private[p] p.AIterable[Int]
+ List[Nothing]
+ scala.collection.immutable.Vector[Nothing]
+ scala.collection.immutable.Map[Int,Int]
+ scala.collection.immutable.Set[Int]
+ Seq[Int]
+ Array[Int]
+ scala.collection.AbstractSet[Int]
+ Comparable[java.lang.String]
+ scala.collection.immutable.LinearSeq[Int]
+ Iterable[Int]
diff --git a/test/files/run/inferred-type-constructors-hou.flags b/test/files/run/inferred-type-constructors-hou.flags
new file mode 100644
index 0000000000..41565c7e32
--- /dev/null
+++ b/test/files/run/inferred-type-constructors-hou.flags
@@ -0,0 +1 @@
+-Ypartial-unification
diff --git a/test/files/run/inferred-type-constructors-hou.scala b/test/files/run/inferred-type-constructors-hou.scala
new file mode 100644
index 0000000000..79a8653f68
--- /dev/null
+++ b/test/files/run/inferred-type-constructors-hou.scala
@@ -0,0 +1,125 @@
+package p {
+ trait TCon[+CC[X]] {
+ def fPublic: CC[Int] = ???
+ private[p] def fPackagePrivate: CC[Int] = ???
+ protected[p] def fPackageProtected: CC[Int] = ???
+ }
+ trait Iterable[+A] extends TCon[Iterable]
+ trait Set[A] extends Iterable[A] with TCon[Set]
+ trait Seq[+A] extends Iterable[A] with TCon[Seq]
+
+ private[p] abstract class AIterable[+A] extends Iterable[A]
+ private[p] abstract class ASeq[+A] extends AIterable[A] with Seq[A]
+ private[p] abstract class ASet[A] extends AIterable[A] with Set[A]
+
+ package m {
+ private[m] abstract class ASeq[A] extends p.ASeq[A] with Seq[A]
+ private[m] abstract class ASet[A] extends p.ASet[A] with Set[A]
+ trait Set[A] extends p.Set[A] with TCon[Set]
+ trait Seq[A] extends p.Seq[A] with TCon[Seq]
+ trait BitSet extends ASet[Int]
+ trait IntSeq extends ASeq[Int]
+ }
+
+ package i {
+ private[i] abstract class ASeq[+A] extends p.ASeq[A] with Seq[A]
+ private[i] abstract class ASet[A] extends p.ASet[A] with Set[A]
+ trait Set[A] extends p.Set[A] with TCon[Set]
+ trait Seq[+A] extends p.Seq[A] with TCon[Seq]
+ trait BitSet extends ASet[Int]
+ trait IntSeq extends ASeq[Int]
+ }
+}
+
+object Test {
+ import scala.reflect.runtime.universe._
+ // Complicated by the absence of usable type constructor type tags.
+ def extract[A, CC[X]](xs: CC[A]): CC[A] = xs
+ def whatis[T: TypeTag](x: T): Unit = {
+ val tpe = typeOf[T]
+ val access = tpe.typeSymbol.asInstanceOf[scala.reflect.internal.HasFlags].accessString.replaceAllLiterally("package ", "")
+ println(f"$access%15s $tpe")
+ }
+
+ trait IntIterable extends p.Iterable[Int]
+ trait IntSet extends p.Set[Int]
+ trait IntSeq extends p.Seq[Int]
+
+ trait MutableIntSet extends p.m.Set[Int]
+ trait MutableIntSeq extends p.m.Seq[Int]
+
+ trait ImmutableIntSet extends p.i.Set[Int]
+ trait ImmutableIntSeq extends p.i.Seq[Int]
+
+ def f1: IntIterable = null
+ def f2: IntSet = null
+ def f3: IntSeq = null
+
+ def g1: MutableIntSet = null
+ def g2: MutableIntSeq = null
+ def g3: p.m.BitSet = null
+
+ def h1: ImmutableIntSeq = null
+ def h2: p.i.BitSet = null
+ def h3: p.i.IntSeq = null
+
+ def main(args: Array[String]): Unit = {
+ whatis(extract(f1))
+ whatis(extract(f2))
+ whatis(extract(f3))
+ whatis(extract(g1))
+ whatis(extract(g2))
+ whatis(extract(g3))
+ whatis(extract(h1))
+ whatis(extract(h2))
+ whatis(extract(h3))
+
+ whatis(extract(if (true) f1 else f2))
+ whatis(extract(if (true) f1 else f3))
+ whatis(extract(if (true) f1 else g1))
+ whatis(extract(if (true) f1 else g2))
+ whatis(extract(if (true) f1 else g3))
+ whatis(extract(if (true) f1 else h1))
+ whatis(extract(if (true) f1 else h2))
+ whatis(extract(if (true) f1 else h3))
+ whatis(extract(if (true) f2 else f3))
+ whatis(extract(if (true) f2 else g1))
+ whatis(extract(if (true) f2 else g2))
+ whatis(extract(if (true) f2 else g3))
+ whatis(extract(if (true) f2 else h1))
+ whatis(extract(if (true) f2 else h2))
+ whatis(extract(if (true) f2 else h3))
+ whatis(extract(if (true) f3 else g1))
+ whatis(extract(if (true) f3 else g2))
+ whatis(extract(if (true) f3 else g3))
+ whatis(extract(if (true) f3 else h1))
+ whatis(extract(if (true) f3 else h2))
+ whatis(extract(if (true) f3 else h3))
+ whatis(extract(if (true) g1 else g2))
+ whatis(extract(if (true) g1 else g3))
+ whatis(extract(if (true) g1 else h1))
+ whatis(extract(if (true) g1 else h2))
+ whatis(extract(if (true) g1 else h3))
+ whatis(extract(if (true) g2 else g3))
+ whatis(extract(if (true) g2 else h1))
+ whatis(extract(if (true) g2 else h2))
+ whatis(extract(if (true) g2 else h3))
+ whatis(extract(if (true) g3 else h1))
+ whatis(extract(if (true) g3 else h2))
+ whatis(extract(if (true) g3 else h3))
+ whatis(extract(if (true) h1 else h2))
+ whatis(extract(if (true) h1 else h3))
+ whatis(extract(if (true) h2 else h3))
+
+ whatis(extract(Nil))
+ whatis(extract(Vector()))
+ whatis(extract(Map[Int,Int]()))
+ whatis(extract(Set[Int]()))
+ whatis(extract(Seq[Int]()))
+ whatis(extract(Array[Int]()))
+ whatis(extract(scala.collection.immutable.BitSet(1)))
+ whatis(extract("abc"))
+ whatis(extract(if (true) Stream(1) else List(1)))
+ whatis(extract(if (true) Seq(1) else Set(1)))
+ }
+}
diff --git a/test/files/run/reflection-mem-typecheck.scala b/test/files/run/reflection-mem-typecheck.scala
index e3cabf689d..f1fe983ede 100644
--- a/test/files/run/reflection-mem-typecheck.scala
+++ b/test/files/run/reflection-mem-typecheck.scala
@@ -11,7 +11,7 @@ object Test extends MemoryTest {
cm.mkToolBox()
}
- override def maxDelta = 10
+ override def maxDelta = 12
override def calcsPerIter = 8
override def calc() {
var snippet = """
diff --git a/test/files/run/t7046-1/Macros_1.scala b/test/files/run/t7046-1/Macros_1.scala
new file mode 100644
index 0000000000..2a5bf82f62
--- /dev/null
+++ b/test/files/run/t7046-1/Macros_1.scala
@@ -0,0 +1,15 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+
+object Macros {
+ def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = {
+ import c.universe._;
+ val ttpe = ttag.tpe
+ val tsym = ttpe.typeSymbol.asClass
+ val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString)
+
+ c.Expr[List[String]](q"$subclasses")
+ }
+
+ def knownDirectSubclasses[T]: List[String] = macro impl[T]
+}
diff --git a/test/files/run/t7046-1/Test_2.scala b/test/files/run/t7046-1/Test_2.scala
new file mode 100644
index 0000000000..28459fde72
--- /dev/null
+++ b/test/files/run/t7046-1/Test_2.scala
@@ -0,0 +1,23 @@
+object Test extends App {
+ val subs = Macros.knownDirectSubclasses[Foo]
+ assert(subs == List("Wibble", "Wobble", "Bar", "Baz"))
+}
+
+sealed trait Foo
+object Foo {
+ trait Wibble extends Foo
+ case object Wobble extends Foo
+}
+
+trait Bar extends Foo
+
+object Blah {
+ type Quux = Foo
+}
+
+import Blah._
+
+trait Baz extends Quux
+
+class Boz[T](t: T)
+class Unrelated extends Boz(Test.subs)
diff --git a/test/files/run/t7046-2/Macros_1.scala b/test/files/run/t7046-2/Macros_1.scala
new file mode 100644
index 0000000000..2a5bf82f62
--- /dev/null
+++ b/test/files/run/t7046-2/Macros_1.scala
@@ -0,0 +1,15 @@
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+
+object Macros {
+ def impl[T](c: Context)(implicit ttag: c.WeakTypeTag[T]): c.Expr[List[String]] = {
+ import c.universe._;
+ val ttpe = ttag.tpe
+ val tsym = ttpe.typeSymbol.asClass
+ val subclasses = tsym.knownDirectSubclasses.toList.map(_.name.toString)
+
+ c.Expr[List[String]](q"$subclasses")
+ }
+
+ def knownDirectSubclasses[T]: List[String] = macro impl[T]
+}
diff --git a/test/files/run/t7046-2/Test_2.scala b/test/files/run/t7046-2/Test_2.scala
new file mode 100644
index 0000000000..79407f522f
--- /dev/null
+++ b/test/files/run/t7046-2/Test_2.scala
@@ -0,0 +1,14 @@
+object Test extends App {
+ def nested: Unit = {
+ sealed trait Foo
+ object Foo {
+ trait Bar extends Foo
+ trait Baz extends Foo
+ }
+
+ val subs = Macros.knownDirectSubclasses[Foo]
+ assert(subs == List("Bar", "Baz"))
+ }
+
+ nested
+}
diff --git a/test/files/run/t9806.scala b/test/files/run/t9806.scala
new file mode 100644
index 0000000000..ccde989efe
--- /dev/null
+++ b/test/files/run/t9806.scala
@@ -0,0 +1,18 @@
+object Ex extends Exception
+object Test {
+ def main(args: Array[String]) {
+ try foo catch { case Ex => }
+ }
+
+ def isTrue(b: Boolean) = b
+ def foo = {
+ var streamErrors1 = true
+ try {
+ streamErrors1 = false
+ throw Ex
+ } catch {
+ case ex if streamErrors1 =>
+ assert(isTrue(streamErrors1))
+ }
+ }
+}
diff --git a/test/files/run/t9841.scala b/test/files/run/t9841.scala
new file mode 100644
index 0000000000..19cfef28a5
--- /dev/null
+++ b/test/files/run/t9841.scala
@@ -0,0 +1,24 @@
+// SI-9841 regrettable behavior initializing private inner object
+// A fix is not yet planned for 2.11.9, but it works in 2.12.x.
+//
+//at Container.Container$$Inner$lzycompute(t9841.scala:4)
+//at Container.Container$$Inner(t9841.scala:4)
+//at Container$Inner$.<init>(t9841.scala:5)
+//
+class Container {
+ private case class Inner(s: String)
+ private object Inner {
+ val Empty = Inner("")
+ }
+ private val state = Inner.Empty
+}
+
+object Test extends App {
+ val catcher: PartialFunction[Throwable, Unit] = {
+ case _: StackOverflowError =>
+ }
+ try {
+ new Container
+ Console println "Expected StackOverflowError"
+ } catch catcher
+}
diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala
index d980cadeb3..f18a4de4e9 100644
--- a/test/junit/scala/collection/IteratorTest.scala
+++ b/test/junit/scala/collection/IteratorTest.scala
@@ -164,6 +164,12 @@ class IteratorTest {
assertEquals(1, y.next)
assertFalse(x.hasNext) // was true, after advancing underlying iterator
}
+ // SI-9913
+ @Test def `span leading iterator finishes at state -1`(): Unit = {
+ val (yes, no) = Iterator(1, 2, 3).span(_ => true)
+ assertFalse(no.hasNext)
+ assertTrue(yes.hasNext)
+ }
// SI-9623
@Test def noExcessiveHasNextInJoinIterator: Unit = {
var counter = 0
diff --git a/test/junit/scala/tools/nsc/settings/SettingsTest.scala b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
index 96f83c4c2f..183cb792cc 100644
--- a/test/junit/scala/tools/nsc/settings/SettingsTest.scala
+++ b/test/junit/scala/tools/nsc/settings/SettingsTest.scala
@@ -180,4 +180,71 @@ class SettingsTest {
assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource", "2.11"), _ == "-Xsource requires an argument, the syntax is -Xsource:<version>")
assertThrows[IllegalArgumentException](check(expected = "2.11", "-Xsource:2.invalid"), _ contains "There was a problem parsing 2.invalid")
}
+
+ // equal with stripped margins and normalized line endings
+ private def marginallyEquals(s1: String, s2: String): Boolean = {
+ def normally(s: String): String = s.stripMargin.lines.mkString("\n")
+ normally(s1) == normally(s2)
+ }
+
+ @Test def helpHasDefault(): Unit = {
+ val s = new MutableSettings(msg => throw new IllegalArgumentException(msg))
+ object mChoices extends s.MultiChoiceEnumeration {
+ val a = Choice("a", "help a")
+ val b = Choice("b", "help b")
+ val c = Choice("c", "help c")
+ }
+ val m = s.MultiChoiceSetting("-m", "args", "magic sauce", mChoices, Some(List("b")))
+
+ def check(args: String*)(t: s.MultiChoiceSetting[mChoices.type] => Boolean): Boolean = {
+ m.clear()
+ val (ok, rest) = s.processArguments(args.toList, processAll = true)
+ assert(rest.isEmpty)
+ t(m)
+ }
+
+ import mChoices._
+
+ assertTrue(check("-m")(_.value == Set(b)))
+ assertTrue(check("-m") { _ =>
+ val expected =
+ """|magic sauce
+ | a help a
+ | b help b
+ | c help c
+ |Default: b
+ |"""
+ marginallyEquals(expected, m.help)
+ })
+ }
+ @Test def helpHasDefaultAll(): Unit = {
+ val s = new MutableSettings(msg => throw new IllegalArgumentException(msg))
+ object mChoices extends s.MultiChoiceEnumeration {
+ val a = Choice("a", "help a")
+ val b = Choice("b", "help b")
+ val c = Choice("c", "help c")
+ }
+ val m = s.MultiChoiceSetting("-m", "args", "magic sauce", mChoices, Some(List("_")))
+
+ def check(args: String*)(t: s.MultiChoiceSetting[mChoices.type] => Boolean): Boolean = {
+ m.clear()
+ val (ok, rest) = s.processArguments(args.toList, processAll = true)
+ assert(rest.isEmpty)
+ t(m)
+ }
+
+ import mChoices._
+
+ assertTrue(check("-m")(_.value == Set(a, b, c)))
+ assertTrue(check("-m") { _ =>
+ val expected =
+ """|magic sauce
+ | a help a
+ | b help b
+ | c help c
+ |Default: All choices are enabled by default.
+ |"""
+ marginallyEquals(expected, m.help)
+ })
+ }
}