Skip to content

Commit 7004d07

Browse files
authored
Merge pull request #3254 from dotty-staging/fix-#3252
Fix #3252: Change logic in adaptNoArgs
2 parents 543b697 + 539c203 commit 7004d07

File tree

3 files changed

+178
-157
lines changed

3 files changed

+178
-157
lines changed

compiler/src/dotty/tools/dotc/core/TyperState.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,5 +167,6 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
167167

168168
override def toText(printer: Printer): Text = constraint.toText(printer)
169169

170-
def hashesStr: String = hashCode.toString + " -> " + previous.hashesStr
170+
def hashesStr: String =
171+
if (previous == null) "" else hashCode.toString + " -> " + previous.hashesStr
171172
}

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

Lines changed: 173 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1990,169 +1990,186 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19901990
}
19911991
}
19921992

1993-
def adaptNoArgs(wtp: Type): Tree = wtp match {
1994-
case wtp: ExprType =>
1995-
adaptInterpolated(tree.withType(wtp.resultType), pt)
1996-
case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) =>
1997-
val tvarsToInstantiate = tvarsInParams(tree)
1998-
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
1999-
val constr = ctx.typerState.constraint
2000-
def addImplicitArgs(implicit ctx: Context) = {
2001-
val errors = new mutable.ListBuffer[() => String]
2002-
def implicitArgError(msg: => String) = {
2003-
errors += (() => msg)
2004-
EmptyTree
2005-
}
2006-
def issueErrors() = {
2007-
for (err <- errors) ctx.error(err(), tree.pos.endPos)
2008-
tree.withType(wtp.resultType)
2009-
}
2010-
val args = (wtp.paramNames, wtp.paramInfos).zipped map { (pname, formal) =>
2011-
def implicitArgError(msg: String => String) =
2012-
errors += (() => msg(em"parameter $pname of $methodStr"))
2013-
if (errors.nonEmpty) EmptyTree
2014-
else inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
2015-
}
2016-
if (errors.nonEmpty) {
2017-
// If there are several arguments, some arguments might already
2018-
// have influenced the context, binding variables, but later ones
2019-
// might fail. In that case the constraint needs to be reset.
2020-
ctx.typerState.constraint = constr
2021-
2022-
// If method has default params, fall back to regular application
2023-
// where all inferred implicits are passed as named args.
2024-
if (tree.symbol.hasDefaultParams) {
2025-
val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) =>
2026-
arg match {
2027-
case EmptyTree => Nil
2028-
case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
2029-
}
2030-
}
2031-
tryEither { implicit ctx =>
2032-
typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt)
2033-
} { (_, _) =>
2034-
issueErrors()
2035-
}
2036-
} else issueErrors()
2037-
}
2038-
else adapt(tpd.Apply(tree, args), pt)
1993+
def adaptNoArgsImplicitMethod(wtp: ImplicitMethodType): Tree = {
1994+
val tvarsToInstantiate = tvarsInParams(tree)
1995+
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
1996+
val constr = ctx.typerState.constraint
1997+
def addImplicitArgs(implicit ctx: Context) = {
1998+
val errors = new mutable.ListBuffer[() => String]
1999+
def implicitArgError(msg: => String) = {
2000+
errors += (() => msg)
2001+
EmptyTree
20392002
}
2040-
addImplicitArgs(argCtx(tree))
2041-
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
2042-
// Follow proxies and approximate type paramrefs by their upper bound
2043-
// in the current constraint in order to figure out robustly
2044-
// whether an expected type is some sort of function type.
2045-
def underlyingApplied(tp: Type): Type = tp.stripTypeVar match {
2046-
case tp: RefinedType => tp
2047-
case tp: AppliedType => tp
2048-
case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi)
2049-
case tp: TypeProxy => underlyingApplied(tp.superType)
2050-
case _ => tp
2003+
def issueErrors() = {
2004+
for (err <- errors) ctx.error(err(), tree.pos.endPos)
2005+
tree.withType(wtp.resultType)
20512006
}
2052-
val ptNorm = underlyingApplied(pt)
2053-
val arity =
2054-
if (defn.isFunctionType(ptNorm))
2055-
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
2056-
// if method type is fully defined, but expected type is not,
2057-
// prioritize method parameter types as parameter types of the eta-expanded closure
2058-
0
2059-
else defn.functionArity(ptNorm)
2060-
else {
2061-
val nparams = wtp.paramInfos.length
2062-
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
2063-
else -1 // no eta expansion in this case
2064-
}
2065-
2066-
/** A synthetic apply should be eta-expanded if it is the apply of an implicit function
2067-
* class, and the expected type is a function type. This rule is needed so we can pass
2068-
* an implicit function to a regular function type. So the following is OK
2069-
*
2070-
* val f: implicit A => B = ???
2071-
* val g: A => B = f
2072-
*
2073-
* and the last line expands to
2074-
*
2075-
* val g: A => B = (x$0: A) => f.apply(x$0)
2076-
*
2077-
* One could be tempted not to eta expand the rhs, but that would violate the invariant
2078-
* that expressions of implicit function types are always implicit closures, which is
2079-
* exploited by ShortcutImplicits.
2080-
*
2081-
* On the other hand, the following would give an error if there is no implicit
2082-
* instance of A available.
2083-
*
2084-
* val x: AnyRef = f
2085-
*
2086-
* That's intentional, we want to fail here, otherwise some unsuccesful implicit searches
2087-
* would go undetected.
2088-
*
2089-
* Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala.
2090-
*/
2091-
def isExpandableApply =
2092-
defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && defn.isFunctionType(ptNorm)
2093-
2094-
/** Is reference to this symbol `f` automatically expanded to `f()`? */
2095-
def isAutoApplied(sym: Symbol): Boolean = {
2096-
sym.isConstructor ||
2097-
sym.matchNullaryLoosely ||
2098-
ctx.testScala2Mode(em"${sym.showLocated} requires () argument", tree.pos,
2099-
patch(tree.pos.endPos, "()"))
2007+
val args = (wtp.paramNames, wtp.paramInfos).zipped map { (pname, formal) =>
2008+
def implicitArgError(msg: String => String) =
2009+
errors += (() => msg(em"parameter $pname of $methodStr"))
2010+
if (errors.nonEmpty) EmptyTree
2011+
else inferImplicitArg(formal, implicitArgError, tree.pos.endPos)
21002012
}
2013+
if (errors.nonEmpty) {
2014+
// If there are several arguments, some arguments might already
2015+
// have influenced the context, binding variables, but later ones
2016+
// might fail. In that case the constraint needs to be reset.
2017+
ctx.typerState.constraint = constr
2018+
2019+
// If method has default params, fall back to regular application
2020+
// where all inferred implicits are passed as named args.
2021+
if (tree.symbol.hasDefaultParams) {
2022+
val namedArgs = (wtp.paramNames, args).zipped.flatMap { (pname, arg) =>
2023+
arg match {
2024+
case EmptyTree => Nil
2025+
case _ => untpd.NamedArg(pname, untpd.TypedSplice(arg)) :: Nil
2026+
}
2027+
}
2028+
tryEither { implicit ctx =>
2029+
typed(untpd.Apply(untpd.TypedSplice(tree), namedArgs), pt)
2030+
} { (_, _) =>
2031+
issueErrors()
2032+
}
2033+
} else issueErrors()
2034+
}
2035+
else adapt(tpd.Apply(tree, args), pt)
2036+
}
2037+
addImplicitArgs(argCtx(tree))
2038+
}
2039+
2040+
/** A synthetic apply should be eta-expanded if it is the apply of an implicit function
2041+
* class, and the expected type is a function type. This rule is needed so we can pass
2042+
* an implicit function to a regular function type. So the following is OK
2043+
*
2044+
* val f: implicit A => B = ???
2045+
* val g: A => B = f
2046+
*
2047+
* and the last line expands to
2048+
*
2049+
* val g: A => B = (x$0: A) => f.apply(x$0)
2050+
*
2051+
* One could be tempted not to eta expand the rhs, but that would violate the invariant
2052+
* that expressions of implicit function types are always implicit closures, which is
2053+
* exploited by ShortcutImplicits.
2054+
*
2055+
* On the other hand, the following would give an error if there is no implicit
2056+
* instance of A available.
2057+
*
2058+
* val x: AnyRef = f
2059+
*
2060+
* That's intentional, we want to fail here, otherwise some unsuccesful implicit searches
2061+
* would go undetected.
2062+
*
2063+
* Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala.
2064+
*/
2065+
def adaptNoArgsUnappliedMethod(wtp: MethodType, functionExpected: Boolean, arity: Int): Tree = {
2066+
def isExpandableApply =
2067+
defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && functionExpected
2068+
2069+
/** Is reference to this symbol `f` automatically expanded to `f()`? */
2070+
def isAutoApplied(sym: Symbol): Boolean = {
2071+
sym.isConstructor ||
2072+
sym.matchNullaryLoosely ||
2073+
ctx.testScala2Mode(em"${sym.showLocated} requires () argument", tree.pos,
2074+
patch(tree.pos.endPos, "()"))
2075+
}
2076+
2077+
// Reasons NOT to eta expand:
2078+
// - we reference a constructor
2079+
// - we are in a pattern
2080+
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
2081+
if (arity >= 0 &&
2082+
!tree.symbol.isConstructor &&
2083+
!ctx.mode.is(Mode.Pattern) &&
2084+
!(isSyntheticApply(tree) && !isExpandableApply))
2085+
typed(etaExpand(tree, wtp, arity), pt)
2086+
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
2087+
adaptInterpolated(tpd.Apply(tree, Nil), pt)
2088+
else if (wtp.isImplicit)
2089+
err.typeMismatch(tree, pt)
2090+
else
2091+
missingArgs(wtp)
2092+
}
21012093

2102-
// Reasons NOT to eta expand:
2103-
// - we reference a constructor
2104-
// - we are in a pattern
2105-
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
2106-
if (arity >= 0 &&
2107-
!tree.symbol.isConstructor &&
2108-
!ctx.mode.is(Mode.Pattern) &&
2109-
!(isSyntheticApply(tree) && !isExpandableApply))
2110-
typed(etaExpand(tree, wtp, arity), pt)
2111-
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
2112-
adaptInterpolated(tpd.Apply(tree, Nil), pt)
2113-
else if (wtp.isImplicit)
2114-
err.typeMismatch(tree, pt)
2094+
def adaptNoArgsOther(wtp: Type) = {
2095+
ctx.typeComparer.GADTused = false
2096+
if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
2097+
!untpd.isImplicitClosure(tree) &&
2098+
!isApplyProto(pt) &&
2099+
!ctx.isAfterTyper) {
2100+
typr.println(i"insert apply on implicit $tree")
2101+
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
2102+
}
2103+
else if (ctx.mode is Mode.Pattern) {
2104+
checkEqualityEvidence(tree, pt)
2105+
tree
2106+
}
2107+
else if (tree.tpe <:< pt) {
2108+
if (pt.hasAnnotation(defn.InlineParamAnnot))
2109+
checkInlineConformant(tree, "argument to inline parameter")
2110+
if (Inliner.hasBodyToInline(tree.symbol) &&
2111+
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
2112+
!ctx.settings.YnoInline.value &&
2113+
!ctx.isAfterTyper &&
2114+
!ctx.reporter.hasErrors)
2115+
adapt(Inliner.inlineCall(tree, pt), pt)
2116+
else if (ctx.typeComparer.GADTused && pt.isValueType)
2117+
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
2118+
// I suspect, but am not 100% sure that this might affect inferred types,
2119+
// if the expected type is a supertype of the GADT bound. It would be good to come
2120+
// up with a test case for this.
2121+
tree.asInstance(pt)
21152122
else
2116-
missingArgs(wtp)
2117-
case _ =>
2118-
ctx.typeComparer.GADTused = false
2119-
if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
2120-
!untpd.isImplicitClosure(tree) &&
2121-
!isApplyProto(pt) &&
2122-
!ctx.isAfterTyper) {
2123-
typr.println(i"insert apply on implicit $tree")
2124-
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
2125-
}
2126-
else if (ctx.mode is Mode.Pattern) {
2127-
checkEqualityEvidence(tree, pt)
21282123
tree
2129-
}
2130-
else if (tree.tpe <:< pt) {
2131-
if (pt.hasAnnotation(defn.InlineParamAnnot))
2132-
checkInlineConformant(tree, "argument to inline parameter")
2133-
if (Inliner.hasBodyToInline(tree.symbol) &&
2134-
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
2135-
!ctx.settings.YnoInline.value &&
2136-
!ctx.isAfterTyper &&
2137-
!ctx.reporter.hasErrors)
2138-
adapt(Inliner.inlineCall(tree, pt), pt)
2139-
else if (ctx.typeComparer.GADTused && pt.isValueType)
2140-
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
2141-
// I suspect, but am not 100% sure that this might affect inferred types,
2142-
// if the expected type is a supertype of the GADT bound. It would be good to come
2143-
// up with a test case for this.
2144-
tree.asInstance(pt)
2145-
else
2146-
tree
2147-
}
2148-
else wtp match {
2149-
case wtp: MethodType => missingArgs(wtp)
2150-
case _ =>
2151-
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
2152-
//typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
2153-
adaptToSubType(wtp)
2154-
}
2124+
}
2125+
else wtp match {
2126+
case wtp: MethodType => missingArgs(wtp)
2127+
case _ =>
2128+
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
2129+
//typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
2130+
adaptToSubType(wtp)
2131+
}
21552132
}
2133+
2134+
// Follow proxies and approximate type paramrefs by their upper bound
2135+
// in the current constraint in order to figure out robustly
2136+
// whether an expected type is some sort of function type.
2137+
def underlyingApplied(tp: Type): Type = tp.stripTypeVar match {
2138+
case tp: RefinedType => tp
2139+
case tp: AppliedType => tp
2140+
case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi)
2141+
case tp: TypeProxy => underlyingApplied(tp.superType)
2142+
case _ => tp
2143+
}
2144+
2145+
def adaptNoArgs(wtp: Type): Tree = {
2146+
val ptNorm = underlyingApplied(pt)
2147+
val functionExpected = defn.isFunctionType(ptNorm)
2148+
wtp match {
2149+
case wtp: ExprType =>
2150+
adaptInterpolated(tree.withType(wtp.resultType), pt)
2151+
case wtp: ImplicitMethodType
2152+
if constrainResult(wtp, followAlias(pt)) || !functionExpected =>
2153+
adaptNoArgsImplicitMethod(wtp)
2154+
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
2155+
val arity =
2156+
if (functionExpected)
2157+
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
2158+
// if method type is fully defined, but expected type is not,
2159+
// prioritize method parameter types as parameter types of the eta-expanded closure
2160+
0
2161+
else defn.functionArity(ptNorm)
2162+
else {
2163+
val nparams = wtp.paramInfos.length
2164+
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
2165+
else -1 // no eta expansion in this case
2166+
}
2167+
adaptNoArgsUnappliedMethod(wtp, functionExpected, arity)
2168+
case _ =>
2169+
adaptNoArgsOther(wtp)
2170+
}
2171+
}
2172+
21562173
/** Adapt an expression of constant type to a different constant type `tpe`. */
21572174
def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {
21582175
def lit = Literal(tpe.value).withPos(tree.pos)

tests/pos/i3252.scala

Lines changed: 3 additions & 0 deletions
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)