aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-12-19 08:40:59 +0100
committerMartin Odersky <odersky@gmail.com>2013-12-19 08:40:59 +0100
commit6be96724899f9c34db2e6c2617534dc9c4a15528 (patch)
treee396c405b43c6fc6f4fc8a124cb19acefb4dc755
parent56d8347bcd606443dde576480325677a89f5c809 (diff)
downloaddotty-6be96724899f9c34db2e6c2617534dc9c4a15528.tar.gz
dotty-6be96724899f9c34db2e6c2617534dc9c4a15528.tar.bz2
dotty-6be96724899f9c34db2e6c2617534dc9c4a15528.zip
Following type aliases when pattern matching.
Faced with a pattern like Apply(x, xs) we first look for an Apply object which is an extractor. If this fails, we now interprete Apply as a type. If it is a type alias which points to a class type that has a companion module, we then try that companion module as an extractor. Scala 2.x does ot that way, and it's used widely within dotty itself. Think tpd.Apply as the found object, Trees.Apply as the extractor. Also, added a fix to normalization which made normalization go deep into a method type.
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala39
-rw-r--r--src/dotty/tools/dotc/typer/Implicits.scala2
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala52
3 files changed, 78 insertions, 15 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 104fe0555..4202e05e1 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -333,7 +333,7 @@ trait Applications extends Compatibility { self: Typer =>
/** Subclass of Application for applicability tests with trees as arguments. */
class ApplicableToTrees(methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context)
extends TestApplication(methRef, methRef, args, resultType) {
- def argType(arg: Tree): Type = normalize(arg.tpe)
+ def argType(arg: Tree): Type = normalize(arg.tpe, NoType)
def treeToArg(arg: Tree): Tree = arg
def isVarArg(arg: Tree): Boolean = tpd.isWildcardStarArg(arg)
}
@@ -546,7 +546,31 @@ trait Applications extends Compatibility { self: Typer =>
def notAnExtractor(tree: Tree) =
errorTree(tree, s"${qual.show} cannot be used as an extractor in a pattern because it lacks an unapply or unapplySeq method")
- val unapply = {
+ /** If this is a term ref tree, try to typecheck with its type name.
+ * If this refers to a type alias, follow the alias, and if
+ * one finds a class, reference the class companion module.
+ */
+ def followTypeAlias(tree: untpd.Tree): untpd.Tree = {
+ tree match {
+ case tree: untpd.RefTree =>
+ val ttree = typedType(tree.withName(tree.name.toTypeName))
+ ttree.tpe match {
+ case alias: TypeRef if alias.symbol.isAliasType =>
+ companionRef(alias) match {
+ case companion: TermRef => return untpd.ref(companion)
+ case _ =>
+ }
+ case _ =>
+ }
+ case _ =>
+ }
+ untpd.EmptyTree
+ }
+
+ /** A typed qual.unappy or qual.unappySeq tree, if this typechecks.
+ * Otherwise fallBack with (maltyped) qual.unapply as argument
+ */
+ def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = {
val unappProto = new UnapplyFunProto(this)
tryEither {
implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
@@ -555,11 +579,20 @@ trait Applications extends Compatibility { self: Typer =>
tryEither {
implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
} {
- (_, _) => notAnExtractor(sel)
+ (_, _) => fallBack(sel)
}
}
}
+ /** Produce a typed qual.unappy or qual.unappySeq tree, or
+ * else if this fails follow a type alias and try again.
+ */
+ val unapply = trySelectUnapply(qual) { sel =>
+ val qual1 = followTypeAlias(qual)
+ if (qual1.isEmpty) notAnExtractor(sel)
+ else trySelectUnapply(qual1)(_ => notAnExtractor(sel))
+ }
+
def fromScala2x = unapply.symbol.exists && (unapply.symbol.owner is Scala2x)
def unapplyArgs(unapplyResult: Type)(implicit ctx: Context): List[Type] = {
diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala
index a6cbd58d1..c236f8335 100644
--- a/src/dotty/tools/dotc/typer/Implicits.scala
+++ b/src/dotty/tools/dotc/typer/Implicits.scala
@@ -40,7 +40,7 @@ object Implicits {
/** Return those references in `refs` that are compatible with type `pt`. */
protected def filterMatching(pt: Type)(implicit ctx: Context): List[TermRef] = track("filterMatching") {
- def refMatches(ref: TermRef)(implicit ctx: Context) = isCompatible(normalize(ref), pt)
+ def refMatches(ref: TermRef)(implicit ctx: Context) = isCompatible(normalize(ref, pt), pt)
refs filter (refMatches(_)(ctx.fresh.withExploreTyperState)) // create a defensive copy of ctx to avoid constraint pollution
}
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 80fd9de90..92467c0e2 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -35,7 +35,7 @@ object Inferencing {
/** Test compatibility after normalization in a fresh typerstate */
def normalizedCompatible(tp: Type, pt: Type)(implicit ctx: Context) = {
val nestedCtx = ctx.fresh.withExploreTyperState
- isCompatible(normalize(tp)(nestedCtx), pt)(nestedCtx)
+ isCompatible(normalize(tp, pt)(nestedCtx), pt)(nestedCtx)
}
}
@@ -78,12 +78,14 @@ object Inferencing {
/** A prototype for selections in pattern constructors */
class UnapplySelectionProto(name: Name) extends SelectionProto(name, WildcardType)
+ trait ApplyingProto extends ProtoType
+
/** A prototype for expressions that appear in function position
*
* [](args): resultType
*/
case class FunProto(args: List[untpd.Tree], override val resultType: Type, typer: Typer)(implicit ctx: Context)
- extends UncachedGroundType with ProtoType {
+ extends UncachedGroundType with ApplyingProto {
private var myTypedArgs: List[Tree] = Nil
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
@@ -127,9 +129,14 @@ object Inferencing {
* []: argType => resultType
*/
case class ViewProto(argType: Type, override val resultType: Type)(implicit ctx: Context)
- extends CachedGroundType with ProtoType {
- def isMatchedBy(tp: Type)(implicit ctx: Context) =
+ extends CachedGroundType with ApplyingProto {
+ // def lookingForInfo = resultType match {
+ // case rt: SelectionProto => rt.name.toString == "info"
+ // case _ => false
+ // }
+ def isMatchedBy(tp: Type)(implicit ctx: Context) = /*ctx.conditionalTraceIndented(lookingForInfo, i"?.info isMatchedBy $tp ${tp.getClass}")*/ {
ctx.typer.isApplicable(tp, argType :: Nil, resultType)
+ }
override def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
AndType(argType, resultType).namedPartsWith(p) // this is more efficient than oring two namedParts sets
override def computeHash = doHash(argType, resultType)
@@ -158,12 +165,12 @@ object Inferencing {
* - converts non-dependent method types to the corresponding function types
* - dereferences parameterless method types
*/
- def normalize(tp: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
+ def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
tp.widenSingleton match {
- case pt: PolyType => normalize(constrained(pt).resultType)
- case mt: MethodType if !mt.isDependent =>
+ case pt: PolyType => normalize(constrained(pt).resultType, pt)
+ case mt: MethodType if !mt.isDependent /*&& !pt.isInstanceOf[ApplyingProto]*/ =>
if (mt.isImplicit) mt.resultType
- else defn.FunctionType(mt.paramTypes, mt.resultType)
+ else defn.FunctionType(mt.paramTypes, normalize(mt.resultType, pt))
case et: ExprType => et.resultType
case _ => tp
}
@@ -213,10 +220,31 @@ object Inferencing {
/** Recursively widen and also follow type declarations and type aliases. */
def widenForMatchSelector(tp: Type)(implicit ctx: Context): Type = tp.widen match {
- case tp: TypeRef if !tp.symbol.isClass => widenForMatchSelector(tp.bounds.hi)
+ case tp: TypeRef if !tp.symbol.isClass => widenForMatchSelector(tp.info.bounds.hi)
case tp => tp
}
+ /** Following type aliases and stripping refinements and annotations, if one arrives at a
+ * class type reference where the class has a companion module, a reference to
+ * that companion module. Otherwise NoType
+ */
+ def companionRef(tp: Type)(implicit ctx: Context): Type = tp.dealias match {
+ case tp: TypeRef if tp.symbol.isClass =>
+ val companion = tp.classSymbol.companionModule
+ if (companion.exists)
+ companion.valRef.asSeenFrom(tp.prefix, companion.symbol.owner)
+ else
+ NoType
+ case tp: TypeVar =>
+ companionRef(tp.underlying)
+ case tp: AnnotatedType =>
+ companionRef(tp.underlying)
+ case tp: RefinedType =>
+ companionRef(tp.underlying)
+ case _ =>
+ NoType
+ }
+
/** Check that type arguments `args` conform to corresponding bounds in `poly` */
def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit =
for ((arg, bounds) <- args zip poly.paramBounds) {
@@ -239,8 +267,10 @@ object Inferencing {
case tp: TypeRef if tp.symbol.isClass =>
checkStable(tp.prefix, pos)
tp
- case _: TypeVar | _: AnnotatedType =>
- checkClassTypeWithStablePrefix(tp.asInstanceOf[TypeProxy].underlying, pos)
+ case tp: TypeVar =>
+ checkClassTypeWithStablePrefix(tp.underlying, pos)
+ case tp: AnnotatedType =>
+ checkClassTypeWithStablePrefix(tp.underlying, pos)
case _ =>
ctx.error(i"$tp is not a class type", pos)
defn.ObjectClass.typeRef