Skip to content

Commit 2e4487f

Browse files
authored
Merge pull request #4529 from dotty-staging/fix-4496-part2
Fix #4496: call ensureAccessible to bypass class-level accessibility
2 parents c8e18a3 + eb43fe9 commit 2e4487f

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

library/src/scala/reflect/Selectable.scala

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class Selectable(val receiver: Any) extends AnyVal with scala.Selectable {
55
val rcls = receiver.getClass
66
try {
77
val fld = rcls.getField(name)
8+
ensureAccessible(fld)
89
fld.get(receiver)
910
}
1011
catch {
@@ -17,6 +18,7 @@ class Selectable(val receiver: Any) extends AnyVal with scala.Selectable {
1718
val rcls = receiver.getClass
1819
val paramClasses = paramTypes.map(_.runtimeClass)
1920
val mth = rcls.getMethod(name, paramClasses: _*)
21+
ensureAccessible(mth)
2022
paramTypes.length match {
2123
case 0 => () =>
2224
mth.invoke(receiver)

tests/neg/i4496b.scala

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import scala.reflect.Selectable.reflectiveSelectable
2+
3+
trait Foo1 { val a: Int }
4+
trait Foo2 { def a: Int }
5+
trait Foo3 { var a: Int }
6+
7+
object TestStructuralVar {
8+
type T0 = {var a: Int} // error
9+
object TestStructuralVar {
10+
type T = {val a: Int; def a_=(x: Int): Unit}
11+
def upcast1(v: Foo1): T = v // error
12+
def upcast2(v: Foo2): T = v // error
13+
def upcast3(v: Foo3): T = v
14+
def verify(v: T) = ()
15+
def test(): Unit = {
16+
verify(upcast1(new Foo1 { val a = 10 }))
17+
verify(upcast2(new Foo2 { val a = 10 }))
18+
verify(upcast3(new Foo3 { var a = 10 }))
19+
}
20+
}
21+
22+
def main(args: Array[String]): Unit = {
23+
TestStructuralVar.test()
24+
}
25+
}

tests/run/i4496b.scala

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import scala.reflect.Selectable.reflectiveSelectable
2+
3+
trait Foo1 { val a: Int }
4+
trait Foo2 { def a: Int }
5+
trait Foo3 { var a: Int }
6+
7+
object Test {
8+
private class FooBar1 extends Foo1 { val a: Int = 10 }
9+
private class FooBar2 extends Foo2 { def a: Int = 10 }
10+
private class FooBar3 extends Foo3 { var a: Int = 10 }
11+
12+
private class Bar1 { val a: Int = 10 }
13+
private class Bar2 { def a: Int = 10 }
14+
private class Bar3 { var a: Int = 10 }
15+
16+
object TestStructuralVal {
17+
// This test is also an (abstracted) motivating example.
18+
19+
// Consider one module upcasting all these instances to T. These casts are clearly well-typed.
20+
type T = {val a: Int}
21+
def upcast1(v: Foo1): T = v
22+
def upcast2(v: Foo2): T = v
23+
def upcast3(v: Foo3): T = v
24+
25+
// These accesses are also clearly well-typed
26+
def consume(v: T) = v.a
27+
inline def consumeInl(v: T) = v.a
28+
def verify(v: T) = {
29+
assert(consume(v) == 10)
30+
assert(consumeInl(v) == 10)
31+
assert(v.a == 10)
32+
}
33+
34+
def test(): Unit = {
35+
// These calls are also clearly well-typed, hence can't be rejected.
36+
verify(upcast1(new Foo1 { val a = 10 }))
37+
verify(upcast2(new Foo2 { val a = 10 }))
38+
verify(upcast3(new Foo3 { var a = 10 }))
39+
// Ditto, so we must override access control to the class.
40+
verify(upcast1(new FooBar1))
41+
verify(upcast2(new FooBar2))
42+
verify(upcast3(new FooBar3))
43+
44+
// Other testcases
45+
verify(new {val a = 10} : T)
46+
verify(new {var a = 10} : T)
47+
verify(new {def a = 10} : T)
48+
49+
verify(new Bar1 : T)
50+
verify(new Bar2 : T)
51+
verify(new Bar3 : T)
52+
}
53+
}
54+
55+
object TestStructuralDef {
56+
type T = {def a: Int}
57+
def upcast1(v: Foo1): T = v
58+
def upcast2(v: Foo2): T = v
59+
def upcast3(v: Foo3): T = v
60+
def consume(v: T) = v.a
61+
inline def consumeInl(v: T) = v.a
62+
def verify(v: T) = {
63+
assert(consume(v) == 10)
64+
assert(consumeInl(v) == 10)
65+
assert(v.a == 10)
66+
}
67+
68+
def test(): Unit = {
69+
verify(upcast1(new Foo1 { val a = 10 }))
70+
verify(upcast2(new Foo2 { val a = 10 }))
71+
verify(upcast3(new Foo3 { var a = 10 }))
72+
73+
verify(upcast1(new FooBar1))
74+
verify(upcast2(new FooBar2))
75+
verify(upcast3(new FooBar3))
76+
77+
verify(new {val a = 10} : T)
78+
verify(new {var a = 10} : T)
79+
verify(new {def a = 10} : T)
80+
81+
verify(new Bar1 : T)
82+
verify(new Bar2 : T)
83+
verify(new Bar3 : T)
84+
}
85+
}
86+
87+
object TestStructuralVar {
88+
type T = {val a: Int; def a_=(x: Int): Unit}
89+
def upcast3(v: Foo3): T = v
90+
def consume(v: T) = v.a
91+
inline def consumeInl(v: T) = v.a
92+
def verify(v: T) = {
93+
assert(consume(v) == 10)
94+
assert(consumeInl(v) == 10)
95+
assert(v.a == 10)
96+
// Pending, per https://github.com/lampepfl/dotty/issues/4528.
97+
// v.a = 11
98+
// assert(consume(v) == 11)
99+
// assert(consumeInl(v) == 11)
100+
// assert(v.a == 11)
101+
}
102+
103+
def test(): Unit = {
104+
verify(upcast3(new Foo3 { var a = 10 }))
105+
verify(upcast3(new FooBar3))
106+
verify(new {var a = 10} : T)
107+
verify(new Bar3 : T)
108+
}
109+
}
110+
111+
def main(args: Array[String]): Unit = {
112+
TestStructuralVal.test()
113+
TestStructuralDef.test()
114+
TestStructuralVar.test()
115+
}
116+
}

0 commit comments

Comments
 (0)