Skip to content

Commit a709195

Browse files
authored
[APINotes] Add support for capturing all possible versioned APINotes without applying them
Swift-versioned API notes get applied at PCM constrution time relying on '-fapinotes-swift-version=X' argument to pick the appropriate version. This change adds a new APINotes application mode with '-fswift-version-independent-apinotes' which causes *all* versioned API notes to get recorded into the PCM wrapped in 'SwiftVersionedAttr' instances. The expectation in this mode is that the Swift client will perform the required transformations as per the API notes on the client side, when loading the PCM, instead of them getting applied on the producer side. This will allow the same PCM to be usable by Swift clients building with different language versions. In addition to versioned-wrapping the various existing API notes annotations which are carried in declaration attributes, this change adds a new attribute for two annotations which were previously applied directly to the declaration at the PCM producer side: 1) Type and 2) Nullability annotations with 'SwiftTypeAttr' and 'SwiftNullabilityAttr', respectively. The logic to apply these two annotations to a declaration is refactored into API.
1 parent 8a7c973 commit a709195

File tree

9 files changed

+226
-78
lines changed

9 files changed

+226
-78
lines changed

clang/include/clang/APINotes/APINotesManager.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ class APINotesManager {
5050
/// source file from which an entity was declared.
5151
bool ImplicitAPINotes;
5252

53+
/// Whether to apply all APINotes as optionally-applied versioned
54+
/// entities. This means that when building a Clang module,
55+
/// we capture every note on a given decl wrapped in a SwiftVersionedAttr
56+
/// (with an empty version field for unversioned notes), and have the
57+
/// client apply the relevant version's notes.
58+
bool VersionIndependentSwift;
59+
5360
/// The Swift version to use when interpreting versioned API notes.
5461
llvm::VersionTuple SwiftVersion;
5562

@@ -167,6 +174,8 @@ class APINotesManager {
167174

168175
/// Find the API notes readers that correspond to the given source location.
169176
llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
177+
178+
bool captureVersionIndependentSwift() { return VersionIndependentSwift; }
170179
};
171180

172181
} // end namespace api_notes

clang/include/clang/Basic/Attr.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3068,6 +3068,26 @@ def Regparm : TypeAttr {
30683068
let ASTNode = 0;
30693069
}
30703070

3071+
def SwiftType : Attr {
3072+
// This attribute has no spellings as it is only ever created implicitly
3073+
// from API notes.
3074+
let Spellings = [];
3075+
let Args = [StringArgument<"TypeString">];
3076+
let SemaHandler = 0;
3077+
let Documentation = [InternalOnly];
3078+
}
3079+
3080+
def SwiftNullability : Attr {
3081+
// This attribute has no spellings as it is only ever created implicitly
3082+
// from API notes.
3083+
let Spellings = [];
3084+
let Args = [EnumArgument<"Kind", "Kind", /*is_string=*/false,
3085+
["non_null", "nullable", "unspecified", "nullable_result"],
3086+
["NonNull", "Nullable", "Unspecified", "NullableResult"]>];
3087+
let SemaHandler = 0;
3088+
let Documentation = [InternalOnly];
3089+
}
3090+
30713091
def SwiftAsyncName : InheritableAttr {
30723092
let Spellings = [GNU<"swift_async_name">];
30733093
let Args = [StringArgument<"Name">];

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, Compatible, "retain documentation
390390

391391
LANGOPT(APINotes, 1, 0, NotCompatible, "use external API notes")
392392
LANGOPT(APINotesModules, 1, 0, NotCompatible, "use module-based external API notes")
393+
LANGOPT(SwiftVersionIndependentAPINotes, 1, 0, NotCompatible, "use external API notes capturing all versions")
393394

394395
LANGOPT(SanitizeAddressFieldPadding, 2, 0, NotCompatible, "controls how aggressive is ASan "
395396
"field padding (0: none, 1:least "

clang/include/clang/Driver/Options.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,12 @@ defm apinotes_modules : BoolOption<"f", "apinotes-modules",
18911891
NegFlag<SetFalse, [], [ClangOption], "Disable">,
18921892
BothFlags<[], [ClangOption, CC1Option], " module-based external API notes support">>,
18931893
Group<f_clang_Group>;
1894+
defm swift_version_independent_apinotes : BoolOption<"f", "swift-version-independent-apinotes",
1895+
LangOpts<"SwiftVersionIndependentAPINotes">, DefaultFalse,
1896+
PosFlag<SetTrue, [], [ClangOption], "Enable">,
1897+
NegFlag<SetFalse, [], [ClangOption], "Disable">,
1898+
BothFlags<[], [ClangOption, CC1Option], " version-independent external API notes support">>,
1899+
Group<f_clang_Group>;
18941900
def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">,
18951901
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
18961902
MetaVarName<"<version>">,

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1614,7 +1614,17 @@ class Sema final : public SemaBase {
16141614
///
16151615
/// Triggered by declaration-attribute processing.
16161616
void ProcessAPINotes(Decl *D);
1617-
1617+
/// Apply the 'Nullability:' annotation to the specified declaration
1618+
void ApplyNullability(Decl *D, NullabilityKind Nullability);
1619+
/// Apply the 'Type:' annotation to the specified declaration
1620+
void ApplyAPINotesType(Decl *D, StringRef TypeString);
1621+
1622+
/// Whether APINotes should be gathered for all applicable Swift language
1623+
/// versions, without being applied. Leaving clients of the current module
1624+
/// to select and apply the correct version.
1625+
bool captureSwiftVersionIndependentAPINotes() {
1626+
return APINotes.captureVersionIndependentSwift();
1627+
}
16181628
///@}
16191629

16201630
//

clang/lib/APINotes/APINotesManager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
4949
} // namespace
5050

5151
APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts)
52-
: SM(SM), ImplicitAPINotes(LangOpts.APINotes) {}
52+
: SM(SM), ImplicitAPINotes(LangOpts.APINotes),
53+
VersionIndependentSwift(LangOpts.SwiftVersionIndependentAPINotes) {}
5354

5455
APINotesManager::~APINotesManager() {
5556
// Free the API notes readers.

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7047,6 +7047,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
70477047
CmdArgs.push_back("-fapinotes-modules");
70487048
Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
70497049

7050+
if (Args.hasFlag(options::OPT_fswift_version_independent_apinotes,
7051+
options::OPT_fno_swift_version_independent_apinotes, false))
7052+
CmdArgs.push_back("-fswift-version-independent-apinotes");
7053+
70507054
// -fblocks=0 is default.
70517055
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
70527056
TC.IsBlocksDefault()) ||

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 137 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -52,63 +52,58 @@ static bool isIndirectPointerType(QualType Type) {
5252
Pointee->isMemberPointerType();
5353
}
5454

55-
/// Apply nullability to the given declaration.
56-
static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability,
57-
VersionedInfoMetadata Metadata) {
58-
if (!Metadata.IsActive)
59-
return;
55+
static void applyAPINotesType(Sema &S, Decl *decl, StringRef typeString,
56+
VersionedInfoMetadata metadata) {
57+
if (typeString.empty())
6058

61-
auto GetModified =
62-
[&](Decl *D, QualType QT,
63-
NullabilityKind Nullability) -> std::optional<QualType> {
64-
QualType Original = QT;
65-
S.CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
66-
isa<ParmVarDecl>(D),
67-
/*OverrideExisting=*/true);
68-
return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
69-
: std::nullopt;
70-
};
71-
72-
if (auto Function = dyn_cast<FunctionDecl>(D)) {
73-
if (auto Modified =
74-
GetModified(D, Function->getReturnType(), Nullability)) {
75-
const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
76-
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(FnType))
77-
Function->setType(S.Context.getFunctionType(
78-
*Modified, proto->getParamTypes(), proto->getExtProtoInfo()));
79-
else
80-
Function->setType(
81-
S.Context.getFunctionNoProtoType(*Modified, FnType->getExtInfo()));
82-
}
83-
} else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
84-
if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
85-
Method->setReturnType(*Modified);
59+
return;
8660

87-
// Make it a context-sensitive keyword if we can.
88-
if (!isIndirectPointerType(*Modified))
89-
Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
90-
Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
91-
}
92-
} else if (auto Value = dyn_cast<ValueDecl>(D)) {
93-
if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
94-
Value->setType(*Modified);
61+
// Version-independent APINotes add "type" annotations
62+
// with a versioned attribute for the client to select and apply.
63+
if (S.captureSwiftVersionIndependentAPINotes()) {
64+
auto *typeAttr = SwiftTypeAttr::CreateImplicit(S.Context, typeString);
65+
auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
66+
S.Context, metadata.Version, typeAttr, metadata.IsReplacement);
67+
decl->addAttr(versioned);
68+
} else {
69+
if (!metadata.IsActive)
70+
return;
71+
S.ApplyAPINotesType(decl, typeString);
72+
}
73+
}
9574

96-
// Make it a context-sensitive keyword if we can.
97-
if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
98-
if (Parm->isObjCMethodParameter() && !isIndirectPointerType(*Modified))
99-
Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
100-
Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
101-
}
75+
/// Apply nullability to the given declaration.
76+
static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
77+
VersionedInfoMetadata metadata) {
78+
// Version-independent APINotes add "nullability" annotations
79+
// with a versioned attribute for the client to select and apply.
80+
if (S.captureSwiftVersionIndependentAPINotes()) {
81+
SwiftNullabilityAttr::Kind attrNullabilityKind;
82+
switch (nullability) {
83+
case NullabilityKind::NonNull:
84+
attrNullabilityKind = SwiftNullabilityAttr::Kind::NonNull;
85+
break;
86+
case NullabilityKind::Nullable:
87+
attrNullabilityKind = SwiftNullabilityAttr::Kind::Nullable;
88+
break;
89+
case NullabilityKind::Unspecified:
90+
attrNullabilityKind = SwiftNullabilityAttr::Kind::Unspecified;
91+
break;
92+
case NullabilityKind::NullableResult:
93+
attrNullabilityKind = SwiftNullabilityAttr::Kind::NullableResult;
94+
break;
10295
}
103-
} else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
104-
if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
105-
Property->setType(*Modified, Property->getTypeSourceInfo());
96+
auto *nullabilityAttr =
97+
SwiftNullabilityAttr::CreateImplicit(S.Context, attrNullabilityKind);
98+
auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
99+
S.Context, metadata.Version, nullabilityAttr, metadata.IsReplacement);
100+
decl->addAttr(versioned);
101+
return;
102+
} else {
103+
if (!metadata.IsActive)
104+
return;
106105

107-
// Make it a property attribute if we can.
108-
if (!isIndirectPointerType(*Modified))
109-
Property->setPropertyAttributes(
110-
ObjCPropertyAttribute::kind_null_resettable);
111-
}
106+
S.ApplyNullability(decl, nullability);
112107
}
113108
}
114109

@@ -361,42 +356,99 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc,
361356
return false;
362357
}
363358

364-
/// Process API notes for a variable or property.
365-
static void ProcessAPINotes(Sema &S, Decl *D,
366-
const api_notes::VariableInfo &Info,
367-
VersionedInfoMetadata Metadata) {
368-
// Type override.
369-
if (Metadata.IsActive && !Info.getType().empty() &&
370-
S.ParseTypeFromStringCallback) {
371-
auto ParsedType = S.ParseTypeFromStringCallback(
372-
Info.getType(), "<API Notes>", D->getLocation());
359+
void Sema::ApplyAPINotesType(Decl *D, StringRef TypeString) {
360+
if (!TypeString.empty() && ParseTypeFromStringCallback) {
361+
auto ParsedType = ParseTypeFromStringCallback(TypeString, "<API Notes>",
362+
D->getLocation());
373363
if (ParsedType.isUsable()) {
374364
QualType Type = Sema::GetTypeFromParser(ParsedType.get());
375-
auto TypeInfo =
376-
S.Context.getTrivialTypeSourceInfo(Type, D->getLocation());
377-
365+
auto TypeInfo = Context.getTrivialTypeSourceInfo(Type, D->getLocation());
378366
if (auto Var = dyn_cast<VarDecl>(D)) {
379367
// Make adjustments to parameter types.
380368
if (isa<ParmVarDecl>(Var)) {
381-
Type = S.ObjC().AdjustParameterTypeForObjCAutoRefCount(
369+
Type = ObjC().AdjustParameterTypeForObjCAutoRefCount(
382370
Type, D->getLocation(), TypeInfo);
383-
Type = S.Context.getAdjustedParameterType(Type);
371+
Type = Context.getAdjustedParameterType(Type);
384372
}
385373

386-
if (!checkAPINotesReplacementType(S, Var->getLocation(), Var->getType(),
387-
Type)) {
374+
if (!checkAPINotesReplacementType(*this, Var->getLocation(),
375+
Var->getType(), Type)) {
388376
Var->setType(Type);
389377
Var->setTypeSourceInfo(TypeInfo);
390378
}
391-
} else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
392-
if (!checkAPINotesReplacementType(S, Property->getLocation(),
393-
Property->getType(), Type))
394-
Property->setType(Type, TypeInfo);
395-
396-
} else
379+
} else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
380+
if (!checkAPINotesReplacementType(*this, property->getLocation(),
381+
property->getType(), Type)) {
382+
property->setType(Type, TypeInfo);
383+
}
384+
} else {
397385
llvm_unreachable("API notes allowed a type on an unknown declaration");
386+
}
387+
}
388+
}
389+
}
390+
391+
void Sema::ApplyNullability(Decl *D, NullabilityKind Nullability) {
392+
auto GetModified =
393+
[&](class Decl *D, QualType QT,
394+
NullabilityKind Nullability) -> std::optional<QualType> {
395+
QualType Original = QT;
396+
CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
397+
isa<ParmVarDecl>(D),
398+
/*OverrideExisting=*/true);
399+
return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
400+
: std::nullopt;
401+
};
402+
403+
if (auto Function = dyn_cast<FunctionDecl>(D)) {
404+
if (auto Modified =
405+
GetModified(D, Function->getReturnType(), Nullability)) {
406+
const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
407+
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(FnType))
408+
Function->setType(Context.getFunctionType(
409+
*Modified, proto->getParamTypes(), proto->getExtProtoInfo()));
410+
else
411+
Function->setType(
412+
Context.getFunctionNoProtoType(*Modified, FnType->getExtInfo()));
413+
}
414+
} else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
415+
if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
416+
Method->setReturnType(*Modified);
417+
418+
// Make it a context-sensitive keyword if we can.
419+
if (!isIndirectPointerType(*Modified))
420+
Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
421+
Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
422+
}
423+
} else if (auto Value = dyn_cast<ValueDecl>(D)) {
424+
if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
425+
Value->setType(*Modified);
426+
427+
// Make it a context-sensitive keyword if we can.
428+
if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
429+
if (Parm->isObjCMethodParameter() && !isIndirectPointerType(*Modified))
430+
Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
431+
Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
432+
}
433+
}
434+
} else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
435+
if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
436+
Property->setType(*Modified, Property->getTypeSourceInfo());
437+
438+
// Make it a property attribute if we can.
439+
if (!isIndirectPointerType(*Modified))
440+
Property->setPropertyAttributes(
441+
ObjCPropertyAttribute::kind_null_resettable);
398442
}
399443
}
444+
}
445+
446+
/// Process API notes for a variable or property.
447+
static void ProcessAPINotes(Sema &S, Decl *D,
448+
const api_notes::VariableInfo &Info,
449+
VersionedInfoMetadata Metadata) {
450+
// Type override.
451+
applyAPINotesType(S, D, Info.getType(), Metadata);
400452

401453
// Nullability.
402454
if (auto Nullability = Info.getNullability())
@@ -814,7 +866,8 @@ static void ProcessVersionedAPINotes(
814866
Sema &S, SpecificDecl *D,
815867
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
816868

817-
maybeAttachUnversionedSwiftName(S, D, Info);
869+
if (!S.captureSwiftVersionIndependentAPINotes())
870+
maybeAttachUnversionedSwiftName(S, D, Info);
818871

819872
unsigned Selected = Info.getSelected().value_or(Info.size());
820873

@@ -824,10 +877,18 @@ static void ProcessVersionedAPINotes(
824877
std::tie(Version, InfoSlice) = Info[i];
825878
auto Active = (i == Selected) ? IsActive_t::Active : IsActive_t::Inactive;
826879
auto Replacement = IsSubstitution_t::Original;
827-
if (Active == IsActive_t::Inactive && Version.empty()) {
880+
881+
// When collection all APINotes as version-independent,
882+
// capture all as inactive and defer to the client select the
883+
// right one.
884+
if (S.captureSwiftVersionIndependentAPINotes()) {
885+
Active = IsActive_t::Inactive;
886+
Replacement = IsSubstitution_t::Original;
887+
} else if (Active == IsActive_t::Inactive && Version.empty()) {
828888
Replacement = IsSubstitution_t::Replacement;
829889
Version = Info[Selected].first;
830890
}
891+
831892
ProcessAPINotes(S, D, InfoSlice,
832893
VersionedInfoMetadata(Version, Active, Replacement));
833894
}

0 commit comments

Comments
 (0)