summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Pilkington <erik.pilkington@gmail.com>2016-07-16 00:35:23 +0000
committerErik Pilkington <erik.pilkington@gmail.com>2016-07-16 00:35:23 +0000
commitd3b0a63ca6016aceefdc147eeb88e90a988dacca (patch)
tree2576c0af7b32df8726bede4d459e7b275416754e
parentc45b5c87351f6fd018c60eb4839db3a12f289179 (diff)
[ObjC] Implement @available in the Parser and AST
This patch adds a new AST node: ObjCAvailabilityCheckExpr, and teaches the Parser and Sema to generate it. This node represents an availability check of the form: @available(macos 10.10, *); Which will eventually compile to a runtime check of the host's OS version. This is the first patch of the feature I proposed here: http://lists.llvm.org/pipermail/cfe-dev/2016-July/049851.html Differential Revision: https://reviews.llvm.org/D22171 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@275654 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang-c/Index.h6
-rw-r--r--include/clang/AST/Availability.h63
-rw-r--r--include/clang/AST/ExprObjC.h47
-rw-r--r--include/clang/AST/RecursiveASTVisitor.h1
-rw-r--r--include/clang/Basic/DiagnosticGroups.td1
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td15
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--include/clang/Basic/StmtNodes.td1
-rw-r--r--include/clang/Basic/TokenKinds.def3
-rw-r--r--include/clang/Parse/Parser.h4
-rw-r--r--include/clang/Sema/Sema.h5
-rw-r--r--include/clang/Serialization/ASTBitCodes.h4
-rw-r--r--lib/AST/Expr.cpp1
-rw-r--r--lib/AST/ExprClassification.cpp1
-rw-r--r--lib/AST/ExprConstant.cpp1
-rw-r--r--lib/AST/ItaniumMangle.cpp1
-rw-r--r--lib/AST/StmtPrinter.cpp5
-rw-r--r--lib/AST/StmtProfile.cpp5
-rw-r--r--lib/Parse/ParseDecl.cpp2
-rw-r--r--lib/Parse/ParseExpr.cpp116
-rw-r--r--lib/Parse/ParseObjc.cpp2
-rw-r--r--lib/Sema/SemaExceptionSpec.cpp1
-rw-r--r--lib/Sema/SemaExpr.cpp24
-rw-r--r--lib/Sema/TreeTransform.h6
-rw-r--r--lib/Serialization/ASTReaderStmt.cpp11
-rw-r--r--lib/Serialization/ASTWriterStmt.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp1
-rw-r--r--test/Parser/objc-available.m24
-rw-r--r--tools/libclang/CIndex.cpp2
-rw-r--r--tools/libclang/CXCursor.cpp6
30 files changed, 364 insertions, 5 deletions
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index ff3c86ff2e..13db2085ba 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -2014,7 +2014,11 @@ enum CXCursorKind {
*/
CXCursor_OMPArraySectionExpr = 147,
- CXCursor_LastExpr = CXCursor_OMPArraySectionExpr,
+ /** \brief Represents an @available(...) check.
+ */
+ CXCursor_ObjCAvailabilityCheckExpr = 148,
+
+ CXCursor_LastExpr = CXCursor_ObjCAvailabilityCheckExpr,
/* Statements */
CXCursor_FirstStmt = 200,
diff --git a/include/clang/AST/Availability.h b/include/clang/AST/Availability.h
new file mode 100644
index 0000000000..5ed8313784
--- /dev/null
+++ b/include/clang/AST/Availability.h
@@ -0,0 +1,63 @@
+//===--- Availability.h - Classes for availability --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This files defines some classes that implement availability checking.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_AVAILABILITY_H
+#define LLVM_CLANG_AST_AVAILABILITY_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+
+/// \brief One specifier in an @available expression.
+///
+/// \code
+/// @available(macos 10.10, *)
+/// \endcode
+///
+/// Here, 'macos 10.10' and '*' both map to an instance of this type.
+///
+class AvailabilitySpec {
+ /// Represents the version that this specifier requires. If the host OS
+ /// version is greater than or equal to Version, the @available will evaluate
+ /// to true.
+ VersionTuple Version;
+
+ /// Name of the platform that Version corresponds to.
+ StringRef Platform;
+
+ SourceLocation BeginLoc, EndLoc;
+
+public:
+ AvailabilitySpec(VersionTuple Version, StringRef Platform,
+ SourceLocation BeginLoc, SourceLocation EndLoc)
+ : Version(Version), Platform(Platform), BeginLoc(BeginLoc),
+ EndLoc(EndLoc) {}
+
+ /// This constructor is used when representing the '*' case.
+ AvailabilitySpec(SourceLocation StarLoc)
+ : BeginLoc(StarLoc), EndLoc(StarLoc) {}
+
+ VersionTuple getVersion() const { return Version; }
+ StringRef getPlatform() const { return Platform; }
+ SourceLocation getBeginLoc() const { return BeginLoc; }
+ SourceLocation getEndLoc() const { return EndLoc; }
+
+ /// Returns true when this represents the '*' case.
+ bool isOtherPlatformSpec() const { return Version.empty(); }
+};
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h
index 61e6383bff..5f9623db24 100644
--- a/include/clang/AST/ExprObjC.h
+++ b/include/clang/AST/ExprObjC.h
@@ -1562,7 +1562,52 @@ public:
return T->getStmtClass() == ObjCBridgedCastExprClass;
}
};
-
+
+/// \brief A runtime availability query.
+///
+/// There are 2 ways to spell this node:
+/// \code
+/// @available(macos 10.10, ios 8, *); // Objective-C
+/// __builtin_available(macos 10.10, ios 8, *); // C, C++, and Objective-C
+/// \endcode
+///
+/// Note that we only need to keep track of one \c VersionTuple here, which is
+/// the one that corresponds to the current deployment target. This is meant to
+/// be used in the condition of an \c if, but it is also usable as top level
+/// expressions.
+///
+class ObjCAvailabilityCheckExpr : public Expr {
+ VersionTuple VersionToCheck;
+ SourceLocation AtLoc, RParen;
+
+ friend class ASTStmtReader;
+public:
+ ObjCAvailabilityCheckExpr(VersionTuple VersionToCheck, SourceLocation AtLoc,
+ SourceLocation RParen, QualType Ty)
+ : Expr(ObjCAvailabilityCheckExprClass, Ty, VK_RValue, OK_Ordinary, false,
+ false, false, false),
+ VersionToCheck(VersionToCheck), AtLoc(AtLoc), RParen(RParen) {}
+
+ explicit ObjCAvailabilityCheckExpr(EmptyShell Shell)
+ : Expr(ObjCAvailabilityCheckExprClass, Shell) {}
+
+ SourceLocation getLocStart() const { return AtLoc; }
+ SourceLocation getLocEnd() const { return RParen; }
+ SourceRange getSourceRange() const { return {AtLoc, RParen}; }
+
+ /// \brief This may be '*', in which case this should fold to true.
+ bool hasVersion() const { return !VersionToCheck.empty(); }
+ VersionTuple getVersion() { return VersionToCheck; }
+
+ child_range children() {
+ return child_range(child_iterator(), child_iterator());
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == ObjCAvailabilityCheckExprClass;
+ }
+};
+
} // end namespace clang
#endif
diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h
index 94637a568c..f918b830d4 100644
--- a/include/clang/AST/RecursiveASTVisitor.h
+++ b/include/clang/AST/RecursiveASTVisitor.h
@@ -2352,6 +2352,7 @@ DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, {})
DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, {
TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc()));
})
+DEF_TRAVERSE_STMT(ObjCAvailabilityCheckExpr, {})
DEF_TRAVERSE_STMT(ParenExpr, {})
DEF_TRAVERSE_STMT(ParenListExpr, {})
DEF_TRAVERSE_STMT(PredefinedExpr, {})
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 979c81f636..72dfedbbae 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -96,6 +96,7 @@ def DeprecatedAttributes : DiagGroup<"deprecated-attributes">;
def DeprecatedDeclarations : DiagGroup<"deprecated-declarations">;
def UnavailableDeclarations : DiagGroup<"unavailable-declarations">;
def PartialAvailability : DiagGroup<"partial-availability">;
+def UnguardedAvailability : DiagGroup<"unguarded-availability">;
def DeprecatedImplementations :DiagGroup<"deprecated-implementations">;
def DeprecatedIncrementBool : DiagGroup<"deprecated-increment-bool">;
def DeprecatedRegister : DiagGroup<"deprecated-register">;
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index e01d9463b3..e5c64681e4 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -823,6 +823,21 @@ def warn_availability_and_unavailable : Warning<
"'unavailable' availability overrides all other availability information">,
InGroup<Availability>;
+// @available(...)
+def err_avail_query_expected_condition : Error<
+ "expected an availability condition here">;
+def err_avail_query_expected_platform_name : Error<
+ "expected a platform name here">;
+
+def err_avail_query_unrecognized_platform_name : Error<
+ "unrecognized platform name %0">;
+def err_availability_query_wildcard_required: Error<
+ "must handle potential future platforms with '*'">;
+def err_availability_query_repeated_platform: Error<
+ "version for '%0' already specified">;
+def err_availability_query_repeated_star : Error<
+ "'*' query has already been specified">;
+
// Type safety attributes
def err_type_safety_unknown_flag : Error<
"invalid comparison flag %0; use 'layout_compatible' or 'must_be_null'">;
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 45e77d84d4..1203fe765b 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2599,6 +2599,9 @@ def note_overridden_method : Note<
def note_protocol_method : Note<
"protocol method is here">;
+def warn_available_using_star_case : Warning<
+ "using '*' case here, platform %0 is not accounted for">, InGroup<UnguardedAvailability>;
+
// Thread Safety Attributes
def warn_invalid_capability_name : Warning<
"invalid capability name '%0'; capability name must be 'mutex' or 'role'">,
diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td
index df7e7362e2..973b3bb6fd 100644
--- a/include/clang/Basic/StmtNodes.td
+++ b/include/clang/Basic/StmtNodes.td
@@ -165,6 +165,7 @@ def ObjCIsaExpr : DStmt<Expr>;
def ObjCIndirectCopyRestoreExpr : DStmt<Expr>;
def ObjCBoolLiteralExpr : DStmt<Expr>;
def ObjCSubscriptRefExpr : DStmt<Expr>;
+def ObjCAvailabilityCheckExpr : DStmt<Expr>;
// Obj-C ARC Expressions.
def ObjCBridgedCastExpr : DStmt<ExplicitCastExpr>;
diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def
index a08d280e7e..882c6e4cac 100644
--- a/include/clang/Basic/TokenKinds.def
+++ b/include/clang/Basic/TokenKinds.def
@@ -630,6 +630,8 @@ KEYWORD(__builtin_convertvector , KEYALL)
ALIAS("__char16_t" , char16_t , KEYCXX)
ALIAS("__char32_t" , char32_t , KEYCXX)
+KEYWORD(__builtin_available , KEYALL)
+
// Clang-specific keywords enabled only in testing.
TESTING_KEYWORD(__unknown_anytype , KEYALL)
@@ -668,6 +670,7 @@ OBJC2_AT_KEYWORD(optional)
OBJC2_AT_KEYWORD(synthesize)
OBJC2_AT_KEYWORD(dynamic)
OBJC2_AT_KEYWORD(import)
+OBJC2_AT_KEYWORD(available)
// TODO: What to do about context-sensitive keywords like:
// bycopy/byref/in/inout/oneway/out?
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 9d59f257af..5838a447c3 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_PARSE_PARSER_H
#define LLVM_CLANG_PARSE_PARSER_H
+#include "clang/AST/Availability.h"
#include "clang/Basic/OpenMPKinds.h"
#include "clang/Basic/OperatorPrecedence.h"
#include "clang/Basic/Specifiers.h"
@@ -2244,6 +2245,9 @@ private:
SourceLocation ScopeLoc,
AttributeList::Syntax Syntax);
+ Optional<AvailabilitySpec> ParseAvailabilitySpec();
+ ExprResult ParseAvailabilityCheckExpr(SourceLocation StartLoc);
+
void ParseObjCBridgeRelatedAttribute(IdentifierInfo &ObjCBridgeRelated,
SourceLocation ObjCBridgeRelatedLoc,
ParsedAttributes &attrs,
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 4f2f7300ae..25aa805dac 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -16,6 +16,7 @@
#define LLVM_CLANG_SEMA_SEMA_H
#include "clang/AST/Attr.h"
+#include "clang/AST/Availability.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
@@ -4739,6 +4740,10 @@ public:
/// ActOnObjCBoolLiteral - Parse {__objc_yes,__objc_no} literals.
ExprResult ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind);
+ ExprResult
+ ActOnObjCAvailabilityCheckExpr(llvm::ArrayRef<AvailabilitySpec> AvailSpecs,
+ SourceLocation AtLoc, SourceLocation RParen);
+
/// ActOnCXXNullPtrLiteral - Parse 'nullptr'.
ExprResult ActOnCXXNullPtrLiteral(SourceLocation Loc);
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index 809dbc8457..79c6a06222 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -1349,8 +1349,10 @@ namespace clang {
STMT_OBJC_AT_THROW,
/// \brief An ObjCAutoreleasePoolStmt record.
STMT_OBJC_AUTORELEASE_POOL,
- /// \brief A ObjCBoolLiteralExpr record.
+ /// \brief An ObjCBoolLiteralExpr record.
EXPR_OBJC_BOOL_LITERAL,
+ /// \brief An ObjCAvailabilityCheckExpr record.
+ EXPR_OBJC_AVAILABILITY_CHECK,
// C++
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index bd4cf396bf..091e8787d8 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -2856,6 +2856,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case ObjCStringLiteralClass:
case ObjCEncodeExprClass:
case ObjCBoolLiteralExprClass:
+ case ObjCAvailabilityCheckExprClass:
case CXXUuidofExprClass:
case OpaqueValueExprClass:
// These never have a side-effect.
diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp
index 642cdd1a95..89cc9bc18e 100644
--- a/lib/AST/ExprClassification.cpp
+++ b/lib/AST/ExprClassification.cpp
@@ -178,6 +178,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::ObjCArrayLiteralClass:
case Expr::ObjCDictionaryLiteralClass:
case Expr::ObjCBoolLiteralExprClass:
+ case Expr::ObjCAvailabilityCheckExprClass:
case Expr::ParenListExprClass:
case Expr::SizeOfPackExprClass:
case Expr::SubstNonTypeTemplateParmPackExprClass:
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index 28bdf6da3a..a25a6c7e51 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -9402,6 +9402,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::ObjCPropertyRefExprClass:
case Expr::ObjCSubscriptRefExprClass:
case Expr::ObjCIsaExprClass:
+ case Expr::ObjCAvailabilityCheckExprClass:
case Expr::ShuffleVectorExprClass:
case Expr::ConvertVectorExprClass:
case Expr::BlockExprClass:
diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp
index 5a7c45594e..694fde3175 100644
--- a/lib/AST/ItaniumMangle.cpp
+++ b/lib/AST/ItaniumMangle.cpp
@@ -3239,6 +3239,7 @@ recurse:
case Expr::ObjCDictionaryLiteralClass:
case Expr::ObjCSubscriptRefExprClass:
case Expr::ObjCIndirectCopyRestoreExprClass:
+ case Expr::ObjCAvailabilityCheckExprClass:
case Expr::OffsetOfExprClass:
case Expr::PredefinedExprClass:
case Expr::ShuffleVectorExprClass:
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 64d9a32956..8797a13335 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -497,6 +497,11 @@ void StmtPrinter::VisitObjCAtThrowStmt(ObjCAtThrowStmt *Node) {
OS << ";\n";
}
+void StmtPrinter::VisitObjCAvailabilityCheckExpr(
+ ObjCAvailabilityCheckExpr *Node) {
+ OS << "@available(...)";
+}
+
void StmtPrinter::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Node) {
Indent() << "@synchronized (";
PrintExpr(Node->getSynchExpr());
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 5ce6e5e13a..0a39413853 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -1632,6 +1632,11 @@ void StmtProfiler::VisitObjCBridgedCastExpr(const ObjCBridgedCastExpr *S) {
ID.AddBoolean(S->getBridgeKind());
}
+void StmtProfiler::VisitObjCAvailabilityCheckExpr(
+ const ObjCAvailabilityCheckExpr *S) {
+ VisitExpr(S);
+}
+
void StmtProfiler::VisitDecl(const Decl *D) {
ID.AddInteger(D? D->getKind() : 0);
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 9a754ee23c..45e1c3e465 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -720,7 +720,7 @@ static bool VersionNumberSeparator(const char Separator) {
/// simple-integer ',' simple-integer
/// simple-integer ',' simple-integer ',' simple-integer
VersionTuple Parser::ParseVersionTuple(SourceRange &Range) {
- Range = Tok.getLocation();
+ Range = SourceRange(Tok.getLocation(), Tok.getEndLoc());
if (!Tok.is(tok::numeric_constant)) {
Diag(Tok, diag::err_expected_version);
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 4e39d42ea6..47d1326b79 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -1009,6 +1009,8 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
case tok::kw__Generic: // primary-expression: generic-selection [C11 6.5.1]
Res = ParseGenericSelectionExpression();
break;
+ case tok::kw___builtin_available:
+ return ParseAvailabilityCheckExpr(Tok.getLocation());
case tok::kw___builtin_va_arg:
case tok::kw___builtin_offsetof:
case tok::kw___builtin_choose_expr:
@@ -2869,3 +2871,117 @@ ExprResult Parser::ParseObjCBoolLiteral() {
tok::TokenKind Kind = Tok.getKind();
return Actions.ActOnObjCBoolLiteral(ConsumeToken(), Kind);
}
+
+/// Validate availability spec list, emitting diagnostics if necessary. Returns
+/// true if invalid.
+static bool CheckAvailabilitySpecList(Parser &P,
+ ArrayRef<AvailabilitySpec> AvailSpecs) {
+ llvm::SmallSet<StringRef, 4> Platforms;
+ bool HasOtherPlatformSpec = false;
+ bool Valid = true;
+ for (const auto &Spec : AvailSpecs) {
+ if (Spec.isOtherPlatformSpec()) {
+ if (HasOtherPlatformSpec) {
+ P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_star);
+ Valid = false;
+ }
+
+ HasOtherPlatformSpec = true;
+ continue;
+ }
+
+ bool Inserted = Platforms.insert(Spec.getPlatform()).second;
+ if (!Inserted) {
+ // Rule out multiple version specs referring to the same platform.
+ // For example, we emit an error for:
+ // @available(macos 10.10, macos 10.11, *)
+ StringRef Platform = Spec.getPlatform();
+ P.Diag(Spec.getBeginLoc(), diag::err_availability_query_repeated_platform)
+ << Spec.getEndLoc() << Platform;
+ Valid = false;
+ }
+ }
+
+ if (!HasOtherPlatformSpec) {
+ SourceLocation InsertWildcardLoc = AvailSpecs.back().getEndLoc();
+ P.Diag(InsertWildcardLoc, diag::err_availability_query_wildcard_required)
+ << FixItHint::CreateInsertion(InsertWildcardLoc, ", *");
+ return true;
+ }
+
+ return !Valid;
+}
+
+/// Parse availability query specification.
+///
+/// availability-spec:
+/// '*'
+/// identifier version-tuple
+Optional<AvailabilitySpec> Parser::ParseAvailabilitySpec() {
+ if (Tok.is(tok::star)) {
+ return AvailabilitySpec(ConsumeToken());
+ } else {
+ // Parse the platform name.
+ if (Tok.isNot(tok::identifier)) {
+ Diag(Tok, diag::err_avail_query_expected_platform_name);
+ return None;
+ }
+
+ IdentifierLoc *PlatformIdentifier = ParseIdentifierLoc();
+ SourceRange VersionRange;
+ VersionTuple Version = ParseVersionTuple(VersionRange);
+
+ if (Version.empty())
+ return None;
+
+ StringRef Platform = PlatformIdentifier->Ident->getName();
+
+ if (AvailabilityAttr::getPrettyPlatformName(Platform).empty()) {
+ Diag(PlatformIdentifier->Loc,
+ diag::err_avail_query_unrecognized_platform_name)
+ << Platform;
+ return None;
+ }
+
+ return AvailabilitySpec(Version, Platform, PlatformIdentifier->Loc,
+ VersionRange.getEnd());
+ }
+}
+
+ExprResult Parser::ParseAvailabilityCheckExpr(SourceLocation BeginLoc) {
+ assert(Tok.is(tok::kw___builtin_available) ||
+ Tok.isObjCAtKeyword(tok::objc_available));
+
+ // Eat the available or __builtin_available.
+ ConsumeToken();
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ if (Parens.expectAndConsume())
+ return ExprError();
+
+ SmallVector<AvailabilitySpec, 4> AvailSpecs;
+ bool HasError = false;
+ while (true) {
+ Optional<AvailabilitySpec> Spec = ParseAvailabilitySpec();
+ if (!Spec)
+ HasError = true;
+ else
+ AvailSpecs.push_back(*Spec);
+
+ if (!TryConsumeToken(tok::comma))
+ break;
+ }
+
+ if (HasError) {
+ SkipUntil(tok::r_paren, StopAtSemi);
+ return ExprError();
+ }
+
+ CheckAvailabilitySpecList(*this, AvailSpecs);
+
+ if (Parens.consumeClose())
+ return ExprError();
+
+ return Actions.ActOnObjCAvailabilityCheckExpr(AvailSpecs, BeginLoc,
+ Parens.getCloseLocation());
+}
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 22c832650b..67abe5839b 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -2858,6 +2858,8 @@ ExprResult Parser::ParseObjCAtExpression(SourceLocation AtLoc) {
return ParsePostfixExpressionSuffix(ParseObjCProtocolExpression(AtLoc));
case tok::objc_selector:
return ParsePostfixExpressionSuffix(ParseObjCSelectorExpression(AtLoc));
+ case tok::objc_available:
+ return ParseAvailabilityCheckExpr(AtLoc);
default: {
const char *str = nullptr;
if (GetLookAheadToken(1).is(tok::l_brace)) {
diff --git a/lib/Sema/SemaExceptionSpec.cpp b/lib/Sema/SemaExceptionSpec.cpp
index 61ea87aa9d..4a21eb308f 100644
--- a/lib/Sema/SemaExceptionSpec.cpp
+++ b/lib/Sema/SemaExceptionSpec.cpp
@@ -1146,6 +1146,7 @@ CanThrowResult Sema::canThrow(const Expr *E) {
case Expr::ObjCIndirectCopyRestoreExprClass:
case Expr::ObjCProtocolExprClass:
case Expr::ObjCSelectorExprClass:
+ case Expr::ObjCAvailabilityCheckExprClass:
case Expr::OffsetOfExprClass:
case Expr::PackExpansionExprClass:
case Expr::PseudoObjectExprClass:
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index 0c8a913694..57159fb597 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -15083,3 +15083,27 @@ Sema::ActOnObjCBoolLiteral(SourceLocation OpLoc, tok::TokenKind Kind) {
return new (Context)
ObjCBoolLiteralExpr(Kind == tok::kw___objc_yes, BoolT, OpLoc);
}
+
+ExprResult Sema::ActOnObjCAvailabilityCheckExpr(
+ llvm::ArrayRef<AvailabilitySpec> AvailSpecs, SourceLocation AtLoc,
+ SourceLocation RParen) {
+
+ StringRef Platform = getASTContext().getTargetInfo().getPlatformName();
+
+ auto Spec = std::find_if(AvailSpecs.begin(), AvailSpecs.end(),
+ [&](const AvailabilitySpec &Spec) {
+ return Spec.getPlatform() == Platform;
+ });
+
+ VersionTuple Version;
+ if (Spec != AvailSpecs.end())
+ Version = Spec->getVersion();
+ else
+ // This is the '*' case in @available. We should diagnose this; the
+ // programmer should explicitly account for this case if they target this
+ // platform.
+ Diag(AtLoc, diag::warn_available_using_star_case) << RParen << Platform;
+
+ return new (Context)
+ ObjCAvailabilityCheckExpr(Version, AtLoc, RParen, Context.BoolTy);
+}
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 0dbdebf5db..36859f61d3 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -11112,6 +11112,12 @@ TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) {
Result.get());
}
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformObjCAvailabilityCheckExpr(
+ ObjCAvailabilityCheckExpr *E) {
+ return E;
+}
+
template<typename Derived>
ExprResult
TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) {
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 2fa3a4400b..395da42d4f 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -1182,6 +1182,14 @@ void ASTStmtReader::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) {
E->setLocation(ReadSourceLocation(Record, Idx));
}
+void ASTStmtReader::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
+ VisitExpr(E);
+ SourceRange R = Reader.ReadSourceRange(F, Record, Idx);
+ E->AtLoc = R.getBegin();
+ E->RParen = R.getEnd();
+ E->VersionToCheck = Reader.ReadVersionTuple(Record, Idx);
+}
+
//===----------------------------------------------------------------------===//
// C++ Expressions and Statements
//===----------------------------------------------------------------------===//
@@ -3221,6 +3229,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
case EXPR_OBJC_BOOL_LITERAL:
S = new (Context) ObjCBoolLiteralExpr(Empty);
break;
+ case EXPR_OBJC_AVAILABILITY_CHECK:
+ S = new (Context) ObjCAvailabilityCheckExpr(Empty);
+ break;
case STMT_SEH_LEAVE:
S = new (Context) SEHLeaveStmt(Empty);
break;
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 73f7219ba5..84e718e9ef 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -1142,6 +1142,13 @@ void ASTStmtWriter::VisitObjCBoolLiteralExpr(ObjCBoolLiteralExpr *E) {
Code = serialization::EXPR_OBJC_BOOL_LITERAL;
}
+void ASTStmtWriter::VisitObjCAvailabilityCheckExpr(ObjCAvailabilityCheckExpr *E) {
+ VisitExpr(E);
+ Record.AddSourceRange(E->getSourceRange());
+ Record.AddVersionTuple(E->getVersion());
+ Code = serialization::EXPR_OBJC_AVAILABILITY_CHECK;
+}
+
//===----------------------------------------------------------------------===//
// C++ Expressions and Statements.
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index baa03e0e51..405aecdee0 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -916,6 +916,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXScalarValueInitExprClass:
case Stmt::CXXBoolLiteralExprClass:
case Stmt::ObjCBoolLiteralExprClass:
+ case Stmt::ObjCAvailabilityCheckExprClass:
case Stmt::FloatingLiteralClass:
case Stmt::NoInitExprClass:
case Stmt::SizeOfPackExprClass:
diff --git a/test/Parser/objc-available.m b/test/Parser/objc-available.m
new file mode 100644
index 0000000000..ca30753144
--- /dev/null
+++ b/test/Parser/objc-available.m
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -fsyntax-only -Wunguarded-availability -triple x86_64-apple-macosx10.10.0 -verify %s
+
+void f() {
+
+ if (@available(macos 10.12, *)) {}
+ else if (@available(macos 10.11, *)) {}
+ else {}
+
+ (void)__builtin_available(ios 8, macos 10.10, *);
+
+ (void)@available(macos 10.11); // expected-error{{must handle potential future platforms with '*'}}
+ (void)@available(macos 10.11, macos 10.11, *); // expected-error{{version for 'macos' already specified}}
+
+ (void)@available(erik_os 10.11, *); // expected-error{{unrecognized platform name erik_os}}
+
+ (void)@available(erik_os 10.10, hat_os 1.0, *); // expected-error 2 {{unrecognized platform name}}
+
+ (void)@available(ios 8, *); // expected-warning{{using '*' case here, platform macos is not accounted for}}
+
+ (void)@available(); // expected-error{{expected a platform name here}}
+ (void)@available(macos 10.10,); // expected-error{{expected a platform name here}}
+ (void)@available(macos); // expected-error{{expected a version}}
+ (void)@available; // expected-error{{expected '('}}
+}
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index f4f9c16225..027bf95b66 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -4611,6 +4611,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return cxstring::createRef("ObjCStringLiteral");
case CXCursor_ObjCBoolLiteralExpr:
return cxstring::createRef("ObjCBoolLiteralExpr");
+ case CXCursor_ObjCAvailabilityCheckExpr:
+ return cxstring::createRef("ObjCAvailabilityCheckExpr");
case CXCursor_ObjCSelfExpr:
return cxstring::createRef("ObjCSelfExpr");
case CXCursor_ObjCEncodeExpr:
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index 80bb569150..19ab0f929c 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -447,7 +447,11 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::ObjCBoolLiteralExprClass:
K = CXCursor_ObjCBoolLiteralExpr;
break;
-
+
+ case Stmt::ObjCAvailabilityCheckExprClass:
+ K = CXCursor_ObjCAvailabilityCheckExpr;
+ break;
+
case Stmt::ObjCBridgedCastExprClass:
K = CXCursor_ObjCBridgedCastExpr;
break;