Skip to content

Commit d55cfd2

Browse files
authored
Merge pull request #61606 from NuriAmari/fwd-declarations
Import Forward Declared Objective-C Interfaces and Protocols
2 parents c95e11c + 86c5698 commit d55cfd2

File tree

53 files changed

+1615
-56
lines changed

Some content is hidden

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

53 files changed

+1615
-56
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ class ASTContext final {
356356
llvm::SmallPtrSet<DerivativeAttr *, 1>>
357357
DerivativeAttrs;
358358

359+
/// The Swift module currently being compiled.
360+
ModuleDecl *MainModule = nullptr;
361+
359362
private:
360363
/// The current generation number, which reflects the number of
361364
/// times that external modules have been loaded.

include/swift/AST/Decl.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
630630
HasAnyUnavailableValues : 1
631631
);
632632

633-
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1,
633+
SWIFT_INLINE_BITFIELD(ModuleDecl, TypeDecl, 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1,
634634
/// If the module is compiled as static library.
635635
StaticLibrary : 1,
636636

@@ -676,7 +676,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
676676

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

682686
SWIFT_INLINE_BITFIELD(PrecedenceGroupDecl, Decl, 1+2,

include/swift/AST/DiagnosticsClangImporter.def

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,27 @@ NOTE(invoked_func_not_imported, none, "function %0 unavailable (cannot import)",
249249
NOTE(record_method_not_imported, none, "method %0 unavailable (cannot import)", (const clang::NamedDecl*))
250250
NOTE(objc_property_not_imported, none, "property %0 unavailable (cannot import)", (const clang::NamedDecl*))
251251

252-
NOTE(forward_declared_interface_label, none, "interface %0 forward declared here", (const clang::NamedDecl*))
253-
NOTE(forward_declared_protocol_label, none, "protocol %0 forward declared here", (const clang::NamedDecl*))
252+
NOTE(placeholder_for_forward_declared_interface_member_access_failure, none,
253+
"class '%0' will be imported as an opaque placeholder class and may be "
254+
"missing members; import the definition to access the complete "
255+
"interface", (StringRef))
256+
NOTE(placeholder_for_forward_declared_protocol_member_access_failure, none,
257+
"protocol '%0' will be imported as an opaque placeholder protocol "
258+
"and may be missing members; import the definition to access the "
259+
"complete protocol", (StringRef))
260+
NOTE(forward_declared_interface_label, none,
261+
"interface %0 forward declared here", (const clang::NamedDecl*))
262+
NOTE(forward_declared_protocol_label, none,
263+
"protocol %0 forward declared here", (const clang::NamedDecl*))
264+
265+
NOTE(forward_declared_interface_clashes_with_imported_objc_Swift_interface, none,
266+
"interface %0 is incomplete and cannot be imported as a stub; "
267+
"its name conflicts with a %1 in module %2",
268+
(const clang::NamedDecl*, StringRef, StringRef))
269+
NOTE(forward_declared_protocol_clashes_with_imported_objc_Swift_protocol, none,
270+
"protocol %0 is incomplete and cannot be imported as a stub; "
271+
"its name conflicts with a %1 in module %2",
272+
(const clang::NamedDecl*, StringRef, StringRef))
254273

255274
#define UNDEFINE_DIAGNOSTIC_MACROS
256275
#include "DefineDiagnosticMacros.h"

include/swift/AST/Module.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,8 @@ class ModuleDecl
275275
llvm::SmallDenseMap<Identifier, SmallVector<OverlayFile *, 1>>
276276
declaredCrossImports;
277277

278+
llvm::DenseMap<Identifier, SmallVector<Decl *, 2>> ObjCNameLookupCache;
279+
278280
/// A description of what should be implicitly imported by each file of this
279281
/// module.
280282
const ImplicitImportInfo ImportInfo;
@@ -667,6 +669,14 @@ class ModuleDecl
667669
Bits.ModuleDecl.IsConcurrencyChecked = value;
668670
}
669671

672+
bool isObjCNameLookupCachePopulated() const {
673+
return Bits.ModuleDecl.ObjCNameLookupCachePopulated;
674+
}
675+
676+
void setIsObjCNameLookupCachePopulated(bool value) {
677+
Bits.ModuleDecl.ObjCNameLookupCachePopulated = value;
678+
}
679+
670680
/// For the main module, retrieves the list of primary source files being
671681
/// compiled, that is, the files we're generating code for.
672682
ArrayRef<SourceFile *> getPrimarySourceFiles() const;
@@ -704,6 +714,24 @@ class ModuleDecl
704714
VisibleDeclConsumer &Consumer,
705715
NLKind LookupKind) const;
706716

717+
private:
718+
void populateObjCNameLookupCache();
719+
720+
public:
721+
/// Finds top-levels decls of this module by @objc provided name.
722+
/// Decls that have no @objc attribute are not considered.
723+
///
724+
/// This does a simple local lookup, not recursively looking through imports.
725+
/// The order of the results is not guaranteed to be meaningful.
726+
///
727+
/// \param Results Vector collecting the decls.
728+
///
729+
/// \param name The @objc simple name to look for. Declarations with matching
730+
/// name and "anonymous" @objc attribute, as well a matching named @objc
731+
/// attribute will be added to Results.
732+
void lookupTopLevelDeclsByObjCName(SmallVectorImpl<Decl *> &Results,
733+
DeclName name);
734+
707735
/// This is a hack for 'main' file parsing and the integrated REPL.
708736
///
709737
/// FIXME: Refactor main file parsing to not pump the parser incrementally.

include/swift/Option/FrontendOptions.td

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

311+
def enable_import_objc_forward_declarations :
312+
Flag<["-"], "enable-import-objc-forward-declarations">,
313+
HelpText<"Attempt to import Objective-C forward declarations">;
314+
311315
def enable_experimental_pairwise_build_block :
312316
Flag<["-"], "enable-experimental-pairwise-build-block">,
313317
HelpText<"Enable experimental pairwise 'buildBlock' for result builders">;

lib/AST/Module.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ ModuleDecl::ModuleDecl(Identifier name, ASTContext &ctx,
610610
Bits.ModuleDecl.HasIncrementalInfo = 0;
611611
Bits.ModuleDecl.HasHermeticSealAtLink = 0;
612612
Bits.ModuleDecl.IsConcurrencyChecked = 0;
613+
Bits.ModuleDecl.ObjCNameLookupCachePopulated = 0;
613614
}
614615

615616
ImplicitImportList ModuleDecl::getImplicitImports() const {
@@ -1189,6 +1190,60 @@ void ModuleDecl::getTopLevelDeclsWhereAttributesMatch(
11891190
FORWARD(getTopLevelDeclsWhereAttributesMatch, (Results, matchAttributes));
11901191
}
11911192

1193+
void ModuleDecl::lookupTopLevelDeclsByObjCName(SmallVectorImpl<Decl *> &Results,
1194+
DeclName name) {
1195+
if (!isObjCNameLookupCachePopulated())
1196+
populateObjCNameLookupCache();
1197+
1198+
// A top level decl can't be special anyways
1199+
if (name.isSpecial())
1200+
return;
1201+
1202+
auto resultsForFileUnit = ObjCNameLookupCache.find(name.getBaseIdentifier());
1203+
if (resultsForFileUnit == ObjCNameLookupCache.end())
1204+
return;
1205+
1206+
Results.append(resultsForFileUnit->second.begin(),
1207+
resultsForFileUnit->second.end());
1208+
}
1209+
1210+
void ModuleDecl::populateObjCNameLookupCache() {
1211+
SmallVector<Decl *> topLevelObjCExposedDeclsInFileUnit;
1212+
auto hasObjCAttrNamePredicate = [](const DeclAttributes &attrs) -> bool {
1213+
return attrs.hasAttribute<ObjCAttr>();
1214+
};
1215+
1216+
for (FileUnit *file : getFiles()) {
1217+
file->getTopLevelDeclsWhereAttributesMatch(
1218+
topLevelObjCExposedDeclsInFileUnit, hasObjCAttrNamePredicate);
1219+
if (auto *synth = file->getSynthesizedFile()) {
1220+
synth->getTopLevelDeclsWhereAttributesMatch(
1221+
topLevelObjCExposedDeclsInFileUnit, hasObjCAttrNamePredicate);
1222+
}
1223+
}
1224+
1225+
for (Decl *decl : topLevelObjCExposedDeclsInFileUnit) {
1226+
if (ValueDecl *VD = dyn_cast<ValueDecl>(decl); VD && VD->hasName()) {
1227+
const auto &declObjCAttribute = VD->getAttrs().getAttribute<ObjCAttr>();
1228+
// No top level decl (class, protocol, extension etc.) is allowed to have a
1229+
// compound name, @objc provided or otherwise. Global functions are allowed to
1230+
// have compound names, but not allowed to have @objc attributes. Thus we
1231+
// are sure to not hit asserts getting the simple name.
1232+
//
1233+
// Similarly, init, dealloc and subscript (the special names) can't be top
1234+
// level decls, so we won't hit asserts getting the base identifier out of the
1235+
// value decl.
1236+
const Identifier &declObjCName =
1237+
declObjCAttribute->hasName()
1238+
? declObjCAttribute->getName()->getSimpleName()
1239+
: VD->getName().getBaseIdentifier();
1240+
ObjCNameLookupCache[declObjCName].push_back(decl);
1241+
}
1242+
}
1243+
1244+
setIsObjCNameLookupCachePopulated(true);
1245+
}
1246+
11921247
void SourceFile::getTopLevelDecls(SmallVectorImpl<Decl*> &Results) const {
11931248
auto decls = getTopLevelDecls();
11941249
Results.append(decls.begin(), decls.end());
@@ -3360,6 +3415,9 @@ bool SourceFile::shouldCrossImport() const {
33603415
void ModuleDecl::clearLookupCache() {
33613416
getASTContext().getImportCache().clear();
33623417

3418+
setIsObjCNameLookupCachePopulated(false);
3419+
ObjCNameLookupCache.clear();
3420+
33633421
if (!Cache)
33643422
return;
33653423

lib/ClangImporter/ClangImporter.cpp

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6243,7 +6243,15 @@ void ClangImporter::diagnoseTopLevelValue(const DeclName &name) {
62436243

62446244
void ClangImporter::diagnoseMemberValue(const DeclName &name,
62456245
const Type &baseType) {
6246-
if (!baseType->getAnyNominal())
6246+
6247+
// Return early for any type that namelookup::extractDirectlyReferencedNominalTypes
6248+
// does not know how to handle.
6249+
if (!(baseType->getAnyNominal() ||
6250+
baseType->is<ExistentialType>() ||
6251+
baseType->is<UnboundGenericType>() ||
6252+
baseType->is<ArchetypeType>() ||
6253+
baseType->is<ProtocolCompositionType>() ||
6254+
baseType->is<TupleType>()))
62476255
return;
62486256

62496257
SmallVector<NominalTypeDecl *, 4> nominalTypesToLookInto;
@@ -6255,6 +6263,41 @@ void ClangImporter::diagnoseMemberValue(const DeclName &name,
62556263
Impl.diagnoseMemberValue(name,
62566264
cast<clang::DeclContext>(clangContainerDecl));
62576265
}
6266+
6267+
if (Impl.ImportForwardDeclarations) {
6268+
const clang::Decl *clangContainerDecl = containerDecl->getClangDecl();
6269+
if (const clang::ObjCInterfaceDecl *objCInterfaceDecl =
6270+
llvm::dyn_cast_or_null<clang::ObjCInterfaceDecl>(
6271+
clangContainerDecl); objCInterfaceDecl && !objCInterfaceDecl->hasDefinition()) {
6272+
// Emit a diagnostic about how the base type represents a forward
6273+
// declared ObjC interface and is in all likelihood missing members.
6274+
// We only attach this diagnostic in diagnoseMemberValue rather than
6275+
// in SwiftDeclConverter because it is only relevant when the user
6276+
// tries to access an unavailable member.
6277+
Impl.addImportDiagnostic(
6278+
objCInterfaceDecl,
6279+
Diagnostic(
6280+
diag::
6281+
placeholder_for_forward_declared_interface_member_access_failure,
6282+
objCInterfaceDecl->getName()),
6283+
objCInterfaceDecl->getSourceRange().getBegin());
6284+
// Emit any diagnostics attached to the source Clang node (ie. forward
6285+
// declaration here note)
6286+
Impl.diagnoseTargetDirectly(clangContainerDecl);
6287+
} else if (const clang::ObjCProtocolDecl *objCProtocolDecl =
6288+
llvm::dyn_cast_or_null<clang::ObjCProtocolDecl>(
6289+
clangContainerDecl); objCProtocolDecl && !objCProtocolDecl->hasDefinition()) {
6290+
// Same as above but for protocols
6291+
Impl.addImportDiagnostic(
6292+
objCProtocolDecl,
6293+
Diagnostic(
6294+
diag::
6295+
placeholder_for_forward_declared_protocol_member_access_failure,
6296+
objCProtocolDecl->getName()),
6297+
objCProtocolDecl->getSourceRange().getBegin());
6298+
Impl.diagnoseTargetDirectly(clangContainerDecl);
6299+
}
6300+
}
62586301
}
62596302
}
62606303

0 commit comments

Comments
 (0)