Skip to content

Commit 138bbe2

Browse files
committed
[Clang importer] Delay resolution of import-as-member declarations when needed.
It's possible for swift_name to make a global declaration into a member of another entity that has not been seen yet. In such cases, delay resolution until the end of the translation unit (module). Fixes the rest of rdar://problem/25502497.
1 parent 52cd871 commit 138bbe2

File tree

7 files changed

+133
-19
lines changed

7 files changed

+133
-19
lines changed

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ WARNING(inconsistent_swift_name,none,
7272
(bool, StringRef, StringRef, DeclName, StringRef, DeclName,
7373
StringRef))
7474

75+
WARNING(unresolvable_clang_decl,none,
76+
"imported declaration '%0' could not be mapped to '%1'",
77+
(StringRef, StringRef))
78+
7579
#ifndef DIAG_NO_UNDEF
7680
# if defined(DIAG)
7781
# undef DIAG

lib/ClangImporter/ClangImporter.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,25 @@ void ClangImporter::Implementation::addMacrosToLookupTable(
844844
}
845845
}
846846

847+
void ClangImporter::Implementation::finalizeLookupTable(
848+
clang::ASTContext &clangCtx,
849+
clang::Preprocessor &pp,
850+
SwiftLookupTable &table) {
851+
// Resolve any unresolved entries.
852+
SmallVector<SwiftLookupTable::SingleEntry, 4> unresolved;
853+
if (table.resolveUnresolvedEntries(unresolved)) {
854+
// Complain about unresolved entries that remain.
855+
for (auto entry : unresolved) {
856+
auto decl = entry.get<clang::NamedDecl *>();
857+
auto swiftName = decl->getAttr<clang::SwiftNameAttr>();
858+
859+
SwiftContext.Diags.diagnose(SourceLoc(), diag::unresolvable_clang_decl,
860+
decl->getNameAsString(),
861+
swiftName->getName());
862+
}
863+
}
864+
}
865+
847866
bool ClangImporter::Implementation::importHeader(
848867
Module *adapter, StringRef headerName, SourceLoc diagLoc,
849868
bool trackParsedSymbols,
@@ -920,6 +939,10 @@ bool ClangImporter::Implementation::importHeader(
920939
}
921940
}
922941

942+
// Finalize the lookup table, which may fail.
943+
finalizeLookupTable(getClangASTContext(), getClangPreprocessor(),
944+
BridgingHeaderLookupTable);
945+
923946
// FIXME: What do we do if there was already an error?
924947
if (!hadError && clangDiags.hasErrorOccurred()) {
925948
SwiftContext.Diags.diagnose(diagLoc, diag::bridging_header_error,
@@ -4043,6 +4066,9 @@ ClangImporter::Implementation::SwiftNameLookupExtension::createExtensionWriter(
40434066

40444067
// Add macros to the lookup table.
40454068
Impl.addMacrosToLookupTable(sema.Context, sema.getPreprocessor(), table);
4069+
4070+
// Finalize the lookup table, which may fail.
4071+
Impl.finalizeLookupTable(sema.Context, sema.getPreprocessor(), table);
40464072
};
40474073

40484074
return std::unique_ptr<clang::ModuleFileExtensionWriter>(

lib/ClangImporter/ImporterImpl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,11 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
696696
void addMacrosToLookupTable(clang::ASTContext &clangCtx,
697697
clang::Preprocessor &pp, SwiftLookupTable &table);
698698

699+
/// Finalize a lookup table, handling any as-yet-unresolved entries
700+
/// and emitting diagnostics if necessary.
701+
void finalizeLookupTable(clang::ASTContext &clangCtx,
702+
clang::Preprocessor &pp, SwiftLookupTable &table);
703+
699704
public:
700705
void registerExternalDecl(Decl *D) {
701706
RegisteredExternalDecls.push_back(D);

lib/ClangImporter/SwiftLookupTable.cpp

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ translateDeclToContext(clang::NamedDecl *decl) {
8484
// If this typedef is merely a restatement of a tag declaration's type,
8585
// return the result for that tag.
8686
if (auto tag = typedefName->getUnderlyingType()->getAsTagDecl())
87-
return translateDeclToContext(tag);
87+
return translateDeclToContext(const_cast<clang::TagDecl *>(tag));
8888

8989
// Otherwise, this must be a typedef mapped to a strong type.
9090
return std::make_pair(SwiftLookupTable::ContextKind::Typedef,
@@ -105,13 +105,8 @@ SwiftLookupTable::translateContext(EffectiveClangContext context) {
105105
return std::make_pair(ContextKind::TranslationUnit, StringRef());
106106

107107
// Tag declaration context.
108-
if (auto tag = dyn_cast<clang::TagDecl>(dc)) {
109-
if (tag->getIdentifier())
110-
return std::make_pair(ContextKind::Tag, tag->getName());
111-
if (auto typedefDecl = tag->getTypedefNameForAnonDecl())
112-
return std::make_pair(ContextKind::Tag, typedefDecl->getName());
113-
return None;
114-
}
108+
if (auto tag = dyn_cast<clang::TagDecl>(dc))
109+
return translateDeclToContext(const_cast<clang::TagDecl *>(tag));
115110

116111
// Objective-C class context.
117112
if (auto objcClass = dyn_cast<clang::ObjCInterfaceDecl>(dc))
@@ -130,10 +125,8 @@ SwiftLookupTable::translateContext(EffectiveClangContext context) {
130125

131126
case EffectiveClangContext::UnresolvedContext:
132127
// Resolve the context.
133-
if (auto decl = resolveContext(context.getUnresolvedName())) {
134-
if (auto context = translateDeclToContext(decl))
135-
return context;
136-
}
128+
if (auto decl = resolveContext(context.getUnresolvedName()))
129+
return translateDeclToContext(decl);
137130

138131
return None;
139132
}
@@ -167,6 +160,66 @@ void SwiftLookupTable::addCategory(clang::ObjCCategoryDecl *category) {
167160
Categories.push_back(category);
168161
}
169162

163+
bool SwiftLookupTable::resolveUnresolvedEntries(
164+
SmallVectorImpl<SingleEntry> &unresolved) {
165+
// Common case: nothing left to resolve.
166+
unresolved.clear();
167+
if (UnresolvedEntries.empty()) return false;
168+
169+
// Reprocess each of the unresolved entries to see if it can be
170+
// resolved now that we're done. This occurs when a swift_name'd
171+
// entity becomes a member of an entity that follows it in the
172+
// translation unit, e.g., given:
173+
//
174+
// \code
175+
// typedef enum FooSomeEnumeration __attribute__((Foo.SomeEnum)) {
176+
// ...
177+
// } FooSomeEnumeration;
178+
//
179+
// typedef struct Foo {
180+
//
181+
// } Foo;
182+
// \endcode
183+
//
184+
// FooSomeEnumeration belongs inside "Foo", but we haven't actually
185+
// seen "Foo" yet. Therefore, we will reprocess FooSomeEnumeration
186+
// at the end, once "Foo" is available. There are several reasons
187+
// this loop can execute:
188+
//
189+
// * Import-as-member places an entity inside of an another entity
190+
// that comes later in the translation unit. The number of
191+
// iterations that can be caused by this is bounded by the nesting
192+
// depth. (At present, that depth is limited to 2).
193+
//
194+
// * An erroneous import-as-member will cause an extra iteration at
195+
// the end, so that the loop can detect that nothing changed and
196+
// return a failure.
197+
while (true) {
198+
// Take the list of unresolved entries to process.
199+
auto prevNumUnresolvedEntries = UnresolvedEntries.size();
200+
auto currentUnresolved = std::move(UnresolvedEntries);
201+
UnresolvedEntries.clear();
202+
203+
// Process each of the currently-unresolved entries.
204+
for (const auto &entry : currentUnresolved)
205+
addEntry(std::get<0>(entry), std::get<1>(entry), std::get<2>(entry));
206+
207+
// Are we done?
208+
if (UnresolvedEntries.empty()) return false;
209+
210+
// If nothing changed, fail: something is unresolvable, and the
211+
// caller should complain.
212+
if (UnresolvedEntries.size() == prevNumUnresolvedEntries) {
213+
for (const auto &entry : UnresolvedEntries)
214+
unresolved.push_back(std::get<1>(entry));
215+
return true;
216+
}
217+
218+
// Something got resolved, so loop again.
219+
assert(UnresolvedEntries.size() < prevNumUnresolvedEntries);
220+
}
221+
}
222+
170223
/// Determine whether the entry is a global declaration that is being
171224
/// mapped as a member of a particular type or extension thereof.
172225
///
@@ -237,7 +290,19 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
237290

238291
// Translate the context.
239292
auto contextOpt = translateContext(effectiveContext);
240-
if (!contextOpt) return;
293+
if (!contextOpt) {
294+
// If is is a declaration with a swift_name attribute, we might be
295+
// able to resolve this later.
296+
if (auto decl = newEntry.dyn_cast<clang::NamedDecl *>()) {
297+
if (decl->hasAttr<clang::SwiftNameAttr>()) {
298+
UnresolvedEntries.push_back(
299+
std::make_tuple(name, newEntry, effectiveContext));
300+
}
301+
}
302+
303+
return;
304+
}
305+
241306
auto context = *contextOpt;
242307

243308
// If this is a global imported as a member, record is as such.

lib/ClangImporter/SwiftLookupTable.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ class SwiftLookupTable {
281281
/// The reader responsible for lazily loading the contents of this table.
282282
SwiftLookupTableReader *Reader;
283283

284+
/// Entries whose effective contexts could not be resolved, and
285+
/// therefore will need to be added later.
286+
SmallVector<std::tuple<DeclName, SingleEntry, EffectiveClangContext>, 4>
287+
UnresolvedEntries;
288+
284289
friend class SwiftLookupTableReader;
285290
friend class SwiftLookupTableWriter;
286291

@@ -320,6 +325,15 @@ class SwiftLookupTable {
320325
/// Add an Objective-C category or extension to the table.
321326
void addCategory(clang::ObjCCategoryDecl *category);
322327

328+
/// Resolve any unresolved entries.
329+
///
330+
/// \param unresolved Will be populated with the list of entries
331+
/// that could not be resolved.
332+
///
333+
/// \returns true if any remaining entries could not be resolved,
334+
/// and false otherwise.
335+
bool resolveUnresolvedEntries(SmallVectorImpl<SingleEntry> &unresolved);
336+
323337
private:
324338
/// Lookup the set of entities with the given base name.
325339
///

test/IDE/Inputs/custom-modules/ImportAsMemberClass.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33

44
@import Foundation;
55

6-
__attribute__((swift_name("SomeClass")))
7-
@interface IAMSomeClass : NSObject
8-
@end
9-
106
typedef NS_OPTIONS(NSInteger, IAMSomeClassOptions) {
117
IAMSomeClassFuzzyDice = 0x01,
128
IAMSomeClassSpoiler = 0x02
139
} __attribute__((swift_name("SomeClass.Options")));
1410

11+
__attribute__((swift_name("SomeClass")))
12+
@interface IAMSomeClass : NSObject
13+
@end
14+
1515
__attribute__((swift_name("SomeClass.init(value:)")))
1616
IAMSomeClass * _Nonnull MakeIAMSomeClass(double x);
1717

test/IDE/import_as_member_objc.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
// PRINT-CLASS-NEXT: init()
2424
// PRINT-CLASS-NEXT: }
2525
// PRINT-CLASS-NEXT: extension SomeClass {
26+
// PRINT-CLASS-NEXT: /*not inherited*/ init(value x: Double)
27+
// PRINT-CLASS-NEXT: func applyOptions(_ options: SomeClass.Options)
2628
// PRINT-CLASS-NEXT: struct Options : OptionSet {
2729
// PRINT-CLASS-NEXT: init(rawValue rawValue: Int)
2830
// PRINT-CLASS-NEXT: let rawValue: Int
2931
// PRINT-CLASS-NEXT: static var fuzzyDice: SomeClass.Options { get }
3032
// PRINT-CLASS-NEXT: static var spoiler: SomeClass.Options { get }
3133
// PRINT-CLASS-NEXT: }
32-
// PRINT-CLASS-NEXT: /*not inherited*/ init(value x: Double)
33-
// PRINT-CLASS-NEXT: func applyOptions(_ options: SomeClass.Options)
3434
// PRINT-CLASS-NEXT: }
3535

3636
// RUN: %target-parse-verify-swift -I %S/Inputs/custom-modules

0 commit comments

Comments
 (0)