Implement a new 'availability' attribute, that allows one to specify
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
diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 8d6a541..11d28df 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -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 @@
   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) {