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

qualified private allowed to escape via aliases #5008

Closed
scabug opened this issue Sep 19, 2011 · 8 comments
Closed

qualified private allowed to escape via aliases #5008

scabug opened this issue Sep 19, 2011 · 8 comments
Assignees

Comments

@scabug
Copy link

scabug commented Sep 19, 2011

There are so many access related tickets open I can't tell in a reasonable amount of time if this already exists. Not so great.

package foo {
  class A {
    private[foo] trait Foo
    // Public type alias is disallowed if Foo is private or private[this].
    // But allowed if private[foo] or private[A].
    type Bar = Foo
  
    def f(x: Foo) = 5
  }
}

package bar {
  class B extends foo.A {
    // Disallowed as expected.
    //
    // val x1 = new Foo { }
    // ./a.scala:12: error: trait Foo in class A cannot be accessed in bar.B
    //     val x1 = new Foo { }
    //                  ^
    // one error found
    
    // allowed
    val x2 = new Bar { }
    // allowed
    override def f(x: Bar) = 10
  }
}
@scabug
Copy link
Author

scabug commented Sep 19, 2011

Imported From: https://issues.scala-lang.org/browse/SI-5008?orig=1
Reporter: @paulp
See #4400

@scabug
Copy link
Author

scabug commented Oct 11, 2011

@paulp said:
Assigning to meeting to find out what I can't figure out from the spec: the precise intended distinction between visibility and access of less-than-public types.

@scabug
Copy link
Author

scabug commented Jan 31, 2012

@lrytz said:
according to martin: in the spec, there's a nifty difference between private (private[this]) and qualified private members. he is sure that the behavior in this bug is according to the spec, in any case it seems correct.

@scabug
Copy link
Author

scabug commented Jan 31, 2012

@paulp said:
Please afford me the tiny amount of respect necessary to provide a better resolution than "martin says so." If my tickets are to be closed as "not a bug" without even a nod toward a citation, well, I don't want to finish that sentence.

@scabug
Copy link
Author

scabug commented May 3, 2012

@paulp said:
I sort of wish someone else had drawn this straw so I didn't have to be reminded of how little respect I am given and how the specification is used like some kind of wizardly bludgeoning device for which no explanations are necessary. So I spent another few hours going line by line over the spec looking for the part which specifies this behavior so precisely that a ticket opened by the leading contributer of compiler code could be closed without even a citation.

Not only did I not find it, I can't find anything which unambiguously states what it even means to attach an access modifier to a type; there certainly is nothing about what happens under aliasing; and I'm dying to see the language which would allow one to discover the behavior demonstrated here.

For nine possible modifiers ("public", private[pkg], private[class], private[this], private, protected[pkg], protected[class], protected[this], protected) there are a total of two distinct behaviors shown in this code. I await the elaboration of how the "nifty" difference alluded to but not given can explain the following.

The two behavior groupings are:

private and private[this] - these are prevented from escaping everywhere
except in method parameter positions, where escape is allowed
everything else - everything acts the same as if public

Here is a comment. The code I'll attach separately.

// These are members of class bar.C, completely unrelated to class foo.A.
// The types shown below include types defined within foo.A which are:
//
//   - qualified private
//   - qualified protected
//   - object protected
//
// val a : foo.A = { /* compiled code */ }
// val xprot1 : java.lang.Object with foo.A.FooProt1 = { /* compiled code */ }
// val xprot2 : java.lang.Object with foo.A.FooProt2 = { /* compiled code */ }
// val xprot3 : java.lang.Object with foo.A.FooProt3 = { /* compiled code */ }
// val xprot4 : java.lang.Object with foo.A.FooProt4 = { /* compiled code */ }
// val xpriv3 : java.lang.Object with foo.A.FooPriv3 = { /* compiled code */ }
// val xpriv4 : java.lang.Object with foo.A.FooPriv4 = { /* compiled code */ }
//
// Indeed it will tell me a type which I cannot access:
//
// scala> new bar.C
// res0: bar.C = bar.C@1339a0dc
// 
// scala> res0.xpriv3
// res1: java.lang.Object with res0.a.FooPriv3 = bar.C$$anon$29@39556aec
// 
// scala> new res0.a.FooPriv3
// <console>:9: error: trait FooPriv3 in class A cannot be accessed in foo.A
//               new res0.a.FooPriv3
//                          ^
// Looking at how the compiler prints the types of those vals, one
// develops a suspicion how some of it is being allowed:
//
// val xpriv4: C.this.a.FooPriv4
// val xpriv3: C.this.a.FooPriv3
// val xprot4: C.this.a.FooProt4
// val xprot3: C.this.a.FooProt3
// val xprot2: C.this.a.FooProt2
// val xprot1: C.this.a.FooProt1
// 
// That is, "this" is in the prefix somewhere, it's just not a "this"
// which has any bearing.

@scabug
Copy link
Author

scabug commented May 3, 2012

@paulp said:
This compiles as given; the bits which would lead to compilation failure are commented out.

package foo {
  class A {
    trait Foo
    
    protected trait FooProt1
    protected[this] trait FooProt2
    protected[foo] trait FooProt3
    protected[A] trait FooProt4
    
    private trait FooPriv1
    private[this] trait FooPriv2
    private[foo] trait FooPriv3
    private[A] trait FooPriv4
    
    type BarProt1 = FooProt1
    type BarProt2 = FooProt2
    type BarProt3 = FooProt3
    type BarProt4 = FooProt4
    
    // type BarPriv1 = FooPriv1
    // type BarPriv2 = FooPriv2
    type BarPriv3 = FooPriv3
    type BarPriv4 = FooPriv4
    
    def fprot1(x: FooProt1) = x
    def fprot2(x: FooProt2) = x
    def fprot3(x: FooProt3) = x
    def fprot4(x: FooProt4) = x
    
    // def fpriv1(x: FooPriv1) = x
    // def fpriv2(x: FooPriv2) = x
    def fpriv3(x: FooPriv3) = x
    def fpriv4(x: FooPriv4) = x

    val yprot1 = new FooProt1 { }
    val yprot2 = new FooProt2 { }
    val yprot3 = new FooProt3 { }
    val yprot4 = new FooProt4 { }
    
    // val ypriv1 = new FooPriv1 { }
    // val ypriv2 = new FooPriv2 { }
    val ypriv3 = new FooPriv3 { }
    val ypriv4 = new FooPriv4 { }
    
    def fpriv_alt1(x: FooPriv1) = 0 // !!! isn't the private type now in the signature of the (public) method?
    def fpriv_alt2(x: FooPriv2) = 0 // !!! isn't the private[this] type now in the signature of the (public) method?
  }
  // Same package, subclass
  class B extends A {
    val xprot1 = new BarProt1 { }
    val xprot2 = new BarProt2 { }
    val xprot3 = new BarProt3 { }
    val xprot4 = new BarProt4 { }
    
    // val xpriv1 = new BarPriv1 { }
    // val xpriv2 = new BarPriv2 { }
    val xpriv3 = new BarPriv3 { }
    val xpriv4 = new BarPriv4 { }
    
    override def fprot1(x: BarProt1) = x
    override def fprot2(x: BarProt2) = x
    override def fprot3(x: BarProt3) = x
    override def fprot4(x: BarProt4) = x
    
    // override def fpriv1(x: BarPriv1) = x
    // override def fpriv2(x: BarPriv2) = x
    override def fpriv3(x: BarPriv3) = x
    override def fpriv4(x: BarPriv4) = x
  }
  // Same package, unrelated class
  class C {
    val a = new A
    import a._

    val xprot1 = new BarProt1 { }
    val xprot2 = new BarProt2 { }
    val xprot3 = new BarProt3 { }
    val xprot4 = new BarProt4 { }
    
    // val xpriv1 = new BarPriv1 { }
    // val xpriv2 = new BarPriv2 { }
    val xpriv3 = new BarPriv3 { }
    val xpriv4 = new BarPriv4 { }
  }
}

package bar {
  // Different package, subclass
  class B extends foo.A {
    val xprot1 = new BarProt1 { }
    val xprot2 = new BarProt2 { }
    val xprot3 = new BarProt3 { }
    val xprot4 = new BarProt4 { }
    
    // val xpriv1 = new BarPriv1 { }
    // val xpriv2 = new BarPriv2 { }
    val xpriv3 = new BarPriv3 { }
    val xpriv4 = new BarPriv4 { }
    
    override def fprot1(x: BarProt1) = x
    override def fprot2(x: BarProt2) = x
    override def fprot3(x: BarProt3) = x
    override def fprot4(x: BarProt4) = x
    
    // override def fpriv1(x: BarPriv1) = x
    // override def fpriv2(x: BarPriv2) = x
    override def fpriv3(x: BarPriv3) = x
    override def fpriv4(x: BarPriv4) = x
  }
  // Different package, unrelated class
  class C {
    val a = new foo.A
    import a._

    val xprot1 = new BarProt1 { }
    val xprot2 = new BarProt2 { }
    val xprot3 = new BarProt3 { }
    val xprot4 = new BarProt4 { }
    
    // val xpriv1 = new BarPriv1 { }
    // val xpriv2 = new BarPriv2 { }
    val xpriv3 = new BarPriv3 { }
    val xpriv4 = new BarPriv4 { }
  }
}

@scabug
Copy link
Author

scabug commented May 3, 2012

@paulp said:
I'm marking this as a blocker not because this issue is particularly important but because my continued enthusiasm for the endeavor depends on my being taken more seriously.

@scabug
Copy link
Author

scabug commented May 15, 2012

@paulp said:
Overtaken by events.

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

No branches or pull requests

2 participants