diff options
author | Olivier Blanvillain <olivier.blanvillain@gmail.com> | 2017-04-06 18:35:20 +0200 |
---|---|---|
committer | Olivier Blanvillain <olivier.blanvillain@gmail.com> | 2017-04-06 18:53:30 +0200 |
commit | 944e677f437c39d85280c388cab000b5490e4386 (patch) | |
tree | e83b50e31f8b3661624fc4ec9fb8c09bdbd0a450 | |
parent | c0ff8adcb4f44a0b01077495a7626d201f8100d5 (diff) | |
download | dotty-944e677f437c39d85280c388cab000b5490e4386.tar.gz dotty-944e677f437c39d85280c388cab000b5490e4386.tar.bz2 dotty-944e677f437c39d85280c388cab000b5490e4386.zip |
Decouple Product and pattern-matching
Product pattern use to:
- have a `<: Product` requirement
- compute the arity of a pattern by looking at `N` in a `ProductN` superclass.
This commit changes `<: Product`, instead we look for a `_1` member. The arity is determined by inspecting `_1` to `_N` members instead.
---
Here another attempt to formalize Dotty's pattern-matching (base on #1805). This covers the *Extractor Patterns* [section of the spec](https://www.scala-lang.org/files/archive/spec/2.12/08-pattern-matching.html#extractor-patterns). Dotty support 4 different extractor patterns: Boolean Pattern, Product Pattern, Seq Pattern and Name Based Pattern.
Boolean Pattern
- Extractor defines `def unapply(x: T): Boolean`
- Pattern-matching on exactly `0` patterns
Product Pattern
- Extractor defines `def unapply(x: T): U`
- `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U`
- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN`
Seq Pattern
- Extractor defines `def unapplySeq(x: T): U`
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
- `S <: Seq[V]`
- Pattern-matching on any number of pattern with types `V, V, ..., V`
Name Based Pattern
- Extractor defines `def unapply(x: T): U`
- `U` has (parameterless `def` or `val`) members `isEmpty: Boolean` and `get: S`
- If there is exactly `1` pattern, pattern-matching on `1` pattern with type `S`
- Otherwise fallback to Product Pattern on type `U`
In case of ambiguities, *Product Pattern* is preferred over *Name Based Pattern*.
4 files changed, 13 insertions, 18 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 337f48064..bc55ce638 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -10,6 +10,7 @@ import scala.collection.{ mutable, immutable } import PartialFunction._ import collection.mutable import util.common.alwaysZero +import typer.Applications object Definitions { @@ -847,17 +848,9 @@ class Definitions { } def isProductSubType(tp: Type)(implicit ctx: Context) = - (tp derivesFrom ProductType.symbol) && tp.baseClasses.exists(isProductClass) + Applications.extractorMemberType(tp, nme._1).exists - def productArity(tp: Type)(implicit ctx: Context) = - if (tp derivesFrom ProductType.symbol) - tp.baseClasses.find(isProductClass) match { - case Some(prod) => prod.typeParams.length - case None => -1 - } - else -1 - - /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN ? */ + /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ImplicitFunctionN? */ def isFunctionType(tp: Type)(implicit ctx: Context) = { val arity = functionArity(tp) val sym = tp.dealias.typeSymbol diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index dbc7666f7..519767389 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -233,7 +233,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { // next: MatchMonad[U] // returns MatchMonad[U] def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = { - val resultArity = defn.productArity(b.info) + val resultArity = productArity(b.info) if (isProductMatch(prev.tpe, resultArity)) { val nullCheck: Tree = prev.select(defn.Object_ne).appliedTo(Literal(Constant(null))) ifThenElseZero( diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 4e43e429b..24d43858d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -48,12 +48,12 @@ object Applications { } /** Does `tp` fit the "product match" conditions as an unapply result type - * for a pattern with `numArgs` subpatterns> - * This is the case of `tp` is a subtype of the Product<numArgs> class. + * for a pattern with `numArgs` subpatterns? + * This is the case of `tp` has members `_1` to `_N` where `N == numArgs`. */ def isProductMatch(tp: Type, numArgs: Int)(implicit ctx: Context) = - 0 <= numArgs && numArgs <= Definitions.MaxTupleArity && - tp.derivesFrom(defn.ProductNType(numArgs).typeSymbol) + numArgs > 0 && defn.isProductSubType(tp) && + productSelectorTypes(tp).size == numArgs /** Does `tp` fit the "get match" conditions as an unapply result type? * This is the case of `tp` has a `get` member as well as a @@ -68,6 +68,9 @@ object Applications { sels.takeWhile(_.exists).toList } + def productArity(tp: Type)(implicit ctx: Context) = + if (defn.isProductSubType(tp)) productSelectorTypes(tp).size else -1 + def productSelectors(tp: Type)(implicit ctx: Context): List[Symbol] = { val sels = for (n <- Iterator.from(0)) yield tp.member(nme.selectorName(n)).symbol sels.takeWhile(_.exists).toList diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2760ceba9..c86b15871 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -761,10 +761,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type) = { - val pclass = defn.ProductNType(params.length).symbol isFullyDefined(formal, ForceDegree.noBottom) && - formal.derivesFrom(pclass) && - formal.baseArgTypes(pclass).corresponds(params) { + defn.isProductSubType(formal) && + Applications.productSelectorTypes(formal).corresponds(params) { (argType, param) => param.tpt.isEmpty || argType <:< typedAheadType(param.tpt).tpe } |