Skip to content

Commit 5970e0f

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 9906199 commit 5970e0f

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
@@ -1011,6 +1011,11 @@ namespace swift {
10111011
/// invocations directly from clang cc1 args.
10121012
bool ClangImporterDirectCC1Scan = false;
10131013

1014+
/// Whether the importer should expect all APINotes to be wrapped
1015+
/// in versioned attributes, where the importer must select the appropriate
1016+
/// ones to apply.
1017+
bool LoadVersionIndependentAPINotes = false;
1018+
10141019
/// Return a hash code of any components from these options that should
10151020
/// contribute to a Swift Bridging PCH hash.
10161021
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

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

645+
def version_independent_apinotes : Flag<["-"], "version-independent-apinotes">,
646+
HelpText<"Input clang modules carry all versioned APINotes">;
647+
645648
def disable_sil_ownership_verifier : Flag<["-"], "disable-sil-ownership-verifier">,
646649
HelpText<"Do not verify ownership invariants during SIL Verification ">;
647650

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,10 @@ void importer::getNormalInvocationArguments(
755755
invocationArgStrs.push_back((llvm::Twine(searchPathOpts.RuntimeResourcePath) +
756756
llvm::sys::path::get_separator() +
757757
"apinotes").str());
758+
759+
if (importerOpts.LoadVersionIndependentAPINotes)
760+
invocationArgStrs.insert(invocationArgStrs.end(),
761+
{"-fswift-version-independent-apinotes"});
758762
}
759763

760764
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/Basic/CharInfo.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
@@ -8238,6 +8241,67 @@ static bool isUsingMacroName(clang::SourceManager &SM,
82388241
return content == MacroName;
82398242
}
82408243

8244+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
8245+
llvm::VersionTuple currentVersion,
8246+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
8247+
// Scan through Swift-Versioned clang attributes and select which one to apply
8248+
// if multiple candidates exist.
8249+
SmallVector<clang::SwiftVersionedAttr*, 4> swiftVersionedAttributes;
8250+
for (auto attr : clangDecl->attrs())
8251+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr))
8252+
swiftVersionedAttributes.push_back(versionedAttr);
8253+
8254+
// An attribute version is valid to apply if it is greater than the current version
8255+
// or is unversioned
8256+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
8257+
return attrVersion.empty() || attrVersion >= currentVersion;
8258+
};
8259+
8260+
// We have a better attribute option if there exists another versioned attr
8261+
// wrapper for this attribute kind with a valid version that is lower than the
8262+
// one of the attribute we are considering
8263+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
8264+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
8265+
for (auto VAI = swiftVersionedAttributes.begin(),
8266+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8267+
auto otherVersionedAttr = *VAI;
8268+
auto otherAttrKind = otherVersionedAttr->getAttrToAdd()->getKind();
8269+
auto otherAttrVersion = otherVersionedAttr->getVersion();
8270+
// Same exact attribute, ignore
8271+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
8272+
continue;
8273+
8274+
// For a matching attribute kind, an un-versioned attribute
8275+
// never takes precedence over an exsiting valid versioned one.
8276+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
8277+
continue;
8278+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
8279+
return true;
8280+
8281+
// For two versioned attributes of the same kind, the one with the lower applicable
8282+
// version takes precedence.
8283+
if (otherAttrKind == attrKind &&
8284+
applicableVersion(otherAttrVersion) &&
8285+
otherAttrVersion < attrVersion)
8286+
return true;
8287+
}
8288+
return false;
8289+
};
8290+
8291+
for (auto VAI = swiftVersionedAttributes.begin(),
8292+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8293+
auto versionedAttr = *VAI;
8294+
auto attrKind = versionedAttr->getAttrToAdd()->getKind();
8295+
auto attrVersion = versionedAttr->getVersion();
8296+
if (!applicableVersion(attrVersion))
8297+
discardVersionedAttrSet.insert(versionedAttr);
8298+
else if (haveBetterAttr(attrVersion, attrKind))
8299+
discardVersionedAttrSet.insert(versionedAttr);
8300+
else
8301+
continue;
8302+
}
8303+
}
8304+
82418305
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
82428306
{
82438307
// 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.
@@ -8271,17 +8335,39 @@ void ClangImporter::Implementation::importAttributes(
82718335
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
82728336
isAsync = func->hasAsync();
82738337

8338+
// Determine which versioned attributes are applicable
8339+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8340+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8341+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
8342+
discardVersionedAttrSet);
8343+
82748344
// Scan through Clang attributes and map them onto Swift
82758345
// equivalents.
82768346
bool AnyUnavailable = MappedDecl->getAttrs().isUnavailable(C);
82778347
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
82788348
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8349+
clang::Attr *consideringAttr = *AI;
8350+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
8351+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(consideringAttr)) {
8352+
// "type" and "nullability" attributes are handled earlier since they change
8353+
// the decl's type.
8354+
if (isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) ||
8355+
isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd()))
8356+
continue;
8357+
8358+
if (discardVersionedAttrSet.count(versionedAttr))
8359+
continue;
8360+
8361+
consideringAttr = versionedAttr->getAttrToAdd();
8362+
}
8363+
}
8364+
82798365
//
82808366
// __attribute__((unavailable))
82818367
//
82828368
// Mapping: @available(*,unavailable)
82838369
//
8284-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
8370+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
82858371
auto Message = unavailable->getMessage();
82868372
auto attr = AvailableAttr::createPlatformAgnostic(C, Message);
82878373
MappedDecl->getAttrs().add(attr);
@@ -8294,7 +8380,7 @@ void ClangImporter::Implementation::importAttributes(
82948380
//
82958381
// Mapping: @available(*, unavailable)
82968382
//
8297-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
8383+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
82988384
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
82998385
auto attr = AvailableAttr::createPlatformAgnostic(
83008386
C, "", "", PlatformAgnosticAvailabilityKind::UnavailableInSwift);
@@ -8308,7 +8394,7 @@ void ClangImporter::Implementation::importAttributes(
83088394
//
83098395
// Mapping: @available(*,deprecated)
83108396
//
8311-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
8397+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
83128398
auto Message = deprecated->getMessage();
83138399
auto attr = AvailableAttr::createPlatformAgnostic(C, Message, "",
83148400
PlatformAgnosticAvailabilityKind::Deprecated);
@@ -8318,7 +8404,7 @@ void ClangImporter::Implementation::importAttributes(
83188404

83198405
// __attribute__((availability))
83208406
//
8321-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
8407+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
83228408
StringRef Platform = avail->getPlatform()->getName();
83238409

83248410
// Is this our special "availability(swift, unavailable)" attribute?
@@ -8530,6 +8616,62 @@ void ClangImporter::Implementation::importAttributes(
85308616
}
85318617
}
85328618

8619+
static void applyTypeAndNullabilityAPINotes(const clang::NamedDecl *ClangDecl,
8620+
clang::Sema &Sema,
8621+
const ImportNameVersion CurrentImporterVersion) {
8622+
// Determine which versioned attributes are applicable
8623+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
8624+
filterUsableVersionedAttrs(ClangDecl,
8625+
CurrentImporterVersion.asClangVersionTuple(),
8626+
discardVersionedAttrSet);
8627+
8628+
// When importing from a module built with version-independent APINotes payload,
8629+
// the decl will carry all possible versioned notes, without directly applying any
8630+
// of them. For "type" and "nullability" notes, we must apply them first, here,
8631+
// since they change the actual type of the decl as seen downstream.
8632+
//
8633+
// Other kinds of notes will be handled in `importAttributes`.
8634+
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
8635+
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
8636+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(*AI)) {
8637+
if (!isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) &&
8638+
!isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8639+
continue;
8640+
}
8641+
8642+
if (discardVersionedAttrSet.count(versionedAttr))
8643+
continue;
8644+
8645+
// Apply Type APINotes
8646+
if (auto typeRenameAttr = dyn_cast<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd())) {
8647+
Sema.ApplyAPINotesType(const_cast<clang::NamedDecl*>(ClangDecl),
8648+
typeRenameAttr->getTypeString());
8649+
}
8650+
8651+
// Apply Nullability APINotes
8652+
if (auto nullabilityAttr = dyn_cast<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
8653+
clang::NullabilityKind nullability;
8654+
switch (nullabilityAttr->getKind()) {
8655+
case clang::SwiftNullabilityAttr::Kind::NonNull:
8656+
nullability = clang::NullabilityKind::NonNull;
8657+
break;
8658+
case clang::SwiftNullabilityAttr::Kind::Nullable:
8659+
nullability = clang::NullabilityKind::Nullable;
8660+
break;
8661+
case clang::SwiftNullabilityAttr::Kind::Unspecified:
8662+
nullability = clang::NullabilityKind::Unspecified;
8663+
break;
8664+
case clang::SwiftNullabilityAttr::Kind::NullableResult:
8665+
nullability = clang::NullabilityKind::NullableResult;
8666+
break;
8667+
}
8668+
8669+
Sema.ApplyNullability(const_cast<clang::NamedDecl*>(ClangDecl), nullability);
8670+
}
8671+
}
8672+
}
8673+
}
8674+
85338675
Decl *
85348676
ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
85358677
ImportNameVersion version,
@@ -8552,14 +8694,20 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
85528694
if (access == clang::AS_protected || access == clang::AS_private)
85538695
return nullptr;
85548696

8697+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
8698+
// notes are applied by the client (Importer) instead of the producer of the
8699+
// Clang module we are consuming. Do so now, early, since these notes
8700+
// affect the decl's type.
8701+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
8702+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
8703+
85558704
bool SkippedOverTypedef = false;
85568705
Decl *Result = nullptr;
85578706
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,
85588707
TypedefIsSuperfluous)) {
85598708
Result = importDecl(UnderlyingDecl, version);
85608709
SkippedOverTypedef = true;
85618710
}
8562-
85638711
if (!Result) {
85648712
SwiftDeclConverter converter(*this, version);
85658713
Result = converter.Visit(ClangDecl);

lib/Frontend/CompilerInvocation.cpp

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

1874+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
1875+
18741876
if (FrontendOpts.DisableImplicitModules)
18751877
Opts.DisableImplicitClangModules = true;
18761878

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2007,6 +2007,12 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
20072007
GenericArgs.push_back(blocklist);
20082008
}
20092009

2010+
// Inherit APINotes processing method
2011+
if (clangImporterOpts.LoadVersionIndependentAPINotes) {
2012+
GenericArgs.push_back("-version-independent-apinotes");
2013+
genericSubInvocation.getClangImporterOptions().LoadVersionIndependentAPINotes = true;
2014+
}
2015+
20102016
// For now, we only inherit the C++ interoperability mode in
20112017
// Explicit Module Builds.
20122018
if (langOpts.EnableCXXInterop &&

0 commit comments

Comments
 (0)