[Discussion] A Problem With SE-0025?


(Robert Widmann) #1

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann


(Matthew Johnson) #2

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

···

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #3

Shouldn’t it act theoretically something like this:

// Begin file A (which acts like a scope)

// where `type` can be enum, struct, class or protocol
private type X {}

fileprivate type Y {}

public struct Z {
     
    var x: X
    var y: Y
}
// End file A
private should always behave the same as fileprivate on the same scope level of declaration, where fileprivate is accessible from anywhere within the same file no matter in which level it was declared.

// Begin file A (which acts like a scope)

public struct Z {
     
    fileprivate var a = 0
    private var b = "z"
}

public struct X {
     
        static func foo() {
             
            var z = Z()
            z.a // a is visible here
            z.b // raises an error because b is not visible from that scope
        }
}
// End file A
That how I imagine this to work.

···

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 20:32:09, Robert Widmann via swift-evolution (swift-evolution@swift.org) schrieb:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #4

The scope of the *declaration* is not the issue. The scope of its *members* is.

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

···

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #5

Yes, and it does, and this is not the issue. The problem is that when we generate the initializer for a private type it is invisible to those outside its scope (which is, by definition, everybody else). X cannot be initialized and cannot declare any initializer that can possibly be visible because you cannot declare members with a higher access level than their containing declaration.

~Robert Widmann

2016/06/15 11:46、Adrian Zubarev via swift-evolution <swift-evolution@swift.org> のメッセージ:

···

Shouldn’t it act theoretically something like this:

// Begin file A (which acts like a scope)

// where `type` can be enum, struct, class or protocol
private type X {}

fileprivate type Y {}

public struct Z {
     
    var x: X
    var y: Y
}
// End file A
private should always behave the same as fileprivate on the same scope level of declaration, where fileprivate is accessible from anywhere within the same file no matter in which level it was declared.

// Begin file A (which acts like a scope)

public struct Z {
     
    fileprivate var a = 0
    private var b = "z"
}

public struct X {
     
        static func foo() {
             
            var z = Z()
            z.a // a is visible here
            z.b // raises an error because b is not visible from that scope
        }
}
// End file A
That how I imagine this to work.

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 20:32:09, Robert Widmann via swift-evolution (swift-evolution@swift.org) schrieb:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #6

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
    var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

Does this help?

-Matthew

···

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #7

The scope of the *declaration* is not the issue. The scope of its
*members* is.

Oy, if we're to have both `fileprivate` and `private`, then the previously
visually appealing rule about maximum visibility of members will have to be
a little messy.

IIUC, the problem is this: the current rule is that an internal type can
have members that are at most internally visible (not public), and that a
private type can have members that are at most privately visible. This is
nice and tidy because in all cases, the declaration and the members it
encloses are always in the same module and in the same file. But, with the
new meaning of `private`, the top-level declaration is actually in a
*different* scope than its members. So, a private type must be allowed to
have members that are at most fileprivately visible.

···

On Wed, Jun 15, 2016 at 1:37 PM, Robert Widmann via swift-evolution < swift-evolution@swift.org> wrote:

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

> The scope for a top-level declaration is the file itself. This means
that top-level declarations with `private` and `fileprivate` should have
the same behavior. They should not be uninstantiable or unusable.
>
> -Matthew
>
>> On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>> While implementing SE-0025 (fileprivate), I noticed an interesting bug
in the proposal. Under the implementation outlined there, any top-level
structure, class, or enum declared private cannot possibly be instantiated
and so cannot be used in any way. Because of this, private top-level
declarations are more often than not blown away entirely by the compiler
for being unused. It seems strange to me to allow a key language feature
to act solely as a hint to the optimizer to reduce the size of your
binary. Perhaps the restrictions around private needs to be relaxed or the
line between fileprivate and private needs to be investigated again by the
community before inclusion in the language.
>>
>> Thoughts?
>>
>> ~Robert Widmann
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution@swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #8

Then it is no different from fileprivate.

~Robert Widmann

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

···

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
   var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #9

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
   var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

···

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #10

>
> The scope of the *declaration* is not the issue. The scope of its
*members* is.

Let’s consider an example:

private struct Foo {
    var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file
scope. The `bar` member has no access modifier so it has the same
visibility as the struct itself, which is file scope. This will also be
true of the implicitly synthesized memberwise initializer.

This means that it is possible to initialize `foo` with a newly
constructed instance of `Foo` and to modify the `bar` member anywhere else
in the same file.

If `bar` was also declared `private` this would not be possible as its
visibility would be restricted to the surrounding scope of the initial
declaration of `Foo`. This means `Foo` would need to provide an explicit
initializer or factory method with `fileprivate` visibility in order to be
usable.

Members with no explicit access modifier should have the same *visibility*
as their containing type (with a maximum implicit visibility of internal),
not the same *modifier* as their containing type. The only case where
there is a distinction is the new `private` visibility. Maybe that is what
is causing the confusion?

Does this help?

Perhaps one solution is to prohibit top-level `private` declarations? If
they're required to be written as `fileprivate`, then the visibility and
the modifier used to express it are once again always in sync.

···

On Wed, Jun 15, 2016 at 1:47 PM, Matthew Johnson via swift-evolution < swift-evolution@swift.org> wrote:

> On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> > wrote:

-Matthew

>
> ~Robert Widmann
>
> 2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:
>
>> The scope for a top-level declaration is the file itself. This means
that top-level declarations with `private` and `fileprivate` should have
the same behavior. They should not be uninstantiable or unusable.
>>
>> -Matthew
>>
>>> On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution < > swift-evolution@swift.org> wrote:
>>>
>>> While implementing SE-0025 (fileprivate), I noticed an interesting bug
in the proposal. Under the implementation outlined there, any top-level
structure, class, or enum declared private cannot possibly be instantiated
and so cannot be used in any way. Because of this, private top-level
declarations are more often than not blown away entirely by the compiler
for being unused. It seems strange to me to allow a key language feature
to act solely as a hint to the optimizer to reduce the size of your
binary. Perhaps the restrictions around private needs to be relaxed or the
line between fileprivate and private needs to be investigated again by the
community before inclusion in the language.
>>>
>>> Thoughts?
>>>
>>> ~Robert Widmann
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution@swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #11

Then it is no different from fileprivate.

Yes, at file scope this is true. A reasonable argument can be made for prohibiting it at file scope for the sake of clarity, but if it is allowed it should behave the same as fileprivate.

Note that similar issues apply for nests types:

struct S {
    private struct T {
        var foo: Int
    }
    // ok - T and foo are visible inside the scope of S
    private var t = T(foo: 42)
}

···

Sent from my iPad

On Jun 15, 2016, at 1:51 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

~Robert Widmann

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #12

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

···

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #13

How about this, it might make more sense and relax the behavior a little:

// 1. file scope (Level 0)

// Level 0 because its declared at file scope
private struct A {
     
    /* implicit private */ init() {} // Level 1
}

// Level 0
struct B {
     
    // `A` is visible here and is seen as `fileprivate` because it is accessed
    // from a level >= itself (imagine something like a "scope level tree").
    // also every member of `A` are interpreted as
    // `fileprivate` and everyting is visible again as intended
    var a: A = A()
}

// 2. file scope (Level 0)

struct C {
     
    // `A` is not visible here
    // `B` is visible because its `internal` by default
}
Does this makes sense to you?

···

___________

file Root |
___________|

   >______________________
__|_________________ __|_________________________

struct B (Level 0) | | private struct A (Level 0) |
____________________| |____________________________|

   > >
__|______________ _____|____________________

var a (Level 1) | | private init() (Level 1) |
_________________| |__________________________|

To check if `a` has visibility to `init` of `A` we need to compare the
visibility level of `B` against `A`. If it is equal than `private` is
handled as `fileprivate` which means that any other member from within
the scope of `A` are also handled as `fileprivate`. We've get the same result
if `B`s level is greater than `A`s. That means that we'll get back the
visibility by relaxing `private` in some cases as `fileprivate`.
The described behavior is the same for private on higher levels, but we don’t describe it as fileprivate there (or can we do that?):

extension B {

   private func foo() {
     
        // `boo` should be visible here
    }
     
    private func boo() {
         
        // `foo` should be visible here
    }
}

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 20:53:21, Robert Widmann via swift-evolution (swift-evolution@swift.org) schrieb:

Then it is no different from fileprivate.

~Robert Widmann

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #14

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
    internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com> のメッセージ:

···

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #15

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
    internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

That diagnostic is a good thing. I am not suggesting that you to disregard it.

What you are missing is that the meaning of `private` is scope-dependent. The following example is perfectly valid:

private struct Foo {
    fileprivate var x : String = “”
}

`fileprivate` inside `Foo` specifies the same visibility as `private` at the top level, thus we are not “raising" the visibility of the member. If no modifier is provided for `x` it receives the same visibility as its containing type (up to `internal`).

Consider another example:

struct Foo {
    private struct Bar {
        var x : String = “”
    }
}

In this example we *cannot* mark `x` as `fileprivate` because that *would* raise the visibility of `x` beyond that of `Bar`. `Bar` is only visible inside `Foo`, not at file scope. This means that `x` also has visibility throughout the lexical scope of `Foo`, but not outside of it.

-Matthew

···

On Jun 15, 2016, at 2:19 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #16

How about this, it might make more sense and relax the behavior a little:

// 1. file scope (Level 0)

// Level 0 because its declared at file scope
private struct A {
     
    /* implicit private */ init() {} // Level 1
}

// Level 0
struct B {
     
    // `A` is visible here and is seen as `fileprivate` because it is accessed
    // from a level >= itself (imagine something like a "scope level tree").
    // also every member of `A` are interpreted as
    // `fileprivate` and everyting is visible again as intended
    var a: A = A()
}

// 2. file scope (Level 0)

struct C {
     
    // `A` is not visible here
    // `B` is visible because its `internal` by default
}
Does this makes sense to you?

This does not make sense to me at all. In order for this to be consistent with the rest of our access control rules `init` in #1 has the same visibility as `A`. That is why it is visible inside `B`.

Your example #2 is just incorrect. `A` is visible inside the scope of `C`.

Now that we have introduced a scope-dependent access modifier it is an incorrect mental model to consider members with no access modifier as having the exact same access modifier as the containing scope. This is no longer correct. They have the same *visibility*, not the same *access modifier*.

···

On Jun 15, 2016, at 2:25 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

___________
> file Root |
>___________|
   >______________________
__|_________________ __|_________________________
> struct B (Level 0) | | private struct A (Level 0) |
>____________________| |____________________________|
   > >
__|______________ _____|____________________
> var a (Level 1) | | private init() (Level 1) |
>_________________| |__________________________|

To check if `a` has visibility to `init` of `A` we need to compare the
visibility level of `B` against `A`. If it is equal than `private` is
handled as `fileprivate` which means that any other member from within
the scope of `A` are also handled as `fileprivate`. We've get the same result
if `B`s level is greater than `A`s. That means that we'll get back the
visibility by relaxing `private` in some cases as `fileprivate`.
The described behavior is the same for private on higher levels, but we don’t describe it as fileprivate there (or can we do that?):

extension B {

   private func foo() {
     
        // `boo` should be visible here
    }
     
    private func boo() {
         
        // `foo` should be visible here
    }
}

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 20:53:21, Robert Widmann via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

Then it is no different from fileprivate.

~Robert Widmann

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

>
>> On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:
>>
>> The scope of the *declaration* is not the issue. The scope of its *members* is.
>
> Let’s consider an example:
>
> private struct Foo {
> var bar: Int
> }
>
> // elsewhere in the same file:
> var foo = Foo(bar: 42)
> foo.bar = 44
>
> `Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.
>
> This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.
>
> If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.
>
> Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?
>
> Does this help?
>
> -Matthew
>
>>
>> ~Robert Widmann
>>
>> 2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:
>>
>>> The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.
>>>
>>> -Matthew
>>>
>>>> On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:
>>>>
>>>> While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.
>>>>
>>>> Thoughts?
>>>>
>>>> ~Robert Widmann
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution@swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Robert Widmann) #17

The meaning of private according to the proposal is not scope-dependent, it is decl-dependent. The mental gymnastics we are both going through right now are not in the proposal. I would like them to be.

~Robert Widmann

2016/06/15 12:26、Matthew Johnson <matthew@anandabits.com> のメッセージ:

···

On Jun 15, 2016, at 2:19 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
    internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

That diagnostic is a good thing. I am not suggesting that you to disregard it.

What you are missing is that the meaning of `private` is scope-dependent. The following example is perfectly valid:

private struct Foo {
    fileprivate var x : String = “”
}

`fileprivate` inside `Foo` specifies the same visibility as `private` at the top level, thus we are not “raising" the visibility of the member. If no modifier is provided for `x` it receives the same visibility as its containing type (up to `internal`).

Consider another example:

struct Foo {
    private struct Bar {
        var x : String = “”
    }
}

In this example we *cannot* mark `x` as `fileprivate` because that *would* raise the visibility of `x` beyond that of `Bar`. `Bar` is only visible inside `Foo`, not at file scope. This means that `x` also has visibility throughout the lexical scope of `Foo`, but not outside of it.

-Matthew

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #18

The meaning of private according to the proposal is not scope-dependent, it is decl-dependent. The mental gymnastics we are both going through right now are not in the proposal. I would like them to be.

I agree that the proposal is too sparse on the details and that is the source of the confusion.

Ilya wanted me to write this proposal and only did it himself because I didn’t have time to do it as quickly as he wanted. I know what the original intent of the proposal was (I can’t speak for the core team on their mental model of it when they accepted it).

That said, the title of the proposal is “scoped access level” and the detailed design says "visible only within that lexical scope”. And the entire discussion was always about lexical scope. I believe the reason the “proposed solution” section says “current declaration” is because *most* lexical scopes are introduced by a declaration. That is sloppy language that should have been cleaned up prior to review.

I appreciate you working on implementing this. I also understand if you want to hold off until the core team can chime in on the semantics they intend.

Matthew

···

On Jun 15, 2016, at 2:29 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

~Robert Widmann

2016/06/15 12:26、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 2:19 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
    internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

That diagnostic is a good thing. I am not suggesting that you to disregard it.

What you are missing is that the meaning of `private` is scope-dependent. The following example is perfectly valid:

private struct Foo {
    fileprivate var x : String = “”
}

`fileprivate` inside `Foo` specifies the same visibility as `private` at the top level, thus we are not “raising" the visibility of the member. If no modifier is provided for `x` it receives the same visibility as its containing type (up to `internal`).

Consider another example:

struct Foo {
    private struct Bar {
        var x : String = “”
    }
}

In this example we *cannot* mark `x` as `fileprivate` because that *would* raise the visibility of `x` beyond that of `Bar`. `Bar` is only visible inside `Foo`, not at file scope. This means that `x` also has visibility throughout the lexical scope of `Foo`, but not outside of it.

-Matthew

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Adrian Zubarev) #19

Ok I already see side effects in my idea which breaks the entire scoped access level thing :confused:

class A {
   // incrementTwice() is not visible here
}

extension A {
   private func incrementTwice() {

   }
}
In my model incrementTwice would be visible in A which it shouldn’t (I agree to that).

That seem like a nasty bug that we missed during review.

···

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 21:30:20, Robert Widmann via swift-evolution (swift-evolution@swift.org) schrieb:

The meaning of private according to the proposal is not scope-dependent, it is decl-dependent. The mental gymnastics we are both going through right now are not in the proposal. I would like them to be.

~Robert Widmann

2016/06/15 12:26、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 2:19 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

That diagnostic is a good thing. I am not suggesting that you to disregard it.

What you are missing is that the meaning of `private` is scope-dependent. The following example is perfectly valid:

private struct Foo {
fileprivate var x : String = “”
}

`fileprivate` inside `Foo` specifies the same visibility as `private` at the top level, thus we are not “raising" the visibility of the member. If no modifier is provided for `x` it receives the same visibility as its containing type (up to `internal`).

Consider another example:

struct Foo {
private struct Bar {
var x : String = “”
}
}

In this example we *cannot* mark `x` as `fileprivate` because that *would* raise the visibility of `x` beyond that of `Bar`. `Bar` is only visible inside `Foo`, not at file scope. This means that `x` also has visibility throughout the lexical scope of `Foo`, but not outside of it.

-Matthew

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #20

Ok I already see side effects in my idea which breaks the entire scoped access level thing :confused:

class A {
   // incrementTwice() is not visible here
}

extension A {
   private func incrementTwice() {

   }
}
In my model incrementTwice would be visible in A which it shouldn’t (I agree to that).

That seem like a nasty bug that we missed during review.

You are correct that `incrementTwice` is not visible inside `class A`.

What seems like a nasty bug missed during review? I don’t follow you there.

This proposal was specifically designed to follow Swift’s design of a scope-based access control mechanism rather than a type-based access control mechanism that is common in other languages.

···

On Jun 15, 2016, at 2:34 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 15. Juni 2016 um 21:30:20, Robert Widmann via swift-evolution (swift-evolution@swift.org <mailto:swift-evolution@swift.org>) schrieb:

The meaning of private according to the proposal is not scope-dependent, it is decl-dependent. The mental gymnastics we are both going through right now are not in the proposal. I would like them to be.

~Robert Widmann

2016/06/15 12:26、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 2:19 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

We have diagnostics specifically to prohibit this case. You cannot raise the access level of members.

private struct Foo {
    internal var x : String = ""
}

warning: declaring an internal var for a private struct.

Hence, the highest allowable level of access for x is private and it becomes invisible.

I would not like the compiler to synthesize this in my code, and if it did I would like the proposal to say it will raise access levels of members as you would like it to.

That diagnostic is a good thing. I am not suggesting that you to disregard it.

What you are missing is that the meaning of `private` is scope-dependent. The following example is perfectly valid:

private struct Foo {
    fileprivate var x : String = “”
}

`fileprivate` inside `Foo` specifies the same visibility as `private` at the top level, thus we are not “raising" the visibility of the member. If no modifier is provided for `x` it receives the same visibility as its containing type (up to `internal`).

Consider another example:

struct Foo {
    private struct Bar {
        var x : String = “”
    }
}

In this example we *cannot* mark `x` as `fileprivate` because that *would* raise the visibility of `x` beyond that of `Bar`. `Bar` is only visible inside `Foo`, not at file scope. This means that `x` also has visibility throughout the lexical scope of `Foo`, but not outside of it.

-Matthew

~Robert Widmann

2016/06/15 12:14、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 2:04 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

2016/06/15 11:47、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

On Jun 15, 2016, at 1:37 PM, Robert Widmann <devteam.codafi@gmail.com <mailto:devteam.codafi@gmail.com>> wrote:

The scope of the *declaration* is not the issue. The scope of its *members* is.

Let’s consider an example:

private struct Foo {
  var bar: Int
}

// elsewhere in the same file:
var foo = Foo(bar: 42)
foo.bar = 44

`Foo` is declared private. Private for this declaration is at the file scope. The `bar` member has no access modifier so it has the same visibility as the struct itself, which is file scope. This will also be true of the implicitly synthesized memberwise initializer.

No, it is also private. It does not inherit its parent scope because, following the letter of the proposal, that symbol will only be visible within the current declaration. We cannot arbitrarily break access control rules because it is convenient in one special case.

This means that it is possible to initialize `foo` with a newly constructed instance of `Foo` and to modify the `bar` member anywhere else in the same file.

bar is not visible here. If it were you could break access control rules.

If `bar` was also declared `private` this would not be possible as its visibility would be restricted to the surrounding scope of the initial declaration of `Foo`. This means `Foo` would need to provide an explicit initializer or factory method with `fileprivate` visibility in order to be usable.

bar is private. Declarations within Foo cannot decide to raise that access level to make themselves more visible. If this should be the case, the proposal must be amended as much.

Members with no explicit access modifier should have the same *visibility* as their containing type (with a maximum implicit visibility of internal), not the same *modifier* as their containing type. The only case where there is a distinction is the new `private` visibility. Maybe that is what is causing the confusion?

That is not what the proposal says. The proposal says it is invisible outside the current decl, which is the containing structure here.

The access modifier is applied to `Foo`, not `bar`. `Foo` is visible in the scope of the “current declaration” (this is badly worded - it should say current scope, which is the file). Because `Foo` has a visibility lower than `internal` the default visibility of its members match the visibility of `Foo`, which again is the current scope: the file. The detailed design section of the proposal is sparse, but it correctly uses the phrase "visible only within that lexical scope” rather than the less precise (in the case of top-level code) “current declaration”.

I didn’t write the proposal but I was very heavily involved in the discussions and IIRC I provided the original suggestion for introducing a scoped access modifier.

If my example was the following:

private struct Foo {
  private var bar: Int
}

Then what you are saying would be correct. However, The example I showed did not provide an access modifier for `bar`.

You cannot just apply the same *access modifier* to members of the type that do not contain an access modifier. You have to apply the same *visibility* (limited to internal). When a type is marked as `private` in the lexical scope of the file, its unmodified members will be visible in the same lexical scope as the type itself (which is the file in the current example).

-Matthew

Does this help?

-Matthew

~Robert Widmann

2016/06/15 11:36、Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> のメッセージ:

The scope for a top-level declaration is the file itself. This means that top-level declarations with `private` and `fileprivate` should have the same behavior. They should not be uninstantiable or unusable.

-Matthew

On Jun 15, 2016, at 1:31 PM, Robert Widmann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While implementing SE-0025 (fileprivate), I noticed an interesting bug in the proposal. Under the implementation outlined there, any top-level structure, class, or enum declared private cannot possibly be instantiated and so cannot be used in any way. Because of this, private top-level declarations are more often than not blown away entirely by the compiler for being unused. It seems strange to me to allow a key language feature to act solely as a hint to the optimizer to reduce the size of your binary. Perhaps the restrictions around private needs to be relaxed or the line between fileprivate and private needs to be investigated again by the community before inclusion in the language.

Thoughts?

~Robert Widmann
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution