Skip to content

Commit 81aefd4

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 1a84348 commit 81aefd4

File tree

7 files changed

+298
-4
lines changed

7 files changed

+298
-4
lines changed

include/swift/Basic/LangOptions.h

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

1116+
/// Whether the importer should expect all APINotes to be wrapped
1117+
/// in versioned attributes, where the importer must select the appropriate
1118+
/// ones to apply.
1119+
bool LoadVersionIndependentAPINotes = false;
1120+
11161121
/// Return a hash code of any components from these options that should
11171122
/// contribute to a Swift Bridging PCH hash.
11181123
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

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

676+
def version_independent_apinotes : Flag<["-"], "version-independent-apinotes">,
677+
HelpText<"Input clang modules carry all versioned APINotes">;
678+
676679
def disable_sil_ownership_verifier : Flag<["-"], "disable-sil-ownership-verifier">,
677680
HelpText<"Do not verify ownership invariants during SIL Verification ">;
678681

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,10 @@ void importer::getNormalInvocationArguments(
787787
invocationArgStrs.push_back("-iapinotes-modules");
788788
invocationArgStrs.push_back(path.str().str());
789789
}
790+
791+
if (importerOpts.LoadVersionIndependentAPINotes)
792+
invocationArgStrs.insert(invocationArgStrs.end(),
793+
{"-fswift-version-independent-apinotes"});
790794
}
791795

792796
static void

lib/ClangImporter/ImportDecl.cpp

Lines changed: 155 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
#include "clang/AST/Type.h"
7070
#include "clang/Basic/Specifiers.h"
7171
#include "clang/Basic/TargetInfo.h"
72+
#include "clang/Sema/SemaDiagnostic.h"
7273
#include "clang/Lex/Preprocessor.h"
7374
#include "clang/Sema/Lookup.h"
7475

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

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

9343+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
9344+
llvm::VersionTuple currentVersion,
9345+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
9346+
// Scan through Swift-Versioned clang attributes and select which one to apply
9347+
// if multiple candidates exist.
9348+
SmallVector<clang::SwiftVersionedAdditionAttr*, 4> swiftVersionedAttributes;
9349+
for (auto attr : clangDecl->attrs())
9350+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAdditionAttr>(attr))
9351+
swiftVersionedAttributes.push_back(versionedAttr);
9352+
9353+
// An attribute version is valid to apply if it is greater than the current version
9354+
// or is unversioned
9355+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
9356+
return attrVersion.empty() || attrVersion >= currentVersion;
9357+
};
9358+
9359+
// We have a better attribute option if there exists another versioned attr
9360+
// wrapper for this attribute kind with a valid version that is lower than the
9361+
// one of the attribute we are considering
9362+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
9363+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
9364+
for (auto VAI = swiftVersionedAttributes.begin(),
9365+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
9366+
auto otherVersionedAttr = *VAI;
9367+
auto otherAttrKind = otherVersionedAttr->getAdditionalAttr()->getKind();
9368+
auto otherAttrVersion = otherVersionedAttr->getVersion();
9369+
// Same exact attribute, ignore
9370+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
9371+
continue;
9372+
9373+
// For a matching attribute kind, an un-versioned attribute
9374+
// never takes precedence over an exsiting valid versioned one.
9375+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
9376+
continue;
9377+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
9378+
return true;
9379+
9380+
// For two versioned attributes of the same kind, the one with the lower applicable
9381+
// version takes precedence.
9382+
if (otherAttrKind == attrKind &&
9383+
applicableVersion(otherAttrVersion) &&
9384+
otherAttrVersion < attrVersion)
9385+
return true;
9386+
}
9387+
return false;
9388+
};
9389+
9390+
for (auto VAI = swiftVersionedAttributes.begin(),
9391+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
9392+
auto versionedAttr = *VAI;
9393+
auto attrKind = versionedAttr->getAdditionalAttr()->getKind();
9394+
auto attrVersion = versionedAttr->getVersion();
9395+
if (!applicableVersion(attrVersion))
9396+
discardVersionedAttrSet.insert(versionedAttr);
9397+
else if (haveBetterAttr(attrVersion, attrKind))
9398+
discardVersionedAttrSet.insert(versionedAttr);
9399+
else
9400+
continue;
9401+
}
9402+
}
9403+
93399404
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
93409405
{
93419406
// 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.
@@ -9369,17 +9434,40 @@ void ClangImporter::Implementation::importAttributes(
93699434
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
93709435
isAsync = func->hasAsync();
93719436

9437+
// Determine which versioned attributes are applicable
9438+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
9439+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9440+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
9441+
discardVersionedAttrSet);
9442+
93729443
// Scan through Clang attributes and map them onto Swift
93739444
// equivalents.
93749445
bool AnyUnavailable = MappedDecl->isUnavailable();
93759446
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
93769447
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
9448+
clang::Attr *consideringAttr = *AI;
9449+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
9450+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAdditionAttr>(consideringAttr)) {
9451+
// "type" and "nullability" attributes are handled earlier since they change
9452+
// the decl's type.
9453+
auto additionalAttr = versionedAttr->getAdditionalAttr();
9454+
if (isa<clang::SwiftTypeAttr>(additionalAttr) ||
9455+
isa<clang::SwiftNullabilityAttr>(additionalAttr))
9456+
continue;
9457+
9458+
if (discardVersionedAttrSet.count(versionedAttr))
9459+
continue;
9460+
9461+
consideringAttr = additionalAttr;
9462+
}
9463+
}
9464+
93779465
//
93789466
// __attribute__((unavailable))
93799467
//
93809468
// Mapping: @available(*,unavailable)
93819469
//
9382-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
9470+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
93839471
auto Message = unavailable->getMessage();
93849472
auto attr = AvailableAttr::createUniversallyUnavailable(C, Message);
93859473
MappedDecl->getAttrs().add(attr);
@@ -9392,7 +9480,7 @@ void ClangImporter::Implementation::importAttributes(
93929480
//
93939481
// Mapping: @available(*, unavailable)
93949482
//
9395-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
9483+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
93969484
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
93979485
auto attr = AvailableAttr::createUnavailableInSwift(C, "", "");
93989486
MappedDecl->getAttrs().add(attr);
@@ -9405,7 +9493,7 @@ void ClangImporter::Implementation::importAttributes(
94059493
//
94069494
// Mapping: @available(*,deprecated)
94079495
//
9408-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
9496+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
94099497
auto Message = deprecated->getMessage();
94109498
auto attr = AvailableAttr::createUniversallyDeprecated(C, Message, "");
94119499
MappedDecl->getAttrs().add(attr);
@@ -9414,7 +9502,7 @@ void ClangImporter::Implementation::importAttributes(
94149502

94159503
// __attribute__((availability))
94169504
//
9417-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
9505+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
94189506
StringRef Platform = avail->getPlatform()->getName();
94199507

94209508
// Is this our special "availability(swift, unavailable)" attribute?
@@ -9646,6 +9734,62 @@ void ClangImporter::Implementation::importAttributes(
96469734
}
96479735
}
96489736

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

9804+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
9805+
// notes are applied by the client (Importer) instead of the producer of the
9806+
// Clang module we are consuming. Do so now, early, since these notes
9807+
// affect the decl's type.
9808+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9809+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
9810+
96609811
bool SkippedOverTypedef = false;
96619812
Decl *Result = nullptr;
96629813
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,

lib/Frontend/CompilerInvocation.cpp

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

2172+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
2173+
21722174
if (FrontendOpts.DisableImplicitModules)
21732175
Opts.DisableImplicitClangModules = true;
21742176

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)