-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Fix ambiguous methods in long chains of inheritance #81709
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
@swift-ci please smoke test |
fcb4da1
to
e108828
Compare
@swift-ci please smoke test |
e108828
to
affbce2
Compare
@swift-ci please smoke test |
Could you explain why the current check in |
lib/ClangImporter/ClangImporter.cpp
Outdated
|
||
// If this is a cloned decl, we don't want to reclone it | ||
if (!decl->hasClangNode()) { | ||
for (auto &CBM : clonedBaseMembers) { |
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.
clonedBaseMembers
is a DenseMap
. Is there no way to do a lookup rather than linear scan over the values 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.
The problem is that the key of clonedBaseMembers
is a std::pair<origDecl, newContext>
, where origDecl
is the original method that was cloned and newContext
is the record origDecl
was cloned into. The value is the cloned method of origDecl
in newContext
.
In this case, we want to check if the method decl
was cloned or if it is original. In other words, if the method decl
is present in clonedBaseMembers
. As far as I know, there isn't a way to lookup by value in a DenseMap
and the only way to obtain the original method of a cloned decl is to lookup in clonedBaseMembers
, or find all the methods in the base classes with the same name as decl
and check if any of them is present in clonedBaseMembers
.
Having said that, I don't love this solution, so I'm open to suggestions. I guess the alternative is to:
- use a different data structure
- have an additional data structure, where this search would be more efficient
However, most times we are searching by key (in importBaseMemberDecl
), hence this solution.
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 can think of a few options:
- add an
isClonedFromBaseClass
bit insideValueDecl
that we can check here. For this use case it doesn't matter where it was cloned from, but I don't know what effect adding another bit field to everyValueDecl
, regardless of whether it was cloned or not, would have. - record the equivalent information using a set in the clang importer
- inside
importBaseMemberDecl
, when we addclonedBaseMembers.insert({{decl, newContext, cloned})
, we could also addclonedBaseMembers.insert({{cloned, newContext}, cloned})
. I can't think of a reason we wouldn't have access tonewContext
inisClonedBaseMemberDecl
, butclonedBaseMembers.insert({{decl, nullptr}, cloned})
could work as well I guess. This is essentially just a way to repurpose the map as a set data structure - not sure if that's better or worse than introducing a separate, explicit set.
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.
record the equivalent information using a set in the clang importer
I think this would be the easiest approach.
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 for the suggestions! All these ideas sound good to me, but they prioritize time efficiency at the expense of memory and I'm not sure what's better in this case. Having said that, I prefer the second option instead of adding a field to all ValueDecl
s.
I can't think of a reason we wouldn't have access to
newContext
inisClonedBaseMemberDecl
We do have access to newContext
. We don't have access to the original decl, but your solution wouldn't need it anyways. Since clonedBaseMembers
is only used here, I guess we can repurpose clonedBaseMembers
and this is also probably less bug-prone than having two different data structures and always having to update both. What do you think?
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.
Do we know why we don't have a clang node for the cloned base member?
If we had a Clang node we could check if the parent of the member (the containing record decl) is the same as the record for which we want to clone this member.
@swift-ci please smoke test |
Sure! Considering the example in the description where
( The thing is that, when we clone these methods, we add them to the swift decl of |
Thanks, that clarifies things! |
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 have similar thoughts to @hnrklssn regarding the dictionary lookup performance. Otherwise, this looks good to me.
lib/ClangImporter/ClangImporter.cpp
Outdated
|
||
// If this is a cloned decl, we don't want to reclone it | ||
if (!decl->hasClangNode()) { | ||
for (auto &CBM : clonedBaseMembers) { |
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.
record the equivalent information using a set in the clang importer
I think this would be the easiest approach.
Oh I didn't see this. Do you prefer this one over storing everything in the same data structure? I'm concerned it might be the cause of bugs in the future if we ever want to use For the third option, we could call the DenseMap |
@susmonteiro if it's possible to restructure the DenseMap to keep everything in one data structure, in a way that lookups would still be fast, that would be the best solution. If not, I'd say it's fine to add another DenseSet to store the cloned members. |
affbce2
to
79227e7
Compare
@hnrklssn @egorzhdan I implemented your feedback. One comment though: I decided to keep the check in a separate |
@swift-ci please smoke test |
@swift-ci please smoke test Linux platform |
@swift-ci please test Windows platform |
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, thanks!
Let's suppose we have 3 structs, where
S3
inherits fromS2
andS2
inherits fromS1
. IfS1
has a methodfoo
, that is not overridden by their children structs, we would previously clone this method inS3
twice: once fromS1
(the original method) and the other fromS2
(which would be a clone of a clone). This resulted in 2 definitions of the methodfoo
inS3
, which would be diagnosed as ambiguous methods.This fixes the above problem by making sure that we never clone a method that was already a clone.
rdar://126229717