From 630246594d476964bad8e2ee7d804e823e1ec279 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Wed, 3 Sep 2014 23:22:21 -0600 Subject: [PATCH 1/5] Add flag to disable withFilter pattern desugaring This adds a flag called `-Zirrefutable-generator-patterns` which changes desugaring of code like the following: for { (a, b) <- List(1 -> 'a', 2 -> 'b') (c, d) <- List(3 -> 'c', 4 -> 'd') } yield (a + c, b + d) Which previously would turn into something like: List(1 -> 'a', 2 -> 'b').withFilter { case (a, b) => true case _ => false }.flatMap { case (a, b) => List(3 -> 'c', 4 -> 'd').withFilter { case (c, d) => true case _ => false }.map { case (c, d) => (a + c, b + d) } } With this new flag, it only becomes: List(1 -> 'a', 2 -> 'b').flatMap { case (a, b) => List(3 -> 'c', 4 -> 'd').map { case (c, d) => (a + c, b + d) } } Which creates a few benefits: 1. We don't have to do a useless call to withFilter 2. We can use patterns on things without withFilter 3. Things don't silently disappear when patterns are wrong --- src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 5 +++++ src/reflect/scala/reflect/internal/TreeGen.scala | 2 +- .../scala/reflect/internal/settings/MutableSettings.scala | 2 ++ src/reflect/scala/reflect/runtime/Settings.scala | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 8e695986141..4a34dfcf3a4 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -217,6 +217,11 @@ trait ScalaSettings extends AbsScalaSettings private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug." + /** + * -Z Typelevel settings + */ + val ZirrefutableGeneratorPatterns = BooleanSetting("-Zirrefutable-generator-patterns", "Treat patterns in for comprehensions as irrefutable. Do not add filter or withFilter calls.") + /** Area-specific debug output. */ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 6e8e992d165..c5b36142c62 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -768,7 +768,7 @@ abstract class TreeGen { } def mkCheckIfRefutable(pat: Tree, rhs: Tree)(implicit fresh: FreshNameCreator) = - if (treeInfo.isVarPatternDeep(pat)) rhs + if (treeInfo.isVarPatternDeep(pat) || settings.ZirrefutableGeneratorPatterns) rhs else { val cases = List( CaseDef(pat.duplicate, EmptyTree, Literal(Constant(true))), diff --git a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala index a494c7f0d07..a4cc0918405 100644 --- a/src/reflect/scala/reflect/internal/settings/MutableSettings.scala +++ b/src/reflect/scala/reflect/internal/settings/MutableSettings.scala @@ -55,6 +55,8 @@ abstract class MutableSettings extends AbsSettings { def maxClassfileName: IntSetting def isScala211: Boolean + + def ZirrefutableGeneratorPatterns: BooleanSetting } object MutableSettings { diff --git a/src/reflect/scala/reflect/runtime/Settings.scala b/src/reflect/scala/reflect/runtime/Settings.scala index 27d574b1de5..3681844cc70 100644 --- a/src/reflect/scala/reflect/runtime/Settings.scala +++ b/src/reflect/scala/reflect/runtime/Settings.scala @@ -51,4 +51,6 @@ private[reflect] class Settings extends MutableSettings { val Yrecursion = new IntSetting(0) val maxClassfileName = new IntSetting(255) def isScala211 = true + + def ZirrefutableGeneratorPatterns = new BooleanSetting(false) } From 062e56c23546268f8be68eb6bc44352b91dfcf33 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Thu, 4 Sep 2014 06:50:01 -0600 Subject: [PATCH 2/5] Warn on non-exhaustive for-comprehension patterns We assume patterns are exhaustive when desugaring with `-Zirrefutable-generator-patterns` but we should still emit a warning if we think they might not be. --- src/reflect/scala/reflect/internal/TreeGen.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index c5b36142c62..890444a1727 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -622,7 +622,7 @@ abstract class TreeGen { body) setPos splitpos case None => atPos(splitpos) { - mkVisitor(List(CaseDef(pat, EmptyTree, body)), checkExhaustive = false) + mkVisitor(List(CaseDef(pat, EmptyTree, body)), checkExhaustive = settings.ZirrefutableGeneratorPatterns) } } } From aceb21674e2d381c86a5104354e12357395e79c5 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Thu, 4 Sep 2014 07:51:13 -0600 Subject: [PATCH 3/5] Add a test for -Zirrefutable-generator-patterns --- test/files/pos/Zirrefutable-generator-patterns.flags | 1 + test/files/pos/Zirrefutable-generator-patterns.scala | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 test/files/pos/Zirrefutable-generator-patterns.flags create mode 100644 test/files/pos/Zirrefutable-generator-patterns.scala diff --git a/test/files/pos/Zirrefutable-generator-patterns.flags b/test/files/pos/Zirrefutable-generator-patterns.flags new file mode 100644 index 00000000000..4358f7ddae7 --- /dev/null +++ b/test/files/pos/Zirrefutable-generator-patterns.flags @@ -0,0 +1 @@ +-Zirrefutable-generator-patterns diff --git a/test/files/pos/Zirrefutable-generator-patterns.scala b/test/files/pos/Zirrefutable-generator-patterns.scala new file mode 100644 index 00000000000..49721d10a76 --- /dev/null +++ b/test/files/pos/Zirrefutable-generator-patterns.scala @@ -0,0 +1,11 @@ +case class Foo[A](a: A) { + def map[B](f: A => B): Foo[B] = Foo(f(a)) + def flatMap[B](f: A => Foo[B]): Foo[B] = Foo(f(a).a) +} + +object Test { + for { + (a, b) <- Foo((1, 'd')) + (c, d, e) <- Foo((1, true, "three")) + } yield (a + c, e :+ b, !d) +} From d7be78debd97ad429071c20f3f1c3ddf8de2ae21 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Thu, 4 Sep 2014 14:45:17 -0600 Subject: [PATCH 4/5] List Typelevel options when -Z flag is given --- src/compiler/scala/tools/nsc/CompilerCommand.scala | 2 ++ src/compiler/scala/tools/nsc/settings/AbsSettings.scala | 1 + src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 3ded4563789..0581f9fc37c 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -82,6 +82,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { def usageMsg = createUsageMsg("where possible standard", shouldExplain = false, _.isStandard) def xusageMsg = createUsageMsg("Possible advanced", shouldExplain = true, _.isAdvanced) def yusageMsg = createUsageMsg("Possible private", shouldExplain = true, _.isPrivate) + def zusageMsg = createUsageMsg("Possible Typelevel", shouldExplain = true, _.isTypelevel) /** For info settings, compiler should just print a message and quit. */ def shouldStopWithInfo = settings.isInfo @@ -95,6 +96,7 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { else if (help) usageMsg + global.pluginOptionsHelp else if (Xhelp) xusageMsg else if (Yhelp) yusageMsg + else if (Zhelp) zusageMsg else if (showPlugins) global.pluginDescriptions else if (showPhases) global.phaseDescriptions + ( if (debug) "\n" + global.phaseFlagDescriptions else "" diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala index 4727e6d8673..e07191a05bf 100644 --- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala @@ -109,6 +109,7 @@ trait AbsSettings extends scala.reflect.internal.settings.AbsSettings { */ def isAdvanced = name match { case "-Y" => true ; case "-X" => false ; case _ => name startsWith "-X" } def isPrivate = name match { case "-Y" => false ; case _ => name startsWith "-Y" } + def isTypelevel = name match { case "-Z" => false ; case _ => name startsWith "-Z" } def isStandard = !isAdvanced && !isPrivate def isForDebug = name endsWith "-debug" // by convention, i.e. -Ytyper-debug def isDeprecated = deprecationMessage.isDefined diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 4a34dfcf3a4..bba6b9a0ce3 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -42,7 +42,7 @@ trait ScalaSettings extends AbsScalaSettings def optimiseSettings = List[BooleanSetting](inline, inlineHandlers, Xcloselim, Xdce, YconstOptimization) /** If any of these settings is enabled, the compiler should print a message and exit. */ - def infoSettings = List[Setting](version, help, Xhelp, Yhelp, showPlugins, showPhases, genPhaseGraph) + def infoSettings = List[Setting](version, help, Xhelp, Yhelp, Zhelp, showPlugins, showPhases, genPhaseGraph) /** Any -multichoice:help? Nicer if any option could report that it had help to offer. */ private def multihelp = allSettings exists { case s: MultiChoiceSetting => s.isHelping case _ => false } @@ -220,6 +220,7 @@ trait ScalaSettings extends AbsScalaSettings /** * -Z Typelevel settings */ + val Zhelp = BooleanSetting("-Z", "Print a synopsis of Typelevel options.") val ZirrefutableGeneratorPatterns = BooleanSetting("-Zirrefutable-generator-patterns", "Treat patterns in for comprehensions as irrefutable. Do not add filter or withFilter calls.") /** Area-specific debug output. From 7ac3809dabeaddd2407668eed7154fdc24fde1a2 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Tue, 23 Sep 2014 19:46:24 -0600 Subject: [PATCH 5/5] Filter bincompat of ZirrefutableGeneratorPatterns --- bincompat-forward.whitelist.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index ec80e7cce95..c01b2b95c04 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -368,6 +368,10 @@ filter { { matchName="scala.reflect.io.AbstractFile.filterImpl" problemName=MissingMethodProblem + }, + { + matchName="scala.reflect.runtime.Settings.ZirrefutableGeneratorPatterns" + problemName=MissingMethodProblem } ] }