Scoped Conformances

Another thing unclear about this: how would it be implemented if T was not a visible type in the current scope, so you can't really express (someAny as! T) in code? Note that this program prints false today:

// A.swift
internal protocol P {}
fileprivate struct T: P {}
internal let someAny: Any = T()
// Main.swift
print(someAny as? P == nil) // false

It seems to me the same rules should hold even if there was a module boundary between A.swift and Main.swift, and the internal declarations all became public.

I agree. I'm a bit out of my depth discussing the implementation concerns here, but the issue you raise doesn't immediately strike me as problematic—clearly there is already some mechanism that the runtime uses to look up conformances of out-of-scope types. The conformance as written would implicitly translate—under scoped conformances—to fileprivate struct T: internal P {}, so I don't see any issue with the conformance being discoverable from Main.swift.

It seems to me that scoped conformances strictly reduces the visibility of conformances and the associated members. To implement my desired semantics we would have to have a way of excluding the scoped conformances from that lookup mechanism, but if the current model doesn't reasonably allow for that evolution, I guess I'll have to concede!

I'm a little confused about what you mean here. Under what conditions would that conformance "implicitly translate?" Do you mean with all the code as written, or with A and Main in separate modules with all the internals changed to public?

In the code as I wrote it, with today's compiler the invisibility of T from Main does not prevent a T existential from observably conforming to P. That is also true when A and Main are separate modules:

// Module A
public protocol P {}
fileprivate struct T: P {}
public let someAny: Any = T()
// Module Main
import A
print(someAny as? P == nil) // false; i.e. the cast works

If I understand what you're saying above, T: P should be treated as internal to A, and because it is not visible in Main, the cast should fail. If so, you're advocating a change in semantics of existing programs.

I don't really understand what you're saying here, I'm sorry to say, but with regard to implementation concerns…It seems to me that—even if we ignore the semantic change you're seemingly asking for—your model requires the program to publicly publish enough information, about even private types, to prevent certain dynamic casts from succeeding in scopes where they otherwise might succeed. I don't know what the extent of the practical implementation and code size burdens would be, but it's definitely more work for the compiler than might be needed in some other models.

Sorry, was trying to be brief so as not to dominate the discussion more than I already have, but I guess I sacrificed clarity in the process. To be clear: I'm definitely not advocating for semantic changes to any existing programs.

I was just saying that, in a world where we have scoped conformances, writing

// A.swift
internal protocol P {}
fileprivate struct T: P {}
internal let someAny: Any = T()
// Main.swift
print(someAny as? P == nil) // false

should be the same as writing:

// A.swift
internal protocol P {}
fileprivate struct T: internal P {} // <-- note 'internal' here
internal let someAny: Any = T()
// Main.swift
print(someAny as? P == nil) // false

I.e., the visibility of the conformance implicitly adopts the visibility of the conformed-to protocol. Under this model, I see no issue with the fact that the cast in Main.swift succeeds. The conformance was internal, so the conformance should be visible even if the underlying type is not.

The same applies to the modification where P and someAny are made public—the conformance T: P is implicitly public, so the cast succeeds.

Here we're really bumping up against the limits of my knowledge, so let me ask a question:

The compiler today has method by which the conformances of non-visible types are discoverable, so there's already some form of information being published about private types. Specifically, the conformances (even of private types!) to visible protocols are getting published somewhere. What is this mechanism that enables this, and would it be feasible to modify it in a way that would enable us to reduce the amount/type of information published so that a particular conformance would not be discoverable via dynamic casts?

OK, thanks for clarifying. It seems that my attempt to elegantly formalize the semantics you're asking for as x as! T as? P in the scope where x as? P appears doesn't quite work, because T may not be visible there.

I don't know; my assumption is that dynamic casts are done by looking up conformances via the type metadata, which is the same for a given type regardless of scope. That would seemingly make it pretty unfeasible.

Ah, yeah, I didn't realize that you were trying to write something that could in theory actually be written at the cast site. I still think the idea expresses the right semantics even if it can't be spelled out in code. E.g., if it were possible to write (x as! type(of: x)) as? P, I believe that captures the behavior I would want.

I see. If the semantics I want are simply unimplementable with the existing model, then oh well. As I said before, I'd still be in favor of scoped conformances even if this one corner case feels like a potential gotcha!

If there is good documentation, I hope that it would be less a "gotcha," and more a nuance that will be easily discovered and widely understood.