]> granicus.if.org Git - clang/commitdiff
Implement a new 'availability' attribute, that allows one to specify
authorDouglas Gregor <dgregor@apple.com>
Wed, 23 Mar 2011 00:50:03 +0000 (00:50 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 23 Mar 2011 00:50:03 +0000 (00:50 +0000)
which versions of an OS provide a certain facility. For example,

  void foo()
  __attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6)));

says that the function "foo" was introduced in 10.2, deprecated in
10.4, and completely obsoleted in 10.6. This attribute ties in with
the deployment targets (e.g., -mmacosx-version-min=10.1 specifies that
we want to deploy back to Mac OS X 10.1). There are several concrete
behaviors that this attribute enables, as illustrated with the
function foo() above:

  - If we choose a deployment target >= Mac OS X 10.4, uses of "foo"
    will result in a deprecation warning, as if we had placed
    attribute((deprecated)) on it (but with a better diagnostic)
  - If we choose a deployment target >= Mac OS X 10.6, uses of "foo"
    will result in an "unavailable" warning (in C)/error (in C++), as
    if we had placed attribute((unavailable)) on it
  - If we choose a deployment target prior to 10.2, foo() is
    weak-imported (if it is a kind of entity that can be weak
    imported), as if we had placed the weak_import attribute on it.

Naturally, there can be multiple availability attributes on a
declaration, for different platforms; only the current platform
matters when checking availability attributes.

The only platforms this attribute currently works for are "ios" and
"macosx", since we already have -mxxxx-version-min flags for them and we
have experience there with macro tricks translating down to the
deprecated/unavailable/weak_import attributes. The end goal is to open
this up to other platforms, and even extension to other "platforms"
that are really libraries (say, through a #pragma clang
define_system), but that hasn't yet been designed and we may want to
shake out more issues with this narrower problem first.

Addresses <rdar://problem/6690412>.

As a drive-by bug-fix, if an entity is both deprecated and
unavailable, we only emit the "unavailable" diagnostic.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@128127 91177308-0d34-0410-b5e6-96231b3b80d8

45 files changed:
include/clang/AST/ASTContext.h
include/clang/AST/Attr.h
include/clang/AST/Decl.h
include/clang/AST/DeclBase.h
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/TargetInfo.h
include/clang/Basic/VersionTuple.h [new file with mode: 0644]
include/clang/Parse/Parser.h
include/clang/Sema/AttributeList.h
include/clang/Sema/Sema.h
include/clang/Serialization/ASTReader.h
include/clang/Serialization/ASTWriter.h
lib/AST/ASTContext.cpp
lib/AST/DeclBase.cpp
lib/AST/ExprConstant.cpp
lib/Basic/CMakeLists.txt
lib/Basic/TargetInfo.cpp
lib/Basic/Targets.cpp
lib/Basic/VersionTuple.cpp [new file with mode: 0644]
lib/CodeGen/CGObjCMac.cpp
lib/CodeGen/CodeGenModule.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/Parser.cpp
lib/Sema/AttributeList.cpp
lib/Sema/CodeCompleteConsumer.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaDeclObjC.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/SemaObjCProperty.cpp
lib/Sema/SemaOverload.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/CodeGenObjC/attr-availability.m [new file with mode: 0644]
test/Parser/attr-availability.c [new file with mode: 0644]
test/Sema/attr-availability-ios.c [new file with mode: 0644]
test/Sema/attr-availability-macosx.c [new file with mode: 0644]
test/Sema/attr-availability.c [new file with mode: 0644]
test/SemaCXX/attr-unavailable.cpp
test/SemaObjC/protocol-attribute.m
test/SemaObjC/special-dep-unavail-warning.m
tools/libclang/CIndex.cpp

index 9e3d10f63b16bcbe8d89699a16487b02a77ea0bb..1bf13f64372ad3a1450173fb511e442a1d71f6d4 100644 (file)
@@ -19,6 +19,7 @@
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/VersionTuple.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/PrettyPrinter.h"
index 502b9f775fc94a23010dde457aea2f7e5b3c07f5..719023926bae2615a31e7a6d919b3f84b2065839 100644 (file)
 #include "llvm/Support/Casting.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "clang/Basic/AttrKinds.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/VersionTuple.h"
 #include <cassert>
 #include <cstring>
 #include <algorithm>
index 08e4525f558ae061ee94f51be70605e409887b99..339074bde02dfebe4bac84f9e6b87d6e0f2424cb 100644 (file)
@@ -119,14 +119,6 @@ public:
     return getIdentifier() ? getIdentifier()->getName() : "";
   }
 
-  llvm::StringRef getMessageUnavailableAttr(bool unavailable) const {
-    if (!unavailable)
-      return "";
-    if (const UnavailableAttr *UA = getAttr<UnavailableAttr>())
-      return UA->getMessage();
-    return "";
-  }
-
   /// getNameAsString - Get a human-readable name for the declaration, even if
   /// it is one of the special kinds of names (C++ constructor, Objective-C
   /// selector, etc).  Creating this name requires expensive string
index b35d134d05345f9ba25f27a0a2cce01ba1c7823d..caab56a0cd24d27721e101ae2233a0b3068ea187 100644 (file)
@@ -62,6 +62,15 @@ public:
 
 namespace clang {
 
+  /// \brief Captures the result of checking the availability of a
+  /// declaration.
+  enum AvailabilityResult {
+    AR_Available = 0,
+    AR_NotYetIntroduced,
+    AR_Deprecated,
+    AR_Unavailable
+  };
+
 /// Decl - This represents one declaration (or definition), e.g. a variable,
 /// typedef, function, struct, etc.
 ///
@@ -399,6 +408,52 @@ public:
 
   void setUsed(bool U = true) { Used = U; }
 
+  /// \brief Determine the availability of the given declaration.
+  ///
+  /// This routine will determine the most restrictive availability of
+  /// the given declaration (e.g., preferring 'unavailable' to
+  /// 'deprecated').
+  ///
+  /// \param Message If non-NULL and the result is not \c
+  /// AR_Available, will be set to a (possibly empty) message
+  /// describing why the declaration has not been introduced, is
+  /// deprecated, or is unavailable.
+  AvailabilityResult getAvailability(std::string *Message = 0) const;
+
+  /// \brief Determine whether this declaration is marked 'deprecated'.
+  ///
+  /// \param Message If non-NULL and the declaration is deprecated,
+  /// this will be set to the message describing why the declaration
+  /// was deprecated (which may be empty).
+  bool isDeprecated(std::string *Message = 0) const {
+    return getAvailability(Message) == AR_Deprecated;
+  }
+
+  /// \brief Determine whether this declaration is marked 'unavailable'.
+  ///
+  /// \param Message If non-NULL and the declaration is unavailable,
+  /// this will be set to the message describing why the declaration
+  /// was made unavailable (which may be empty).
+  bool isUnavailable(std::string *Message = 0) const {
+    return getAvailability(Message) == AR_Unavailable;
+  }
+
+  /// \brief Determine whether this is a weak-imported symbol.
+  ///
+  /// Weak-imported symbols are typically marked with the
+  /// 'weak_import' attr\0ibute, but may also be marked with an
+  /// 'availability' attribute where we're targing a platform prior to
+  /// the introduction of this feature.
+  bool isWeakImported() const;
+
+  /// \brief Determines whether this symbol can be weak-imported,
+  /// e.g., whether it would be well-formed to add the weak_import
+  /// attribute.
+  ///
+  /// \param IsDefinition Set to \c true to indicate that this
+  /// declaration cannot be weak-imported because it has a definition.
+  bool canBeWeakImported(bool &IsDefinition) const;
+
   /// \brief Retrieve the level of precompiled header from which this
   /// declaration was generated.
   ///
index 3579e2c6f13d110c924b5d92094c0f09a4b99429..6c3d5e5d0c2bcb1e0e89a62769a6ebb2500a3618 100644 (file)
@@ -55,6 +55,9 @@ class TypeArgument<string name> : Argument<name>;
 class UnsignedArgument<string name> : Argument<name>;
 class VariadicUnsignedArgument<string name> : Argument<name>;
 
+// A version of the form major.minor[.subminor].
+class VersionArgument<string name> : Argument<name>;
+
 // This one's a doozy, so it gets its own special type
 // It can be an unsigned integer, or a type. Either can
 // be dependent.
@@ -134,6 +137,19 @@ def AsmLabel : InheritableAttr {
   let Args = [StringArgument<"Label">];
 }
 
+def Availability : InheritableAttr {
+  let Spellings = ["availability"];
+  let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">,
+              VersionArgument<"deprecated">, VersionArgument<"obsoleted">];
+  let AdditionalMembers =
+[{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) {
+    return llvm::StringSwitch<llvm::StringRef>(Platform)
+             .Case("ios", "iOS")
+             .Case("macosx", "Mac OS X")
+             .Default(llvm::StringRef());
+} }];
+}
+
 def Blocks : InheritableAttr {
   let Spellings = ["blocks"];
   let Args = [EnumArgument<"Type", "BlockType", ["byref"], ["ByRef"]>];
index d3cc08319aeca697b8ed7edfce127684cf3f311c..2b7c5de25c9b3dcc13c381a116ed1ada69536b47 100644 (file)
@@ -24,6 +24,7 @@ def : DiagGroup<"aggregate-return">;
 def AmbigMemberTemplate : DiagGroup<"ambiguous-member-template">;
 def : DiagGroup<"attributes">;
 def : DiagGroup<"bad-function-cast">;
+def Availability : DiagGroup<"availability">;
 def BoolConversions : DiagGroup<"bool-conversions">;
 def CXXCompat: DiagGroup<"c++-compat">;
 def CastAlign : DiagGroup<"cast-align">;
index 0f011f574136202a68194c8ff5f342a37afcca35..8f4414d3d46c9db70faf9f7faadd804b06bab809 100644 (file)
@@ -119,6 +119,7 @@ def err_function_declared_typedef : Error<
   "function definition declared 'typedef'">;
 def err_iboutletcollection_builtintype : Error<
   "type argument of iboutletcollection attribute cannot be a builtin type">;
+
 def err_expected_fn_body : Error<
   "expected function body after function declarator">;
 def err_expected_method_body : Error<"expected method body">;
@@ -130,6 +131,7 @@ def err_expected_statement : Error<"expected statement">;
 def err_expected_lparen_after : Error<"expected '(' after '%0'">;
 def err_expected_lparen_after_id : Error<"expected '(' after %0">;
 def err_expected_less_after : Error<"expected '<' after '%0'">;
+def err_expected_equal_after : Error<"expected '=' after %0">;
 def err_expected_comma : Error<"expected ','">;
 def err_expected_lbrace_in_compound_literal : Error<
   "expected '{' in compound literal">;
@@ -431,6 +433,21 @@ def err_paren_sizeof_parameter_pack : Error<
 def err_sizeof_parameter_pack : Error<
   "expected parenthesized parameter pack name in 'sizeof...' expression">;
 
+// Availability attribute
+def err_expected_version : Error<
+  "expected a version of the form 'major[.minor[.subminor]]'">;
+def err_zero_version : Error<
+  "version number must have non-zero major, minor, or sub-minor version">;
+def err_availability_expected_platform : Error<
+  "expected a platform name, e.g., 'macosx'">;
+def err_availability_expected_change : Error<
+  "expected 'introduced', 'deprecated', or 'obsoleted'">;
+def err_availability_unknown_change : Error<
+  "%0 is not an availability stage; use 'introduced', 'deprecated', or "
+  "'obsoleted'">;
+def err_availability_redundant : Error<
+  "redundant %0 availability change; only the last specified change will "        "be used">;
+
 // Language specific pragmas
 // - Generic warnings
 def warn_pragma_expected_lparen : Warning<
index 41feb7e01969208c05862563ad38c59fe785268d..fa66d51369f9700ebbda67d2f8024387c45e7390 100644 (file)
@@ -1148,6 +1148,14 @@ def err_regparm_mismatch : Error<"function declared with with regparm(%0) "
   "attribute was previously declared "
   "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">;
 
+// Availability attribute
+def warn_availability_unknown_platform : Warning<
+  "unknown platform %0 in availability macro">;
+def warn_availability_version_ordering : Warning<
+  "feature cannot be %select{introduced|deprecated|obsoleted}0 in %1 version "
+  "%2 before it was %select{introduced|deprecated|obsoleted}3 in version %4; "
+  "attribute ignored">;
+
 def warn_impcast_vector_scalar : Warning<
   "implicit conversion turns vector to scalar: %0 to %1">,
   InGroup<DiagGroup<"conversion">>, DefaultIgnore;
@@ -1339,11 +1347,11 @@ def err_ovl_no_viable_member_function_in_call : Error<
 def err_ovl_ambiguous_call : Error<
   "call to %0 is ambiguous">;
 def err_ovl_deleted_call : Error<
-  "call to %select{unavailable|deleted}0 function %1 %2">;
+  "call to %select{unavailable|deleted}0 function %1%2">;
 def err_ovl_ambiguous_member_call : Error<
   "call to member function %0 is ambiguous">;
 def err_ovl_deleted_member_call : Error<
-  "call to %select{unavailable|deleted}0 member function %1 %2">;
+  "call to %select{unavailable|deleted}0 member function %1%2">;
 def note_ovl_too_many_candidates : Note<
     "remaining %0 candidate%s0 omitted; "
     "pass -fshow-overloads=all to show them">;
@@ -1495,7 +1503,7 @@ def err_ovl_ambiguous_oper_binary : Error<
   "use of overloaded operator '%0' is ambiguous (with operand types %1 and %2)">;
 def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
 def err_ovl_deleted_oper : Error<
-  "overload resolution selected %select{unavailable|deleted}0 operator '%1' %2">;
+  "overload resolution selected %select{unavailable|deleted}0 operator '%1'%2">;
 def err_ovl_no_viable_subscript :
     Error<"no viable overloaded operator[] for type %0">;
 def err_ovl_no_oper :
@@ -1509,7 +1517,7 @@ def err_ovl_no_viable_object_call : Error<
 def err_ovl_ambiguous_object_call : Error<
   "call to object of type %0 is ambiguous">;
 def err_ovl_deleted_object_call : Error<
-  "call to %select{unavailable|deleted}0 function call operator in type %1 %2">;
+  "call to %select{unavailable|deleted}0 function call operator in type %1%2">;
 def note_ovl_surrogate_cand : Note<"conversion candidate of type %0">;
 def err_member_call_without_object : Error<
   "call to non-static member function without an object argument">;
@@ -3831,5 +3839,6 @@ def err_sizeof_pack_no_pack_name_suggest : Error<
 def note_parameter_pack_here : Note<"parameter pack %0 declared here">;
 
 } // end of sema category
+
 } // end of sema component.
 
index af5fd5598a54777179a6e61fac041b06da8ac824..b830bf2f82cd2a1d05a4069ffffd030bc5bb50b8 100644 (file)
 
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Triple.h"
 #include "llvm/Support/DataTypes.h"
 #include "clang/Basic/AddressSpaces.h"
+#include "clang/Basic/VersionTuple.h"
 #include <cassert>
 #include <vector>
 #include <string>
 
 namespace llvm {
 struct fltSemantics;
-class StringRef;
 }
 
 namespace clang {
@@ -81,6 +82,9 @@ protected:
   TargetCXXABI CXXABI;
   const LangAS::Map *AddrSpaceMap;
 
+  mutable llvm::StringRef PlatformName;
+  mutable VersionTuple PlatformMinVersion;
+
   unsigned HasAlignMac68kSupport : 1;
   unsigned RealTypeUsesObjCFPRet : 3;
 
@@ -537,6 +541,14 @@ public:
     return *AddrSpaceMap;
   }
 
+  /// \brief Retrieve the name of the platform as it is used in the
+  /// availability attribute.
+  llvm::StringRef getPlatformName() const { return PlatformName; }
+
+  /// \brief Retrieve the minimum desired version of the platform, to
+  /// which the program should be compiled.
+  VersionTuple getPlatformMinVersion() const { return PlatformMinVersion; }
+
 protected:
   virtual uint64_t getPointerWidthV(unsigned AddrSpace) const {
     return PointerWidth;
diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h
new file mode 100644 (file)
index 0000000..91eb68e
--- /dev/null
@@ -0,0 +1,126 @@
+//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This header defines the VersionTuple class, which represents a version in
+// the form major[.minor[.subminor]].
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_BASIC_VERSIONTUPLE_H
+#define LLVM_CLANG_BASIC_VERSIONTUPLE_H
+
+#include "llvm/ADT/Optional.h"
+#include <string>
+
+namespace llvm {
+  class raw_ostream;
+}
+
+namespace clang {
+
+/// \brief Represents a version number in the form major[.minor[.subminor]].
+class VersionTuple {
+  unsigned Major;
+  unsigned Minor : 31;
+  unsigned Subminor : 31;
+  unsigned HasMinor : 1;
+  unsigned HasSubminor : 1;
+
+public:
+  VersionTuple() 
+    : Major(0), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false) { }
+
+  explicit VersionTuple(unsigned Major)
+    : Major(Major), Minor(0), Subminor(0), HasMinor(false), HasSubminor(false)
+  { }
+
+  explicit VersionTuple(unsigned Major, unsigned Minor)
+    : Major(Major), Minor(Minor), Subminor(0), HasMinor(true), 
+      HasSubminor(false)
+  { }
+
+  explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor)
+    : Major(Major), Minor(Minor), Subminor(Subminor), HasMinor(true), 
+      HasSubminor(true)
+  { }
+
+  /// \brief Determine whether this version information is empty
+  /// (e.g., all version components are zero).
+  bool empty() const { return Major == 0 && Minor == 0 && Subminor == 0; }
+
+  /// \brief Retrieve the major version number.
+  unsigned getMajor() const { return Major; }
+
+  /// \brief Retrieve the minor version number, if provided.
+  llvm::Optional<unsigned> getMinor() const { 
+    if (!HasMinor)
+      return llvm::Optional<unsigned>();
+    return Minor;
+  }
+
+  /// \brief Retrieve the subminor version number, if provided.
+  llvm::Optional<unsigned> getSubminor() const { 
+    if (!HasSubminor)
+      return llvm::Optional<unsigned>();
+    return Subminor;
+  }
+
+  /// \brief Determine if two version numbers are equivalent. If not
+  /// provided, minor and subminor version numbers are considered to be zero.
+  friend bool operator==(const VersionTuple& X, const VersionTuple &Y) {
+    return X.Major == Y.Major && X.Minor == Y.Minor && X.Subminor == Y.Subminor;
+  }
+
+  /// \brief Determine if two version numbers are not equivalent. If
+  /// not provided, minor and subminor version numbers are considered to be 
+  /// zero.
+  friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) {
+    return !(X == Y);
+  }
+
+  /// \brief Determine whether one version number precedes another. If not
+  /// provided, minor and subminor version numbers are considered to be zero.
+  friend bool operator<(const VersionTuple &X, const VersionTuple &Y) {
+    if (X.Major != Y.Major)
+      return X.Major < Y.Major;
+
+    if (X.Minor != Y.Minor)
+      return X.Minor < Y.Minor;
+
+    return X.Subminor < Y.Subminor;
+  }
+
+  /// \brief Determine whether one version number follows another. If not
+  /// provided, minor and subminor version numbers are considered to be zero.
+  friend bool operator>(const VersionTuple &X, const VersionTuple &Y) {
+    return Y < X;
+  }
+
+  /// \brief Determine whether one version number precedes or is
+  /// equivalent to another. If not provided, minor and subminor
+  /// version numbers are considered to be zero.
+  friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) {
+    return !(Y < X);
+  }
+
+  /// \brief Determine whether one version number follows or is
+  /// equivalent to another. If not provided, minor and subminor
+  /// version numbers are considered to be zero.
+  friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
+    return !(X < Y);
+  }
+
+  /// \brief Retrieve a string representation of the version number/
+  std::string getAsString() const;
+};
+
+/// \brief Print a version number.
+llvm::raw_ostream& operator<<(llvm::raw_ostream &Out, const VersionTuple &V);
+
+} // end namespace clang
+#endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H
index d07f605270f6c07f08ff66e2a269f054f86f1558..b51632c6acca111628186596eca00ff7ee72354c 100644 (file)
@@ -33,7 +33,8 @@ namespace clang {
   class PragmaUnusedHandler;
   class ColonProtectionRAIIObject;
   class InMessageExpressionRAIIObject;
-  
+  class VersionTuple;
+
 /// PrettyStackTraceParserEntry - If a crash happens while the parser is active,
 /// an entry is printed for it.
 class PrettyStackTraceParserEntry : public llvm::PrettyStackTraceEntry {
@@ -111,6 +112,15 @@ class Parser : public CodeCompletionHandler {
   IdentifierInfo *Ident_vector;
   IdentifierInfo *Ident_pixel;
 
+  /// \brief Identifier for "introduced".
+  IdentifierInfo *Ident_introduced;
+
+  /// \brief Identifier for "deprecated".
+  IdentifierInfo *Ident_deprecated;
+
+  /// \brief Identifier for "obsoleted".
+  IdentifierInfo *Ident_obsoleted;
+
   /// C++0x contextual keywords. 
   mutable IdentifierInfo *Ident_final;
   mutable IdentifierInfo *Ident_override;
@@ -1547,6 +1557,12 @@ private:
   void ParseOpenCLAttributes(ParsedAttributes &attrs);
   void ParseOpenCLQualifiers(DeclSpec &DS);
 
+  VersionTuple ParseVersionTuple(SourceRange &Range);
+  void ParseAvailabilityAttribute(IdentifierInfo &Availability,
+                                  SourceLocation AvailabilityLoc,
+                                  ParsedAttributes &attrs,
+                                  SourceLocation *endLoc);
+
   void ParseTypeofSpecifier(DeclSpec &DS);
   void ParseDecltypeSpecifier(DeclSpec &DS);
   
index 382e50494edf1af81e3b9b1634f77adf5b167cff..3657b44b02215b7e6309d197b425a830fa8c1908 100644 (file)
@@ -18,6 +18,7 @@
 #include "llvm/Support/Allocator.h"
 #include "clang/Sema/Ownership.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/VersionTuple.h"
 #include "clang/AST/Expr.h"
 #include <cassert>
 
@@ -25,6 +26,23 @@ namespace clang {
   class IdentifierInfo;
   class Expr;
 
+/// \brief Represents information about a change in availability for
+/// an entity, which is part of the encoding of the 'availability'
+/// attribute.
+struct AvailabilityChange {
+  /// \brief The location of the keyword indicating the kind of change.
+  SourceLocation KeywordLoc;
+
+  /// \brief The version number at which the change occurred.
+  VersionTuple Version;
+
+  /// \brief The source range covering the version number.
+  SourceRange VersionRange;
+
+  /// \brief Determine whether this availability change is valid.
+  bool isValid() const { return !Version.empty(); }
+};
+
 /// AttributeList - Represents GCC's __attribute__ declaration. There are
 /// 4 forms of this construct...they are:
 ///
@@ -48,6 +66,11 @@ private:
   AttributeList *Next;
   bool DeclspecAttribute, CXX0XAttribute;
 
+  // For the 'availability' attribute.
+  AvailabilityChange AvailabilityIntroduced;
+  AvailabilityChange AvailabilityDeprecated;
+  AvailabilityChange AvailabilityObsoleted;
+
   /// True if already diagnosed as invalid.
   mutable bool Invalid;
 
@@ -61,6 +84,15 @@ private:
                 IdentifierInfo *ParmName, SourceLocation ParmLoc,
                 Expr **args, unsigned numargs,
                 bool declspec, bool cxx0x);
+
+  AttributeList(llvm::BumpPtrAllocator &Alloc,
+                IdentifierInfo *AttrName, SourceLocation AttrLoc,
+                IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
+                IdentifierInfo *ParmName, SourceLocation ParmLoc,
+                const AvailabilityChange &Introduced,
+                const AvailabilityChange &Deprecated,
+                const AvailabilityChange &Obsoleted,
+                bool declspec, bool cxx0x);
 public:
   class Factory {
     llvm::BumpPtrAllocator Alloc;
@@ -78,6 +110,20 @@ public:
         return Mem;
       }
 
+    AttributeList *Create(IdentifierInfo *AttrName, SourceLocation AttrLoc,
+      IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
+      IdentifierInfo *ParmName, SourceLocation ParmLoc,
+      const AvailabilityChange &Introduced,
+      const AvailabilityChange &Deprecated,
+      const AvailabilityChange &Obsoleted,
+      bool declspec = false, bool cxx0x = false) {
+        AttributeList *Mem = Alloc.Allocate<AttributeList>();
+        new (Mem) AttributeList(Alloc, AttrName, AttrLoc, ScopeName, ScopeLoc,
+                                ParmName, ParmLoc, Introduced, Deprecated,
+                                Obsoleted, declspec, cxx0x);
+        return Mem;
+      }
+
     AttributeList* CreateIntegerAttribute(ASTContext &C, IdentifierInfo *Name,
                                           SourceLocation TokLoc, int Arg) {
       Expr* IArg = IntegerLiteral::Create(C, llvm::APInt(32, (uint64_t)Arg),
@@ -96,6 +142,7 @@ public:
     AT_always_inline,
     AT_analyzer_noreturn,
     AT_annotate,
+    AT_availability,      // Clang-specific
     AT_base_check,
     AT_blocks,
     AT_carries_dependency,
@@ -244,6 +291,21 @@ public:
   arg_iterator arg_end() const {
     return arg_iterator(Args, NumArgs);
   }
+
+  const AvailabilityChange &getAvailabilityIntroduced() const {
+    assert(getKind() == AT_availability && "Not an availability attribute");
+    return AvailabilityIntroduced;
+  }
+
+  const AvailabilityChange &getAvailabilityDeprecated() const {
+    assert(getKind() == AT_availability && "Not an availability attribute");
+    return AvailabilityDeprecated;
+  }
+
+  const AvailabilityChange &getAvailabilityObsoleted() const {
+    assert(getKind() == AT_availability && "Not an availability attribute");
+    return AvailabilityObsoleted;
+  }
 };
 
 /// addAttributeLists - Add two AttributeLists together
index 043722b29c945a649724b3e673c0efa2326227a2..bacd7691a383a39e885ee4bb000cf8afc56f533e 100644 (file)
@@ -1901,6 +1901,7 @@ public:
 
   bool DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, 
                          bool UnknownObjCClass=false);
+  std::string getDeletedOrUnavailableSuffix(const FunctionDecl *FD);
   bool DiagnosePropertyAccessorMismatch(ObjCPropertyDecl *PD,
                                         ObjCMethodDecl *Getter,
                                         SourceLocation Loc);
index 835a0dbb104143d2d44d614acd2d427bb486ff7c..559d14554b611c6b62949fb605ecebff5e643b76 100644 (file)
@@ -69,6 +69,7 @@ class ASTStmtReader;
 class ASTIdentifierLookupTrait;
 class TypeLocReader;
 struct HeaderFileInfo;
+class VersionTuple;
 
 struct PCHPredefinesBlock {
   /// \brief The file ID for this predefines buffer in a PCH file.
@@ -1208,6 +1209,9 @@ public:
   // \brief Read a string
   std::string ReadString(const RecordData &Record, unsigned &Idx);
 
+  /// \brief Read a version tuple.
+  VersionTuple ReadVersionTuple(const RecordData &Record, unsigned &Idx);
+
   CXXTemporary *ReadCXXTemporary(const RecordData &Record, unsigned &Idx);
       
   /// \brief Reads attributes from the current stream position.
index 210f1e67ca3c9c4b78e80b0ec8537ad647e4ad9d..474631fce04b83a2bc0b6c48268e14d104fdb500 100644 (file)
@@ -56,6 +56,7 @@ class Sema;
 class SourceManager;
 class SwitchCase;
 class TargetInfo;
+class VersionTuple;
 
 /// \brief Writes an AST file containing the contents of a translation unit.
 ///
@@ -514,6 +515,9 @@ public:
   /// \brief Add a string to the given record.
   void AddString(llvm::StringRef Str, RecordDataImpl &Record);
 
+  /// \brief Add a version tuple to the given record
+  void AddVersionTuple(const VersionTuple &Version, RecordDataImpl &Record);
+
   /// \brief Mark a declaration context as needing an update.
   void AddUpdatedDeclContext(const DeclContext *DC) {
     UpdatedDeclContexts.insert(DC);
index fd5fd0e54b2d9b40352bcca8d2032e08e18f557e..e208bfdf1df5174d1e6a30229e0431c8a9713ed4 100644 (file)
@@ -451,7 +451,6 @@ void ASTContext::eraseDeclAttrs(const Decl *D) {
   }
 }
 
-
 MemberSpecializationInfo *
 ASTContext::getInstantiatedFromStaticDataMember(const VarDecl *Var) {
   assert(Var->isStaticDataMember() && "Not a static data member");
index 8d6a5412a55b15cc96761ab69f2010d7be52379f..11d28df4be7e27bf66c203bb19c0ed9890c4fe00 100644 (file)
@@ -25,6 +25,7 @@
 #include "clang/AST/Stmt.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/ASTMutationListener.h"
+#include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
@@ -241,6 +242,155 @@ bool Decl::isUsed(bool CheckUsedAttr) const {
   return false; 
 }
 
+/// \brief Determine the availability of the given declaration based on
+/// the target platform.
+///
+/// When it returns an availability result other than \c AR_Available,
+/// if the \p Message parameter is non-NULL, it will be set to a
+/// string describing why the entity is unavailable.
+///
+/// FIXME: Make these strings localizable, since they end up in
+/// diagnostics.
+static AvailabilityResult CheckAvailability(ASTContext &Context,
+                                            const AvailabilityAttr *A,
+                                            std::string *Message) {
+  llvm::StringRef TargetPlatform = Context.Target.getPlatformName();
+  llvm::StringRef PrettyPlatformName
+    = AvailabilityAttr::getPrettyPlatformName(TargetPlatform);
+  if (PrettyPlatformName.empty())
+    PrettyPlatformName = TargetPlatform;
+
+  VersionTuple TargetMinVersion = Context.Target.getPlatformMinVersion();
+  if (TargetMinVersion.empty())
+    return AR_Available;
+
+  // Match the platform name.
+  if (A->getPlatform()->getName() != TargetPlatform)
+    return AR_Available;
+
+  // Make sure that this declaration has already been introduced.
+  if (!A->getIntroduced().empty() && 
+      TargetMinVersion < A->getIntroduced()) {
+    if (Message) {
+      Message->clear();
+      llvm::raw_string_ostream Out(*Message);
+      Out << "introduced in " << PrettyPlatformName << ' ' 
+          << A->getIntroduced();
+    }
+
+    return AR_NotYetIntroduced;
+  }
+
+  // Make sure that this declaration hasn't been obsoleted.
+  if (!A->getObsoleted().empty() && TargetMinVersion >= A->getObsoleted()) {
+    if (Message) {
+      Message->clear();
+      llvm::raw_string_ostream Out(*Message);
+      Out << "obsoleted in " << PrettyPlatformName << ' ' 
+          << A->getObsoleted();
+    }
+    
+    return AR_Unavailable;
+  }
+
+  // Make sure that this declaration hasn't been deprecated.
+  if (!A->getDeprecated().empty() && TargetMinVersion >= A->getDeprecated()) {
+    if (Message) {
+      Message->clear();
+      llvm::raw_string_ostream Out(*Message);
+      Out << "first deprecated in " << PrettyPlatformName << ' '
+          << A->getDeprecated();
+    }
+    
+    return AR_Deprecated;
+  }
+
+  return AR_Available;
+}
+
+AvailabilityResult Decl::getAvailability(std::string *Message) const {
+  AvailabilityResult Result = AR_Available;
+  std::string ResultMessage;
+
+  for (attr_iterator A = attr_begin(), AEnd = attr_end(); A != AEnd; ++A) {
+    if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(*A)) {
+      if (Result >= AR_Deprecated)
+        continue;
+
+      if (Message)
+        ResultMessage = Deprecated->getMessage();
+
+      Result = AR_Deprecated;
+      continue;
+    }
+
+    if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(*A)) {
+      if (Message)
+        *Message = Unavailable->getMessage();
+      return AR_Unavailable;
+    }
+
+    if (AvailabilityAttr *Availability = dyn_cast<AvailabilityAttr>(*A)) {
+      AvailabilityResult AR = CheckAvailability(getASTContext(), Availability,
+                                                Message);
+
+      if (AR == AR_Unavailable)
+        return AR_Unavailable;
+
+      if (AR > Result) {
+        Result = AR;
+        if (Message)
+          ResultMessage.swap(*Message);
+      }
+      continue;
+    }
+  }
+
+  if (Message)
+    Message->swap(ResultMessage);
+  return Result;
+}
+
+bool Decl::canBeWeakImported(bool &IsDefinition) const {
+  IsDefinition = false;
+  if (const VarDecl *Var = dyn_cast<VarDecl>(this)) {
+    if (!Var->hasExternalStorage() || Var->getInit()) {
+      IsDefinition = true;
+      return false;
+    }
+  } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(this)) {
+    if (FD->hasBody()) {
+      IsDefinition = true;
+      return false;
+    }
+  } else if (isa<ObjCPropertyDecl>(this) || isa<ObjCMethodDecl>(this))
+    return false;
+  else if (!(getASTContext().getLangOptions().ObjCNonFragileABI &&
+             isa<ObjCInterfaceDecl>(this)))
+    return false;
+
+  return true;
+}
+
+bool Decl::isWeakImported() const {
+  bool IsDefinition;
+  if (!canBeWeakImported(IsDefinition))
+    return false;
+
+  ASTContext &Context = getASTContext();
+  for (attr_iterator A = attr_begin(), AEnd = attr_end(); A != AEnd; ++A) {
+    if (isa<WeakImportAttr>(*A))
+      return true;
+
+    if (AvailabilityAttr *Availability = dyn_cast<AvailabilityAttr>(*A)) {
+      if (CheckAvailability(getASTContext(), Availability, 0) 
+                                                         == AR_NotYetIntroduced)
+        return true;
+    }
+  }
+
+  return false;
+}
 
 unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
   switch (DeclKind) {
index b6ab33258d835f6ed572b5c7699091292ed83031..345c7fa8c077ad868ad76db455f33ba7b74c3c5f 100644 (file)
@@ -175,7 +175,7 @@ static bool EvalPointerValueAsBool(LValue& Value, bool& Result) {
   const ValueDecl* Decl = DeclRef->getDecl();
   if (Decl->hasAttr<WeakAttr>() ||
       Decl->hasAttr<WeakRefAttr>() ||
-      Decl->hasAttr<WeakImportAttr>())
+      Decl->isWeakImported())
     return false;
 
   return true;
index 91e7deb078ad132bbf0826ffb9da5fd256e6fdd0..b23a156cba0e580937a52fcb7035474942a3fdac 100644 (file)
@@ -14,6 +14,7 @@ add_clang_library(clangBasic
   Targets.cpp
   TokenKinds.cpp
   Version.cpp
+  VersionTuple.cpp
   )
 
 # Determine Subversion revision.
index 1696cae840ce5787679f3ceca8b65216690d4e6d..078452e9889f26ca044d948fab2158192a842ba8 100644 (file)
@@ -70,6 +70,10 @@ TargetInfo::TargetInfo(const std::string &T) : Triple(T) {
 
   // Default to an empty address space map.
   AddrSpaceMap = &DefaultAddrSpaceMap;
+
+  // Default to an unknown platform name.
+  PlatformName = "unknown";
+  PlatformMinVersion = VersionTuple();
 }
 
 // Out of line virtual dtor for TargetInfo.
index 653cf80688df9b012dce2a8001268f39ba76ad6e..90b0ee0c87e306c64dd26a694c335a33348aee9a 100644 (file)
@@ -76,7 +76,9 @@ public:
 
 
 static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
-                             const llvm::Triple &Triple) {
+                             const llvm::Triple &Triple,
+                             llvm::StringRef &PlatformName,
+                             VersionTuple &PlatformMinVersion) {
   Builder.defineMacro("__APPLE_CC__", "5621");
   Builder.defineMacro("__APPLE__");
   Builder.defineMacro("__MACH__");
@@ -120,6 +122,9 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
     Str[4] = '0' + (Rev % 10);
     Str[5] = '\0';
     Builder.defineMacro("__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__", Str);
+
+    PlatformName = "ios";
+    PlatformMinVersion = VersionTuple(Maj, Min, Rev);
   } else {
     // For historical reasons that make little sense, the version passed here is
     // the "darwin" version, which drops the 10 and offsets by 4.
@@ -136,6 +141,9 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts,
     Str[3] = '0' + Rev;
     Str[4] = '\0';
     Builder.defineMacro("__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__", Str);
+
+    PlatformName = "macosx";
+    PlatformMinVersion = VersionTuple(Maj, Min, Rev);
   }
 }
 
@@ -145,7 +153,8 @@ class DarwinTargetInfo : public OSTargetInfo<Target> {
 protected:
   virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                             MacroBuilder &Builder) const {
-    getDarwinDefines(Builder, Opts, Triple);
+    getDarwinDefines(Builder, Opts, Triple, this->PlatformName, 
+                     this->PlatformMinVersion);
   }
 
 public:
@@ -2005,7 +2014,7 @@ class DarwinARMTargetInfo :
 protected:
   virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple,
                             MacroBuilder &Builder) const {
-    getDarwinDefines(Builder, Opts, Triple);
+    getDarwinDefines(Builder, Opts, Triple, PlatformName, PlatformMinVersion);
   }
 
 public:
diff --git a/lib/Basic/VersionTuple.cpp b/lib/Basic/VersionTuple.cpp
new file mode 100644 (file)
index 0000000..d5cf126
--- /dev/null
@@ -0,0 +1,36 @@
+//===- VersionTuple.cpp - Version Number Handling ---------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the VersionTuple class, which represents a version in
+// the form major[.minor[.subminor]].
+//
+//===----------------------------------------------------------------------===//
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+
+std::string VersionTuple::getAsString() const {
+  std::string Result;
+  {
+    llvm::raw_string_ostream Out(Result);
+    Out << *this;
+  }
+  return Result;
+}
+
+llvm::raw_ostream& clang::operator<<(llvm::raw_ostream &Out, 
+                                     const VersionTuple &V) {
+  Out << V.getMajor();
+  if (llvm::Optional<unsigned> Minor = V.getMinor())
+    Out << '.' << *Minor;
+  if (llvm::Optional<unsigned> Subminor = V.getSubminor())
+    Out << '.' << *Subminor;
+  return Out;
+}
index 607df6d0b802d0c37aca09842aadb8c3da50cbff..a4d95d443fac5234414a6e017aace37d4df322f7 100644 (file)
@@ -5018,14 +5018,14 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) {
     while (const ObjCInterfaceDecl *Super = Root->getSuperClass())
       Root = Super;
     IsAGV = GetClassGlobal(ObjCMetaClassName + Root->getNameAsString());
-    if (Root->hasAttr<WeakImportAttr>())
+    if (Root->isWeakImported())
       IsAGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
     // work on super class metadata symbol.
     std::string SuperClassName =
       ObjCMetaClassName + 
         ID->getClassInterface()->getSuperClass()->getNameAsString();
     SuperClassGV = GetClassGlobal(SuperClassName);
-    if (ID->getClassInterface()->getSuperClass()->hasAttr<WeakImportAttr>())
+    if (ID->getClassInterface()->getSuperClass()->isWeakImported())
       SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
   }
   llvm::GlobalVariable *CLASS_RO_GV = BuildClassRoTInitializer(flags,
@@ -5055,7 +5055,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) {
     std::string RootClassName =
       ID->getClassInterface()->getSuperClass()->getNameAsString();
     SuperClassGV = GetClassGlobal(ObjCClassName + RootClassName);
-    if (ID->getClassInterface()->getSuperClass()->hasAttr<WeakImportAttr>())
+    if (ID->getClassInterface()->getSuperClass()->isWeakImported())
       SuperClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
   }
   GetClassSizeInfo(ID, InstanceStart, InstanceSize);
@@ -5137,7 +5137,7 @@ void CGObjCNonFragileABIMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) {
   Values[0] = GetClassName(OCD->getIdentifier());
   // meta-class entry symbol
   llvm::GlobalVariable *ClassGV = GetClassGlobal(ExtClassName);
-  if (Interface->hasAttr<WeakImportAttr>())
+  if (Interface->isWeakImported())
     ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
   
   Values[1] = ClassGV;
@@ -5810,7 +5810,7 @@ llvm::Value *CGObjCNonFragileABIMac::EmitMetaClassRef(CGBuilderTy &Builder,
 /// decl.
 llvm::Value *CGObjCNonFragileABIMac::GetClass(CGBuilderTy &Builder,
                                               const ObjCInterfaceDecl *ID) {
-  if (ID->hasAttr<WeakImportAttr>()) {
+  if (ID->isWeakImported()) {
     std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString());
     llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName);
     ClassGV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
index 7eee83c928d04c862e0a28922e54eb6a88b62e4d..647dcc50a973399d9af38464a1235eb851abf889 100644 (file)
@@ -509,7 +509,7 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD,
   if (FD->hasAttr<DLLImportAttr>()) {
     F->setLinkage(llvm::Function::DLLImportLinkage);
   } else if (FD->hasAttr<WeakAttr>() ||
-             FD->hasAttr<WeakImportAttr>()) {
+             FD->isWeakImported()) {
     // "extern_weak" is overloaded in LLVM; we probably should have
     // separate linkage types for this.
     F->setLinkage(llvm::Function::ExternalWeakLinkage);
@@ -986,7 +986,7 @@ CodeGenModule::GetOrCreateLLVMGlobal(llvm::StringRef MangledName,
     } else {
       if (D->hasAttr<DLLImportAttr>())
         GV->setLinkage(llvm::GlobalValue::DLLImportLinkage);
-      else if (D->hasAttr<WeakAttr>() || D->hasAttr<WeakImportAttr>())
+      else if (D->hasAttr<WeakAttr>() || D->isWeakImported())
         GV->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
 
       // Set visibility on a declaration only if it's explicit.
@@ -1530,7 +1530,7 @@ void CodeGenModule::EmitAliasDefinition(GlobalDecl GD) {
     }
   } else if (D->hasAttr<WeakAttr>() ||
              D->hasAttr<WeakRefAttr>() ||
-             D->hasAttr<WeakImportAttr>()) {
+             D->isWeakImported()) {
     GA->setLinkage(llvm::Function::WeakAnyLinkage);
   }
 
index f27ff79f19304ad691c589105f6e3ce43e55c6f0..6928d0d6d0dce1e377900fe70bd41ec08fadfbfc 100644 (file)
@@ -112,8 +112,11 @@ void Parser::ParseGNUAttributes(ParsedAttributes &attrs,
       IdentifierInfo *AttrName = Tok.getIdentifierInfo();
       SourceLocation AttrNameLoc = ConsumeToken();
 
+      // Availability attributes have their own grammar.
+      if (AttrName->isStr("availability"))
+        ParseAvailabilityAttribute(*AttrName, AttrNameLoc, attrs, endLoc);
       // check if we have a "parameterized" attribute
-      if (Tok.is(tok::l_paren)) {
+      else if (Tok.is(tok::l_paren)) {
         ConsumeParen(); // ignore the left paren loc for now
 
         if (Tok.is(tok::identifier)) {
@@ -362,6 +365,241 @@ void Parser::ParseOpenCLQualifiers(DeclSpec &DS) {
   }
 }
 
+/// \brief Parse a version number.
+///
+/// version:
+///   simple-integer
+///   simple-integer ',' simple-integer
+///   simple-integer ',' simple-integer ',' simple-integer
+VersionTuple Parser::ParseVersionTuple(SourceRange &Range) {
+  Range = Tok.getLocation();
+
+  if (!Tok.is(tok::numeric_constant)) {
+    Diag(Tok, diag::err_expected_version);
+    SkipUntil(tok::comma, tok::r_paren, true, true, true);
+    return VersionTuple();
+  }
+
+  // Parse the major (and possibly minor and subminor) versions, which
+  // are stored in the numeric constant. We utilize a quirk of the
+  // lexer, which is that it handles something like 1.2.3 as a single
+  // numeric constant, rather than two separate tokens.
+  llvm::SmallString<512> Buffer;
+  Buffer.resize(Tok.getLength()+1);
+  const char *ThisTokBegin = &Buffer[0];
+
+  // Get the spelling of the token, which eliminates trigraphs, etc.
+  bool Invalid = false;
+  unsigned ActualLength = PP.getSpelling(Tok, ThisTokBegin, &Invalid);
+  if (Invalid)
+    return VersionTuple();
+
+  // Parse the major version.
+  unsigned AfterMajor = 0;
+  unsigned Major = 0;
+  while (AfterMajor < ActualLength && isdigit(ThisTokBegin[AfterMajor])) {
+    Major = Major * 10 + ThisTokBegin[AfterMajor] - '0';
+    ++AfterMajor;
+  }
+
+  if (AfterMajor == 0) {
+    Diag(Tok, diag::err_expected_version);
+    SkipUntil(tok::comma, tok::r_paren, true, true, true);
+    return VersionTuple();
+  }
+
+  if (AfterMajor == ActualLength) {
+    ConsumeToken();
+
+    // We only had a single version component.
+    if (Major == 0) {
+      Diag(Tok, diag::err_zero_version);
+      return VersionTuple();
+    }
+
+    return VersionTuple(Major);
+  }
+
+  if (ThisTokBegin[AfterMajor] != '.' || (AfterMajor + 1 == ActualLength)) {
+    Diag(Tok, diag::err_expected_version);
+    SkipUntil(tok::comma, tok::r_paren, true, true, true);
+    return VersionTuple();
+  }
+
+  // Parse the minor version.
+  unsigned AfterMinor = AfterMajor + 1;
+  unsigned Minor = 0;
+  while (AfterMinor < ActualLength && isdigit(ThisTokBegin[AfterMinor])) {
+    Minor = Minor * 10 + ThisTokBegin[AfterMinor] - '0';
+    ++AfterMinor;
+  }
+
+  if (AfterMinor == ActualLength) {
+    ConsumeToken();
+    
+    // We had major.minor.
+    if (Major == 0 && Minor == 0) {
+      Diag(Tok, diag::err_zero_version);
+      return VersionTuple();
+    }
+
+    return VersionTuple(Major, Minor);      
+  }
+
+  // If what follows is not a '.', we have a problem.
+  if (ThisTokBegin[AfterMinor] != '.') {
+    Diag(Tok, diag::err_expected_version);
+    SkipUntil(tok::comma, tok::r_paren, true, true, true);
+    return VersionTuple();    
+  }
+
+  // Parse the subminor version.
+  unsigned AfterSubminor = AfterMinor + 1;
+  unsigned Subminor = 0;
+  while (AfterSubminor < ActualLength && isdigit(ThisTokBegin[AfterSubminor])) {
+    Subminor = Subminor * 10 + ThisTokBegin[AfterSubminor] - '0';
+    ++AfterSubminor;
+  }
+
+  if (AfterSubminor != ActualLength) {
+    Diag(Tok, diag::err_expected_version);
+    SkipUntil(tok::comma, tok::r_paren, true, true, true);
+    return VersionTuple();
+  }
+  ConsumeToken();
+  return VersionTuple(Major, Minor, Subminor);
+}
+
+/// \brief Parse the contents of the "availability" attribute.
+///
+/// availability-attribute:
+///   'availability' '(' platform ',' version-arg-list ')'
+///
+/// platform:
+///   identifier
+///
+/// version-arg-list:
+///   version-arg
+///   version-arg ',' version-arg-list
+///
+/// version-arg:
+///   'introduced' '=' version
+///   'deprecated' '=' version
+///   'removed' = version
+void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability,
+                                        SourceLocation AvailabilityLoc,
+                                        ParsedAttributes &attrs,
+                                        SourceLocation *endLoc) {
+  SourceLocation PlatformLoc;
+  IdentifierInfo *Platform = 0;
+
+  enum { Introduced, Deprecated, Obsoleted, Unknown };
+  AvailabilityChange Changes[Unknown];
+
+  // Opening '('.
+  SourceLocation LParenLoc;
+  if (!Tok.is(tok::l_paren)) {
+    Diag(Tok, diag::err_expected_lparen);
+    return;
+  }
+  LParenLoc = ConsumeParen();
+
+  // Parse the platform name,
+  if (Tok.isNot(tok::identifier)) {
+    Diag(Tok, diag::err_availability_expected_platform);
+    SkipUntil(tok::r_paren);
+    return;
+  }
+  Platform = Tok.getIdentifierInfo();
+  PlatformLoc = ConsumeToken();
+
+  // Parse the ',' following the platform name.
+  if (ExpectAndConsume(tok::comma, diag::err_expected_comma, "", tok::r_paren))
+    return;
+
+  // If we haven't grabbed the pointers for the identifiers
+  // "introduced", "deprecated", and "obsoleted", do so now.
+  if (!Ident_introduced) {
+    Ident_introduced = PP.getIdentifierInfo("introduced");
+    Ident_deprecated = PP.getIdentifierInfo("deprecated");
+    Ident_obsoleted = PP.getIdentifierInfo("obsoleted");
+  }
+
+  // Parse the set of introductions/deprecations/removals.
+  do {
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_availability_expected_change);
+      SkipUntil(tok::r_paren);
+      return;
+    }
+    IdentifierInfo *Keyword = Tok.getIdentifierInfo();
+    SourceLocation KeywordLoc = ConsumeToken();
+
+    if (Tok.isNot(tok::equal)) {
+      Diag(Tok, diag::err_expected_equal_after)
+        << Keyword;
+      SkipUntil(tok::r_paren);
+      return;
+    }
+    ConsumeToken();
+    
+    SourceRange VersionRange;
+    VersionTuple Version = ParseVersionTuple(VersionRange);
+    
+    if (Version.empty()) {
+      SkipUntil(tok::r_paren);
+      return;
+    }
+
+    unsigned Index;
+    if (Keyword == Ident_introduced)
+      Index = Introduced;
+    else if (Keyword == Ident_deprecated)
+      Index = Deprecated;
+    else if (Keyword == Ident_obsoleted)
+      Index = Obsoleted;
+    else 
+      Index = Unknown;
+
+    if (Index < Unknown) {
+      if (!Changes[Index].KeywordLoc.isInvalid()) {
+        Diag(KeywordLoc, diag::err_availability_redundant)
+          << Keyword 
+          << SourceRange(Changes[Index].KeywordLoc,
+                         Changes[Index].VersionRange.getEnd());
+      }
+
+      Changes[Index].KeywordLoc = KeywordLoc;
+      Changes[Index].Version = Version;
+      Changes[Index].VersionRange = VersionRange;
+    } else {
+      Diag(KeywordLoc, diag::err_availability_unknown_change)
+        << Keyword << VersionRange;
+    }
+
+    if (Tok.isNot(tok::comma))
+      break;
+
+    ConsumeToken();
+  } while (true);
+
+  // Closing ')'.
+  SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc);
+  if (RParenLoc.isInvalid())
+    return;
+
+  if (endLoc)
+    *endLoc = RParenLoc;
+
+  // Record this attribute
+  attrs.add(AttrFactory.Create(&Availability, AvailabilityLoc,
+                               0, SourceLocation(),
+                               Platform, PlatformLoc,
+                               Changes[Introduced],
+                               Changes[Deprecated],
+                               Changes[Obsoleted], false, false));
+}
+
 void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) {
   Diag(attrs.Range.getBegin(), diag::err_attributes_not_allowed)
     << attrs.Range;
index 60ca1d49e7cbd9bcd2bb185251c130b0ca323800..f18531ebbaba0c354cf8675a1c06c9a2439f867f 100644 (file)
@@ -422,6 +422,10 @@ void Parser::Initialize() {
     Ident_vector = &PP.getIdentifierTable().get("vector");
     Ident_pixel = &PP.getIdentifierTable().get("pixel");
   }
+
+  Ident_introduced = 0;
+  Ident_deprecated = 0;
+  Ident_obsoleted = 0;
 }
 
 /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
index a715989c685e0ecd4c772df60c8c59b01fdf80f5..ae5ea673eba173fc8b2d1b23f8e066b964dbcac4 100644 (file)
@@ -36,6 +36,24 @@ AttributeList::AttributeList(llvm::BumpPtrAllocator &Alloc,
   }
 }
 
+AttributeList::AttributeList(llvm::BumpPtrAllocator &Alloc,
+                             IdentifierInfo *AttrName, SourceLocation AttrLoc,
+                             IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
+                             IdentifierInfo *ParmName, SourceLocation ParmLoc,
+                             const AvailabilityChange &Introduced,
+                             const AvailabilityChange &Deprecated,
+                             const AvailabilityChange &Obsoleted,
+                             bool declspec, bool cxx0x)
+  : AttrName(AttrName), AttrLoc(AttrLoc), ScopeName(ScopeName), 
+    ScopeLoc(ScopeLoc), ParmName(ParmName), ParmLoc(ParmLoc), 
+    Args(0), NumArgs(0), Next(0),
+    DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), 
+    AvailabilityIntroduced(Introduced),
+    AvailabilityDeprecated(Deprecated),
+    AvailabilityObsoleted(Obsoleted),
+    Invalid(false) {
+}
+
 AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
   llvm::StringRef AttrName = Name->getName();
 
@@ -83,6 +101,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
     .Case("may_alias", AT_may_alias)
     .Case("base_check", AT_base_check)
     .Case("deprecated", AT_deprecated)
+    .Case("availability", AT_availability)
     .Case("visibility", AT_visibility)
     .Case("destructor", AT_destructor)
     .Case("format_arg", AT_format_arg)
index b7037ce83e7fdbfe8da2a56ae691b98911aa3628..2334ab5128a70d9648ab1c57ea36e51b89966e50 100644 (file)
@@ -375,12 +375,21 @@ void CodeCompletionResult::computeCursorKindAndAvailability() {
   switch (Kind) {
   case RK_Declaration:
     // Set the availability based on attributes.
-    Availability = CXAvailability_Available;      
-    if (Declaration->getAttr<UnavailableAttr>())
-      Availability = CXAvailability_NotAvailable;
-    else if (Declaration->getAttr<DeprecatedAttr>())
+    switch (Declaration->getAvailability()) {
+    case AR_Available:
+    case AR_NotYetIntroduced:
+      Availability = CXAvailability_Available;      
+      break;
+      
+    case AR_Deprecated:
       Availability = CXAvailability_Deprecated;
+      break;
       
+    case AR_Unavailable:
+      Availability = CXAvailability_NotAvailable;
+      break;
+    }
+
     if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Declaration))
       if (Function->isDeleted())
         Availability = CXAvailability_NotAvailable;
index 3c1127784d4e51987e000c175d12e29a060d01f0..3f33f144e48734f9f614c43acdc604fd850780b7 100644 (file)
@@ -1074,6 +1074,57 @@ static void HandleUnavailableAttr(Decl *d, const AttributeList &Attr, Sema &S) {
   d->addAttr(::new (S.Context) UnavailableAttr(Attr.getLoc(), S.Context, Str));
 }
 
+static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, 
+                                   Sema &S) {
+  IdentifierInfo *Platform = Attr.getParameterName();
+  SourceLocation PlatformLoc = Attr.getParameterLoc();
+
+  llvm::StringRef PlatformName
+    = AvailabilityAttr::getPrettyPlatformName(Platform->getName());
+  if (PlatformName.empty()) {
+    S.Diag(PlatformLoc, diag::warn_availability_unknown_platform)
+      << Platform;
+
+    PlatformName = Platform->getName();
+  }
+
+  AvailabilityChange Introduced = Attr.getAvailabilityIntroduced();
+  AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated();
+  AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted();
+
+  // Ensure that Introduced < Deprecated < Obsoleted (although not all
+  // of these steps are needed).
+  if (Introduced.isValid() && Deprecated.isValid() &&
+      !(Introduced.Version < Deprecated.Version)) {
+    S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
+      << 1 << PlatformName << Deprecated.Version.getAsString()
+      << 0 << Introduced.Version.getAsString();
+    return;
+  }
+
+  if (Introduced.isValid() && Obsoleted.isValid() &&
+      !(Introduced.Version < Obsoleted.Version)) {
+    S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
+      << 2 << PlatformName << Obsoleted.Version.getAsString()
+      << 0 << Introduced.Version.getAsString();
+    return;
+  }
+
+  if (Deprecated.isValid() && Obsoleted.isValid() &&
+      !(Deprecated.Version < Obsoleted.Version)) {
+    S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering)
+      << 2 << PlatformName << Obsoleted.Version.getAsString()
+      << 1 << Deprecated.Version.getAsString();
+    return;
+  }
+
+  d->addAttr(::new (S.Context) AvailabilityAttr(Attr.getLoc(), S.Context, 
+                                                Platform,
+                                                Introduced.Version,
+                                                Deprecated.Version,
+                                                Obsoleted.Version));
+}
+
 static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) {
   // check the attribute arguments.
   if (Attr.getNumArgs() != 1) {
@@ -1380,27 +1431,18 @@ static void HandleWeakImportAttr(Decl *D, const AttributeList &Attr, Sema &S) {
 
   // weak_import only applies to variable & function declarations.
   bool isDef = false;
-  if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
-    isDef = (!VD->hasExternalStorage() || VD->getInit());
-  } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
-    isDef = FD->hasBody();
-  } else if (isa<ObjCPropertyDecl>(D) || isa<ObjCMethodDecl>(D)) {
-    // We ignore weak import on properties and methods
-    return;
-  } else if (!(S.LangOpts.ObjCNonFragileABI && isa<ObjCInterfaceDecl>(D))) {
-    // Don't issue the warning for darwin as target; yet, ignore the attribute.
-    if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin ||
-        !isa<ObjCInterfaceDecl>(D)) 
+  if (!D->canBeWeakImported(isDef)) {
+    if (isDef)
+      S.Diag(Attr.getLoc(),
+             diag::warn_attribute_weak_import_invalid_on_definition)
+        << "weak_import" << 2 /*variable and function*/;
+    else if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin ||
+             (!isa<ObjCInterfaceDecl>(D) &&
+              !isa<ObjCPropertyDecl>(D) &&
+              !isa<ObjCMethodDecl>(D)))
       S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
         << Attr.getName() << ExpectedVariableOrFunction;
-      return;
-  }
 
-  // Merge should handle any subsequent violations.
-  if (isDef) {
-    S.Diag(Attr.getLoc(),
-           diag::warn_attribute_weak_import_invalid_on_definition)
-      << "weak_import" << 2 /*variable and function*/;
     return;
   }
 
@@ -2745,6 +2787,7 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D,
   case AttributeList::AT_analyzer_noreturn:
     HandleAnalyzerNoReturnAttr  (D, Attr, S); break;
   case AttributeList::AT_annotate:    HandleAnnotateAttr    (D, Attr, S); break;
+  case AttributeList::AT_availability:HandleAvailabilityAttr(D, Attr, S); break;
   case AttributeList::AT_carries_dependency:
                                       HandleDependencyAttr  (D, Attr, S); break;
   case AttributeList::AT_common:      HandleCommonAttr      (D, Attr, S); break;
@@ -3058,7 +3101,7 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state,
 
 static bool isDeclDeprecated(Decl *D) {
   do {
-    if (D->hasAttr<DeprecatedAttr>())
+    if (D->isDeprecated())
       return true;
   } while ((D = cast_or_null<Decl>(D->getDeclContext())));
   return false;
index 8717385b51658a95f373381122e98d07aa0db519..101ade72e3c2e343bb9541e831e85bcaa631281b 100644 (file)
@@ -28,7 +28,7 @@ static void DiagnoseObjCImplementedDeprecations(Sema &S,
                                                 NamedDecl *ND,
                                                 SourceLocation ImplLoc,
                                                 int select) {
-  if (ND && ND->getAttr<DeprecatedAttr>()) {
+  if (ND && ND->isDeprecated()) {
     S.Diag(ImplLoc, diag::warn_deprecated_def) << select;
     if (select == 0)
       S.Diag(ND->getLocation(), diag::note_method_declared_at);
@@ -1369,15 +1369,14 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl,
       PrevObjCMethod->setDefined(impl);
       // If a method is deprecated, push it in the global pool.
       // This is used for better diagnostics.
-      if (Method->getAttr<DeprecatedAttr>()) {
-        if (!PrevObjCMethod->getAttr<DeprecatedAttr>())
+      if (Method->isDeprecated()) {
+        if (!PrevObjCMethod->isDeprecated())
           List->Method = Method;
       }
       // If new method is unavailable, push it into global pool
       // unless previous one is deprecated.
-      if (Method->getAttr<UnavailableAttr>()) {
-        if (!PrevObjCMethod->getAttr<UnavailableAttr>() &&
-            !PrevObjCMethod->getAttr<DeprecatedAttr>())
+      if (Method->isUnavailable()) {
+        if (PrevObjCMethod->getAvailability() < AR_Deprecated)
           List->Method = Method;
       }
       return;
index b8f0c729d1788ba0652738d8c709b2f5f56281e8..c2f3a434b81226a83613fe82958e11ca5773dd42 100644 (file)
@@ -68,7 +68,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
         Diag(Suppressed[I].first, Suppressed[I].second);
       
       // Clear out the list of suppressed diagnostics, so that we don't emit
-      // them again for this specialization. However, we don't remove this
+      // them again for this specialization. However, we don't obsolete this
       // entry from the table, because we want to avoid ever emitting these
       // diagnostics again.
       Suppressed.clear();
@@ -82,13 +82,28 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
     return true;
   }
 
-  // See if the decl is deprecated.
-  if (const DeprecatedAttr *DA = D->getAttr<DeprecatedAttr>())
-    EmitDeprecationWarning(D, DA->getMessage(), Loc, UnknownObjCClass);
+  // See if this is a deleted function.
+  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+    if (FD->isDeleted()) {
+      Diag(Loc, diag::err_deleted_function_use);
+      Diag(D->getLocation(), diag::note_unavailable_here) << true;
+      return true;
+    }
+  }
+
+  // See if this declaration is unavailable or deprecated.
+  std::string Message;
+  switch (D->getAvailability(&Message)) {
+  case AR_Available:
+  case AR_NotYetIntroduced:
+    break;
 
-  // See if the decl is unavailable
-  if (const UnavailableAttr *UA = D->getAttr<UnavailableAttr>()) {
-    if (UA->getMessage().empty()) {
+  case AR_Deprecated:
+    EmitDeprecationWarning(D, Message, Loc, UnknownObjCClass);
+    break;
+
+  case AR_Unavailable:
+    if (Message.empty()) {
       if (!UnknownObjCClass)
         Diag(Loc, diag::err_unavailable) << D->getDeclName();
       else
@@ -97,17 +112,9 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
     }
     else 
       Diag(Loc, diag::err_unavailable_message) 
-        << D->getDeclName() << UA->getMessage();
-    Diag(D->getLocation(), diag::note_unavailable_here) << 0;
-  }
-
-  // See if this is a deleted function.
-  if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
-    if (FD->isDeleted()) {
-      Diag(Loc, diag::err_deleted_function_use);
-      Diag(D->getLocation(), diag::note_unavailable_here) << true;
-      return true;
-    }
+        << D->getDeclName() << Message;
+    Diag(D->getLocation(), diag::note_unavailable_here) << 0;    
+    break;
   }
 
   // Warn if this is used but marked unused.
@@ -117,6 +124,23 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc,
   return false;
 }
 
+/// \brief Retrieve the message suffix that should be added to a
+/// diagnostic complaining about the given function being deleted or
+/// unavailable.
+std::string Sema::getDeletedOrUnavailableSuffix(const FunctionDecl *FD) {
+  // FIXME: C++0x implicitly-deleted special member functions could be
+  // detected here so that we could improve diagnostics to say, e.g.,
+  // "base class 'A' had a deleted copy constructor".
+  if (FD->isDeleted())
+    return std::string();
+
+  std::string Message;
+  if (FD->getAvailability(&Message))
+    return ": " + Message;
+
+  return std::string();
+}
+
 /// DiagnoseSentinelCalls - This routine checks on method dispatch calls
 /// (and other functions in future), which have been declared with sentinel
 /// attribute. It warns if call does not have the sentinel argument.
index 8fa2a5d2e86f476d4539a4f654d5eeeadc443758..0a8bd8fb526741397c3c447d8d4da20b6af30c2e 100644 (file)
@@ -1388,16 +1388,16 @@ bool Sema::FindAllocationOverload(SourceLocation StartLoc, SourceRange Range,
     Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args, NumArgs);
     return true;
 
-  case OR_Deleted:
+  case OR_Deleted: {
     Diag(StartLoc, diag::err_ovl_deleted_call)
       << Best->Function->isDeleted()
       << Name 
-      << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+      << getDeletedOrUnavailableSuffix(Best->Function)
       << Range;
     Candidates.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
     return true;
   }
+  }
   assert(false && "Unreachable, bad result from BestViableFunction");
   return true;
 }
index 91de095ac7e71c28dafe82829c2165050235dd68..f607d658951f11f801f5e67ebabb284947fec91c 100644 (file)
@@ -1138,10 +1138,14 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl,
 static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod,
                              ObjCPropertyDecl *Property) {
   // Should we just clone all attributes over?
-  if (DeprecatedAttr *A = Property->getAttr<DeprecatedAttr>())
-    PropertyMethod->addAttr(A->clone(S.Context));
-  if (UnavailableAttr *A = Property->getAttr<UnavailableAttr>())
-    PropertyMethod->addAttr(A->clone(S.Context));
+  for (Decl::attr_iterator A = Property->attr_begin(), 
+                        AEnd = Property->attr_end(); 
+       A != AEnd; ++A) {
+    if (isa<DeprecatedAttr>(*A) || 
+        isa<UnavailableAttr>(*A) || 
+        isa<AvailabilityAttr>(*A))
+      PropertyMethod->addAttr((*A)->clone(S.Context));
+  }
 }
 
 /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods
index 110f8cdc1cbd5862fdba93167c92e273130972df..7a4e68d3cbb12273322367d73aff5f4e1ab5da02 100644 (file)
@@ -6282,8 +6282,7 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc,
 
   // Best is the best viable function.
   if (Best->Function &&
-      (Best->Function->isDeleted() ||
-       Best->Function->getAttr<UnavailableAttr>()))
+      (Best->Function->isDeleted() || Best->Function->isUnavailable()))
     return OR_Deleted;
 
   return OR_Success;
@@ -6735,7 +6734,7 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand,
   FunctionDecl *Fn = Cand->Function;
 
   // Note deleted candidates, but only if they're viable.
-  if (Cand->Viable && (Fn->isDeleted() || Fn->hasAttr<UnavailableAttr>())) {
+  if (Cand->Viable && (Fn->isDeleted() || Fn->isUnavailable())) {
     std::string FnDesc;
     OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, FnDesc);
 
@@ -7744,8 +7743,7 @@ Sema::BuildOverloadedCallExpr(Scope *S, Expr *Fn, UnresolvedLookupExpr *ULE,
       Diag(Fn->getSourceRange().getBegin(), diag::err_ovl_deleted_call)
         << Best->Function->isDeleted()
         << ULE->getName()
-        << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+        << getDeletedOrUnavailableSuffix(Best->Function)
         << Fn->getSourceRange();
       CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
     }
@@ -7928,8 +7926,7 @@ Sema::CreateOverloadedUnaryOp(SourceLocation OpLoc, unsigned OpcIn,
       Diag(OpLoc, diag::err_ovl_deleted_oper)
         << Best->Function->isDeleted()
         << UnaryOperator::getOpcodeStr(Opc)
-        << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+        << getDeletedOrUnavailableSuffix(Best->Function)
         << Input->getSourceRange();
       CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
       return ExprError();
@@ -8199,8 +8196,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
       Diag(OpLoc, diag::err_ovl_deleted_oper)
         << Best->Function->isDeleted()
         << BinaryOperator::getOpcodeStr(Opc)
-        << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+        << getDeletedOrUnavailableSuffix(Best->Function)
         << Args[0]->getSourceRange() << Args[1]->getSourceRange();
       CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2);
       return ExprError();
@@ -8349,8 +8345,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
     case OR_Deleted:
       Diag(LLoc, diag::err_ovl_deleted_oper)
         << Best->Function->isDeleted() << "[]"
-        << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+        << getDeletedOrUnavailableSuffix(Best->Function)
         << Args[0]->getSourceRange() << Args[1]->getSourceRange();
       CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2,
                                   "[]", LLoc);
@@ -8468,8 +8463,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
       Diag(UnresExpr->getMemberLoc(), diag::err_ovl_deleted_member_call)
         << Best->Function->isDeleted()
         << DeclName 
-        << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+        << getDeletedOrUnavailableSuffix(Best->Function)
         << MemExprE->getSourceRange();
       CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
       // FIXME: Leaking incoming expressions!
@@ -8643,8 +8637,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object,
          diag::err_ovl_deleted_object_call)
       << Best->Function->isDeleted()
       << Object->getType() 
-      << Best->Function->getMessageUnavailableAttr(
-           !Best->Function->isDeleted())
+      << getDeletedOrUnavailableSuffix(Best->Function)
       << Object->getSourceRange();
     CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
     break;
@@ -8852,8 +8845,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) {
     Diag(OpLoc,  diag::err_ovl_deleted_oper)
       << Best->Function->isDeleted()
       << "->" 
-      << Best->Function->getMessageUnavailableAttr(
-             !Best->Function->isDeleted())
+      << getDeletedOrUnavailableSuffix(Best->Function)
       << Base->getSourceRange();
     CandidateSet.NoteCandidates(*this, OCD_AllCandidates, &Base, 1);
     return ExprError();
index b34b62ab6f3e8960fa2ef3c1a7dba32784d063f3..1b55f71c36b1da45b5216bc52555edc1b317324b 100644 (file)
@@ -37,6 +37,7 @@
 #include "clang/Basic/FileSystemStatCache.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/Version.h"
+#include "clang/Basic/VersionTuple.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Bitcode/BitstreamReader.h"
 #include "llvm/Support/MemoryBuffer.h"
@@ -4870,6 +4871,18 @@ std::string ASTReader::ReadString(const RecordData &Record, unsigned &Idx) {
   return Result;
 }
 
+VersionTuple ASTReader::ReadVersionTuple(const RecordData &Record, 
+                                         unsigned &Idx) {
+  unsigned Major = Record[Idx++];
+  unsigned Minor = Record[Idx++];
+  unsigned Subminor = Record[Idx++];
+  if (Minor == 0)
+    return VersionTuple(Major);
+  if (Subminor == 0)
+    return VersionTuple(Major, Minor - 1);
+  return VersionTuple(Major, Minor - 1, Subminor - 1);
+}
+
 CXXTemporary *ASTReader::ReadCXXTemporary(const RecordData &Record,
                                           unsigned &Idx) {
   CXXDestructorDecl *Decl = cast<CXXDestructorDecl>(GetDecl(Record[Idx++]));
index 877650f9de386e644a5f8fe539b1ac4a066b2c71..482ce4bec7c763a0c3dbf1dd18c63ebad84148de 100644 (file)
@@ -37,6 +37,7 @@
 #include "clang/Basic/SourceManagerInternals.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/Version.h"
+#include "clang/Basic/VersionTuple.h"
 #include "llvm/ADT/APFloat.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/StringExtras.h"
@@ -2622,6 +2623,19 @@ void ASTWriter::AddString(llvm::StringRef Str, RecordDataImpl &Record) {
   Record.insert(Record.end(), Str.begin(), Str.end());
 }
 
+void ASTWriter::AddVersionTuple(const VersionTuple &Version,
+                                RecordDataImpl &Record) {
+  Record.push_back(Version.getMajor());
+  if (llvm::Optional<unsigned> Minor = Version.getMinor())
+    Record.push_back(*Minor + 1);
+  else
+    Record.push_back(0);
+  if (llvm::Optional<unsigned> Subminor = Version.getSubminor())
+    Record.push_back(*Subminor + 1);
+  else
+    Record.push_back(0);
+}
+
 /// \brief Note that the identifier II occurs at the given offset
 /// within the identifier table.
 void ASTWriter::SetIdentifierOffset(const IdentifierInfo *II, uint32_t Offset) {
diff --git a/test/CodeGenObjC/attr-availability.m b/test/CodeGenObjC/attr-availability.m
new file mode 100644 (file)
index 0000000..4f1fad8
--- /dev/null
@@ -0,0 +1,24 @@
+// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin8.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_4 %s
+// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin9.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_5 %s
+// RUN: %clang_cc1 -fobjc-nonfragile-abi "-triple" "x86_64-apple-darwin10.0.0" -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-10_6 %s
+
+// CHECK-10_4: @"OBJC_CLASS_$_WeakClass1" = extern_weak global
+// CHECK-10_5: @"OBJC_CLASS_$_WeakClass1" = external global
+// CHECK-10_6: @"OBJC_CLASS_$_WeakClass1" = external global
+__attribute__((availability(macosx,introduced=10.5)))
+@interface WeakClass1 @end
+
+@implementation WeakClass1(MyCategory) @end
+
+@implementation WeakClass1(YourCategory) @end
+
+// CHECK-10_4: @"OBJC_CLASS_$_WeakClass2" = extern_weak global 
+// CHECK-10_5: @"OBJC_CLASS_$_WeakClass2" = extern_weak global 
+// CHECK-10_6: @"OBJC_CLASS_$_WeakClass2" = external global 
+__attribute__((availability(macosx,introduced=10.6)))
+@interface WeakClass2 @end
+
+@implementation WeakClass2(MyCategory) @end
+
+@implementation WeakClass2(YourCategory) @end
+
diff --git a/test/Parser/attr-availability.c b/test/Parser/attr-availability.c
new file mode 100644 (file)
index 0000000..10d55b5
--- /dev/null
@@ -0,0 +1,9 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+void f0() __attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6)));
+
+void f1() __attribute__((availability(macosx,deprecated=10.4,introduced=10.2,obsoleted=10.6)));
+
+void f2() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsoleted=10.6)));
+
+void f3() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsoleted=10.6,introduced=10.2))); // expected-error{{redundant 'introduced' availability change; only the last specified change will be used}}
+
diff --git a/test/Sema/attr-availability-ios.c b/test/Sema/attr-availability-ios.c
new file mode 100644 (file)
index 0000000..e531d6b
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 "-triple" "x86_64-apple-darwin3.0.0-iphoneos" -fsyntax-only -verify %s
+
+void f0(int) __attribute__((availability(ios,introduced=2.0,deprecated=2.1)));
+void f1(int) __attribute__((availability(ios,introduced=2.1)));
+void f2(int) __attribute__((availability(ios,introduced=2.0,deprecated=3.0)));
+void f3(int) __attribute__((availability(ios,introduced=3.0)));
+void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}}
+
+void test() {
+  f0(0); // expected-warning{{'f0' is deprecated: first deprecated in iOS 2.1}}
+  f1(0);
+  f2(0); // expected-warning{{'f2' is deprecated: first deprecated in iOS 3.0}}
+  f3(0);
+  f4(0); // expected-error{{f4' is unavailable: obsoleted in iOS 3.0}}
+}
diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c
new file mode 100644 (file)
index 0000000..b43da6f
--- /dev/null
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 "-triple" "x86_64-apple-darwin9.0.0" -fsyntax-only -verify %s
+
+void f0(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6)));
+void f1(int) __attribute__((availability(macosx,introduced=10.5)));
+void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5)));
+void f3(int) __attribute__((availability(macosx,introduced=10.6)));
+void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}}
+
+void test() {
+  f0(0);
+  f1(0);
+  f2(0); // expected-warning{{'f2' is deprecated: first deprecated in Mac OS X 10.5}}
+  f3(0);
+  f4(0); // expected-error{{f4' is unavailable: obsoleted in Mac OS X 10.5}}
+}
diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c
new file mode 100644 (file)
index 0000000..1314cf5
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+void f0() __attribute__((availability(macosx,introduced=10.4,deprecated=10.2))); // expected-warning{{feature cannot be deprecated in Mac OS X version 10.2 before it was introduced in version 10.4; attribute ignored}}
+void f1() __attribute__((availability(ios,obsoleted=2.1,deprecated=3.0)));  // expected-warning{{feature cannot be obsoleted in iOS version 2.1 before it was deprecated in version 3.0; attribute ignored}}
+
+void f2() __attribute__((availability(otheros,introduced=2.2))); // expected-warning{{unknown platform 'otheros' in availability macro}}
index fe3e8b14706fca17937eed4c1f4660de7457d90b..5f34ed9904135fa3e34eaababed0a6f9f94dcc0c 100644 (file)
@@ -25,6 +25,6 @@ namespace radar9046492 {
 
 void foo() FOO; // expected-note {{candidate function has been explicitly made unavailable}}
 void bar() {
-  foo(); // expected-error {{call to unavailable function 'foo' not available - replaced}}
+  foo(); // expected-error {{call to unavailable function 'foo': not available - replaced}}
 }
 }
index 52c980396eda873f989aa6f156358e71cfb8e82e..178774c6640f5ee50c5e33b67867efc58a6aa0fe 100644 (file)
@@ -40,7 +40,7 @@ Class <MyProto1> clsP1 = 0;  // expected-warning {{'MyProto1' is deprecated}}
 
 __attribute ((unavailable)) __attribute ((deprecated)) @protocol XProto; // expected-note{{marked unavailable}}
 
-id <XProto> idX = 0; // expected-error {{'XProto' is unavailable}} expected-warning {{'XProto' is deprecated}}
+id <XProto> idX = 0; // expected-error {{'XProto' is unavailable}}
 
 int main ()
 {
index b7a2d663441ce0e460b8bea88d4a7b0579ccc016..8e03e1c4c288eba55c7a4914a407c5ed6a94acef 100644 (file)
@@ -36,10 +36,8 @@ void test(C *c) {
   [c unavailMeth1]; // expected-warning {{'unavailMeth1' maybe unavailable because receiver type is unknown}}
   [c depInA2]; // expected-warning {{'depInA2' maybe deprecated because receiver type is unknown}}
   [c unavailMeth2]; // expected-warning {{'unavailMeth2' maybe unavailable because receiver type is unknown}}
-  [c depunavailInA]; // expected-warning {{'depunavailInA' maybe deprecated because receiver type is unknown}} \
-                     // expected-warning {{'depunavailInA' maybe unavailable because receiver type is unknown}} 
-  [c depunavailInA1]; // expected-warning {{'depunavailInA1' maybe deprecated because receiver type is unknown}} \
-                      // expected-warning {{'depunavailInA1' maybe unavailable because receiver type is unknown}}
+  [c depunavailInA]; // expected-warning {{'depunavailInA' maybe unavailable because receiver type is unknown}} 
+  [c depunavailInA1];// expected-warning {{'depunavailInA1' maybe unavailable because receiver type is unknown}}
   [c FuzzyMeth];      // expected-warning {{'FuzzyMeth' maybe deprecated because receiver type is unknown}}
   [c FuzzyMeth1]; // expected-warning {{'FuzzyMeth1' maybe deprecated because receiver type is unknown}}
 
index c9a923cfc5f620559a8560e6e5b7337566a8fc03..b12e7fe41e6109c5e182f16301771e716ccc7bd4 100644 (file)
@@ -4897,14 +4897,22 @@ extern "C" {
 enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) {
   if (clang_isDeclaration(cursor.kind))
     if (Decl *D = cxcursor::getCursorDecl(cursor)) {
-      if (D->hasAttr<UnavailableAttr>() ||
-          (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted()))
+      if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted())
         return CXAvailability_Available;
       
-      if (D->hasAttr<DeprecatedAttr>())
+      switch (D->getAvailability()) {
+      case AR_Available:
+      case AR_NotYetIntroduced:
+        return CXAvailability_Available;
+
+      case AR_Deprecated:
         return CXAvailability_Deprecated;
+
+      case AR_Unavailable:
+        return CXAvailability_NotAvailable;
+      }
     }
-  
+
   return CXAvailability_Available;
 }