Skip to content

Commit c68a6a0

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 132726c commit c68a6a0

File tree

7 files changed

+296
-5
lines changed

7 files changed

+296
-5
lines changed

include/swift/Basic/LangOptions.h

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

1042+
/// Whether the importer should expect all APINotes to be wrapped
1043+
/// in versioned attributes, where the importer must select the appropriate
1044+
/// ones to apply.
1045+
bool LoadVersionIndependentAPINotes = false;
1046+
10421047
/// Return a hash code of any components from these options that should
10431048
/// contribute to a Swift Bridging PCH hash.
10441049
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

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

658+
def version_independent_apinotes : Flag<["-"], "version-independent-apinotes">,
659+
HelpText<"Input clang modules carry all versioned APINotes">;
660+
658661
def disable_sil_ownership_verifier : Flag<["-"], "disable-sil-ownership-verifier">,
659662
HelpText<"Do not verify ownership invariants during SIL Verification ">;
660663

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,10 @@ void importer::getNormalInvocationArguments(
774774
invocationArgStrs.push_back("-iapinotes-modules");
775775
invocationArgStrs.push_back(path.str().str());
776776
}
777+
778+
if (importerOpts.LoadVersionIndependentAPINotes)
779+
invocationArgStrs.insert(invocationArgStrs.end(),
780+
{"-fswift-version-independent-apinotes"});
777781
}
778782

779783
static void

lib/ClangImporter/ImportDecl.cpp

Lines changed: 153 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "clang/AST/Type.h"
6161
#include "clang/Basic/Specifiers.h"
6262
#include "clang/Basic/TargetInfo.h"
63+
#include "clang/Sema/SemaDiagnostic.h"
6364
#include "clang/Lex/Preprocessor.h"
6465
#include "clang/Sema/Lookup.h"
6566

@@ -155,6 +156,8 @@ bool ClangImporter::Implementation::recordHasReferenceSemantics(
155156
return false;
156157

157158
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
159+
// ACTODO: Check versioned attrs in case of
160+
// captureSwiftVersionIndependentAPINotes
158161
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
159162
return swiftAttr->getAttribute() == "import_reference" ||
160163
// TODO: Remove this once libSwift hosttools no longer
@@ -8513,6 +8516,67 @@ static bool isUsingMacroName(clang::SourceManager &SM,
85138516
return content == MacroName;
85148517
}
85158518

8519+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
8520+
llvm::VersionTuple currentVersion,
8521+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
8522+
// Scan through Swift-Versioned clang attributes and select which one to apply
8523+
// if multiple candidates exist.
8524+
SmallVector<clang::SwiftVersionedAttr*, 4> swiftVersionedAttributes;
8525+
for (auto attr : clangDecl->attrs())
8526+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr))
8527+
swiftVersionedAttributes.push_back(versionedAttr);
8528+
8529+
// An attribute version is valid to apply if it is greater than the current version
8530+
// or is unversioned
8531+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
8532+
return attrVersion.empty() || attrVersion >= currentVersion;
8533+
};
8534+
8535+
// We have a better attribute option if there exists another versioned attr
8536+
// wrapper for this attribute kind with a valid version that is lower than the
8537+
// one of the attribute we are considering
8538+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
8539+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
8540+
for (auto VAI = swiftVersionedAttributes.begin(),
8541+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8542+
auto otherVersionedAttr = *VAI;
8543+
auto otherAttrKind = otherVersionedAttr->getAttrToAdd()->getKind();
8544+
auto otherAttrVersion = otherVersionedAttr->getVersion();
8545+
// Same exact attribute, ignore
8546+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
8547+
continue;
8548+
8549+
// For a matching attribute kind, an un-versioned attribute
8550+
// never takes precedence over an exsiting valid versioned one.
8551+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
8552+
continue;
8553+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
8554+
return true;
8555+
8556+
// For two versioned attributes of the same kind, the one with the lower applicable
8557+
// version takes precedence.
8558+
if (otherAttrKind == attrKind &&
8559+
applicableVersion(otherAttrVersion) &&
8560+
otherAttrVersion < attrVersion)
8561+
return true;
8562+
}
8563+
return false;
8564+
};
8565+
8566+
for (auto VAI = swiftVersionedAttributes.begin(),
8567+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8568+
auto versionedAttr = *VAI;
8569+
auto attrKind = versionedAttr->getAttrToAdd()->getKind();
8570+
auto attrVersion = versionedAttr->getVersion();
8571+
if (!applicableVersion(attrVersion))
8572+
discardVersionedAttrSet.insert(versionedAttr);
8573+
else if (haveBetterAttr(attrVersion, attrKind))
8574+
discardVersionedAttrSet.insert(versionedAttr);
8575+
else
8576+
continue;
8577+
}
8578+
}
8579+
85168580
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
85178581
{
85188582
// 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.
@@ -8546,17 +8610,39 @@ void ClangImporter::Implementation::importAttributes(
85468610
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
85478611
isAsync = func->hasAsync();
85488612

8613+
// Determine which versioned attributes are applicable
8614+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8615+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8616+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
8617+
discardVersionedAttrSet);
8618+
85498619
// Scan through Clang attributes and map them onto Swift
85508620
// equivalents.
85518621
bool AnyUnavailable = MappedDecl->getAttrs().isUnavailable(C);
85528622
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
85538623
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8624+
clang::Attr *consideringAttr = *AI;
8625+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
8626+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(consideringAttr)) {
8627+
// "type" and "nullability" attributes are handled earlier since they change
8628+
// the decl's type.
8629+
if (isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) ||
8630+
isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd()))
8631+
continue;
8632+
8633+
if (discardVersionedAttrSet.count(versionedAttr))
8634+
continue;
8635+
8636+
consideringAttr = versionedAttr->getAttrToAdd();
8637+
}
8638+
}
8639+
85548640
//
85558641
// __attribute__((unavailable))
85568642
//
85578643
// Mapping: @available(*,unavailable)
85588644
//
8559-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
8645+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
85608646
auto Message = unavailable->getMessage();
85618647
auto attr = AvailableAttr::createPlatformAgnostic(C, Message);
85628648
MappedDecl->getAttrs().add(attr);
@@ -8569,7 +8655,7 @@ void ClangImporter::Implementation::importAttributes(
85698655
//
85708656
// Mapping: @available(*, unavailable)
85718657
//
8572-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
8658+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
85738659
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
85748660
auto attr = AvailableAttr::createPlatformAgnostic(
85758661
C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift);
@@ -8583,7 +8669,7 @@ void ClangImporter::Implementation::importAttributes(
85838669
//
85848670
// Mapping: @available(*,deprecated)
85858671
//
8586-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
8672+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
85878673
auto Message = deprecated->getMessage();
85888674
auto attr = AvailableAttr::createPlatformAgnostic(C, Message, "",
85898675
PlatformAgnosticAvailabilityKind::Deprecated);
@@ -8593,7 +8679,7 @@ void ClangImporter::Implementation::importAttributes(
85938679

85948680
// __attribute__((availability))
85958681
//
8596-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
8682+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
85978683
StringRef Platform = avail->getPlatform()->getName();
85988684

85998685
// Is this our special "availability(swift, unavailable)" attribute?
@@ -8805,6 +8891,62 @@ void ClangImporter::Implementation::importAttributes(
88058891
}
88068892
}
88078893

8894+
static void applyTypeAndNullabilityAPINotes(const clang::NamedDecl *ClangDecl,
8895+
clang::Sema &Sema,
8896+
const ImportNameVersion CurrentImporterVersion) {
8897+
// Determine which versioned attributes are applicable
8898+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8899+
filterUsableVersionedAttrs(ClangDecl,
8900+
CurrentImporterVersion.asClangVersionTuple(),
8901+
discardVersionedAttrSet);
8902+
8903+
// When importing from a module built with version-independent APINotes payload,
8904+
// the decl will carry all possible versioned notes, without directly applying any
8905+
// of them. For "type" and "nullability" notes, we must apply them first, here,
8906+
// since they change the actual type of the decl as seen downstream.
8907+
//
8908+
// Other kinds of notes will be handled in `importAttributes`.
8909+
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
8910+
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8911+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(*AI)) {
8912+
if (!isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) &&
8913+
!isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8914+
continue;
8915+
}
8916+
8917+
if (discardVersionedAttrSet.count(versionedAttr))
8918+
continue;
8919+
8920+
// Apply Type APINotes
8921+
if (auto typeRenameAttr = dyn_cast<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd())) {
8922+
Sema.ApplyAPINotesType(const_cast<clang::NamedDecl*>(ClangDecl),
8923+
typeRenameAttr->getTypeString());
8924+
}
8925+
8926+
// Apply Nullability APINotes
8927+
if (auto nullabilityAttr = dyn_cast<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8928+
clang::NullabilityKind nullability;
8929+
switch (nullabilityAttr->getKind()) {
8930+
case clang::SwiftNullabilityAttr::Kind::NonNull:
8931+
nullability = clang::NullabilityKind::NonNull;
8932+
break;
8933+
case clang::SwiftNullabilityAttr::Kind::Nullable:
8934+
nullability = clang::NullabilityKind::Nullable;
8935+
break;
8936+
case clang::SwiftNullabilityAttr::Kind::Unspecified:
8937+
nullability = clang::NullabilityKind::Unspecified;
8938+
break;
8939+
case clang::SwiftNullabilityAttr::Kind::NullableResult:
8940+
nullability = clang::NullabilityKind::NullableResult;
8941+
break;
8942+
}
8943+
8944+
Sema.ApplyNullability(const_cast<clang::NamedDecl*>(ClangDecl), nullability);
8945+
}
8946+
}
8947+
}
8948+
}
8949+
88088950
Decl *
88098951
ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
88108952
ImportNameVersion version,
@@ -8827,14 +8969,20 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
88278969
if (access == clang::AS_protected || access == clang::AS_private)
88288970
return nullptr;
88298971

8972+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
8973+
// notes are applied by the client (Importer) instead of the producer of the
8974+
// Clang module we are consuming. Do so now, early, since these notes
8975+
// affect the decl's type.
8976+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8977+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
8978+
88308979
bool SkippedOverTypedef = false;
88318980
Decl *Result = nullptr;
88328981
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,
88338982
TypedefIsSuperfluous)) {
88348983
Result = importDecl(UnderlyingDecl, version);
88358984
SkippedOverTypedef = true;
88368985
}
8837-
88388986
if (!Result) {
88398987
SwiftDeclConverter converter(*this, version);
88408988
Result = converter.Visit(ClangDecl);

lib/Frontend/CompilerInvocation.cpp

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

1959+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
1960+
19591961
if (FrontendOpts.DisableImplicitModules)
19601962
Opts.DisableImplicitClangModules = true;
19611963

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2040,6 +2040,12 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
20402040
GenericArgs.push_back(blocklist);
20412041
}
20422042

2043+
// Inherit APINotes processing method
2044+
if (clangImporterOpts.LoadVersionIndependentAPINotes) {
2045+
GenericArgs.push_back("-version-independent-apinotes");
2046+
genericSubInvocation.getClangImporterOptions().LoadVersionIndependentAPINotes = true;
2047+
}
2048+
20432049
// For now, we only inherit the C++ interoperability mode in
20442050
// Explicit Module Builds.
20452051
if (langOpts.EnableCXXInterop &&

0 commit comments

Comments
 (0)