Skip to content

Commit 652c75b

Browse files
authored
Merge pull request scala#6025 from hrhino/feature/alternative-lubs
Type Alternative patterns as the lub of their branches.
2 parents ca07e6c + 31210e7 commit 652c75b

File tree

5 files changed

+56
-11
lines changed

5 files changed

+56
-11
lines changed

spec/08-pattern-matching.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,15 @@ shorthand for the constructor or extractor pattern $\mathit{op}(p, q_1
280280
Pattern ::= Pattern1 { ‘|’ Pattern1 }
281281
```
282282

283-
A _pattern alternative_ `$p_1$ | $\ldots$ | $p_n$`
284-
consists of a number of alternative patterns $p_i$. All alternative
285-
patterns are type checked with the expected type of the pattern. They
286-
may not bind variables other than wildcards. The alternative pattern
287-
matches a value $v$ if at least one its alternatives matches $v$.
283+
A _pattern alternative_ `$p_1$ | $\ldots$ | $p_n$` consists of a number of
284+
_alternative patterns_ $p_i$. All alternative patterns are type checked with
285+
the expected type of the pattern. The type of the pattern alternative is the
286+
[weak least upper bound](03-types.html#weak-conformance) of the types of
287+
$p_1 , \ldots , p_n$.
288+
289+
Alternative patterns may not bind variables other than wildcards.
290+
The alternative pattern matches a value $v$ if at least one its alternatives
291+
matches $v$.
288292

289293
### XML Patterns
290294

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
11851185
} else tree.tpe match {
11861186
case atp @ AnnotatedType(_, _) if canAdaptAnnotations(tree, this, mode, pt) => // (-1)
11871187
adaptAnnotations(tree, this, mode, pt)
1188-
case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0)
1188+
case ct @ ConstantType(value) if mode.inNone(TYPEmode | FUNmode | PATTERNmode) && (ct <:< pt) && canAdaptConstantTypeToLiteral => // (0)
1189+
/* We don't adapt in PATTERNmode because patmat checks for duplicated
1190+
* alternatives later on in MatchOptimizations, so that this desugars
1191+
* to a single check. Moreover, as we don't adaptConstant now, we are
1192+
* able to issue a "duplicated alternative" warning at the same time;
1193+
* see scala/bug#7290 for context. Theoretically I could warn earlier
1194+
* in typedAlternative, but there are other checks done in patmat, so
1195+
* it's easier just to wait until then.
1196+
*/
11891197
adaptConstant(value)
11901198
case OverloadedType(pre, alts) if !mode.inFunMode => // (1)
11911199
inferExprAlternative(tree, pt)
@@ -5254,9 +5262,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
52545262
}
52555263

52565264
def typedAlternative(alt: Alternative) = {
5257-
context withinPatAlternative (
5258-
treeCopy.Alternative(tree, alt.trees mapConserve (alt => typed(alt, mode, pt))) setType pt
5259-
)
5265+
context withinPatAlternative {
5266+
val trees1 = alt.trees mapConserve (alt => typed(alt, mode, pt))
5267+
val tpe = if (settings.isScala213) weakLub(trees1 map (_.tpe)) else pt
5268+
treeCopy.Alternative(tree, trees1) setType tpe
5269+
}
52605270
}
52615271
def typedStar(tree: Star) = {
52625272
if (!context.starPatterns && !isPastTyper)

src/reflect/scala/reflect/internal/tpe/GlbLubs.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ private[internal] trait GlbLubs {
219219
def sameWeakLubAsLub(tps: List[Type]) = tps match {
220220
case Nil => true
221221
case tp :: Nil => !typeHasAnnotations(tp)
222-
case tps => !(tps exists typeHasAnnotations) && !(tps forall isNumericValueType)
222+
case tps => !(tps exists typeHasAnnotations) && !shouldUseNumericLub(tps)
223223
}
224224

225225
/** If the arguments are all numeric value types, the numeric
@@ -231,14 +231,17 @@ private[internal] trait GlbLubs {
231231
def weakLub(tps: List[Type]): Type = (
232232
if (tps.isEmpty)
233233
NothingTpe
234-
else if (tps forall isNumericValueType)
234+
else if (shouldUseNumericLub(tps))
235235
numericLub(tps)
236236
else if (tps exists typeHasAnnotations)
237237
annotationsLub(lub(tps map (_.withoutAnnotations)), tps)
238238
else
239239
lub(tps)
240240
)
241241

242+
private def shouldUseNumericLub(tps: List[Type]) =
243+
tps forall (tp => isNumericValueType(tp.widen))
244+
242245
def numericLub(ts: List[Type]) =
243246
ts reduceLeft ((t1, t2) =>
244247
if (isNumericSubType(t1, t2)) t2

test/files/pos/alternative-lubs.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/alternative-lubs.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
sealed trait A
2+
sealed trait B extends A
3+
case class B1(i: Int) extends B
4+
case class B2(i: Int) extends B
5+
6+
object Test {
7+
val b1: B1 = B1(0)
8+
val b2: B2 = B2(0)
9+
10+
def takesB(b: B): Int = 10
11+
def takesB1(it: b1.type): Int = 11
12+
13+
def foo0(a: A) = a match {
14+
case b @ (`b1` | `b2`) => takesB(b)
15+
case b @ (_ : `b1`.type | _ : `b2`.type) => takesB(b)
16+
case b @ (_ : `b1`.type | _ : `b1`.type) => takesB1(b)
17+
case b @ (B1(_) | B2(_)) => takesB(b)
18+
case _ => 20
19+
}
20+
21+
// weak subtyping, fun...
22+
def foo1(a: Option[Any]) = (a : @unchecked) match { case Some(x @ (1 | 1)) => x }
23+
def foo2(a: Option[Any]) = (a : @unchecked) match { case Some(x @ (1 | 1L)) => x }
24+
25+
foo1(None) : Int
26+
foo2(None) : Long
27+
}

0 commit comments

Comments
 (0)