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 15, 2025
1 parent 40dde0d commit e959883
Show file tree
Hide file tree
Showing 40 changed files with 908 additions and 39 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
21 changes: 12 additions & 9 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 Expand Up @@ -1917,15 +1920,15 @@ enum StmtCode {

/* TO_UPSTREAM(BoundsSafety) ON */
// BoundsSafety
EXPR_ASSUMPTION, // AssumptionExpr
EXPR_BOUNDS_SAFETY_POINTER_PROMOTION, // BoundsSafetyPointerPromotionExpr
EXPR_FORGE_PTR, // ForgePtrExpr
EXPR_GET_BOUND, // GetBoundExpr
EXPR_PREDEFINED_BOUNDS_CHECK, // PredefinedBoundsCheckExpr
EXPR_BOUNDS_CHECK, // BoundsCheckExpr
EXPR_MATERIALIZE_SEQUENCE, // MaterializeSequenceExpr
EXPR_TERMINATED_BY_TO_INDEXABLE, // TerminatedByToIndexableExpr
EXPR_TERMINATED_BY_FROM_INDEXABLE, // TerminatedByFromIndexableExpr
EXPR_ASSUMPTION, // AssumptionExpr
EXPR_BOUNDS_SAFETY_POINTER_PROMOTION, // BoundsSafetyPointerPromotionExpr
EXPR_FORGE_PTR, // ForgePtrExpr
EXPR_GET_BOUND, // GetBoundExpr
EXPR_PREDEFINED_BOUNDS_CHECK, // PredefinedBoundsCheckExpr
EXPR_BOUNDS_CHECK, // BoundsCheckExpr
EXPR_MATERIALIZE_SEQUENCE, // MaterializeSequenceExpr
EXPR_TERMINATED_BY_TO_INDEXABLE, // TerminatedByToIndexableExpr
EXPR_TERMINATED_BY_FROM_INDEXABLE, // TerminatedByFromIndexableExpr
/* TO_UPSTREAM(BoundsSafety) OFF */

// Microsoft
Expand Down
75 changes: 75 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,81 @@ 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 (Dst->isInvalidDecl())
return;

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
Loading

0 comments on commit e959883

Please sign in to comment.