Skip to content

Commit 3ca50b6

Browse files
committed
[APINotes] Add support for handling Clang modules carrying all versions of APINotes
Controlled from Swift with '-version-independent-apinotes', which, for the underlying Clang invocation enables '-fswift-version-independent-apinotes', results in PCMs which aggregate all versioned APINotes wrapped in a 'SwiftVersionedAttr', with the intent to have the client pick and apply only those that match its current Swift version, discarding the rest.
1 parent bf6694d commit 3ca50b6

File tree

7 files changed

+308
-4
lines changed

7 files changed

+308
-4
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,11 @@ namespace swift {
11081108
/// invocations directly from clang cc1 args.
11091109
bool ClangImporterDirectCC1Scan = false;
11101110

1111+
/// Whether the importer should expect all APINotes to be wrapped
1112+
/// in versioned attributes, where the importer must select the appropriate
1113+
/// ones to apply.
1114+
bool LoadVersionIndependentAPINotes = false;
1115+
11111116
/// Return a hash code of any components from these options that should
11121117
/// contribute to a Swift Bridging PCH hash.
11131118
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,9 @@ def emit_pch : Flag<["-"], "emit-pch">,
669669
def pch_disable_validation : Flag<["-"], "pch-disable-validation">,
670670
HelpText<"Disable validating the persistent PCH">;
671671

672+
def version_independent_apinotes : Flag<["-"], "version-independent-apinotes">,
673+
HelpText<"Input clang modules carry all versioned APINotes">;
674+
672675
def disable_sil_ownership_verifier : Flag<["-"], "disable-sil-ownership-verifier">,
673676
HelpText<"Do not verify ownership invariants during SIL Verification ">;
674677

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,10 @@ void importer::getNormalInvocationArguments(
781781
invocationArgStrs.push_back("-iapinotes-modules");
782782
invocationArgStrs.push_back(path.str().str());
783783
}
784+
785+
if (importerOpts.LoadVersionIndependentAPINotes)
786+
invocationArgStrs.insert(invocationArgStrs.end(),
787+
{"-fswift-version-independent-apinotes"});
784788
}
785789

786790
static void

lib/ClangImporter/ImportDecl.cpp

Lines changed: 165 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "clang/AST/Type.h"
6969
#include "clang/Basic/Specifiers.h"
7070
#include "clang/Basic/TargetInfo.h"
71+
#include "clang/Sema/SemaDiagnostic.h"
7172
#include "clang/Lex/Preprocessor.h"
7273
#include "clang/Sema/Lookup.h"
7374

@@ -172,6 +173,9 @@ bool importer::recordHasReferenceSemantics(
172173
!importerImpl->SwiftContext.LangOpts.CForeignReferenceTypes)
173174
return false;
174175

176+
// ACTODO: Check versioned attrs in case of
177+
// captureSwiftVersionIndependentAPINotes
178+
175179
// At this point decl might not be fully imported into Swift yet, which
176180
// means we might not have asked Clang to generate its implicit members, such
177181
// as copy or move constructors. This would cause CxxRecordSemanticsRequest to
@@ -9339,6 +9343,67 @@ static bool isUsingMacroName(clang::SourceManager &SM,
93399343
return content == MacroName;
93409344
}
93419345

9346+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
9347+
llvm::VersionTuple currentVersion,
9348+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
9349+
// Scan through Swift-Versioned clang attributes and select which one to apply
9350+
// if multiple candidates exist.
9351+
SmallVector<clang::SwiftVersionedAttr*, 4> swiftVersionedAttributes;
9352+
for (auto attr : clangDecl->attrs())
9353+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr))
9354+
swiftVersionedAttributes.push_back(versionedAttr);
9355+
9356+
// An attribute version is valid to apply if it is greater than the current version
9357+
// or is unversioned
9358+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
9359+
return attrVersion.empty() || attrVersion >= currentVersion;
9360+
};
9361+
9362+
// We have a better attribute option if there exists another versioned attr
9363+
// wrapper for this attribute kind with a valid version that is lower than the
9364+
// one of the attribute we are considering
9365+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
9366+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
9367+
for (auto VAI = swiftVersionedAttributes.begin(),
9368+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
9369+
auto otherVersionedAttr = *VAI;
9370+
auto otherAttrKind = otherVersionedAttr->getAttrToAdd()->getKind();
9371+
auto otherAttrVersion = otherVersionedAttr->getVersion();
9372+
// Same exact attribute, ignore
9373+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
9374+
continue;
9375+
9376+
// For a matching attribute kind, an un-versioned attribute
9377+
// never takes precedence over an exsiting valid versioned one.
9378+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
9379+
continue;
9380+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
9381+
return true;
9382+
9383+
// For two versioned attributes of the same kind, the one with the lower applicable
9384+
// version takes precedence.
9385+
if (otherAttrKind == attrKind &&
9386+
applicableVersion(otherAttrVersion) &&
9387+
otherAttrVersion < attrVersion)
9388+
return true;
9389+
}
9390+
return false;
9391+
};
9392+
9393+
for (auto VAI = swiftVersionedAttributes.begin(),
9394+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
9395+
auto versionedAttr = *VAI;
9396+
auto attrKind = versionedAttr->getAttrToAdd()->getKind();
9397+
auto attrVersion = versionedAttr->getVersion();
9398+
if (!applicableVersion(attrVersion))
9399+
discardVersionedAttrSet.insert(versionedAttr);
9400+
else if (haveBetterAttr(attrVersion, attrKind))
9401+
discardVersionedAttrSet.insert(versionedAttr);
9402+
else
9403+
continue;
9404+
}
9405+
}
9406+
93429407
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
93439408
{
93449409
// sourceDecl->getClangDecl() can be null because some lazily instantiated cases like C++ members that were instantiated from using-shadow-decls have no corresponding Clang decl.
@@ -9372,17 +9437,39 @@ void ClangImporter::Implementation::importAttributes(
93729437
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
93739438
isAsync = func->hasAsync();
93749439

9440+
// Determine which versioned attributes are applicable
9441+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
9442+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9443+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
9444+
discardVersionedAttrSet);
9445+
93759446
// Scan through Clang attributes and map them onto Swift
93769447
// equivalents.
93779448
bool AnyUnavailable = MappedDecl->isUnavailable();
93789449
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
93799450
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
9451+
clang::Attr *consideringAttr = *AI;
9452+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
9453+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(consideringAttr)) {
9454+
// "type" and "nullability" attributes are handled earlier since they change
9455+
// the decl's type.
9456+
if (isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) ||
9457+
isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd()))
9458+
continue;
9459+
9460+
if (discardVersionedAttrSet.count(versionedAttr))
9461+
continue;
9462+
9463+
consideringAttr = versionedAttr->getAttrToAdd();
9464+
}
9465+
}
9466+
93809467
//
93819468
// __attribute__((unavailable))
93829469
//
93839470
// Mapping: @available(*,unavailable)
93849471
//
9385-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
9472+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
93869473
auto Message = unavailable->getMessage();
93879474
auto attr = AvailableAttr::createUniversallyUnavailable(C, Message);
93889475
MappedDecl->getAttrs().add(attr);
@@ -9395,7 +9482,7 @@ void ClangImporter::Implementation::importAttributes(
93959482
//
93969483
// Mapping: @available(*, unavailable)
93979484
//
9398-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
9485+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
93999486
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
94009487
auto attr = AvailableAttr::createUnavailableInSwift(C, "", "");
94019488
MappedDecl->getAttrs().add(attr);
@@ -9408,7 +9495,7 @@ void ClangImporter::Implementation::importAttributes(
94089495
//
94099496
// Mapping: @available(*,deprecated)
94109497
//
9411-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
9498+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
94129499
auto Message = deprecated->getMessage();
94139500
auto attr = AvailableAttr::createUniversallyDeprecated(C, Message, "");
94149501
MappedDecl->getAttrs().add(attr);
@@ -9417,7 +9504,7 @@ void ClangImporter::Implementation::importAttributes(
94179504

94189505
// __attribute__((availability))
94199506
//
9420-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
9507+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
94219508
StringRef Platform = avail->getPlatform()->getName();
94229509

94239510
// Is this our special "availability(swift, unavailable)" attribute?
@@ -9649,6 +9736,62 @@ void ClangImporter::Implementation::importAttributes(
96499736
}
96509737
}
96519738

9739+
static void applyTypeAndNullabilityAPINotes(const clang::NamedDecl *ClangDecl,
9740+
clang::Sema &Sema,
9741+
const ImportNameVersion CurrentImporterVersion) {
9742+
// Determine which versioned attributes are applicable
9743+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
9744+
filterUsableVersionedAttrs(ClangDecl,
9745+
CurrentImporterVersion.asClangVersionTuple(),
9746+
discardVersionedAttrSet);
9747+
9748+
// When importing from a module built with version-independent APINotes payload,
9749+
// the decl will carry all possible versioned notes, without directly applying any
9750+
// of them. For "type" and "nullability" notes, we must apply them first, here,
9751+
// since they change the actual type of the decl as seen downstream.
9752+
//
9753+
// Other kinds of notes will be handled in `importAttributes`.
9754+
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
9755+
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
9756+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(*AI)) {
9757+
if (!isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) &&
9758+
!isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
9759+
continue;
9760+
}
9761+
9762+
if (discardVersionedAttrSet.count(versionedAttr))
9763+
continue;
9764+
9765+
// Apply Type APINotes
9766+
if (auto typeRenameAttr = dyn_cast<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd())) {
9767+
Sema.ApplyAPINotesType(const_cast<clang::NamedDecl*>(ClangDecl),
9768+
typeRenameAttr->getTypeString());
9769+
}
9770+
9771+
// Apply Nullability APINotes
9772+
if (auto nullabilityAttr = dyn_cast<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
9773+
clang::NullabilityKind nullability;
9774+
switch (nullabilityAttr->getKind()) {
9775+
case clang::SwiftNullabilityAttr::Kind::NonNull:
9776+
nullability = clang::NullabilityKind::NonNull;
9777+
break;
9778+
case clang::SwiftNullabilityAttr::Kind::Nullable:
9779+
nullability = clang::NullabilityKind::Nullable;
9780+
break;
9781+
case clang::SwiftNullabilityAttr::Kind::Unspecified:
9782+
nullability = clang::NullabilityKind::Unspecified;
9783+
break;
9784+
case clang::SwiftNullabilityAttr::Kind::NullableResult:
9785+
nullability = clang::NullabilityKind::NullableResult;
9786+
break;
9787+
}
9788+
9789+
Sema.ApplyNullability(const_cast<clang::NamedDecl*>(ClangDecl), nullability);
9790+
}
9791+
}
9792+
}
9793+
}
9794+
96529795
Decl *
96539796
ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
96549797
ImportNameVersion version,
@@ -9660,6 +9803,24 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
96609803
if (ClangDecl->isInvalidDecl())
96619804
return nullptr;
96629805

9806+
// Private and protected C++ class members should never be used from Swift,
9807+
// however, parts of the Swift typechecker rely on being able to iterate over
9808+
// all of the stored fields of a particular struct. This means we still need
9809+
// to add private fields to the Swift AST.
9810+
//
9811+
// Other kinds of private and protected C++ decls are not relevant for Swift.
9812+
clang::AccessSpecifier access = ClangDecl->getAccess();
9813+
if ((access == clang::AS_protected || access == clang::AS_private) &&
9814+
!isa<clang::FieldDecl>(ClangDecl))
9815+
return nullptr;
9816+
9817+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
9818+
// notes are applied by the client (Importer) instead of the producer of the
9819+
// Clang module we are consuming. Do so now, early, since these notes
9820+
// affect the decl's type.
9821+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9822+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
9823+
96639824
bool SkippedOverTypedef = false;
96649825
Decl *Result = nullptr;
96659826
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,6 +2149,8 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, ArgList &Args,
21492149
Opts.PCHDisableValidation |= Args.hasArg(OPT_pch_disable_validation);
21502150
}
21512151

2152+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
2153+
21522154
if (FrontendOpts.DisableImplicitModules)
21532155
Opts.DisableImplicitClangModules = true;
21542156

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,6 +2018,12 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
20182018
GenericArgs.push_back(blocklist);
20192019
}
20202020

2021+
// Inherit APINotes processing method
2022+
if (clangImporterOpts.LoadVersionIndependentAPINotes) {
2023+
GenericArgs.push_back("-version-independent-apinotes");
2024+
genericSubInvocation.getClangImporterOptions().LoadVersionIndependentAPINotes = true;
2025+
}
2026+
20212027
// Inherit the C++ interoperability mode.
20222028
if (langOpts.EnableCXXInterop) {
20232029
// Modelled after a reverse of validateCxxInteropCompatibilityMode

0 commit comments

Comments
 (0)