Skip to content

Commit 3258f12

Browse files
committed
Add support for capturing all possible versioned APINotes without applying them
1 parent 126f1b5 commit 3258f12

File tree

8 files changed

+197
-77
lines changed

8 files changed

+197
-77
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
@@ -437,6 +437,7 @@ LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments fr
437437

438438
LANGOPT(APINotes, 1, 0, "use external API notes")
439439
LANGOPT(APINotesModules, 1, 0, "use module-based external API notes")
440+
LANGOPT(SwiftVersionIndependentAPINotes, 1, 0, "use external API notes capturing all versions")
440441
LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch")
441442

442443
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "

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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15543,6 +15543,17 @@ class Sema final : public SemaBase {
1554315543
///
1554415544
/// Triggered by declaration-attribute processing.
1554515545
void ProcessAPINotes(Decl *D);
15546+
/// ACTODO: Comment
15547+
void ApplyNullability(Decl *D, NullabilityKind Nullability);
15548+
/// ACTODO: Comment
15549+
void ApplyAPINotesType(Decl *D, StringRef TypeString);
15550+
15551+
/// Whether APINotes should be gathered for all
15552+
/// applicable Swift versions, without being applied. Leaving
15553+
/// clients of the current module to apply the correct version.
15554+
bool captureSwiftVersionIndependentAPINotes() {
15555+
return APINotes.captureVersionIndependentSwift();
15556+
}
1554615557

1554715558
///@}
1554815559

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
@@ -7203,6 +7203,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job,
72037203

72047204
Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
72057205
}
7206+
7207+
if (Args.hasFlag(options::OPT_fswift_version_independent_apinotes,
7208+
options::OPT_fno_swift_version_independent_apinotes, false))
7209+
CmdArgs.push_back("-fswift-version-independent-apinotes");
72067210

72077211
// -fblocks=0 is default.
72087212
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 144 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -53,63 +53,59 @@ 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(
66+
S.Context, typeString);
67+
auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
68+
S.Context, metadata.Version, typeAttr, metadata.IsReplacement);
69+
decl->addAttr(versioned);
70+
} else {
71+
if (!metadata.IsActive)
72+
return;
73+
S.ApplyAPINotesType(decl, typeString);
74+
}
75+
}
9676

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

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

@@ -363,42 +359,105 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation Loc,
363359
return false;
364360
}
365361

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());
362+
void Sema::ApplyAPINotesType(Decl *D, StringRef TypeString) {
363+
if (!TypeString.empty() &&
364+
ParseTypeFromStringCallback) {
365+
auto ParsedType = ParseTypeFromStringCallback(TypeString,
366+
"<API Notes>",
367+
D->getLocation());
375368
if (ParsedType.isUsable()) {
376369
QualType Type = Sema::GetTypeFromParser(ParsedType.get());
377370
auto TypeInfo =
378-
S.Context.getTrivialTypeSourceInfo(Type, D->getLocation());
379-
371+
Context.getTrivialTypeSourceInfo(Type, D->getLocation());
380372
if (auto Var = dyn_cast<VarDecl>(D)) {
381373
// Make adjustments to parameter types.
382374
if (isa<ParmVarDecl>(Var)) {
383-
Type = S.ObjC().AdjustParameterTypeForObjCAutoRefCount(
384-
Type, D->getLocation(), TypeInfo);
385-
Type = S.Context.getAdjustedParameterType(Type);
375+
Type = ObjC().AdjustParameterTypeForObjCAutoRefCount(Type,
376+
D->getLocation(),
377+
TypeInfo);
378+
Type = Context.getAdjustedParameterType(Type);
386379
}
387-
388-
if (!checkAPINotesReplacementType(S, Var->getLocation(), Var->getType(),
380+
381+
if (!checkAPINotesReplacementType(*this, Var->getLocation(),
382+
Var->getType(),
389383
Type)) {
390384
Var->setType(Type);
391385
Var->setTypeSourceInfo(TypeInfo);
392386
}
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
387+
} else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
388+
if (!checkAPINotesReplacementType(*this, property->getLocation(),
389+
property->getType(),
390+
Type)) {
391+
property->setType(Type, TypeInfo);
392+
}
393+
} else {
399394
llvm_unreachable("API notes allowed a type on an unknown declaration");
395+
}
396+
}
397+
}
398+
}
399+
400+
void Sema::ApplyNullability(Decl *D, NullabilityKind Nullability) {
401+
auto GetModified =
402+
[&](class Decl *D, QualType QT,
403+
NullabilityKind Nullability) -> std::optional<QualType> {
404+
QualType Original = QT;
405+
CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
406+
isa<ParmVarDecl>(D),
407+
/*OverrideExisting=*/true);
408+
return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
409+
: std::nullopt;
410+
};
411+
412+
if (auto Function = dyn_cast<FunctionDecl>(D)) {
413+
if (auto Modified =
414+
GetModified(D, Function->getReturnType(), Nullability)) {
415+
const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
416+
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(FnType))
417+
Function->setType(Context.getFunctionType(
418+
*Modified, proto->getParamTypes(), proto->getExtProtoInfo()));
419+
else
420+
Function->setType(
421+
Context.getFunctionNoProtoType(*Modified, FnType->getExtInfo()));
422+
}
423+
} else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
424+
if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
425+
Method->setReturnType(*Modified);
426+
427+
// Make it a context-sensitive keyword if we can.
428+
if (!isIndirectPointerType(*Modified))
429+
Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
430+
Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
431+
}
432+
} else if (auto Value = dyn_cast<ValueDecl>(D)) {
433+
if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
434+
Value->setType(*Modified);
435+
436+
// Make it a context-sensitive keyword if we can.
437+
if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
438+
if (Parm->isObjCMethodParameter() && !isIndirectPointerType(*Modified))
439+
Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
440+
Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
441+
}
442+
}
443+
} else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
444+
if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
445+
Property->setType(*Modified, Property->getTypeSourceInfo());
446+
447+
// Make it a property attribute if we can.
448+
if (!isIndirectPointerType(*Modified))
449+
Property->setPropertyAttributes(
450+
ObjCPropertyAttribute::kind_null_resettable);
400451
}
401452
}
453+
}
454+
455+
/// Process API notes for a variable or property.
456+
static void ProcessAPINotes(Sema &S, Decl *D,
457+
const api_notes::VariableInfo &Info,
458+
VersionedInfoMetadata Metadata) {
459+
// Type override.
460+
applyAPINotesType(S, D, Info.getType(), Metadata);
402461

403462
// Nullability.
404463
if (auto Nullability = Info.getNullability())
@@ -892,7 +951,8 @@ static void ProcessVersionedAPINotes(
892951
Sema &S, SpecificDecl *D,
893952
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
894953

895-
maybeAttachUnversionedSwiftName(S, D, Info);
954+
if (!S.captureSwiftVersionIndependentAPINotes())
955+
maybeAttachUnversionedSwiftName(S, D, Info);
896956

897957
unsigned Selected = Info.getSelected().value_or(Info.size());
898958

@@ -902,10 +962,18 @@ static void ProcessVersionedAPINotes(
902962
std::tie(Version, InfoSlice) = Info[i];
903963
auto Active = (i == Selected) ? IsActive_t::Active : IsActive_t::Inactive;
904964
auto Replacement = IsSubstitution_t::Original;
905-
if (Active == IsActive_t::Inactive && Version.empty()) {
965+
966+
// When collection all APINotes as version-independent,
967+
// capture all as inactive and defer to the client select the
968+
// right one.
969+
if (S.captureSwiftVersionIndependentAPINotes()) {
970+
Active = IsActive_t::Inactive;
971+
Replacement = IsSubstitution_t::Original;
972+
} else if (Active == IsActive_t::Inactive && Version.empty()) {
906973
Replacement = IsSubstitution_t::Replacement;
907974
Version = Info[Selected].first;
908975
}
976+
909977
ProcessAPINotes(S, D, InfoSlice,
910978
VersionedInfoMetadata(Version, Active, Replacement));
911979
}

0 commit comments

Comments
 (0)