Skip to content

Commit d0f1e60

Browse files
authored
Merge pull request #4549 from dotty-staging/change-inline-untpd
Drop requirement that implicit functions must be non-empty
2 parents 2d6aa75 + c85cee0 commit d0f1e60

File tree

15 files changed

+87
-148
lines changed

15 files changed

+87
-148
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -813,12 +813,12 @@ object desugar {
813813
*
814814
* If `inlineable` is true, tag $anonfun with an @inline annotation.
815815
*/
816-
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), inlineable: Boolean)(implicit ctx: Context) = {
816+
def makeClosure(params: List[ValDef], body: Tree, tpt: Tree = TypeTree(), isInlineable: Boolean, isImplicit: Boolean)(implicit ctx: Context) = {
817817
var mods = synthetic | Artifact
818-
if (inlineable) mods |= Inline
818+
if (isInlineable) mods |= Inline
819819
Block(
820820
DefDef(nme.ANON_FUN, Nil, params :: Nil, tpt, body).withMods(mods),
821-
Closure(Nil, Ident(nme.ANON_FUN), EmptyTree))
821+
Closure(Nil, Ident(nme.ANON_FUN), if (isImplicit) ImplicitEmptyTree else EmptyTree))
822822
}
823823

824824
/** If `nparams` == 1, expand partial function
@@ -863,7 +863,7 @@ object desugar {
863863

864864
def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
865865
val params = makeImplicitParameters(formals.map(TypeTree))
866-
new NonEmptyFunction(params, body, Modifiers(Implicit))
866+
new FunctionWithMods(params, body, Modifiers(Implicit))
867867
}
868868

869869
/** Add annotation to tree:

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,11 +298,15 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
298298

299299
/** Is `tree` an implicit function or closure, possibly nested in a block? */
300300
def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
301+
case tree: FunctionWithMods => tree.mods.is(Implicit)
301302
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit)
302303
case Closure(_, meth, _) => true
303304
case Block(Nil, expr) => isImplicitClosure(expr)
304-
case Block(DefDef(nme.ANON_FUN, _, (param :: _) :: _, _, _) :: Nil, _: Closure) =>
305-
param.mods.is(Implicit)
305+
case Block(DefDef(nme.ANON_FUN, _, params :: _, _, _) :: Nil, cl: Closure) =>
306+
params match {
307+
case param :: _ => param.mods.is(Implicit)
308+
case Nil => cl.tpt.eq(untpd.ImplicitEmptyTree) || defn.isImplicitFunctionType(cl.tpt.typeOpt)
309+
}
306310
case _ => false
307311
}
308312

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,7 @@ object Trees {
884884

885885
@sharable val EmptyTree: Thicket = genericEmptyTree
886886
@sharable val EmptyValDef: ValDef = genericEmptyValDef
887+
@sharable val ImplicitEmptyTree: Thicket = Thicket(Nil) // an empty tree marking an implicit closure
887888

888889
// ----- Auxiliary creation methods ------------------
889890

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
5656
override def isType = body.isType
5757
}
5858

59-
/** A function type that should have non empty args */
60-
class NonEmptyFunction(args: List[Tree], body: Tree, val mods: Modifiers) extends Function(args, body)
59+
/** A function type with `implicit` or `erased` modifiers */
60+
class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers) extends Function(args, body)
6161

6262
/** A function created from a wildcard expression
6363
* @param placeholderParams a list of definitions of synthetic parameters.
6464
* @param body the function body where wildcards are replaced by
6565
* references to synthetic parameters.
66+
* This is equivalent to Function, except that forms a special case for the overlapping
67+
* positions tests.
6668
*/
6769
class WildcardFunction(placeholderParams: List[ValDef], body: Tree) extends Function(placeholderParams, body)
6870

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

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -861,22 +861,17 @@ class Definitions {
861861

862862
lazy val TupleType = mkArityArray("scala.Tuple", MaxTupleArity, 2)
863863

864-
def FunctionClass(n: Int, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context) = {
865-
if (isImplicit && isErased) {
866-
require(n > 0)
864+
def FunctionClass(n: Int, isImplicit: Boolean = false, isErased: Boolean = false)(implicit ctx: Context) =
865+
if (isImplicit && isErased)
867866
ctx.requiredClass("scala.ErasedImplicitFunction" + n.toString)
868-
}
869-
else if (isImplicit) {
870-
require(n > 0)
867+
else if (isImplicit)
871868
ctx.requiredClass("scala.ImplicitFunction" + n.toString)
872-
}
873-
else if (isErased) {
874-
require(n > 0)
869+
else if (isErased)
875870
ctx.requiredClass("scala.ErasedFunction" + n.toString)
876-
}
877-
else if (n <= MaxImplementedFunctionArity) FunctionClassPerRun()(ctx)(n)
878-
else ctx.requiredClass("scala.Function" + n.toString)
879-
}
871+
else if (n <= MaxImplementedFunctionArity)
872+
FunctionClassPerRun()(ctx)(n)
873+
else
874+
ctx.requiredClass("scala.Function" + n.toString)
880875

881876
lazy val Function0_applyR = ImplementedFunctionType(0).symbol.requiredMethodRef(nme.apply)
882877
def Function0_apply(implicit ctx: Context) = Function0_applyR.symbol
@@ -907,14 +902,14 @@ class Definitions {
907902

908903
/** Is a function class.
909904
* - FunctionN for N >= 0
910-
* - ImplicitFunctionN for N > 0
905+
* - ImplicitFunctionN for N >= 0
911906
* - ErasedFunctionN for N > 0
912907
* - ErasedImplicitFunctionN for N > 0
913908
*/
914909
def isFunctionClass(cls: Symbol) = scalaClassName(cls).isFunction
915910

916911
/** Is an implicit function class.
917-
* - ImplicitFunctionN for N > 0
912+
* - ImplicitFunctionN for N >= 0
918913
* - ErasedImplicitFunctionN for N > 0
919914
*/
920915
def isImplicitFunctionClass(cls: Symbol) = scalaClassName(cls).isImplicitFunction
@@ -948,7 +943,7 @@ class Definitions {
948943
* - FunctionN for N > 22 becomes FunctionXXL
949944
* - FunctionN for 22 > N >= 0 remains as FunctionN
950945
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
951-
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
946+
* - ImplicitFunctionN for N <= 22 becomes FunctionN
952947
* - ErasedFunctionN becomes Function0
953948
* - ImplicitErasedFunctionN becomes Function0
954949
* - anything else becomes a NoSymbol
@@ -965,7 +960,7 @@ class Definitions {
965960
* - FunctionN for N > 22 becomes FunctionXXL
966961
* - FunctionN for 22 > N >= 0 remains as FunctionN
967962
* - ImplicitFunctionN for N > 22 becomes FunctionXXL
968-
* - ImplicitFunctionN for 22 > N >= 0 becomes FunctionN
963+
* - ImplicitFunctionN for N <= 22 becomes FunctionN
969964
* - ErasedFunctionN becomes Function0
970965
* - ImplicitErasedFunctionN becomes Function0
971966
* - anything else becomes a NoType

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

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -167,59 +167,42 @@ object NameOps {
167167
}
168168
}
169169

170-
/** Is a synthetic function name
171-
* - N for FunctionN
172-
* - N for ImplicitFunctionN (N >= 1)
173-
* - (-1) otherwise
174-
*/
175170
def functionArity: Int =
176-
functionArityFor(str.Function) max {
171+
functionArityFor(str.Function) max
172+
functionArityFor(str.ImplicitFunction) max {
177173
val n =
178-
functionArityFor(str.ImplicitFunction) max
179174
functionArityFor(str.ErasedFunction) max
180175
functionArityFor(str.ErasedImplicitFunction)
181176
if (n == 0) -1 else n
182177
}
183178

184-
/** Is a function name
185-
* - FunctionN for N >= 0
186-
* - ImplicitFunctionN for N >= 1
187-
* - ErasedFunctionN for N >= 1
188-
* - ErasedImplicitFunctionN for N >= 1
189-
* - false otherwise
179+
/** Is a function name, i.e one of FunctionN, ImplicitFunctionN for N >= 0 or ErasedFunctionN, ErasedImplicitFunctionN for N > 0
190180
*/
191181
def isFunction: Boolean = functionArity >= 0
192182

193-
/** Is a implicit function name
194-
* - ImplicitFunctionN for N >= 1
195-
* - ErasedImplicitFunctionN for N >= 1
196-
* - false otherwise
183+
/** Is an implicit function name, i.e one of ImplicitFunctionN for N >= 0 or ErasedImplicitFunctionN for N > 0
197184
*/
198185
def isImplicitFunction: Boolean = {
199-
functionArityFor(str.ImplicitFunction) >= 1 ||
200-
functionArityFor(str.ErasedImplicitFunction) >= 1
186+
functionArityFor(str.ImplicitFunction) >= 0 ||
187+
functionArityFor(str.ErasedImplicitFunction) > 0
201188
}
202189

203-
/** Is a implicit function name
204-
* - ErasedFunctionN for N >= 1
205-
* - ErasedImplicitFunctionN for N >= 1
206-
* - false otherwise
190+
/** Is an erased function name, i.e. one of ErasedFunctionN, ErasedImplicitFunctionN for N > 0
207191
*/
208192
def isErasedFunction: Boolean = {
209-
functionArityFor(str.ErasedFunction) >= 1 ||
210-
functionArityFor(str.ErasedImplicitFunction) >= 1
193+
functionArityFor(str.ErasedFunction) > 0 ||
194+
functionArityFor(str.ErasedImplicitFunction) > 0
211195
}
212196

213-
/** Is a synthetic function name
197+
/** Is a synthetic function name, i.e. one of
214198
* - FunctionN for N > 22
215-
* - ImplicitFunctionN for N >= 1
216-
* - ErasedFunctionN for N >= 1
217-
* - ErasedImplicitFunctionN for N >= 1
218-
* - false otherwise
199+
* - ImplicitFunctionN for N >= 0
200+
* - ErasedFunctionN for N > 0
201+
* - ErasedImplicitFunctionN for N > 0
219202
*/
220203
def isSyntheticFunction: Boolean = {
221204
functionArityFor(str.Function) > MaxImplementedFunctionArity ||
222-
functionArityFor(str.ImplicitFunction) >= 1 ||
205+
functionArityFor(str.ImplicitFunction) >= 0 ||
223206
isErasedFunction
224207
}
225208

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2893,7 +2893,6 @@ object Types {
28932893
final override def isImplicitMethod: Boolean = companion.eq(ImplicitMethodType) || companion.eq(ErasedImplicitMethodType)
28942894
final override def isErasedMethod: Boolean = companion.eq(ErasedMethodType) || companion.eq(ErasedImplicitMethodType)
28952895

2896-
28972896
def computeSignature(implicit ctx: Context): Signature = {
28982897
val params = if (isErasedMethod) Nil else paramInfos
28992898
resultSignature.prepend(params, isJavaMethod)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ object Parsers {
732732
def functionRest(params: List[Tree]): Tree =
733733
atPos(start, accept(ARROW)) {
734734
val t = typ()
735-
if (imods.is(Implicit) || imods.is(Erased)) new NonEmptyFunction(params, t, imods)
735+
if (imods.is(Implicit) || imods.is(Erased)) new FunctionWithMods(params, t, imods)
736736
else Function(params, t)
737737
}
738738
def funArgTypesRest(first: Tree, following: () => Tree) = {
@@ -800,7 +800,7 @@ object Parsers {
800800
case ARROW => functionRest(t :: Nil)
801801
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
802802
case _ =>
803-
if (imods.is(Implicit) && !t.isInstanceOf[NonEmptyFunction])
803+
if (imods.is(Implicit) && !t.isInstanceOf[FunctionWithMods])
804804
syntaxError("Types with implicit keyword can only be function types", Position(start, start + nme.IMPLICITkw.asSimpleName.length))
805805
t
806806
}

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,26 +1720,6 @@ object messages {
17201720
}
17211721
}
17221722

1723-
case class FunctionTypeNeedsNonEmptyParameterList(isImplicit: Boolean, isErased: Boolean)(implicit ctx: Context)
1724-
extends Message(FunctionTypeNeedsNonEmptyParameterListID) {
1725-
assert(isImplicit || isErased)
1726-
val kind = "Syntax"
1727-
val mods = ((isErased, "erased") :: (isImplicit, "implicit") :: Nil).collect { case (true, mod) => mod }.mkString(" ")
1728-
val msg = mods + " function type needs non-empty parameter list"
1729-
val explanation = {
1730-
val code1 = s"type Transactional[T] = $mods Transaction => T"
1731-
val code2 = "val cl: implicit A => B"
1732-
hl"""It is not allowed to leave $mods function parameter list empty.
1733-
|Possible ways to define $mods function type:
1734-
|
1735-
|$code1
1736-
|
1737-
|or
1738-
|
1739-
|$code2""".stripMargin
1740-
}
1741-
}
1742-
17431723
case class WrongNumberOfParameters(expected: Int)(implicit ctx: Context)
17441724
extends Message(WrongNumberOfParametersID) {
17451725
val kind = "Syntax"

compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ import dotty.tools.dotc.util.Positions.Position
1515
* These fall into five categories
1616
*
1717
* 1. Partial function closures, we need to generate isDefinedAt and applyOrElse methods for these.
18-
* 2. Closures implementing non-trait classes.
18+
* 2. Closures implementing non-trait classes
1919
* 3. Closures implementing classes that inherit from a class other than Object
2020
* (a lambda cannot not be a run-time subtype of such a class)
2121
* 4. Closures that implement traits which run initialization code.
2222
* 5. Closures that get synthesized abstract methods in the transformation pipeline. These methods can be
2323
* (1) superaccessors, (2) outer references, (3) accessors for fields.
24+
*
25+
* However, implicit function types do not count as SAM types.
2426
*/
2527
class ExpandSAMs extends MiniPhase {
2628
override def phaseName = "expandSAMs"
@@ -34,7 +36,10 @@ class ExpandSAMs extends MiniPhase {
3436
override def transformBlock(tree: Block)(implicit ctx: Context): Tree = tree match {
3537
case Block(stats @ (fn: DefDef) :: Nil, Closure(_, fnRef, tpt)) if fnRef.symbol == fn.symbol =>
3638
tpt.tpe match {
37-
case NoType => tree // it's a plain function
39+
case NoType =>
40+
tree // it's a plain function
41+
case tpe if defn.isImplicitFunctionType(tpe) =>
42+
tree
3843
case tpe @ SAMType(_) if tpe.isRef(defn.PartialFunctionClass) =>
3944
val tpe1 = checkRefinements(tpe, fn.pos)
4045
toPartialFunction(tree, tpe1)

compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,15 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
7373
ctx.fresh.updateStore(DirectMeth, newMutableSymbolMap[Symbol])
7474

7575
/** Should `sym` get a ..$direct companion?
76-
* This is the case if (1) `sym` is a method with an implicit function type as final result type.
76+
* This is the case if `sym` is a method with a non-nullary implicit function type as final result type.
7777
* However if `specializeMonoTargets` is false, we exclude symbols that are known
7878
* to be only targets of monomorphic calls because they are effectively
7979
* final and don't override anything.
8080
*/
8181
private def shouldBeSpecialized(sym: Symbol)(implicit ctx: Context) =
8282
sym.is(Method, butNot = Accessor) &&
8383
defn.isImplicitFunctionType(sym.info.finalResultType) &&
84+
defn.functionArity(sym.info.finalResultType) > 0 &&
8485
!sym.isAnonymousFunction &&
8586
(specializeMonoTargets || !sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty)
8687

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -755,14 +755,17 @@ class Typer extends Namer
755755
def typedFunctionType(tree: untpd.Function, pt: Type)(implicit ctx: Context) = {
756756
val untpd.Function(args, body) = tree
757757
val (isImplicit, isErased) = tree match {
758-
case tree: untpd.NonEmptyFunction =>
759-
if (args.nonEmpty) (tree.mods.is(Implicit), tree.mods.is(Erased))
760-
else {
761-
ctx.error(FunctionTypeNeedsNonEmptyParameterList(tree.mods.is(Implicit), tree.mods.is(Erased)), tree.pos)
762-
(false, false)
758+
case tree: untpd.FunctionWithMods =>
759+
val isImplicit = tree.mods.is(Implicit)
760+
var isErased = tree.mods.is(Erased)
761+
if (isErased && args.isEmpty) {
762+
ctx.error("An empty function cannot not be erased", tree.pos)
763+
isErased = false
763764
}
765+
(isImplicit, isErased)
764766
case _ => (false, false)
765767
}
768+
766769
val funCls = defn.FunctionClass(args.length, isImplicit, isErased)
767770

768771
/** Typechecks dependent function type with given parameters `params` */
@@ -797,6 +800,11 @@ class Typer extends Namer
797800
def typedFunctionValue(tree: untpd.Function, pt: Type)(implicit ctx: Context) = {
798801
val untpd.Function(params: List[untpd.ValDef] @unchecked, body) = tree
799802

803+
val isImplicit = tree match {
804+
case tree: untpd.FunctionWithMods => tree.mods.is(Implicit)
805+
case _ => false
806+
}
807+
800808
pt match {
801809
case pt: TypeVar if untpd.isFunctionWithUnknownParamType(tree) =>
802810
// try to instantiate `pt` if this is possible. If it does not
@@ -915,8 +923,8 @@ class Typer extends Namer
915923
else cpy.ValDef(param)(
916924
tpt = untpd.TypeTree(
917925
inferredParamType(param, protoFormal(i)).underlyingIfRepeated(isJava = false)))
918-
val inlineable = pt.hasAnnotation(defn.InlineParamAnnot)
919-
desugar.makeClosure(inferredParams, fnBody, resultTpt, inlineable)
926+
val isInlineable = pt.hasAnnotation(defn.InlineParamAnnot)
927+
desugar.makeClosure(inferredParams, fnBody, resultTpt, isInlineable, isImplicit)
920928
}
921929
typed(desugared, pt)
922930
}
@@ -941,7 +949,11 @@ class Typer extends Namer
941949
|because it has internal parameter dependencies,
942950
|position = ${tree.pos}, raw type = ${mt.toString}""") // !!! DEBUG. Eventually, convert to an error?
943951
}
944-
else EmptyTree
952+
else if ((tree.tpt `eq` untpd.ImplicitEmptyTree) && mt.paramNames.isEmpty)
953+
// Note implicitness of function in target type sicne there are no method parameters that indicate it.
954+
TypeTree(defn.FunctionOf(Nil, mt.resType, isImplicit = true, isErased = false))
955+
else
956+
EmptyTree
945957
}
946958
case tp =>
947959
throw new java.lang.Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}")

0 commit comments

Comments
 (0)