Skip to content

Commit f830b1c

Browse files
authored
[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
1 parent c337446 commit f830b1c

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1613,6 +1613,14 @@ namespace {
16131613
if (!dc)
16141614
return nullptr;
16151615

1616+
// It's possible that we already encountered and imported decl while
1617+
// importing its decl context. If we are able to find a cached result,
1618+
// use it to avoid making a duplicate imported decl.
1619+
auto alreadyImported =
1620+
Impl.ImportedDecls.find({decl->getCanonicalDecl(), getVersion()});
1621+
if (alreadyImported != Impl.ImportedDecls.end())
1622+
return alreadyImported->second;
1623+
16161624
auto name = importedName.getBaseIdentifier(Impl.SwiftContext);
16171625

16181626
// Create the enum declaration and record it.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: split-file %s %t
2+
// RUN: %target-swift-frontend -typecheck -verify -I %t/Inputs -cxx-interoperability-mode=default -module-name main %t/program.swift
3+
// RUN: %target-swift-frontend -typecheck -verify -I %t/Inputs -cxx-interoperability-mode=off -module-name main %t/program.swift
4+
// REQUIRES: objc_interop
5+
6+
//--- Inputs/module.modulemap
7+
module TheModule {
8+
header "the-header.h"
9+
}
10+
11+
//--- Inputs/the-header.h
12+
#pragma once
13+
14+
#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))
15+
16+
#define __CF_OPTIONS_ATTRIBUTES __attribute__((flag_enum,enum_extensibility(open)))
17+
#if (__cplusplus)
18+
#define CF_OPTIONS(_type, _name) __attribute__((availability(swift,unavailable))) _type _name; enum __CF_OPTIONS_ATTRIBUTES : _name
19+
#else
20+
#define CF_OPTIONS(_type, _name) enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; enum _name : _type
21+
#endif
22+
23+
typedef CF_OPTIONS(unsigned, TheFlags) {
24+
TheFlagsFoo = (1 << 1),
25+
TheFlagsBar = (1 << 2)
26+
} CF_SWIFT_NAME(The.Flags);
27+
28+
typedef TheFlags DaFlags;
29+
30+
struct TheContext {
31+
TheFlags flags;
32+
} CF_SWIFT_NAME(The);
33+
34+
//--- program.swift
35+
import TheModule
36+
37+
func f(_ _: DaFlags) {}

0 commit comments

Comments
 (0)