Skip to content

Commit dc5e93f

Browse files
Backport "Fix "Compiler crash when calling another file's inline def that uses a private constructor"" to 3.8.2 (#25189)
Backports #24941 to the 3.8.2-RC2. PR submitted by the release tooling. [skip ci]
2 parents e57963a + f4bb4da commit dc5e93f

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,11 @@ class ExpandPrivate extends MiniPhase with IdentityDenotTransformer { thisPhase
8585
(j < 0 || p2(j) == separatorChar)
8686
}
8787

88-
assert(d.symbol.source.exists &&
88+
// Skip assertion for @publicInBinary members - they are designed to be accessed
89+
// across compilation units (e.g., when inlined). See SIP-52.
90+
val isPublicInBinary = d.hasPublicInBinary
91+
assert(isPublicInBinary ||
92+
d.symbol.source.exists &&
8993
ctx.owner.source.exists &&
9094
isSimilar(d.symbol.source.path, ctx.owner.source.path),
9195
s"private ${d.symbol.showLocated} in ${d.symbol.source} accessed from ${ctx.owner.showLocated} in ${ctx.owner.source}")

tests/pos/i24869/Demo_2.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package demo
2+
3+
object Demo extends Utils {
4+
// Test case 1: Basic case class with @publicInBinary private constructor
5+
val test1 = TestClass(???)
6+
7+
// Test case 2: Regular class with @publicInBinary private constructor
8+
val test2 = RegularClass.create(42)
9+
10+
// Test case 3: Nested class with @publicInBinary private constructor
11+
val outer = new Outer
12+
val test3 = outer.Inner.make("hello")
13+
14+
// Test case 4: @publicInBinary on private[Scope] method
15+
val wpm = new WithPrivateMethod
16+
val test4 = WithPrivateMethod.callSecret(wpm, 21)
17+
18+
// Test case 5: @publicInBinary on private val accessor (via constructor)
19+
val test5a = WithPrivateVal.create("secret")
20+
val test5b = WithPrivateVal.getHidden(test5a)
21+
22+
// Test case 6: Multiple @publicInBinary constructors
23+
val test6a = MultiConstructor.make1(100)
24+
val test6b = MultiConstructor.make2(200, "custom")
25+
26+
// Test case 7: Case class with @publicInBinary and parameters
27+
val test7 = DataClass.make("test", 123)
28+
29+
// Test case 8: @publicInBinary on private[Scope] val
30+
val wpvs = new WithPrivateValScoped
31+
val test8 = WithPrivateValScoped.getSecret(wpvs)
32+
33+
// Test case 9: @publicInBinary on private[Scope] lazy val
34+
val wlv = new WithLazyVal
35+
val test9 = WithLazyVal.getComputed(wlv)
36+
37+
// Test case 10: Top-level class with @publicInBinary
38+
val test10 = TopLevelPublicInBinary.create(3.14)
39+
40+
// Test case 11: Trait with companion having @publicInBinary class
41+
val test11: Describable = DescribableImpl.create("description")
42+
}

tests/pos/i24869/Utils_1.scala

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package demo
2+
3+
import scala.annotation.publicInBinary
4+
5+
class Utils {
6+
// Test case 1: Basic case class with @publicInBinary private constructor
7+
final case class TestClass @publicInBinary private () {}
8+
9+
object TestClass {
10+
inline def apply(inline clips: List[String]): TestClass = new TestClass()
11+
}
12+
13+
// Test case 2: Regular class with @publicInBinary private constructor
14+
class RegularClass @publicInBinary private (val x: Int) {
15+
def this() = this(0)
16+
}
17+
18+
object RegularClass {
19+
inline def create(inline value: Int): RegularClass = new RegularClass(value)
20+
}
21+
22+
// Test case 3: Nested class with @publicInBinary private constructor
23+
class Outer {
24+
class Inner @publicInBinary private (val name: String)
25+
object Inner {
26+
inline def make(inline n: String): Inner = new Inner(n)
27+
}
28+
}
29+
30+
// Test case 4: @publicInBinary on private[Scope] method (not constructor)
31+
class WithPrivateMethod {
32+
@publicInBinary private[WithPrivateMethod] def secretMethod(x: Int): Int = x * 2
33+
}
34+
35+
object WithPrivateMethod {
36+
inline def callSecret(w: WithPrivateMethod, inline x: Int): Int = w.secretMethod(x)
37+
}
38+
39+
// Test case 5: @publicInBinary on private val accessor (via constructor)
40+
class WithPrivateVal @publicInBinary private (val hidden: String)
41+
42+
object WithPrivateVal {
43+
inline def getHidden(w: WithPrivateVal): String = w.hidden
44+
inline def create(inline s: String): WithPrivateVal = new WithPrivateVal(s)
45+
}
46+
47+
// Test case 6: Multiple @publicInBinary constructors with different arities
48+
class MultiConstructor @publicInBinary private (val a: Int, val b: String) {
49+
@publicInBinary private def this(a: Int) = this(a, "default")
50+
}
51+
52+
object MultiConstructor {
53+
inline def make1(inline x: Int): MultiConstructor = new MultiConstructor(x)
54+
inline def make2(inline x: Int, inline y: String): MultiConstructor = new MultiConstructor(x, y)
55+
}
56+
57+
// Test case 7: Case class with @publicInBinary and parameters
58+
final case class DataClass @publicInBinary private (name: String, value: Int)
59+
60+
object DataClass {
61+
inline def make(inline n: String, inline v: Int): DataClass = new DataClass(n, v)
62+
}
63+
64+
// Test case 8: @publicInBinary on private[Scope] val
65+
class WithPrivateValScoped {
66+
@publicInBinary private[WithPrivateValScoped] val secretVal: Int = 42
67+
}
68+
69+
object WithPrivateValScoped {
70+
inline def getSecret(w: WithPrivateValScoped): Int = w.secretVal
71+
}
72+
73+
// Test case 9: @publicInBinary on private[Scope] lazy val
74+
class WithLazyVal {
75+
@publicInBinary private[WithLazyVal] lazy val computed: String = "computed"
76+
}
77+
78+
object WithLazyVal {
79+
inline def getComputed(w: WithLazyVal): String = w.computed
80+
}
81+
}
82+
83+
// Test case 10: Top-level class with @publicInBinary (not nested in Utils)
84+
class TopLevelPublicInBinary @publicInBinary private (val value: Double)
85+
86+
object TopLevelPublicInBinary {
87+
inline def create(inline v: Double): TopLevelPublicInBinary = new TopLevelPublicInBinary(v)
88+
}
89+
90+
// Test case 11: Trait with companion having @publicInBinary class
91+
trait Describable {
92+
def describe: String
93+
}
94+
95+
class DescribableImpl @publicInBinary private (val desc: String) extends Describable {
96+
def describe: String = desc
97+
}
98+
99+
object DescribableImpl {
100+
inline def create(inline d: String): Describable = new DescribableImpl(d)
101+
}

0 commit comments

Comments
 (0)