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
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