Skip to content

Commit d6778a6

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 586ac09 commit d6778a6

File tree

6 files changed

+173
-5
lines changed

6 files changed

+173
-5
lines changed

include/swift/Basic/LangOptions.h

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

1036+
/// Whether the importer should expect all APINotes to be wrapped
1037+
/// in versioned attributes, where the importer must select the appropriate
1038+
/// ones to apply.
1039+
bool LoadVersionIndependentAPINotes = false;
1040+
10361041
/// Return a hash code of any components from these options that should
10371042
/// contribute to a Swift Bridging PCH hash.
10381043
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

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

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

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,10 @@ void importer::getNormalInvocationArguments(
767767
invocationArgStrs.push_back("-iapinotes-modules");
768768
invocationArgStrs.push_back(path.str().str());
769769
}
770+
771+
if (importerOpts.LoadVersionIndependentAPINotes)
772+
invocationArgStrs.insert(invocationArgStrs.end(),
773+
{"-fswift-version-independent-apinotes"});
770774
}
771775

772776
static void

lib/ClangImporter/ImportDecl.cpp

Lines changed: 153 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
#include "clang/AST/DeclObjCCommon.h"
5858
#include "clang/Basic/Specifiers.h"
5959
#include "clang/Basic/TargetInfo.h"
60+
#include "clang/Sema/SemaDiagnostic.h"
6061
#include "clang/Lex/Preprocessor.h"
6162
#include "clang/Sema/Lookup.h"
6263

@@ -152,6 +153,8 @@ bool ClangImporter::Implementation::recordHasReferenceSemantics(
152153
return false;
153154

154155
return decl->hasAttrs() && llvm::any_of(decl->getAttrs(), [](auto *attr) {
156+
// ACTODO: Check versioned attrs in case of
157+
// captureSwiftVersionIndependentAPINotes
155158
if (auto swiftAttr = dyn_cast<clang::SwiftAttrAttr>(attr))
156159
return swiftAttr->getAttribute() == "import_reference" ||
157160
// TODO: Remove this once libSwift hosttools no longer
@@ -8356,6 +8359,67 @@ static bool isUsingMacroName(clang::SourceManager &SM,
83568359
return content == MacroName;
83578360
}
83588361

8362+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
8363+
llvm::VersionTuple currentVersion,
8364+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
8365+
// Scan through Swift-Versioned clang attributes and select which one to apply
8366+
// if multiple candidates exist.
8367+
SmallVector<clang::SwiftVersionedAttr*, 4> swiftVersionedAttributes;
8368+
for (auto attr : clangDecl->attrs())
8369+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr))
8370+
swiftVersionedAttributes.push_back(versionedAttr);
8371+
8372+
// An attribute version is valid to apply if it is greater than the current version
8373+
// or is unversioned
8374+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
8375+
return attrVersion.empty() || attrVersion >= currentVersion;
8376+
};
8377+
8378+
// We have a better attribute option if there exists another versioned attr
8379+
// wrapper for this attribute kind with a valid version that is lower than the
8380+
// one of the attribute we are considering
8381+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
8382+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
8383+
for (auto VAI = swiftVersionedAttributes.begin(),
8384+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8385+
auto otherVersionedAttr = *VAI;
8386+
auto otherAttrKind = otherVersionedAttr->getAttrToAdd()->getKind();
8387+
auto otherAttrVersion = otherVersionedAttr->getVersion();
8388+
// Same exact attribute, ignore
8389+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
8390+
continue;
8391+
8392+
// For a matching attribute kind, an un-versioned attribute
8393+
// never takes precedence over an exsiting valid versioned one.
8394+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
8395+
continue;
8396+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
8397+
return true;
8398+
8399+
// For two versioned attributes of the same kind, the one with the lower applicable
8400+
// version takes precedence.
8401+
if (otherAttrKind == attrKind &&
8402+
applicableVersion(otherAttrVersion) &&
8403+
otherAttrVersion < attrVersion)
8404+
return true;
8405+
}
8406+
return false;
8407+
};
8408+
8409+
for (auto VAI = swiftVersionedAttributes.begin(),
8410+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8411+
auto versionedAttr = *VAI;
8412+
auto attrKind = versionedAttr->getAttrToAdd()->getKind();
8413+
auto attrVersion = versionedAttr->getVersion();
8414+
if (!applicableVersion(attrVersion))
8415+
discardVersionedAttrSet.insert(versionedAttr);
8416+
else if (haveBetterAttr(attrVersion, attrKind))
8417+
discardVersionedAttrSet.insert(versionedAttr);
8418+
else
8419+
continue;
8420+
}
8421+
}
8422+
83598423
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
83608424
{
83618425
// 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.
@@ -8389,17 +8453,39 @@ void ClangImporter::Implementation::importAttributes(
83898453
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
83908454
isAsync = func->hasAsync();
83918455

8456+
// Determine which versioned attributes are applicable
8457+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8458+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8459+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
8460+
discardVersionedAttrSet);
8461+
83928462
// Scan through Clang attributes and map them onto Swift
83938463
// equivalents.
83948464
bool AnyUnavailable = MappedDecl->getAttrs().isUnavailable(C);
83958465
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
83968466
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8467+
clang::Attr *consideringAttr = *AI;
8468+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
8469+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(consideringAttr)) {
8470+
// "type" and "nullability" attributes are handled earlier since they change
8471+
// the decl's type.
8472+
if (isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) ||
8473+
isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd()))
8474+
continue;
8475+
8476+
if (discardVersionedAttrSet.count(versionedAttr))
8477+
continue;
8478+
8479+
consideringAttr = versionedAttr->getAttrToAdd();
8480+
}
8481+
}
8482+
83978483
//
83988484
// __attribute__((unavailable))
83998485
//
84008486
// Mapping: @available(*,unavailable)
84018487
//
8402-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
8488+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
84038489
auto Message = unavailable->getMessage();
84048490
auto attr = AvailableAttr::createPlatformAgnostic(C, Message);
84058491
MappedDecl->getAttrs().add(attr);
@@ -8412,7 +8498,7 @@ void ClangImporter::Implementation::importAttributes(
84128498
//
84138499
// Mapping: @available(*, unavailable)
84148500
//
8415-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
8501+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
84168502
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
84178503
auto attr = AvailableAttr::createPlatformAgnostic(
84188504
C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift);
@@ -8426,7 +8512,7 @@ void ClangImporter::Implementation::importAttributes(
84268512
//
84278513
// Mapping: @available(*,deprecated)
84288514
//
8429-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
8515+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
84308516
auto Message = deprecated->getMessage();
84318517
auto attr = AvailableAttr::createPlatformAgnostic(C, Message, "",
84328518
PlatformAgnosticAvailabilityKind::Deprecated);
@@ -8436,7 +8522,7 @@ void ClangImporter::Implementation::importAttributes(
84368522

84378523
// __attribute__((availability))
84388524
//
8439-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
8525+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
84408526
StringRef Platform = avail->getPlatform()->getName();
84418527

84428528
// Is this our special "availability(swift, unavailable)" attribute?
@@ -8648,6 +8734,62 @@ void ClangImporter::Implementation::importAttributes(
86488734
}
86498735
}
86508736

8737+
static void applyTypeAndNullabilityAPINotes(const clang::NamedDecl *ClangDecl,
8738+
clang::Sema &Sema,
8739+
const ImportNameVersion CurrentImporterVersion) {
8740+
// Determine which versioned attributes are applicable
8741+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8742+
filterUsableVersionedAttrs(ClangDecl,
8743+
CurrentImporterVersion.asClangVersionTuple(),
8744+
discardVersionedAttrSet);
8745+
8746+
// When importing from a module built with version-independent APINotes payload,
8747+
// the decl will carry all possible versioned notes, without directly applying any
8748+
// of them. For "type" and "nullability" notes, we must apply them first, here,
8749+
// since they change the actual type of the decl as seen downstream.
8750+
//
8751+
// Other kinds of notes will be handled in `importAttributes`.
8752+
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
8753+
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8754+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(*AI)) {
8755+
if (!isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) &&
8756+
!isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8757+
continue;
8758+
}
8759+
8760+
if (discardVersionedAttrSet.count(versionedAttr))
8761+
continue;
8762+
8763+
// Apply Type APINotes
8764+
if (auto typeRenameAttr = dyn_cast<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd())) {
8765+
Sema.ApplyAPINotesType(const_cast<clang::NamedDecl*>(ClangDecl),
8766+
typeRenameAttr->getTypeString());
8767+
}
8768+
8769+
// Apply Nullability APINotes
8770+
if (auto nullabilityAttr = dyn_cast<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8771+
clang::NullabilityKind nullability;
8772+
switch (nullabilityAttr->getKind()) {
8773+
case clang::SwiftNullabilityAttr::Kind::NonNull:
8774+
nullability = clang::NullabilityKind::NonNull;
8775+
break;
8776+
case clang::SwiftNullabilityAttr::Kind::Nullable:
8777+
nullability = clang::NullabilityKind::Nullable;
8778+
break;
8779+
case clang::SwiftNullabilityAttr::Kind::Unspecified:
8780+
nullability = clang::NullabilityKind::Unspecified;
8781+
break;
8782+
case clang::SwiftNullabilityAttr::Kind::NullableResult:
8783+
nullability = clang::NullabilityKind::NullableResult;
8784+
break;
8785+
}
8786+
8787+
Sema.ApplyNullability(const_cast<clang::NamedDecl*>(ClangDecl), nullability);
8788+
}
8789+
}
8790+
}
8791+
}
8792+
86518793
Decl *
86528794
ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
86538795
ImportNameVersion version,
@@ -8670,14 +8812,20 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
86708812
if (access == clang::AS_protected || access == clang::AS_private)
86718813
return nullptr;
86728814

8815+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
8816+
// notes are applied by the client (Importer) instead of the producer of the
8817+
// Clang module we are consuming. Do so now, early, since these notes
8818+
// affect the decl's type.
8819+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8820+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
8821+
86738822
bool SkippedOverTypedef = false;
86748823
Decl *Result = nullptr;
86758824
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,
86768825
TypedefIsSuperfluous)) {
86778826
Result = importDecl(UnderlyingDecl, version);
86788827
SkippedOverTypedef = true;
86798828
}
8680-
86818829
if (!Result) {
86828830
SwiftDeclConverter converter(*this, version);
86838831
Result = converter.Visit(ClangDecl);

lib/Frontend/CompilerInvocation.cpp

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

1943+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
1944+
19431945
if (FrontendOpts.DisableImplicitModules)
19441946
Opts.DisableImplicitClangModules = true;
19451947

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,12 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
20272027
GenericArgs.push_back(blocklist);
20282028
}
20292029

2030+
// Inherit APINotes processing method
2031+
if (clangImporterOpts.LoadVersionIndependentAPINotes) {
2032+
GenericArgs.push_back("-version-independent-apinotes");
2033+
genericSubInvocation.getClangImporterOptions().LoadVersionIndependentAPINotes = true;
2034+
}
2035+
20302036
// For now, we only inherit the C++ interoperability mode in
20312037
// Explicit Module Builds.
20322038
if (langOpts.EnableCXXInterop &&

0 commit comments

Comments
 (0)