summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Infer.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-08-06 19:34:49 +0000
committerPaul Phillips <paulp@improving.org>2011-08-06 19:34:49 +0000
commit990fa046e6bbe95d5def208b38fead77d2437f9f (patch)
treecaed0b83c9d4220f4ebe48ab844a8c41884df45d /src/compiler/scala/tools/nsc/typechecker/Infer.scala
parentead69ed24557ff42966a2bd5f71b6434ac5343b6 (diff)
downloadscala-990fa046e6bbe95d5def208b38fead77d2437f9f.tar.gz
scala-990fa046e6bbe95d5def208b38fead77d2437f9f.tar.bz2
scala-990fa046e6bbe95d5def208b38fead77d2437f9f.zip
Fixed bug in the disambiguation of f(foo='bar')...
Fixed bug in the disambiguation of f(foo='bar') style method calls in the presence of overloading, parameterization, and by-name arguments. Took the opportunity to clean things up a little bit. Closes SI-4592, review by rytz.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Infer.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala92
1 files changed, 61 insertions, 31 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 7305cef34f..495594da5e 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -755,7 +755,8 @@ trait Infer {
val argtpes1 = argtpes map {
case NamedType(name, tp) => // a named argument
var res = tp
- val pos = params.indexWhere(p => (p.name == name || deprecatedName(p) == Some(name)) && !p.isSynthetic)
+ val pos = params.indexWhere(p => paramMatchesName(p, name) && !p.isSynthetic)
+
if (pos == -1) {
if (positionalAllowed) { // treat assignment as positional argument
argPos(index) = index
@@ -1634,6 +1635,54 @@ trait Infer {
}
}
+ @inline private def wrapTypeError(expr: => Boolean): Boolean =
+ try expr
+ catch { case _: TypeError => false }
+
+ // Checks against the name of the parameter and also any @deprecatedName.
+ private def paramMatchesName(param: Symbol, name: Name) =
+ param.name == name || param.deprecatedParamName.exists(_ == name)
+
+ // Check the first parameter list the same way.
+ private def methodMatchesName(method: Symbol, name: Name) = method.paramss match {
+ case ps :: _ => ps exists (p => paramMatchesName(p, name))
+ case _ => false
+ }
+
+ private def resolveOverloadedMethod(argtpes: List[Type], eligible: List[Symbol]) = {
+ // If there are any foo=bar style arguments, and any of the overloaded
+ // methods has a parameter named `foo`, then only those methods are considered.
+ val namesOfArgs = argtpes collect { case NamedType(name, _) => name }
+ val namesMatch = (
+ if (namesOfArgs.isEmpty) Nil
+ else eligible filter { m =>
+ namesOfArgs forall { name =>
+ methodMatchesName(m, name)
+ }
+ }
+ )
+
+ if (namesMatch.nonEmpty) namesMatch
+ else if (eligible.isEmpty || eligible.tail.isEmpty) eligible
+ else eligible filter { alt =>
+ // for functional values, the `apply` method might be overloaded
+ val mtypes = followApply(alt.tpe) match {
+ case OverloadedType(_, alts) => alts map (_.tpe)
+ case t => List(t)
+ }
+ // Drop those that use a default; keep those that use vararg/tupling conversion.
+ mtypes exists (t =>
+ !t.typeSymbol.hasDefaultFlag && {
+ compareLengths(t.params, argtpes) < 0 || // tupling (*)
+ hasExactlyNumParams(t, argtpes.length) // same nb or vararg
+ }
+ )
+ // (*) more arguments than parameters, but still applicable: tupling conversion works.
+ // todo: should not return "false" when paramTypes = (Unit) no argument is given
+ // (tupling would work)
+ }
+ }
+
/** Assign <code>tree</code> the type of an alternative which is applicable
* to <code>argtpes</code>, and whose result type is compatible with `pt`.
* If several applicable alternatives exist, drop the alternatives which use
@@ -1656,40 +1705,21 @@ trait Infer {
debuglog("infer method alt "+ tree.symbol +" with alternatives "+
(alts map pre.memberType) +", argtpes = "+ argtpes +", pt = "+ pt)
- var allApplicable = alts filter (alt =>
- // TODO: this will need to be re-written once we substitute throwing exceptions
- // with generating error trees. We wrap this applicability in try/catch because of #4457.
- try {isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)} catch {case _: TypeError => false})
-
- //log("applicable: "+ (allApplicable map pre.memberType))
-
- if (varArgsOnly)
- allApplicable = allApplicable filter (alt => isVarArgsList(alt.tpe.params))
-
- // if there are multiple, drop those that use a default
- // (keep those that use vararg / tupling conversion)
- val applicable =
- if (allApplicable.lengthCompare(1) <= 0) allApplicable
- else allApplicable filter (alt => {
- val mtypes = followApply(alt.tpe) match {
- // for functional values, the `apply` method might be overloaded
- case OverloadedType(_, alts) => alts map (_.tpe)
- case t => List(t)
- }
- mtypes exists (t =>
- compareLengths(t.params, argtpes) < 0 || // tupling (*)
- hasExactlyNumParams(t, argtpes.length) // same nb or vararg
- )
- // (*) more arguments than parameters, but still applicable: tuplig conversion works.
- // todo: should not return "false" when paramTypes = (Unit) no argument is given
- // (tupling would work)
- })
+ val applicable = resolveOverloadedMethod(argtpes, {
+ alts filter { alt =>
+ // TODO: this will need to be re-written once we substitute throwing exceptions
+ // with generating error trees. We wrap this applicability in try/catch because of #4457.
+ wrapTypeError(isApplicable(undetparams, followApply(pre.memberType(alt)), argtpes, pt)) &&
+ (!varArgsOnly || isVarArgsList(alt.tpe.params))
+ }
+ })
- def improves(sym1: Symbol, sym2: Symbol) =
-// util.trace("improve "+sym1+sym1.locationString+" on "+sym2+sym2.locationString)(
+ def improves(sym1: Symbol, sym2: Symbol) = {
+ // util.trace("improve "+sym1+sym1.locationString+" on "+sym2+sym2.locationString)
sym2 == NoSymbol || sym2.isError || sym2.hasAnnotation(BridgeClass) ||
isStrictlyMoreSpecific(followApply(pre.memberType(sym1)),
followApply(pre.memberType(sym2)), sym1, sym2)
+ }
val best = ((NoSymbol: Symbol) /: applicable) ((best, alt) =>
if (improves(alt, best)) alt else best)