Skip to content

Commit

Permalink
Add support for availability-style features
Browse files Browse the repository at this point in the history
rdar://137999979
  • Loading branch information
ahatanaka committed Jan 9, 2025
1 parent 40dde0d commit 9c6c6b4
Show file tree
Hide file tree
Showing 40 changed files with 888 additions and 30 deletions.
16 changes: 16 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,22 @@ class ASTContext : public RefCountedBase<ASTContext> {
return DiagAllocator;
}

enum FeatureAvailKind { On, Off, Dynamic };

struct FeatureAvailInfo {
FeatureAvailKind Kind;
std::string PredFnName;
};

FeatureAvailInfo getFeatureAvailInfo(StringRef FeatureName) const;

void inheritFeatureAvailability(Decl *Dst, Decl *Src);

SmallVector<FeatureAvailabilityAttr *, 2>
getFeatureAvailabilityAttrs(const Decl *D) const;

bool hasUnavailableFeature(const Decl *D) const;

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

Expand Down
36 changes: 36 additions & 0 deletions clang/include/clang/AST/ExprObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,42 @@ class ObjCAvailabilityCheckExpr : public Expr {
}
};

class ObjCFeatureCheckExpr : public Expr {
friend class ASTStmtReader;

IdentifierInfo *FeatureName;
SourceLocation AtLoc, RParen;

public:
ObjCFeatureCheckExpr(IdentifierInfo *FeatureName, SourceLocation AtLoc,
SourceLocation RParen, QualType Ty)
: Expr(ObjCFeatureCheckExprClass, Ty, VK_PRValue, OK_Ordinary),
FeatureName(FeatureName), AtLoc(AtLoc), RParen(RParen) {
setDependence(ExprDependence::None);
}

explicit ObjCFeatureCheckExpr(EmptyShell Shell)
: Expr(ObjCFeatureCheckExprClass, Shell) {}

SourceLocation getBeginLoc() const { return AtLoc; }
SourceLocation getEndLoc() const { return RParen; }
SourceRange getSourceRange() const { return {AtLoc, RParen}; }

IdentifierInfo *getFeatureName() const { return FeatureName; }

child_range children() {
return child_range(child_iterator(), child_iterator());
}

const_child_range children() const {
return const_child_range(const_child_iterator(), const_child_iterator());
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == ObjCFeatureCheckExprClass;
}
};

} // namespace clang

#endif // LLVM_CLANG_AST_EXPROBJC_H
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2901,6 +2901,7 @@ DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
})

DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
DEF_TRAVERSE_STMT(ObjCFeatureCheckExpr, {})
DEF_TRAVERSE_STMT(ParenExpr, {})
DEF_TRAVERSE_STMT(ParenListExpr, {})
DEF_TRAVERSE_STMT(SYCLUniqueStableNameExpr, {
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,13 @@ static llvm::Triple::EnvironmentType getEnvironmentType(llvm::StringRef Environm
let Documentation = [AvailabilityDocs];
}

def FeatureAvailability : InheritableAttr {
let Spellings = [Clang<"feature">];
let Args = [IdentifierArgument<"name">];
let Subjects = SubjectList<[Named]>;
let Documentation = [Undocumented];
}

def ExternalSourceSymbol : InheritableAttr {
let Spellings = [Clang<"external_source_symbol", /*allowInC=*/1,
/*version=*/20230206>];
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3985,6 +3985,10 @@ 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 needs guard for feature %1">;
def err_label_in_conditionally_guarded_feature : Error<
"labels cannot appear in regions conditionally guarded by features">;
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
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def ObjCIndirectCopyRestoreExpr : StmtNode<Expr>;
def ObjCBoolLiteralExpr : StmtNode<Expr>;
def ObjCSubscriptRefExpr : StmtNode<Expr>;
def ObjCAvailabilityCheckExpr : StmtNode<Expr>;
def ObjCFeatureCheckExpr : StmtNode<Expr>;

// Obj-C ARC Expressions.
def ObjCBridgedCastExpr : StmtNode<ExplicitCastExpr>;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ ALIAS("__char16_t" , char16_t , KEYCXX)
ALIAS("__char32_t" , char32_t , KEYCXX)
KEYWORD(__builtin_bit_cast , KEYALL)
KEYWORD(__builtin_available , KEYALL)
KEYWORD(__builtin_feature , KEYALL)
KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)

// Keywords defined by Attr.td.
Expand Down Expand Up @@ -851,6 +852,7 @@ OBJC_AT_KEYWORD(synthesize)
OBJC_AT_KEYWORD(dynamic)
OBJC_AT_KEYWORD(import)
OBJC_AT_KEYWORD(available)
OBJC_AT_KEYWORD(feature)

//===----------------------------------------------------------------------===//
// Notable identifiers.
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 @@ -3528,6 +3528,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<[ClangOption, 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
1 change: 1 addition & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3148,6 +3148,7 @@ class Parser : public CodeCompletionHandler {

std::optional<AvailabilitySpec> ParseAvailabilitySpec();
ExprResult ParseAvailabilityCheckExpr(SourceLocation StartLoc);
ExprResult ParseFeatureCheckExpr(SourceLocation StartLoc);

void ParseExternalSourceSymbolAttribute(IdentifierInfo &ExternalSourceSymbol,
SourceLocation Loc,
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
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
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2174,8 +2174,16 @@ class Sema final : public SemaBase {
/// Issue any -Wunguarded-availability warnings in \c FD
void DiagnoseUnguardedAvailabilityViolations(Decl *FD);

void DiagnoseUnguardedFeatureAvailabilityViolations(Decl *FD);

void handleDelayedAvailabilityCheck(sema::DelayedDiagnostic &DD, Decl *Ctx);

void handleDelayedFeatureAvailabilityCheck(sema::DelayedDiagnostic &DD,
Decl *Ctx);

void DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D,
ArrayRef<SourceLocation> Locs);

/// Retrieve the current function, if any, that should be analyzed for
/// potential availability violations.
sema::FunctionScopeInfo *getCurFunctionAvailabilityContext();
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Sema/SemaObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,10 @@ class SemaObjC : public SemaBase {
ActOnObjCAvailabilityCheckExpr(llvm::ArrayRef<AvailabilitySpec> AvailSpecs,
SourceLocation AtLoc, SourceLocation RParen);

ExprResult ActOnObjCFeatureCheckExpr(IdentifierInfo *FeatureName,
SourceLocation AtLoc,
SourceLocation RParen);

/// Prepare a conversion of the given expression to an ObjC object
/// pointer type.
CastKind PrepareCastToObjCObjectPointer(ExprResult &E);
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -1806,6 +1806,9 @@ enum StmtCode {
/// An ObjCAvailabilityCheckExpr record.
EXPR_OBJC_AVAILABILITY_CHECK,

/// An ObjCAvailabilityCheckExpr record.
EXPR_OBJC_FEATURE_CHECK,

// C++

/// A CXXCatchStmt record.
Expand Down
72 changes: 72 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,78 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
addTranslationUnitDecl();
}

ASTContext::FeatureAvailInfo
ASTContext::getFeatureAvailInfo(StringRef FeatureName) const {
FeatureAvailInfo Info;

for (auto &V : getLangOpts().FeatureAvailability) {
unsigned EndOfKey = V.find(':');
std::string Key = V.substr(0, EndOfKey);

if (Key != FeatureName)
continue;

unsigned EndOfKind = V.find(':', EndOfKey + 1);
std::string Kind = V.substr(EndOfKey + 1, EndOfKind - (EndOfKey + 1));

if (Kind == "on")
Info.Kind = FeatureAvailKind::On;
else if (Kind == "off")
Info.Kind = FeatureAvailKind::Off;
else if (Kind == "dyn") {
Info.Kind = FeatureAvailKind::Dynamic;
Info.PredFnName = V.substr(EndOfKind + 1, V.size() - (EndOfKind + 1));
} else
llvm_unreachable("invalid FeatureAvailKind");
return Info;
}

llvm_unreachable("feature not found");
}

void ASTContext::inheritFeatureAvailability(Decl *Dst, Decl *Src) {
if (!Src->hasAttrs())
return;

for (auto *Attr : Src->getAttrs()) {
auto *FA = dyn_cast<FeatureAvailabilityAttr>(Attr);
if (!FA)
continue;
auto *NewAttr = FA->clone(*this);
NewAttr->setInherited(true);
Dst->addAttr(NewAttr);
}
}

SmallVector<FeatureAvailabilityAttr *, 2>
ASTContext::getFeatureAvailabilityAttrs(const Decl *D) const {
if (!D->hasAttrs())
return {};

SmallVector<FeatureAvailabilityAttr *, 2> Attrs;

for (auto *Attr : D->getAttrs())
if (auto *FA = dyn_cast<FeatureAvailabilityAttr>(Attr))
Attrs.push_back(FA);

return Attrs;
}

bool ASTContext::hasUnavailableFeature(const Decl *D) const {
if (D->hasAttrs())
for (auto *Attr : D->getAttrs()) {
auto *FA = dyn_cast<FeatureAvailabilityAttr>(Attr);
if (!FA)
continue;
auto FeatureName = FA->getName()->getName();
auto FeatureInfo = getFeatureAvailInfo(FeatureName);
if (FeatureInfo.Kind == FeatureAvailKind::Off)
return true;
}

return false;
}

void ASTContext::cleanup() {
// Release the DenseMaps associated with DeclContext objects.
// FIXME: Is this the ideal solution?
Expand Down
20 changes: 20 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12318,6 +12318,26 @@ class IntExprEvaluator
return Success(E->getValue(), E);
}

bool VisitObjCFeatureCheckExpr(const ObjCFeatureCheckExpr *E) {
IdentifierInfo *IdentInfo = E->getFeatureName();
auto FeatureName = IdentInfo->getName();
auto FeatureInfo = Info.Ctx.getFeatureAvailInfo(FeatureName);
unsigned ResultInt;

switch (FeatureInfo.Kind) {
case ASTContext::FeatureAvailKind::On:
ResultInt = 1;
break;
case ASTContext::FeatureAvailKind::Off:
ResultInt = 0;
break;
case ASTContext::FeatureAvailKind::Dynamic:
return false;
}

return Success(APSInt(APInt(1, ResultInt)), E);
}

bool CheckReferencedDecl(const Expr *E, const Decl *D);
bool VisitDeclRefExpr(const DeclRefExpr *E) {
if (CheckReferencedDecl(E, E->getDecl()))
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,10 @@ void StmtPrinter::VisitObjCAvailabilityCheckExpr(
OS << "@available(...)";
}

void StmtPrinter::VisitObjCFeatureCheckExpr(ObjCFeatureCheckExpr *Node) {
OS << "@feature(...)";
}

void StmtPrinter::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Node) {
Indent() << "@synchronized (";
PrintExpr(Node->getSynchExpr());
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2444,6 +2444,10 @@ void StmtProfiler::VisitObjCAvailabilityCheckExpr(
VisitExpr(S);
}

void StmtProfiler::VisitObjCFeatureCheckExpr(const ObjCFeatureCheckExpr *S) {
VisitExpr(S);
}

void StmtProfiler::VisitTemplateArguments(const TemplateArgumentLoc *Args,
unsigned NumArgs) {
ID.AddInteger(NumArgs);
Expand Down
Loading

0 comments on commit 9c6c6b4

Please sign in to comment.