diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ac3245c37db3..0709d9b59238 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1044,7 +1044,13 @@ object Types { case _ => NoType } - /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */ + /** If this is a repeated type, its element type, otherwise the type itself */ + def repeatedToSingle(implicit ctx: Context): Type = this match { + case tp @ ExprType(tp1) => tp.derivedExprType(tp1.repeatedToSingle) + case _ => if (isRepeatedParam) this.argTypesHi.head else this + } + + /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */ def notApplied: Type = this // ----- Normalizing typerefs over refined types ---------------------------- diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 72df0a9f598b..867fc399a900 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1056,12 +1056,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => */ def isAsSpecific(alt1: TermRef, tp1: Type, alt2: TermRef, tp2: Type): Boolean = ctx.traceIndented(i"isAsSpecific $tp1 $tp2", overload) { tp1 match { case tp1: MethodType => // (1) - def repeatedToSingle(tp: Type): Type = tp match { - case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1)) - case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp - } val formals1 = - if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos map repeatedToSingle + if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos.map(_.repeatedToSingle) else tp1.paramInfos isApplicable(alt2, formals1, WildcardType) || tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType] diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0c08fcf14770..433d3bf711c1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -724,41 +724,70 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit */ var fnBody = tree.body + /** A map from parameter names to unique positions where the parameter + * appears in the argument list of an application. + */ + var paramIndex = Map[Name, Int]() + + /** If parameter `param` appears exactly once as an argument in `args`, + * the singleton list consisting of its position in `args`, otherwise `Nil`. + */ + def paramIndices(param: untpd.ValDef, args: List[untpd.Tree], start: Int): List[Int] = args match { + case arg :: args1 => + if (refersTo(arg, param)) + if (paramIndices(param, args1, start + 1).isEmpty) start :: Nil + else Nil + else paramIndices(param, args1, start + 1) + case _ => Nil + } + /** If function is of the form - * (x1, ..., xN) => f(x1, ..., XN) - * the type of `f`, otherwise NoType. (updates `fnBody` as a side effect). + * (x1, ..., xN) => f(... x1, ..., XN, ...) + * where each `xi` occurs exactly once in the argument list of `f` (in + * any order), the type of `f`, otherwise NoType. + * Updates `fnBody` and `paramIndex` as a side effect. + * @post: If result exists, `paramIndex` is defined for the name of + * every parameter in `params`. */ def calleeType: Type = fnBody match { - case Apply(expr, args) if (args corresponds params)(refersTo) => - expr match { - case untpd.TypedSplice(expr1) => - expr1.tpe - case _ => - val protoArgs = args map (_ withType WildcardType) - val callProto = FunProto(protoArgs, WildcardType, this) - val expr1 = typedExpr(expr, callProto) - fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) - expr1.tpe - } + case Apply(expr, args) => + paramIndex = { + for (param <- params; idx <- paramIndices(param, args, 0)) + yield param.name -> idx + }.toMap + if (paramIndex.size == params.length) + expr match { + case untpd.TypedSplice(expr1) => + expr1.tpe + case _ => + val protoArgs = args map (_ withType WildcardType) + val callProto = FunProto(protoArgs, WildcardType, this) + val expr1 = typedExpr(expr, callProto) + fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) + expr1.tpe + } + else NoType case _ => NoType } /** Two attempts: First, if expected type is fully defined pick this one. * Second, if function is of the form - * (x1, ..., xN) => f(x1, ..., XN) - * and f has a method type MT, pick the corresponding parameter type in MT, - * if this one is fully defined. + * (x1, ..., xN) => f(... x1, ..., XN, ...) + * where each `xi` occurs exactly once in the argument list of `f` (in + * any order), and f has a method type MT, pick the corresponding parameter + * type in MT, if this one is fully defined. * If both attempts fail, issue a "missing parameter type" error. */ def inferredParamType(param: untpd.ValDef, formal: Type): Type = { if (isFullyDefined(formal, ForceDegree.noBottom)) return formal calleeType.widen match { case mtpe: MethodType => - val pos = params indexWhere (_.name == param.name) + val pos = paramIndex(param.name) if (pos < mtpe.paramInfos.length) { val ptype = mtpe.paramInfos(pos) - if (isFullyDefined(ptype, ForceDegree.noBottom)) return ptype + if (isFullyDefined(ptype, ForceDegree.noBottom) && !ptype.isRepeatedParam) + return ptype } case _ => } diff --git a/tests/pos/i2570.scala b/tests/pos/i2570.scala new file mode 100644 index 000000000000..1fa9a07567e3 --- /dev/null +++ b/tests/pos/i2570.scala @@ -0,0 +1,29 @@ +object Test { + + def repeat(s: String, i: Int, j: Int = 22) = s * i + + val f1 = repeat("abc", _) + val f2: Int => String = f1 + val f3 = repeat(_, 3) + val f4: String => String = f3 + val f5 = repeat("abc", _, _) + val f6: (Int, Int) => String = f5 + val f7 = repeat(_, 11, _) + val f8: (String, Int) => String = f7 + + def sum(x: Int, y: => Int) = x + y + + val g1 = sum(2, _) + val g2: (=> Int) => Int = g1 + + val h0: ((Int, => Int) => Int) = sum + + def sum2(x: Int, ys: Int*) = (x /: ys)(_ + _) + val h1: ((Int, Seq[Int]) => Int) = sum2 + +// Not yet: +// val h1 = repeat +// val h2: (String, Int, Int) = h1 +// val h3 = sum +// val h4: (Int, => Int) = h3 +}