Skip to content

Commit 0078e85

Browse files
authored
Merge pull request #10983 from artemcm/artemcm/VersionIndependentVersionedAPINotesCherryPick
[Upstream 🍒][APINotes] Add support for capturing all possible versioned APINotes without applying them
2 parents 30770f0 + 7a78798 commit 0078e85

File tree

9 files changed

+226
-79
lines changed

9 files changed

+226
-79
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
@@ -2987,6 +2987,26 @@ def Regparm : TypeAttr {
29872987
let ASTNode = 0;
29882988
}
29892989

2990+
def SwiftType : Attr {
2991+
// This attribute has no spellings as it is only ever created implicitly
2992+
// from API notes.
2993+
let Spellings = [];
2994+
let Args = [StringArgument<"TypeString">];
2995+
let SemaHandler = 0;
2996+
let Documentation = [InternalOnly];
2997+
}
2998+
2999+
def SwiftNullability : Attr {
3000+
// This attribute has no spellings as it is only ever created implicitly
3001+
// from API notes.
3002+
let Spellings = [];
3003+
let Args = [EnumArgument<"Kind", "Kind", /*is_string=*/false,
3004+
["non_null", "nullable", "unspecified", "nullable_result"],
3005+
["NonNull", "Nullable", "Unspecified", "NullableResult"]>];
3006+
let SemaHandler = 0;
3007+
let Documentation = [InternalOnly];
3008+
}
3009+
29903010
def SwiftAsyncName : InheritableAttr {
29913011
let Spellings = [GNU<"swift_async_name">];
29923012
let Args = [StringArgument<"Name">];

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments fr
438438
LANGOPT(APINotes, 1, 0, "use external API notes")
439439
LANGOPT(APINotesModules, 1, 0, "use module-based external API notes")
440440
LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch")
441+
LANGOPT(SwiftVersionIndependentAPINotes, 1, 0, "use external API notes capturing all versions")
441442

442443
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
443444
"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
@@ -1934,6 +1934,12 @@ defm apinotes_modules : BoolOption<"f", "apinotes-modules",
19341934
NegFlag<SetFalse, [], [ClangOption], "Disable">,
19351935
BothFlags<[], [ClangOption, CC1Option], " module-based external API notes support">>,
19361936
Group<f_clang_Group>;
1937+
defm swift_version_independent_apinotes : BoolOption<"f", "swift-version-independent-apinotes",
1938+
LangOpts<"SwiftVersionIndependentAPINotes">, DefaultFalse,
1939+
PosFlag<SetTrue, [], [ClangOption], "Enable">,
1940+
NegFlag<SetFalse, [], [ClangOption], "Disable">,
1941+
BothFlags<[], [ClangOption, CC1Option], " version-independent external API notes support">>,
1942+
Group<f_clang_Group>;
19371943
def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">,
19381944
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
19391945
MetaVarName<"<version>">,

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,7 +1416,6 @@ class Sema final : public SemaBase {
14161416
// -------------------------------------------------------------------------
14171417
//
14181418
//
1419-
14201419
/// \name C++ Access Control
14211420
/// Implementations are in SemaAccess.cpp
14221421
///@{
@@ -15543,7 +15542,17 @@ class Sema final : public SemaBase {
1554315542
///
1554415543
/// Triggered by declaration-attribute processing.
1554515544
void ProcessAPINotes(Decl *D);
15546-
15545+
/// Apply the 'Nullability:' annotation to the specified declaration
15546+
void ApplyNullability(Decl *D, NullabilityKind Nullability);
15547+
/// Apply the 'Type:' annotation to the specified declaration
15548+
void ApplyAPINotesType(Decl *D, StringRef TypeString);
15549+
15550+
/// Whether APINotes should be gathered for all applicable Swift language
15551+
/// versions, without being applied. Leaving clients of the current module
15552+
/// to select and apply the correct version.
15553+
bool captureSwiftVersionIndependentAPINotes() {
15554+
return APINotes.captureVersionIndependentSwift();
15555+
}
1554715556
///@}
1554815557

1554915558
//

clang/lib/APINotes/APINotesManager.cpp

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

5353
APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts)
54-
: SM(SM), ImplicitAPINotes(LangOpts.APINotes) {}
54+
: SM(SM), ImplicitAPINotes(LangOpts.APINotes),
55+
VersionIndependentSwift(LangOpts.SwiftVersionIndependentAPINotes) {}
5556

5657
APINotesManager::~APINotesManager() {
5758
// 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
@@ -7214,6 +7214,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job,
72147214
Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
72157215
}
72167216

7217+
if (Args.hasFlag(options::OPT_fswift_version_independent_apinotes,
7218+
options::OPT_fno_swift_version_independent_apinotes, false))
7219+
CmdArgs.push_back("-fswift-version-independent-apinotes");
7220+
72177221
// -fblocks=0 is default.
72187222
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
72197223
TC.IsBlocksDefault()) ||

clang/lib/Sema/SemaAPINotes.cpp

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

56-
/// Apply nullability to the given declaration.
57-
static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability,
58-
VersionedInfoMetadata Metadata) {
59-
if (!Metadata.IsActive)
60-
return;
61-
62-
auto GetModified =
63-
[&](Decl *D, QualType QT,
64-
NullabilityKind Nullability) -> std::optional<QualType> {
65-
QualType Original = QT;
66-
S.CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
67-
isa<ParmVarDecl>(D),
68-
/*OverrideExisting=*/true);
69-
return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
70-
: std::nullopt;
71-
};
56+
static void applyAPINotesType(Sema &S, Decl *decl, StringRef typeString,
57+
VersionedInfoMetadata metadata) {
58+
if (typeString.empty())
7259

73-
if (auto Function = dyn_cast<FunctionDecl>(D)) {
74-
if (auto Modified =
75-
GetModified(D, Function->getReturnType(), Nullability)) {
76-
const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
77-
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(FnType))
78-
Function->setType(S.Context.getFunctionType(
79-
*Modified, proto->getParamTypes(), proto->getExtProtoInfo()));
80-
else
81-
Function->setType(
82-
S.Context.getFunctionNoProtoType(*Modified, FnType->getExtInfo()));
83-
}
84-
} else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
85-
if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
86-
Method->setReturnType(*Modified);
60+
return;
8761

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

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

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

@@ -363,42 +358,99 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc,
363358
return false;
364359
}
365360

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

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

403455
// Nullability.
404456
if (auto Nullability = Info.getNullability())
@@ -892,7 +944,8 @@ static void ProcessVersionedAPINotes(
892944
Sema &S, SpecificDecl *D,
893945
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
894946

895-
maybeAttachUnversionedSwiftName(S, D, Info);
947+
if (!S.captureSwiftVersionIndependentAPINotes())
948+
maybeAttachUnversionedSwiftName(S, D, Info);
896949

897950
unsigned Selected = Info.getSelected().value_or(Info.size());
898951

@@ -902,10 +955,18 @@ static void ProcessVersionedAPINotes(
902955
std::tie(Version, InfoSlice) = Info[i];
903956
auto Active = (i == Selected) ? IsActive_t::Active : IsActive_t::Inactive;
904957
auto Replacement = IsSubstitution_t::Original;
905-
if (Active == IsActive_t::Inactive && Version.empty()) {
958+
959+
// When collection all APINotes as version-independent,
960+
// capture all as inactive and defer to the client select the
961+
// right one.
962+
if (S.captureSwiftVersionIndependentAPINotes()) {
963+
Active = IsActive_t::Inactive;
964+
Replacement = IsSubstitution_t::Original;
965+
} else if (Active == IsActive_t::Inactive && Version.empty()) {
906966
Replacement = IsSubstitution_t::Replacement;
907967
Version = Info[Selected].first;
908968
}
969+
909970
ProcessAPINotes(S, D, InfoSlice,
910971
VersionedInfoMetadata(Version, Active, Replacement));
911972
}

0 commit comments

Comments
 (0)