Update cxx operator implementation#58910
Conversation
zoecarver
left a comment
There was a problem hiding this comment.
This looks great! Thank you so much Ehud! I only have a few minor comments.
Also, either in this PR or another (maybe better to do another one), would you mind re-enabling these tests on Windows (they should work now) and adding the test from this PR: https://github.com/apple/swift/pull/32869/files ?
And as we've discussed offline, I think this will allow us to get rid of a lot of special-case code throughout silgen! Thanks again :)
lib/ClangImporter/ClangImporter.cpp
Outdated
There was a problem hiding this comment.
nit:
| ? DeclBaseName(SwiftContext.getIdentifier("__operator" + getOperatorNameForToken(std::string{name.getBaseName().getIdentifier()}))) | |
| ? DeclBaseName(SwiftContext.getIdentifier("__operator" + getOperatorNameForToken(name.getBaseName().getIdentifier().str()))) |
There was a problem hiding this comment.
I can do .str().str() to go from identifier to StringRef to std::string
Or should I make getOperatorNameForToken take take StringRef ?
lib/ClangImporter/ImportDecl.cpp
Outdated
There was a problem hiding this comment.
Can we update this to use getOperatorNameForToken?
lib/ClangImporter/ImportName.cpp
Outdated
There was a problem hiding this comment.
Here std::string is a temporary, so you can't get a "ref" to it (StringRef is basically a pointer to the temporary memory). Beyond that, I think we'll need to make baseName a std::string or allocate an identifier (like you did above), because it's declared/used out of this scope.
There was a problem hiding this comment.
What is happening in the other cases here? Like:
baseName = "callAsFunction";Is this not equivalent to:
baseName = StringRef{"callAsFunction"};?
Is this an rValue that gets moved into baseName?
There was a problem hiding this comment.
String literals are sort of like globals, they last for the duration of the program, so you don't have to worry about their lifetime. std::strings on the other hand are destroyed at the end of the scope if they're locals, otherwise at the end of the expression of they're temporaries. A StringRef doesn't store anything, it's a reference to a char *. If you take the reference of a "global" that's always fine. If you take the reference of a local/temporary, that's where we run into problems, because the string may have already been destroyed.
|
Also, would you mind running this whole PR through git-clang-format. You can just run |
|
@swift-ci please test. |
There was a problem hiding this comment.
There's an XFAIL at the top of this file and in a few of these operator tests. I suspect you didn't see these locally because your repo is out of date. Anyway, you'll also need to re-enable operators once you pull from main. Let me know if you have any questions about this.
lib/ClangImporter/ClangImporter.cpp
Outdated
There was a problem hiding this comment.
Also nit: Seems to be missing clang-format
You can use either git-clang-format tool that is on ${PATH_TO_SWIFT}/llvm-project/clang/tools/clang-format and you can add it to your path, and run on the git directory after git add .
Or clang-format -lines 1630:1660 lib/Sema/CSFix.h -i for example to run at lines in specific.
lib/ClangImporter/ClangImporter.cpp
Outdated
There was a problem hiding this comment.
It seems like those two loops are exact the same code(just over different lists), could this be abstracted into a function/lambda in some way?
There was a problem hiding this comment.
Yes I think so! Just gotta rebase and fix some tests first.
…names __operator(Minus, Plus,...) and fix test cases
33c7002 to
e7fe6f0
Compare
|
@swift-ci please smoke test |
|
@swift-ci please smoke test |
|
Is this ready for re-review? |
Yes please :) |
|
@swift-ci please smoke test |
|
|
||
| // Find the lookup table entry for this base name. | ||
| auto known = findOrCreate(LookupTable, baseName, | ||
| auto known = findOrCreate(LookupTable, baseName, |
| // Support for importing operators is temporarily disabled: rdar://91070109 | ||
| if (decl->getDeclName().getNameKind() == clang::DeclarationName::CXXOperatorName && | ||
| decl->getDeclName().getCXXOverloadedOperator() != clang::OO_Subscript) | ||
| return nullptr; |
There was a problem hiding this comment.
The one thing we will probably want to keep disabled is templated operators. Those cause issues with ambiguity. But we can probably disable those in a follow up patch, given this patch is pretty big already.
zoecarver
left a comment
There was a problem hiding this comment.
I want to do a final review pass but this generally looks good to me. Thanks again, Ehud!
lib/AST/DeclContext.cpp
Outdated
| } | ||
| } | ||
|
|
||
|
|
There was a problem hiding this comment.
Were those changes intended?
lib/ClangImporter/ClangImporter.cpp
Outdated
| if (isVisibleFromModule(ModuleFilter, VD)) | ||
| NextConsumer.foundDecl(VD, Reason, dynamicLookupInfo); | ||
| if (!VD->hasClangNode() || isVisibleFromModule(ModuleFilter, VD)) | ||
| NextConsumer.foundDecl(VD, Reason, dynamicLookupInfo); |
There was a problem hiding this comment.
Probably just run git-clang-formater will fix all formatting issues =]
lib/ClangImporter/ImportDecl.cpp
Outdated
| ); | ||
|
|
||
| topLevelStaticFuncDecl->setAccess(AccessLevel::Public); | ||
| topLevelStaticFuncDecl->setImplicit(); |
There was a problem hiding this comment.
Woudn't createImplicit already set topLevelStaticFuncDecl as implicit already making this redundant?
lib/ClangImporter/ImportDecl.cpp
Outdated
| ctx, | ||
| StaticSpellingKind::None, | ||
| opDeclName, SourceLoc(), | ||
| false, false, |
There was a problem hiding this comment.
| false, false, | |
| /*Async=*/false, /*Throws=*/ false, |
lib/ClangImporter/ImportName.cpp
Outdated
| case clang::OverloadedOperatorKind::OO_AmpAmp: | ||
| case clang::OverloadedOperatorKind::OO_PipePipe: | ||
| baseName = clang::getOperatorSpelling(op); | ||
| baseName = isa<clang::CXXMethodDecl>(functionDecl) ? swiftCtx.getIdentifier("__operator" + std::string{getOperatorName(op)}).str() : swiftCtx.getIdentifier(clang::getOperatorSpelling(op)).str(); |
There was a problem hiding this comment.
Nit: just a suggestion for simplification :)
| baseName = isa<clang::CXXMethodDecl>(functionDecl) ? swiftCtx.getIdentifier("__operator" + std::string{getOperatorName(op)}).str() : swiftCtx.getIdentifier(clang::getOperatorSpelling(op)).str(); | |
| auto operatorName = isa<clang::CXXMethodDecl>(functionDecl) ? "__operator" + std::string{getOperatorName(op)} ? getOperatorSpelling(op); | |
| baseName = swiftCtx.getIdentifier(operatorName).str(); |
There was a problem hiding this comment.
baseName is defined before the switch (both the nested operator switch and outer switch) and used for all cases (not just operators)
There was a problem hiding this comment.
Not sure I understand, the implication suggestion was just to do ternary on the argument of swiftCtx.getIdentifier, but it was just a nit...
There was a problem hiding this comment.
Oh I see! I misread (on GH phone) I thought you were just renaming baseName to operatorName
Ya this looks neat! Thanks
|
@swift-ci please test |
|
@zoecarver Ready for another pass! |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Reason, | ||
| DynamicLookupInfo dynamicLookupInfo) override { | ||
| if (isVisibleFromModule(ModuleFilter, VD)) | ||
| if (!VD->hasClangNode() || isVisibleFromModule(ModuleFilter, VD)) |
There was a problem hiding this comment.
Why do we need !VD->hasClangNode() here?
There was a problem hiding this comment.
Good question! Basically it can happen that we try looking up the pure swift function that we synthesize which doesn't have a clang node (since we add it as an altDecl to the original clang operator that we import). And if that happens it will call isVisibleFromModule which assume it has clangNode which our pure swift function does not have. So this guards against that.
There was a problem hiding this comment.
It's when VD isn't something we imported, but something we found via alternativeDecls. I think it would be a good idea to add a comment saying that. Maybe also add a comment to the top of the consumer that says it only filters imported decls/decls that have an associated clang node.
There was a problem hiding this comment.
Hmm, does that mean that operators are effectively visible from Swift even if their module isn't imported? E.g.
// Foo.h
static MyClass operator+(MyClass a, MyClass b) { /* ... */ };// A.swift
@_implementationOnly import Foo// B.swift
import A
// + shouldn't be visible hereCould we instead add a clang decl to the synthesized functions to avoid a special case here? That's how this is handled for synthesized subscripts.
There was a problem hiding this comment.
@egorzhdan is that done in subscripts result->addMember(subscript) ?
There was a problem hiding this comment.
This happens in SwiftDeclConverter::makeSubscript, SubscriptDecl *subscript is created with a getterImpl->getClangNode() parameter.
There was a problem hiding this comment.
hmm.. I was using createImplicit which doesn't have that option. But maybe createImported which does would be better here? And pass in the clangNode? And then call ->setImplicit() after to make it implicit?@zoecarver ?
There was a problem hiding this comment.
feels a bit weird just cause this function technically wasn't imported.
lib/ClangImporter/ImportDecl.cpp
Outdated
|
|
||
| Impl.addAlternateDecl(MD, opFuncDecl); | ||
|
|
||
| Impl.markUnavailable( |
There was a problem hiding this comment.
This formatting seems really weird.
zoecarver
left a comment
There was a problem hiding this comment.
There are still some formatting issues but please land this once those are fixed. The patch looks good to me.
|
@swift-ci please test |
egorzhdan
left a comment
There was a problem hiding this comment.
This looks great, other than a couple of minor code formatting issues. Thanks!
There is a potential issue when an operator is declared in a different module from the struct, however, that never worked even with the previous implementation and can be fixed later.
|
@swift-ci please test |
|
@swift-ci please smoke test |
|
@swift-ci please smoke test |
|
@swift-ci please smoke test |
|
@swift-ci please test |
|
@swift-ci please test macos |
This PR aims to improve the CXX class member operator implementation by making it a more true mapping from the CXX world.
Previous Impl
Previously if a class in CPP has an operator member function
it would get mapped into static method inside the generated
FooclassThis achieves the correct result but the operator is a
staticfunction and therefore theFooclass doesn't really have the same methods on it that it did in CPPNew Impl
This change maps the member operator function to a member function in the swift class & also create a separate swift operator static function which calls the member function
A few windows tests need tor enabled in a follow up
Huge thanks to @zoecarver !