george.burgess.iv updated this revision to Diff 81259.
george.burgess.iv marked an inline comment as done.
george.burgess.iv added a comment.

Addressed alignment comment. Thanks! (Late parsing is still to-be-done)


https://reviews.llvm.org/D27424

Files:
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/DiagnosticCommonKinds.td
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Overload.h
  include/clang/Sema/Sema.h
  lib/Parse/ParseDecl.cpp
  lib/Sema/SemaDeclAttr.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/SemaTemplateInstantiateDecl.cpp
  test/Sema/diagnose_if.c
  test/SemaCXX/diagnose_if.cpp

Index: test/SemaCXX/diagnose_if.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/diagnose_if.cpp
@@ -0,0 +1,352 @@
+// RUN: %clang_cc1 %s -verify -fno-builtin -std=c++11
+
+#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+
+namespace type_dependent {
+template <typename T>
+void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from diagnose_if}}
+
+template <typename T>
+void alwaysok() _diagnose_if(T(), "oh no", "error") {}
+
+template <typename T>
+void alwayswarn() _diagnose_if(!T(), "oh no", "warning") {} // expected-note 4{{from diagnose_if}}
+
+template <typename T>
+void neverwarn() _diagnose_if(T(), "oh no", "warning") {}
+
+void runAll() {
+  alwaysok<int>();
+  alwaysok<int>();
+
+  {
+    void (*pok)() = alwaysok<int>;
+    pok = &alwaysok<int>;
+  }
+
+  neverok<int>(); // expected-error{{oh no}}
+  neverok<short>(); // expected-error{{oh no}}
+
+  {
+    void (*pok)() = neverok<int>; // expected-error{{oh no}}
+  }
+  {
+    void (*pok)();
+    pok = &neverok<int>; // expected-error{{oh no}}
+  }
+
+  alwayswarn<int>(); // expected-warning{{oh no}}
+  alwayswarn<short>(); // expected-warning{{oh no}}
+  {
+    void (*pok)() = alwayswarn<int>; // expected-warning{{oh no}}
+    pok = &alwayswarn<int>; // expected-warning{{oh no}}
+  }
+
+  neverwarn<int>();
+  neverwarn<short>();
+  {
+    void (*pok)() = neverwarn<int>;
+    pok = &neverwarn<int>;
+  }
+}
+
+template <typename T>
+void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+
+template <typename T>
+void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note {{from diagnose_if}}
+
+void runIf() {
+  errorIf(0);
+  errorIf(1); // expected-error{{call to unavailable function}}
+
+  warnIf(0);
+  warnIf(1); // expected-warning{{oh no}}
+}
+}
+
+namespace value_dependent {
+template <int N>
+void neverok() _diagnose_if(N == 0 || N != 0, "oh no", "error") {} // expected-note 4{{from diagnose_if}}
+
+template <int N>
+void alwaysok() _diagnose_if(N == 0 && N != 0, "oh no", "error") {}
+
+template <int N>
+void alwayswarn() _diagnose_if(N == 0 || N != 0, "oh no", "warning") {} // expected-note 4{{from diagnose_if}}
+
+template <int N>
+void neverwarn() _diagnose_if(N == 0 && N != 0, "oh no", "warning") {}
+
+void runAll() {
+  alwaysok<0>();
+  alwaysok<1>();
+
+  {
+    void (*pok)() = alwaysok<0>;
+    pok = &alwaysok<0>;
+  }
+
+  neverok<0>(); // expected-error{{oh no}}
+  neverok<1>(); // expected-error{{oh no}}
+
+  {
+    void (*pok)() = neverok<0>; // expected-error{{oh no}}
+  }
+  {
+    void (*pok)();
+    pok = &neverok<0>; // expected-error{{oh no}}
+  }
+
+  alwayswarn<0>(); // expected-warning{{oh no}}
+  alwayswarn<1>(); // expected-warning{{oh no}}
+  {
+    void (*pok)() = alwayswarn<0>; // expected-warning{{oh no}}
+    pok = &alwayswarn<0>; // expected-warning{{oh no}}
+  }
+
+  neverwarn<0>();
+  neverwarn<1>();
+  {
+    void (*pok)() = neverwarn<0>;
+    pok = &neverwarn<0>;
+  }
+}
+
+template <int N>
+void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+
+template <int N>
+void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note {{from diagnose_if}}
+
+void runIf() {
+  errorIf<0>(0);
+  errorIf<0>(1); // expected-error{{call to unavailable function}}
+
+  warnIf<0>(0);
+  warnIf<0>(1); // expected-warning{{oh no}}
+}
+}
+
+namespace no_overload_interaction {
+void foo(int) _diagnose_if(1, "oh no", "error"); // expected-note{{from diagnose_if}}
+void foo(short);
+
+void bar(int);
+void bar(short) _diagnose_if(1, "oh no", "error");
+
+void fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{candidate disabled: oh no}}
+void fooArg(short); // expected-note{{candidate function}}
+
+void barArg(int);
+void barArg(short a) _diagnose_if(a, "oh no", "error");
+
+void runAll() {
+  foo(1); // expected-error{{oh no}}
+  bar(1);
+
+  fooArg(1); // expected-error{{call to unavailable function}}
+  barArg(1);
+
+  auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
+}
+}
+
+// We have some inline space craziness going on. This is meant to go over that
+// in hopes of hitting some edge cases. At the time of writing, our inline
+// buffer can support 16 DiagnoseIfAttr*s. If a function only has one attr, no
+// allocation is done.
+namespace overloading_multiple_coverage {
+#define _diagnose_thirteen_times \
+  _diagnose_if(a <= 1, "oh no", "warning") \
+  _diagnose_if(a <= 2, "oh no", "warning") \
+  _diagnose_if(a <= 3, "oh no", "warning") \
+  _diagnose_if(a <= 4, "oh no", "warning") \
+  _diagnose_if(a <= 5, "oh no", "warning") \
+  _diagnose_if(a <= 6, "oh no", "warning") \
+  _diagnose_if(a <= 7, "oh no", "warning") \
+  _diagnose_if(a <= 8, "oh no", "warning") \
+  _diagnose_if(a <= 9, "oh no", "warning") \
+  _diagnose_if(a <= 10, "oh no", "warning") \
+  _diagnose_if(a <= 11, "oh no", "warning") \
+  _diagnose_if(a <= 12, "oh no", "warning") \
+  _diagnose_if(a <= 13, "oh no", "warning")
+
+void foo(short a) _diagnose_thirteen_times; // expected-note 25{{from diagnose_if}}
+void foo(unsigned short a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning");
+void foo(long a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning");
+void foo(unsigned long a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning");
+void foo(int a) _diagnose_if(a <= 1, "oh no", "warning"); // expected-note{{from diagnose_if}}
+
+void runFoo() {
+  foo(int(1)); // expected-warning{{oh no}}
+  foo(short(1)); // expected-warning 13{{oh no}}
+
+  foo(int(2));
+  foo(short(2)); // expected-warning 12{{oh no}}
+}
+
+void bar(short a) _diagnose_thirteen_times; // expected-note 25{{from diagnose_if}}
+void bar(unsigned short a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning")
+  _diagnose_if(a <= 3, "oh no", "warning");
+void bar(long a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning");
+void bar(unsigned long a) _diagnose_if(a <= 1, "oh no", "warning")
+  _diagnose_if(a <= 2, "oh no", "warning");
+void bar(int a) _diagnose_if(a <= 1, "oh no", "warning"); // expected-note{{from diagnose_if}}
+
+void runBar() {
+  bar(int(1)); // expected-warning{{oh no}}
+  bar(short(1)); // expected-warning 13{{oh no}}
+
+  bar(int(2));
+  bar(short(2)); // expected-warning 12{{oh no}}
+}
+#undef _diagnose_thirteen_times
+}
+
+namespace with_default_args {
+void foo(int a = 0) _diagnose_if(a, "oh no", "warning"); // expected-note 1{{from diagnose_if}}
+void bar(int a = 1) _diagnose_if(a, "oh no", "warning"); // expected-note 2{{from diagnose_if}}
+
+void runAll() {
+  foo();
+  foo(0);
+  foo(1); // expected-warning{{oh no}}
+
+  bar(); // expected-warning{{oh no}}
+  bar(0);
+  bar(1); // expected-warning{{oh no}}
+}
+}
+
+namespace naked_mem_expr {
+struct Foo {
+  void foo(int a) _diagnose_if(a, "should warn", "warning"); // expected-note{{from diagnose_if}}
+  void bar(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from diagnose_if}}
+};
+
+void runFoo() {
+  Foo().foo(0);
+  Foo().foo(1); // expected-warning{{should warn}}
+
+  Foo().bar(0);
+  Foo().bar(1); // expected-error{{oh no}}
+}
+}
+
+namespace class_template {
+template <typename T>
+struct Errors {
+  void foo(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from diagnose_if}}
+  void bar(int i) _diagnose_if(i != T(), "bad i", "error"); // expected-note{{from diagnose_if}}
+
+  void fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note 2{{int bad i}}
+  void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note 2{{short bad i}}
+
+  void barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note 2{{int bad i}}
+  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note 2{{short bad i}}
+};
+
+void runErrors() {
+  Errors<int>().foo(0);
+  Errors<int>().foo(1); // expected-error{{bad i}}
+
+  Errors<int>().bar(0);
+  Errors<int>().bar(1); // expected-error{{bad i}}
+
+  Errors<int>().fooOvl(0);
+  Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(short(0));
+  Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
+
+  Errors<int>().barOvl(0);
+  Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(short(0));
+  Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
+}
+
+template <typename T>
+struct Warnings {
+  void foo(int i) _diagnose_if(i, "bad i", "warning"); // expected-note{{from diagnose_if}}
+  void bar(int i) _diagnose_if(i != T(), "bad i", "warning"); // expected-note{{from diagnose_if}}
+
+  void fooOvl(int i) _diagnose_if(i, "int bad i", "warning"); // expected-note{{from diagnose_if}}
+  void fooOvl(short i) _diagnose_if(i, "short bad i", "warning"); // expected-note{{from diagnose_if}}
+
+  void barOvl(int i) _diagnose_if(i != T(), "int bad i", "warning"); // expected-note{{from diagnose_if}}
+  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "warning"); // expected-note{{from diagnose_if}}
+};
+
+void runWarnings() {
+  Warnings<int>().foo(0);
+  Warnings<int>().foo(1); // expected-warning{{bad i}}
+
+  Warnings<int>().bar(0);
+  Warnings<int>().bar(1); // expected-warning{{bad i}}
+
+  Warnings<int>().fooOvl(0);
+  Warnings<int>().fooOvl(1); // expected-warning{{int bad i}}
+  Warnings<int>().fooOvl(short(0));
+  Warnings<int>().fooOvl(short(1)); // expected-warning{{short bad i}}
+
+  Warnings<int>().barOvl(0);
+  Warnings<int>().barOvl(1); // expected-warning{{int bad i}}
+  Warnings<int>().barOvl(short(0));
+  Warnings<int>().barOvl(short(1)); // expected-warning{{short bad i}}
+}
+}
+
+namespace template_specialization {
+template <typename T>
+struct Foo {
+  void foo() _diagnose_if(1, "override me", "error"); // expected-note{{from diagnose_if}}
+  void bar(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from diagnose_if}}
+  void baz(int i);
+};
+
+template <>
+struct Foo<int> {
+  void foo();
+  void bar(int i);
+  void baz(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from diagnose_if}}
+};
+
+void runAll() {
+  Foo<double>().foo(); // expected-error{{override me}}
+  Foo<int>().foo();
+
+  Foo<double>().bar(1); // expected-error{{bad i}}
+  Foo<int>().bar(1);
+
+  Foo<double>().baz(1);
+  Foo<int>().baz(1); // expected-error{{bad i}}
+}
+}
+
+namespace late_constexpr {
+constexpr int foo();
+constexpr int foo(int a);
+
+void bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from diagnose_if}} expected-note{{not viable: requires 0 arguments}}
+void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{bad foo}}
+
+void early() {
+  bar();
+  bar(0);
+  bar(1);
+}
+
+constexpr int foo() { return 1; }
+constexpr int foo(int a) { return a; }
+
+void late() {
+  bar(); // expected-error{{bad foo}}
+  bar(0);
+  bar(1); // expected-error{{call to unavailable function}}
+}
+}
Index: test/Sema/diagnose_if.c
===================================================================
--- /dev/null
+++ test/Sema/diagnose_if.c
@@ -0,0 +1,152 @@
+// RUN: %clang_cc1 %s -verify -fno-builtin
+
+#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+
+void failure() _diagnose_if(); // expected-error{{exactly 3 arguments}}
+void failure() _diagnose_if(0); // expected-error{{exactly 3 arguments}}
+void failure() _diagnose_if(0, ""); // expected-error{{exactly 3 arguments}}
+void failure() _diagnose_if(0, "", "error", 1); // expected-error{{exactly 3 arguments}}
+void failure() _diagnose_if(0, 0, "error"); // expected-error{{requires a string}}
+void failure() _diagnose_if(0, "", "invalid"); // expected-error{{invalid diagnostic type}}
+void failure() _diagnose_if(0, "", "ERROR"); // expected-error{{invalid diagnostic type}}
+void failure(int a) _diagnose_if(a, "", ""); // expected-error{{invalid diagnostic type}}
+void failure() _diagnose_if(a, "", ""); // expected-error{{undeclared identifier 'a'}}
+
+int globalVar;
+void never_constant() _diagnose_if(globalVar, "", "error"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}
+void never_constant() _diagnose_if(globalVar, "", "warning"); // expected-error{{'diagnose_if' attribute expression never produces a constant expression}} expected-note{{subexpression not valid}}
+
+int alwaysok(int q) _diagnose_if(0, "", "error");
+int neverok(int q) _diagnose_if(1, "oh no", "error"); // expected-note 5{{from diagnose_if attribute on 'neverok'}}
+int alwayswarn(int q) _diagnose_if(1, "oh no", "warning"); // expected-note 5{{from diagnose_if attribute}}
+int neverwarn(int q) _diagnose_if(0, "", "warning");
+
+void runConstant() {
+  int m;
+  alwaysok(0);
+  alwaysok(1);
+  alwaysok(m);
+
+  {
+    int (*pok)(int) = alwaysok;
+    pok = &alwaysok;
+  }
+
+  neverok(0); // expected-error{{oh no}}
+  neverok(1); // expected-error{{oh no}}
+  neverok(m); // expected-error{{oh no}}
+  {
+    int (*pok)(int) = neverok; // expected-error{{oh no}}
+    pok = &neverok; // expected-error{{oh no}}
+  }
+
+  alwayswarn(0); // expected-warning{{oh no}}
+  alwayswarn(1); // expected-warning{{oh no}}
+  alwayswarn(m); // expected-warning{{oh no}}
+  {
+    int (*pok)(int) = alwayswarn; // expected-warning{{oh no}}
+    pok = &alwayswarn; // expected-warning{{oh no}}
+  }
+
+  neverwarn(0);
+  neverwarn(1);
+  neverwarn(m);
+  {
+    int (*pok)(int) = neverwarn;
+    pok = &neverwarn;
+  }
+}
+
+int abs(int q) _diagnose_if(q >= 0, "redundant abs call", "error"); //expected-note{{from diagnose_if}}
+void runVariable() {
+  int m;
+  abs(-1);
+  abs(1); // expected-error{{redundant abs call}}
+  abs(m);
+
+  int (*pabs)(int) = abs;
+  pabs = &abs;
+}
+
+#define _overloadable __attribute__((overloadable))
+
+int ovl1(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{oh no}}
+int ovl1(void *m) _overloadable; // expected-note{{candidate function}}
+
+int ovl2(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{candidate function}}
+int ovl2(char *m) _overloadable; // expected-note{{candidate function}}
+void overloadsYay() {
+  ovl1((void *)0);
+  ovl1(""); // expected-error{{call to unavailable function}}
+
+  ovl2((void *)0); // expected-error{{ambiguous}}
+}
+
+void errorWarnDiagnose1() _diagnose_if(1, "oh no", "error") // expected-note{{from diagnose_if}}
+  _diagnose_if(1, "nop", "warning");
+void errorWarnDiagnose2() _diagnose_if(1, "oh no", "error") // expected-note{{from diagnose_if}}
+  _diagnose_if(1, "nop", "error");
+void errorWarnDiagnose3() _diagnose_if(1, "nop", "warning")
+  _diagnose_if(1, "oh no", "error"); // expected-note{{from diagnose_if}}
+
+void errorWarnDiagnoseArg1(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from diagnose_if}}
+  _diagnose_if(a == 1, "nop", "warning");
+void errorWarnDiagnoseArg2(int a) _diagnose_if(a == 1, "oh no", "error") // expected-note{{from diagnose_if}}
+  _diagnose_if(a == 1, "nop", "error");
+void errorWarnDiagnoseArg3(int a) _diagnose_if(a == 1, "nop", "warning")
+  _diagnose_if(a == 1, "oh no", "error"); // expected-note{{from diagnose_if}}
+
+void runErrorWarnDiagnose() {
+  errorWarnDiagnose1(); // expected-error{{oh no}}
+  errorWarnDiagnose2(); // expected-error{{oh no}}
+  errorWarnDiagnose3(); // expected-error{{oh no}}
+
+  errorWarnDiagnoseArg1(1); // expected-error{{oh no}}
+  errorWarnDiagnoseArg2(1); // expected-error{{oh no}}
+  errorWarnDiagnoseArg3(1); // expected-error{{oh no}}
+}
+
+void warnWarnDiagnose() _diagnose_if(1, "oh no!", "warning") _diagnose_if(1, "foo", "warning"); // expected-note 2{{from diagnose_if}}
+void runWarnWarnDiagnose() {
+  warnWarnDiagnose(); // expected-warning{{oh no!}} expected-warning{{foo}}
+}
+
+void declsStackErr1(int a) _diagnose_if(a & 1, "decl1", "error"); // expected-note 2{{from diagnose_if}}
+void declsStackErr1(int a) _diagnose_if(a & 2, "decl2", "error"); // expected-note{{from diagnose_if}}
+void declsStackErr2();
+void declsStackErr2() _diagnose_if(1, "complaint", "error"); // expected-note{{from diagnose_if}}
+void declsStackErr3() _diagnose_if(1, "complaint", "error"); // expected-note{{from diagnose_if}}
+void declsStackErr3();
+void runDeclsStackErr() {
+  declsStackErr1(0);
+  declsStackErr1(1); // expected-error{{decl1}}
+  declsStackErr1(2); // expected-error{{decl2}}
+  declsStackErr1(3); // expected-error{{decl1}}
+  declsStackErr2(); // expected-error{{complaint}}
+  declsStackErr3(); // expected-error{{complaint}}
+}
+
+void declsStackWarn1(int a) _diagnose_if(a & 1, "decl1", "warning"); // expected-note 2{{from diagnose_if}}
+void declsStackWarn1(int a) _diagnose_if(a & 2, "decl2", "warning"); // expected-note 2{{from diagnose_if}}
+void declsStackWarn2();
+void declsStackWarn2() _diagnose_if(1, "complaint", "warning"); // expected-note{{from diagnose_if}}
+void declsStackWarn3() _diagnose_if(1, "complaint", "warning"); // expected-note{{from diagnose_if}}
+void declsStackWarn3();
+void runDeclsStackWarn() {
+  declsStackWarn1(0);
+  declsStackWarn1(1); // expected-warning{{decl1}}
+  declsStackWarn1(2); // expected-warning{{decl2}}
+  declsStackWarn1(3); // expected-warning{{decl1}} expected-warning{{decl2}}
+  declsStackWarn2(); // expected-warning{{complaint}}
+  declsStackWarn3(); // expected-warning{{complaint}}
+}
+
+void noMsg(int n) _diagnose_if(n, "", "warning"); // expected-note{{from diagnose_if}}
+void runNoMsg() {
+  noMsg(1); // expected-warning{{<no message provided>}}
+}
+
+void alwaysWarnWithArg(int a) _diagnose_if(1 || a, "alwaysWarn", "warning"); // expected-note{{from diagnose_if}}
+void runAlwaysWarnWithArg(int a) {
+  alwaysWarnWithArg(a); // expected-warning{{alwaysWarn}}
+}
Index: lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -168,39 +168,59 @@
                         Aligned->getSpellingListIndex());
 }
 
-static void instantiateDependentEnableIfAttr(
+static Expr *instantiateDependentFunctionAttrCondition(
     Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
-    const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
+    const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
   Expr *Cond = nullptr;
   {
     EnterExpressionEvaluationContext Unevaluated(S, Sema::Unevaluated);
-    ExprResult Result = S.SubstExpr(A->getCond(), TemplateArgs);
+    ExprResult Result = S.SubstExpr(OldCond, TemplateArgs);
     if (Result.isInvalid())
-      return;
+      return nullptr;
     Cond = Result.getAs<Expr>();
   }
   if (!Cond->isTypeDependent()) {
     ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
     if (Converted.isInvalid())
-      return;
+      return nullptr;
     Cond = Converted.get();
   }
 
   SmallVector<PartialDiagnosticAt, 8> Diags;
-  if (A->getCond()->isValueDependent() && !Cond->isValueDependent() &&
-      !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(New),
-                                                Diags)) {
-    S.Diag(A->getLocation(), diag::err_enable_if_never_constant_expr);
-    for (int I = 0, N = Diags.size(); I != N; ++I)
-      S.Diag(Diags[I].first, Diags[I].second);
-    return;
+  if (OldCond->isValueDependent() && !Cond->isValueDependent() &&
+      !Expr::isPotentialConstantExprUnevaluated(Cond, New, Diags)) {
+    S.Diag(A->getLocation(), diag::err_attr_cond_never_constant_expr)
+        << A->getSpelling();
+    for (const PartialDiagnosticAt &P : Diags)
+      S.Diag(P.first, P.second);
+    return nullptr;
   }
+  return Cond;
+}
 
-  EnableIfAttr *EIA = new (S.getASTContext())
-                        EnableIfAttr(A->getLocation(), S.getASTContext(), Cond,
-                                     A->getMessage(),
-                                     A->getSpellingListIndex());
-  New->addAttr(EIA);
+static void instantiateDependentEnableIfAttr(
+    Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+    const EnableIfAttr *EIA, const Decl *Tmpl, FunctionDecl *New) {
+  Expr *Cond = instantiateDependentFunctionAttrCondition(
+      S, TemplateArgs, EIA, EIA->getCond(), Tmpl, New);
+
+  if (Cond)
+    New->addAttr(new (S.getASTContext()) EnableIfAttr(
+        EIA->getLocation(), S.getASTContext(), Cond, EIA->getMessage(),
+        EIA->getSpellingListIndex()));
+}
+
+static void instantiateDependentDiagnoseIfAttr(
+    Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+    const DiagnoseIfAttr *DIA, const Decl *Tmpl, FunctionDecl *New) {
+  Expr *Cond = instantiateDependentFunctionAttrCondition(
+      S, TemplateArgs, DIA, DIA->getCond(), Tmpl, New);
+
+  if (Cond)
+    New->addAttr(new (S.getASTContext()) DiagnoseIfAttr(
+        DIA->getLocation(), S.getASTContext(), Cond, DIA->getMessage(),
+        DIA->getDiagnosticType(), DIA->getArgDependent(), New,
+        DIA->getSpellingListIndex()));
 }
 
 // Constructs and adds to New a new instance of CUDALaunchBoundsAttr using
@@ -334,7 +354,13 @@
 
     if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
       instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
-                                       New);
+                                       cast<FunctionDecl>(New));
+      continue;
+    }
+
+    if (const auto *DiagnoseIf = dyn_cast<DiagnoseIfAttr>(TmplAttr)) {
+      instantiateDependentDiagnoseIfAttr(*this, TemplateArgs, DiagnoseIf, Tmpl,
+                                         cast<FunctionDecl>(New));
       continue;
     }
 
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -29,6 +29,7 @@
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallString.h"
@@ -823,12 +824,20 @@
 
 void OverloadCandidateSet::clear() {
   destroyCandidates();
-  ConversionSequenceAllocator.Reset();
-  NumInlineSequences = 0;
+  // DiagnoseIfAttrs are just pointers, so we don't need to destroy them.
+  SlabAllocator.Reset();
+  NumInlineBytesUsed = 0;
   Candidates.clear();
   Functions.clear();
 }
 
+DiagnoseIfAttr **
+OverloadCandidateSet::addDiagnoseIfComplaints(ArrayRef<DiagnoseIfAttr *> CA) {
+  auto *DIA = slabAllocate<DiagnoseIfAttr *>(CA.size());
+  std::uninitialized_copy(CA.begin(), CA.end(), DIA);
+  return DIA;
+}
+
 namespace {
   class UnbridgedCastsSet {
     struct Entry {
@@ -5786,6 +5795,27 @@
   return false;
 }
 
+static void initDiagnoseIfComplaint(Sema &S, OverloadCandidateSet &CandidateSet,
+                                    OverloadCandidate &Candidate,
+                                    FunctionDecl *Function,
+                                    ArrayRef<Expr *> Args,
+                                    bool MissingImplicitThis = false) {
+  SmallVector<DiagnoseIfAttr *, 8> Results;
+  if (DiagnoseIfAttr *DIA = S.checkArgDependentDiagnoseIf(
+          Function, Args, Results, MissingImplicitThis)) {
+    Results.clear();
+    Results.push_back(DIA);
+  }
+
+  Candidate.NumTriggeredDiagnoseIfs = Results.size();
+  if (Results.empty())
+    Candidate.DiagnoseIfInfo = nullptr;
+  else if (Results.size() == 1)
+    Candidate.DiagnoseIfInfo = Results[0];
+  else
+    Candidate.DiagnoseIfInfo = CandidateSet.addDiagnoseIfComplaints(Results);
+}
+
 /// AddOverloadCandidate - Adds the given function to the set of
 /// candidate functions, using the given function call arguments.  If
 /// @p SuppressUserConversions, then don't allow user-defined
@@ -5949,6 +5979,8 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
 }
 
 ObjCMethodDecl *
@@ -6061,16 +6093,10 @@
   return Result;
 }
 
-EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                                  bool MissingImplicitThis) {
-  auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
-  if (EnableIfAttrs.empty())
-    return nullptr;
-
-  SFINAETrap Trap(*this);
-  SmallVector<Expr *, 16> ConvertedArgs;
-  bool InitializationFailed = false;
-
+static bool convertArgsForAvailabilityChecks(
+    Sema &S, FunctionDecl *Function, ArrayRef<Expr *> Args,
+    bool MissingImplicitThis, Sema::SFINAETrap &Trap,
+    SmallVectorImpl<Expr *> &ConvertedArgs) {
   // Ignore any variadic arguments. Converting them is pointless, since the
   // user can't refer to them in the enable_if condition.
   unsigned ArgSizeNoVarargs = std::min(Function->param_size(), Args.size());
@@ -6082,45 +6108,55 @@
         !cast<CXXMethodDecl>(Function)->isStatic() &&
         !isa<CXXConstructorDecl>(Function)) {
       CXXMethodDecl *Method = cast<CXXMethodDecl>(Function);
-      R = PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
-                                              Method, Method);
+      R = S.PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
+                                                Method, Method);
     } else {
-      R = PerformCopyInitialization(InitializedEntity::InitializeParameter(
-                                        Context, Function->getParamDecl(I)),
-                                    SourceLocation(), Args[I]);
+      R = S.PerformCopyInitialization(InitializedEntity::InitializeParameter(
+                                          S.Context, Function->getParamDecl(I)),
+                                      SourceLocation(), Args[I]);
     }
 
-    if (R.isInvalid()) {
-      InitializationFailed = true;
-      break;
-    }
+    if (R.isInvalid())
+      return false;
 
     ConvertedArgs.push_back(R.get());
   }
 
-  if (InitializationFailed || Trap.hasErrorOccurred())
-    return EnableIfAttrs[0];
+  if (Trap.hasErrorOccurred())
+    return false;
 
   // Push default arguments if needed.
   if (!Function->isVariadic() && Args.size() < Function->getNumParams()) {
     for (unsigned i = Args.size(), e = Function->getNumParams(); i != e; ++i) {
       ParmVarDecl *P = Function->getParamDecl(i);
-      ExprResult R = PerformCopyInitialization(
-          InitializedEntity::InitializeParameter(Context,
+      ExprResult R = S.PerformCopyInitialization(
+          InitializedEntity::InitializeParameter(S.Context,
                                                  Function->getParamDecl(i)),
           SourceLocation(),
           P->hasUninstantiatedDefaultArg() ? P->getUninstantiatedDefaultArg()
                                            : P->getDefaultArg());
-      if (R.isInvalid()) {
-        InitializationFailed = true;
-        break;
-      }
+      if (R.isInvalid())
+        return false;
       ConvertedArgs.push_back(R.get());
     }
 
-    if (InitializationFailed || Trap.hasErrorOccurred())
-      return EnableIfAttrs[0];
+    if (Trap.hasErrorOccurred())
+      return false;
   }
+  return true;
+}
+
+EnableIfAttr *Sema::CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
+                                  bool MissingImplicitThis) {
+  auto EnableIfAttrs = getOrderedEnableIfAttrs(Function);
+  if (EnableIfAttrs.empty())
+    return nullptr;
+
+  SFINAETrap Trap(*this);
+  SmallVector<Expr *, 16> ConvertedArgs;
+  if (!convertArgsForAvailabilityChecks(
+          *this, Function, Args, MissingImplicitThis, Trap, ConvertedArgs))
+    return EnableIfAttrs[0];
 
   for (auto *EIA : EnableIfAttrs) {
     APValue Result;
@@ -6136,6 +6172,92 @@
   return nullptr;
 }
 
+static bool gatherDiagnoseIfAttrs(FunctionDecl *Function, bool ArgDependent,
+                                  SmallVectorImpl<DiagnoseIfAttr *> &Errors,
+                                  SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
+  for (Attr *A : Function->attrs())
+    if (auto *DIA = dyn_cast<DiagnoseIfAttr>(A))
+      if (ArgDependent == DIA->getArgDependent()) {
+        if (DIA->isError())
+          Errors.push_back(DIA);
+        else
+          Nonfatal.push_back(DIA);
+      }
+
+  return !Errors.empty() || !Nonfatal.empty();
+}
+
+template <typename CheckFn>
+static DiagnoseIfAttr *
+checkDiagnoseIfAttrsWith(const SmallVectorImpl<DiagnoseIfAttr *> &Errors,
+                         SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
+                         CheckFn &&IsSuccessful) {
+  // attrs() iterates through attributes in reverse, and we want to traverse
+  // through diagnose_if attributes in top-to-bottom order. So, we need to
+  // reverse them.
+  auto ErrAttr = llvm::find_if(llvm::reverse(Errors), IsSuccessful);
+  if (ErrAttr != Errors.rend())
+    return *ErrAttr;
+
+  auto NewEnd = llvm::remove_if(
+      Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); });
+  Nonfatal.erase(NewEnd, Nonfatal.end());
+
+  // Again, these things are stored in reverse order, and we care about having
+  // them in the correct order.
+  std::reverse(Nonfatal.begin(), Nonfatal.end());
+  return nullptr;
+}
+
+DiagnoseIfAttr *
+Sema::checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
+                                  SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
+                                  bool MissingImplicitThis) {
+  SmallVector<DiagnoseIfAttr *, 4> Errors;
+  if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/true, Errors, Nonfatal))
+    return nullptr;
+
+  SFINAETrap Trap(*this);
+  SmallVector<Expr *, 16> ConvertedArgs;
+  if (!convertArgsForAvailabilityChecks(
+          *this, Function, Args, MissingImplicitThis, Trap, ConvertedArgs))
+    return nullptr;
+
+  return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) {
+    APValue Result;
+    // It's sane to use the same ConvertedArgs for any redecl of this function,
+    // since EvaluateWithSubstitution only cares about the position of each
+    // argument in the arg list, not the ParmVarDecl* it maps to.
+    if (!DIA->getCond()->EvaluateWithSubstitution(
+            Result, Context, DIA->getParent(), ConvertedArgs))
+      return false;
+    return Result.isInt() && Result.getInt().getBoolValue();
+  });
+}
+
+DiagnoseIfAttr *Sema::checkArgIndependentDiagnoseIf(
+    FunctionDecl *Function, SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
+  SmallVector<DiagnoseIfAttr *, 4> Errors;
+  if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/false, Errors,
+                             Nonfatal))
+    return nullptr;
+
+  return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) {
+    bool Result;
+    return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
+           Result;
+  });
+}
+
+void Sema::emitDiagnoseIfDiagnostic(SourceLocation Loc,
+                                    const DiagnoseIfAttr *DIA) {
+  auto Code = DIA->isError() ? diag::err_diagnose_if_succeeded
+                             : diag::warn_diagnose_if_succeeded;
+  Diag(Loc, Code) << DIA->getMessage();
+  Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+      << DIA->getParent() << DIA->getCond()->getSourceRange();
+}
+
 /// \brief Add all of the function declarations in the given function set to
 /// the overload candidate set.
 void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns,
@@ -6334,6 +6456,8 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Method, Args, true);
 }
 
 /// \brief Add a C++ member function template as a candidate to the candidate
@@ -6643,6 +6767,8 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
 }
 
 /// \brief Adds a conversion function template specialization
@@ -6795,6 +6921,8 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
+
+  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
 }
 
 /// \brief Add overload candidates for overloaded operators that are
@@ -8877,6 +9005,11 @@
   }
 }
 
+static bool isCandidateUnavailableDueToDiagnoseIf(const OverloadCandidate &OC) {
+  return OC.NumTriggeredDiagnoseIfs == 1 &&
+         OC.DiagnoseIfInfo.get<DiagnoseIfAttr *>()->isError();
+}
+
 /// \brief Computes the best viable function (C++ 13.3.3)
 /// within an overload candidate set.
 ///
@@ -8957,13 +9090,19 @@
   // Best is the best viable function.
   if (Best->Function &&
       (Best->Function->isDeleted() ||
-       S.isFunctionConsideredUnavailable(Best->Function)))
+       S.isFunctionConsideredUnavailable(Best->Function) ||
+       isCandidateUnavailableDueToDiagnoseIf(*Best)))
     return OR_Deleted;
 
   if (!EquivalentCands.empty())
     S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
                                                     EquivalentCands);
 
+  for (DiagnoseIfAttr *W : Best->getDiagnoseIfInfo()) {
+    assert(W->isWarning() && "Errors should've been caught earlier!");
+    S.emitDiagnoseIfDiagnostic(Loc, W);
+  }
+
   return OR_Success;
 }
 
@@ -9794,7 +9933,7 @@
   EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
 
   S.Diag(Callee->getLocation(),
-         diag::note_ovl_candidate_disabled_by_enable_if_attr)
+         diag::note_ovl_candidate_disabled_by_function_cond_attr)
       << Attr->getCond()->getSourceRange() << Attr->getMessage();
 }
 
@@ -9817,21 +9956,28 @@
   FunctionDecl *Fn = Cand->Function;
 
   // Note deleted candidates, but only if they're viable.
-  if (Cand->Viable && (Fn->isDeleted() ||
-      S.isFunctionConsideredUnavailable(Fn))) {
-    std::string FnDesc;
-    OverloadCandidateKind FnKind =
+  if (Cand->Viable) {
+    if (Fn->isDeleted() || S.isFunctionConsideredUnavailable(Fn)) {
+      std::string FnDesc;
+      OverloadCandidateKind FnKind =
         ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
 
-    S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
-      << FnKind << FnDesc
-      << (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0);
-    MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
-    return;
-  }
+      S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
+        << FnKind << FnDesc
+        << (Fn->isDeleted() ? (Fn->isDeletedAsWritten() ? 1 : 2) : 0);
+      MaybeEmitInheritedConstructorNote(S, Cand->FoundDecl);
+      return;
+    }
+    if (isCandidateUnavailableDueToDiagnoseIf(*Cand)) {
+      auto *A = Cand->DiagnoseIfInfo.get<DiagnoseIfAttr *>();
+      assert(A->isError() && "Non-error diagnose_if disables a candidate?");
+      S.Diag(Cand->Function->getLocation(),
+             diag::note_ovl_candidate_disabled_by_function_cond_attr)
+          << A->getCond()->getSourceRange() << A->getMessage();
+      return;
+    }
 
-  // We don't really have anything else to say about viable candidates.
-  if (Cand->Viable) {
+    // We don't really have anything else to say about viable candidates.
     S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
     return;
   }
@@ -12511,10 +12657,20 @@
            diag::err_ovl_no_viable_member_function_in_call)
           << Method << Method->getSourceRange();
       Diag(Method->getLocation(),
-           diag::note_ovl_candidate_disabled_by_enable_if_attr)
+           diag::note_ovl_candidate_disabled_by_function_cond_attr)
           << Attr->getCond()->getSourceRange() << Attr->getMessage();
       return ExprError();
     }
+
+    SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
+    if (const DiagnoseIfAttr *Attr = checkArgDependentDiagnoseIf(
+            Method, Args, Nonfatal, /*MissingImplicitThis=*/true)) {
+      emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
+      return ExprError();
+    }
+
+    for (DiagnoseIfAttr *Attr : Nonfatal)
+      emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
   }
 
   if ((isa<CXXConstructorDecl>(CurContext) || 
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -342,6 +342,7 @@
   }
 
   // See if this is a deleted function.
+  SmallVector<DiagnoseIfAttr *, 4> DiagnoseIfWarnings;
   if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
     if (FD->isDeleted()) {
       auto *Ctor = dyn_cast<CXXConstructorDecl>(FD);
@@ -363,6 +364,12 @@
 
     if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
       return true;
+
+    if (DiagnoseIfAttr *A =
+            checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) {
+      emitDiagnoseIfDiagnostic(Loc, A);
+      return true;
+    }
   }
 
   // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
@@ -377,6 +384,10 @@
     Diag(D->getLocation(), diag::note_entity_declared_at) << D;
     return true;
   }
+
+  for (DiagnoseIfAttr *W : DiagnoseIfWarnings)
+    emitDiagnoseIfDiagnostic(Loc, W);
+
   DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
                              ObjCPropertyAccess);
 
@@ -5140,12 +5151,40 @@
   return OverloadDecl;
 }
 
-static bool isNumberOfArgsValidForCall(Sema &S, const FunctionDecl *Callee,
-                                       std::size_t NumArgs) {
-  if (S.TooManyArguments(Callee->getNumParams(), NumArgs,
-                         /*PartialOverloading=*/false))
-    return Callee->isVariadic();
-  return Callee->getMinRequiredArguments() <= NumArgs;
+static void checkDirectCallValidity(Sema &S, const Expr *Fn,
+                                    FunctionDecl *Callee,
+                                    MultiExprArg ArgExprs) {
+  // `Callee` (when called with ArgExprs) may be ill-formed. enable_if (and
+  // similar attributes) really don't like it when functions are called with an
+  // invalid number of args.
+  if (S.TooManyArguments(Callee->getNumParams(), ArgExprs.size(),
+                         /*PartialOverloading=*/false) &&
+      !Callee->isVariadic())
+    return;
+  if (Callee->getMinRequiredArguments() > ArgExprs.size())
+    return;
+
+  if (const EnableIfAttr *Attr = S.CheckEnableIf(Callee, ArgExprs, true)) {
+    S.Diag(Fn->getLocStart(),
+           isa<CXXMethodDecl>(Callee)
+               ? diag::err_ovl_no_viable_member_function_in_call
+               : diag::err_ovl_no_viable_function_in_call)
+        << Callee << Callee->getSourceRange();
+    S.Diag(Callee->getLocation(),
+           diag::note_ovl_candidate_disabled_by_function_cond_attr)
+        << Attr->getCond()->getSourceRange() << Attr->getMessage();
+    return;
+  }
+
+  SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
+  if (const DiagnoseIfAttr *Attr = S.checkArgDependentDiagnoseIf(
+          Callee, ArgExprs, Nonfatal, /*MissingImplicitThis=*/true)) {
+    S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), Attr);
+    return;
+  }
+
+  for (DiagnoseIfAttr *Attr : Nonfatal)
+    S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), Attr);
 }
 
 /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
@@ -5278,25 +5317,7 @@
                                            Fn->getLocStart()))
       return ExprError();
 
-    // CheckEnableIf assumes that the we're passing in a sane number of args for
-    // FD, but that doesn't always hold true here. This is because, in some
-    // cases, we'll emit a diag about an ill-formed function call, but then
-    // we'll continue on as if the function call wasn't ill-formed. So, if the
-    // number of args looks incorrect, don't do enable_if checks; we should've
-    // already emitted an error about the bad call.
-    if (FD->hasAttr<EnableIfAttr>() &&
-        isNumberOfArgsValidForCall(*this, FD, ArgExprs.size())) {
-      if (const EnableIfAttr *Attr = CheckEnableIf(FD, ArgExprs, true)) {
-        Diag(Fn->getLocStart(),
-             isa<CXXMethodDecl>(FD)
-                 ? diag::err_ovl_no_viable_member_function_in_call
-                 : diag::err_ovl_no_viable_function_in_call)
-            << FD << FD->getSourceRange();
-        Diag(FD->getLocation(),
-             diag::note_ovl_candidate_disabled_by_enable_if_attr)
-            << Attr->getCond()->getSourceRange() << Attr->getMessage();
-      }
-    }
+    checkDirectCallValidity(*this, Fn, FD, ArgExprs);
   }
 
   return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, ArgExprs, RParenLoc,
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -32,6 +32,7 @@
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/MathExtras.h"
 
@@ -805,34 +806,99 @@
                                Attr.getAttributeSpellingListIndex()));
 }
 
-static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
-  S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);
-
-  Expr *Cond = Attr.getArgAsExpr(0);
+static bool checkFunctionConditionAttr(Sema &S, Decl *D,
+                                       const AttributeList &Attr,
+                                       Expr *&Cond, StringRef &Msg) {
+  Cond = Attr.getArgAsExpr(0);
   if (!Cond->isTypeDependent()) {
     ExprResult Converted = S.PerformContextuallyConvertToBool(Cond);
     if (Converted.isInvalid())
-      return;
+      return false;
     Cond = Converted.get();
   }
 
-  StringRef Msg;
   if (!S.checkStringLiteralArgumentAttr(Attr, 1, Msg))
-    return;
+    return false;
+
+  if (Msg.empty())
+    Msg = "<no message provided>";
 
   SmallVector<PartialDiagnosticAt, 8> Diags;
   if (!Cond->isValueDependent() &&
       !Expr::isPotentialConstantExprUnevaluated(Cond, cast<FunctionDecl>(D),
                                                 Diags)) {
-    S.Diag(Attr.getLoc(), diag::err_enable_if_never_constant_expr);
+    S.Diag(Attr.getLoc(), diag::err_attr_cond_never_constant_expr)
+        << Attr.getName();
     for (const PartialDiagnosticAt &PDiag : Diags)
       S.Diag(PDiag.first, PDiag.second);
+    return false;
+  }
+  return true;
+}
+
+static void handleEnableIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  S.Diag(Attr.getLoc(), diag::ext_clang_enable_if);
+
+  Expr *Cond;
+  StringRef Msg;
+  if (checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
+    D->addAttr(::new (S.Context)
+                   EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
+                                Attr.getAttributeSpellingListIndex()));
+}
+
+namespace {
+/// Determines if a given Expr references any of the given ParmVarDecls.
+class ParmVarDeclChecker : public RecursiveASTVisitor<ParmVarDeclChecker> {
+  llvm::SmallPtrSet<const ParmVarDecl *, 16> Parms;
+  bool Result;
+
+public:
+  ParmVarDeclChecker(FunctionDecl *FD) {
+    Parms.insert(FD->param_begin(), FD->param_end());
+  }
+
+  bool referencesParmVars(Expr *E) {
+    Result = false;
+    TraverseStmt(E);
+    return Result;
+  }
+
+  bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+    if (auto *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl()))
+      if (Parms.count(PVD)) {
+        Result = true;
+        return false;
+      }
+    return true;
+  }
+};
+}
+
+static void handleDiagnoseIfAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+  S.Diag(Attr.getLoc(), diag::ext_clang_diagnose_if);
+
+  Expr *Cond;
+  StringRef Msg;
+  if (!checkFunctionConditionAttr(S, D, Attr, Cond, Msg))
+    return;
+
+  StringRef DiagTypeStr;
+  if (!S.checkStringLiteralArgumentAttr(Attr, 2, DiagTypeStr))
+    return;
+
+  DiagnoseIfAttr::DiagnosticType DiagType;
+  if (!DiagnoseIfAttr::ConvertStrToDiagnosticType(DiagTypeStr, DiagType)) {
+    S.Diag(Attr.getArgAsExpr(2)->getLocStart(),
+           diag::err_diagnose_if_invalid_diagnostic_type);
     return;
   }
 
-  D->addAttr(::new (S.Context)
-             EnableIfAttr(Attr.getRange(), S.Context, Cond, Msg,
-                          Attr.getAttributeSpellingListIndex()));
+  auto *FD = cast<FunctionDecl>(D);
+  bool ArgDependent = ParmVarDeclChecker(FD).referencesParmVars(Cond);
+  D->addAttr(::new (S.Context) DiagnoseIfAttr(
+      Attr.getRange(), S.Context, Cond, Msg, DiagType, ArgDependent, FD,
+      Attr.getAttributeSpellingListIndex()));
 }
 
 static void handlePassObjectSizeAttr(Sema &S, Decl *D,
@@ -5600,6 +5666,9 @@
   case AttributeList::AT_EnableIf:
     handleEnableIfAttr(S, D, Attr);
     break;
+  case AttributeList::AT_DiagnoseIf:
+    handleDiagnoseIfAttr(S, D, Attr);
+    break;
   case AttributeList::AT_ExtVectorType:
     handleExtVectorTypeAttr(S, scope, D, Attr);
     break;
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -331,6 +331,11 @@
   return static_cast<unsigned>(ArgExprs.size());
 }
 
+static bool attrMayReferToFunctionParams(StringRef Name) {
+  StringRef NormalizedName = normalizeAttrName(Name);
+  return NormalizedName == "enable_if" || NormalizedName == "diagnose_if";
+}
+
 /// Parse the arguments to a parameterized GNU attribute or
 /// a C++11 attribute in "gnu" namespace.
 void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
@@ -368,8 +373,8 @@
   // These may refer to the function arguments, but need to be parsed early to
   // participate in determining whether it's a redeclaration.
   llvm::Optional<ParseScope> PrototypeScope;
-  if (normalizeAttrName(AttrName->getName()) == "enable_if" &&
-      D && D->isFunctionDeclarator()) {
+  if (D && D->isFunctionDeclarator() &&
+      attrMayReferToFunctionParams(AttrName->getName())) {
     DeclaratorChunk::FunctionTypeInfo FTI = D->getFunctionTypeInfo();
     PrototypeScope.emplace(this, Scope::FunctionPrototypeScope |
                                      Scope::FunctionDeclarationScope |
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2603,6 +2603,37 @@
   EnableIfAttr *CheckEnableIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
                               bool MissingImplicitThis = false);
 
+  /// Check the diagnose_if attributes on the given function. Returns the
+  /// first succesful fatal attribute, or null if calling Function(Args) isn't
+  /// an error.
+  ///
+  /// This only considers ArgDependent DiagnoseIfAttrs.
+  ///
+  /// This will populate Nonfatal with all non-error DiagnoseIfAttrs that
+  /// succeed. If this function returns non-null, the contents of Nonfatal are
+  /// unspecified.
+  DiagnoseIfAttr *
+  checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
+                              SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
+                              bool MissingImplicitThis = false);
+
+  /// Check the diagnose_if expressions on the given function. Returns the
+  /// first succesful fatal attribute, or null if using Function isn't
+  /// an error.
+  ///
+  /// This ignores all ArgDependent DiagnoseIfAttrs.
+  ///
+  /// This will populate Nonfatal with all non-error DiagnoseIfAttrs that
+  /// succeed. If this function returns non-null, the contents of Nonfatal are
+  /// unspecified.
+  DiagnoseIfAttr *
+  checkArgIndependentDiagnoseIf(FunctionDecl *Function,
+                                SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal);
+
+  /// Emits the diagnostic contained in the given DiagnoseIfAttr at Loc. Also
+  /// emits a note about the location of said attribute.
+  void emitDiagnoseIfDiagnostic(SourceLocation Loc, const DiagnoseIfAttr *DIA);
+
   /// Returns whether the given function's address can be taken or not,
   /// optionally emitting a diagnostic if the address can't be taken.
   ///
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -656,6 +656,26 @@
     /// to be used while performing partial ordering of function templates.
     unsigned ExplicitCallArguments;
 
+    /// The number of diagnose_if attributes that this overload triggered.
+    /// If any of the triggered attributes are errors, this won't count
+    /// diagnose_if warnings.
+    unsigned NumTriggeredDiagnoseIfs = 0;
+
+    /// Basically an TinyPtrVector<DiagnoseIfAttr *> that doesn't own the
+    /// vector: If NumTriggeredDiagnoseIfs is 0 or 1, this is a DiagnoseIfAttr
+    /// *, otherwise it's a pointer to an array of `NumTriggeredDiagnoseIfs`
+    /// DiagnoseIfAttr *s.
+    llvm::PointerUnion<DiagnoseIfAttr *, DiagnoseIfAttr **> DiagnoseIfInfo;
+
+    /// Gets an ArrayRef for the data at DiagnoseIfInfo. Note that this may give
+    /// you a pointer into DiagnoseIfInfo.
+    ArrayRef<DiagnoseIfAttr *> getDiagnoseIfInfo() const {
+      auto *Ptr = NumTriggeredDiagnoseIfs <= 1
+                      ? DiagnoseIfInfo.getAddrOfPtr1()
+                      : DiagnoseIfInfo.get<DiagnoseIfAttr **>();
+      return {Ptr, NumTriggeredDiagnoseIfs};
+    }
+
     union {
       DeductionFailureInfo DeductionFailure;
       
@@ -720,31 +740,58 @@
     SmallVector<OverloadCandidate, 16> Candidates;
     llvm::SmallPtrSet<Decl *, 16> Functions;
 
-    // Allocator for OverloadCandidate::Conversions. We store the first few
-    // elements inline to avoid allocation for small sets.
-    llvm::BumpPtrAllocator ConversionSequenceAllocator;
+    // Allocator for OverloadCandidate::Conversions and DiagnoseIfAttr* arrays.
+    // We store the first few of each of these inline to avoid allocation for
+    // small sets.
+    llvm::BumpPtrAllocator SlabAllocator;
 
     SourceLocation Loc;
     CandidateSetKind Kind;
 
-    unsigned NumInlineSequences;
-    llvm::AlignedCharArray<alignof(ImplicitConversionSequence),
-                           16 * sizeof(ImplicitConversionSequence)>
-        InlineSpace;
+    constexpr static unsigned NumInlineBytes =
+        24 * sizeof(ImplicitConversionSequence);
+    unsigned NumInlineBytesUsed;
+    llvm::AlignedCharArray<alignof(void *), NumInlineBytes> InlineSpace;
+
+    /// If we have space, allocates from inline storage. Otherwise, allocates
+    /// from the slab allocator.
+    /// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
+    /// instead.
+    template <typename T>
+    T *slabAllocate(unsigned N) {
+      // It's simpler if this doesn't need to consider alignment.
+      static_assert(alignof(T) == alignof(void *),
+                    "Only works for pointer-aligned types.");
+
+      unsigned NBytes = sizeof(T) * N;
+      if (NBytes > NumInlineBytes - NumInlineBytesUsed)
+        return SlabAllocator.Allocate<T>(N);
+      char *FreeSpaceStart = InlineSpace.buffer + NumInlineBytesUsed;
+      assert(uintptr_t(FreeSpaceStart) % alignof(void *) == 0 &&
+             "Misaligned storage!");
+
+      NumInlineBytesUsed += NBytes;
+      return reinterpret_cast<T *>(FreeSpaceStart);
+    }
 
     OverloadCandidateSet(const OverloadCandidateSet &) = delete;
     void operator=(const OverloadCandidateSet &) = delete;
 
     void destroyCandidates();
 
   public:
     OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
-        : Loc(Loc), Kind(CSK), NumInlineSequences(0) {}
+        : Loc(Loc), Kind(CSK), NumInlineBytesUsed(0) {}
     ~OverloadCandidateSet() { destroyCandidates(); }
 
     SourceLocation getLocation() const { return Loc; }
     CandidateSetKind getKind() const { return Kind; }
 
+    /// Make a DiagnoseIfAttr* array in a block of memory that will live for
+    /// as long as this OverloadCandidateSet. Returns a pointer to the start
+    /// of that array.
+    DiagnoseIfAttr **addDiagnoseIfComplaints(ArrayRef<DiagnoseIfAttr *> CA);
+
     /// \brief Determine when this overload candidate will be new to the
     /// overload set.
     bool isNewCandidate(Decl *F) {
@@ -767,19 +814,7 @@
       Candidates.push_back(OverloadCandidate());
       OverloadCandidate &C = Candidates.back();
 
-      // Assign space from the inline array if there are enough free slots
-      // available.
-      if (NumConversions + NumInlineSequences <= 16) {
-        ImplicitConversionSequence *I =
-            (ImplicitConversionSequence *)InlineSpace.buffer;
-        C.Conversions = &I[NumInlineSequences];
-        NumInlineSequences += NumConversions;
-      } else {
-        // Otherwise get memory from the allocator.
-        C.Conversions = ConversionSequenceAllocator
-                          .Allocate<ImplicitConversionSequence>(NumConversions);
-      }
-
+      C.Conversions = slabAllocate<ImplicitConversionSequence>(NumConversions);
       // Construct the new objects.
       for (unsigned i = 0; i != NumConversions; ++i)
         new (&C.Conversions[i]) ImplicitConversionSequence();
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2139,8 +2139,10 @@
 def ext_constexpr_function_never_constant_expr : ExtWarn<
   "constexpr %select{function|constructor}0 never produces a "
   "constant expression">, InGroup<DiagGroup<"invalid-constexpr">>, DefaultError;
-def err_enable_if_never_constant_expr : Error<
-  "'enable_if' attribute expression never produces a constant expression">;
+def err_attr_cond_never_constant_expr : Error<
+  "%0 attribute expression never produces a constant expression">;
+def err_diagnose_if_invalid_diagnostic_type : Error<
+  "invalid diagnostic type for diagnose_if. Try 'error' or 'warning'.">;
 def err_constexpr_body_no_return : Error<
   "no return statement in constexpr function">;
 def err_constexpr_return_missing_expr : Error<
@@ -3356,7 +3358,9 @@
 def note_ovl_candidate_has_pass_object_size_params: Note<
     "candidate address cannot be taken because parameter %0 has "
     "pass_object_size attribute">;
-def note_ovl_candidate_disabled_by_enable_if_attr : Note<
+def err_diagnose_if_succeeded: Error<"%0">;
+def warn_diagnose_if_succeeded : Warning<"%0">, InGroup<UserDefinedWarnings>;
+def note_ovl_candidate_disabled_by_function_cond_attr : Note<
     "candidate disabled: %0">;
 def err_addrof_function_disabled_by_enable_if_attr : Error<
     "cannot take address of function %0 becuase it has one or more "
@@ -4373,6 +4377,7 @@
 def err_undeclared_use : Error<"use of undeclared %0">;
 def warn_deprecated : Warning<"%0 is deprecated">,
     InGroup<DeprecatedDeclarations>;
+def note_from_diagnose_if : Note<"from diagnose_if attribute on %0:">;
 def warn_property_method_deprecated :
     Warning<"property access is using %0 method which is deprecated">,
     InGroup<DeprecatedDeclarations>;
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -494,6 +494,7 @@
 def UnusedGetterReturnValue : DiagGroup<"unused-getter-return-value">;
 def UsedButMarkedUnused : DiagGroup<"used-but-marked-unused">;
 def UserDefinedLiterals : DiagGroup<"user-defined-literals">;
+def UserDefinedWarnings : DiagGroup<"user-defined-warnings">;
 def Reorder : DiagGroup<"reorder">;
 def UndeclaredSelector : DiagGroup<"undeclared-selector">;
 def ImplicitAtomic : DiagGroup<"implicit-atomic-properties">;
@@ -682,7 +683,8 @@
     OverloadedVirtual,
     PrivateExtern,
     SelTypeCast,
-    ExternCCompat
+    ExternCCompat,
+    UserDefinedWarnings
  ]>;
 
 // Thread Safety warnings 
Index: include/clang/Basic/DiagnosticCommonKinds.td
===================================================================
--- include/clang/Basic/DiagnosticCommonKinds.td
+++ include/clang/Basic/DiagnosticCommonKinds.td
@@ -161,6 +161,8 @@
   InGroup<CXX11Compat>;
 def ext_clang_enable_if : Extension<"'enable_if' is a clang extension">,
                           InGroup<GccCompat>;
+def ext_clang_diagnose_if : Extension<"'diagnose_if' is a clang extension">,
+                            InGroup<GccCompat>;
 
 // SEH
 def err_seh_expected_handler : Error<
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -340,6 +340,65 @@
   }];
 }
 
+def DiagnoseIfDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``diagnose_if`` attribute can be placed on function declarations to emit
+warnings or errors at compile-time if calls to the attributed function meet
+certain user-defined criteria. For example:
+
+.. code-block:: c
+  void abs(int a)
+    __attribute__((diagnose_if(a >= 0, "Redundant abs call", "warning")));
+  void must_abs(int a)
+    __attribute__((diagnose_if(a >= 0, "Redundant abs call", "error")));
+
+  int val = abs(1); // warning: Redundant abs call
+  int val2 = must_abs(1); // error: Redundant abs call
+  int val3 = abs(val);
+  int val4 = must_abs(val); // because run-time checks are not emitted for
+                            // diagnose_if attributes, this executes without
+                            // issue.
+
+
+``diagnose_if`` is closely related to ``enable_if``, with a few key differences:
+
+* Overload resolution is not aware of ``diagnose_if`` attributes: they're
+  considered only after we select the best candidate from a given candidate set.
+* Function declarations that differ only in their ``diagnose_if`` attributes are
+  considered to be redeclarations of the same function (not overloads).
+* If the condition provided to ``diagnose_if`` cannot be evaluated, no
+  diagnostic will be emitted.
+
+Otherwise, ``diagnose_if`` is essentially the logical negation of ``enable_if``.
+
+As a result of bullet number two, ``diagnose_if`` attributes will stack on the
+same function. For example:
+
+.. code-block:: c
+
+  int foo() __attribute__((diagnose_if(1, "diag1", "warning")));
+  int foo() __attribute__((diagnose_if(1, "diag2", "warning")));
+
+  int bar = foo(); // warning: diag1
+                   // warning: diag2
+  int (*fooptr)(void) = foo; // warning: diag1
+                             // warning: diag2
+
+  constexpr int supportsAPILevel(int N) { return N < 5; }
+  int baz(int a)
+    __attribute__((diagnose_if(!supportsAPILevel(10),
+                               "Upgrade to API level 10 to use baz", "error")));
+  int baz(int a)
+    __attribute__((diagnose_if(!a, "0 is not recommended.", "warning")));
+
+  int (*bazptr)(int) = baz; // error: Upgrade to API level 10 to use baz
+  int v = baz(0); // error: Upgrade to API level 10 to use baz
+
+Query for this feature with ``__has_attribute(diagnose_if)``.
+  }];
+}
+
 def PassObjectSizeDocs : Documentation {
   let Category = DocCatVariable; // Technically it's a parameter doc, but eh.
   let Content = [{
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -140,12 +140,15 @@
   bit Fake = fake;
 }
 
-class BoolArgument<string name, bit opt = 0> : Argument<name, opt>;
+class BoolArgument<string name, bit opt = 0, bit fake = 0> : Argument<name, opt,
+                                                                      fake>;
 class IdentifierArgument<string name, bit opt = 0> : Argument<name, opt>;
 class IntArgument<string name, bit opt = 0> : Argument<name, opt>;
 class StringArgument<string name, bit opt = 0> : Argument<name, opt>;
 class ExprArgument<string name, bit opt = 0> : Argument<name, opt>;
-class FunctionArgument<string name, bit opt = 0> : Argument<name, opt>;
+class FunctionArgument<string name, bit opt = 0, bit fake = 0> : Argument<name,
+                                                                          opt,
+                                                                          fake>;
 class TypeArgument<string name, bit opt = 0> : Argument<name, opt>;
 class UnsignedArgument<string name, bit opt = 0> : Argument<name, opt>;
 class VariadicUnsignedArgument<string name> : Argument<name, 1>;
@@ -1577,6 +1580,25 @@
   let Documentation = [Undocumented];
 }
 
+def DiagnoseIf : InheritableAttr {
+  let Spellings = [GNU<"diagnose_if">];
+  let Subjects = SubjectList<[Function]>;
+  let Args = [ExprArgument<"Cond">, StringArgument<"Message">,
+              EnumArgument<"DiagnosticType",
+                           "DiagnosticType",
+                           ["error", "warning"],
+                           ["DT_Error", "DT_Warning"]>,
+              BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
+              FunctionArgument<"Parent", 0, /*fake*/ 1>];
+  let DuplicatesAllowedWhileMerging = 1;
+  let AdditionalMembers = [{
+    bool isError() const { return diagnosticType == DT_Error; }
+    bool isWarning() const { return diagnosticType == DT_Warning; }
+  }];
+  let TemplateDependent = 1;
+  let Documentation = [DiagnoseIfDocs];
+}
+
 def ArcWeakrefUnavailable : InheritableAttr {
   let Spellings = [GNU<"objc_arc_weak_reference_unavailable">];
   let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to