Skip to content

Commit 3b005bf

Browse files
committed
Extend availability attribute to support feature-based availability
This commit extends the `__availability` attribute to accept a feature name and a boolean-like integer argument (0 or 1) to indicate availability based on feature presence or absence. `__builtin_available` and `@available` take a feature name as their arguments. References to annotated declarations in unguarded contexts are rejected. For example: ``` __attribute__((availability(domain:feature1, 0))) void available_func(void); __attribute__((availability(domain:feature1, 1))) void unavailable_func(void); if (__builtin_available(feature1)) { available_func(); unavailable_func(); // error } else { available_func(); // error unavailable_func(); } ``` rdar://137999979
1 parent de6bfa2 commit 3b005bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1348
-27
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/AST/RawCommentList.h"
2626
#include "clang/AST/TemplateName.h"
2727
#include "clang/Basic/LLVM.h"
28+
#include "clang/Basic/LangOptions.h"
2829
#include "clang/Basic/PartialDiagnostic.h"
2930
#include "clang/Basic/SourceLocation.h"
3031
#include "llvm/ADT/DenseMap.h"
@@ -801,6 +802,35 @@ class ASTContext : public RefCountedBase<ASTContext> {
801802
return DiagAllocator;
802803
}
803804

805+
struct AvailabilityDomainInfo {
806+
FeatureAvailKind Kind = FeatureAvailKind::None;
807+
ImplicitCastExpr *Call = nullptr;
808+
bool isInvalid() const { return Kind == FeatureAvailKind::None; }
809+
};
810+
811+
std::map<StringRef, AvailabilityDomainInfo> AvailabilityDomainMap;
812+
813+
void addAvailabilityDomainMap(StringRef Name, AvailabilityDomainInfo Info) {
814+
AvailabilityDomainMap[Name] = Info;
815+
}
816+
817+
std::pair<DomainAvailabilityAttr *, bool>
818+
checkNewFeatureAvailability(Decl *D, StringRef DomainName, bool Unavailable);
819+
820+
bool hasFeatureAvailabilityAttr(const Decl *D) const;
821+
822+
// Retrieve availability domain information for a feature.
823+
AvailabilityDomainInfo getFeatureAvailInfo(StringRef FeatureName) const;
824+
825+
// Retrieve feature name and availability domain information on a decl. If the
826+
// decl doesn't have attribute availability_domain on it, the name will be
827+
// empty and AvailabilityDomainInfo::Kind will be set to
828+
// FeatureAvailKind::None.
829+
std::pair<StringRef, AvailabilityDomainInfo>
830+
getFeatureAvailInfo(Decl *D) const;
831+
832+
bool hasUnavailableFeature(const Decl *D) const;
833+
804834
const TargetInfo &getTargetInfo() const { return *Target; }
805835
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }
806836

clang/include/clang/AST/Availability.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class AvailabilitySpec {
3737
/// Name of the platform that Version corresponds to.
3838
StringRef Platform;
3939

40+
StringRef DomainName;
41+
4042
SourceLocation BeginLoc, EndLoc;
4143

4244
public:
@@ -45,6 +47,9 @@ class AvailabilitySpec {
4547
: Version(Version), Platform(Platform), BeginLoc(BeginLoc),
4648
EndLoc(EndLoc) {}
4749

50+
AvailabilitySpec(StringRef DomainName, SourceLocation Loc)
51+
: DomainName(DomainName), BeginLoc(Loc), EndLoc(Loc) {}
52+
4853
/// This constructor is used when representing the '*' case.
4954
AvailabilitySpec(SourceLocation StarLoc)
5055
: BeginLoc(StarLoc), EndLoc(StarLoc) {}
@@ -56,6 +61,9 @@ class AvailabilitySpec {
5661

5762
/// Returns true when this represents the '*' case.
5863
bool isOtherPlatformSpec() const { return Version.empty(); }
64+
65+
bool isDomainName() const { return !DomainName.empty(); }
66+
StringRef getDomainName() const { return DomainName; }
5967
};
6068

6169
class Decl;

clang/include/clang/AST/ExprObjC.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,7 +1693,9 @@ class ObjCBridgedCastExpr final
16931693
/// be used in the condition of an \c if, but it is also usable as top level
16941694
/// expressions.
16951695
///
1696-
class ObjCAvailabilityCheckExpr : public Expr {
1696+
class ObjCAvailabilityCheckExpr final
1697+
: public Expr,
1698+
private llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char> {
16971699
public:
16981700
struct VersionAsWritten {
16991701
/// Platform version canonicalized for use with availability checks.
@@ -1704,21 +1706,46 @@ class ObjCAvailabilityCheckExpr : public Expr {
17041706

17051707
private:
17061708
friend class ASTStmtReader;
1709+
friend llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char>;
17071710

17081711
VersionAsWritten VersionToCheck;
17091712
SourceLocation AtLoc, RParen;
17101713

1714+
void setHasDomainName(bool V) {
1715+
ObjCAvailabilityCheckExprBits.HasDomainName = V;
1716+
}
1717+
1718+
ObjCAvailabilityCheckExpr(SourceLocation AtLoc, SourceLocation RParen,
1719+
QualType Ty, StringRef DomainName)
1720+
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
1721+
VersionToCheck(), AtLoc(AtLoc), RParen(RParen) {
1722+
setDependence(ExprDependence::None);
1723+
setHasDomainName(true);
1724+
strcpy(getTrailingObjects<char>(), DomainName.data());
1725+
}
1726+
17111727
public:
17121728
ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck,
17131729
SourceLocation AtLoc,
17141730
SourceLocation RParen, QualType Ty)
17151731
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
17161732
VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {
17171733
setDependence(ExprDependence::None);
1734+
setHasDomainName(false);
17181735
}
17191736

1737+
static ObjCAvailabilityCheckExpr *
1738+
CreateAvailabilityFeatureCheck(SourceLocation AtLoc, SourceLocation RParen,
1739+
QualType Ty, StringRef DomainName,
1740+
const ASTContext &C);
1741+
17201742
explicit ObjCAvailabilityCheckExpr(EmptyShell Shell)
1721-
: Expr(ObjCAvailabilityCheckExprClass, Shell) {}
1743+
: Expr(ObjCAvailabilityCheckExprClass, Shell) {
1744+
setHasDomainName(false);
1745+
}
1746+
1747+
static ObjCAvailabilityCheckExpr *
1748+
CreateEmpty(const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen);
17221749

17231750
SourceLocation getBeginLoc() const { return AtLoc; }
17241751
SourceLocation getEndLoc() const { return RParen; }
@@ -1731,6 +1758,14 @@ class ObjCAvailabilityCheckExpr : public Expr {
17311758
return VersionToCheck.SourceVersion;
17321759
}
17331760

1761+
bool hasDomainName() const {
1762+
return ObjCAvailabilityCheckExprBits.HasDomainName;
1763+
}
1764+
StringRef getDomainName() const {
1765+
assert(hasDomainName());
1766+
return getTrailingObjects<char>();
1767+
}
1768+
17341769
child_range children() {
17351770
return child_range(child_iterator(), child_iterator());
17361771
}

clang/include/clang/AST/Stmt.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,15 @@ class alignas(void *) Stmt {
12261226
unsigned ShouldCopy : 1;
12271227
};
12281228

1229+
class ObjCAvailabilityCheckExprBitfields {
1230+
friend class ObjCAvailabilityCheckExpr;
1231+
LLVM_PREFERRED_TYPE(ExprBitfields)
1232+
unsigned : NumExprBits;
1233+
1234+
LLVM_PREFERRED_TYPE(bool)
1235+
unsigned HasDomainName : 1;
1236+
};
1237+
12291238
//===--- Clang Extensions bitfields classes ---===//
12301239

12311240
class OpaqueValueExprBitfields {
@@ -1324,6 +1333,7 @@ class alignas(void *) Stmt {
13241333

13251334
// Obj-C Expressions
13261335
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
1336+
ObjCAvailabilityCheckExprBitfields ObjCAvailabilityCheckExprBits;
13271337

13281338
// Clang Extensions
13291339
OpaqueValueExprBitfields OpaqueValueExprBits;

clang/include/clang/Basic/Attr.td

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,25 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm
11241124
let Documentation = [AvailabilityDocs];
11251125
}
11261126

1127+
def DomainAvailability : InheritableAttr {
1128+
// This attribute has no spellings as it is created implicitly when
1129+
// __attribute__((availability(domain:...))) is seen.
1130+
let Spellings = [Clang<"">];
1131+
let Args = [StringArgument<"domain">, BoolArgument<"unavailable">];
1132+
let AdditionalMembers = [{
1133+
std::string getFeatureAttributeStr() const;
1134+
}];
1135+
let Documentation = [Undocumented];
1136+
}
1137+
1138+
// This attribute is used to annotate structs of type `_AvailabilityDomain`.
1139+
def AvailabilityDomain : InheritableAttr {
1140+
let Spellings = [Clang<"availability_domain">];
1141+
let Args = [IdentifierArgument<"name">];
1142+
let Subjects = SubjectList<[Var]>;
1143+
let Documentation = [Undocumented];
1144+
}
1145+
11271146
def ExternalSourceSymbol : InheritableAttr {
11281147
let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1,
11291148
/*version=*/20230206>];

clang/include/clang/Basic/DiagnosticFrontendKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,9 @@ def err_fe_invalid_exception_model
144144
def err_fe_invalid_source_date_epoch : Error<
145145
"environment variable 'SOURCE_DATE_EPOCH' ('%0') must be a non-negative decimal integer <= %1">;
146146

147+
def err_feature_availability_flag_invalid_value : Error<
148+
"invalid value '%0' passed to '-ffeature-availability='; expected <feature>:<on|off>">;
149+
147150
def err_fe_incompatible_option_with_remote_cache : Error<
148151
"'%0' is incompatible with remote caching backend">;
149152
def err_fe_unable_to_load_include_tree : Error<

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,11 @@ def err_availability_expected_platform : Error<
11221122
def err_availability_expected_environment : Error<
11231123
"expected an environment name, e.g., 'compute'">;
11241124

1125+
def err_features_domain_name : Error<
1126+
"expected a domain name">;
1127+
def err_feature_invalid_availability_check : Error<
1128+
"cannot pass a domain argument along with other arguments">;
1129+
11251130
// objc_bridge_related attribute
11261131
def err_objcbridge_related_expected_related_class : Error<
11271132
"expected a related Objective-C class name, e.g., 'NSColor'">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3960,6 +3960,28 @@ def err_availability_unexpected_parameter: Error<
39603960
def warn_unguarded_availability :
39613961
Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
39623962
InGroup<UnguardedAvailability>, DefaultIgnore;
3963+
def err_unguarded_feature : Error<
3964+
"use of %0 requires feature '%1' to be %select{available|unavailable}2">;
3965+
def err_label_in_conditionally_guarded_feature : Error<
3966+
"labels cannot appear in regions conditionally guarded by features">;
3967+
def err_features_invalid_for_decl : Error<
3968+
"feature attributes cannot be applied to %0">;
3969+
def err_features_invalid_name : Error<
3970+
"cannot find definition of feature attribute '%0'">;
3971+
def err_features_invalid__arg : Error<
3972+
"invalid argument %0: must evaluate to 0 or 1">;
3973+
def err_feature_invalid_for_decl : Error<
3974+
"feature attribute '%0(%1)' cannot be applied to this decl">;
3975+
def err_feature_merge_incompatible : Error<
3976+
"cannot merge incompatible feature attribute to this decl">;
3977+
def err_new_feature_redeclaration : Error<
3978+
"new feature attributes cannot be added to redeclarations">;
3979+
def err_feature_invalid_added : Error<
3980+
"cannot add feature availability to this decl">;
3981+
def note_feature_incompatible0 : Note<
3982+
"feature attribute %0">;
3983+
def note_feature_incompatible1 : Note<
3984+
"is incompatible with %0">;
39633985
def warn_unguarded_availability_unavailable :
39643986
Warning<"%0 is unavailable">,
39653987
InGroup<UnguardedAvailability>, DefaultIgnore;

clang/include/clang/Basic/LangOptions.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ enum class PointerAuthenticationMode : unsigned {
6464
SignAndAuth
6565
};
6666

67+
enum class FeatureAvailKind { None, Available, Unavailable, Dynamic };
68+
6769
/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
6870
/// this large collection of bitfields is a trivial class type.
6971
class LangOptionsBase {
@@ -544,6 +546,8 @@ class LangOptions : public LangOptionsBase {
544546
/// This list is sorted.
545547
std::vector<std::string> ModuleFeatures;
546548

549+
std::vector<std::string> FeatureAvailability;
550+
547551
/// Options for parsing comments.
548552
CommentOptions CommentOpts;
549553

@@ -561,6 +565,16 @@ class LangOptions : public LangOptionsBase {
561565
/// host code generation.
562566
std::string OMPHostIRFile;
563567

568+
llvm::StringMap<FeatureAvailKind> FeatureAvailabilityMap;
569+
570+
void setFeatureAvailability(StringRef Feature, FeatureAvailKind Kind) {
571+
FeatureAvailabilityMap[Feature] = Kind;
572+
}
573+
574+
FeatureAvailKind getFeatureAvailability(StringRef Feature) const {
575+
return FeatureAvailabilityMap.lookup(Feature);
576+
}
577+
564578
/// The user provided compilation unit ID, if non-empty. This is used to
565579
/// externalize static variables which is needed to support accessing static
566580
/// device variables in host code for single source offloading languages

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,6 +3433,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group<f_Group>,
34333433
def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group<f_Group>,
34343434
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
34353435
HelpText<"Disable implicit builtin knowledge of a specific function">;
3436+
def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group<f_Group>,
3437+
Visibility<[CC1Option]>,
3438+
HelpText<"feature availability">,
3439+
MarshallingInfoStringVector<LangOpts<"FeatureAvailability">>;
34363440
def fno_common : Flag<["-"], "fno-common">, Group<f_Group>,
34373441
Visibility<[ClangOption, CC1Option]>,
34383442
HelpText<"Compile common globals like normal definitions">;

clang/include/clang/Parse/Parser.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3127,6 +3127,12 @@ class Parser : public CodeCompletionHandler {
31273127
void ParseHLSLQualifiers(ParsedAttributes &Attrs);
31283128

31293129
VersionTuple ParseVersionTuple(SourceRange &Range);
3130+
void ParseFeatureAvailabilityAttribute(
3131+
IdentifierInfo &Availability, SourceLocation AvailabilityLoc,
3132+
ParsedAttributes &attrs, SourceLocation *endLoc,
3133+
IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Form Form,
3134+
BalancedDelimiterTracker &T);
3135+
31303136
void ParseAvailabilityAttribute(IdentifierInfo &Availability,
31313137
SourceLocation AvailabilityLoc,
31323138
ParsedAttributes &attrs,

clang/include/clang/Sema/DelayedDiagnostic.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,12 @@ class AccessedEntity {
125125
/// the complete parsing of the current declaration.
126126
class DelayedDiagnostic {
127127
public:
128-
enum DDKind : unsigned char { Availability, Access, ForbiddenType };
128+
enum DDKind : unsigned char {
129+
Availability,
130+
FeatureAvailability,
131+
Access,
132+
ForbiddenType
133+
};
129134

130135
DDKind Kind;
131136
bool Triggered;
@@ -143,6 +148,9 @@ class DelayedDiagnostic {
143148
StringRef Msg,
144149
bool ObjCPropertyAccess);
145150

151+
static DelayedDiagnostic
152+
makeFeatureAvailability(NamedDecl *D, ArrayRef<SourceLocation> Locs);
153+
146154
static DelayedDiagnostic makeAccess(SourceLocation Loc,
147155
const AccessedEntity &Entity) {
148156
DelayedDiagnostic DD;
@@ -201,6 +209,12 @@ class DelayedDiagnostic {
201209
return AvailabilityData.AR;
202210
}
203211

212+
const NamedDecl *getFeatureAvailabilityDecl() const {
213+
assert(Kind == FeatureAvailability &&
214+
"Not a feature availability diagnostic.");
215+
return FeatureAvailabilityData.Decl;
216+
}
217+
204218
/// The diagnostic ID to emit. Used like so:
205219
/// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic())
206220
/// << diag.getForbiddenTypeOperand()
@@ -246,6 +260,10 @@ class DelayedDiagnostic {
246260
bool ObjCPropertyAccess;
247261
};
248262

263+
struct FAD {
264+
const NamedDecl *Decl;
265+
};
266+
249267
struct FTD {
250268
unsigned Diagnostic;
251269
unsigned Argument;
@@ -254,6 +272,7 @@ class DelayedDiagnostic {
254272

255273
union {
256274
struct AD AvailabilityData;
275+
struct FAD FeatureAvailabilityData;
257276
struct FTD ForbiddenTypeData;
258277

259278
/// Access control.

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ class ParsedAttr final
363363
return IsProperty;
364364
}
365365

366+
bool isAvailabilityAttribute() const { return IsAvailability; }
367+
366368
bool isInvalid() const { return Invalid; }
367369
void setInvalid(bool b = true) const { Invalid = b; }
368370

clang/include/clang/Sema/ScopeInfo.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class FunctionScopeInfo {
144144
/// unavailable.
145145
bool HasPotentialAvailabilityViolations : 1;
146146

147+
bool HasPotentialFeatureAvailabilityViolations : 1;
148+
147149
/// A flag that is set when parsing a method that must call super's
148150
/// implementation, such as \c -dealloc, \c -finalize, or any method marked
149151
/// with \c __attribute__((objc_requires_super)).
@@ -394,7 +396,8 @@ class FunctionScopeInfo {
394396
HasBranchIntoScope(false), HasIndirectGoto(false), HasMustTail(false),
395397
HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false),
396398
HasFallthroughStmt(false), UsesFPIntrin(false),
397-
HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false),
399+
HasPotentialAvailabilityViolations(false),
400+
HasPotentialFeatureAvailabilityViolations(false), ObjCShouldCallSuper(false),
398401
ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false),
399402
ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false),
400403
NeedsCoroutineSuspends(true), FoundImmediateEscalatingExpression(false),

0 commit comments

Comments
 (0)