Skip to content

Commit bd42e7f

Browse files
committed
Implement importing of forward declared objc protocols and interfaces
This modifies the ClangImporter to introduce an opaque placeholder representation for forward declared Objective-C interfaces and protocols when imported into Swift. In the compiler, the new functionality is hidden behind a frontend flag -enable-import-objc-forward-declarations, and is on by default for language mode >6. The feature is disabled entirely in LLDB expression evaluation / Swift REPL, regardless of language version.
1 parent e032b31 commit bd42e7f

File tree

50 files changed

+1259
-89
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1259
-89
lines changed

include/swift/AST/Decl.h

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -629,54 +629,61 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
629629
HasAnyUnavailableValues : 1
630630
);
631631

632-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1,
633-
/// If the module is compiled as static library.
634-
StaticLibrary : 1,
632+
SWIFT_INLINE_BITFIELD(
633+
ModuleDecl, TypeDecl,
634+
1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1,
635+
/// If the module is compiled as static library.
636+
StaticLibrary : 1,
635637

636-
/// If the module was or is being compiled with `-enable-testing`.
637-
TestingEnabled : 1,
638+
/// If the module was or is being compiled with `-enable-testing`.
639+
TestingEnabled : 1,
638640

639-
/// If the module failed to load
640-
FailedToLoad : 1,
641+
/// If the module failed to load
642+
FailedToLoad : 1,
641643

642-
/// Whether the module is resilient.
643-
///
644-
/// \sa ResilienceStrategy
645-
RawResilienceStrategy : 1,
644+
/// Whether the module is resilient.
645+
///
646+
/// \sa ResilienceStrategy
647+
RawResilienceStrategy : 1,
646648

647-
/// Whether the module was rebuilt from a module interface instead of being
648-
/// build from the full source.
649-
IsBuiltFromInterface : 1,
649+
/// Whether the module was rebuilt from a module interface instead of
650+
/// being build from the full source.
651+
IsBuiltFromInterface : 1,
650652

651-
/// Whether all imports have been resolved. Used to detect circular imports.
652-
HasResolvedImports : 1,
653+
/// Whether all imports have been resolved. Used to detect circular
654+
/// imports.
655+
HasResolvedImports : 1,
653656

654-
/// If the module was or is being compiled with `-enable-private-imports`.
655-
PrivateImportsEnabled : 1,
657+
/// If the module was or is being compiled with `-enable-private-imports`.
658+
PrivateImportsEnabled : 1,
656659

657-
/// If the module is compiled with `-enable-implicit-dynamic`.
658-
ImplicitDynamicEnabled : 1,
660+
/// If the module is compiled with `-enable-implicit-dynamic`.
661+
ImplicitDynamicEnabled : 1,
659662

660-
/// Whether the module is a system module.
661-
IsSystemModule : 1,
663+
/// Whether the module is a system module.
664+
IsSystemModule : 1,
662665

663-
/// Whether the module was imported from Clang (or, someday, maybe another
664-
/// language).
665-
IsNonSwiftModule : 1,
666+
/// Whether the module was imported from Clang (or, someday, maybe another
667+
/// language).
668+
IsNonSwiftModule : 1,
666669

667-
/// Whether this module is the main module.
668-
IsMainModule : 1,
670+
/// Whether this module is the main module.
671+
IsMainModule : 1,
669672

670-
/// Whether this module has incremental dependency information available.
671-
HasIncrementalInfo : 1,
673+
/// Whether this module has incremental dependency information available.
674+
HasIncrementalInfo : 1,
672675

673-
/// Whether this module was built with -experimental-hermetic-seal-at-link.
674-
HasHermeticSealAtLink : 1,
676+
/// Whether this module was built with
677+
/// -experimental-hermetic-seal-at-link.
678+
HasHermeticSealAtLink : 1,
675679

676-
/// Whether this module has been compiled with comprehensive checking for
677-
/// concurrency, e.g., Sendable checking.
678-
IsConcurrencyChecked : 1
679-
);
680+
/// Whether this module has been compiled with comprehensive checking for
681+
/// concurrency, e.g., Sendable checking.
682+
IsConcurrencyChecked : 1,
683+
684+
// If the map from @objc provided name to top level swift::Decl in this
685+
// module is populated
686+
ObjCNameLookupCachePopulated : 1);
680687

681688
SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,
682689
/// Is this an assignment operator?

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ NOTE(invoked_func_not_imported, none, "function %0 not imported", (const clang::
215215
NOTE(record_method_not_imported, none, "method %0 not imported", (const clang::NamedDecl*))
216216
NOTE(objc_property_not_imported, none, "property %0 not imported", (const clang::NamedDecl*))
217217

218+
NOTE(placeholder_for_forward_declared_interface_member_access_failure, none, "class '%0' will be imported as an opaque placeholder class and may be missing members; import the definition to access the complete interface", (StringRef))
219+
NOTE(placeholder_for_forward_declared_protocol_member_access_failure, none, "protocol '%0' will be imported as an opaque placeholder protocol and may be missing members; import the definition to access the complete protocol", (StringRef))
218220
NOTE(forward_declared_interface_label, none, "interface %0 forward declared here", (const clang::NamedDecl*))
219221
NOTE(forward_declared_protocol_label, none, "protocol %0 forward declared here", (const clang::NamedDecl*))
220222

include/swift/AST/Module.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ class ModuleDecl
242242
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>
243243
declaredCrossImports;
244244

245+
llvm::DenseMap<Identifier, SmallVector<Decl *, 2>> ObjCNameLookupCache;
246+
245247
/// A description of what should be implicitly imported by each file of this
246248
/// module.
247249
const ImplicitImportInfo ImportInfo;
@@ -619,6 +621,14 @@ class ModuleDecl
619621
Bits.ModuleDecl.IsConcurrencyChecked = value;
620622
}
621623

624+
bool isObjCNameLookupCachePopulated() const {
625+
return Bits.ModuleDecl.ObjCNameLookupCachePopulated;
626+
}
627+
628+
void setIsObjCNameLookupCachePopulated(bool value) {
629+
Bits.ModuleDecl.ObjCNameLookupCachePopulated = value;
630+
}
631+
622632
/// For the main module, retrieves the list of primary source files being
623633
/// compiled, that is, the files we're generating code for.
624634
ArrayRef<SourceFile *> getPrimarySourceFiles() const;
@@ -640,6 +650,12 @@ class ModuleDecl
640650
void lookupValue(DeclName Name, NLKind LookupKind,
641651
SmallVectorImpl<ValueDecl*> &Result) const;
642652

653+
/// Look up a value just as ModuleDecl::lookupValue does, but provide the
654+
/// Swift Module or SourceFile context from which the lookup is made.
655+
void lookupValueWithContext(DeclName Name, NLKind LookupKind,
656+
SmallVectorImpl<ValueDecl *> &Result,
657+
const DeclContext *ImportingModuleOrFile) const;
658+
643659
/// Look up a local type declaration by its mangled name.
644660
///
645661
/// This does a simple local lookup, not recursively looking through imports.
@@ -656,6 +672,24 @@ class ModuleDecl
656672
VisibleDeclConsumer &Consumer,
657673
NLKind LookupKind) const;
658674

675+
private:
676+
void populateObjCNameLookupCache();
677+
678+
public:
679+
/// Finds top-levels decls of this module by @objc provided name.
680+
/// Decls that have no @objc attribute are not considered.
681+
///
682+
/// This does a simple local lookup, not recursively looking through imports.
683+
/// The order of the resutls is not guaranteed to be meaningful.
684+
///
685+
/// \param Results Vector collecting the decls.
686+
///
687+
/// \param name The @objc simple name to look for. Declarations with matching
688+
/// name and "anonymous" @objc attribute, as well a matching named @objc
689+
/// attribute will be added to Results.
690+
void lookupTopLevelDeclsByObjCName(SmallVectorImpl<Decl *> &Results,
691+
DeclName name);
692+
659693
/// This is a hack for 'main' file parsing and the integrated REPL.
660694
///
661695
/// FIXME: Refactor main file parsing to not pump the parser incrementally.

include/swift/ClangImporter/ClangModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class ClangModuleUnit final : public LoadedFile {
6868
virtual void lookupValue(DeclName name, NLKind lookupKind,
6969
SmallVectorImpl<ValueDecl*> &results) const override;
7070

71+
void lookupValueWithContext(DeclName name, NLKind lookupKind,
72+
SmallVectorImpl<ValueDecl *> &results,
73+
const DeclContext *importingModuleOrFile) const;
74+
7175
virtual TypeDecl *
7276
lookupNestedType(Identifier name,
7377
const NominalTypeDecl *baseType) const override;

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ def enable_experimental_eager_clang_module_diagnostics :
304304
Flag<["-"], "enable-experimental-eager-clang-module-diagnostics">,
305305
HelpText<"Enable experimental eager diagnostics reporting on the importability of all referenced C, C++, and Objective-C libraries">;
306306

307+
def enable_import_objc_forward_declarations :
308+
Flag<["-"], "enable-import-objc-forward-declarations">,
309+
HelpText<"Attempt to import Objective-C forward declarations">;
310+
307311
def enable_experimental_pairwise_build_block :
308312
Flag<["-"], "enable-experimental-pairwise-build-block">,
309313
HelpText<"Enable experimental pairwise 'buildBlock' for result builders">;

lib/AST/Module.cpp

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "swift/Basic/Compiler.h"
4444
#include "swift/Basic/SourceManager.h"
4545
#include "swift/Basic/Statistic.h"
46+
#include "swift/ClangImporter/ClangModule.h"
4647
#include "swift/Demangling/ManglingMacros.h"
4748
#include "swift/Parse/Token.h"
4849
#include "swift/Strings.h"
@@ -57,8 +58,8 @@
5758
#include "llvm/Support/MemoryBuffer.h"
5859
#include "llvm/Support/Path.h"
5960
#include "llvm/Support/SaveAndRestore.h"
60-
#include "llvm/Support/raw_ostream.h"
6161
#include "llvm/Support/YAMLTraits.h"
62+
#include "llvm/Support/raw_ostream.h"
6263

6364
using namespace swift;
6465

@@ -737,6 +738,30 @@ void ModuleDecl::lookupValue(DeclName Name, NLKind LookupKind,
737738
FORWARD(lookupValue, (Name, LookupKind, Result));
738739
}
739740

741+
void ModuleDecl::lookupValueWithContext(
742+
DeclName Name, NLKind LookupKind, SmallVectorImpl<ValueDecl *> &Result,
743+
const DeclContext *ImportingModuleOrFile) const {
744+
if (isParsedModule(this)) {
745+
getSourceLookupCache().lookupValue(Name, LookupKind, Result);
746+
return;
747+
}
748+
749+
for (const FileUnit *file : getFiles()) {
750+
// At the time of writing, Clang module units are the only FileUnits
751+
// that make use of the Context. If this changes, add appropriate calls
752+
// below.
753+
if (auto clangModuleUnit = dyn_cast<ClangModuleUnit>(file)) {
754+
clangModuleUnit->lookupValueWithContext(Name, LookupKind, Result,
755+
ImportingModuleOrFile);
756+
} else {
757+
file->lookupValue(Name, LookupKind, Result);
758+
}
759+
if (auto *synth = file->getSynthesizedFile()) {
760+
synth->lookupValue(Name, LookupKind, Result);
761+
}
762+
}
763+
}
764+
740765
TypeDecl * ModuleDecl::lookupLocalType(StringRef MangledName) const {
741766
for (auto file : getFiles()) {
742767
auto TD = file->lookupLocalType(MangledName);
@@ -1024,6 +1049,53 @@ void ModuleDecl::getTopLevelDeclsWhereAttributesMatch(
10241049
FORWARD(getTopLevelDeclsWhereAttributesMatch, (Results, matchAttributes));
10251050
}
10261051

1052+
void ModuleDecl::lookupTopLevelDeclsByObjCName(SmallVectorImpl<Decl *> &Results,
1053+
DeclName name) {
1054+
if (!isObjCNameLookupCachePopulated())
1055+
populateObjCNameLookupCache();
1056+
1057+
if (name.isSpecial())
1058+
return;
1059+
1060+
auto resultsForFileUnit = ObjCNameLookupCache.find(name.getBaseIdentifier());
1061+
if (resultsForFileUnit == ObjCNameLookupCache.end())
1062+
return;
1063+
1064+
Results.append(resultsForFileUnit->second.begin(),
1065+
resultsForFileUnit->second.end());
1066+
}
1067+
1068+
void ModuleDecl::populateObjCNameLookupCache() {
1069+
SmallVector<Decl *> topLevelObjCExposedDeclsInFileUnit;
1070+
auto hasObjCAttrNamePredicate = [](const DeclAttributes &attrs) -> bool {
1071+
return attrs.hasAttribute<ObjCAttr>();
1072+
};
1073+
1074+
for (FileUnit *file : getFiles()) {
1075+
file->getTopLevelDeclsWhereAttributesMatch(
1076+
topLevelObjCExposedDeclsInFileUnit, hasObjCAttrNamePredicate);
1077+
if (auto *synth = file->getSynthesizedFile()) {
1078+
synth->getTopLevelDeclsWhereAttributesMatch(
1079+
topLevelObjCExposedDeclsInFileUnit, hasObjCAttrNamePredicate);
1080+
}
1081+
}
1082+
1083+
for (Decl *decl : topLevelObjCExposedDeclsInFileUnit) {
1084+
if (ValueDecl *VD = dyn_cast<ValueDecl>(decl)) {
1085+
if (VD->hasName()) {
1086+
const auto &declObjCAttribute = VD->getAttrs().getAttribute<ObjCAttr>();
1087+
const Identifier &declObjCName =
1088+
declObjCAttribute->hasName()
1089+
? declObjCAttribute->getName()->getSimpleName()
1090+
: VD->getName().getBaseIdentifier();
1091+
ObjCNameLookupCache[declObjCName].push_back(decl);
1092+
}
1093+
}
1094+
}
1095+
1096+
setIsObjCNameLookupCachePopulated(true);
1097+
}
1098+
10271099
void SourceFile::getTopLevelDecls(SmallVectorImpl<Decl*> &Results) const {
10281100
auto decls = getTopLevelDecls();
10291101
Results.append(decls.begin(), decls.end());

lib/AST/ModuleNameLookup.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,14 @@ class LookupByName : public ModuleNameLookup<LookupByName> {
8181
}
8282

8383
void doLocalLookup(ModuleDecl *module, ImportPath::Access path,
84-
SmallVectorImpl<ValueDecl *> &localDecls) {
84+
SmallVectorImpl<ValueDecl *> &localDecls,
85+
const DeclContext *importingModuleOrFile) {
8586
// If this import is specific to some named decl ("import Swift.Int")
8687
// then filter out any lookups that don't match.
8788
if (!path.matches(name))
8889
return;
89-
module->lookupValue(name, lookupKind, localDecls);
90+
module->lookupValueWithContext(name, lookupKind, localDecls,
91+
importingModuleOrFile);
9092
}
9193
};
9294

@@ -112,7 +114,8 @@ class LookupVisibleDecls : public ModuleNameLookup<LookupVisibleDecls> {
112114
}
113115

114116
void doLocalLookup(ModuleDecl *module, ImportPath::Access path,
115-
SmallVectorImpl<ValueDecl *> &localDecls) {
117+
SmallVectorImpl<ValueDecl *> &localDecls,
118+
const DeclContext *importingModuleOrFile) {
116119
VectorDeclConsumer consumer(localDecls);
117120
module->lookupVisibleDecls(path, consumer, lookupKind);
118121
}
@@ -169,7 +172,7 @@ void ModuleNameLookup<LookupStrategy>::lookupInModule(
169172

170173
// Do the lookup into the current module.
171174
auto *module = moduleOrFile->getParentModule();
172-
getDerived()->doLocalLookup(module, accessPath, decls);
175+
getDerived()->doLocalLookup(module, accessPath, decls, moduleOrFile);
173176
updateNewDecls(moduleScopeContext);
174177

175178
bool canReturnEarly = (initialCount != decls.size() &&
@@ -198,7 +201,7 @@ void ModuleNameLookup<LookupStrategy>::lookupInModule(
198201
return;
199202

200203
getDerived()->doLocalLookup(import.importedModule, import.accessPath,
201-
decls);
204+
decls, moduleOrFile);
202205
updateNewDecls(moduleScopeContext);
203206
};
204207

0 commit comments

Comments
 (0)