Skip to content

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

Merged
merged 10 commits into from
Apr 22, 2020

Conversation

martinboehme
Copy link
Contributor

@martinboehme martinboehme commented Apr 15, 2020

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 for inline functions in C99, where exactly one translation unit needs to declare the function extern 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.

… 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.
@martinboehme
Copy link
Contributor Author

@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++.
@@ -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;
}
Copy link
Contributor

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?

Copy link
Contributor Author

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?).

Copy link
Contributor

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 FunctionDecls, although really it needs to do them for VarDecls as well. I think having a helper function that tries to find a body Stmt* for a ValueDecl wouldn't be remiss.

Copy link
Contributor Author

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.

Ah -- that makes sense.

Currently the rest of the function only does special things for FunctionDecls, although really it needs to do them for VarDecls 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 a ValueDecl 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.

Copy link
Contributor

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 VarDecls do actually have a Stmt*, but this is a fine approach, too.

Copy link
Contributor Author

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.
@swiftlang swiftlang deleted a comment from swift-ci Apr 17, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 20, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 20, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 20, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 20, 2020
@gribozavr
Copy link
Contributor

@swift-ci Please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - f2bfa26

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - f2bfa26

@swiftlang swiftlang deleted a comment from swift-ci Apr 22, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 22, 2020
@swiftlang swiftlang deleted a comment from swift-ci Apr 22, 2020
@gribozavr
Copy link
Contributor

@swift-ci Please test

@gribozavr
Copy link
Contributor

@swift-ci Please test Windows

@gribozavr
Copy link
Contributor

Didn't notice that we already have a recent passing Windows build: https://ci-external.swift.org/job/swift-PR-windows/2043/

Merging.

@gribozavr gribozavr merged commit e37e475 into swiftlang:master Apr 22, 2020
Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@gribozavr gribozavr added the c++ interop Feature: Interoperability with C++ label May 7, 2020
aschwaighofer pushed a commit to aschwaighofer/swift that referenced this pull request Sep 10, 2020
Bug fix: Emit code for C++ inline function called from another inline function
aschwaighofer pushed a commit to aschwaighofer/swift that referenced this pull request Sep 10, 2020
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ interop Feature: Interoperability with C++
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants