Skip to content

[cxx-interop] Fix a crash with [[no_unique_address]] #80786

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 1 commit into from
Apr 25, 2025

Conversation

Xazax-hun
Copy link
Contributor

@Xazax-hun Xazax-hun commented Apr 13, 2025

Swift does not support storing fields in the padding of the previous fields just yet, so let's not import fields like that from C++. Represent them as opaque blobs instead.

Fixes #80764

rdar://149072458

@Xazax-hun Xazax-hun added the c++ interop Feature: Interoperability with C++ label Apr 13, 2025
@Xazax-hun Xazax-hun force-pushed the gaborh/unique-address-padding branch from cf5df84 to 75838d6 Compare April 13, 2025 12:05
@Xazax-hun
Copy link
Contributor Author

@swift-ci please smoke test

Copy link
Contributor

@j-hui j-hui left a comment

Choose a reason for hiding this comment

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

I'm wondering whether skipping this field is really the right approach, and whether there are other alternatives. Can you say more about why these overlapping fields are problematic for Swift at the moment?

@Xazax-hun
Copy link
Contributor Author

I'm wondering whether skipping this field is really the right approach, and whether there are other alternatives. Can you say more about why these overlapping fields are problematic for Swift at the moment?

Absolutely, I would love to support this and that would be the right approach long term. We use FixedTypeInfo to represent the fields, and these classes and all the code using them have the underlying assumption that the sizeof(field) == sizeof(type). This invariant breaks with fields where the padding can be reused. So a significant portion of the code needs to be updated and tested for this to work. Should be doable, but it would be a significantly bigger change than this PR.

@Xazax-hun Xazax-hun force-pushed the gaborh/unique-address-padding branch from 75838d6 to 664efc6 Compare April 13, 2025 21:50
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.

I would really prefer to just fix the IRGen bug here instead of declining to import a field just because it has this attribute on it. We should try to get Swift out of the business of making assumptions about C/C++ layout, which we should be able to do in three ways:

  • We can completely get out of the business of constructing an LLVM struct type for the C struct by just using Clang's convertTypeForMemory API.
  • We can build the FieldInfos by walking the Swift stored properties and just using Clang's getLLVMFieldNumber API to get the field number within the above.
  • We can construct the spare bit vector by starting with all bits set and then clearing bits as appropriate for individual subobjects. We might need some new methods on ClusteredBitVector to support that kind of mutation, but that should be straightforward because APInt should directly support the operations we'd need.

@Xazax-hun
Copy link
Contributor Author

Xazax-hun commented Apr 24, 2025

I would really prefer to just fix the IRGen bug here

Absolutely! I think that is the correct thing to do here. Unfortunately, I believe it is a bit more involved so I'd like to land this workaround until we can get to the proper fix.

declining to import a field just because it has this attribute on it

Actually, we still can import zero sized fields or fields with this attribute that do not have their tail padding reused to other fields. Currently, it looks like it is not that easy to trigger this kind of layout optimization in Clang, so this might not be too restrictive for a short term workaround.

We should try to get Swift out of the business of making assumptions about C/C++ layout

I agree, but I think fixing the LLVM struct type and the spare bit vector might not be sufficient. Specifically, we generate FixedTypeInfo for the field types and that has the implicit assumption that sizeof(field) == sizeof(typeof(field)) but this is not true. My understanding is after using your proposed approach we would still look into trouble because we need to update the code paths that rely on getting the size from the FixedTypeInfo. At least, I attempted to do a quick fix that generated the correct LLVM struct type but I still run into some issues. I did not have time to go deeper with my investigations but wanted to address the crash until I have time to implement a better solution.

Would you be OK with this workaround temporarily?

@Xazax-hun Xazax-hun force-pushed the gaborh/unique-address-padding branch from 664efc6 to 5291e21 Compare April 24, 2025 16:55
@Xazax-hun
Copy link
Contributor Author

@swift-ci please smoke test

@rjmccall
Copy link
Contributor

Actually, we still can import zero sized fields or fields with this attribute that do not have their tail padding reused to other fields. Currently, it looks like it is not that easy to trigger this kind of layout optimization in Clang, so this might not be too restrictive for a short term workaround.

Yes, if you can change this to only apply to fields that actually have their tail padding used for something, that seems much less likely to have unwanted impact in the short term.

I agree, but I think fixing the LLVM struct type and the spare bit vector might not be sufficient. Specifically, we generate FixedTypeInfo for the field types and that has the implicit assumption that sizeof(field) == sizeof(typeof(field)) but this is not true.

Hmm. Yes, that's a much nastier problem; we need to make sure we're not generating code that writes beyond dsize for these types. I see why you want to simply not import them, then.

@Xazax-hun
Copy link
Contributor Author

only apply to fields that actually have their tail padding used

I attempt to do something like that in this PR, I have fieldLayout.getDataSize() != fieldLayout.getSize() as a condition. That being said, I am not checking whether the following field is actually in the tail padding. Do you think that would be necessary?

@rjmccall
Copy link
Contributor

only apply to fields that actually have their tail padding used

I attempt to do something like that in this PR, I have fieldLayout.getDataSize() != fieldLayout.getSize() as a condition. That being said, I am not checking whether the following field is actually in the tail padding. Do you think that would be necessary?

As it is, you're going to refuse to import in every field of non-POD type with tail padding. Well, hmm, except that it's only when they have the attribute; maybe that makes it okay in practice because people rarely use this attribute. Still, it would certainly be more conservative to only do this when it actually matters for layout.

@Xazax-hun
Copy link
Contributor Author

Makes sense. I update the PR to check the offset of the next field (if there is one).

Swift does not support storing fields in the padding of the previous
fields just yet, so let's not import fields like that from C++.
Represent them as opaque blobs instead.

Fixes #80764

rdar://149072458
@Xazax-hun Xazax-hun force-pushed the gaborh/unique-address-padding branch from 5291e21 to 939ee49 Compare April 25, 2025 11:46
@Xazax-hun
Copy link
Contributor Author

@swift-ci please smoke test

Xazax-hun pushed a commit that referenced this pull request Apr 25, 2025
Explanation: Swift does not support storing fields in the padding of the previous
fields just yet, so let's not import fields like that from C++.
Represent them as opaque blobs instead.
Issue: rdar://149072458
Risk: Low, the fix is targeted at a scenario that was crashing before.
Testing: Regression test added.
Original PR: #80786
Reviewer:
Copy link
Contributor

@j-hui j-hui left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks for fleshing out the test!

@Xazax-hun Xazax-hun merged commit 68869c6 into main Apr 25, 2025
3 checks passed
@Xazax-hun Xazax-hun deleted the gaborh/unique-address-padding branch April 25, 2025 17:14
j-hui added a commit to j-hui/swift that referenced this pull request May 1, 2025
…c import

In swiftlang#80786, we started importing certain padded fields as opaque blobs.
Part of this logic involved querying those fields' ASTRecordLayout.
However, dependendent types (which are imported symbolically) do not
have an ASTRecordLayout, so calling getASTRecordLayout() would lead to
an assertion error for class templates where a no_unique_address field
is some kind of dependent C++ record type.

This patch avoids the field padding check during symbolic import mode
because that check is only relevant for codegen anyway.

rdar://150067288
j-hui added a commit to j-hui/swift that referenced this pull request May 1, 2025
…c import

In swiftlang#80786, we started importing certain padded fields as opaque blobs.
Part of this logic involved querying those fields' ASTRecordLayout.
However, dependent types (which are imported symbolically) do not have
an ASTRecordLayout, so calling Clang's getASTRecordLayout() would lead
to an assertion error for class templates where a no_unique_address
field is some kind of dependent C++ record type.

This patch avoids the field padding check during symbolic import mode
because that check is only relevant for codegen anyway.

rdar://150067288
j-hui added a commit to j-hui/swift that referenced this pull request May 1, 2025
…c import

In swiftlang#80786, we started importing certain padded fields as opaque blobs.
Part of this logic involved querying those fields' ASTRecordLayout.
However, dependent types (which are imported symbolically) do not have
an ASTRecordLayout, so calling Clang's getASTRecordLayout() would lead
to an assertion error for class templates where a no_unique_address
field is some kind of dependent C++ record type.

This patch avoids the field padding check during symbolic import mode
because that check is only relevant for codegen anyway.

rdar://150067288
j-hui added a commit to j-hui/swift that referenced this pull request May 1, 2025
…c import

In swiftlang#80786, we started importing certain padded fields as opaque blobs.
Part of this logic involved querying those fields' ASTRecordLayout.
However, dependent types (which are imported symbolically) do not have
an ASTRecordLayout, so calling Clang's getASTRecordLayout() would lead
to an assertion error for class templates where a no_unique_address
field is some kind of dependent C++ record type.

This patch avoids the field padding check during symbolic import mode
because that check is only relevant for codegen anyway.

rdar://150067288
(cherry picked from commit 1fdb239)
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.

[cxx-interop] C++ class with [[no_unique_address]] member crashes IRGen
3 participants