Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

structural types evade access restrictions and other messy parts #5352

Closed
scabug opened this issue Dec 30, 2011 · 3 comments
Closed

structural types evade access restrictions and other messy parts #5352

scabug opened this issue Dec 30, 2011 · 3 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Dec 30, 2011

See #3197 for a different side of the same coin. This is worse though.

scala> class Bar1 extends Bar { protected def f(): Int = 5 }
defined class Bar1

scala> class Bar2 extends Bar { protected def f(): Int = 5 }
defined class Bar2

scala> List(new Bar1, new Bar2)
res1: List[Bar{protected def f(): Int}] = List(Bar1@55661f7b, Bar2@239cf00a)

scala> type BarF = { def f(): Int }
defined type alias BarF

scala> var x: BarF = _
x: BarF = null

scala> x = res1.head
x: BarF = Bar1@55661f7b

scala> x.f
res2: Int = 5

scala> (new Bar1).f
<console>:10: error: method f in class Bar1 cannot be accessed in Bar1
 Access to protected method f not permitted because
 enclosing object $iw is not a subclass of 
 class Bar1 where target is defined
              (new Bar1).f
                         ^

We really need to establish what is allowed in terms of modifiers within refinement types. You can't even declare them, they have to be inferred:

scala> type Foo = { protected def f(): Int }
<console>:1: error: illegal start of declaration
       type Foo = { protected def f(): Int }
                    ^

It loses the local boundary:

scala> class A {
     |   abstract class Bar { protected[this] def f(): Any }
     |   class Bar1 extends Bar { protected[this] def f(): Int = 5 }
     |   class Bar2 extends Bar { protected[this] def f(): Int = 5 }
     |   
     |   def f = List(new Bar1, new Bar2)
     | }
defined class A

scala> (new A).f _
res0: () => List[A#Bar{protected def f(): Int}] = <function0>

It doesn't notice refinements in access on their own, but applies them if it is in the neighborhood refining the return type, leading to unpredictable behavior:

package bippy {
  package dingus {
    abstract class Bar { protected[dingus] def f(): Any }
    class Bar1 extends Bar { protected[bippy] def f(): Int = 5 }
    class Bar2 extends Bar { protected[bippy] def f(): Int = 5 }
    // Infers List[Bar{protected[bippy] def f(): Int}]
    
    abstract class Bar { protected[dingus] def f(): Int } // note no return type refinement
    class Bar1 extends Bar { protected[bippy] def f(): Int = 5 }
    class Bar2 extends Bar { protected[bippy] def f(): Int = 5 }
    // Infers List[Bar]
  }
  import dingus._
  object A { def g2 = List(new Bar1, new Bar2) }
}

There is no way to widen access if a method was declared protected but may be widened in subclasses, as demonstrated in #3197.

To top it off, all the access modifiers are useless. You can't call a protected method unless you're a member of the same class, but when is this going to arise with an inferred structural type (and again, the access modifiers can only arise through inference.)

scala> object A {
     |   abstract class Bar { protected def f(): Any }
     |   class Bar1 extends Bar { protected def f(): Int = 5 }
     |   class Bar2 extends Bar { protected def f(): Int = 5 ; def h = g map (_.f()) }
     |   
     |   def g = List(new Bar1, new Bar2)
     | }
<console>:10: error: method f cannot be accessed in A.Bar{protected def f(): Int}
 Access to protected method f not permitted because
 enclosing class Bar2 in object A is not a subclass of 
 A in object A where target is defined
         class Bar2 extends Bar { protected def f(): Int = 5 ; def h = g map (_.f()) }
                                                                                ^                                                                                      ^

Now there's a nice confusing error. Regardless of what ought to happen, I don't think there's any way to call that protected f.

I propose that structural types only infer public signatures. If everything going into the lub doesn't have a public signature, there's no such method in the lub. Simple and removes no capability which actually works.

@scabug
Copy link
Author

scabug commented Dec 30, 2011

Imported From: https://issues.scala-lang.org/browse/SI-5352?orig=1
Reporter: @paulp
Other Milestones: 2.10.0

@scabug
Copy link
Author

scabug commented Jan 31, 2012

@odersky said:
I think we need to let only public members leak into refinements. Everything else leads to trouble. Paul, I give you first shot at enforcing that, but feel free to reassign if somebody else should do it.

@scabug
Copy link
Author

scabug commented Jan 31, 2012

@paulp said:
I'd forgotten, but after running into some other issue, I already did it (in bedb33fd7cb .)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants