[ClangImporter] Do not import enum when already imported via DeclContext (#85424)

If we try to import this in ObjC interop mode:

```objc
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
This commit is contained in:
John Hui
2025-11-17 13:18:59 -08:00
committed by GitHub
parent c337446464
commit f830b1c665
2 changed files with 45 additions and 0 deletions

View File

@@ -1613,6 +1613,14 @@ namespace {
if (!dc)
return nullptr;
// It's possible that we already encountered and imported decl while
// importing its decl context. If we are able to find a cached result,
// use it to avoid making a duplicate imported decl.
auto alreadyImported =
Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()});
if (alreadyImported != Impl.ImportedDecls.end())
return alreadyImported->second;
auto name = importedName.getBaseIdentifier(Impl.SwiftContext);
// Create the enum declaration and record it.

View File

@@ -0,0 +1,37 @@
// RUN: split-file %s %t
// RUN: %target-swift-frontend -typecheck -verify -I %t/Inputs -cxx-interoperability-mode=default -module-name main %t/program.swift
// RUN: %target-swift-frontend -typecheck -verify -I %t/Inputs -cxx-interoperability-mode=off -module-name main %t/program.swift
// REQUIRES: objc_interop
//--- Inputs/module.modulemap
module TheModule {
header "the-header.h"
}
//--- Inputs/the-header.h
#pragma once
#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
#if (__cplusplus)
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
#else
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
#endif
typedef CF_OPTIONS(unsigned, TheFlags) {
TheFlagsFoo = (1 << 1),
TheFlagsBar = (1 << 2)
} CF_SWIFT_NAME(The.Flags);
typedef TheFlags DaFlags;
struct TheContext {
TheFlags flags;
} CF_SWIFT_NAME(The);
//--- program.swift
import TheModule
func f(_ _: DaFlags) {}