Skip to content

Commit ab95963

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 109e208 commit ab95963

File tree

7 files changed

+297
-5
lines changed

7 files changed

+297
-5
lines changed

include/swift/Basic/LangOptions.h

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

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

include/swift/Option/FrontendOptions.td

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

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

lib/ClangImporter/ClangImporter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,10 @@ void importer::getNormalInvocationArguments(
770770
invocationArgStrs.push_back("-iapinotes-modules");
771771
invocationArgStrs.push_back(path.str().str());
772772
}
773+
774+
if (importerOpts.LoadVersionIndependentAPINotes)
775+
invocationArgStrs.insert(invocationArgStrs.end(),
776+
{"-fswift-version-independent-apinotes"});
773777
}
774778

775779
static void

lib/ClangImporter/ImportDecl.cpp

Lines changed: 154 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "clang/AST/Type.h"
6565
#include "clang/Basic/Specifiers.h"
6666
#include "clang/Basic/TargetInfo.h"
67+
#include "clang/Sema/SemaDiagnostic.h"
6768
#include "clang/Lex/Preprocessor.h"
6869
#include "clang/Sema/Lookup.h"
6970

@@ -167,6 +168,9 @@ bool ClangImporter::Implementation::recordHasReferenceSemantics(
167168
if (!isa<clang::CXXRecordDecl>(decl) && !ctx.LangOpts.CForeignReferenceTypes)
168169
return false;
169170

171+
// ACTODO: Check versioned attrs in case of
172+
// captureSwiftVersionIndependentAPINotes
173+
170174
// At this point decl might not be fully imported into Swift yet, which
171175
// means we might not have asked Clang to generate its implicit members, such
172176
// as copy or move constructors. This would cause CxxRecordSemanticsRequest to
@@ -8969,6 +8973,67 @@ static bool isUsingMacroName(clang::SourceManager &SM,
89698973
return content == MacroName;
89708974
}
89718975

8976+
static void filterUsableVersionedAttrs(const clang::NamedDecl *clangDecl,
8977+
llvm::VersionTuple currentVersion,
8978+
std::unordered_set<clang::Attr*> &discardVersionedAttrSet) {
8979+
// Scan through Swift-Versioned clang attributes and select which one to apply
8980+
// if multiple candidates exist.
8981+
SmallVector<clang::SwiftVersionedAttr*, 4> swiftVersionedAttributes;
8982+
for (auto attr : clangDecl->attrs())
8983+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr))
8984+
swiftVersionedAttributes.push_back(versionedAttr);
8985+
8986+
// An attribute version is valid to apply if it is greater than the current version
8987+
// or is unversioned
8988+
auto applicableVersion = [currentVersion] (clang::VersionTuple attrVersion) -> bool {
8989+
return attrVersion.empty() || attrVersion >= currentVersion;
8990+
};
8991+
8992+
// We have a better attribute option if there exists another versioned attr
8993+
// wrapper for this attribute kind with a valid version that is lower than the
8994+
// one of the attribute we are considering
8995+
auto haveBetterAttr = [swiftVersionedAttributes, applicableVersion]
8996+
(clang::VersionTuple attrVersion, clang::attr::Kind attrKind) -> bool {
8997+
for (auto VAI = swiftVersionedAttributes.begin(),
8998+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
8999+
auto otherVersionedAttr = *VAI;
9000+
auto otherAttrKind = otherVersionedAttr->getAttrToAdd()->getKind();
9001+
auto otherAttrVersion = otherVersionedAttr->getVersion();
9002+
// Same exact attribute, ignore
9003+
if (otherAttrKind == attrKind && otherAttrVersion == attrVersion)
9004+
continue;
9005+
9006+
// For a matching attribute kind, an un-versioned attribute
9007+
// never takes precedence over an exsiting valid versioned one.
9008+
if (otherAttrKind == attrKind && !attrVersion.empty() && otherAttrVersion.empty())
9009+
continue;
9010+
if (otherAttrKind == attrKind && applicableVersion(otherAttrVersion) && attrVersion.empty())
9011+
return true;
9012+
9013+
// For two versioned attributes of the same kind, the one with the lower applicable
9014+
// version takes precedence.
9015+
if (otherAttrKind == attrKind &&
9016+
applicableVersion(otherAttrVersion) &&
9017+
otherAttrVersion < attrVersion)
9018+
return true;
9019+
}
9020+
return false;
9021+
};
9022+
9023+
for (auto VAI = swiftVersionedAttributes.begin(),
9024+
VAE = swiftVersionedAttributes.end(); VAI != VAE; ++VAI) {
9025+
auto versionedAttr = *VAI;
9026+
auto attrKind = versionedAttr->getAttrToAdd()->getKind();
9027+
auto attrVersion = versionedAttr->getVersion();
9028+
if (!applicableVersion(attrVersion))
9029+
discardVersionedAttrSet.insert(versionedAttr);
9030+
else if (haveBetterAttr(attrVersion, attrKind))
9031+
discardVersionedAttrSet.insert(versionedAttr);
9032+
else
9033+
continue;
9034+
}
9035+
}
9036+
89729037
void ClangImporter::Implementation::importAttributesFromClangDeclToSynthesizedSwiftDecl(Decl *sourceDecl, Decl* synthesizedDecl)
89739038
{
89749039
// 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.
@@ -9002,17 +9067,39 @@ void ClangImporter::Implementation::importAttributes(
90029067
if (auto func = dyn_cast<AbstractFunctionDecl>(MappedDecl))
90039068
isAsync = func->hasAsync();
90049069

9070+
// Determine which versioned attributes are applicable
9071+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
9072+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9073+
filterUsableVersionedAttrs(ClangDecl, CurrentVersion.asClangVersionTuple(),
9074+
discardVersionedAttrSet);
9075+
90059076
// Scan through Clang attributes and map them onto Swift
90069077
// equivalents.
90079078
bool AnyUnavailable = MappedDecl->isUnavailable();
90089079
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
90099080
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
9081+
clang::Attr *consideringAttr = *AI;
9082+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes) {
9083+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(consideringAttr)) {
9084+
// "type" and "nullability" attributes are handled earlier since they change
9085+
// the decl's type.
9086+
if (isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) ||
9087+
isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd()))
9088+
continue;
9089+
9090+
if (discardVersionedAttrSet.count(versionedAttr))
9091+
continue;
9092+
9093+
consideringAttr = versionedAttr->getAttrToAdd();
9094+
}
9095+
}
9096+
90109097
//
90119098
// __attribute__((unavailable))
90129099
//
90139100
// Mapping: @available(*,unavailable)
90149101
//
9015-
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(*AI)) {
9102+
if (auto unavailable = dyn_cast<clang::UnavailableAttr>(consideringAttr)) {
90169103
auto Message = unavailable->getMessage();
90179104
auto attr = AvailableAttr::createUniversallyUnavailable(C, Message);
90189105
MappedDecl->getAttrs().add(attr);
@@ -9025,7 +9112,7 @@ void ClangImporter::Implementation::importAttributes(
90259112
//
90269113
// Mapping: @available(*, unavailable)
90279114
//
9028-
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(*AI))
9115+
if (auto unavailable_annot = dyn_cast<clang::AnnotateAttr>(consideringAttr))
90299116
if (unavailable_annot->getAnnotation() == "swift1_unavailable") {
90309117
auto attr = AvailableAttr::createUnavailableInSwift(C, "", "");
90319118
MappedDecl->getAttrs().add(attr);
@@ -9038,7 +9125,7 @@ void ClangImporter::Implementation::importAttributes(
90389125
//
90399126
// Mapping: @available(*,deprecated)
90409127
//
9041-
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(*AI)) {
9128+
if (auto deprecated = dyn_cast<clang::DeprecatedAttr>(consideringAttr)) {
90429129
auto Message = deprecated->getMessage();
90439130
auto attr = AvailableAttr::createUniversallyDeprecated(C, Message, "");
90449131
MappedDecl->getAttrs().add(attr);
@@ -9047,7 +9134,7 @@ void ClangImporter::Implementation::importAttributes(
90479134

90489135
// __attribute__((availability))
90499136
//
9050-
if (auto avail = dyn_cast<clang::AvailabilityAttr>(*AI)) {
9137+
if (auto avail = dyn_cast<clang::AvailabilityAttr>(consideringAttr)) {
90519138
StringRef Platform = avail->getPlatform()->getName();
90529139

90539140
// Is this our special "availability(swift, unavailable)" attribute?
@@ -9251,6 +9338,62 @@ void ClangImporter::Implementation::importAttributes(
92519338
}
92529339
}
92539340

9341+
static void applyTypeAndNullabilityAPINotes(const clang::NamedDecl *ClangDecl,
9342+
clang::Sema &Sema,
9343+
const ImportNameVersion CurrentImporterVersion) {
9344+
// Determine which versioned attributes are applicable
9345+
std::unordered_set<clang::Attr*> discardVersionedAttrSet;
9346+
filterUsableVersionedAttrs(ClangDecl,
9347+
CurrentImporterVersion.asClangVersionTuple(),
9348+
discardVersionedAttrSet);
9349+
9350+
// When importing from a module built with version-independent APINotes payload,
9351+
// the decl will carry all possible versioned notes, without directly applying any
9352+
// of them. For "type" and "nullability" notes, we must apply them first, here,
9353+
// since they change the actual type of the decl as seen downstream.
9354+
//
9355+
// Other kinds of notes will be handled in `importAttributes`.
9356+
for (clang::NamedDecl::attr_iterator AI = ClangDecl->attr_begin(),
9357+
AE = ClangDecl->attr_end(); AI != AE; ++AI) {
9358+
if (auto versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(*AI)) {
9359+
if (!isa<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd()) &&
9360+
!isa<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
9361+
continue;
9362+
}
9363+
9364+
if (discardVersionedAttrSet.count(versionedAttr))
9365+
continue;
9366+
9367+
// Apply Type APINotes
9368+
if (auto typeRenameAttr = dyn_cast<clang::SwiftTypeAttr>(versionedAttr->getAttrToAdd())) {
9369+
Sema.ApplyAPINotesType(const_cast<clang::NamedDecl*>(ClangDecl),
9370+
typeRenameAttr->getTypeString());
9371+
}
9372+
9373+
// Apply Nullability APINotes
9374+
if (auto nullabilityAttr = dyn_cast<clang::SwiftNullabilityAttr>(versionedAttr->getAttrToAdd())) {
9375+
clang::NullabilityKind nullability;
9376+
switch (nullabilityAttr->getKind()) {
9377+
case clang::SwiftNullabilityAttr::Kind::NonNull:
9378+
nullability = clang::NullabilityKind::NonNull;
9379+
break;
9380+
case clang::SwiftNullabilityAttr::Kind::Nullable:
9381+
nullability = clang::NullabilityKind::Nullable;
9382+
break;
9383+
case clang::SwiftNullabilityAttr::Kind::Unspecified:
9384+
nullability = clang::NullabilityKind::Unspecified;
9385+
break;
9386+
case clang::SwiftNullabilityAttr::Kind::NullableResult:
9387+
nullability = clang::NullabilityKind::NullableResult;
9388+
break;
9389+
}
9390+
9391+
Sema.ApplyNullability(const_cast<clang::NamedDecl*>(ClangDecl), nullability);
9392+
}
9393+
}
9394+
}
9395+
}
9396+
92549397
Decl *
92559398
ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
92569399
ImportNameVersion version,
@@ -9273,14 +9416,20 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
92739416
!isa<clang::FieldDecl>(ClangDecl))
92749417
return nullptr;
92759418

9419+
// If '-version-independent-apinotes' is used, then "type" and "nullability"
9420+
// notes are applied by the client (Importer) instead of the producer of the
9421+
// Clang module we are consuming. Do so now, early, since these notes
9422+
// affect the decl's type.
9423+
if (SwiftContext.ClangImporterOpts.LoadVersionIndependentAPINotes)
9424+
applyTypeAndNullabilityAPINotes(ClangDecl, getClangSema(), CurrentVersion);
9425+
92769426
bool SkippedOverTypedef = false;
92779427
Decl *Result = nullptr;
92789428
if (auto *UnderlyingDecl = canSkipOverTypedef(*this, ClangDecl,
92799429
TypedefIsSuperfluous)) {
92809430
Result = importDecl(UnderlyingDecl, version);
92819431
SkippedOverTypedef = true;
92829432
}
9283-
92849433
if (!Result) {
92859434
SwiftDeclConverter converter(*this, version);
92869435
Result = converter.Visit(ClangDecl);

lib/Frontend/CompilerInvocation.cpp

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

1996+
Opts.LoadVersionIndependentAPINotes |= Args.hasArg(OPT_version_independent_apinotes);
1997+
19961998
if (FrontendOpts.DisableImplicitModules)
19971999
Opts.DisableImplicitClangModules = true;
19982000

lib/Frontend/ModuleInterfaceLoader.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,12 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl(
19721972
GenericArgs.push_back(blocklist);
19731973
}
19741974

1975+
// Inherit APINotes processing method
1976+
if (clangImporterOpts.LoadVersionIndependentAPINotes) {
1977+
GenericArgs.push_back("-version-independent-apinotes");
1978+
genericSubInvocation.getClangImporterOptions().LoadVersionIndependentAPINotes = true;
1979+
}
1980+
19751981
// Inherit the C++ interoperability mode.
19761982
if (langOpts.EnableCXXInterop) {
19771983
// Modelled after a reverse of validateCxxInteropCompatibilityMode

0 commit comments

Comments
 (0)