Skip to content

Commit 7ee2b42

Browse files
committed
[WIP] Add support for capturing all possible versioned APINotes without applying them
Leaving it up to the client to select the appropriate version to apply
1 parent d1d2bfa commit 7ee2b42

File tree

8 files changed

+195
-83
lines changed

8 files changed

+195
-83
lines changed

clang/include/clang/APINotes/APINotesManager.h

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

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

@@ -159,6 +166,8 @@ class APINotesManager {
159166

160167
/// Find the API notes readers that correspond to the given source location.
161168
llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
169+
170+
bool captureVersionIndependentSwift() { return VersionIndependentSwift; }
162171
};
163172

164173
} // end namespace api_notes

clang/include/clang/Basic/Attr.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,6 +2523,26 @@ def SwiftVersionedRemoval : Attr {
25232523
}];
25242524
}
25252525

2526+
def SwiftType : Attr {
2527+
// This attribute has no spellings as it is only ever created implicitly
2528+
// from API notes.
2529+
let Spellings = [];
2530+
let Args = [StringArgument<"TypeString">];
2531+
let SemaHandler = 0;
2532+
let Documentation = [InternalOnly];
2533+
}
2534+
2535+
def SwiftNullability : Attr {
2536+
// This attribute has no spellings as it is only ever created implicitly
2537+
// from API notes.
2538+
let Spellings = [];
2539+
let Args = [EnumArgument<"Kind", "Kind",
2540+
["non_null", "nullable", "unspecified", "nullable_result"],
2541+
["NonNull", "Nullable", "Unspecified", "NullableResult"]>];
2542+
let SemaHandler = 0;
2543+
let Documentation = [InternalOnly];
2544+
}
2545+
25262546
def SwiftAsyncName : InheritableAttr {
25272547
let Spellings = [GNU<"swift_async_name">];
25282548
let Args = [StringArgument<"Name">];

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ LANGOPT(XLPragmaPack, 1, 0, "IBM XL #pragma pack handling")
410410
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
411411
LANGOPT(APINotes, 1, 0, "use external API notes")
412412
LANGOPT(APINotesModules, 1, 0, "use external API notes")
413+
LANGOPT(SwiftVersionIndependentAPINotes, 1, 0, "use external API notes capturing all versions")
413414
LANGOPT(NeededByPCHOrCompilationUsesPCH, 1, 0, "compilation involves pch")
414415

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

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,11 @@ defm apinotes_modules : BoolOption<"f", "apinotes-modules",
15201520
PosFlag<SetTrue, [], "Enable">, NegFlag<SetFalse, [], "Disable">,
15211521
BothFlags<[CC1Option], " module-based external API notes support">>,
15221522
Group<f_clang_Group>;
1523+
defm swift_version_independent_apinotes : BoolOption<"f", "swift-version-independent-apinotes",
1524+
LangOpts<"SwiftVersionIndependentAPINotes">, DefaultFalse,
1525+
PosFlag<SetTrue, [], "Enable">, NegFlag<SetFalse, [], "Disable">,
1526+
BothFlags<[CC1Option], " version-independent external API notes support">>,
1527+
Group<f_clang_Group>;
15231528
def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">,
15241529
Group<i_Group>, Flags<[NoXarchOption]>, MetaVarName<"<directory>">,
15251530
HelpText<"Does nothing; API notes are no longer cached separately from modules">;

clang/include/clang/Sema/Sema.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4750,6 +4750,15 @@ class Sema final {
47504750
///
47514751
/// Triggered by declaration-attribute processing.
47524752
void ProcessAPINotes(Decl *D);
4753+
void ApplyNullability(Decl *D, NullabilityKind Nullability);
4754+
void ApplyAPINotesType(Decl *D, StringRef TypeString);
4755+
4756+
/// Whether APINotes should be gathered for all
4757+
/// applicable Swift versions, without being applied. Leaving
4758+
/// clients of the current module to apply the correct version.
4759+
bool captureSwiftVersionIndependentAPINotes() {
4760+
return APINotes.captureVersionIndependentSwift();
4761+
}
47534762

47544763
/// Determine if type T is a valid subject for a nonnull and similar
47554764
/// attributes. By default, we look through references (the behavior used by

clang/lib/APINotes/APINotesManager.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ namespace {
6565

6666
APINotesManager::APINotesManager(SourceManager &sourceMgr,
6767
const LangOptions &langOpts)
68-
: SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes) { }
68+
: SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes),
69+
VersionIndependentSwift(langOpts.SwiftVersionIndependentAPINotes) { }
6970

7071
APINotesManager::~APINotesManager() {
7172
// 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
@@ -6823,6 +6823,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job,
68236823
Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
68246824
}
68256825

6826+
if (Args.hasFlag(options::OPT_fswift_version_independent_apinotes,
6827+
options::OPT_fno_swift_version_independent_apinotes, false))
6828+
CmdArgs.push_back("-fswift-version-independent-apinotes");
6829+
68266830
// -fblocks=0 is default.
68276831
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
68286832
TC.IsBlocksDefault()) ||

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 145 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -51,75 +51,57 @@ static bool isMultiLevelPointerType(QualType type) {
5151
pointee->isMemberPointerType();
5252
}
5353

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

60-
QualType type;
61-
62-
// Nullability for a function/method appertains to the retain type.
63-
if (auto function = dyn_cast<FunctionDecl>(decl)) {
64-
type = function->getReturnType();
65-
} else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
66-
type = method->getReturnType();
67-
} else if (auto value = dyn_cast<ValueDecl>(decl)) {
68-
type = value->getType();
69-
} else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
70-
type = property->getType();
59+
// Version-independent APINotes add "type" annotations
60+
// with a versioned attribute for the client to select and apply.
61+
if (S.captureSwiftVersionIndependentAPINotes()) {
62+
auto *typeAttr = SwiftTypeAttr::CreateImplicit(
63+
S.Context, typeString);
64+
auto *versioned = SwiftVersionedAttr::CreateImplicit(
65+
S.Context, metadata.Version, typeAttr, metadata.IsReplacement);
66+
decl->addAttr(versioned);
7167
} else {
72-
return;
68+
if (!metadata.IsActive)
69+
return;
70+
S.ApplyAPINotesType(decl, typeString);
7371
}
72+
}
7473

75-
// Check the nullability specifier on this type.
76-
QualType origType = type;
77-
S.checkImplicitNullabilityTypeSpecifier(type, nullability,
78-
decl->getLocation(),
79-
isa<ParmVarDecl>(decl),
80-
/*overrideExisting=*/true);
81-
if (type.getTypePtr() == origType.getTypePtr())
82-
return;
83-
84-
if (auto function = dyn_cast<FunctionDecl>(decl)) {
85-
const FunctionType *fnType = function->getType()->castAs<FunctionType>();
86-
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
87-
function->setType(S.Context.getFunctionType(type, proto->getParamTypes(),
88-
proto->getExtProtoInfo()));
89-
} else {
90-
function->setType(S.Context.getFunctionNoProtoType(type,
91-
fnType->getExtInfo()));
92-
}
93-
} else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
94-
method->setReturnType(type);
95-
96-
// Make it a context-sensitive keyword if we can.
97-
if (!isMultiLevelPointerType(type)) {
98-
method->setObjCDeclQualifier(
99-
Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() |
100-
Decl::OBJC_TQ_CSNullability));
101-
}
102-
} else if (auto value = dyn_cast<ValueDecl>(decl)) {
103-
value->setType(type);
104-
105-
// Make it a context-sensitive keyword if we can.
106-
if (auto parm = dyn_cast<ParmVarDecl>(decl)) {
107-
if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) {
108-
parm->setObjCDeclQualifier(
109-
Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() |
110-
Decl::OBJC_TQ_CSNullability));
111-
}
112-
}
113-
} else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
114-
property->setType(type, property->getTypeSourceInfo());
115-
116-
// Make it a property attribute if we can.
117-
if (!isMultiLevelPointerType(type)) {
118-
property->setPropertyAttributes(
119-
ObjCPropertyAttribute::kind_null_resettable);
74+
static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
75+
VersionedInfoMetadata metadata) {
76+
// Version-independent APINotes add "nullability" annotations
77+
// with a versioned attribute for the client to select and apply.
78+
if (S.captureSwiftVersionIndependentAPINotes()) {
79+
SwiftNullabilityAttr::Kind attrNullabilityKind;
80+
switch (nullability) {
81+
case NullabilityKind::NonNull:
82+
attrNullabilityKind = SwiftNullabilityAttr::Kind::NonNull;
83+
break;
84+
case NullabilityKind::Nullable:
85+
attrNullabilityKind = SwiftNullabilityAttr::Kind::Nullable;
86+
break;
87+
case NullabilityKind::Unspecified:
88+
attrNullabilityKind = SwiftNullabilityAttr::Kind::Unspecified;
89+
break;
90+
case NullabilityKind::NullableResult:
91+
attrNullabilityKind = SwiftNullabilityAttr::Kind::NullableResult;
92+
break;
12093
}
94+
auto *nullabilityAttr = SwiftNullabilityAttr::CreateImplicit(
95+
S.Context, attrNullabilityKind);
96+
auto *versioned = SwiftVersionedAttr::CreateImplicit(
97+
S.Context, metadata.Version, nullabilityAttr, metadata.IsReplacement);
98+
decl->addAttr(versioned);
99+
return;
121100
} else {
122-
llvm_unreachable("cannot handle nullability here");
101+
if (!metadata.IsActive)
102+
return;
103+
104+
S.ApplyNullability(decl, nullability);
123105
}
124106
}
125107

@@ -376,37 +358,33 @@ static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc,
376358
return false;
377359
}
378360

379-
/// Process API notes for a variable or property.
380-
static void ProcessAPINotes(Sema &S, Decl *D,
381-
const api_notes::VariableInfo &info,
382-
VersionedInfoMetadata metadata) {
383-
// Type override.
384-
if (metadata.IsActive && !info.getType().empty() &&
385-
S.ParseTypeFromStringCallback) {
386-
auto parsedType = S.ParseTypeFromStringCallback(info.getType(),
387-
"<API Notes>",
388-
D->getLocation());
361+
void Sema::ApplyAPINotesType(Decl *D, StringRef TypeString) {
362+
if (!TypeString.empty() &&
363+
ParseTypeFromStringCallback) {
364+
auto parsedType = ParseTypeFromStringCallback(TypeString,
365+
"<API Notes>",
366+
D->getLocation());
389367
if (parsedType.isUsable()) {
390368
QualType type = Sema::GetTypeFromParser(parsedType.get());
391369
auto typeInfo =
392-
S.Context.getTrivialTypeSourceInfo(type, D->getLocation());
370+
Context.getTrivialTypeSourceInfo(type, D->getLocation());
393371

394372
if (auto var = dyn_cast<VarDecl>(D)) {
395373
// Make adjustments to parameter types.
396374
if (isa<ParmVarDecl>(var)) {
397-
type = S.adjustParameterTypeForObjCAutoRefCount(type,
398-
D->getLocation(),
399-
typeInfo);
400-
type = S.Context.getAdjustedParameterType(type);
375+
type = adjustParameterTypeForObjCAutoRefCount(type,
376+
D->getLocation(),
377+
typeInfo);
378+
type = Context.getAdjustedParameterType(type);
401379
}
402380

403-
if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(),
381+
if (!checkAPINotesReplacementType(*this, var->getLocation(), var->getType(),
404382
type)) {
405383
var->setType(type);
406384
var->setTypeSourceInfo(typeInfo);
407385
}
408386
} else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
409-
if (!checkAPINotesReplacementType(S, property->getLocation(),
387+
if (!checkAPINotesReplacementType(*this, property->getLocation(),
410388
property->getType(),
411389
type)) {
412390
property->setType(type, typeInfo);
@@ -416,6 +394,83 @@ static void ProcessAPINotes(Sema &S, Decl *D,
416394
}
417395
}
418396
}
397+
}
398+
399+
// Apply nullability to the given declaration.
400+
void Sema::ApplyNullability(Decl *Decl, NullabilityKind Nullability) {
401+
QualType type;
402+
403+
// Nullability for a function/method appertains to the retain type.
404+
if (auto function = dyn_cast<FunctionDecl>(Decl)) {
405+
type = function->getReturnType();
406+
} else if (auto method = dyn_cast<ObjCMethodDecl>(Decl)) {
407+
type = method->getReturnType();
408+
} else if (auto value = dyn_cast<ValueDecl>(Decl)) {
409+
type = value->getType();
410+
} else if (auto property = dyn_cast<ObjCPropertyDecl>(Decl)) {
411+
type = property->getType();
412+
} else {
413+
return;
414+
}
415+
416+
// Check the nullability specifier on this type.
417+
QualType origType = type;
418+
checkImplicitNullabilityTypeSpecifier(type, Nullability,
419+
Decl->getLocation(),
420+
isa<ParmVarDecl>(Decl),
421+
/*overrideExisting=*/true);
422+
if (type.getTypePtr() == origType.getTypePtr())
423+
return;
424+
425+
if (auto function = dyn_cast<FunctionDecl>(Decl)) {
426+
const FunctionType *fnType = function->getType()->castAs<FunctionType>();
427+
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
428+
function->setType(Context.getFunctionType(type, proto->getParamTypes(),
429+
proto->getExtProtoInfo()));
430+
} else {
431+
function->setType(Context.getFunctionNoProtoType(type,
432+
fnType->getExtInfo()));
433+
}
434+
} else if (auto method = dyn_cast<ObjCMethodDecl>(Decl)) {
435+
method->setReturnType(type);
436+
437+
// Make it a context-sensitive keyword if we can.
438+
if (!isMultiLevelPointerType(type)) {
439+
method->setObjCDeclQualifier(
440+
Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() |
441+
Decl::OBJC_TQ_CSNullability));
442+
}
443+
} else if (auto value = dyn_cast<ValueDecl>(Decl)) {
444+
value->setType(type);
445+
446+
// Make it a context-sensitive keyword if we can.
447+
if (auto parm = dyn_cast<ParmVarDecl>(Decl)) {
448+
if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) {
449+
parm->setObjCDeclQualifier(
450+
Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() |
451+
Decl::OBJC_TQ_CSNullability));
452+
}
453+
}
454+
} else if (auto property = dyn_cast<ObjCPropertyDecl>(Decl)) {
455+
property->setType(type, property->getTypeSourceInfo());
456+
457+
// Make it a property attribute if we can.
458+
if (!isMultiLevelPointerType(type)) {
459+
property->setPropertyAttributes(
460+
ObjCPropertyAttribute::kind_null_resettable);
461+
}
462+
} else {
463+
llvm_unreachable("cannot handle nullability here");
464+
}
465+
}
466+
467+
468+
/// Process API notes for a variable or property.
469+
static void ProcessAPINotes(Sema &S, Decl *D,
470+
const api_notes::VariableInfo &info,
471+
VersionedInfoMetadata metadata) {
472+
// Type override.
473+
applyAPINotesType(S, D, info.getType(), metadata);
419474

420475
// Nullability.
421476
if (auto Nullability = info.getNullability()) {
@@ -802,8 +857,8 @@ template <typename SpecificDecl, typename SpecificInfo>
802857
static void ProcessVersionedAPINotes(
803858
Sema &S, SpecificDecl *D,
804859
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
805-
806-
maybeAttachUnversionedSwiftName(S, D, Info);
860+
if (!S.captureSwiftVersionIndependentAPINotes())
861+
maybeAttachUnversionedSwiftName(S, D, Info);
807862

808863
unsigned Selected = Info.getSelected().value_or(Info.size());
809864

@@ -813,10 +868,18 @@ static void ProcessVersionedAPINotes(
813868
std::tie(Version, InfoSlice) = Info[i];
814869
auto Active = (i == Selected) ? IsActive : IsNotActive;
815870
auto Replacement = IsNotReplacement;
816-
if (Active == IsNotActive && Version.empty()) {
871+
872+
// When collection all APINotes as version-independent,
873+
// capture all as inactive and defer to the client select the
874+
// right one.
875+
if (S.captureSwiftVersionIndependentAPINotes()) {
876+
Active = IsNotActive;
877+
Replacement = IsNotReplacement;
878+
} else if (Active == IsNotActive && Version.empty()) {
817879
Replacement = IsReplacement;
818880
Version = Info[Selected].first;
819881
}
882+
820883
ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active,
821884
Replacement));
822885
}

0 commit comments

Comments
 (0)