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 🥳

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 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

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.

2 participants