Skip to content

Fix #3332: Disallow bound variables in pattern alternatives #3429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ case class Mode(val bits: Int) extends AnyVal {
def &~ (that: Mode) = Mode(bits & ~that.bits)
def is (that: Mode) = (bits & that.bits) == that.bits

def isExpr = (this & PatternOrType) == None
def isExpr = (this & PatternOrTypeBits) == None

override def toString =
(0 until 31).filter(i => (bits & (1 << i)) != 0).map(modeName).mkString("Mode(", ",", ")")
Expand Down Expand Up @@ -42,6 +42,9 @@ object Mode {
/** We are looking at the arguments of a supercall */
val InSuperCall = newMode(6, "InSuperCall")

/** We are in a pattern alternative */
val InPatternAlternative = newMode(7, "InPatternAlternative")

/** Allow GADTFlexType labelled types to have their bounds adjusted */
val GADTflexible = newMode(8, "GADTflexible")

Expand Down Expand Up @@ -87,7 +90,7 @@ object Mode {
/** Don't suppress exceptions thrown during show */
val PrintShowExceptions = newMode(18, "PrintShowExceptions")

val PatternOrType = Pattern | Type
val PatternOrTypeBits = Pattern | Type | InPatternAlternative
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you rename this value? I think the old name is more consistent with the other mode's name

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because otherwise we'd risk that someone thinks it's Pattern | Type only. And PatternOrTypeorInPatternAlternative was too long.


/** We are elaborating the fully qualified name of a package clause.
* In this case, identifiers should never be imported.
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -936,10 +936,10 @@ class Namer { typer: Typer =>
}

def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type))
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits addMode Mode.Type))

def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrType))
typedAheadImpl(tree, typer.typed(_, pt)(ctx retractMode Mode.PatternOrTypeBits))

def typedAheadAnnotation(tree: Tree)(implicit ctx: Context): Symbol = tree match {
case Apply(fn, _) => typedAheadAnnotation(fn)
Expand Down
14 changes: 10 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1210,13 +1210,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
tpd.cpy.UnApply(body1)(fn, Nil,
typed(untpd.Bind(tree.name, untpd.TypedSplice(arg)).withPos(tree.pos), arg.tpe) :: Nil)
case _ =>
val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos)
assignType(cpy.Bind(tree)(tree.name, body1), sym)
if (tree.name == nme.WILDCARD) body1
else {
val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos)
if (ctx.mode.is(Mode.InPatternAlternative))
ctx.error(i"Illegal variable ${sym.name} in pattern alternative", tree.pos)
assignType(cpy.Bind(tree)(tree.name, body1), sym)
}
}
}

def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") {
val trees1 = tree.trees mapconserve (typed(_, pt))
val nestedCtx = ctx.addMode(Mode.InPatternAlternative)
val trees1 = tree.trees.mapconserve(typed(_, pt)(nestedCtx))
assignType(cpy.Alternative(tree)(trees1), trees1)
}

Expand Down Expand Up @@ -1754,7 +1760,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
Inliner.removeInlineAccessors(mdef.symbol)

def typedExpr(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree =
typed(tree, pt)(ctx retractMode Mode.PatternOrType)
typed(tree, pt)(ctx retractMode Mode.PatternOrTypeBits)
def typedType(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = // todo: retract mode between Type and Pattern?
typed(tree, pt)(ctx addMode Mode.Type)
def typedPattern(tree: untpd.Tree, selType: Type = WildcardType)(implicit ctx: Context): Tree =
Expand Down
21 changes: 21 additions & 0 deletions tests/neg/i3332.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
object Main {
def main(args: Array[String]): Unit = {
(1: Any) match {
case x: String | Int => "OK" // error: Illegal variable in pattern alternative
case (_: String) | (_: Int) => "OK"
case (s: String) | _: Int => s // error: Illegal variable in pattern alternative
}
}
}

// #1612
object Test {
case class A()
def g(p:(Int,Int)) = p match {
case (10,n) | (n,10) => println(n) // error // error (Illegal variable in pattern alternative)
case _ => println("nope")
}
def test(x: Any) = x match {
case _: String | _ @ A() => 1
}
}