Cannot extend private local type

Having a top-level private class, for example:

private final class Bar {}

then, I'm able to extend it in an extension (in the same file), for example:

extension Bar {
    static func baz() {}
}

However, when I have a local private class:

enum Foo {
     private final class Bar {}
}

and try to extend it (in the same file):

private extension Foo.Bar {
    static func baz() {}
}

The compiler emits the error

private extension Foo.Bar 
                      ^  'Bar' is inaccessible due to 'private' protection level.

A use case would be when using the Observation module (from Version 15.0 beta 5 (15A5209g)), where for example a "Model" class, has been put into some name space, say "MyFeature":

import Observation

enum MyFeature { 

    @Observable
    private final class Model {
        ... 
    }
}

This wont compile, since the expansion of macro @Observable adds an extension (in file scope) like:

extension MyFeature.Model: Observation.Observable {}

and the compiler complains. The extension should also be private, if I'm correct? But anyway, it seems, this is a language issue.

Well, one could of course change the accessibility of the class Model to internal or public – but, IMHO, this would be against best practices, if the symbol "Model" would and should be only visible in this file.

1 Like

Here, private refers to the lexical scope inside Foo. Since extensions are always at file scope, you've explicitly made Foo.Bar inaccessible for extension by using private.

If you want to be able to extend Foo.Bar from file scope, then it needs to be at least fileprivate.

Based on your description of what visibility you want ("only visible in this file"), it sounds like fileprivate is explicitly what you'd want.

If you want to avoid using fileprivate explicitly, you can also accomplish the behavior by declaring Bar in a private extension of Foo, since again extensions are at file scope:

enum Foo { ... }
private extension Foo {
  final class Bar { }
}
extension Foo.Bar { ... }
2 Likes

This fixes the issue. However, how is a symbol defined in a module different than when it is defined in a local scope? A global private definition can be extended. Shouldn't this yield the same compiler error, unless the global symbol is declared fileprivate as well:

fileprivate final class Bar {}

extension Bar {
    static func baz() {}
}

Swift's access control modifiers are lexical: private at file scope (it's not "global") means it is private to the file—i.e., fileprivate is identical to private in that position.

2 Likes

OK, this explains it.
Thanks a lot!