Skip to content

Commit dceacdd

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 dceacdd

File tree

7 files changed

+309
-4
lines changed

7 files changed

+309
-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: 166 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::SwiftVersionedAdditionAttr*, 4> swiftVersionedAttributes;
9352+
for (auto attr : clangDecl->attrs())
9353+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAdditionAttr>(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->getAdditionalAttr()->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->getAdditionalAttr()->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,40 @@ 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::SwiftVersionedAdditionAttr>(consideringAttr)) {
9454+
// "type" and "nullability" attributes are handled earlier since they change
9455+
// the decl's type.
9456+
auto additionalAttr = versionedAttr->getAdditionalAttr();
9457+
if (isa<clang::SwiftTypeAttr>(additionalAttr) ||
9458+
isa<clang::SwiftNullabilityAttr>(additionalAttr))
9459+
continue;
9460+
9461+
if (discardVersionedAttrSet.count(versionedAttr))
9462+
continue;
9463+
9464+
consideringAttr = additionalAttr;
9465+
}
9466+
}
9467+
93809468
//
93819469
// __attribute__((unavailable))
93829470
//
93839471
// Mapping: @available(*,unavailable)
93849472
//
9385-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
9473+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
93869474
auto Message = unavailable->getMessage();
93879475
auto attr = AvailableAttr::createUniversallyUnavailable(C, Message);
93889476
MappedDecl->getAttrs().add(attr);
@@ -9395,7 +9483,7 @@ void ClangImporter::Implementation::importAttributes(
93959483
//
93969484
// Mapping: @available(*, unavailable)
93979485
//
9398-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
9486+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
93999487
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
94009488
auto attr = AvailableAttr::createUnavailableInSwift(C, "", "");
94019489
MappedDecl->getAttrs().add(attr);
@@ -9408,7 +9496,7 @@ void ClangImporter::Implementation::importAttributes(
94089496
//
94099497
// Mapping: @available(*,deprecated)
94109498
//
9411-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
9499+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
94129500
auto Message = deprecated->getMessage();
94139501
auto attr = AvailableAttr::createUniversallyDeprecated(C, Message, "");
94149502
MappedDecl->getAttrs().add(attr);
@@ -9417,7 +9505,7 @@ void ClangImporter::Implementation::importAttributes(
94179505

94189506
// __attribute__((availability))
94199507
//
9420-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
9508+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
94219509
StringRef Platform = avail->getPlatform()->getName();
94229510

94239511
// Is this our special "availability(swift, unavailable)" attribute?
@@ -9649,6 +9737,62 @@ void ClangImporter::Implementation::importAttributes(
96499737
}
96509738
}
96519739

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

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