-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix SE-0025 edge case involving public member of protocol extension of private protocol #15735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2fac858
to
ff18963
Compare
@swift-ci Please smoke test |
@jrose-apple You might to take a look at this PR. @gottesmm If you feel like it, take a look at the SILOptimizer change. This was a latent bug exposed by my changes. |
cast<SingleValueInstruction>(AI)->replaceAllUsesWith(NewInstPair.first); | ||
|
||
DeadApplies.push_back(AI); | ||
replaceDeadApply(Apply, NewInstPair.first); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not code that I am that familiar with. But are you 200% sure that you are not going to run into any problems around iterator invalidation and or removing values still with uses. That is the reason why people usually follow this pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other parts of the optimizer use replaceDeadApply(), so if that was wrong they would be wrong too. Note that we don't have any active iterators in this loop. We're iterating over a vector of SILInstructions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not saying that using replaceDeadArray is necessarily wrong. What I am saying is that you are changing the code in a functional way by moving the time the applies are deleted from after the transformation to during the transformation. I am trying to prove to myself that doing so is safe (the pattern you are removing is a common defensive coding pattern in optimization passes). When I look at the git history these lines were added in the initial pass. @rudkx do you remember why you did this here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll see if the source compat suite passes :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't necessarily mean anything.
@@ -30,3 +30,18 @@ public class PublicClass { | |||
} | |||
} | |||
|
|||
public protocol Thrower { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test needs FileCheck patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has them, see the function caller()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what you are talking about. I see caller on line 45 without a FileCheck pattern showing that the callee was or was not devirtualized. Or am I misreading the test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, fixed
@@ -30,3 +30,18 @@ public class PublicClass { | |||
} | |||
} | |||
|
|||
public protocol Thrower { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know what you are talking about. I see caller on line 45 without a FileCheck pattern showing that the callee was or was not devirtualized. Or am I misreading the test?
cast<SingleValueInstruction>(AI)->replaceAllUsesWith(NewInstPair.first); | ||
|
||
DeadApplies.push_back(AI); | ||
replaceDeadApply(Apply, NewInstPair.first); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not saying that using replaceDeadArray is necessarily wrong. What I am saying is that you are changing the code in a functional way by moving the time the applies are deleted from after the transformation to during the transformation. I am trying to prove to myself that doing so is safe (the pattern you are removing is a common defensive coding pattern in optimization passes). When I look at the git history these lines were added in the initial pass. @rudkx do you remember why you did this here?
|
||
replaceDeadApply(InnerAI, NewInst); | ||
replaceDeadApply(InnerAI, NewInstPair.first); | ||
|
||
auto newApplyAI = NewInstPair.second.getInstruction(); | ||
assert(newApplyAI && "devirtualized but removed apply site?"); | ||
I = newApplyAI->getIterator(); | ||
auto NewAI = FullApplySite::isa(newApplyAI); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just eliminate NewAI, i.e.:
return {FullApplySite::isa(newApplyAI), I};
Also next time, it would be helpful for reviewers who read the mailing list if you could put stuff like this in a separate focused PR. The reason why I am asking is that logically has nothing to do with the title/subject of the PR, then people who just look at Titles on the mailing list may not know that this is code that they should review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yes logically the SIL optimizer changes are distinct, and could be in a separate PR, but they were motivated by other changes in this PR. It can be tricky to find the right balance here. Sometimes, splitting up a change into too many PRs can also be difficult to review because its hard to get the big picture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is why in the PR you specifically tell the reviewer the big picture and uses references in between the PRs/etc to provide the connection for posterity. NOTE I am not saying that I have always done this well.
ff18963
to
aad1fc7
Compare
@swift-ci Please smoke test |
@swift-ci Please test source compatibility |
@swift-ci Please test |
Build failed |
Build failed |
@gottesmm I forgot to push revised patches. I'll do after the source compat suite completes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only reviewed the AST/Sema parts, but I'm pretty unhappy with how many extra hacks there are here. I'd still like to pursue the "formally change the language to not require access" option that would not require special casing protocol extension scopes nearly as much.
I suggest adding a cross-module execution test case that shows the tricky linker cases, including using the protocol in a generic context from the client module with optimizations enabled (to make sure we don't devirtualize).
include/swift/AST/Decl.h
Outdated
/// containing scope is not really visible. However, if the containing scope | ||
/// is a protocol extension, they can show up here, so we have to filter | ||
/// them out directly. | ||
AccessLevel adjustAccessLevelForProtocolExtension(AccessLevel access) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: this should be static
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It calls getDeclContext() on this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh…that seems fishy, then. Why isn't it only called when needed?
include/swift/AST/Decl.h
Outdated
getFormalAccessScope(const DeclContext *useDC = nullptr, | ||
bool respectVersionedAttr = false) const; | ||
getFormalAccessScope(const DeclContext *useDC=nullptr, | ||
bool isUsageFromInline=false) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: Why remove the spaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this name isn't really correct. The formal access scope doesn't change when used from an inline function; it changes when it's only being used as a symbol and not as part of API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I renamed this in the other PR that got merged already.
include/swift/AST/Decl.h
Outdated
@@ -2319,7 +2326,8 @@ class ValueDecl : public Decl { | |||
/// A public declaration is accessible everywhere. | |||
/// | |||
/// If \p DC is null, returns true only if this declaration is public. | |||
bool isAccessibleFrom(const DeclContext *DC) const; | |||
bool isAccessibleFrom(const DeclContext *DC, | |||
bool forConformance=false) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please include an explanation of the forConformance
flag in the doc comment. This whole thing was really subtle and deserves to be written down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
aad1fc7
to
2c8d502
Compare
A protocol extension of a private protocol can define internal or public members. We should not be able to find these members from another file or module if an internal or public type conforms to the protocol. Fixes <rdar://problem/21380336>.
2c8d502
to
a5579d1
Compare
@jrose-apple I added some more comments, cleaned it up a bit and added the SIL optimizer test case you requested. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I still don't think this is the right answer for the language, but it's a non-regressing answer. Can you file a Radar to track one of us looking into changing the witness rule?
Sure thing. |
@swift-ci Please smoke test |
1 similar comment
@swift-ci Please smoke test |
The witness table for a conformance to a private protocol is private. For this reason any member of a protocol extension of such a protocol should also be private, since it expects to receive a witness table and therefore there's no way to call it from the outside.
However, the type checker was incorrectly allowing such members to be found by name lookup if a public type conformed to the private protocol. Calling such a member resulted in a linker error.
The type checker also allowed these members to witness protocol requirements of public protocols. Since we cannot break source compatibility, this behavior is preserved. When calling the protocol requirement from outside of the module with the concrete base type, we used to fail with a linker error; now we call the requirement virtually using a
witness_method
instruction.Fixes rdar://problem/21380336, rdar://problem/29044612, let's go search JIRA for dupes... https://bugs.swift.org/browse/SR-2315, https://bugs.swift.org/browse/SR-2571, https://bugs.swift.org/browse/SR-2925, https://bugs.swift.org/browse/SR-3312, https://bugs.swift.org/browse/SR-6325, damn...