aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/Typer.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-11-05 13:28:35 +0100
committerMartin Odersky <odersky@gmail.com>2016-11-24 16:54:24 +0100
commitd8f5c6c5b57b0688a2024fedc26104540234739c (patch)
tree06afc63002cc84335d9146fddb9748f0efc4d732 /compiler/src/dotty/tools/dotc/typer/Typer.scala
parentb21f954604d5785515d26238e2c43dc332f9fb5b (diff)
downloaddotty-d8f5c6c5b57b0688a2024fedc26104540234739c.tar.gz
dotty-d8f5c6c5b57b0688a2024fedc26104540234739c.tar.bz2
dotty-d8f5c6c5b57b0688a2024fedc26104540234739c.zip
Avoid inserting multiple .apply's.
This can lead to stackoverflow, as i1639.scala shows. Fixes #1639.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/Typer.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala32
1 files changed, 23 insertions, 9 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index ccc74cfff..f5a4e36a2 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1585,18 +1585,34 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
* `fallBack`.
*
* 1st strategy: Try to insert `.apply` so that the result conforms to prototype `pt`.
+ * This strategy is not tried if the prototype represents already
+ * another `.apply` or `.apply()` selection.
* 2nd strategy: If tree is a select `qual.name`, try to insert an implicit conversion
* around the qualifier part `qual` so that the result conforms to the expected type
* with wildcard result type.
*/
- def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: (Tree, TyperState) => Tree)(implicit ctx: Context): Tree =
- tryEither { implicit ctx =>
+ def tryInsertApplyOrImplicit(tree: Tree, pt: ProtoType)(fallBack: => Tree)(implicit ctx: Context): Tree = {
+
+ /** Is `pt` a prototype of an `apply` selection, or a parameterless function yielding one? */
+ def isApplyProto(pt: Type): Boolean = pt match {
+ case pt: SelectionProto => pt.name == nme.apply
+ case pt: FunProto => pt.args.isEmpty && isApplyProto(pt.resultType)
+ case pt: IgnoredProto => isApplyProto(pt.ignored)
+ case _ => false
+ }
+
+ def tryApply(implicit ctx: Context) = {
val sel = typedSelect(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
if (sel.tpe.isError) sel else adapt(sel, pt)
- } { (failedTree, failedState) =>
- tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack(failedTree, failedState))
}
+ def tryImplicit =
+ tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack)
+
+ if (isApplyProto(pt)) tryImplicit
+ else tryEither(tryApply(_))((_, _) => tryImplicit)
+ }
+
/** If this tree is a select node `qual.name`, try to insert an implicit conversion
* `c` around `qual` so that `c(qual).name` conforms to `pt`.
*/
@@ -1688,7 +1704,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil
pt match {
case pt: FunProto =>
- tryInsertApplyOrImplicit(tree, pt)((_, _) => noMatches)
+ tryInsertApplyOrImplicit(tree, pt)(noMatches)
case _ =>
if (altDenots exists (_.info.paramTypess == ListOfNil))
typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt)
@@ -1727,7 +1743,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case Apply(_, _) => " more"
case _ => ""
}
- (_, _) => errorTree(tree, em"$methodStr does not take$more parameters")
+ errorTree(tree, em"$methodStr does not take$more parameters")
}
}
@@ -1946,9 +1962,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case pt: FunProto =>
adaptToArgs(wtp, pt)
case pt: PolyProto =>
- tryInsertApplyOrImplicit(tree, pt) {
- (_, _) => tree // error will be reported in typedTypeApply
- }
+ tryInsertApplyOrImplicit(tree, pt)(tree) // error will be reported in typedTypeApply
case _ =>
if (ctx.mode is Mode.Type) adaptType(tree.tpe)
else adaptNoArgs(wtp)