Skip to content

Commit 2dbae8e

Browse files
authored
Merge pull request #10956 from jrose-apple/lookupNestedType-for-Clang
[ClangImporter] Add direct access for import-as-member types.
2 parents 4d7ae8c + 3d42828 commit 2dbae8e

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
@@ -555,6 +555,18 @@ class FileUnit : public DeclContext {
555555
return nullptr;
556556
}
557557

558+
/// Directly look for a nested type declared within this module inside the
559+
/// given nominal type (including any extensions).
560+
///
561+
/// This is a fast-path hack to avoid circular dependencies in deserialization
562+
/// and the Clang importer.
563+
///
564+
/// Private and fileprivate types should not be returned by this lookup.
565+
virtual TypeDecl *lookupNestedType(Identifier name,
566+
const NominalTypeDecl *parent) const {
567+
return nullptr;
568+
}
569+
558570
/// Find ValueDecls in the module and pass them to the given consumer object.
559571
///
560572
/// 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
@@ -2432,6 +2432,91 @@ static bool isVisibleClangEntry(clang::ASTContext &ctx,
24322432
return true;
24332433
}
24342434

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

31293214
EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
3130-
NominalTypeDecl *nominal) {
3215+
const NominalTypeDecl *nominal) {
31313216
// If we have a Clang declaration, look at it to determine the
31323217
// effective Clang context.
31333218
if (auto constClangDecl = nominal->getClangDecl()) {
@@ -3142,7 +3227,7 @@ EffectiveClangContext ClangImporter::Implementation::getEffectiveClangContext(
31423227

31433228
// Resolve the type.
31443229
if (auto typeResolver = getTypeResolver())
3145-
typeResolver->resolveDeclSignature(nominal);
3230+
typeResolver->resolveDeclSignature(const_cast<NominalTypeDecl *>(nominal));
31463231

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

lib/ClangImporter/ImporterImpl.h

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

11821182
/// Determine the effective Clang context for the given Swift nominal type.
1183-
EffectiveClangContext getEffectiveClangContext(NominalTypeDecl *nominal);
1183+
EffectiveClangContext
1184+
getEffectiveClangContext(const NominalTypeDecl *nominal);
11841185

11851186
/// Attempts to import the name of \p decl with each possible
11861187
/// 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
@@ -1319,9 +1319,7 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13191319
&blobData);
13201320
switch (recordID) {
13211321
case XREF_TYPE_PATH_PIECE: {
1322-
if (values.size() == 1) {
1323-
ModuleDecl *module = values.front()->getModuleContext();
1324-
1322+
if (values.size() == 1 && isa<NominalTypeDecl>(values.front())) {
13251323
// Fast path for nested types that avoids deserializing all
13261324
// members of the parent type.
13271325
IdentifierID IID;
@@ -1334,29 +1332,23 @@ ModuleFile::resolveCrossReference(ModuleDecl *baseModule, uint32_t pathLen) {
13341332
"If you're seeing a crash here, try passing "
13351333
"-Xfrontend -disable-serialization-nested-type-lookup-table"};
13361334

1335+
auto *baseType = cast<NominalTypeDecl>(values.front());
13371336
TypeDecl *nestedType = nullptr;
13381337
if (onlyInNominal) {
13391338
// Only look in the file containing the type itself.
13401339
const DeclContext *dc = values.front()->getDeclContext();
1341-
auto *serializedFile =
1342-
dyn_cast<SerializedASTFile>(dc->getModuleScopeContext());
1343-
if (serializedFile) {
1344-
nestedType =
1345-
serializedFile->File.lookupNestedType(memberName,
1346-
values.front());
1340+
auto *containingFile =
1341+
dyn_cast<FileUnit>(dc->getModuleScopeContext());
1342+
if (containingFile) {
1343+
nestedType = containingFile->lookupNestedType(memberName, baseType);
13471344
}
13481345
} else {
13491346
// Fault in extensions, then ask every serialized AST in the module.
1350-
(void)cast<NominalTypeDecl>(values.front())->getExtensions();
1351-
for (FileUnit *file : module->getFiles()) {
1347+
(void)baseType->getExtensions();
1348+
for (FileUnit *file : baseType->getModuleContext()->getFiles()) {
13521349
if (file == getFile())
13531350
continue;
1354-
auto *serializedFile = dyn_cast<SerializedASTFile>(file);
1355-
if (!serializedFile)
1356-
continue;
1357-
nestedType =
1358-
serializedFile->File.lookupNestedType(memberName,
1359-
values.front());
1351+
nestedType = file->lookupNestedType(memberName, baseType);
13601352
if (nestedType)
13611353
break;
13621354
}

lib/Serialization/ModuleFile.cpp

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

13651365
TypeDecl *ModuleFile::lookupNestedType(Identifier name,
1366-
const ValueDecl *parent) {
1366+
const NominalTypeDecl *parent) {
13671367
PrettyStackTraceModuleFile stackEntry(*this);
13681368

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