aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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