Skip to content

Commit d2b562a

Browse files
authored
Merge pull request #76756 from tshortli/member-import-visibility-cxx
SE-0444: Fix interactions with Cxx interop
2 parents 6066418 + b11bb1c commit d2b562a

File tree

14 files changed

+149
-18
lines changed

14 files changed

+149
-18
lines changed

include/swift/AST/ClangModuleLoader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class ClangModuleLoader : public ModuleLoader {
147147
virtual void dumpSwiftLookupTables() const = 0;
148148

149149
/// Returns the module that contains imports and declarations from all loaded
150-
/// Objective-C header files.
150+
/// header files.
151151
virtual ModuleDecl *getImportedHeaderModule() const = 0;
152152

153153
/// Retrieves the Swift wrapper for the given Clang module, creating

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -979,6 +979,12 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
979979
LLVM_READONLY
980980
ModuleDecl *getModuleContext() const;
981981

982+
/// Retrieve the module in which this declaration would be found by name
983+
/// lookup queries. The result can differ from that of `getModuleContext()`
984+
/// when the decl was imported via Cxx interop.
985+
LLVM_READONLY
986+
ModuleDecl *getModuleContextForNameLookup() const;
987+
982988
/// getASTContext - Return the ASTContext that this decl lives in.
983989
LLVM_READONLY
984990
ASTContext &getASTContext() const {

include/swift/AST/Module.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,9 @@ class ModuleDecl
614614
void findDeclaredCrossImportOverlaysTransitive(
615615
SmallVectorImpl<ModuleDecl *> &overlays);
616616

617+
/// Returns true if this module is the Clang header import module.
618+
bool isClangHeaderImportModule() const;
619+
617620
/// Convenience accessor for clients that know what kind of file they're
618621
/// dealing with.
619622
SourceFile &getMainSourceFile() const;

lib/AST/Decl.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,42 @@ ModuleDecl *Decl::getModuleContext() const {
770770
return getDeclContext()->getParentModule();
771771
}
772772

773+
/// If `decl` is an imported Cxx decl, returns the actual module that the decl
774+
/// was imported from. This is necessary to compensate for the way that
775+
/// Cxx `namespace` declarations are imported. Since namespaces can span
776+
/// multiple Clang modules, the corresponding decls and all of their members get
777+
/// associated with the Clang imported header module (named `__ObjC`). This
778+
/// utility reaches through the Clang AST to find the actual module that members
779+
/// of namespaces originated from.
780+
static ModuleDecl *getModuleContextForNameLookupForCxxDecl(const Decl *decl) {
781+
auto &ctx = decl->getASTContext();
782+
if (!ctx.LangOpts.EnableCXXInterop)
783+
return nullptr;
784+
785+
if (!decl->hasClangNode())
786+
return nullptr;
787+
788+
auto parentModule = decl->getModuleContext();
789+
790+
// We only need to look for the real parent module when the existing parent
791+
// is the imported header module.
792+
if (!parentModule->isClangHeaderImportModule())
793+
return nullptr;
794+
795+
auto clangModule = decl->getClangDecl()->getOwningModule();
796+
if (!clangModule)
797+
return nullptr;
798+
799+
return ctx.getClangModuleLoader()->getWrapperForModule(clangModule);
800+
}
801+
802+
ModuleDecl *Decl::getModuleContextForNameLookup() const {
803+
if (auto parentModule = getModuleContextForNameLookupForCxxDecl(this))
804+
return parentModule;
805+
806+
return getModuleContext();
807+
}
808+
773809
/// Retrieve the diagnostic engine for diagnostics emission.
774810
DiagnosticEngine &Decl::getDiags() const {
775811
return getASTContext().Diags;
@@ -3946,7 +3982,7 @@ ValueDecl::getSatisfiedProtocolRequirements(bool Sorted) const {
39463982
std::optional<AttributedImport<ImportedModule>>
39473983
ValueDecl::findImport(const DeclContext *fromDC) const {
39483984
// If the type is from the current module, there's no import.
3949-
auto module = getModuleContext();
3985+
auto module = getModuleContextForNameLookup();
39503986
if (module == fromDC->getParentModule())
39513987
return std::nullopt;
39523988

lib/AST/Module.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2471,6 +2471,14 @@ bool ModuleDecl::getRequiredBystandersIfCrossImportOverlay(
24712471
return false;
24722472
}
24732473

2474+
bool ModuleDecl::isClangHeaderImportModule() const {
2475+
auto importer = getASTContext().getClangModuleLoader();
2476+
if (!importer)
2477+
return false;
2478+
2479+
return this == importer->getImportedHeaderModule();
2480+
}
2481+
24742482
namespace {
24752483
struct OverlayFileContents {
24762484
struct Module {

lib/AST/NameLookup.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2504,7 +2504,7 @@ bool DeclContext::lookupQualified(Type type,
25042504
}
25052505

25062506
bool DeclContext::isDeclImported(const Decl *decl) const {
2507-
auto declModule = decl->getDeclContext()->getParentModule();
2507+
auto declModule = decl->getModuleContextForNameLookup();
25082508
return getASTContext().getImportCache().isImportedBy(declModule, this);
25092509
}
25102510

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,7 @@ writeImports(raw_ostream &out, llvm::SmallPtrSetImpl<ImportModuleTy> &imports,
404404
if (bridgingHeader.empty())
405405
return import != &M && import->getName() == M.getName();
406406

407-
auto importer = static_cast<ClangImporter *>(
408-
import->getASTContext().getClangModuleLoader());
409-
return import == importer->getImportedHeaderModule();
407+
return import->isClangHeaderImportModule();
410408
};
411409

412410
clang::FileSystemOptions fileSystemOptions;

lib/Sema/TypeCheckNameLookup.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ class MissingImportFixItCache {
809809
static void diagnoseMissingImportForMember(const ValueDecl *decl,
810810
SourceFile *sf, SourceLoc loc) {
811811
auto &ctx = sf->getASTContext();
812-
auto definingModule = decl->getModuleContext();
812+
auto definingModule = decl->getModuleContextForNameLookup();
813813
ctx.Diags.diagnose(loc, diag::candidate_from_missing_import,
814814
decl->getDescriptiveKind(), decl->getName(),
815815
definingModule);
@@ -823,7 +823,7 @@ diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
823823
diagnoseMissingImportForMember(decl, sf, loc);
824824

825825
auto &ctx = sf->getASTContext();
826-
auto definingModule = decl->getModuleContext();
826+
auto definingModule = decl->getModuleContextForNameLookup();
827827
SourceLoc bestLoc = ctx.Diags.getBestAddImportFixItLoc(decl, sf);
828828
if (!bestLoc.isValid())
829829
return;
@@ -874,9 +874,18 @@ diagnoseAndFixMissingImportForMember(const ValueDecl *decl, SourceFile *sf,
874874
bool swift::maybeDiagnoseMissingImportForMember(const ValueDecl *decl,
875875
const DeclContext *dc,
876876
SourceLoc loc) {
877-
if (decl->findImport(dc))
877+
if (dc->isDeclImported(decl))
878878
return false;
879879

880+
if (dc->getASTContext().LangOpts.EnableCXXInterop) {
881+
// With Cxx interop enabled, there are some declarations that always belong
882+
// to the Clang header import module which should always be implicitly
883+
// visible. However, that module is not implicitly imported in source files
884+
// so we need to special case it here and avoid diagnosing.
885+
if (decl->getModuleContextForNameLookup()->isClangHeaderImportModule())
886+
return false;
887+
}
888+
880889
auto sf = dc->getParentSourceFile();
881890
if (!sf)
882891
return false;

lib/Serialization/Serialization.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -744,11 +744,7 @@ IdentifierID Serializer::addContainingModuleRef(const DeclContext *DC,
744744
return CURRENT_MODULE_ID;
745745
if (M == this->M->getASTContext().TheBuiltinModule)
746746
return BUILTIN_MODULE_ID;
747-
748-
auto clangImporter =
749-
static_cast<ClangImporter *>(
750-
this->M->getASTContext().getClangModuleLoader());
751-
if (M == clangImporter->getImportedHeaderModule())
747+
if (M->isClangHeaderImportModule())
752748
return OBJC_HEADER_MODULE_ID;
753749

754750
auto exportedModuleName = file->getExportedModuleName();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_DIRECT_H
2+
#define TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_DIRECT_H
3+
4+
#include <members-transitive.h>
5+
6+
namespace DirectNamespace { }
7+
8+
namespace CommonNamespace {
9+
10+
struct DirectStruct {
11+
int memberVar;
12+
};
13+
14+
} // namespace CommonNamespace
15+
16+
CommonNamespace::TransitiveStruct returnsTransitiveStruct() {
17+
return CommonNamespace::TransitiveStruct{};
18+
}
19+
20+
TopLevelTransitiveStruct returnsTopLevelTransitiveStruct() {
21+
return TopLevelTransitiveStruct{};
22+
}
23+
24+
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_DIRECT_H
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_TRANSITIVE_H
2+
#define TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_TRANSITIVE_H
3+
4+
namespace TransitiveNamespace { }
5+
6+
namespace CommonNamespace {
7+
8+
struct TransitiveStruct {
9+
bool memberVar;
10+
};
11+
12+
} // namespace CommonNamespace
13+
14+
struct TopLevelTransitiveStruct {
15+
bool memberVar;
16+
};
17+
18+
#endif // TEST_INTEROP_CXX_CLASS_INPUTS_MEMBERS_TRANSITIVE_H

test/Interop/Cxx/namespace/Inputs/module.modulemap

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,13 @@ module Enums {
5959
header "enums.h"
6060
requires cplusplus
6161
}
62+
63+
module MembersDirect {
64+
header "members-direct.h"
65+
requires cplusplus
66+
}
67+
68+
module MembersTransitive {
69+
header "members-transitive.h"
70+
requires cplusplus
71+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop
2+
// RUN: %target-typecheck-verify-swift -I %S/Inputs -enable-experimental-cxx-interop -enable-upcoming-feature MemberImportVisibility -verify-additional-prefix member-visibility-
3+
4+
import MembersDirect
5+
6+
// expected-member-visibility-note@-1 4 {{add import of module 'MembersTransitive'}}
7+
8+
extension DirectNamespace { }
9+
extension TransitiveNamespace { } // expected-error {{cannot find type 'TransitiveNamespace' in scope}}
10+
11+
extension CommonNamespace.DirectStruct {
12+
func extensionMethod() -> Int32 {
13+
return memberVar
14+
}
15+
}
16+
17+
extension CommonNamespace.TransitiveStruct { // expected-member-visibility-error {{struct 'TransitiveStruct' is not available due to missing import of defining module 'MembersTransitive'}}
18+
func extensionMethod() -> Bool {
19+
return memberVar // expected-member-visibility-error {{property 'memberVar' is not available due to missing import of defining module 'MembersTransitive'}}
20+
}
21+
}
22+
23+
func test() {
24+
let _: Bool = returnsTransitiveStruct().memberVar // expected-member-visibility-error {{property 'memberVar' is not available due to missing import of defining module 'MembersTransitive'}}
25+
let _: Bool = returnsTopLevelTransitiveStruct().memberVar // expected-member-visibility-error {{property 'memberVar' is not available due to missing import of defining module 'MembersTransitive'}}
26+
}

tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,8 +1043,6 @@ static void collectModuleDependencies(ModuleDecl *TopMod,
10431043
if (!TopMod)
10441044
return;
10451045

1046-
auto ClangModuleLoader = TopMod->getASTContext().getClangModuleLoader();
1047-
10481046
ModuleDecl::ImportFilter ImportFilter = {
10491047
ModuleDecl::ImportFilterKind::Exported,
10501048
ModuleDecl::ImportFilterKind::Default};
@@ -1061,8 +1059,7 @@ static void collectModuleDependencies(ModuleDecl *TopMod,
10611059
if (Mod->isSystemModule())
10621060
continue;
10631061
// FIXME: Setup dependencies on the included headers.
1064-
if (ClangModuleLoader &&
1065-
Mod == ClangModuleLoader->getImportedHeaderModule())
1062+
if (Mod->isClangHeaderImportModule())
10661063
continue;
10671064
bool NewVisit = Visited.insert(Mod).second;
10681065
if (!NewVisit)

0 commit comments

Comments
 (0)