diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 6a2a03ef49d8df..d10525ff01ac2b 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -822,6 +822,22 @@ class ASTContext : public RefCountedBase { 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 + getFeatureAvailabilityAttrs(const Decl *D) const; + + bool hasUnavailableFeature(const Decl *D) const; + const TargetInfo &getTargetInfo() const { return *Target; } const TargetInfo *getAuxTargetInfo() const { return AuxTarget; } diff --git a/clang/include/clang/AST/ExprObjC.h b/clang/include/clang/AST/ExprObjC.h index 59961011a3a457..d7475d52a5a4fc 100644 --- a/clang/include/clang/AST/ExprObjC.h +++ b/clang/include/clang/AST/ExprObjC.h @@ -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 diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 99dccd721ec140..31bdfbecfd4ca1 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -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, { diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index eab75cbe17ae80..9c799b4c5626ed 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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>]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c84c9b75b93c4b..77dd1da080b7d3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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, 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, DefaultIgnore; diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index b451cc135253f2..344f92e431af6c 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -579,6 +579,8 @@ class LangOptions : public LangOptionsBase { /// This list is sorted. std::vector ModuleFeatures; + std::vector FeatureAvailability; + /// Options for parsing comments. CommentOptions CommentOpts; diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td index 13692c6b135c56..51f04fc10dabc8 100644 --- a/clang/include/clang/Basic/StmtNodes.td +++ b/clang/include/clang/Basic/StmtNodes.td @@ -189,6 +189,7 @@ def ObjCIndirectCopyRestoreExpr : StmtNode; def ObjCBoolLiteralExpr : StmtNode; def ObjCSubscriptRefExpr : StmtNode; def ObjCAvailabilityCheckExpr : StmtNode; +def ObjCFeatureCheckExpr : StmtNode; // Obj-C ARC Expressions. def ObjCBridgedCastExpr : StmtNode; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index e430c6d79bcebd..677940d8412af2 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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. @@ -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. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7bb8714b43fffb..8bc6841d064f9c 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3528,6 +3528,10 @@ def fno_builtin : Flag<["-"], "fno-builtin">, Group, def fno_builtin_ : Joined<["-"], "fno-builtin-">, Group, Visibility<[ClangOption, CC1Option, CLOption, DXCOption]>, HelpText<"Disable implicit builtin knowledge of a specific function">; +def ffeature_availability_EQ : Joined<["-"], "ffeature-availability=">, Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"feature availability">, + MarshallingInfoStringVector>; def fno_common : Flag<["-"], "fno-common">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Compile common globals like normal definitions">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 03ef27f1d63ae7..bee5efee7298bb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3148,6 +3148,7 @@ class Parser : public CodeCompletionHandler { std::optional ParseAvailabilitySpec(); ExprResult ParseAvailabilityCheckExpr(SourceLocation StartLoc); + ExprResult ParseFeatureCheckExpr(SourceLocation StartLoc); void ParseExternalSourceSymbolAttribute(IdentifierInfo &ExternalSourceSymbol, SourceLocation Loc, diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h index 0105089a393f17..93ecae3529231d 100644 --- a/clang/include/clang/Sema/DelayedDiagnostic.h +++ b/clang/include/clang/Sema/DelayedDiagnostic.h @@ -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; @@ -143,6 +148,9 @@ class DelayedDiagnostic { StringRef Msg, bool ObjCPropertyAccess); + static DelayedDiagnostic + makeFeatureAvailability(NamedDecl *D, ArrayRef Locs); + static DelayedDiagnostic makeAccess(SourceLocation Loc, const AccessedEntity &Entity) { DelayedDiagnostic DD; @@ -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() @@ -246,6 +260,10 @@ class DelayedDiagnostic { bool ObjCPropertyAccess; }; + struct FAD { + const NamedDecl *Decl; + }; + struct FTD { unsigned Diagnostic; unsigned Argument; @@ -254,6 +272,7 @@ class DelayedDiagnostic { union { struct AD AvailabilityData; + struct FAD FeatureAvailabilityData; struct FTD ForbiddenTypeData; /// Access control. diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h index 958d65055fa9b2..34c541b1e18b69 100644 --- a/clang/include/clang/Sema/ScopeInfo.h +++ b/clang/include/clang/Sema/ScopeInfo.h @@ -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)). @@ -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(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 614f846547ce97..958e6f050c1fa0 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -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 Locs); + /// Retrieve the current function, if any, that should be analyzed for /// potential availability violations. sema::FunctionScopeInfo *getCurFunctionAvailabilityContext(); diff --git a/clang/include/clang/Sema/SemaObjC.h b/clang/include/clang/Sema/SemaObjC.h index 791a7f45b832f7..847d0799ce9c7b 100644 --- a/clang/include/clang/Sema/SemaObjC.h +++ b/clang/include/clang/Sema/SemaObjC.h @@ -900,6 +900,10 @@ class SemaObjC : public SemaBase { ActOnObjCAvailabilityCheckExpr(llvm::ArrayRef 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); diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 6719deef3b4c7b..b57a375fca866d 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1806,6 +1806,9 @@ enum StmtCode { /// An ObjCAvailabilityCheckExpr record. EXPR_OBJC_AVAILABILITY_CHECK, + /// An ObjCAvailabilityCheckExpr record. + EXPR_OBJC_FEATURE_CHECK, + // C++ /// A CXXCatchStmt record. @@ -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 diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 29c265bf01d123..a08727a25539e7 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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(Attr); + if (!FA) + continue; + auto *NewAttr = FA->clone(*this); + NewAttr->setInherited(true); + Dst->addAttr(NewAttr); + } +} + +SmallVector +ASTContext::getFeatureAvailabilityAttrs(const Decl *D) const { + if (!D->hasAttrs()) + return {}; + + SmallVector Attrs; + + for (auto *Attr : D->getAttrs()) + if (auto *FA = dyn_cast(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(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? diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3331617c3d2ca4..965e159e24b9df 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -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())) diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp index 87bb1bcfc9a2f7..31618d1d413dbf 100644 --- a/clang/lib/AST/StmtPrinter.cpp +++ b/clang/lib/AST/StmtPrinter.cpp @@ -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()); diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp index 4c1ffe5bd21385..16e1a266c10261 100644 --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -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); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 18c710236f8d72..ce808c9b38c022 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -5318,6 +5318,9 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { return; } + if (Context.hasUnavailableFeature(D)) + return; + // The tentative definition is the only definition. EmitGlobalVarDefinition(D); } @@ -6989,6 +6992,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { if (auto *FD = dyn_cast(D); FD && FD->isImmediateFunction()) return; + if (Context.hasUnavailableFeature(D)) + return; + switch (D->getKind()) { case Decl::CXXConversion: case Decl::CXXMethod: @@ -7118,6 +7124,8 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { } case Decl::ObjCMethod: { auto *OMD = cast(D); + if (Context.hasUnavailableFeature(OMD->getClassInterface())) + break; // If this is not a prototype, emit the body. if (OMD->getBody()) CodeGenFunction(*this).GenerateObjCMethod(OMD); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 402acc52c7ab6a..d1decd8b916e16 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4053,6 +4053,14 @@ static void RenderBuiltinOptions(const ToolChain &TC, const llvm::Triple &T, } } +static void RenderFeatueAvailabilityOptions(const ArgList &Args, + ArgStringList &CmdArgs) { + for (const Arg *A : Args.filtered(options::OPT_ffeature_availability_EQ)) { + A->claim(); + A->render(Args, CmdArgs); + } +} + bool Driver::getDefaultModuleCachePath(SmallVectorImpl &Result) { if (const char *Str = std::getenv("CLANG_MODULE_CACHE_PATH")) { Twine Path{Str}; @@ -7359,6 +7367,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &Job, } RenderBuiltinOptions(TC, RawTriple, Args, CmdArgs); + RenderFeatueAvailabilityOptions(Args, CmdArgs); + Args.addOptOutFlag(CmdArgs, options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index b48a02e121ac70..e3f0dc4cd6195b 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1393,6 +1393,9 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw___builtin_available: Res = ParseAvailabilityCheckExpr(Tok.getLocation()); break; + case tok::kw___builtin_feature: + Res = ParseFeatureCheckExpr(Tok.getLocation()); + break; case tok::kw___builtin_va_arg: case tok::kw___builtin_offsetof: case tok::kw___builtin_choose_expr: @@ -4249,4 +4252,26 @@ ExprResult Parser::ParseUnsafeTerminatedByFromIndexable() { Terminator.get(), Pointer.get(), PointerToTerminator, KWLoc, T.getCloseLocation()); } -/* TO_UPSTREAM(BoundsSafety) OFF*/ \ No newline at end of file +/* TO_UPSTREAM(BoundsSafety) OFF*/ + +ExprResult Parser::ParseFeatureCheckExpr(SourceLocation BeginLoc) { + assert(Tok.is(tok::kw___builtin_feature) || + Tok.isObjCAtKeyword(tok::objc_feature)); + + // Eat the feature or __builtin_feature. + ConsumeToken(); + + BalancedDelimiterTracker Parens(*this, tok::l_paren); + + if (Parens.expectAndConsume()) + return ExprError(); + + IdentifierLoc *FeatureIdentifier = ParseIdentifierLoc(); + IdentifierInfo *FeatureName = FeatureIdentifier->Ident; + + if (Parens.consumeClose()) + return ExprError(); + + return Actions.ObjC().ActOnObjCFeatureCheckExpr(FeatureName, BeginLoc, + Parens.getCloseLocation()); +} diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 08dbff290fc7f6..40a64325d498f5 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -2930,27 +2930,28 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) { return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc)); case tok::objc_available: return ParseAvailabilityCheckExpr(AtLoc); - default: { - const char *str = nullptr; - // Only provide the @try/@finally/@autoreleasepool fixit when we're sure - // that this is a proper statement where such directives could actually - // occur. - if (GetLookAheadToken(1).is(tok::l_brace) && - ExprStatementTokLoc == AtLoc) { - char ch = Tok.getIdentifierInfo()->getNameStart()[0]; - str = - ch == 't' ? "try" - : (ch == 'f' ? "finally" - : (ch == 'a' ? "autoreleasepool" : nullptr)); - } - if (str) { - SourceLocation kwLoc = Tok.getLocation(); - return ExprError(Diag(AtLoc, diag::err_unexpected_at) << - FixItHint::CreateReplacement(kwLoc, str)); - } - else - return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + case tok::objc_feature: + return ParseFeatureCheckExpr(AtLoc); + default: { + const char *str = nullptr; + // Only provide the @try/@finally/@autoreleasepool fixit when we're sure + // that this is a proper statement where such directives could actually + // occur. + if (GetLookAheadToken(1).is(tok::l_brace) && + ExprStatementTokLoc == AtLoc) { + char ch = Tok.getIdentifierInfo()->getNameStart()[0]; + str = ch == 't' + ? "try" + : (ch == 'f' ? "finally" + : (ch == 'a' ? "autoreleasepool" : nullptr)); } + if (str) { + SourceLocation kwLoc = Tok.getLocation(); + return ExprError(Diag(AtLoc, diag::err_unexpected_at) + << FixItHint::CreateReplacement(kwLoc, str)); + } else + return ExprError(Diag(AtLoc, diag::err_unexpected_at)); + } } } } diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1b52078f6eb978..e0d91491c295ed 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -61,6 +61,7 @@ add_clang_library(clangSema SemaExprCXX.cpp SemaExprMember.cpp SemaExprObjC.cpp + SemaFeatureAvailability.cpp SemaFixItUtils.cpp SemaFunctionEffects.cpp SemaHLSL.cpp diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp index cb2721b92090ec..423ad3f1451b72 100644 --- a/clang/lib/Sema/DelayedDiagnostic.cpp +++ b/clang/lib/Sema/DelayedDiagnostic.cpp @@ -56,6 +56,18 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR, return DD; } +DelayedDiagnostic +DelayedDiagnostic::makeFeatureAvailability(NamedDecl *D, + ArrayRef Locs) { + assert(!Locs.empty()); + DelayedDiagnostic DD; + DD.Kind = FeatureAvailability; + DD.Triggered = false; + DD.Loc = Locs.front(); + DD.FeatureAvailabilityData.Decl = D; + return DD; +} + void DelayedDiagnostic::Destroy() { switch (Kind) { case Access: diff --git a/clang/lib/Sema/ScopeInfo.cpp b/clang/lib/Sema/ScopeInfo.cpp index d089836fa36dd9..da2d574f41a7d6 100644 --- a/clang/lib/Sema/ScopeInfo.cpp +++ b/clang/lib/Sema/ScopeInfo.cpp @@ -30,6 +30,7 @@ void FunctionScopeInfo::Clear() { HasFallthroughStmt = false; UsesFPIntrin = false; HasPotentialAvailabilityViolations = false; + HasPotentialFeatureAvailabilityViolations = false; ObjCShouldCallSuper = false; ObjCIsDesignatedInit = false; ObjCWarnForNoDesignatedInitChain = false; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index f1b79c7fcaadba..f3b6ba1d19aab1 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -17246,6 +17246,9 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, if (Body && FSI->HasPotentialAvailabilityViolations) DiagnoseUnguardedAvailabilityViolations(dcl); + if (Body && FSI->HasPotentialFeatureAvailabilityViolations) + DiagnoseUnguardedFeatureAvailabilityViolations(dcl); + assert(!FSI->ObjCShouldCallSuper && "This should only be set for ObjC methods, which should have been " "handled in the block above."); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 45fa2126dd1295..d12a545bc35431 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2599,6 +2599,19 @@ static void handleAvailabilityAttr(Sema &S, Decl *D, const ParsedAttr &AL) { } } +static void handleFeatureAvailabilityAttr(Sema &S, Decl *D, + const ParsedAttr &AL) { + if (!AL.isArgIdent(0)) { + S.Diag(AL.getLoc(), diag::err_attribute_argument_n_type) + << AL << 0 << AANT_ArgumentIdentifier; + return; + } + + // FIXME: error checking + IdentifierInfo *II = AL.getArgAsIdent(0)->Ident; + D->addAttr(::new (S.Context) FeatureAvailabilityAttr(S.Context, AL, II)); +} + static void handleExternalSourceSymbolAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.checkAtLeastNumArgs(S, 1) || !AL.checkAtMostNumArgs(S, 4)) @@ -8678,6 +8691,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_FeatureAvailability: + handleFeatureAvailabilityAttr(S, D, AL); + break; + case ParsedAttr::AT_ArmNew: S.ARM().handleNewAttr(D, AL); break; @@ -9118,6 +9135,12 @@ void Sema::PopParsingDeclaration(ParsingDeclState state, Decl *decl) { handleDelayedAvailabilityCheck(diag, decl); break; + case DelayedDiagnostic::FeatureAvailability: + // Don't bother giving diagnostics if the decl is invalid. + if (!decl->isInvalidDecl()) + handleDelayedFeatureAvailabilityCheck(diag, decl); + break; + case DelayedDiagnostic::Access: // Only produce one access control diagnostic for a structured binding // declaration: we don't need to tell the user that all the fields are diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index 8f571f71b20c99..bdd01f7c921adf 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1892,6 +1892,7 @@ ObjCCategoryDecl *SemaObjC::ActOnStartCategoryInterface( // checking for protocol uses. SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, CDecl, AttrList); SemaRef.AddPragmaAttributes(SemaRef.TUScope, CDecl); + Context.inheritFeatureAvailability(CDecl, IDecl); if (NumProtoRefs) { diagnoseUseOfProtocols(SemaRef, CDecl, (ObjCProtocolDecl *const *)ProtoRefs, @@ -1947,6 +1948,7 @@ ObjCCategoryImplDecl *SemaObjC::ActOnStartCategoryImplementation( SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, CDecl, Attrs); SemaRef.AddPragmaAttributes(SemaRef.TUScope, CDecl); + Context.inheritFeatureAvailability(CDecl, IDecl); // FIXME: PushOnScopeChains? SemaRef.CurContext->addDecl(CDecl); @@ -2081,6 +2083,7 @@ ObjCImplementationDecl *SemaObjC::ActOnStartClassImplementation( SemaRef.ProcessDeclAttributeList(SemaRef.TUScope, IMPDecl, Attrs); SemaRef.AddPragmaAttributes(SemaRef.TUScope, IMPDecl); + Context.inheritFeatureAvailability(IMPDecl, IDecl); if (CheckObjCDeclScope(IMPDecl)) { ActOnObjCContainerStartDefinition(IMPDecl); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 9f37e8a072bf76..9ba5ee849dffbd 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -374,6 +374,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef Locs, DiagnoseAvailabilityOfDecl(D, Locs, UnknownObjCClass, ObjCPropertyAccess, AvoidPartialAvailabilityChecks, ClassReceiver); + DiagnoseFeatureAvailabilityOfDecl(D, Locs); + DiagnoseUnusedOfDecl(*this, D, Loc); diagnoseUseOfInternalDeclInInlineFunction(*this, D, Loc); @@ -19571,6 +19573,9 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, if (Body && getCurFunction()->HasPotentialAvailabilityViolations) DiagnoseUnguardedAvailabilityViolations(BD); + if (Body && getCurFunction()->HasPotentialFeatureAvailabilityViolations) + DiagnoseUnguardedFeatureAvailabilityViolations(BD); + // Try to apply the named return value optimization. We have to check again // if we can do this, though, because blocks keep return statements around // to deduce an implicit return type. diff --git a/clang/lib/Sema/SemaExprObjC.cpp b/clang/lib/Sema/SemaExprObjC.cpp index 10d72852e077bd..dc77617c5c1d61 100644 --- a/clang/lib/Sema/SemaExprObjC.cpp +++ b/clang/lib/Sema/SemaExprObjC.cpp @@ -5191,6 +5191,14 @@ ExprResult SemaObjC::ActOnObjCAvailabilityCheckExpr( ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy); } +ExprResult SemaObjC::ActOnObjCFeatureCheckExpr(IdentifierInfo *FeatureName, + SourceLocation AtLoc, + SourceLocation RParen) { + ASTContext &Context = getASTContext(); + return new (Context) + ObjCFeatureCheckExpr(FeatureName, AtLoc, RParen, Context.BoolTy); +} + /// Prepare a conversion of the given expression to an ObjC object /// pointer type. CastKind SemaObjC::PrepareCastToObjCObjectPointer(ExprResult &E) { diff --git a/clang/lib/Sema/SemaFeatureAvailability.cpp b/clang/lib/Sema/SemaFeatureAvailability.cpp new file mode 100644 index 00000000000000..0c3f24535889e9 --- /dev/null +++ b/clang/lib/Sema/SemaFeatureAvailability.cpp @@ -0,0 +1,240 @@ +//===--- SemaFeatureAvailability.cpp - Availability attribute handling ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file processes the feature availability attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallSet.h" +#include + +using namespace clang; +using namespace sema; + +class DiagnoseUnguardedFeatureAvailability + : public RecursiveASTVisitor { + + typedef RecursiveASTVisitor Base; + + Sema &SemaRef; + const Decl *D; + + SmallVector, bool>, 4> FeatureStack; + + bool isFeatureUseGuarded(const std::string &FeatureUse) const; + + bool isConditionallyGuardedByFeature() const; + + void addFeaturesToStack(const Decl *D) { + if (!D) + return; + do { + for (auto *Attr : SemaRef.Context.getFeatureAvailabilityAttrs(D)) + FeatureStack.back().first.insert(Attr->getName()->getName().str()); + } while ((D = cast_or_null(D->getDeclContext()))); + } + +public: + DiagnoseUnguardedFeatureAvailability(Sema &SemaRef, const Decl *D, + Decl *Ctx = nullptr) + : SemaRef(SemaRef), D(D) { + FeatureStack.push_back({{}, false}); + addFeaturesToStack(cast_or_null(D->getDeclContext())); + addFeaturesToStack(Ctx); + } + + void diagnoseDeclFeatureAvailability(const NamedDecl *D, SourceLocation Loc); + + bool TraverseIfStmt(IfStmt *If); + + bool VisitDeclRefExpr(DeclRefExpr *DRE) { + diagnoseDeclFeatureAvailability(DRE->getDecl(), DRE->getBeginLoc()); + return true; + } + + bool VisitMemberExpr(MemberExpr *ME) { + diagnoseDeclFeatureAvailability(ME->getMemberDecl(), ME->getBeginLoc()); + return true; + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *OME) { + diagnoseDeclFeatureAvailability(OME->getMethodDecl(), OME->getBeginLoc()); + return true; + } + + bool VisitLabelStmt(LabelStmt *LS) { + if (isConditionallyGuardedByFeature()) + SemaRef.Diag(LS->getBeginLoc(), + diag::err_label_in_conditionally_guarded_feature); + return true; + } + + bool VisitFunctionDecl(const FunctionDecl *FD) { + pushDeclFeatures(FD); + bool R = TraverseStmt(FD->getBody()); + FeatureStack.pop_back(); + return R; + } + + bool VisitBlockDecl(const BlockDecl *BD) { + pushDeclFeatures(BD); + bool R = TraverseStmt(BD->getBody()); + FeatureStack.pop_back(); + return R; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *OMD) { + pushDeclFeatures(OMD); + bool R = TraverseStmt(OMD->getBody()); + FeatureStack.pop_back(); + return R; + } + + // This is needed to avoid emit the diagnostic twice. + bool WalkUpFromBlockDecl(BlockDecl *BD) { return true; } + + void pushDeclFeatures(const Decl *D) { + FeatureStack.push_back({{}, false}); + for (auto *Attr : SemaRef.Context.getFeatureAvailabilityAttrs(D)) + FeatureStack.back().first.insert(Attr->getName()->getName().str()); + } + + void IssueDiagnostics() { + if (auto *FD = dyn_cast(D)) + VisitFunctionDecl(FD); + else if (auto *BD = dyn_cast(D)) + VisitBlockDecl(BD); + else if (auto *OMD = dyn_cast(D)) + VisitObjCMethodDecl(OMD); + } +}; + +struct ExtractedFeatureExpr { + const ObjCFeatureCheckExpr *E = nullptr; + bool isNegated = false; +}; + +static ExtractedFeatureExpr extractFeatureExpr(const Expr *IfCond) { + const auto *E = IfCond; + bool IsNegated = false; + while (true) { + E = E->IgnoreParens(); + if (const auto *AE = dyn_cast(E)) { + return ExtractedFeatureExpr{AE, IsNegated}; + } + + const auto *UO = dyn_cast(E); + if (!UO || UO->getOpcode() != UO_LNot) { + return ExtractedFeatureExpr{}; + } + E = UO->getSubExpr(); + IsNegated = !IsNegated; + } +} + +bool DiagnoseUnguardedFeatureAvailability::isConditionallyGuardedByFeature() + const { + return FeatureStack.back().second; +} + +bool DiagnoseUnguardedFeatureAvailability::TraverseIfStmt(IfStmt *If) { + ExtractedFeatureExpr IfCond = extractFeatureExpr(If->getCond()); + if (!IfCond.E) { + // This isn't an availability checking 'if', we can just continue. + return Base::TraverseIfStmt(If); + } + + std::string FeatureStr = IfCond.E->getFeatureName()->getName().str(); + auto *Guarded = If->getThen(); + auto *Unguarded = If->getElse(); + if (IfCond.isNegated) { + std::swap(Guarded, Unguarded); + } + + FeatureStack.push_back({{}, true}); + FeatureStack.back().first.insert(FeatureStr); + bool ShouldContinue = TraverseStmt(Guarded); + FeatureStack.pop_back(); + + return ShouldContinue && TraverseStmt(Unguarded); +} + +bool DiagnoseUnguardedFeatureAvailability::isFeatureUseGuarded( + const std::string &FeatureUse) const { + for (auto &Features : FeatureStack) + if (Features.first.count(FeatureUse)) + return true; + return false; +} + +void DiagnoseUnguardedFeatureAvailability::diagnoseDeclFeatureAvailability( + const NamedDecl *D, SourceLocation Loc) { + SmallVector AttrsOnDecl = + SemaRef.Context.getFeatureAvailabilityAttrs(D); + + for (auto *Attr : AttrsOnDecl) { + std::string FeatureUse = Attr->getName()->getName().str(); + if (!isFeatureUseGuarded(FeatureUse)) + SemaRef.Diag(Loc, diag::err_unguarded_feature) << D << FeatureUse; + } +} + +static void DoEmitFeatureAvailabilityWarning(Sema &S, const NamedDecl *D, + Decl *Ctx, SourceLocation Loc) { + DiagnoseUnguardedFeatureAvailability Diag(S, D, Ctx); + Diag.diagnoseDeclFeatureAvailability(D, Loc); +} + +void Sema::handleDelayedFeatureAvailabilityCheck(DelayedDiagnostic &DD, + Decl *Ctx) { + assert(DD.Kind == DelayedDiagnostic::FeatureAvailability && + "Expected a feature availability diagnostic here"); + + DD.Triggered = true; + DoEmitFeatureAvailabilityWarning(*this, DD.getFeatureAvailabilityDecl(), Ctx, + DD.Loc); +} + +void Sema::DiagnoseUnguardedFeatureAvailabilityViolations(Decl *D) { + Stmt *Body = nullptr; + + if (auto *FD = D->getAsFunction()) { + Body = FD->getBody(); + } else if (auto *MD = dyn_cast(D)) + Body = MD->getBody(); + else if (auto *BD = dyn_cast(D)) + Body = BD->getBody(); + + assert(Body && "Need a body here!"); + + DiagnoseUnguardedFeatureAvailability(*this, D).IssueDiagnostics(); +} + +void Sema::DiagnoseFeatureAvailabilityOfDecl(NamedDecl *D, + ArrayRef Locs) { + if (D->hasAttr()) + if (FunctionScopeInfo *Context = getCurFunctionAvailabilityContext()) { + Context->HasPotentialFeatureAvailabilityViolations = true; + return; + } + + if (DelayedDiagnostics.shouldDelayDiagnostics()) { + DelayedDiagnostics.add(DelayedDiagnostic::makeFeatureAvailability(D, Locs)); + return; + } + + Decl *Ctx = cast(getCurLexicalContext()); + DiagnoseUnguardedFeatureAvailability Diag(*this, D, Ctx); + Diag.diagnoseDeclFeatureAvailability(D, Locs.front()); +} diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 93a17e84598115..2e2e9507698005 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -2340,9 +2340,8 @@ static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, ObjCPropertyDecl *Property) { // Should we just clone all attributes over? for (const auto *A : Property->attrs()) { - if (isa(A) || - isa(A) || - isa(A)) + if (isa(A) || isa(A) || + isa(A) || isa(A)) PropertyMethod->addAttr(A->clone(S.Context)); } } diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 6325f2f7c21994..c47ba0a8410992 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -646,6 +646,10 @@ Sema::ActOnLabelStmt(SourceLocation IdentLoc, LabelDecl *TheDecl, TheDecl->setLocation(IdentLoc); } } + + if (FunctionScopeInfo *Info = getCurFunctionAvailabilityContext()) + Info->HasPotentialFeatureAvailabilityViolations = true; + return LS; } diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4c55fedff3da9b..58a3f18cfe92f5 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -16662,6 +16662,12 @@ ExprResult TreeTransform::TransformObjCAvailabilityCheckExpr( return E; } +template +ExprResult +TreeTransform::TransformObjCFeatureCheckExpr(ObjCFeatureCheckExpr *E) { + return E; +} + template ExprResult TreeTransform::TransformObjCMessageExpr(ObjCMessageExpr *E) { diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp index 6a32bfe761f169..5995888b2afb49 100644 --- a/clang/lib/Serialization/ASTReaderStmt.cpp +++ b/clang/lib/Serialization/ASTReaderStmt.cpp @@ -1673,6 +1673,14 @@ void ASTStmtReader::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) E->VersionToCheck.SourceVersion = Record.readVersionTuple(); } +void ASTStmtReader::VisitObjCFeatureCheckExpr(ObjCFeatureCheckExpr *E) { + VisitExpr(E); + SourceRange R = Record.readSourceRange(); + E->AtLoc = R.getBegin(); + E->RParen = R.getEnd(); + E->FeatureName = Record.readIdentifier(); +} + //===----------------------------------------------------------------------===// // C++ Expressions and Statements //===----------------------------------------------------------------------===// @@ -3573,6 +3581,10 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) ObjCAvailabilityCheckExpr(Empty); break; + case EXPR_OBJC_FEATURE_CHECK: + S = new (Context) ObjCFeatureCheckExpr(Empty); + break; + case STMT_SEH_LEAVE: S = new (Context) SEHLeaveStmt(Empty); break; diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index ebfa17eb9f5a55..c7e427532a9512 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -1638,6 +1638,13 @@ void ASTStmtWriter::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) Code = serialization::EXPR_OBJC_AVAILABILITY_CHECK; } +void ASTStmtWriter::VisitObjCFeatureCheckExpr(ObjCFeatureCheckExpr *E) { + VisitExpr(E); + Record.AddSourceRange(E->getSourceRange()); + Record.AddIdentifierRef(E->getFeatureName()); + Code = serialization::EXPR_OBJC_FEATURE_CHECK; +} + //===----------------------------------------------------------------------===// // C++ Expressions and Statements. //===----------------------------------------------------------------------===// diff --git a/clang/test/CodeGen/feature-availability.c b/clang/test/CodeGen/feature-availability.c new file mode 100644 index 00000000000000..f05f39d963c996 --- /dev/null +++ b/clang/test/CodeGen/feature-availability.c @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -emit-llvm -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_S0:.*]] = type { i32 } +// CHECK: @g0 = external global i32, align 4 +// CHECK-NOT: @g1 +// CHECK-NOT: @g2 + +__attribute__((feature(feature1))) int func0(void); +__attribute__((feature(feature2))) int func1(void); +int func2(void); + +__attribute__((feature(feature1))) extern int g0; +__attribute__((feature(feature2))) int g1 = 100; +__attribute__((feature(feature2))) int g2; + +struct __attribute__((feature(feature1))) S0 { + int d0; +}; + +// CHECK-LABEL: define void @test0() +// CHECK-NOT: br +// CHECK: call i32 @func0() +// CHECK: store i32 123, ptr @g0, align 4 +// CHECK-NOT: func1() +// CHECK-NOT: func2() +void test0(void) { + if (__builtin_feature(feature1)) { + func0(); + g0 = 123; + } + + if (__builtin_feature(feature2)) { + func1(); + g1 = 123; + } + + if (__builtin_feature(feature1)) + if (__builtin_feature(feature2)) { + func2(); + } +} + +// CHECK-LABEL: define void @test1() +__attribute__((feature(feature1))) +void test1(void) { +} + +// CHECK-NOT: @test2( +__attribute__((feature(feature2))) +void test2(void) { +} + +// CHECK-LABEL: define void @test3( +// CHECK: %[[D0:.*]] = getelementptr inbounds nuw %[[STRUCT_S0]], ptr %{{.*}}, i32 0, i32 0 +// CHECK: store i32 134, ptr %[[D0]], align 4 +__attribute__((feature(feature1))) +void test3(struct S0 *s0) { + s0->d0 = 134; +} diff --git a/clang/test/CodeGenObjC/feature-availability.m b/clang/test/CodeGenObjC/feature-availability.m new file mode 100644 index 00000000000000..ef1ba87f99bbac --- /dev/null +++ b/clang/test/CodeGenObjC/feature-availability.m @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -triple arm64-apple-macosx -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:off -emit-llvm -o - %s | FileCheck %s + +// CHECK: @"OBJC_CLASS_$_C0" = +// CHECK: @"OBJC_METACLASS_$_C0" = +// CHECK: @OBJC_CLASS_NAME_ = private unnamed_addr constant [3 x i8] c"C0\00", +// CHECK: @"_OBJC_METACLASS_RO_$_C0" = +// CHECK: @OBJC_METH_VAR_NAME_ = private unnamed_addr constant [3 x i8] c"m0\00", +// CHECK: @"_OBJC_$_INSTANCE_METHODS_C0" = +// CHECK: @"_OBJC_CLASS_RO_$_C0" = +// CHECK: @OBJC_CLASS_NAME_.1 = private unnamed_addr constant [5 x i8] c"Cat0\00", +// CHECK-NOT: C1 +// CHECK-NOT: Cat1 +// CHECK: @"OBJC_LABEL_CLASS_$" = private global [1 x ptr] [ptr @"OBJC_CLASS_$_C0"], section "__DATA,__objc_classlist,regular,no_dead_strip", align 8 + +__attribute__((feature(feature1))) +@interface C0 +-(void)m0; +@end + +// CHECK: define internal void @"\01-[C0 m0]"( + +@implementation C0 +-(void)m0 { +} +@end + +@interface C0(Cat0) +@end + +@implementation C0(Cat0) +@end + +__attribute__((feature(feature2))) +@interface C1 +-(void)m1; +@end + +// CHECK-NOT: void @"\01-[C1 m1]"( +@implementation C1 +-(void)m1 { +} +@end + +@interface C1(Cat1) +@end + +@implementation C1(Cat1) +@end + diff --git a/clang/test/SemaObjCXX/feature-availability.mm b/clang/test/SemaObjCXX/feature-availability.mm new file mode 100644 index 00000000000000..446b45573838a2 --- /dev/null +++ b/clang/test/SemaObjCXX/feature-availability.mm @@ -0,0 +1,177 @@ +// RUN: %clang_cc1 -fblocks -ffeature-availability=feature1:on -ffeature-availability=feature2:on -fsyntax-only -verify %s + +void func0(); +__attribute__((feature(feature1))) int func1(); +__attribute__((feature(feature2))) void func2(); + +__attribute__((feature(feature1))) extern int g0; +__attribute__((feature(feature1))) int g1; +int g2 = func1(); // expected-error {{use of 'func1' needs guard for feature feature1}} +__attribute__((feature(feature1))) int g3 = func1(); + +struct S0 { + void m0() { + func1(); // expected-error{{use of 'func1' needs guard for feature feature1}} + } + __attribute__((feature(feature1))) void m1(); + __attribute__((feature(feature1))) int d1; +}; + +struct __attribute__((feature(feature1))) S1 { + void m0() { + func1(); + func2(); // expected-error{{use of 'func2' needs guard for feature feature2}} + } + __attribute__((feature(feature2))) void m1(); + __attribute__((feature(feature2))) int d1; +}; + +struct S2 { + S1 s1; // expected-error{{use of 'S1' needs guard for feature feature1}} +}; + +struct __attribute__((feature(feature2))) S3 : S1 {}; // expected-error {{use of 'S1' needs guard for feature feature1}} +struct __attribute__((feature(feature1))) S4 : S1 {}; + +__attribute__((feature(feature1))) +@interface Base0 +@end + +@interface Derived0 : Base0 { // expected-error {{use of 'Base0' needs guard for feature feature1}} + S1 *ivar0; // expected-error {{use of 'S1' needs guard for feature feature1}} +} +@property S1 *p0; // expected-error {{use of 'S1' needs guard for feature feature1}} +@property int p1 __attribute__((feature(feature1))); +@end + +@interface Derived0() +@property S1 *p0_Ext; // expected-error {{use of 'S1' needs guard for feature feature1}} +@end + +@implementation Derived0 +-(void)m0 { + func1(); // expected-error {{use of 'func1' needs guard for feature feature1}} +} +-(void)m1 { + self.p1 = 1; // expected-error {{use of 'setP1:' needs guard for feature feature1}} +} +@end + +@interface Derived0(C0) +@property S1 *p0_C0; // expected-error {{use of 'S1' needs guard for feature feature1}} +@end + +__attribute__((feature(feature1))) +@interface Derived1 : Base0 { + S1 *ivar0; +} +@property S1 *p0; +@end + +@interface Derived1() +@property S1 *p0_Ext; +@end + +@implementation Derived1 +-(void)m0 { + func1(); +} +@end + +@interface Derived1(C0) +@property S1 *p0_C0; +@end + +@protocol P0 +@property S1 *p0; // expected-error {{use of 'S1' needs guard for feature feature1}} +@end + +void test0(S0 *s0) { + func0(); + func1(); // expected-error{{use of 'func1' needs guard for feature feature1}} + s0->m0(); + s0->m1(); // expected-error{{use of 'm1' needs guard for feature feature1}} + s0->d1 = 0; // expected-error{{use of 'd1' needs guard for feature feature1}} + + if (__builtin_feature(feature1)) + if (__builtin_feature(feature2)) + func1(); + + if (__builtin_feature(feature2)) + func1(); // expected-error{{use of 'func1' needs guard for feature feature1}} + + if (__builtin_feature(feature1)) + label0: // expected-error{{labels cannot appear in regions conditionally guarded by features}} + ; + + if (__builtin_feature(feature1)) { + [](){ + func1(); + func2(); // expected-error{{use of 'func2' needs guard for feature feature2}} + }(); + + ^{ + func1(); + func2(); // expected-error{{use of 'func2' needs guard for feature feature2}} + }(); + } + + label1: + ; +} + +__attribute__((feature(feature1))) +void test1(S0 *s0) { + func0(); + func1(); + s0->m0(); + s0->m1(); + s0->d1 = 0; + + if (__builtin_feature(feature1)) + if (__builtin_feature(feature2)) + func1(); + + if (__builtin_feature(feature2)) + func1(); + + if (__builtin_feature(feature1)) + label0: // expected-error{{labels cannot appear in regions conditionally guarded by features}} + ; + + if (__builtin_feature(feature1)) { + [](){ + func1(); + func2(); // expected-error{{use of 'func2' needs guard for feature feature2}} + }(); + + ^{ + func1(); + func2(); // expected-error{{use of 'func2' needs guard for feature feature2}} + }(); + } + + label1: + ; +} + +void test2(S1 *s1) { // expected-error{{use of 'S1' needs guard for feature feature1}} + s1->m1(); // expected-error{{use of 'm1' needs guard for feature feature2}} + s1->d1 = 0; // expected-error{{use of 'd1' needs guard for feature feature2}} + + if (__builtin_feature(feature1)) { + s1->m1(); // expected-error{{use of 'm1' needs guard for feature feature2}} + s1->d1 = 0; // expected-error{{use of 'd1' needs guard for feature feature2}} + } + + if (__builtin_feature(feature2)) { + s1->m1(); + s1->d1 = 0; + } + + if (__builtin_feature(feature1)) + if (__builtin_feature(feature2)) { + s1->m1(); + s1->d1 = 0; + } +}