Scala Programming Language
  1. Scala Programming Language
  2. SI-5008

qualified private allowed to escape via aliases

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Trivial Trivial
    • Resolution: Won't Fix
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: None
    • Labels:
      None

      Description

      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
        }
      }
      

        Issue Links

          Activity

          Hide
          Paul Phillips added a comment -

          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.

          Show
          Paul Phillips added a comment - 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.
          Hide
          Lukas Rytz added a comment -

          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.

          Show
          Lukas Rytz added a comment - 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.
          Hide
          Paul Phillips added a comment -

          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.

          Show
          Paul Phillips added a comment - 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.
          Hide
          Paul Phillips added a comment -

          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.
          
          Show
          Paul Phillips added a comment - 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.
          Hide
          Paul Phillips added a comment -

          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 { }
            }
          }
          
          Show
          Paul Phillips added a comment - 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 { } } }
          Hide
          Paul Phillips added a comment -

          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.

          Show
          Paul Phillips added a comment - 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.
          Hide
          Paul Phillips added a comment -

          Overtaken by events.

          Show
          Paul Phillips added a comment - Overtaken by events.

            People

            • Assignee:
              Martin Odersky
              Reporter:
              Paul Phillips
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development