Should access control on extensions be banned in Swift 6?

The core team's plan of record was to make fileprivate unnecessary over time; completing that vision requires only allowing stored properties in same-file extensions, a feature which has been discussed to generally positive feedback here. Your contrary position that private should not be allowed at file scope was considered and explicitly rejected.

Allowing private at file scope is not what's forcing private extension { func foo() } not to be equivalent to extension { private func foo() }.

Rather, it is fundamental to the meaning of private that it designates a lexical scope as the visibility boundary based on its nesting level; so, if it's written at a different nesting level, it designates a different scope as a visibility boundary.

We can use an ad hoc rule that private extension { func... } is equivalent to extension { fileprivate func... } for the present moment to maintain this invariant because extensions are only allowed at file scope; if they were nestable like types are, we'd need to come up with a different spelling or rule to preserve the same behavior.

Let me reiterate again: private designates the scope at which it's written as a visibility boundary—that is its raison d'être. In no place in the language is there intended to be an ability for one to write private in one lexical scope to "cascade" (as you put it) and designate a different inner scope as the visibility boundary. This is not a bug, it is an absolute invariant of private. It is also, notably, not the subject of this thread—it’s correctness at the public API boundary that is prompting a focused revisiting of the existing design.

I don't understand why this is an example of "broken nonsense" when its behavior is exactly in line with the meaning of private elsewhere in the language, and in particular:

extension Foo {
	private func bar() {}
}

private struct Foo {
	func baz() {}
}

func main() {
	Foo().bar() // error: 'bar' is inaccessible due to 'private' protection level
	Foo().baz() // ✅
}

Please feel free to ask for more clarification if any of this remains unclear.

Right, tooling can only get you so far. And even though Xcode’s new scope-heading feature is quite nice, it doesn’t solve the problem that you still need to habitually check the scope you’re adding members to, every time. A moment’s lapse could lead to inadvertently exposing a member publicly unless you adopt additional linter rules like “all access modifiers must be explicit” or “public extension is not permitted.”

1 Like

Hypotheticals. In the meantime,

…that's a fileprivate struct, and over five years of no visible progress demonstrate that preparing for some hypothetical future where we shouldn't be annotating it that way, is delusive.


Replies, not about access control.

You two seem to be advocating for something significantly more extreme than required self. I love clarity and safeguards too but this is the kind of thing I'm imagining when I read what you're saying:

enum Module.A {
  struct Module.A.B {
    func Module.A.B.ƒ() -> Swift.Int {
      let Module.A.B.ƒ.x = 1
      return Module.A.B.ƒ.x
    }
  }
}
on that

As written it it's not valid Swift of today and I hope nobody is suggesting it was valid, as once you open this pandora box – why to stop just there: you may as well forget what "switch" or "if", etc statement you are currently "in" so there would be a need to specify that information as well near every identifier, along with some labels uniquely identifying one "if" statement from another . . . Not! Just have blocks short to not lose the context, and to facilitate that I would welcome ability to write functions on out of line (as was suggested on this forum recently):

func A.B.ƒ() -> Int {
    123
}

instead of:

extension A.B {
    func ƒ() -> Int {
    	123
    }
}

About access control: I can't help but notice this is a heated subject area for some reason. I remember that one of the first things in Swift that struck me odd was that compared to C++ everything is not "private by default" † (and coming from that school of thought I considered it should be) but years passed – you get used to anything, so I resigned on that. Though I would welcome if situation is improved and oddities disappear, should we agree on some common grounds about what's good and what's not.

oops, I started forgetting that language... struct members were public by default in C++.