-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Bug fix: Emit code for C++ inline function called from another inline function #31035
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
… function. Adds a test that fails without the fix. The fix consists simply of removing a return statement; as a result, we now always emit code for functions referenced from the function we are currently emitting. Previously, this was not done for externally visible functions. This goes all the way back to when `GenClangDecl.cpp` was originally introduced here: swiftlang@ee22004 I speculate that the reason for doing this was that in C and Objective-C, we can assume that an externally visible function will be provided by some `.o` file we're linking against (though I'm not sure if this is in fact true for C99 inline functions). At any rate, this assumption is no longer true for C++ inline functions.
@rjmccall I'd like your feedback on whether this fix looks right. |
One larger change is that we're now also running this test in C as well as C++.
lib/IRGen/GenClangDecl.cpp
Outdated
@@ -41,7 +41,6 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) { | |||
if (!valueDecl || valueDecl->isExternallyVisible()) { | |||
ClangCodeGen->HandleTopLevelDecl( | |||
clang::DeclGroupRef(const_cast<clang::Decl*>(decl))); | |||
return; | |||
} |
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 looks correct, but can we preserve the fast path where the declaration does not have a definition?
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 assume you mean the fast path where the declaration isn't a ValueDecl
(which I've done)?
There wasn't a fast path before for the case where the declaration doesn't have a definition (and I believe this isn't possible to check in the general case of a ValueDecl
?).
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.
Well, the fast-path was buggy, we've established that. :) I think the point of the isExternallyVisible()
check was supposed to be to look for a function declaration like void foo();
that declares something with external linkage, but of course that's not the right way to check for that.
Currently the rest of the function only does special things for FunctionDecl
s, although really it needs to do them for VarDecl
s as well. I think having a helper function that tries to find a body Stmt*
for a ValueDecl
wouldn't be remiss.
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.
Well, the fast-path was buggy, we've established that. :) I think the point of the
isExternallyVisible()
check was supposed to be to look for a function declaration likevoid foo();
that declares something with external linkage, but of course that's not the right way to check for that.
Ah -- that makes sense.
Currently the rest of the function only does special things for
FunctionDecl
s, although really it needs to do them forVarDecl
s as well.
Oooooh, right! I've verified this case was not being handled correctly either. See the extra test case I've added -- calledTransitivelyFromVarInit()
was not being emitted before I made the code changes in my latest commit.
I think having a helper function that tries to find a body
Stmt*
for aValueDecl
wouldn't be remiss.
A VarDecl
doesn't have a Stmt *
though...? But I see what you mean, and I've introduced a helper function that returns a redeclaration of the decl containing "executable code" (body for a function, initializer for a variable), if one exists. This decl contains the body (in the case of a function) or initializer (in the case of a variable), so it's the right thing to traverse to find DeclRefs of things we should emit.
Not super-pleased with the naming of the helper function BTW (getDeclWithExecutableCode()
) -- let me know if you have a better suggestion.
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.
A quirk of Clang's AST is that Expr
is a subclass of Stmt
, so VarDecl
s do actually have a Stmt*
, but this is a fine approach, too.
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.
Oh right, I had forgotten about that -- makes sense, given that an expression is a valid statement in C++.
Reinstate fast path where declaration is not a ValueDecl.
d2626e7
to
e9ca0c7
Compare
@swift-ci Please test |
Build failed |
Build failed |
@swift-ci Please test |
@swift-ci Please test Windows |
Didn't notice that we already have a recent passing Windows build: https://ci-external.swift.org/job/swift-PR-windows/2043/ Merging. |
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
Bug fix: Emit code for C++ inline function called from another inline function
Explanation: LLVM currently asserts on local extern variables in C headers passed to Swift when the definition exists outside that header. Scope: This never worked. Risk: Low. Testing: Regression test added. Reviewed-by: John McCall Original PR: swiftlang#33306 This fix also required the cherry-pick of: swiftlang#31035 and swiftlang#31272 rdar://67951491
Adds a test that fails without the fix.
The fix consists simply of removing a return statement; as a result, we now always emit code for functions referenced from the function we are currently emitting.
Previously, this was not done for externally visible functions. This goes all the way back to when
GenClangDecl.cpp
was originally introduced in ee22004.I speculate that the reason for doing this was that in C and Objective-C, we can assume that an externally visible function will be provided by some
.o
file we're linking against. This is also true forinline
functions in C99, where exactly one translation unit needs to declare the functionextern inline
to cause it to get emitted to the.o
file.C++ inline functions follow a different model, however, so we need to make sure we emit code for them.