diff options
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 39 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Implicits.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 52 |
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 |