Question about access control of extension

My question is about access control of extension in book the swift programming language.

Original description is:

You can’t provide an explicit access-level modifier for an extension if you’re using that extension to add protocol conformance. Instead, the protocol’s own access level is used to provide the default access level for each protocol requirement implementation within the extension.

And I wrote a example to understand it:

fileprivate protocol Math {
    func sum(_ operandA: Int, _ operandB: Int) -> Int
}

class Calculation {
}

extension Calculation: Math {
    func sum(_ operandA: Int, _ operandB: Int) -> Int {
        return operandA + operandB
    }
}

As the rule above, function sum is fileprivate in Calculation extension by default.
But when I test it in Xcode, function sum can be invoked by another source file.
Did I get it wrong?

It's not wrong. You cannot access Math protocol out side of this file. The sum method you call is belong to Calculation class(internal class and method).

@nhatlee But how to understand this: the protocol’s own access level is used to provide the default access level for each protocol requirement implementation within the extension. It seems to mean that the implementation will get the default access by protocol.

This appears to be a bug, and I also reproduce it.

Note that you *can* give the method itself a fileprivate access level within the extension, and that works as expected.

If you change your code to:

fileprivate protocol Math {
    func sum(_ operandA: Int, _ operandB: Int) -> Int
}

extension Math {
    func sum(_ operandA: Int, _ operandB: Int) -> Int {
        return operandA + operandB
    }
}

class Calculation {
}

extension Calculation: Math {
    
}

Then into another file you call:

let cal = Calculation()
cal.sum(1, 2)// Error: 'sum' is inaccessible due to 'fileprivate' protection level

I think it makes sense. Not a bug.

There is no problem with this example, I can understand this situation. But it still can't explain the document, just read the description again: if you’re using that extension to add protocol conformance, the point is add protocol conformance by extension, it means adding extension to some type by conforming some protocol, not adding extension to protocol itself, it's different.

And look at next one: for each protocol requirement implementation within the extension, it means the implementation is in the same extension prior.

So, your example is not appropriate for the document.

Yeah, I know, I agree with you too.

In this case, it’s The Swift Programming Language that is wrong. The implied access level of any method inside an extension that doesn’t use a shorthand default access modifier is internal, bounded by the visibility of its type. The approved rules are outlined in SE-0025:

  • The default level of access control anywhere is internal .
  • The compiler should not warn when a broader level of access control is used within a type with more restrictive access, such as internal within a private type. This allows the designer of the type to select the access they would use were they to make the type more widely accessible. (The members still cannot be accessed outside the enclosing lexical scope because the type itself is still restricted, i.e. outside code will never encounter a value of that type.)
  • The type of a member can reference only declarations that are accessible wherever the member is accessible. (This relaxes an existing rule that states that the type of a declaration may not reference any declarations that have broader access.)

I’d recommend filing a bug report against The Swift Programming Language.

5 Likes

I filed rdar://problem/46527620 to track this. @melerpaine, thanks for pointing it out! If you want an email when it's fixed, you can file your own bug and I'll dupe mine to yours.

2 Likes

Does this really mean we could not write

private struct Person { 
   let name: String
}

prior to SE-0025?

The pre-SE-0025 rule was that the default access level was min(enclosing context, internal). So the code you wrote would be allowed, but internal let name would not.

Because Person is private, right? This applies to the following rule,

  • The compiler should not warn when a broader level of access control is used within a type with more restrictive access, such as internal within a private type.

but the one I am quoting implies that pre-SE-0025, name would have not been allowed because String has a broader access:

Oh. Yeah, that wasn't the intent of that line, but I don't remember what failed about the example shown there:

struct Outer {
  private typealias Value = Int
  private struct Inner {
    var value: Value
  }
}

(I haven't seen Ilya around in a long time, but I don't think he'd remember either.)

1 Like