Skip to content

Conversation

@j-hui
Copy link
Contributor

@j-hui j-hui commented Nov 11, 2025

If we try to import this in ObjC interop mode:

typedef CF_OPTIONS(uint32_t, MyFlags) {
  ...
} CF_SWIFT_NAME(MyCtx.Flags);

struct MyStruct {
  MyFlags flags;
  ...
} CF_SWIFT_NAME(MyCtx);

ClangImporter tries to import MyCtx/MyStruct before it imports MyFlags (via importDeclContextOf()), which in turn tries to import MyFlags again due to the flags field. The existing cycle-breaking mechanism prevents us from looping infinitely, but leads us to import two copies of the Swift EnumDecl, which can cause errors later during CodeGen.

This patch adds an assertion to catch such issues earlier, and breaks the cycle by checking the cache again. This patch no longer does so because that caused issues beyond the scope of this patch.

rdar://162317760

@j-hui
Copy link
Contributor Author

j-hui commented Nov 11, 2025

@swift-ci please test

if (!HadForwardDeclaration) {
auto it = ImportedDecls.try_emplace({Canon, version}, Result);
if (!it.second && Result != it.first->second) {
ABORT([&](auto &out) {
Copy link
Member

Choose a reason for hiding this comment

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

nice, didn't know about this macro

Canon->getSourceRange().print(out, Instance->getSourceManager());
}
out << "\nImported as Swift Decl:\n";
Result->dump(out, 4);
Copy link
Member

Choose a reason for hiding this comment

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

there's an indentation parameter to dump?? is that true for all of them or just decls?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Appears to be available for all swift::Decls

@j-hui j-hui force-pushed the check-cache-4-enumz branch from 6c51588 to b1aa927 Compare November 11, 2025 04:42
@j-hui
Copy link
Contributor Author

j-hui commented Nov 11, 2025

@swift-ci please test

@hnrklssn
Copy link
Member

[2025-11-11T06:00:16.957Z] error: compile command failed due to signal 6 (use -v to see invocation)
[2025-11-11T06:00:16.957Z] Abort: function importDeclAndCacheImpl at ImportDecl.cpp:10041
[2025-11-11T06:00:16.957Z] Imported the same clang::Decl twice: 'std::common_type<class std::chrono::duration<long long>, class std::chrono::duration<long long> >::type' </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk/usr/include/c++/v1/__chrono/duration.h:56:3, line:57:7>
[2025-11-11T06:00:16.957Z] Imported as Swift Decl:
[2025-11-11T06:00:16.957Z]     (typealias decl_context=0x7f7bc1aca800 range=[/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk/usr/include/c++/v1/__chrono/duration.h:56:3 - line:57:7] "type" access=public type="std.__1.chrono.duration<CLongLong, std.__1.ratio<_CLong_1, _CLong_1>>")
[2025-11-11T06:00:16.957Z] 
[2025-11-11T06:00:16.957Z] Previously imported as Swift Decl:
[2025-11-11T06:00:16.957Z]     (typealias decl_context=0x7f7bc1aca800 range=[/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk/usr/include/c++/v1/__chrono/duration.h:56:3 - line:57:7] "type" access=public type="std.__1.chrono.duration<CLongLong, std.__1.ratio<_CLong_1, _CLong_1>>")

It works 🥳

@j-hui j-hui force-pushed the check-cache-4-enumz branch from b1aa927 to 005acc4 Compare November 13, 2025 07:13
@j-hui
Copy link
Contributor Author

j-hui commented Nov 13, 2025

@swift-ci please test

@j-hui j-hui force-pushed the check-cache-4-enumz branch from 005acc4 to 2092b20 Compare November 13, 2025 08:35
@j-hui
Copy link
Contributor Author

j-hui commented Nov 13, 2025

@swift-ci please test

@hnrklssn
Copy link
Member

What was the change after force push?

@j-hui
Copy link
Contributor Author

j-hui commented Nov 13, 2025

The change was making #ifdef __cplusplus to #if (__cplusplus)

@egorzhdan
Copy link
Contributor

@hnrklssn the GitHub UI doesn't make it very obvious, but you can click on the "force-pushed" label and that will show you the diff 😉

Before we import an enum, we import its DeclContext. If that DeclContext
gets resolved to a struct that contains that field of that enum type,
e.g.:

    typedef CF_OPTIONS(uint32_t, MyFlags) {
      ...
    } CF_SWIFT_NAME(MyCtx.Flags);

    struct MyStruct {
      MyFlags flags;
      ...
    } CF_SWIFT_NAME(MyCtx);

We end up with a cycle, i.e., MyFlags -> MyCtx -> MyStruct -> MyFlags.

Existing cycle-breaking mechanisms seem to prevent this from looping
infinitely, but it leads to us importing two copies of the enum, which
can cause errors later during CodeGen.

This patch breaks the cycle by checking the cache again.

N.B. as of this commit, the circular-import-as-member.swift test doesn't
actually fail *without* the fix, because ClangImporter is quite
lenient about importing the same clang::Decl multiple times. An earlier
attempt at this patch ABORT()'d the compiler as soon as a duplicate is
detected, but that led to more isseus beyond the scope of this patch.

rdar://162317760
@j-hui j-hui force-pushed the check-cache-4-enumz branch from 2092b20 to 5cca1a6 Compare November 13, 2025 18:13
@j-hui
Copy link
Contributor Author

j-hui commented Nov 13, 2025

@swift-ci please test

@hnrklssn
Copy link
Member

hnrklssn commented Nov 13, 2025

the GitHub UI doesn't make it very obvious, but you can click on the "force-pushed" label and that will show you the diff 😉

You can also click the "Compare" button to the right ;)
But I never do, because force pushing almost always means that the author rebased their changes IME, and then you get the entire diff of the everything that occurred on main in-between, which isn't very useful.

@j-hui j-hui enabled auto-merge November 14, 2025 01:41
@j-hui j-hui disabled auto-merge November 14, 2025 01:42
@j-hui j-hui merged commit f830b1c into swiftlang:main Nov 17, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants