-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Update cxx operator implementation #58910
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
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
88f7260
start work on operators
Huddie 178b012
Add operators as class members
Huddie b9f2196
Add changes
Huddie e14e2fb
Add changes
Huddie 4267958
Mapping decl base name to imported name
Huddie 11730e8
Remap class operator function names (-/+/*....) to imported operator …
Huddie bd4db51
Added synth member to result
Huddie c36eeed
Update Identifier.h
Huddie 540e52a
Update Identifier.h
Huddie e7fe6f0
Fix tests and re-enable support for CXX operators
Huddie 3196ea9
Refactor and format
Huddie 65473f5
Use Identifier instead of StringRef to avoid dangling pointer
Huddie 16c1e1d
Format
Huddie 238eec4
PR comment
Huddie 597668f
Test diff mangled name for windows
Huddie 2422c0c
Keep the XFAIL on windows for now
Huddie bb3a721
Manually fix some formatting
Huddie 1d1df27
Fix switch error + format
Huddie 54ed81c
Format
Huddie 9aea433
Add in newline
Huddie e7f8a99
Fix spacing
Huddie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3593,6 +3593,26 @@ namespace { | |
// by changing the name of one. That changed method needs to be added | ||
// to the lookup table since it cannot be found lazily. | ||
if (auto cxxMethod = dyn_cast<clang::CXXMethodDecl>(m)) { | ||
auto cxxOperatorKind = cxxMethod->getOverloadedOperator(); | ||
|
||
// Check if this method _is_ an overloaded operator but is not a | ||
// call / subscript. Those 2 operators do not need static versions | ||
if (cxxOperatorKind != clang::OverloadedOperatorKind::OO_None && | ||
cxxOperatorKind != clang::OverloadedOperatorKind::OO_Call && | ||
cxxOperatorKind != | ||
clang::OverloadedOperatorKind::OO_Subscript) { | ||
|
||
auto opFuncDecl = makeOperator(MD, cxxMethod); | ||
|
||
Impl.addAlternateDecl(MD, opFuncDecl); | ||
|
||
auto msg = "use " + std::string{clang::getOperatorSpelling(cxxOperatorKind)} + " instead"; | ||
Impl.markUnavailable(MD,msg); | ||
|
||
// Make the actual member operator private. | ||
MD->overwriteAccess(AccessLevel::Private); | ||
} | ||
|
||
if (cxxMethod->getDeclName().isIdentifier()) { | ||
auto &mutableFuncPtrs = Impl.cxxMethods[cxxMethod->getName()].second; | ||
if(mutableFuncPtrs.contains(cxxMethod)) { | ||
|
@@ -4133,11 +4153,6 @@ namespace { | |
if (!dc) | ||
return nullptr; | ||
|
||
// 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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. |
||
|
||
// Handle cases where 2 CXX methods differ strictly in "constness" | ||
// In such a case append a suffix ("Mutating") to the mutable version | ||
// of the method when importing to swift | ||
|
@@ -4285,16 +4300,7 @@ namespace { | |
templateParams); | ||
|
||
if (auto *mdecl = dyn_cast<clang::CXXMethodDecl>(decl)) { | ||
// Subscripts and call operators are imported as normal methods. | ||
bool staticOperator = mdecl->isOverloadedOperator() && | ||
mdecl->getOverloadedOperator() != clang::OO_Call && | ||
mdecl->getOverloadedOperator() != clang::OO_Subscript; | ||
if (mdecl->isStatic() || | ||
// C++ operators that are implemented as non-static member | ||
// functions get imported into Swift as static member functions | ||
// that use an additional parameter for the left-hand side operand | ||
// instead of the receiver object. | ||
staticOperator) { | ||
if (mdecl->isStatic()) { | ||
selfIdx = None; | ||
} else { | ||
// Swift imports the "self" param last, even for clang functions. | ||
|
@@ -5393,9 +5399,13 @@ namespace { | |
/// \param setter function returning `UnsafeMutablePointer<T>` | ||
/// \return subscript declaration | ||
SubscriptDecl *makeSubscript(FuncDecl *getter, FuncDecl *setter); | ||
FuncDecl *makeOperator(FuncDecl *operatorMethod, | ||
clang::CXXMethodDecl *clangOperator); | ||
|
||
VarDecl *makeComputedPropertyFromCXXMethods(FuncDecl *getter, | ||
FuncDecl *setter); | ||
|
||
|
||
/// Import the accessor and its attributes. | ||
AccessorDecl *importAccessor(const clang::ObjCMethodDecl *clangAccessor, | ||
AbstractStorageDecl *storage, | ||
|
@@ -8080,6 +8090,133 @@ SwiftDeclConverter::makeSubscript(FuncDecl *getter, FuncDecl *setter) { | |
return subscript; | ||
} | ||
|
||
static std::pair<BraceStmt *, bool> | ||
synthesizeOperatorMethodBody(AbstractFunctionDecl *afd, void *context) { | ||
ASTContext &ctx = afd->getASTContext(); | ||
|
||
auto funcDecl = cast<FuncDecl>(afd); | ||
auto methodDecl = | ||
static_cast<FuncDecl *>(context); /* Swift version of CXXMethod */ | ||
|
||
SmallVector<Expr *, 8> forwardingParams; | ||
|
||
// We start from +1 since the first param is our lhs. All other params are | ||
// forwarded | ||
for (auto itr = funcDecl->getParameters()->begin() + 1; | ||
itr != funcDecl->getParameters()->end(); itr++) { | ||
auto param = *itr; | ||
Expr *paramRefExpr = | ||
new (ctx) DeclRefExpr(param, DeclNameLoc(), /*Implicit=*/true); | ||
paramRefExpr->setType(param->getType()); | ||
|
||
if (param->isInOut()) { | ||
paramRefExpr->setType(LValueType::get(param->getType())); | ||
|
||
paramRefExpr = new (ctx) InOutExpr(SourceLoc(), paramRefExpr, | ||
param->getType(), /*isImplicit*/ true); | ||
paramRefExpr->setType(InOutType::get(param->getType())); | ||
} | ||
|
||
forwardingParams.push_back(paramRefExpr); | ||
} | ||
|
||
auto methodExpr = | ||
new (ctx) DeclRefExpr(methodDecl, DeclNameLoc(), /*implicit*/ true); | ||
methodExpr->setType(methodDecl->getInterfaceType()); | ||
|
||
// Lhs parameter | ||
auto baseParam = funcDecl->getParameters()->front(); | ||
Expr *baseExpr = | ||
new (ctx) DeclRefExpr(baseParam, DeclNameLoc(), /*implicit*/ true); | ||
baseExpr->setType(baseParam->getType()); | ||
if (baseParam->isInOut()) { | ||
baseExpr->setType(LValueType::get(baseParam->getType())); | ||
|
||
baseExpr = new (ctx) InOutExpr(SourceLoc(), baseExpr, baseParam->getType(), | ||
/*isImplicit*/ true); | ||
baseExpr->setType(InOutType::get(baseParam->getType())); | ||
} | ||
|
||
auto dotCallExpr = | ||
DotSyntaxCallExpr::create(ctx, methodExpr, SourceLoc(), baseExpr); | ||
dotCallExpr->setType(methodDecl->getMethodInterfaceType()); | ||
dotCallExpr->setThrows(false); | ||
|
||
auto *argList = ArgumentList::forImplicitUnlabeled(ctx, forwardingParams); | ||
auto callExpr = CallExpr::createImplicit(ctx, dotCallExpr, argList); | ||
callExpr->setType(funcDecl->getResultInterfaceType()); | ||
callExpr->setThrows(false); | ||
|
||
auto returnStmt = new (ctx) ReturnStmt(SourceLoc(), callExpr, | ||
/*implicit=*/true); | ||
|
||
auto body = BraceStmt::create(ctx, SourceLoc(), {returnStmt}, SourceLoc(), | ||
/*implicit=*/true); | ||
return {body, /*isTypeChecked=*/true}; | ||
} | ||
|
||
FuncDecl * | ||
SwiftDeclConverter::makeOperator(FuncDecl *operatorMethod, | ||
clang::CXXMethodDecl *clangOperator) { | ||
auto &ctx = Impl.SwiftContext; | ||
auto opName = | ||
clang::getOperatorSpelling(clangOperator->getOverloadedOperator()); | ||
auto paramList = operatorMethod->getParameters(); | ||
auto genericParamList = operatorMethod->getGenericParams(); | ||
|
||
auto opId = ctx.getIdentifier(opName); | ||
|
||
auto parentCtx = operatorMethod->getDeclContext(); | ||
|
||
auto lhsParam = new (ctx) ParamDecl( | ||
SourceLoc(), | ||
SourceLoc(),Identifier(), | ||
SourceLoc(),ctx.getIdentifier("lhs"), | ||
parentCtx); | ||
|
||
lhsParam->setInterfaceType(operatorMethod->getDeclContext()->getSelfInterfaceType()); | ||
|
||
if (operatorMethod->isMutating()) { | ||
// This implicitly makes the parameter indirect. | ||
lhsParam->setSpecifier(ParamSpecifier::InOut); | ||
} else { | ||
lhsParam->setSpecifier(ParamSpecifier::Default); | ||
} | ||
|
||
SmallVector<ParamDecl *, 4> newParams; | ||
newParams.push_back(lhsParam); | ||
|
||
for (auto param : *paramList) { | ||
newParams.push_back(param); | ||
} | ||
|
||
auto oldArgNames = operatorMethod->getName().getArgumentNames(); | ||
SmallVector<Identifier, 4> newArgNames; | ||
newArgNames.push_back(Identifier()); | ||
|
||
for (auto id : oldArgNames) { | ||
newArgNames.push_back(id); | ||
} | ||
|
||
auto opDeclName = DeclName( | ||
ctx,opId, | ||
{newArgNames.begin(), newArgNames.end()}); | ||
|
||
auto topLevelStaticFuncDecl = FuncDecl::createImplicit( | ||
ctx, StaticSpellingKind::None, opDeclName, SourceLoc(), | ||
/*Async*/ false, /*Throws*/ false, genericParamList, | ||
ParameterList::create(ctx, newParams), | ||
operatorMethod->getResultInterfaceType(), parentCtx); | ||
|
||
topLevelStaticFuncDecl->setAccess(AccessLevel::Public); | ||
topLevelStaticFuncDecl->setIsDynamic(false); | ||
topLevelStaticFuncDecl->setStatic(); | ||
topLevelStaticFuncDecl->setBodySynthesizer(synthesizeOperatorMethodBody, | ||
operatorMethod); | ||
|
||
return topLevelStaticFuncDecl; | ||
} | ||
|
||
void SwiftDeclConverter::addProtocols( | ||
ProtocolDecl *protocol, SmallVectorImpl<ProtocolDecl *> &protocols, | ||
llvm::SmallPtrSetImpl<ProtocolDecl *> &known) { | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 do we need
!VD->hasClangNode()
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, does that mean that operators are effectively visible from Swift even if their module isn't imported? E.g.
// B.swift import A // + shouldn't be visible here
Could 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@egorzhdan is that done in subscripts
result->addMember(subscript)
?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 happens in
SwiftDeclConverter::makeSubscript
,SubscriptDecl *subscript
is created with agetterImpl->getClangNode()
parameter.Uh oh!
There was an error while loading. Please reload this page.
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.
hmm.. I was using
createImplicit
which doesn't have that option. But maybecreateImported
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
feels a bit weird just cause this function technically wasn't imported.