diff options
author | Martin Odersky <odersky@gmail.com> | 2016-11-05 13:28:35 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2016-11-24 16:54:24 +0100 |
commit | d8f5c6c5b57b0688a2024fedc26104540234739c (patch) | |
tree | 06afc63002cc84335d9146fddb9748f0efc4d732 /compiler/src/dotty/tools/dotc/typer/Typer.scala | |
parent | b21f954604d5785515d26238e2c43dc332f9fb5b (diff) | |
download | dotty-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.scala | 32 |
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) |