Skip to content

Commit 96cabbb

Browse files
committed
Fix #3252: Change logic in adaptNoArgs
adaptNoArgs would do an implicit argument search only if the result type conformed to the expected type. #3252 shows that this needs to be revised.
1 parent a516a4e commit 96cabbb

File tree

2 files changed

+72
-60
lines changed

2 files changed

+72
-60
lines changed

compiler/src/dotty/tools/dotc/typer/Typer.scala

+69-60
Original file line numberDiff line numberDiff line change
@@ -1994,68 +1994,76 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19941994
}
19951995
}
19961996

1997-
def adaptNoArgs(wtp: Type): Tree = wtp match {
1998-
case wtp: ExprType =>
1999-
adaptInterpolated(tree.withType(wtp.resultType), pt)
2000-
case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) =>
2001-
val tvarsToInstantiate = tvarsInParams(tree)
2002-
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
2003-
val constr = ctx.typerState.constraint
2004-
def addImplicitArgs(implicit ctx: Context) = {
2005-
val errors = new mutable.ListBuffer[() => String]
2006-
def implicitArgError(msg: => String) = {
2007-
errors += (() => msg)
2008-
EmptyTree
2009-
}
2010-
def issueErrors() = {
2011-
for (err <- errors) ctx.error(err(), tree.pos.endPos)
2012-
tree.withType(wtp.resultType)
2013-
}
2014-
val args = (wtp.paramNames, wtp.paramInfos).zipped map { (pname, formal) =>
2015-
def implicitArgError(msg: String => String) =
2016-
errors += (() => msg(em"parameter $pname of $methodStr"))
2017-
if (errors.nonEmpty) EmptyTree
2018-
else inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
2019-
}
2020-
if (errors.nonEmpty) {
2021-
// If there are several arguments, some arguments might already
2022-
// have influenced the context, binding variables, but later ones
2023-
// might fail. In that case the constraint needs to be reset.
2024-
ctx.typerState.constraint = constr
2025-
2026-
// If method has default params, fall back to regular application
2027-
// where all inferred implicits are passed as named args.
2028-
if (tree.symbol.hasDefaultParams) {
2029-
val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) =>
2030-
arg match {
2031-
case EmptyTree => Nil
2032-
case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
2033-
}
2034-
}
2035-
tryEither { implicit ctx =>
2036-
typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt)
2037-
} { (_, _) =>
2038-
issueErrors()
2039-
}
2040-
} else issueErrors()
2041-
}
2042-
else adapt(tpd.Apply(tree, args), pt)
1997+
def adaptImplicitMethod(wtp: ImplicitMethodType): Tree = {
1998+
val tvarsToInstantiate = tvarsInParams(tree)
1999+
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
2000+
val constr = ctx.typerState.constraint
2001+
def addImplicitArgs(implicit ctx: Context) = {
2002+
val errors = new mutable.ListBuffer[() => String]
2003+
def implicitArgError(msg: => String) = {
2004+
errors += (() => msg)
2005+
EmptyTree
2006+
}
2007+
def issueErrors() = {
2008+
for (err <- errors) ctx.error(err(), tree.pos.endPos)
2009+
tree.withType(wtp.resultType)
20432010
}
2044-
addImplicitArgs(argCtx(tree))
2045-
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
2046-
// Follow proxies and approximate type paramrefs by their upper bound
2047-
// in the current constraint in order to figure out robustly
2048-
// whether an expected type is some sort of function type.
2049-
def underlyingApplied(tp: Type): Type = tp.stripTypeVar match {
2050-
case tp: RefinedType => tp
2051-
case tp: AppliedType => tp
2052-
case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi)
2053-
case tp: TypeProxy => underlyingApplied(tp.superType)
2054-
case _ => tp
2011+
val args = (wtp.paramNames, wtp.paramInfos).zipped map { (pname, formal) =>
2012+
def implicitArgError(msg: String => String) =
2013+
errors += (() => msg(em"parameter $pname of $methodStr"))
2014+
if (errors.nonEmpty) EmptyTree
2015+
else inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
20552016
}
2056-
val ptNorm = underlyingApplied(pt)
2017+
if (errors.nonEmpty) {
2018+
// If there are several arguments, some arguments might already
2019+
// have influenced the context, binding variables, but later ones
2020+
// might fail. In that case the constraint needs to be reset.
2021+
ctx.typerState.constraint = constr
2022+
2023+
// If method has default params, fall back to regular application
2024+
// where all inferred implicits are passed as named args.
2025+
if (tree.symbol.hasDefaultParams) {
2026+
val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) =>
2027+
arg match {
2028+
case EmptyTree => Nil
2029+
case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
2030+
}
2031+
}
2032+
tryEither { implicit ctx =>
2033+
typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt)
2034+
} { (_, _) =>
2035+
issueErrors()
2036+
}
2037+
} else issueErrors()
2038+
}
2039+
else adapt(tpd.Apply(tree, args), pt)
2040+
}
2041+
addImplicitArgs(argCtx(tree))
2042+
}
2043+
2044+
// Follow proxies and approximate type paramrefs by their upper bound
2045+
// in the current constraint in order to figure out robustly
2046+
// whether an expected type is some sort of function type.
2047+
def underlyingApplied(tp: Type): Type = tp.stripTypeVar match {
2048+
case tp: RefinedType => tp
2049+
case tp: AppliedType => tp
2050+
case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi)
2051+
case tp: TypeProxy => underlyingApplied(tp.superType)
2052+
case _ => tp
2053+
}
2054+
2055+
def adaptNoArgs(wtp: Type): Tree = {
2056+
val ptNorm = underlyingApplied(pt)
2057+
val functionExpected = defn.isFunctionType(ptNorm)
2058+
wtp match {
2059+
case wtp: ExprType =>
2060+
adaptInterpolated(tree.withType(wtp.resultType), pt)
2061+
case wtp: ImplicitMethodType
2062+
if constrainResult(wtp, followAlias(pt)) || !functionExpected =>
2063+
adaptImplicitMethod(wtp)
2064+
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
20572065
val arity =
2058-
if (defn.isFunctionType(ptNorm))
2066+
if (functionExpected)
20592067
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
20602068
// if method type is fully defined, but expected type is not,
20612069
// prioritize method parameter types as parameter types of the eta-expanded closure
@@ -2156,7 +2164,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
21562164
//typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
21572165
adaptToSubType(wtp)
21582166
}
2159-
}
2167+
}}
2168+
21602169
/** Adapt an expression of constant type to a different constant type `tpe`. */
21612170
def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {
21622171
def lit = Literal(tpe.value).withPos(tree.pos)

tests/pos/i3252.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Test {
2+
val i: Long = List(1, 2, 3).sum
3+
}

0 commit comments

Comments
 (0)