Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for availability-style features #9815

Draft
wants to merge 1 commit into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,32 @@ class ASTContext : public RefCountedBase<ASTContext> {
return DiagAllocator;
}

enum class FeatureAvailKind { None, Available, Unavailable, Dynamic };

struct AvailabilityDomainInfo {
FeatureAvailKind Kind = FeatureAvailKind::None;
ImplicitCastExpr *Call;
bool isInvalid() const { return Kind == FeatureAvailKind::None; }
};

std::map<StringRef, AvailabilityDomainInfo> AvailabilityDomainMap;

void addAvailabilityDomainMap(StringRef Name, AvailabilityDomainInfo Info) {
AvailabilityDomainMap[Name] = Info;
}

std::pair<AvailabilityAttr *, bool>
checkNewFeatureAvailability(Decl *D, StringRef DomainName, bool Unavailable);

llvm::iterator_range<specific_attr_iterator<AvailabilityAttr>>
getFeatureAvailabilityAttrs(const Decl *D) const;

bool hasFeatureAvailabilityAttr(const Decl *D) const;

AvailabilityDomainInfo getFeatureAvailInfo(StringRef FeatureName) const;

bool hasUnavailableFeature(const Decl *D) const;

const TargetInfo &getTargetInfo() const { return *Target; }
const TargetInfo *getAuxTargetInfo() const { return AuxTarget; }

Expand Down
14 changes: 11 additions & 3 deletions clang/include/clang/AST/AttrIterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "llvm/Support/Casting.h"
#include <cassert>
#include <cstddef>
#include <functional>
#include <iterator>
#include <type_traits>

Expand All @@ -44,13 +45,17 @@ class specific_attr_iterator {
/// past-the-end iterator when we move to a past-the-end position.
mutable Iterator Current;

std::function<bool(const SpecificAttr *)> PredFn;

void AdvanceToNext() const {
while (!isa<SpecificAttr>(*Current))
while (!isa<SpecificAttr>(*Current) ||
!PredFn(cast<SpecificAttr>(*Current)))
++Current;
}

void AdvanceToNext(Iterator I) const {
while (Current != I && !isa<SpecificAttr>(*Current))
while (Current != I && (!isa<SpecificAttr>(*Current) ||
!PredFn(cast<SpecificAttr>(*Current))))
++Current;
}

Expand All @@ -62,7 +67,10 @@ class specific_attr_iterator {
using difference_type = std::ptrdiff_t;

specific_attr_iterator() = default;
explicit specific_attr_iterator(Iterator i) : Current(i) {}
explicit specific_attr_iterator(
Iterator i, std::function<bool(const SpecificAttr *)> P =
[](const SpecificAttr *A) { return true; })
: Current(i), PredFn(P) {}

reference operator*() const {
AdvanceToNext();
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/Availability.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class AvailabilitySpec {
/// Name of the platform that Version corresponds to.
StringRef Platform;

StringRef DomainName;

SourceLocation BeginLoc, EndLoc;

public:
Expand All @@ -45,6 +47,9 @@ class AvailabilitySpec {
: Version(Version), Platform(Platform), BeginLoc(BeginLoc),
EndLoc(EndLoc) {}

AvailabilitySpec(StringRef DomainName, SourceLocation Loc)
: DomainName(DomainName), BeginLoc(Loc), EndLoc(Loc) {}

/// This constructor is used when representing the '*' case.
AvailabilitySpec(SourceLocation StarLoc)
: BeginLoc(StarLoc), EndLoc(StarLoc) {}
Expand All @@ -56,6 +61,9 @@ class AvailabilitySpec {

/// Returns true when this represents the '*' case.
bool isOtherPlatformSpec() const { return Version.empty(); }

bool isDomainName() const { return !DomainName.empty(); }
StringRef getDomainName() const { return DomainName; }
};

class Decl;
Expand Down
41 changes: 38 additions & 3 deletions clang/include/clang/AST/ExprObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -1689,7 +1689,9 @@ class ObjCBridgedCastExpr final
/// be used in the condition of an \c if, but it is also usable as top level
/// expressions.
///
class ObjCAvailabilityCheckExpr : public Expr {
class ObjCAvailabilityCheckExpr final
: public Expr,
private llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char> {
public:
struct VersionAsWritten {
/// Platform version canonicalized for use with availability checks.
Expand All @@ -1700,21 +1702,46 @@ class ObjCAvailabilityCheckExpr : public Expr {

private:
friend class ASTStmtReader;
friend llvm::TrailingObjects<ObjCAvailabilityCheckExpr, char>;

VersionAsWritten VersionToCheck;
SourceLocation AtLoc, RParen;

void setHasDomainName(bool V) {
ObjCAvailabilityCheckExprBits.HasDomainName = V;
}

explicit ObjCAvailabilityCheckExpr(EmptyShell Shell)
: Expr(ObjCAvailabilityCheckExprClass, Shell) {
setHasDomainName(false);
}

ObjCAvailabilityCheckExpr(SourceLocation AtLoc, SourceLocation RParen,
QualType Ty, StringRef DomainName)
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
VersionToCheck(), AtLoc(AtLoc), RParen(RParen) {
setDependence(ExprDependence::None);
setHasDomainName(true);
strcpy(getTrailingObjects<char>(), DomainName.data());
}

public:
ObjCAvailabilityCheckExpr(VersionAsWritten VersionToCheck,
SourceLocation AtLoc,
SourceLocation RParen, QualType Ty)
: Expr(ObjCAvailabilityCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {
setDependence(ExprDependence::None);
setHasDomainName(false);
}

explicit ObjCAvailabilityCheckExpr(EmptyShell Shell)
: Expr(ObjCAvailabilityCheckExprClass, Shell) {}
static ObjCAvailabilityCheckExpr *
CreateAvailabilityFeatureCheck(SourceLocation AtLoc, SourceLocation RParen,
QualType Ty, StringRef DomainName,
const ASTContext &C);

static ObjCAvailabilityCheckExpr *
CreateEmpty(const ASTContext &C, Stmt::EmptyShell Empty, size_t FeaturesLen);

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

bool hasDomainName() const {
return ObjCAvailabilityCheckExprBits.HasDomainName;
}
StringRef getDomainName() const {
assert(hasDomainName());
return getTrailingObjects<char>();
}

child_range children() {
return child_range(child_iterator(), child_iterator());
}
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,15 @@ class alignas(void *) Stmt {
unsigned ShouldCopy : 1;
};

class ObjCAvailabilityCheckExprBitfields {
friend class ObjCAvailabilityCheckExpr;
LLVM_PREFERRED_TYPE(ExprBitfields)
unsigned : NumExprBits;

LLVM_PREFERRED_TYPE(bool)
unsigned HasDomainName : 1;
};

//===--- Clang Extensions bitfields classes ---===//

class OpaqueValueExprBitfields {
Expand Down Expand Up @@ -1346,6 +1355,7 @@ class alignas(void *) Stmt {

// Obj-C Expressions
ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits;
ObjCAvailabilityCheckExprBitfields ObjCAvailabilityCheckExprBits;

// Clang Extensions
OpaqueValueExprBitfields OpaqueValueExprBits;
Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,8 @@ def Availability : InheritableAttr {
VersionArgument<"deprecated">, VersionArgument<"obsoleted">,
BoolArgument<"unavailable">, StringArgument<"message">,
BoolArgument<"strict">, StringArgument<"replacement">,
IntArgument<"priority">, IdentifierArgument<"environment">];
IntArgument<"priority">, IdentifierArgument<"environment">,
StringArgument<"domain", 1>];
let AdditionalMembers =
[{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
return llvm::StringSwitch<llvm::StringRef>(Platform)
Expand Down Expand Up @@ -1167,13 +1168,22 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm
.Case("library", llvm::Triple::Library)
.Default(llvm::Triple::UnknownEnvironment);
}
static AvailabilityAttr *getDomainAvailability(StringRef DomainName, bool IsUnavailable, const AttributeCommonInfo &CI, ASTContext &Ctx);
std::string getFeatureAttributeStr() const;
}];
let HasCustomParsing = 1;
let InheritEvenIfAlreadyPresent = 1;
let Subjects = SubjectList<[Named]>;
let Documentation = [AvailabilityDocs];
}

def AvailabilityDomain : InheritableAttr {
let Spellings = [Clang<"availability_domain">];
let Args = [IdentifierArgument<"name">];
let Subjects = SubjectList<[Var]>;
let Documentation = [Undocumented];
}

def ExternalSourceSymbol : InheritableAttr {
let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1,
/*version=*/20230206>];
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,11 @@ def err_availability_expected_platform : Error<
def err_availability_expected_environment : Error<
"expected an environment name, e.g., 'compute'">;

def err_features_domain_name : Error<
"expected a domain name">;
def err_feature_invalid_availability_check : Error<
"cannot pass a domain argument along with other arguments">;

// objc_bridge_related attribute
def err_objcbridge_related_expected_related_class : Error<
"expected a related Objective-C class name, e.g., 'NSColor'">;
Expand Down
22 changes: 22 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -4008,6 +4008,28 @@ def err_availability_unexpected_parameter: Error<
def warn_unguarded_availability :
Warning<"%0 is only available %select{|in %4 environment }3on %1 %2 or newer">,
InGroup<UnguardedAvailability>, DefaultIgnore;
def err_unguarded_feature : Error<
"use of %0 requires feature '%1' to be %select{available|unavailable}2">;
def err_label_in_conditionally_guarded_feature : Error<
"labels cannot appear in regions conditionally guarded by features">;
def err_features_invalid_for_decl : Error<
"feature attributes cannot be applied to %0">;
def err_features_invalid_name : Error<
"cannot find definition of feature attribute '%0'">;
def err_features_invalid__arg : Error<
"invalid argument %0: must evaluate to 0 or 1">;
def err_feature_invalid_for_decl : Error<
"feature attribute '%0(%1)' cannot be applied to this decl">;
def err_feature_merge_incompatible : Error<
"cannot merge incompatible feature attribute to this decl">;
def err_new_feature_redeclaration : Error<
"new feature attributes cannot be added to redeclarations">;
def err_feature_invalid_added : Error<
"cannot add feature availability to this decl">;
def note_feature_incompatible0 : Note<
"feature attribute %0">;
def note_feature_incompatible1 : Note<
"is incompatible with %0">;
def warn_unguarded_availability_unavailable :
Warning<"%0 is unavailable">,
InGroup<UnguardedAvailability>, DefaultIgnore;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@ class LangOptions : public LangOptionsBase {
/// This list is sorted.
std::vector<std::string> ModuleFeatures;

std::vector<std::string> FeatureAvailability;

/// Options for parsing comments.
CommentOptions CommentOpts;

Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3539,6 +3539,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group<f_Group>,
def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group<f_Group>,
Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>,
HelpText<"Disable implicit builtin knowledge of a specific function">;
def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group<f_Group>,
Visibility<[CC1Option]>,
HelpText<"feature availability">,
MarshallingInfoStringVector<LangOpts<"FeatureAvailability">>;
def fno_common : Flag<["-"], "fno-common">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Compile common globals like normal definitions">;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3138,6 +3138,12 @@ class Parser : public CodeCompletionHandler {
void ParseHLSLQualifiers(ParsedAttributes &Attrs);

VersionTuple ParseVersionTuple(SourceRange &Range);
void ParseFeatureAvailabilityAttribute(
IdentifierInfo &Availability, SourceLocation AvailabilityLoc,
ParsedAttributes &attrs, SourceLocation *endLoc,
IdentifierInfo *ScopeName, SourceLocation ScopeLoc, ParsedAttr::Form Form,
BalancedDelimiterTracker &T);

void ParseAvailabilityAttribute(IdentifierInfo &Availability,
SourceLocation AvailabilityLoc,
ParsedAttributes &attrs,
Expand Down
21 changes: 20 additions & 1 deletion clang/include/clang/Sema/DelayedDiagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ class AccessedEntity {
/// the complete parsing of the current declaration.
class DelayedDiagnostic {
public:
enum DDKind : unsigned char { Availability, Access, ForbiddenType };
enum DDKind : unsigned char {
Availability,
FeatureAvailability,
Access,
ForbiddenType
};

DDKind Kind;
bool Triggered;
Expand All @@ -143,6 +148,9 @@ class DelayedDiagnostic {
StringRef Msg,
bool ObjCPropertyAccess);

static DelayedDiagnostic
makeFeatureAvailability(NamedDecl *D, ArrayRef<SourceLocation> Locs);

static DelayedDiagnostic makeAccess(SourceLocation Loc,
const AccessedEntity &Entity) {
DelayedDiagnostic DD;
Expand Down Expand Up @@ -201,6 +209,12 @@ class DelayedDiagnostic {
return AvailabilityData.AR;
}

const NamedDecl *getFeatureAvailabilityDecl() const {
assert(Kind == FeatureAvailability &&
"Not a feature availability diagnostic.");
return FeatureAvailabilityData.Decl;
}

/// The diagnostic ID to emit. Used like so:
/// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic())
/// << diag.getForbiddenTypeOperand()
Expand Down Expand Up @@ -246,6 +260,10 @@ class DelayedDiagnostic {
bool ObjCPropertyAccess;
};

struct FAD {
const NamedDecl *Decl;
};

struct FTD {
unsigned Diagnostic;
unsigned Argument;
Expand All @@ -254,6 +272,7 @@ class DelayedDiagnostic {

union {
struct AD AvailabilityData;
struct FAD FeatureAvailabilityData;
struct FTD ForbiddenTypeData;

/// Access control.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/ParsedAttr.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ class ParsedAttr final
return IsProperty;
}

bool isAvailabilityAttribute() const { return IsAvailability; }

bool isInvalid() const { return Invalid; }
void setInvalid(bool b = true) const { Invalid = b; }

Expand Down
13 changes: 8 additions & 5 deletions clang/include/clang/Sema/ScopeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class FunctionScopeInfo {
/// unavailable.
bool HasPotentialAvailabilityViolations : 1;

bool HasPotentialFeatureAvailabilityViolations : 1;

/// A flag that is set when parsing a method that must call super's
/// implementation, such as \c -dealloc, \c -finalize, or any method marked
/// with \c __attribute__((objc_requires_super)).
Expand Down Expand Up @@ -394,11 +396,12 @@ class FunctionScopeInfo {
HasBranchIntoScope(false), HasIndirectGoto(false), HasMustTail(false),
HasDroppedStmt(false), HasOMPDeclareReductionCombiner(false),
HasFallthroughStmt(false), UsesFPIntrin(false),
HasPotentialAvailabilityViolations(false), ObjCShouldCallSuper(false),
ObjCIsDesignatedInit(false), ObjCWarnForNoDesignatedInitChain(false),
ObjCIsSecondaryInit(false), ObjCWarnForNoInitDelegation(false),
NeedsCoroutineSuspends(true), FoundImmediateEscalatingExpression(false),
ErrorTrap(Diag) {}
HasPotentialAvailabilityViolations(false),
HasPotentialFeatureAvailabilityViolations(false),
ObjCShouldCallSuper(false), ObjCIsDesignatedInit(false),
ObjCWarnForNoDesignatedInitChain(false), ObjCIsSecondaryInit(false),
ObjCWarnForNoInitDelegation(false), NeedsCoroutineSuspends(true),
FoundImmediateEscalatingExpression(false), ErrorTrap(Diag) {}

virtual ~FunctionScopeInfo();

Expand Down
Loading