Skip to content

Commit bc7b2cd

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 52cf3b4 commit bc7b2cd

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
@@ -26,6 +26,7 @@
2626
#include "clang/AST/SYCLKernelInfo.h"
2727
#include "clang/AST/TemplateName.h"
2828
#include "clang/Basic/LLVM.h"
29+
#include "clang/Basic/LangOptions.h"
2930
#include "clang/Basic/PartialDiagnostic.h"
3031
#include "clang/Basic/SourceLocation.h"
3132
#include "llvm/ADT/DenseMap.h"
@@ -822,6 +823,35 @@ class ASTContext : public RefCountedBase<ASTContext> {
822823
return DiagAllocator;
823824
}
824825

826+
struct AvailabilityDomainInfo {
827+
FeatureAvailKind Kind = FeatureAvailKind::None;
828+
ImplicitCastExpr *Call = nullptr;
829+
bool isInvalid() const { return Kind == FeatureAvailKind::None; }
830+
};
831+
832+
std::map<StringRef, AvailabilityDomainInfo> AvailabilityDomainMap;
833+
834+
void addAvailabilityDomainMap(StringRef Name, AvailabilityDomainInfo Info) {
835+
AvailabilityDomainMap[Name] = Info;
836+
}
837+
838+
std::pair<DomainAvailabilityAttr *, bool>
839+
checkNewFeatureAvailability(Decl *D, StringRef DomainName, bool Unavailable);
840+
841+
bool hasFeatureAvailabilityAttr(const Decl *D) const;
842+
843+
// Retrieve availability domain information for a feature.
844+
AvailabilityDomainInfo getFeatureAvailInfo(StringRef FeatureName) const;
845+
846+
// Retrieve feature name and availability domain information on a decl. If the
847+
// decl doesn't have attribute availability_domain on it, the name will be
848+
// empty and AvailabilityDomainInfo::Kind will be set to
849+
// FeatureAvailKind::None.
850+
std::pair<StringRef, AvailabilityDomainInfo>
851+
getFeatureAvailInfo(Decl *D) const;
852+
853+
bool hasUnavailableFeature(const Decl *D) const;
854+
825855
const TargetInfo &getTargetInfo() const { return *Target; }
826856
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }
827857

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
@@ -1689,7 +1689,9 @@ class ObjCBridgedCastExpr final
16891689
/// be used in the condition of an \c if, but it is also usable as top level
16901690
/// expressions.
16911691
///
1692-
class ObjCAvailabilityCheckExpr : public Expr {
1692+
class ObjCAvailabilityCheckExpr final
1693+
: public Expr,
1694+
private llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char> {
16931695
public:
16941696
struct VersionAsWritten {
16951697
/// Platform version canonicalized for use with availability checks.
@@ -1700,21 +1702,46 @@ class ObjCAvailabilityCheckExpr : public Expr {
17001702

17011703
private:
17021704
friend class ASTStmtReader;
1705+
friend llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char>;
17031706

17041707
VersionAsWritten VersionToCheck;
17051708
SourceLocation AtLoc, RParen;
17061709

1710+
void setHasDomainName(bool V) {
1711+
ObjCAvailabilityCheckExprBits.HasDomainName = V;
1712+
}
1713+
1714+
ObjCAvailabilityCheckExpr(SourceLocation AtLoc, SourceLocation RParen,
1715+
QualType Ty, StringRef DomainName)
1716+
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
1717+
VersionToCheck(), AtLoc(AtLoc), RParen(RParen) {
1718+
setDependence(ExprDependence::None);
1719+
setHasDomainName(true);
1720+
strcpy(getTrailingObjects<char>(), DomainName.data());
1721+
}
1722+
17071723
public:
17081724
ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck,
17091725
SourceLocation AtLoc,
17101726
SourceLocation RParen, QualType Ty)
17111727
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
17121728
VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {
17131729
setDependence(ExprDependence::None);
1730+
setHasDomainName(false);
17141731
}
17151732

1733+
static ObjCAvailabilityCheckExpr *
1734+
CreateAvailabilityFeatureCheck(SourceLocation AtLoc, SourceLocation RParen,
1735+
QualType Ty, StringRef DomainName,
1736+
const ASTContext &C);
1737+
17161738
explicit ObjCAvailabilityCheckExpr(EmptyShell Shell)
1717-
: Expr(ObjCAvailabilityCheckExprClass, Shell) {}
1739+
: Expr(ObjCAvailabilityCheckExprClass, Shell) {
1740+
setHasDomainName(false);
1741+
}
1742+
1743+
static ObjCAvailabilityCheckExpr *
1744+
CreateEmpty(const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen);
17181745

17191746
SourceLocation getBeginLoc() const { return AtLoc; }
17201747
SourceLocation getEndLoc() const { return RParen; }
@@ -1727,6 +1754,14 @@ class ObjCAvailabilityCheckExpr : public Expr {
17271754
return VersionToCheck.SourceVersion;
17281755
}
17291756

1757+
bool hasDomainName() const {
1758+
return ObjCAvailabilityCheckExprBits.HasDomainName;
1759+
}
1760+
StringRef getDomainName() const {
1761+
assert(hasDomainName());
1762+
return getTrailingObjects<char>();
1763+
}
1764+
17301765
child_range children() {
17311766
return child_range(child_iterator(), child_iterator());
17321767
}

clang/include/clang/AST/Stmt.h

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

1250+
class ObjCAvailabilityCheckExprBitfields {
1251+
friend class ObjCAvailabilityCheckExpr;
1252+
LLVM_PREFERRED_TYPE(ExprBitfields)
1253+
unsigned : NumExprBits;
1254+
1255+
LLVM_PREFERRED_TYPE(bool)
1256+
unsigned HasDomainName : 1;
1257+
};
1258+
12501259
//===--- Clang Extensions bitfields classes ---===//
12511260

12521261
class OpaqueValueExprBitfields {
@@ -1360,6 +1369,7 @@ class alignas(void *) Stmt {
13601369

13611370
// Obj-C Expressions
13621371
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
1372+
ObjCAvailabilityCheckExprBitfields ObjCAvailabilityCheckExprBits;
13631373

13641374
// Clang Extensions
13651375
OpaqueValueExprBitfields OpaqueValueExprBits;

clang/include/clang/Basic/Attr.td

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

1181+
def DomainAvailability : InheritableAttr {
1182+
// This attribute has no spellings as it is created implicitly when
1183+
// __attribute__((availability(domain:...))) is seen.
1184+
let Spellings = [Clang<"">];
1185+
let Args = [StringArgument<"domain">, BoolArgument<"unavailable">];
1186+
let AdditionalMembers = [{
1187+
std::string getFeatureAttributeStr() const;
1188+
}];
1189+
let Documentation = [Undocumented];
1190+
}
1191+
1192+
// This attribute is used to annotate structs of type `_AvailabilityDomain`.
1193+
def AvailabilityDomain : InheritableAttr {
1194+
let Spellings = [Clang<"availability_domain">];
1195+
let Args = [IdentifierArgument<"name">];
1196+
let Subjects = SubjectList<[Var]>;
1197+
let Documentation = [Undocumented];
1198+
}
1199+
11811200
def ExternalSourceSymbol : InheritableAttr {
11821201
let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1,
11831202
/*version=*/20230206>];

clang/include/clang/Basic/DiagnosticFrontendKinds.td

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

145+
def err_feature_availability_flag_invalid_value : Error<
146+
"invalid value '%0' passed to '-ffeature-availability='; expected <feature>:<on|off>">;
147+
145148
def err_fe_incompatible_option_with_remote_cache : Error<
146149
"'%0' is incompatible with remote caching backend">;
147150
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
@@ -1167,6 +1167,11 @@ def err_availability_expected_platform : Error<
11671167
def err_availability_expected_environment : Error<
11681168
"expected an environment name, e.g., 'compute'">;
11691169

1170+
def err_features_domain_name : Error<
1171+
"expected a domain name">;
1172+
def err_feature_invalid_availability_check : Error<
1173+
"cannot pass a domain argument along with other arguments">;
1174+
11701175
// objc_bridge_related attribute
11711176
def err_objcbridge_related_expected_related_class : Error<
11721177
"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
@@ -4022,6 +4022,28 @@ def err_availability_unexpected_parameter: Error<
40224022
def warn_unguarded_availability :
40234023
Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
40244024
InGroup<UnguardedAvailability>, DefaultIgnore;
4025+
def err_unguarded_feature : Error<
4026+
"use of %0 requires feature '%1' to be %select{available|unavailable}2">;
4027+
def err_label_in_conditionally_guarded_feature : Error<
4028+
"labels cannot appear in regions conditionally guarded by features">;
4029+
def err_features_invalid_for_decl : Error<
4030+
"feature attributes cannot be applied to %0">;
4031+
def err_features_invalid_name : Error<
4032+
"cannot find definition of feature attribute '%0'">;
4033+
def err_features_invalid__arg : Error<
4034+
"invalid argument %0: must evaluate to 0 or 1">;
4035+
def err_feature_invalid_for_decl : Error<
4036+
"feature attribute '%0(%1)' cannot be applied to this decl">;
4037+
def err_feature_merge_incompatible : Error<
4038+
"cannot merge incompatible feature attribute to this decl">;
4039+
def err_new_feature_redeclaration : Error<
4040+
"new feature attributes cannot be added to redeclarations">;
4041+
def err_feature_invalid_added : Error<
4042+
"cannot add feature availability to this decl">;
4043+
def note_feature_incompatible0 : Note<
4044+
"feature attribute %0">;
4045+
def note_feature_incompatible1 : Note<
4046+
"is incompatible with %0">;
40254047
def warn_unguarded_availability_unavailable :
40264048
Warning<"%0 is unavailable">,
40274049
InGroup<UnguardedAvailability>, DefaultIgnore;

clang/include/clang/Basic/LangOptions.h

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

68+
enum class FeatureAvailKind { None, Available, Unavailable, Dynamic };
69+
6870
/// Bitfields of LangOptions, split out from LangOptions in order to ensure that
6971
/// this large collection of bitfields is a trivial class type.
7072
class LangOptionsBase {
@@ -590,6 +592,8 @@ class LangOptions : public LangOptionsBase {
590592
/// This list is sorted.
591593
std::vector<std::string> ModuleFeatures;
592594

595+
std::vector<std::string> FeatureAvailability;
596+
593597
/// Options for parsing comments.
594598
CommentOptions CommentOpts;
595599

@@ -607,6 +611,16 @@ class LangOptions : public LangOptionsBase {
607611
/// host code generation.
608612
std::string OMPHostIRFile;
609613

614+
llvm::StringMap<FeatureAvailKind> FeatureAvailabilityMap;
615+
616+
void setFeatureAvailability(StringRef Feature, FeatureAvailKind Kind) {
617+
FeatureAvailabilityMap[Feature] = Kind;
618+
}
619+
620+
FeatureAvailKind getFeatureAvailability(StringRef Feature) const {
621+
return FeatureAvailabilityMap.lookup(Feature);
622+
}
623+
610624
/// The user provided compilation unit ID, if non-empty. This is used to
611625
/// externalize static variables which is needed to support accessing static
612626
/// 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
@@ -3486,6 +3486,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group<f_Group>,
34863486
def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group<f_Group>,
34873487
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
34883488
HelpText<"Disable implicit builtin knowledge of a specific function">;
3489+
def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group<f_Group>,
3490+
Visibility<[CC1Option]>,
3491+
HelpText<"feature availability">,
3492+
MarshallingInfoStringVector<LangOpts<"FeatureAvailability">>;
34893493
def fno_common : Flag<["-"], "fno-common">, Group<f_Group>,
34903494
Visibility<[ClangOption, CC1Option]>,
34913495
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
@@ -3138,6 +3138,12 @@ class Parser : public CodeCompletionHandler {
31383138
void ParseHLSLQualifiers(ParsedAttributes &Attrs);
31393139

31403140
VersionTuple ParseVersionTuple(SourceRange &Range);
3141+
void ParseFeatureAvailabilityAttribute(
3142+
IdentifierInfo &Availability, SourceLocation AvailabilityLoc,
3143+
ParsedAttributes &attrs, SourceLocation *endLoc,
3144+
IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Form Form,
3145+
BalancedDelimiterTracker &T);
3146+
31413147
void ParseAvailabilityAttribute(IdentifierInfo &Availability,
31423148
SourceLocation AvailabilityLoc,
31433149
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)