Skip to content

Commit 121ebb0

Browse files
committed
Merge pull request #10956 from jrose-apple/lookupNestedType-for-Clang
[ClangImporter] Add direct access for import-as-member types.
1 parent f0b00cf commit 121ebb0

File tree

17 files changed

+213
-30
lines changed

17 files changed

+213
-30
lines changed

include/swift/AST/Module.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,18 @@ class FileUnit : public DeclContext {
558558
return nullptr;
559559
}
560560

561+
/// Directly look for a nested type declared within this module inside the
562+
/// given nominal type (including any extensions).
563+
///
564+
/// This is a fast-path hack to avoid circular dependencies in deserialization
565+
/// and the Clang importer.
566+
///
567+
/// Private and fileprivate types should not be returned by this lookup.
568+
virtual TypeDecl *lookupNestedType(Identifier name,
569+
const NominalTypeDecl *parent) const {
570+
return nullptr;
571+
}
572+
561573
/// Find ValueDecls in the module and pass them to the given consumer object.
562574
///
563575
/// This does a simple local lookup, not recursively looking through imports.

include/swift/ClangImporter/ClangModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ class ClangModuleUnit final : public LoadedFile {
6262
DeclName name, NLKind lookupKind,
6363
SmallVectorImpl<ValueDecl*> &results) const override;
6464

65+
virtual TypeDecl *
66+
lookupNestedType(Identifier name,
67+
const NominalTypeDecl *baseType) const override;
68+
6569
virtual void lookupVisibleDecls(ModuleDecl::AccessPathTy accessPath,
6670
VisibleDeclConsumer &consumer,
6771
NLKind lookupKind) const override;

include/swift/Serialization/ModuleFile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ class ModuleFile : public LazyMemberLoader {
603603

604604
/// Searches the module's nested type decls table for the given member of
605605
/// the given type.
606-
TypeDecl *lookupNestedType(Identifier name, const ValueDecl *parent);
606+
TypeDecl *lookupNestedType(Identifier name, const NominalTypeDecl *parent);
607607

608608
/// Searches the module's operators for one with the given name and fixity.
609609
///

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ class SerializedASTFile final : public LoadedFile {
132132

133133
virtual TypeDecl *lookupLocalType(StringRef MangledName) const override;
134134

135+
virtual TypeDecl *
136+
lookupNestedType(Identifier name,
137+
const NominalTypeDecl *parent) const override;
138+
135139
virtual OperatorDecl *lookupOperator(Identifier name,
136140
DeclKind fixity) const override;
137141

lib/ClangImporter/ClangImporter.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,91 @@ static bool isVisibleClangEntry(clang::ASTContext &ctx,
24282428
return true;
24292429
}
24302430

2431+
TypeDecl *
2432+
ClangModuleUnit::lookupNestedType(Identifier name,
2433+
const NominalTypeDecl *baseType) const {
2434+
// Special case for error code enums: try looking directly into the struct
2435+
// first. But only if it looks like a synthesized error wrapped struct.
2436+
if (name == getASTContext().Id_Code && !baseType->hasClangNode() &&
2437+
isa<StructDecl>(baseType) && !baseType->hasLazyMembers() &&
2438+
baseType->isChildContextOf(this)) {
2439+
auto *mutableBase = const_cast<NominalTypeDecl *>(baseType);
2440+
auto codeEnum = mutableBase->lookupDirect(name,/*ignoreNewExtensions*/true);
2441+
// Double-check that we actually have a good result. It's possible what we
2442+
// found is /not/ a synthesized error struct, but just something that looks
2443+
// like it. But if we still found a good result we should return that.
2444+
if (codeEnum.size() == 1 && isa<TypeDecl>(codeEnum.front()))
2445+
return cast<TypeDecl>(codeEnum.front());
2446+
if (codeEnum.size() > 1)
2447+
return nullptr;
2448+
// Otherwise, fall back and try via lookup table.
2449+
}
2450+
2451+
auto lookupTable = owner.findLookupTable(clangModule);
2452+
if (!lookupTable)
2453+
return nullptr;
2454+
2455+
auto baseTypeContext = owner.getEffectiveClangContext(baseType);
2456+
if (!baseTypeContext)
2457+
return nullptr;
2458+
2459+
auto &clangCtx = owner.getClangASTContext();
2460+
2461+
// FIXME: This is very similar to what's in Implementation::lookupValue and
2462+
// Implementation::loadAllMembers.
2463+
SmallVector<TypeDecl *, 2> results;
2464+
for (auto entry : lookupTable->lookup(SerializedSwiftName(name.str()),
2465+
baseTypeContext)) {
2466+
// If the entry is not visible, skip it.
2467+
if (!isVisibleClangEntry(clangCtx, entry)) continue;
2468+
2469+
auto clangDecl = entry.dyn_cast<clang::NamedDecl *>();
2470+
auto clangTypeDecl = dyn_cast_or_null<clang::TypeDecl>(clangDecl);
2471+
if (!clangTypeDecl)
2472+
continue;
2473+
2474+
clangTypeDecl = cast<clang::TypeDecl>(clangTypeDecl->getMostRecentDecl());
2475+
2476+
bool anyMatching = false;
2477+
TypeDecl *originalDecl = nullptr;
2478+
owner.forEachDistinctName(clangTypeDecl, [&](ImportedName newName,
2479+
ImportNameVersion nameVersion){
2480+
if (anyMatching)
2481+
return;
2482+
if (!newName.getDeclName().isSimpleName(name))
2483+
return;
2484+
2485+
auto decl = dyn_cast_or_null<TypeDecl>(
2486+
owner.importDeclReal(clangTypeDecl, nameVersion));
2487+
if (!decl)
2488+
return;
2489+
2490+
if (!originalDecl)
2491+
originalDecl = decl;
2492+
else if (originalDecl == decl)
2493+
return;
2494+
2495+
auto *importedContext = decl->getDeclContext()->
2496+
getAsNominalTypeOrNominalTypeExtensionContext();
2497+
if (importedContext != baseType)
2498+
return;
2499+
2500+
assert(decl->getFullName().matchesRef(name) &&
2501+
"importFullName behaved differently from importDecl");
2502+
results.push_back(decl);
2503+
anyMatching = true;
2504+
});
2505+
}
2506+
2507+
if (results.size() != 1) {
2508+
// It's possible that two types were import-as-member'd onto the same base
2509+
// type with the same name. In this case, fall back to regular lookup.
2510+
return nullptr;
2511+
}
2512+
2513+
return results.front();
2514+
}
2515+
24312516
void ClangImporter::loadExtensions(NominalTypeDecl *nominal,
24322517
unsigned previousGeneration) {
24332518
// Determine the effective Clang context for this Swift nominal type.
@@ -3124,7 +3209,7 @@ void ClangImporter::Implementation::lookupAllObjCMembers(
31243209
}
31253210

31263211
EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
3127-
NominalTypeDecl *nominal) {
3212+
const NominalTypeDecl *nominal) {
31283213
// If we have a Clang declaration, look at it to determine the
31293214
// effective Clang context.
31303215
if (auto constClangDecl = nominal->getClangDecl()) {
@@ -3139,7 +3224,7 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
31393224

31403225
// Resolve the type.
31413226
if (auto typeResolver = getTypeResolver())
3142-
typeResolver->resolveDeclSignature(nominal);
3227+
typeResolver->resolveDeclSignature(const_cast<NominalTypeDecl *>(nominal));
31433228

31443229
// If it's an @objc entity, go look for it.
31453230
if (nominal->isObjC()) {

lib/ClangImporter/ImporterImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11631163
VisibleDeclConsumer &consumer);
11641164

11651165
/// Determine the effective Clang context for the given Swift nominal type.
1166-
EffectiveClangContext getEffectiveClangContext(NominalTypeDecl *nominal);
1166+
EffectiveClangContext
1167+
getEffectiveClangContext(const NominalTypeDecl *nominal);
11671168

11681169
/// Attempts to import the name of \p decl with each possible
11691170
/// ImportNameVersion. \p action will be called with each unique name.

lib/Serialization/Deserialization.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,9 +1329,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13291329
&blobData);
13301330
switch (recordID) {
13311331
case XREF_TYPE_PATH_PIECE: {
1332-
if (values.size() == 1) {
1333-
ModuleDecl *module = values.front()->getModuleContext();
1334-
1332+
if (values.size() == 1 && isa<NominalTypeDecl>(values.front())) {
13351333
// Fast path for nested types that avoids deserializing all
13361334
// members of the parent type.
13371335
IdentifierID IID;
@@ -1344,29 +1342,23 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13441342
"If you're seeing a crash here, try passing "
13451343
"-Xfrontend -disable-serialization-nested-type-lookup-table"};
13461344

1345+
auto *baseType = cast<NominalTypeDecl>(values.front());
13471346
TypeDecl *nestedType = nullptr;
13481347
if (onlyInNominal) {
13491348
// Only look in the file containing the type itself.
13501349
const DeclContext *dc = values.front()->getDeclContext();
1351-
auto *serializedFile =
1352-
dyn_cast<SerializedASTFile>(dc->getModuleScopeContext());
1353-
if (serializedFile) {
1354-
nestedType =
1355-
serializedFile->File.lookupNestedType(memberName,
1356-
values.front());
1350+
auto *containingFile =
1351+
dyn_cast<FileUnit>(dc->getModuleScopeContext());
1352+
if (containingFile) {
1353+
nestedType = containingFile->lookupNestedType(memberName, baseType);
13571354
}
13581355
} else {
13591356
// Fault in extensions, then ask every serialized AST in the module.
1360-
(void)cast<NominalTypeDecl>(values.front())->getExtensions();
1361-
for (FileUnit *file : module->getFiles()) {
1357+
(void)baseType->getExtensions();
1358+
for (FileUnit *file : baseType->getModuleContext()->getFiles()) {
13621359
if (file == getFile())
13631360
continue;
1364-
auto *serializedFile = dyn_cast<SerializedASTFile>(file);
1365-
if (!serializedFile)
1366-
continue;
1367-
nestedType =
1368-
serializedFile->File.lookupNestedType(memberName,
1369-
values.front());
1361+
nestedType = file->lookupNestedType(memberName, baseType);
13701362
if (nestedType)
13711363
break;
13721364
}

lib/Serialization/ModuleFile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,7 @@ TypeDecl *ModuleFile::lookupLocalType(StringRef MangledName) {
13461346
}
13471347

13481348
TypeDecl *ModuleFile::lookupNestedType(Identifier name,
1349-
const ValueDecl *parent) {
1349+
const NominalTypeDecl *parent) {
13501350
PrettyStackTraceModuleFile stackEntry(*this);
13511351

13521352
if (!NestedTypeDecls)

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,12 @@ TypeDecl *SerializedASTFile::lookupLocalType(llvm::StringRef MangledName) const{
525525
return File.lookupLocalType(MangledName);
526526
}
527527

528+
TypeDecl *
529+
SerializedASTFile::lookupNestedType(Identifier name,
530+
const NominalTypeDecl *parent) const {
531+
return File.lookupNestedType(name, parent);
532+
}
533+
528534
OperatorDecl *SerializedASTFile::lookupOperator(Identifier name,
529535
DeclKind fixity) const {
530536
return File.lookupOperator(name, fixity);

test/ClangImporter/Inputs/custom-modules/ImportAsMember.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,12 @@ extern void IAMStruct1SelfComesLast(double x, struct IAMStruct1 s)
5757
extern void IAMStruct1SelfComesThird(int a, float b, struct IAMStruct1 s, double x)
5858
__attribute__((swift_name("Struct1.selfComesThird(a:b:self:x:)")));
5959

60+
61+
struct IAMMultipleNested {
62+
int value;
63+
};
64+
65+
typedef int MNInnerInt __attribute__((swift_name("IAMMultipleNested.Inner")));
66+
typedef float MNInnerFloat __attribute__((swift_name("IAMMultipleNested.Inner")));
67+
6068
#endif // IMPORT_AS_MEMBER_H
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
struct IAMOuter { int x; };
1+
struct IAMSOuter { int x; };
22

3-
struct IAMInner { int y; };
3+
struct IAMSInner { int y; };
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
// The order of these forward-declarations affects whether there was a bug.
2-
struct IAMOuter;
3-
struct IAMInner;
2+
struct IAMSOuter;
3+
struct IAMSInner;
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Name: ImportAsMemberSubmodules
22
Tags:
3-
- Name: IAMInner
4-
SwiftName: IAMOuter.Inner
3+
- Name: IAMSInner
4+
SwiftName: IAMSOuter.Inner
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks %s -verify
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -F %S/Inputs/frameworks -I %S/Inputs/custom-modules %s -verify
22

3+
import ImportAsMember
34
import ImportAsMemberSubmodules
45

5-
let _: IAMOuter.Inner?
6+
let _: IAMSOuter.Inner?
7+
let _: IAMMultipleNested.Inner? // expected-error {{ambiguous type name 'Inner' in 'IAMMultipleNested'}}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
struct Outer {
2+
int value;
3+
};
4+
5+
struct __attribute__((swift_name("Outer.InterestingValue"))) Inner {
6+
int value;
7+
};
8+
9+
#if __OBJC__
10+
11+
@import Foundation;
12+
13+
extern NSString * const ErrorCodeDomain;
14+
enum __attribute__((ns_error_domain(ErrorCodeDomain))) ErrorCodeEnum {
15+
ErrorCodeEnumA
16+
};
17+
18+
#endif // __OBJC__
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module NestedClangTypes {
2+
header "NestedClangTypes.h"
3+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t -I %S/Inputs/xref-nested-clang-type/ %s -module-name Lib
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %t -I %S/Inputs/xref-nested-clang-type/ %s -DCLIENT -verify
4+
5+
// RUN: %empty-directory(%t)
6+
// RUN: cp %S/Inputs/xref-nested-clang-type/NestedClangTypes.h %t
7+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t -import-objc-header %t/NestedClangTypes.h %s -module-name Lib -DNO_MODULE
8+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %t -import-objc-header %t/NestedClangTypes.h %s -DCLIENT -verify
9+
10+
// RUN: %empty-directory(%t)
11+
// RUN: cp %S/Inputs/xref-nested-clang-type/NestedClangTypes.h %t
12+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t -import-objc-header %t/NestedClangTypes.h -pch-output-dir %t/PCHCache %s -module-name Lib -DNO_MODULE
13+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %t -pch-output-dir %t/PCHCache -import-objc-header %t/NestedClangTypes.h %s -DCLIENT -verify
14+
15+
#if CLIENT
16+
17+
import Lib
18+
19+
func test(x: MyInner) {}
20+
21+
#if _runtime(_ObjC)
22+
import Foundation
23+
func test(x: MyCode) {}
24+
#endif // ObjC
25+
26+
#else // CLIENT
27+
28+
#if !NO_MODULE
29+
import NestedClangTypes
30+
#endif
31+
32+
public typealias MyOuter = Outer
33+
public typealias MyInner = Outer.InterestingValue
34+
35+
extension MyOuter {
36+
public func use(inner: MyInner) {}
37+
}
38+
39+
#if _runtime(_ObjC)
40+
import Foundation
41+
42+
public typealias MyCode = ErrorCodeEnum.Code
43+
extension ErrorCodeEnum {
44+
public func whatever(code: MyCode) {}
45+
}
46+
#endif // ObjC
47+
48+
#endif // CLIENT

0 commit comments

Comments
 (0)