aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer/Applications.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2013-11-06 18:39:00 +0100
committerMartin Odersky <odersky@gmail.com>2013-11-06 18:39:00 +0100
commite401bac707d6d56d551db5556a7f58861ea3ae16 (patch)
treebdb6f5f25a9f339914858ff87143064a694eb9fc /src/dotty/tools/dotc/typer/Applications.scala
parentc1abb572fa3adaeef3f66c79ac8946d15c7aeca7 (diff)
downloaddotty-e401bac707d6d56d551db5556a7f58861ea3ae16.tar.gz
dotty-e401bac707d6d56d551db5556a7f58861ea3ae16.tar.bz2
dotty-e401bac707d6d56d551db5556a7f58861ea3ae16.zip
Fixing several type checking problems.
1. Being more precise what gets forced when. 2. stopping repeated evaluation when inserting an implicit methods to make arguments match. Previously the argument got re-evaluated which could lead to misleading errors (e..g missing parameter type if argument was a closure), and could also lead to exponential checking blowup. We now re-use the old argument but in its unadapted form. We do this with a tight coupling between an Application node and a FunProto node - typedArg in the application node forwards to new caching functionality in the FunProto node. It would probably be better overall to merge the two abstractions. FunProto = Application? 3. Various fixes to pattern matching.
Diffstat (limited to 'src/dotty/tools/dotc/typer/Applications.scala')
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala87
1 files changed, 55 insertions, 32 deletions
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 794c7e752..f4936cff0 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -429,9 +429,9 @@ trait Applications extends Compatibility { self: Typer =>
}
/** Subclass of Application for type checking an Apply node with untyped arguments. */
- class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[untpd.Tree], resultType: Type)(implicit ctx: Context)
- extends TypedApply(app, fun, methRef, args, resultType) {
- def typedArg(arg: untpd.Tree, formal: Type): TypedArg = typed(arg, formal)
+ class ApplyToUntyped(app: untpd.Apply, fun: Tree, methRef: TermRef, proto: FunProto, resultType: Type)(implicit ctx: Context)
+ extends TypedApply(app, fun, methRef, proto.args, resultType) {
+ def typedArg(arg: untpd.Tree, formal: Type): TypedArg = proto.typedArg(arg, formal)
def treeToArg(arg: Tree): untpd.Tree = untpd.TypedSplice(arg)
}
@@ -462,7 +462,7 @@ trait Applications extends Compatibility { self: Typer =>
tryEither { implicit ctx =>
val app =
if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt)
- else new ApplyToUntyped(tree, fun1, funRef, tree.args, pt)
+ else new ApplyToUntyped(tree, fun1, funRef, proto, pt)
val result = app.result
ConstFold(result) orElse result
} { failed => fun1 match {
@@ -530,6 +530,26 @@ trait Applications extends Compatibility { self: Typer =>
def typedUnApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = track("typedUnApply") {
val Apply(qual, args) = tree
+ 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 = {
+ val dummyArg = untpd.TypedSplice(dummyTreeOfType(WildcardType))
+ val unappProto = FunProto(dummyArg :: Nil, pt, this)
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
+ } {
+ s =>
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
+ } {
+ _ => notAnExtractor(s.value)
+ }
+ }
+ }
+
+ def fromScala2x = unapply.symbol.exists && (unapply.symbol.owner is Scala2x)
+
def unapplyArgs(unapplyResult: Type)(implicit ctx: Context): List[Type] = {
def recur(tp: Type): List[Type] = {
def extractorMemberType(name: Name) = {
@@ -547,8 +567,11 @@ trait Applications extends Compatibility { self: Typer =>
sels.takeWhile(_.exists).toList
}
def seqSelector = defn.RepeatedParamType.appliedTo(tp.elemType :: Nil)
-
- if (tp derivesFrom defn.ProductClass) productSelectors
+ def optionSelectors(tp: Type): List[Type] =
+ if (defn.isTupleType(tp)) tp.typeArgs else tp :: Nil
+ if (fromScala2x && (tp isRef defn.OptionClass) && tp.typeArgs.length == 1)
+ optionSelectors(tp.typeArgs.head)
+ else if (tp derivesFrom defn.ProductClass) productSelectors
else if (tp derivesFrom defn.SeqClass) seqSelector :: Nil
else if (tp isRef defn.BooleanClass) Nil
else if (extractorMemberType(nme.isDefined).exists &&
@@ -562,43 +585,42 @@ trait Applications extends Compatibility { self: Typer =>
recur(unapplyResult)
}
- 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 = {
- val dummyArg = untpd.TypedSplice(dummyTreeOfType(WildcardType))
- val unappProto = FunProto(dummyArg :: Nil, pt, this)
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
- } {
- s =>
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
- } {
- _ => notAnExtractor(s.value)
- }
- }
- }
-
unapply.tpe.widen match {
case mt: MethodType if !mt.isDependent =>
val unapplyArgType = mt.paramTypes.head
+ println(s"unapp arg tpe = ${unapplyArgType.show}, pt = ${pt.show}")
val ownType =
if (pt <:< unapplyArgType) {
- assert(isFullyDefined(unapplyArgType))
+ fullyDefinedType(unapplyArgType, "extractor argument", tree.pos)
+ println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}")
pt
}
- else if (unapplyArgType <:< widenForSelector(pt))
+ else if (unapplyArgType <:< widenForSelector(pt)) {
ctx.maximizeType(unapplyArgType) match {
- case None => unapplyArgType
case Some(tvar) =>
- errorType(
+ def msg =
s"""There is no best instantiation of pattern type ${unapplyArgType.show}
|that makes it a subtype of selector type ${pt.show}.
- |Non-variant type variable ${tvar.origin.show} cannot be uniquely instantiated.""".stripMargin,
- tree.pos)
+ |Non-variant type variable ${tvar.origin.show} cannot be uniquely instantiated.""".stripMargin
+ if (fromScala2x) {
+ // We can't issue an error here, because in Scala 2, ::[B] is invariant
+ // whereas List[+T] is covariant. According to the strict rule, a pattern
+ // match of a List[C] against a case x :: xs is illegal, because
+ // B cannot be uniquely instantiated. Of course :: should have been
+ // covariant in the first place, but in the Scala libraries it isn't.
+ // So for now we allow these kinds of patterns, even though they
+ // can open unsoundness holes. See SI-7952 for an example of the hole this opens.
+ if (ctx.settings.verbose.value) ctx.warning(msg, tree.pos)
+ }
+ else {
+ println(s" ${unapply.symbol.owner} ${unapply.symbol.owner is Scala2x}")
+ ctx.error(msg, tree.pos)
+ }
+ case _ =>
}
- else errorType(
+ println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}")
+ unapplyArgType
+ } else errorType(
s"Pattern type ${unapplyArgType.show} is neither a subtype nor a supertype of selector type ${pt.show}",
tree.pos)
@@ -608,12 +630,13 @@ trait Applications extends Compatibility { self: Typer =>
case _ => args
}
if (argTypes.length != bunchedArgs.length) {
- ctx.error(s"wrong number of argument patterns for ${err.patternConstrStr(unapply)}", tree.pos)
+ ctx.error(i"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
argTypes = argTypes.take(args.length) ++
List.fill(argTypes.length - args.length)(WildcardType)
}
val typedArgs = (bunchedArgs, argTypes).zipped map (typed(_, _))
val result = cpy.UnApply(tree, unapply, typedArgs) withType ownType
+ println(s"typedargs = $typedArgs")
if ((ownType eq pt) || ownType.isError) result
else Typed(result, TypeTree(ownType))
case tp =>