Skip to content

[cxx-interop] Prevent Swift from importing fields marked with [[no_unique_address]] #80497

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

Closed

Conversation

susmonteiro
Copy link
Contributor

@susmonteiro susmonteiro commented Apr 3, 2025

From libc++ 20, std::string has 3 fields marked with [[no_unique_address]]. This attribute is not supported in Swift and was causing a runtime crash when printing a std::string in Swift. In this patch, we prevent Swift from importing such fields.

rdar://147263490

@susmonteiro
Copy link
Contributor Author

@swift-ci please smoke test

Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

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

Thanks! I think the main thing missing here is a few more tests.

@@ -490,4 +490,11 @@ StdStringTestSuite.test("pass as a default argument") {
}
#endif

StdStringTestSuite.test("print std::string") {
let cxxString: std.string = "Hello"
print(cxxString)
Copy link
Contributor

Choose a reason for hiding this comment

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

This ensures that we don't crash, but I wonder if we can also verify that the right string got printed. One way to do that would be creating a new test file, containing the same code snippet, building and running it, then checking the output.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

let swiftString = String(cxxString)
print(swiftString)
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please also add a test that ensures that the error message is emitted when someone tries to access a no_unique_address field from Swift?
test/Interop/Cxx/class would probably be the best place for such test

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also done. Thanks for the suggestions!

Copy link
Contributor

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

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

Might be interesting to sync up with John McCall on this. When he reviewed my PR to fix a crash in codegen (#79076) I had the impression that he would prefer to keep this information in the compiler instead of skipping this.

Swift already supports 0-sized types, empty structs are zero sized. So in case the root cause of the problem is that we might want to look into mimicking what happens for zero sized Swift types.

All this being said I am not against not importing them.

@@ -4383,6 +4383,15 @@ namespace {
std::optional<ImportedName> correctSwiftName;
ImportedName importedName;

if (decl->hasAttr<clang::NoUniqueAddressAttr>()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is NoUniqueAddressAttr the source of the problem or is it something else e.g., the field having 0 as its size? In case it is the latter we might want to make this workaround more targeted.

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 think the problem is that we are incorrectly handling fields with the no_unique_address attribute, and consequently the swift runtime tries to access fields of std::string that were not imported, resulting in a runtime crash. We eventually want to correctly handle this attribute, but the impact of the bug required a quicker fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, let me elaborate a bit. According to CppReference:

Makes this member subobject potentially-overlapping, i.e., allows this member to be overlapped with other non-static data members or base class subobjects of its class. This means that if the member has an empty class type (e.g. stateless allocator), the compiler may optimize it to occupy no space, just like if it were an empty base. If the member is not empty, any tail padding in it may be also reused to store other data members.

So this attribute can have two effects:

  • Empty sized fields can get optimized away
  • Non-empty types' tail padding can be reused

If we know exactly which one of this is problematic, we could make this check more targeted. E.g., if we only have trouble with empty fields we can check for the size instead of the presence of the attribute, so we end up rejecting less code.

That being said, I am OK with landing this as is and doing the rest of the investigation as a follow-up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This makes sense to me. I can for sure investigate this further and, if possible, refine this workaround. However, if you agree, I would merge this patch and open a new one when we have a better fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sounds good!

@susmonteiro susmonteiro closed this Apr 3, 2025
@susmonteiro susmonteiro force-pushed the susmonteiro/skip-no-unique-address-attribute branch from 279584d to 7418b25 Compare April 3, 2025 16:31
@susmonteiro susmonteiro reopened this Apr 4, 2025
@susmonteiro susmonteiro force-pushed the susmonteiro/skip-no-unique-address-attribute branch from c3eb234 to 4be78c2 Compare April 4, 2025 12:39
Copy link
Contributor

@egorzhdan egorzhdan left a comment

Choose a reason for hiding this comment

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

LGTM, thanks! Just one minor comment.


import MemberVariables
import CxxStdlib
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we actually need this import?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we don't. Thanks

@susmonteiro susmonteiro force-pushed the susmonteiro/skip-no-unique-address-attribute branch from 4be78c2 to f4435ee Compare April 4, 2025 15:12
@susmonteiro
Copy link
Contributor Author

@swift-ci please smoke test

@susmonteiro susmonteiro force-pushed the susmonteiro/skip-no-unique-address-attribute branch from f4435ee to 56be6f8 Compare April 7, 2025 12:33
@susmonteiro
Copy link
Contributor Author

@swift-ci please smoke test

@susmonteiro susmonteiro force-pushed the susmonteiro/skip-no-unique-address-attribute branch from 56be6f8 to fbd4e66 Compare April 7, 2025 15:09
@susmonteiro
Copy link
Contributor Author

@swift-ci please smoke test

@susmonteiro susmonteiro deleted the susmonteiro/skip-no-unique-address-attribute branch May 28, 2025 17:21
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.

4 participants