Skip to content

Commit 9a96ce3

Browse files
committed
Refactor adaptNoArgs
Split into more manageable sub-methods
1 parent 96cabbb commit 9a96ce3

File tree

1 file changed

+113
-105
lines changed

1 file changed

+113
-105
lines changed

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

Lines changed: 113 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1994,7 +1994,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
19941994
}
19951995
}
19961996

1997-
def adaptImplicitMethod(wtp: ImplicitMethodType): Tree = {
1997+
def adaptNoArgsImplicitMethod(wtp: ImplicitMethodType): Tree = {
19981998
val tvarsToInstantiate = tvarsInParams(tree)
19991999
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
20002000
val constr = ctx.typerState.constraint
@@ -2041,6 +2041,100 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20412041
addImplicitArgs(argCtx(tree))
20422042
}
20432043

2044+
/** A synthetic apply should be eta-expanded if it is the apply of an implicit function
2045+
* class, and the expected type is a function type. This rule is needed so we can pass
2046+
* an implicit function to a regular function type. So the following is OK
2047+
*
2048+
* val f: implicit A => B = ???
2049+
* val g: A => B = f
2050+
*
2051+
* and the last line expands to
2052+
*
2053+
* val g: A => B = (x$0: A) => f.apply(x$0)
2054+
*
2055+
* One could be tempted not to eta expand the rhs, but that would violate the invariant
2056+
* that expressions of implicit function types are always implicit closures, which is
2057+
* exploited by ShortcutImplicits.
2058+
*
2059+
* On the other hand, the following would give an error if there is no implicit
2060+
* instance of A available.
2061+
*
2062+
* val x: AnyRef = f
2063+
*
2064+
* That's intentional, we want to fail here, otherwise some unsuccesful implicit searches
2065+
* would go undetected.
2066+
*
2067+
* Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala.
2068+
*/
2069+
def adaptNoArgsUnappliedMethod(wtp: MethodType, functionExpected: Boolean, arity: Int): Tree = {
2070+
def isExpandableApply =
2071+
defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && functionExpected
2072+
2073+
/** Is reference to this symbol `f` automatically expanded to `f()`? */
2074+
def isAutoApplied(sym: Symbol): Boolean = {
2075+
sym.isConstructor ||
2076+
sym.matchNullaryLoosely ||
2077+
ctx.testScala2Mode(em"${sym.showLocated} requires () argument", tree.pos,
2078+
patch(tree.pos.endPos, "()"))
2079+
}
2080+
2081+
// Reasons NOT to eta expand:
2082+
// - we reference a constructor
2083+
// - we are in a pattern
2084+
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
2085+
if (arity >= 0 &&
2086+
!tree.symbol.isConstructor &&
2087+
!ctx.mode.is(Mode.Pattern) &&
2088+
!(isSyntheticApply(tree) && !isExpandableApply))
2089+
typed(etaExpand(tree, wtp, arity), pt)
2090+
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
2091+
adaptInterpolated(tpd.Apply(tree, Nil), pt)
2092+
else if (wtp.isImplicit)
2093+
err.typeMismatch(tree, pt)
2094+
else
2095+
missingArgs(wtp)
2096+
}
2097+
2098+
def adaptNoArgsOther(wtp: Type) = {
2099+
ctx.typeComparer.GADTused = false
2100+
if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
2101+
!untpd.isImplicitClosure(tree) &&
2102+
!isApplyProto(pt) &&
2103+
!ctx.isAfterTyper) {
2104+
typr.println(i"insert apply on implicit $tree")
2105+
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
2106+
}
2107+
else if (ctx.mode is Mode.Pattern) {
2108+
checkEqualityEvidence(tree, pt)
2109+
tree
2110+
}
2111+
else if (tree.tpe <:< pt) {
2112+
if (pt.hasAnnotation(defn.InlineParamAnnot))
2113+
checkInlineConformant(tree, "argument to inline parameter")
2114+
if (Inliner.hasBodyToInline(tree.symbol) &&
2115+
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
2116+
!ctx.settings.YnoInline.value &&
2117+
!ctx.isAfterTyper &&
2118+
!ctx.reporter.hasErrors)
2119+
adapt(Inliner.inlineCall(tree, pt), pt)
2120+
else if (ctx.typeComparer.GADTused && pt.isValueType)
2121+
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
2122+
// I suspect, but am not 100% sure that this might affect inferred types,
2123+
// if the expected type is a supertype of the GADT bound. It would be good to come
2124+
// up with a test case for this.
2125+
tree.asInstance(pt)
2126+
else
2127+
tree
2128+
}
2129+
else wtp match {
2130+
case wtp: MethodType => missingArgs(wtp)
2131+
case _ =>
2132+
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
2133+
//typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
2134+
adaptToSubType(wtp)
2135+
}
2136+
}
2137+
20442138
// Follow proxies and approximate type paramrefs by their upper bound
20452139
// in the current constraint in order to figure out robustly
20462140
// whether an expected type is some sort of function type.
@@ -2060,111 +2154,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20602154
adaptInterpolated(tree.withType(wtp.resultType), pt)
20612155
case wtp: ImplicitMethodType
20622156
if constrainResult(wtp, followAlias(pt)) || !functionExpected =>
2063-
adaptImplicitMethod(wtp)
2157+
adaptNoArgsImplicitMethod(wtp)
20642158
case wtp: MethodType if !pt.isInstanceOf[SingletonType] =>
2065-
val arity =
2066-
if (functionExpected)
2067-
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
2068-
// if method type is fully defined, but expected type is not,
2069-
// prioritize method parameter types as parameter types of the eta-expanded closure
2070-
0
2071-
else defn.functionArity(ptNorm)
2072-
else {
2073-
val nparams = wtp.paramInfos.length
2074-
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
2075-
else -1 // no eta expansion in this case
2076-
}
2077-
2078-
/** A synthetic apply should be eta-expanded if it is the apply of an implicit function
2079-
* class, and the expected type is a function type. This rule is needed so we can pass
2080-
* an implicit function to a regular function type. So the following is OK
2081-
*
2082-
* val f: implicit A => B = ???
2083-
* val g: A => B = f
2084-
*
2085-
* and the last line expands to
2086-
*
2087-
* val g: A => B = (x$0: A) => f.apply(x$0)
2088-
*
2089-
* One could be tempted not to eta expand the rhs, but that would violate the invariant
2090-
* that expressions of implicit function types are always implicit closures, which is
2091-
* exploited by ShortcutImplicits.
2092-
*
2093-
* On the other hand, the following would give an error if there is no implicit
2094-
* instance of A available.
2095-
*
2096-
* val x: AnyRef = f
2097-
*
2098-
* That's intentional, we want to fail here, otherwise some unsuccesful implicit searches
2099-
* would go undetected.
2100-
*
2101-
* Examples for these cases are found in run/implicitFuns.scala and neg/i2006.scala.
2102-
*/
2103-
def isExpandableApply =
2104-
defn.isImplicitFunctionClass(tree.symbol.maybeOwner) && defn.isFunctionType(ptNorm)
2105-
2106-
/** Is reference to this symbol `f` automatically expanded to `f()`? */
2107-
def isAutoApplied(sym: Symbol): Boolean = {
2108-
sym.isConstructor ||
2109-
sym.matchNullaryLoosely ||
2110-
ctx.testScala2Mode(em"${sym.showLocated} requires () argument", tree.pos,
2111-
patch(tree.pos.endPos, "()"))
2112-
}
2113-
2114-
// Reasons NOT to eta expand:
2115-
// - we reference a constructor
2116-
// - we are in a pattern
2117-
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
2118-
if (arity >= 0 &&
2119-
!tree.symbol.isConstructor &&
2120-
!ctx.mode.is(Mode.Pattern) &&
2121-
!(isSyntheticApply(tree) && !isExpandableApply))
2122-
typed(etaExpand(tree, wtp, arity), pt)
2123-
else if (wtp.paramInfos.isEmpty && isAutoApplied(tree.symbol))
2124-
adaptInterpolated(tpd.Apply(tree, Nil), pt)
2125-
else if (wtp.isImplicit)
2126-
err.typeMismatch(tree, pt)
2127-
else
2128-
missingArgs(wtp)
2129-
case _ =>
2130-
ctx.typeComparer.GADTused = false
2131-
if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
2132-
!untpd.isImplicitClosure(tree) &&
2133-
!isApplyProto(pt) &&
2134-
!ctx.isAfterTyper) {
2135-
typr.println(i"insert apply on implicit $tree")
2136-
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
2137-
}
2138-
else if (ctx.mode is Mode.Pattern) {
2139-
checkEqualityEvidence(tree, pt)
2140-
tree
2141-
}
2142-
else if (tree.tpe <:< pt) {
2143-
if (pt.hasAnnotation(defn.InlineParamAnnot))
2144-
checkInlineConformant(tree, "argument to inline parameter")
2145-
if (Inliner.hasBodyToInline(tree.symbol) &&
2146-
!ctx.owner.ownersIterator.exists(_.isInlineMethod) &&
2147-
!ctx.settings.YnoInline.value &&
2148-
!ctx.isAfterTyper &&
2149-
!ctx.reporter.hasErrors)
2150-
adapt(Inliner.inlineCall(tree, pt), pt)
2151-
else if (ctx.typeComparer.GADTused && pt.isValueType)
2152-
// Insert an explicit cast, so that -Ycheck in later phases succeeds.
2153-
// I suspect, but am not 100% sure that this might affect inferred types,
2154-
// if the expected type is a supertype of the GADT bound. It would be good to come
2155-
// up with a test case for this.
2156-
tree.asInstance(pt)
2157-
else
2158-
tree
2159-
}
2160-
else wtp match {
2161-
case wtp: MethodType => missingArgs(wtp)
2162-
case _ =>
2163-
typr.println(i"adapt to subtype ${tree.tpe} !<:< $pt")
2164-
//typr.println(TypeComparer.explained(implicit ctx => tree.tpe <:< pt))
2165-
adaptToSubType(wtp)
2166-
}
2167-
}}
2159+
val arity =
2160+
if (functionExpected)
2161+
if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none))
2162+
// if method type is fully defined, but expected type is not,
2163+
// prioritize method parameter types as parameter types of the eta-expanded closure
2164+
0
2165+
else defn.functionArity(ptNorm)
2166+
else {
2167+
val nparams = wtp.paramInfos.length
2168+
if (nparams > 0 || pt.eq(AnyFunctionProto)) nparams
2169+
else -1 // no eta expansion in this case
2170+
}
2171+
adaptNoArgsUnappliedMethod(wtp, functionExpected, arity)
2172+
case _ =>
2173+
adaptNoArgsOther(wtp)
2174+
}
2175+
}
21682176

21692177
/** Adapt an expression of constant type to a different constant type `tpe`. */
21702178
def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {

0 commit comments

Comments
 (0)