-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Fix name lookup in nested protocol inheritance clauses #72713
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
@@ -1087,7 +1087,8 @@ static DirectlyReferencedTypeDecls | |||
directReferencesForTypeRepr(Evaluator &evaluator, ASTContext &ctx, | |||
TypeRepr *typeRepr, DeclContext *dc, | |||
bool allowUsableFromInline, | |||
bool rhsOfSelfRequirement); | |||
bool rhsOfSelfRequirement, | |||
bool allowProtocolMembers); |
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’s probably time to replace these boolean flags with an OptionSet.
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.
@slavapestov There are a couple of reasons I wanted to avoid doing that with this PR:
-
In my opinion, this kind of cycle should be avoided in a more structured way by ASTScope lookup. It is obvious that if it walks up to a parent scope and enumerates the children, it will find the scope it just visited, and depending on which methods happen to be called, it is incredibly easy for it to get in to a request cycle. If it knew the child it had just visited, it would be able to avoid this.
I tried to implement this, but there's quite a bit of plumbing to do, and of course I don't know if there are any longer-term plans to rework this lookup so I wasn't sure if it was worth doing right now. I'd like to keep this change low-risk so it can be cherry-picked to 6.0, and major refactoring of name lookup seems to me higher risk than is absolutely necessary to fix this issue.
-
I haven't audited all of the places which pass
allowProtocolMembers
as true. Right now, everywhere passes it astrue
in order to maintain the behaviour before this patch, but I suspect some places could afford to turn it off for a slight performance improvement.
Also, could you run the smoke test again? There was a failure on Mac, but the data isn't available on CI any more (I think it was something to do with LLDB? Possibly unrelated to this change. I tried the LLDB tests locally and don't see anything related)
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.
Ping @slavapestov
@swift-ci please smoke test |
@swift-ci Please smoke test |
@karwa Replacing the boolean flags here with an OptionSet is not a high-risk refactoring. Do you mind landing it on the main branch as a separate PR after this one? |
@slavapestov sure 👍 |
OK nice - the macOS tests passed, so it seems the previous LLDB-related failure was indeed unrelated. (GitHub still shows the test as failing because it's the same commit as last time) I've dropped the refactoring commit and will open a separate PR for it. So are we good for this one, @slavapestov? |
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.
LGTM.
As for folding this into ASTScope, it's certainly possible, but the behaviors here are annoying because of backward compatibility. In particular, the LHS and RHS of a conformance requirement are resolved slightly differently, so we would need to create two new scopes for each requirement. I would prefer to phase out some of these quirks in a future -swift-version instead of baking them into the ASTScope design.
@swift-ci Please smoke test macOS |
Is this okay to merge, then? I'd like to tick it off my todo list 😅 |
@karwa Thank you for the fix! |
Currently, this fails to compile with a circular reference error:
Basically what happens is that
InheritedDeclsReferencedRequest
is executed for the nested protocolP
, and tries to look up whatSomeBaseProtocol
is.This goes through to
ASTScope
lookup, which walks up to thestruct ConformanceOnDecl
context and callsIterableTypeBodyPortion::lookupMembersOf
, which performs a qualified lookup request within thestruct ConformanceOnDecl
DeclContext.Qualified lookup then finds the protocol
P
again and callsgetSuperclassDecl
on it, which prompts anotherInheritedDeclsReferencedRequest
, and we have a request cycle.This doesn't happen for non-nested protocols because they live at the top level, so this whole thing where
ASTScope
walks up to their parent and then back down in to them again won't happen.This patch turns off
AllowProtocolMembers
inInheritedDeclsReferencedRequest
if the decl is a protocol, which causes qualified lookup to skip processing protocols it finds. There are no protocol members that are even valid in an inheritance list, so this should be fine.The generics book says:
So I think un-setting this to avoid a cycle seems reasonable. Other flags such as
rhsOfSelfRequirement
seem like they exist to avoid similar cycles (although that particular flag does support protocol members such as associated types, which we don't want here).