From 5ed17fa07855ab10c152e656428ce34df2abc62c Mon Sep 17 00:00:00 2001
From: "Duncan P. N. Exon Smith" <dexonsmith@apple.com>
Date: Mon, 8 Feb 2016 18:35:24 -0800
Subject: [PATCH] SemaCXX: Support templates in availability attributes

This adds a bunch of tests for availability and templates, fixing the
single broken case I found.

If the availability context is `FunctionTemplateDecl`, we should look
through it to the `FunctionDecl`.  This prevents a diagnostic in the
following case:

    class C __attribute__((unavailable));
    template <class T> void foo(C&) __attribute__((unavailable));

I've also added `__has_feature(attribute_availability_in_templates)` so
we can test for this.
---
 lib/AST/DeclBase.cpp              |  3 ++
 lib/Lex/PPMacroExpansion.cpp      |  1 +
 test/SemaCXX/attr-unavailable.cpp | 62 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp
index 58c3cc3..d41d25f 100644
--- a/lib/AST/DeclBase.cpp
+++ b/lib/AST/DeclBase.cpp
@@ -467,6 +467,9 @@ static AvailabilityResult CheckAvailability(ASTContext &Context,
 }
 
 AvailabilityResult Decl::getAvailability(std::string *Message) const {
+  if (auto *FTD = dyn_cast<FunctionTemplateDecl>(this))
+    return FTD->getTemplatedDecl()->getAvailability(Message);
+
   AvailabilityResult Result = AR_Available;
   std::string ResultMessage;
 
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index 8d96d7b..e202d87 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -1067,6 +1067,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
       .Case("attribute_availability_tvos", true)
       .Case("attribute_availability_watchos", true)
       .Case("attribute_availability_with_strict", true)
+      .Case("attribute_availability_in_templates", true)
       .Case("attribute_cf_returns_not_retained", true)
       .Case("attribute_cf_returns_retained", true)
       .Case("attribute_cf_returns_on_parameters", true)
diff --git a/test/SemaCXX/attr-unavailable.cpp b/test/SemaCXX/attr-unavailable.cpp
index 51dc8fe..430cb5c 100644
--- a/test/SemaCXX/attr-unavailable.cpp
+++ b/test/SemaCXX/attr-unavailable.cpp
@@ -5,7 +5,8 @@ double &foo(double); // expected-note {{candidate}}
 void foo(...) __attribute__((__unavailable__)); // expected-note {{candidate function}} \
 // expected-note{{'foo' has been explicitly marked unavailable here}}
 
-void bar(...) __attribute__((__unavailable__)); // expected-note 2{{explicitly marked unavailable}}
+void bar(...) __attribute__((__unavailable__)); // expected-note 2{{explicitly marked unavailable}} \
+       // expected-note 2{{candidate function has been explicitly made unavailable}}
 
 void test_foo(short* sp) {
   int &ir = foo(1);
@@ -56,3 +57,62 @@ typedef enum UnavailableEnum AnotherUnavailableEnum; // expected-error {{'Unavai
 
 __attribute__((unavailable))
 UnavailableEnum testUnavailable(UnavailableEnum X) { return X; }
+
+
+// Check that unavailable classes can be used as arguments to unavailable
+// function, particularly in template functions.
+#if !__has_feature(attribute_availability_in_templates)
+#error "Missing __has_feature"
+#endif
+class __attribute((unavailable)) UnavailableClass; // \
+        expected-note 3{{'UnavailableClass' has been explicitly marked unavailable here}}
+void unavail_class(UnavailableClass&); // expected-error {{'UnavailableClass' is unavailable}}
+void unavail_class_marked(UnavailableClass&) __attribute__((unavailable));
+template <class T> void unavail_class(UnavailableClass&); // expected-error {{'UnavailableClass' is unavailable}}
+template <class T> void unavail_class_marked(UnavailableClass&) __attribute__((unavailable));
+template <class T> void templated(T&);
+void untemplated(UnavailableClass &UC) {  // expected-error {{'UnavailableClass' is unavailable}}
+  templated(UC);
+}
+void untemplated_marked(UnavailableClass &UC) __attribute__((unavailable)) {
+  templated(UC);
+}
+
+template <class T> void templated_calls_bar() { bar(); } // \
+           // expected-error{{call to unavailable function 'bar'}}
+template <class T> void templated_calls_bar_arg(T v) { bar(v); } // \
+           // expected-error{{call to unavailable function 'bar'}}
+template <class T> void templated_calls_bar_arg_never_called(T v) { bar(v); }
+
+template <class T>
+void unavail_templated_calls_bar() __attribute__((unavailable)) { // \
+  expected-note{{candidate function [with T = int] has been explicitly made unavailable}}
+  bar(5);
+}
+template <class T>
+void unavail_templated_calls_bar_arg(T v) __attribute__((unavailable)) { // \
+  expected-note{{candidate function [with T = int] has been explicitly made unavailable}}
+  bar(v);
+}
+
+void calls_templates_which_call_bar() {
+  templated_calls_bar<int>();
+
+  templated_calls_bar_arg(5); // \
+  expected-note{{in instantiation of function template specialization 'templated_calls_bar_arg<int>' requested here}}
+
+  unavail_templated_calls_bar<int>(); // \
+  expected-error{{call to unavailable function 'unavail_templated_calls_bar'}}
+
+  unavail_templated_calls_bar_arg(5); // \
+  expected-error{{call to unavailable function 'unavail_templated_calls_bar_arg'}}
+}
+
+template <class T> void unavail_templated(T) __attribute__((unavailable)); // \
+           expected-note{{candidate function [with T = int] has been explicitly made unavailable}}
+void calls_unavail_templated() {
+  unavail_templated(5); // expected-error{{call to unavailable function 'unavail_templated'}}
+}
+void unavail_calls_unavail_templated() __attribute__((unavailable)) {
+  unavail_templated(5);
+}
-- 
2.6.4 (Apple Git-63)

