aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-12-18 19:29:02 +0100
committerFelix Mulder <felix.mulder@gmail.com>2016-12-20 18:33:44 +0100
commit7c5e36b80e111d17910dbf122c02a458377656d1 (patch)
tree210ba0cde3e81925aab1af83ab1353c6606a1331 /compiler/src/dotty/tools/dotc/typer
parent9c0df5aa3af3906913f6507e04078c32dd2a4742 (diff)
downloaddotty-7c5e36b80e111d17910dbf122c02a458377656d1.tar.gz
dotty-7c5e36b80e111d17910dbf122c02a458377656d1.tar.bz2
dotty-7c5e36b80e111d17910dbf122c02a458377656d1.zip
More lenient handling of mixed parameterless and nullary methods
When faced with a denotation that combines parameterless and nullary method definitions (toString is a common example), ignore any redundant () applications.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Applications.scala26
-rw-r--r--compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala16
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Typer.scala23
3 files changed, 48 insertions, 17 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala
index 8a18e63c0..42c24ffb7 100644
--- a/compiler/src/dotty/tools/dotc/typer/Applications.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala
@@ -657,18 +657,20 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err)
case TryDynamicCallType => typedDynamicApply(tree, pt)
case _ =>
- tryEither {
- implicit ctx => simpleApply(fun1, proto)
- } {
- (failedVal, failedState) =>
- def fail = { failedState.commit(); failedVal }
- // Try once with original prototype and once (if different) with tupled one.
- // The reason we need to try both is that the decision whether to use tupled
- // or not was already taken but might have to be revised when an implicit
- // is inserted on the qualifier.
- tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
- if (proto eq originalProto) fail
- else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
+ if (originalProto.isDropped) fun1
+ else
+ tryEither {
+ implicit ctx => simpleApply(fun1, proto)
+ } {
+ (failedVal, failedState) =>
+ def fail = { failedState.commit(); failedVal }
+ // Try once with original prototype and once (if different) with tupled one.
+ // The reason we need to try both is that the decision whether to use tupled
+ // or not was already taken but might have to be revised when an implicit
+ // is inserted on the qualifier.
+ tryWithImplicitOnQualifier(fun1, originalProto).getOrElse(
+ if (proto eq originalProto) fail
+ else tryWithImplicitOnQualifier(fun1, proto).getOrElse(fail))
}
}
}
diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
index ed6b95c3b..3c318a6af 100644
--- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -250,6 +250,22 @@ object ProtoTypes {
/** Somebody called the `tupled` method of this prototype */
def isTupled: Boolean = myTupled.isInstanceOf[FunProto]
+ /** If true, the application of this prototype was canceled. */
+ private var toDrop: Boolean = false
+
+ /** Cancel the application of this prototype. This can happen for a nullary
+ * application `f()` if `f` refers to a symbol that exists both in parameterless
+ * form `def f` and nullary method form `def f()`. A common example for such
+ * a method is `toString`. If in that case the type in the denotation is
+ * parameterless, we compensate by dropping the application.
+ */
+ def markAsDropped() = {
+ assert(args.isEmpty)
+ toDrop = true
+ }
+
+ def isDropped: Boolean = toDrop
+
override def toString = s"FunProto(${args mkString ","} => $resultType)"
def map(tm: TypeMap)(implicit ctx: Context): FunProto =
diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala
index 07a27a498..d054fe803 100644
--- a/compiler/src/dotty/tools/dotc/typer/Typer.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala
@@ -1640,13 +1640,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case _ => false
}
- /** Add apply node or implicit conversions. Two strategies are tried, and the first
- * that is successful is picked. If neither of the strategies are successful, continues with
- * `fallBack`.
+ /** Potentially add apply node or implicit conversions. Before trying either,
+ * if the function is applied to an empty parameter list (), we try
+ *
+ * 0th strategy: If `tree` overrides a nullary method, mark the prototype
+ * so that the argument is dropped and return `tree` itself.
+ *
+ * After that, two strategies are tried, and the firs that is successful is picked.
+ * If neither of the strategies are successful, continues with`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.
@@ -1661,8 +1667,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def tryImplicit =
tryInsertImplicitOnQualifier(tree, pt).getOrElse(fallBack)
- if (isApplyProto(pt)) tryImplicit
- else tryEither(tryApply(_))((_, _) => tryImplicit)
+ pt match {
+ case pt @ FunProto(Nil, _, _)
+ if tree.symbol.allOverriddenSymbols.exists(_.info.isNullaryMethod) =>
+ pt.markAsDropped()
+ tree
+ case _ =>
+ if (isApplyProto(pt)) tryImplicit
+ else tryEither(tryApply(_))((_, _) => tryImplicit)
+ }
}
/** If this tree is a select node `qual.name`, try to insert an implicit conversion