Skip to content

Commit 22020f3

Browse files
committed
Specialized retained inline FunctionN apply methods
Fixes #19724
1 parent 7f410aa commit 22020f3

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala

+21-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package transform
33

44
import ast.Trees.*, ast.tpd, core.*
55
import Contexts.*, Types.*, Decorators.*, Symbols.*, DenotTransformers.*
6-
import SymDenotations.*, Scopes.*, StdNames.*, NameOps.*, Names.*
6+
import SymDenotations.*, Scopes.*, StdNames.*, NameOps.*, Names.*, NameKinds.*
77
import MegaPhase.MiniPhase
88

99

@@ -25,7 +25,24 @@ class SpecializeFunctions extends MiniPhase {
2525
/** Create forwarders from the generic applys to the specialized ones.
2626
*/
2727
override def transformDefDef(ddef: DefDef)(using Context) = {
28-
if ddef.name != nme.apply
28+
// Note on special case for inline `apply`s:
29+
// `apply` and `apply$retainedBody` are specialized in this transformation.
30+
// `apply$retainedBody` have the name kind `BodyRetainerName`, these contain
31+
// the runtime implementation of an inline `apply` that implements (or overrides)
32+
// the `FunctionN.apply` method. The inline method is not specialized, it will
33+
// be replaced with the implementation of `apply$retainedBody`. The following code
34+
// inline def apply(x: Int): Double = x.toDouble:Double
35+
// private def apply$retainedBody(x: Int): Double = x.toDouble:Double
36+
// in is transformed into
37+
// inline def apply(x: Int): Double = x.toDouble:Double
38+
// private def apply$retainedBody(x: Int): Double = this.apply$mcDI$sp(x)
39+
// def apply$mcDI$sp(v: Int): Double = x.toDouble:Double
40+
// after erasure it will become
41+
// def apply(v: Int): Double = this.apply$mcDI$sp(v) // from apply$retainedBody
42+
// def apply$mcDI$sp(v: Int): Double = v.toDouble():Double
43+
// def apply(v1: Object): Object = Double.box(this.apply(Int.unbox(v1))) // erasure bridge
44+
45+
if ddef.name.asTermName.exclude(BodyRetainerName) != nme.apply
2946
|| ddef.termParamss.length != 1
3047
|| ddef.termParamss.head.length > 2
3148
|| !ctx.owner.isClass
@@ -44,12 +61,12 @@ class SpecializeFunctions extends MiniPhase {
4461
defn.isSpecializableFunction(cls, paramTypes, retType)
4562
}
4663

47-
if (sym.is(Flags.Deferred) || !isSpecializable) return ddef
64+
if (sym.is(Flags.Deferred) || sym.is(Flags.Inline) || !isSpecializable) return ddef
4865

4966
val specializedApply = newSymbol(
5067
cls,
5168
specName.nn,
52-
sym.flags | Flags.Synthetic,
69+
(sym.flags | Flags.Synthetic) &~ Flags.Private, // Private flag can be set if the name is a BodyRetainerName
5370
sym.info
5471
).entered
5572

Diff for: tests/pos/i19724.scala

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object repro:
2+
abstract class Mapper[A, B] extends (A => B)
3+
4+
given Mapper[Int, Double] with
5+
inline def apply(v: Int): Double = v.toDouble

Diff for: tests/run/i19724.scala

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class F0 extends (() => Double):
2+
inline def apply(): Double = 1.toDouble
3+
4+
class F1 extends (Int => Double):
5+
inline def apply(v: Int): Double = v.toDouble
6+
7+
class F2 extends ((Int, Int) => Double):
8+
inline def apply(v1: Int, v2: Int): Double = (v1 + v2).toDouble
9+
10+
@main def Test =
11+
val f0: (() => Double) = new F0
12+
assert(f0() == 1.0)
13+
14+
val f1: (Int => Double) = new F1
15+
assert(f1(3) == 3.0)
16+
17+
val f2: ((Int, Int) => Double) = new F2
18+
assert(f2(3, 2) == 5.0)

0 commit comments

Comments
 (0)