From eb43fe99bc6e2abf0105fc8468874b3acefd0db7 Mon Sep 17 00:00:00 2001
From: "Paolo G. Giarrusso"
Date: Fri, 11 May 2018 20:08:29 +0200
Subject: [PATCH] Fix #4496: call ensureAccessible to bypass class-level
accessibility
Fix #4496.
---
library/src/scala/reflect/Selectable.scala | 2 +
tests/neg/i4496b.scala | 25 +++++
tests/run/i4496b.scala | 116 +++++++++++++++++++++
3 files changed, 143 insertions(+)
create mode 100644 tests/neg/i4496b.scala
create mode 100644 tests/run/i4496b.scala
diff --git a/library/src/scala/reflect/Selectable.scala b/library/src/scala/reflect/Selectable.scala
index 98f8c2666477..951cecc9b439 100644
--- a/library/src/scala/reflect/Selectable.scala
+++ b/library/src/scala/reflect/Selectable.scala
@@ -5,6 +5,7 @@ class Selectable(val receiver: Any) extends AnyVal with scala.Selectable {
val rcls = receiver.getClass
try {
val fld = rcls.getField(name)
+ ensureAccessible(fld)
fld.get(receiver)
}
catch {
@@ -17,6 +18,7 @@ class Selectable(val receiver: Any) extends AnyVal with scala.Selectable {
val rcls = receiver.getClass
val paramClasses = paramTypes.map(_.runtimeClass)
val mth = rcls.getMethod(name, paramClasses: _*)
+ ensureAccessible(mth)
paramTypes.length match {
case 0 => () =>
mth.invoke(receiver)
diff --git a/tests/neg/i4496b.scala b/tests/neg/i4496b.scala
new file mode 100644
index 000000000000..ee7a0f444774
--- /dev/null
+++ b/tests/neg/i4496b.scala
@@ -0,0 +1,25 @@
+import scala.reflect.Selectable.reflectiveSelectable
+
+trait Foo1 { val a: Int }
+trait Foo2 { def a: Int }
+trait Foo3 { var a: Int }
+
+object TestStructuralVar {
+ type T0 = {var a: Int} // error
+ object TestStructuralVar {
+ type T = {val a: Int; def a_=(x: Int): Unit}
+ def upcast1(v: Foo1): T = v // error
+ def upcast2(v: Foo2): T = v // error
+ def upcast3(v: Foo3): T = v
+ def verify(v: T) = ()
+ def test(): Unit = {
+ verify(upcast1(new Foo1 { val a = 10 }))
+ verify(upcast2(new Foo2 { val a = 10 }))
+ verify(upcast3(new Foo3 { var a = 10 }))
+ }
+ }
+
+ def main(args: Array[String]): Unit = {
+ TestStructuralVar.test()
+ }
+}
diff --git a/tests/run/i4496b.scala b/tests/run/i4496b.scala
new file mode 100644
index 000000000000..2e777f64e8ac
--- /dev/null
+++ b/tests/run/i4496b.scala
@@ -0,0 +1,116 @@
+import scala.reflect.Selectable.reflectiveSelectable
+
+trait Foo1 { val a: Int }
+trait Foo2 { def a: Int }
+trait Foo3 { var a: Int }
+
+object Test {
+ private class FooBar1 extends Foo1 { val a: Int = 10 }
+ private class FooBar2 extends Foo2 { def a: Int = 10 }
+ private class FooBar3 extends Foo3 { var a: Int = 10 }
+
+ private class Bar1 { val a: Int = 10 }
+ private class Bar2 { def a: Int = 10 }
+ private class Bar3 { var a: Int = 10 }
+
+ object TestStructuralVal {
+ // This test is also an (abstracted) motivating example.
+
+ // Consider one module upcasting all these instances to T. These casts are clearly well-typed.
+ type T = {val a: Int}
+ def upcast1(v: Foo1): T = v
+ def upcast2(v: Foo2): T = v
+ def upcast3(v: Foo3): T = v
+
+ // These accesses are also clearly well-typed
+ def consume(v: T) = v.a
+ inline def consumeInl(v: T) = v.a
+ def verify(v: T) = {
+ assert(consume(v) == 10)
+ assert(consumeInl(v) == 10)
+ assert(v.a == 10)
+ }
+
+ def test(): Unit = {
+ // These calls are also clearly well-typed, hence can't be rejected.
+ verify(upcast1(new Foo1 { val a = 10 }))
+ verify(upcast2(new Foo2 { val a = 10 }))
+ verify(upcast3(new Foo3 { var a = 10 }))
+ // Ditto, so we must override access control to the class.
+ verify(upcast1(new FooBar1))
+ verify(upcast2(new FooBar2))
+ verify(upcast3(new FooBar3))
+
+ // Other testcases
+ verify(new {val a = 10} : T)
+ verify(new {var a = 10} : T)
+ verify(new {def a = 10} : T)
+
+ verify(new Bar1 : T)
+ verify(new Bar2 : T)
+ verify(new Bar3 : T)
+ }
+ }
+
+ object TestStructuralDef {
+ type T = {def a: Int}
+ def upcast1(v: Foo1): T = v
+ def upcast2(v: Foo2): T = v
+ def upcast3(v: Foo3): T = v
+ def consume(v: T) = v.a
+ inline def consumeInl(v: T) = v.a
+ def verify(v: T) = {
+ assert(consume(v) == 10)
+ assert(consumeInl(v) == 10)
+ assert(v.a == 10)
+ }
+
+ def test(): Unit = {
+ verify(upcast1(new Foo1 { val a = 10 }))
+ verify(upcast2(new Foo2 { val a = 10 }))
+ verify(upcast3(new Foo3 { var a = 10 }))
+
+ verify(upcast1(new FooBar1))
+ verify(upcast2(new FooBar2))
+ verify(upcast3(new FooBar3))
+
+ verify(new {val a = 10} : T)
+ verify(new {var a = 10} : T)
+ verify(new {def a = 10} : T)
+
+ verify(new Bar1 : T)
+ verify(new Bar2 : T)
+ verify(new Bar3 : T)
+ }
+ }
+
+ object TestStructuralVar {
+ type T = {val a: Int; def a_=(x: Int): Unit}
+ def upcast3(v: Foo3): T = v
+ def consume(v: T) = v.a
+ inline def consumeInl(v: T) = v.a
+ def verify(v: T) = {
+ assert(consume(v) == 10)
+ assert(consumeInl(v) == 10)
+ assert(v.a == 10)
+ // Pending, per https://github.com/lampepfl/dotty/issues/4528.
+ // v.a = 11
+ // assert(consume(v) == 11)
+ // assert(consumeInl(v) == 11)
+ // assert(v.a == 11)
+ }
+
+ def test(): Unit = {
+ verify(upcast3(new Foo3 { var a = 10 }))
+ verify(upcast3(new FooBar3))
+ verify(new {var a = 10} : T)
+ verify(new Bar3 : T)
+ }
+ }
+
+ def main(args: Array[String]): Unit = {
+ TestStructuralVal.test()
+ TestStructuralDef.test()
+ TestStructuralVar.test()
+ }
+}