george.burgess.iv updated this revision to Diff 85486.
george.burgess.iv marked 2 inline comments as done.
george.burgess.iv added a comment.

Addressed all feedback.

Richard noted that, because we're now doing these checks after overload 
resolution has occurred, we no longer need to convert arguments for 
arg-dependent diagnose_if checks. As a result, we get to make lots of things 
`const`. Yay!

Also, I plan to submit this (once it's LGTM'ed) to the 4.0 branch. Is that OK 
with you, Richard?


https://reviews.llvm.org/D28889

Files:
  include/clang/Sema/Overload.h
  include/clang/Sema/Sema.h
  lib/Sema/SemaChecking.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaExprCXX.cpp
  lib/Sema/SemaLookup.cpp
  lib/Sema/SemaOverload.cpp
  test/Sema/diagnose_if.c
  test/SemaCXX/diagnose_if.cpp

Index: test/SemaCXX/diagnose_if.cpp
===================================================================
--- test/SemaCXX/diagnose_if.cpp
+++ test/SemaCXX/diagnose_if.cpp
@@ -2,6 +2,8 @@
 
 #define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
 
+using size_t = unsigned long;
+
 namespace type_dependent {
 template <typename T>
 void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
@@ -51,14 +53,14 @@
 }
 
 template <typename T>
-void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 template <typename T>
-void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+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}}
+  errorIf(1); // expected-error{{oh no}}
 
   warnIf(0);
   warnIf(1); // expected-warning{{oh no}}
@@ -114,14 +116,14 @@
 }
 
 template <int N>
-void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 template <int N>
-void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+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}}
+  errorIf<0>(1); // expected-error{{oh no}}
 
   warnIf<0>(0);
   warnIf<0>(1); // expected-warning{{oh no}}
@@ -135,17 +137,17 @@
 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 fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+void fooArg(short);
 
 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}}
+  fooArg(1); // expected-error{{oh no}}
   barArg(1);
 
   auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
@@ -188,11 +190,11 @@
   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 fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+  void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
 
-  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 barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
 };
 
 void runErrors() {
@@ -203,14 +205,14 @@
   Errors<int>().bar(1); // expected-error{{bad i}}
 
   Errors<int>().fooOvl(0);
-  Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(1); // expected-error{{int bad i}}
   Errors<int>().fooOvl(short(0));
-  Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(short(1)); // expected-error{{short bad i}}
 
   Errors<int>().barOvl(0);
-  Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(1); // expected-error{{int bad i}}
   Errors<int>().barOvl(short(0));
-  Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(short(1)); // expected-error{{short bad i}}
 }
 
 template <typename T>
@@ -275,8 +277,8 @@
 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 bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
+void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
 
 void early() {
   bar();
@@ -290,7 +292,7 @@
 void late() {
   bar(); // expected-error{{bad foo}}
   bar(0);
-  bar(1); // expected-error{{call to unavailable function}}
+  bar(1); // expected-error{{bad foo}}
 }
 }
 
@@ -301,11 +303,11 @@
   constexpr bool isFooable() const { return i; }
 
   void go() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
-  operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{oh no}}
+  operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{from 'diagnose_if'}}
 
-  void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{oh no}}
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{from 'diagnose_if'}}
       __attribute__((enable_if(true, ""))) {}
-  void go2() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") {}
 
   constexpr int go3() const _diagnose_if(isFooable(), "oh no", "error")
       __attribute__((enable_if(true, ""))) {
@@ -326,20 +328,20 @@
   }
 };
 
-void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 void run() {
   Foo(0).go();
   Foo(1).go(); // expected-error{{oh no}}
 
   (void)int(Foo(0));
-  (void)int(Foo(1)); // expected-error{{uses deleted function}}
+  (void)int(Foo(1)); // expected-error{{oh no}}
 
   Foo(0).go2();
-  Foo(1).go2(); // expected-error{{call to unavailable member function}}
+  Foo(1).go2(); // expected-error{{oh no}}
 
   go(Foo(0));
-  go(Foo(1)); // expected-error{{call to unavailable function}}
+  go(Foo(1)); // expected-error{{oh no}}
 }
 }
 
@@ -349,17 +351,17 @@
   constexpr Foo(int i): i(i) {}
   constexpr bool bad() const { return i; }
 
-  template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
   template <typename T>
-  constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
   template <typename T>
-  constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
@@ -369,13 +371,13 @@
 
 void run() {
   Foo(0).getVal<int>();
-  Foo(1).getVal<int>(); // expected-error{{call to unavailable member function}}
+  Foo(1).getVal<int>(); // expected-error{{oh no}}
 
   Foo(0).getVal2<int>();
-  Foo(1).getVal2<int>(); // expected-error{{call to unavailable member function}}
+  Foo(1).getVal2<int>(); // expected-error{{oh no}}
 
   (void)int(Foo(0));
-  (void)int(Foo(1)); // expected-error{{uses deleted function}}
+  (void)int(Foo(1)); // expected-error{{oh no}}
 }
 }
 
@@ -385,76 +387,273 @@
   int i;
   constexpr Foo(int i): i(i) {}
   constexpr bool bad() const { return i; }
-  const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return nullptr;
   }
-  void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{oh no}}
+  void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 };
 
 struct ParenOverload {
   int i;
   constexpr ParenOverload(int i): i(i) {}
   constexpr bool bad() const { return i; }
-  void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
-  void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
+  void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+  void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 };
 
 struct ParenTemplate {
   int i;
   constexpr ParenTemplate(int i): i(i) {}
   constexpr bool bad() const { return i; }
   template <typename T>
-  void operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
+  void operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{from 'diagnose_if'}}
 };
 
 void run() {
   (void)Foo(0)->j;
-  (void)Foo(1)->j; // expected-error{{selected unavailable operator '->'}}
+  (void)Foo(1)->j; // expected-error{{oh no}}
 
   Foo(0)();
-  Foo(1)(); // expected-error{{unavailable function call operator}}
+  Foo(1)(); // expected-error{{oh no}}
 
   ParenOverload(0)(1);
   ParenOverload(0)(1.);
 
-  ParenOverload(1)(1); // expected-error{{unavailable function call operator}}
-  ParenOverload(1)(1.); // expected-error{{unavailable function call operator}}
+  ParenOverload(1)(1); // expected-error{{oh no}}
+  ParenOverload(1)(1.); // expected-error{{oh no}}
 
   ParenTemplate(0)(1);
   ParenTemplate(0)(1.);
 
-  ParenTemplate(1)(1); // expected-error{{unavailable function call operator}}
-  ParenTemplate(1)(1.); // expected-error{{unavailable function call operator}}
+  ParenTemplate(1)(1); // expected-error{{oh no}}
+  ParenTemplate(1)(1.); // expected-error{{oh no}}
 }
 
 void runLambda() {
-  auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{oh no}} expected-note{{conversion candidate}}
+  auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{from 'diagnose_if'}}
   L1(0);
-  L1(1); // expected-error{{call to unavailable function call}}
+  L1(1); // expected-error{{oh no}}
+}
+
+struct Brackets {
+  int i;
+  constexpr Brackets(int i): i(i) {}
+  void operator[](int) _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runBrackets(int i) {
+  Brackets{0}[i];
+  Brackets{1}[i]; // expected-warning{{oh no}}
+  Brackets{2}[i]; // expected-error{{oh no}}
+}
+
+struct Unary {
+  int i;
+  constexpr Unary(int i): i(i) {}
+  void operator+() _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runUnary() {
+  +Unary{0};
+  +Unary{1}; // expected-warning{{oh no}}
+  +Unary{2}; // expected-error{{oh no}}
 }
 }
 
 namespace ctors {
 struct Foo {
   int I;
   constexpr Foo(int I): I(I) {}
 
-  constexpr const Foo &operator=(const Foo &) const // expected-note 2{{disabled: oh no}}
-      _diagnose_if(I, "oh no", "error") {
+  constexpr const Foo &operator=(const Foo &) const
+      _diagnose_if(I, "oh no", "error") {  // expected-note{{from 'diagnose_if'}}
     return *this;
   }
 
-  constexpr const Foo &operator=(const Foo &&) const // expected-note{{disabled: oh no}} expected-note{{no known conversion}}
-      _diagnose_if(I, "oh no", "error") {
+  constexpr const Foo &operator=(const Foo &&) const
+      _diagnose_if(I, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return *this;
   }
 };
 
+struct Bar {
+  int I;
+  constexpr Bar(int I) _diagnose_if(I == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(I == 2, "oh no", "error"): I(I) {} // expected-note{{from 'diagnose_if'}}
+};
+
 void run() {
   constexpr Foo F{0};
   constexpr Foo F2{1};
 
-  F2 = F; // expected-error{{selected unavailable operator}}
-  F2 = Foo{2}; // expected-error{{selected unavailable operator}}
+  F2 = F; // expected-error{{oh no}}
+  F2 = Foo{2}; // expected-error{{oh no}}
+
+  Bar{0};
+  Bar{1}; // expected-warning{{oh no}}
+  Bar{2}; // expected-error{{oh no}}
+}
+}
+
+namespace ref_init {
+struct Bar {};
+struct Baz {};
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  operator const Bar &() const _diagnose_if(i, "oh no", "warning"); // expected-note{{from 'diagnose_if'}}
+  operator const Baz &() const _diagnose_if(i, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+void fooBar(const Bar &b);
+void fooBaz(const Baz &b);
+
+void run() {
+  fooBar(Foo{0});
+  fooBar(Foo{1}); // expected-warning{{oh no}}
+  fooBaz(Foo{0});
+  fooBaz(Foo{1}); // expected-error{{oh no}}
+}
+}
+
+namespace udl {
+void operator""_fn(char c)_diagnose_if(c == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(c == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+
+void run() {
+  '\0'_fn;
+  '\1'_fn; // expected-warning{{oh no}}
+  '\2'_fn; // expected-error{{oh no}}
+}
+}
+
+namespace PR31638 {
+struct String {
+  String(char const* __s) _diagnose_if(__s == nullptr, "oh no ptr", "warning"); // expected-note{{from 'diagnose_if'}}
+  String(int __s) _diagnose_if(__s != 0, "oh no int", "warning"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  String s(nullptr); // expected-warning{{oh no ptr}}
+  String ss(42); // expected-warning{{oh no int}}
+}
+}
+
+namespace PR31639 {
+struct Foo {
+  Foo(int I) __attribute__((diagnose_if(I, "oh no", "error"))); // expected-note{{from 'diagnose_if'}}
+};
+
+void bar() { Foo f(1); } // expected-error{{oh no}}
+}
+
+namespace user_defined_conversion {
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  operator size_t() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+      _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  // `new T[N]`, where N is implicitly convertible to size_t, calls
+  // PerformImplicitConversion directly. This lets us test the diagnostic logic
+  // in PerformImplicitConversion.
+  new int[Foo{0}];
+  new int[Foo{1}]; // expected-warning{{oh no}}
+  new int[Foo{2}]; // expected-error{{oh no}}
+}
+}
+
+namespace std {
+  template <typename T>
+  struct initializer_list {
+    const T *ptr;
+    size_t elems;
+
+    constexpr size_t size() const { return elems; }
+  };
+}
+
+namespace initializer_lists {
+struct Foo {
+  Foo(std::initializer_list<int> l)
+    _diagnose_if(l.size() == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(l.size() == 2, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  Foo{std::initializer_list<int>{}};
+  Foo{std::initializer_list<int>{1}}; // expected-warning{{oh no}}
+  Foo{std::initializer_list<int>{1, 2}}; // expected-error{{oh no}}
+  Foo{std::initializer_list<int>{1, 2, 3}};
+}
+}
+
+namespace range_for_loop {
+  namespace adl {
+    struct Foo {
+      int i;
+      constexpr Foo(int i): i(i) {}
+    };
+    void **begin(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+    void **end(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+
+    struct Bar {
+      int i;
+      constexpr Bar(int i): i(i) {}
+    };
+    void **begin(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+    void **end(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+  }
+
+  void run() {
+    for (void *p : adl::Foo(0)) {}
+    // FIXME: This should emit diagnostics. It seems that our constexpr
+    // evaluator isn't able to evaluate `adl::Foo(1)` to a constexpr, though.
+    // I'm assuming this is because we assign it to a temporary.
+    for (void *p : adl::Foo(1)) {}
+
+    for (void *p : adl::Bar(0)) {}
+    // FIXME: Same thing.
+    for (void *p : adl::Bar(1)) {}
+  }
+}
+
+namespace operator_new {
+struct Foo {
+  int j;
+  static void *operator new(size_t i) _diagnose_if(i, "oh no", "warning");
+};
+
+struct Bar {
+  int j;
+  static void *operator new(size_t i) _diagnose_if(!i, "oh no", "warning");
+};
+
+void run() {
+  // FIXME: This should emit a diagnostic.
+  new Foo();
+  // This is here because we sometimes pass a dummy argument `operator new`. We
+  // should ignore this, rather than complaining about it.
+  new Bar();
+}
+}
+
+namespace contextual_implicit_conv {
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  constexpr operator int() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+      _diagnose_if(i == 2, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
+    return i;
+  }
+};
+
+void run() {
+  switch (constexpr Foo i = 0) { default: break; }
+  switch (constexpr Foo i = 1) { default: break; } // expected-warning{{oh no}}
+  switch (constexpr Foo i = 2) { default: break; } // expected-error{{oh no}}
 }
 }
Index: test/Sema/diagnose_if.c
===================================================================
--- test/Sema/diagnose_if.c
+++ test/Sema/diagnose_if.c
@@ -70,14 +70,14 @@
 
 #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 ovl1(const char *n) _overloadable _diagnose_if(n, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+int ovl1(void *m) _overloadable;
 
 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}}
+  ovl1(""); // expected-error{{oh no}}
 
   ovl2((void *)0); // expected-error{{ambiguous}}
 }
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -839,20 +839,12 @@
 
 void OverloadCandidateSet::clear() {
   destroyCandidates();
-  // 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 {
@@ -5831,28 +5823,6 @@
   return false;
 }
 
-static void initDiagnoseIfComplaint(Sema &S, OverloadCandidateSet &CandidateSet,
-                                    OverloadCandidate &Candidate,
-                                    FunctionDecl *Function,
-                                    ArrayRef<Expr *> Args,
-                                    bool MissingImplicitThis = false,
-                                    Expr *ExplicitThis = nullptr) {
-  SmallVector<DiagnoseIfAttr *, 8> Results;
-  if (DiagnoseIfAttr *DIA = S.checkArgDependentDiagnoseIf(
-          Function, Args, Results, MissingImplicitThis, ExplicitThis)) {
-    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
@@ -5886,10 +5856,9 @@
       // object argument (C++ [over.call.func]p3), and the acting context
       // is irrelevant.
       AddMethodCandidate(Method, FoundDecl, Method->getParent(), QualType(),
-                         Expr::Classification::makeSimpleLValue(),
-                         /*ThisArg=*/nullptr, Args, CandidateSet,
-                         SuppressUserConversions, PartialOverloading,
-                         EarlyConversions);
+                         Expr::Classification::makeSimpleLValue(), Args,
+                         CandidateSet, SuppressUserConversions,
+                         PartialOverloading, EarlyConversions);
       return;
     }
     // We treat a constructor like a non-member function, since its object
@@ -6050,8 +6019,6 @@
     Candidate.FailureKind = ovl_fail_ext_disabled;
     return;
   }
-
-  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Function, Args);
 }
 
 ObjCMethodDecl *
@@ -6260,85 +6227,73 @@
   return nullptr;
 }
 
-static bool gatherDiagnoseIfAttrs(FunctionDecl *Function, bool ArgDependent,
-                                  SmallVectorImpl<DiagnoseIfAttr *> &Errors,
-                                  SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
-  for (auto *DIA : Function->specific_attrs<DiagnoseIfAttr>())
-    if (ArgDependent == DIA->getArgDependent()) {
-      if (DIA->isError())
-        Errors.push_back(DIA);
-      else
-        Nonfatal.push_back(DIA);
-    }
+template <typename CheckFn>
+static bool diagnoseDiagnoseIfAttrsWith(Sema &S, const FunctionDecl *FD,
+                                        bool ArgDependent, SourceLocation Loc,
+                                        CheckFn &&IsSuccessful) {
+  SmallVector<const DiagnoseIfAttr *, 8> Attrs;
+  for (const auto *DIA : FD->specific_attrs<DiagnoseIfAttr>())
+    if (ArgDependent == DIA->getArgDependent())
+      Attrs.push_back(DIA);
+
+  // Common case: No diagnose_if attributes, so we can quit early.
+  if (Attrs.empty())
+    return false;
 
-  return !Errors.empty() || !Nonfatal.empty();
-}
+  auto WarningBegin = std::stable_partition(
+      Attrs.begin(), Attrs.end(),
+      [](const DiagnoseIfAttr *DIA) { return DIA->isError(); });
 
-template <typename CheckFn>
-static DiagnoseIfAttr *
-checkDiagnoseIfAttrsWith(const SmallVectorImpl<DiagnoseIfAttr *> &Errors,
-                         SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
-                         CheckFn &&IsSuccessful) {
   // Note that diagnose_if attributes are late-parsed, so they appear in the
   // correct order (unlike enable_if attributes).
-  auto ErrAttr = llvm::find_if(Errors, IsSuccessful);
-  if (ErrAttr != Errors.end())
-    return *ErrAttr;
-
-  llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); });
-  return nullptr;
-}
-
-DiagnoseIfAttr *
-Sema::checkArgDependentDiagnoseIf(FunctionDecl *Function, ArrayRef<Expr *> Args,
-                                  SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
-                                  bool MissingImplicitThis,
-                                  Expr *ThisArg) {
-  SmallVector<DiagnoseIfAttr *, 4> Errors;
-  if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/true, Errors, Nonfatal))
-    return nullptr;
+  auto ErrAttr = llvm::find_if(llvm::make_range(Attrs.begin(), WarningBegin),
+                               IsSuccessful);
+  if (ErrAttr != WarningBegin) {
+    const DiagnoseIfAttr *DIA = *ErrAttr;
+    S.Diag(Loc, diag::err_diagnose_if_succeeded) << DIA->getMessage();
+    S.Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+        << DIA->getParent() << DIA->getCond()->getSourceRange();
+    return true;
+  }
 
-  SFINAETrap Trap(*this);
-  SmallVector<Expr *, 16> ConvertedArgs;
-  Expr *ConvertedThis;
-  if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap,
-                                        MissingImplicitThis, ConvertedThis,
-                                        ConvertedArgs))
-    return nullptr;
+  for (const auto *DIA : llvm::make_range(WarningBegin, Attrs.end()))
+    if (IsSuccessful(DIA)) {
+      S.Diag(Loc, diag::warn_diagnose_if_succeeded) << DIA->getMessage();
+      S.Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+          << DIA->getParent() << DIA->getCond()->getSourceRange();
+    }
 
-  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, ConvertedThis))
-      return false;
-    return Result.isInt() && Result.getInt().getBoolValue();
-  });
+  return false;
 }
 
-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;
-  });
+bool Sema::diagnoseArgDependentDiagnoseIfAttrs(const FunctionDecl *Function,
+                                               const Expr *ThisArg,
+                                               ArrayRef<const Expr *> Args,
+                                               SourceLocation Loc) {
+  return diagnoseDiagnoseIfAttrsWith(
+      *this, Function, /*ArgDependent=*/true, Loc,
+      [&](const 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(), Args, ThisArg))
+          return false;
+        return Result.isInt() && Result.getInt().getBoolValue();
+      });
 }
 
-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();
+bool Sema::diagnoseArgIndependentDiagnoseIfAttrs(const FunctionDecl *Function,
+                                                 SourceLocation Loc) {
+  return diagnoseDiagnoseIfAttrsWith(
+      *this, Function, /*ArgDependent=*/false, Loc,
+      [&](const DiagnoseIfAttr *DIA) {
+        bool Result;
+        return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
+               Result;
+      });
 }
 
 /// \brief Add all of the function declarations in the given function set to
@@ -6356,8 +6311,8 @@
         AddMethodCandidate(cast<CXXMethodDecl>(FD), F.getPair(),
                            cast<CXXMethodDecl>(FD)->getParent(),
                            Args[0]->getType(), Args[0]->Classify(Context),
-                           Args[0], Args.slice(1), CandidateSet,
-                           SuppressUserConversions, PartialOverloading);
+                           Args.slice(1), CandidateSet, SuppressUserConversions,
+                           PartialOverloading);
       else
         AddOverloadCandidate(FD, F.getPair(), Args, CandidateSet,
                              SuppressUserConversions, PartialOverloading);
@@ -6369,7 +6324,7 @@
             FunTmpl, F.getPair(),
             cast<CXXRecordDecl>(FunTmpl->getDeclContext()),
             ExplicitTemplateArgs, Args[0]->getType(),
-            Args[0]->Classify(Context), Args[0], Args.slice(1), CandidateSet,
+            Args[0]->Classify(Context), Args.slice(1), CandidateSet,
             SuppressUserConversions, PartialOverloading);
       else
         AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
@@ -6385,7 +6340,6 @@
 void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
                               QualType ObjectType,
                               Expr::Classification ObjectClassification,
-                              Expr *ThisArg,
                               ArrayRef<Expr *> Args,
                               OverloadCandidateSet& CandidateSet,
                               bool SuppressUserConversions) {
@@ -6399,15 +6353,13 @@
     assert(isa<CXXMethodDecl>(TD->getTemplatedDecl()) &&
            "Expected a member function template");
     AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,
-                               /*ExplicitArgs*/ nullptr,
-                               ObjectType, ObjectClassification,
-                               ThisArg, Args, CandidateSet,
+                               /*ExplicitArgs*/ nullptr, ObjectType,
+                               ObjectClassification, Args, CandidateSet,
                                SuppressUserConversions);
   } else {
     AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl, ActingContext,
-                       ObjectType, ObjectClassification,
-                       ThisArg, Args,
-                       CandidateSet, SuppressUserConversions);
+                       ObjectType, ObjectClassification, Args, CandidateSet,
+                       SuppressUserConversions);
   }
 }
 
@@ -6422,7 +6374,7 @@
 Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl,
                          CXXRecordDecl *ActingContext, QualType ObjectType,
                          Expr::Classification ObjectClassification,
-                         Expr *ThisArg, ArrayRef<Expr *> Args,
+                         ArrayRef<Expr *> Args,
                          OverloadCandidateSet &CandidateSet,
                          bool SuppressUserConversions,
                          bool PartialOverloading,
@@ -6544,9 +6496,6 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
-
-  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Method, Args,
-                          /*MissingImplicitThis=*/!ThisArg, ThisArg);
 }
 
 /// \brief Add a C++ member function template as a candidate to the candidate
@@ -6559,7 +6508,6 @@
                                  TemplateArgumentListInfo *ExplicitTemplateArgs,
                                  QualType ObjectType,
                                  Expr::Classification ObjectClassification,
-                                 Expr *ThisArg,
                                  ArrayRef<Expr *> Args,
                                  OverloadCandidateSet& CandidateSet,
                                  bool SuppressUserConversions,
@@ -6613,9 +6561,9 @@
   assert(isa<CXXMethodDecl>(Specialization) &&
          "Specialization is not a member function?");
   AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
-                     ActingContext, ObjectType, ObjectClassification,
-                     /*ThisArg=*/ThisArg, Args, CandidateSet,
-                     SuppressUserConversions, PartialOverloading, Conversions);
+                     ActingContext, ObjectType, ObjectClassification, Args,
+                     CandidateSet, SuppressUserConversions, PartialOverloading,
+                     Conversions);
 }
 
 /// \brief Add a C++ function template specialization as a candidate
@@ -6942,8 +6890,6 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
-
-  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
 }
 
 /// \brief Adds a conversion function template specialization
@@ -7096,8 +7042,6 @@
     Candidate.DeductionFailure.Data = FailedAttr;
     return;
   }
-
-  initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
 }
 
 /// \brief Add overload candidates for overloaded operators that are
@@ -7146,7 +7090,7 @@
          Oper != OperEnd;
          ++Oper)
       AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
-                         Args[0]->Classify(Context), Args[0], Args.slice(1),
+                         Args[0]->Classify(Context), Args.slice(1),
                          CandidateSet, /*SuppressUserConversions=*/false);
   }
 }
@@ -9178,17 +9122,6 @@
   }
 }
 
-static bool isCandidateUnavailableDueToDiagnoseIf(const OverloadCandidate &OC) {
-  ArrayRef<DiagnoseIfAttr *> Info = OC.getDiagnoseIfInfo();
-  if (!Info.empty() && Info[0]->isError())
-    return true;
-
-  assert(llvm::all_of(Info,
-                      [](const DiagnoseIfAttr *A) { return !A->isError(); }) &&
-         "DiagnoseIf info shouldn't have mixed warnings and errors.");
-  return false;
-}
-
 /// \brief Computes the best viable function (C++ 13.3.3)
 /// within an overload candidate set.
 ///
@@ -9267,19 +9200,13 @@
   // Best is the best viable function.
   if (Best->Function &&
       (Best->Function->isDeleted() ||
-       S.isFunctionConsideredUnavailable(Best->Function) ||
-       isCandidateUnavailableDueToDiagnoseIf(*Best)))
+       S.isFunctionConsideredUnavailable(Best->Function)))
     return OR_Deleted;
 
   if (!EquivalentCands.empty())
     S.diagnoseEquivalentInternalLinkageDeclarations(Loc, Best->Function,
                                                     EquivalentCands);
 
-  for (const auto *W : Best->getDiagnoseIfInfo()) {
-    assert(W->isWarning() && "Errors should've been caught earlier!");
-    S.emitDiagnoseIfDiagnostic(Loc, W);
-  }
-
   return OR_Success;
 }
 
@@ -10162,14 +10089,6 @@
       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.
     S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
@@ -12113,6 +12032,7 @@
       if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl))
         return ExprError();
 
+      checkDiagnoseIfAttrsOnCall(FnDecl, TheCall);
       return MaybeBindToTemporary(TheCall);
     } else {
       // We matched a built-in operator. Convert the arguments, then
@@ -12342,17 +12262,21 @@
                                 FnDecl))
           return ExprError();
 
-        ArrayRef<const Expr *> ArgsArray(Args, 2);
+        ArrayRef<Expr *> ArgsArray(Args, 2);
+        Expr *ImplicitThis = nullptr;
         // Cut off the implicit 'this'.
-        if (isa<CXXMethodDecl>(FnDecl))
+        if (isa<CXXMethodDecl>(FnDecl)) {
+          ImplicitThis = ArgsArray[0];
           ArgsArray = ArgsArray.slice(1);
+        }
 
         // Check for a self move.
         if (Op == OO_Equal)
           DiagnoseSelfMove(Args[0], Args[1], OpLoc);
 
-        checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc, 
-                  TheCall->getSourceRange(), VariadicDoesNotApply);
+        checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc,
+                  TheCall->getSourceRange(), VariadicDoesNotApply,
+                  ImplicitThis);
 
         return MaybeBindToTemporary(TheCall);
       } else {
@@ -12561,6 +12485,7 @@
         if (CheckCallReturnType(FnDecl->getReturnType(), LLoc, TheCall, FnDecl))
           return ExprError();
 
+        checkDiagnoseIfAttrsOnCall(Method, TheCall);
         return MaybeBindToTemporary(TheCall);
       } else {
         // We matched a built-in operator. Convert the arguments, then
@@ -12727,16 +12652,6 @@
       TemplateArgs = &TemplateArgsBuffer;
     }
 
-    // Poor-programmer's Lazy<Expr *>; isImplicitAccess requires stripping
-    // parens/casts, which would be nice to avoid potentially doing multiple
-    // times.
-    llvm::Optional<Expr *> UnresolvedBase;
-    auto GetUnresolvedBase = [&] {
-      if (!UnresolvedBase.hasValue())
-        UnresolvedBase =
-          UnresExpr->isImplicitAccess() ? nullptr : UnresExpr->getBase();
-      return *UnresolvedBase;
-    };
     for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(),
            E = UnresExpr->decls_end(); I != E; ++I) {
 
@@ -12757,14 +12672,12 @@
           continue;
 
         AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType,
-                           ObjectClassification,
-                           /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
+                           ObjectClassification, Args, CandidateSet,
                            /*SuppressUserConversions=*/false);
       } else {
         AddMethodTemplateCandidate(
             cast<FunctionTemplateDecl>(Func), I.getPair(), ActingDC,
-            TemplateArgs, ObjectType, ObjectClassification,
-            /*ThisArg=*/GetUnresolvedBase(), Args, CandidateSet,
+            TemplateArgs, ObjectType, ObjectClassification, Args, CandidateSet,
             /*SuppressUsedConversions=*/false);
       }
     }
@@ -12882,16 +12795,6 @@
           << Attr->getCond()->getSourceRange() << Attr->getMessage();
       return ExprError();
     }
-
-    SmallVector<DiagnoseIfAttr *, 4> Nonfatal;
-    if (const DiagnoseIfAttr *Attr = checkArgDependentDiagnoseIf(
-            Method, Args, Nonfatal, false, MemE->getBase())) {
-      emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
-      return ExprError();
-    }
-
-    for (const auto *Attr : Nonfatal)
-      emitDiagnoseIfDiagnostic(MemE->getMemberLoc(), Attr);
   }
 
   if ((isa<CXXConstructorDecl>(CurContext) || 
@@ -12970,9 +12873,8 @@
   for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
        Oper != OperEnd; ++Oper) {
     AddMethodCandidate(Oper.getPair(), Object.get()->getType(),
-                       Object.get()->Classify(Context),
-                       Object.get(), Args, CandidateSet,
-                       /*SuppressUserConversions=*/ false);
+                       Object.get()->Classify(Context), Args, CandidateSet,
+                       /*SuppressUserConversions=*/false);
   }
 
   // C++ [over.call.object]p2:
@@ -13247,8 +13149,7 @@
   for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end();
        Oper != OperEnd; ++Oper) {
     AddMethodCandidate(Oper.getPair(), Base->getType(), Base->Classify(Context),
-                       Base, None, CandidateSet,
-                       /*SuppressUserConversions=*/false);
+                       None, CandidateSet, /*SuppressUserConversions=*/false);
   }
 
   bool HadMultipleCandidates = (CandidateSet.size() > 1);
@@ -13322,8 +13223,9 @@
                                       Base, ResultTy, VK, OpLoc, false);
 
   if (CheckCallReturnType(Method->getReturnType(), OpLoc, TheCall, Method))
-          return ExprError();
+    return ExprError();
 
+  checkDiagnoseIfAttrsOnCall(Method, TheCall);
   return MaybeBindToTemporary(TheCall);
 }
 
Index: lib/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp
+++ lib/Sema/SemaLookup.cpp
@@ -2960,7 +2960,6 @@
     if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand->getUnderlyingDecl())) {
       if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
         AddMethodCandidate(M, Cand, RD, ThisTy, Classification,
-                           /*ThisArg=*/nullptr,
                            llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
       else if (CtorInfo)
         AddOverloadCandidate(CtorInfo.Constructor, CtorInfo.FoundDecl,
@@ -2973,7 +2972,7 @@
       if (SM == CXXCopyAssignment || SM == CXXMoveAssignment)
         AddMethodTemplateCandidate(
             Tmpl, Cand, RD, nullptr, ThisTy, Classification,
-            /*ThisArg=*/nullptr, llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
+            llvm::makeArrayRef(&Arg, NumArgs), OCS, true);
       else if (CtorInfo)
         AddTemplateOverloadCandidate(
             CtorInfo.ConstructorTmpl, CtorInfo.FoundDecl, nullptr,
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -6713,6 +6713,8 @@
   CXXMemberCallExpr *CE =
     new (Context) CXXMemberCallExpr(Context, ME, None, ResultType, VK,
                                     Exp.get()->getLocEnd());
+
+  checkDiagnoseIfAttrsOnCall(Method, CE);
   return CE;
 }
 
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -342,7 +342,6 @@
   }
 
   // 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);
@@ -365,11 +364,8 @@
     if (getLangOpts().CUDA && !CheckCUDACall(Loc, FD))
       return true;
 
-    if (const DiagnoseIfAttr *A =
-            checkArgIndependentDiagnoseIf(FD, DiagnoseIfWarnings)) {
-      emitDiagnoseIfDiagnostic(Loc, A);
+    if (diagnoseArgIndependentDiagnoseIfAttrs(FD, Loc))
       return true;
-    }
   }
 
   // [OpenMP 4.0], 2.15 declare reduction Directive, Restrictions
@@ -385,9 +381,6 @@
     return true;
   }
 
-  for (const auto *W : DiagnoseIfWarnings)
-    emitDiagnoseIfDiagnostic(Loc, W);
-
   DiagnoseAvailabilityOfDecl(*this, D, Loc, UnknownObjCClass,
                              ObjCPropertyAccess);
 
@@ -5189,16 +5182,6 @@
         << 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 (const auto *W : Nonfatal)
-    S.emitDiagnoseIfDiagnostic(Fn->getLocStart(), W);
 }
 
 /// ActOnCallExpr - Handle a call to Fn with the specified array of arguments.
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -2285,10 +2285,8 @@
 /// \brief Diagnose use of %s directive in an NSString which is being passed
 /// as formatting string to formatting method.
 static void
-DiagnoseCStringFormatDirectiveInCFAPI(Sema &S,
-                                        const NamedDecl *FDecl,
-                                        Expr **Args,
-                                        unsigned NumArgs) {
+DiagnoseCStringFormatDirectiveInCFAPI(Sema &S, const NamedDecl *FDecl,
+                                      ArrayRef<Expr *> Args) {
   unsigned Idx = 0;
   bool Format = false;
   ObjCStringFormatFamily SFFamily = FDecl->getObjCFStringFormattingFamily();
@@ -2303,7 +2301,7 @@
         break;
       }
     }
-  if (!Format || NumArgs <= Idx)
+  if (!Format || Args.size() <= Idx)
     return;
   const Expr *FormatExpr = Args[Idx];
   if (const CStyleCastExpr *CSCE = dyn_cast<CStyleCastExpr>(FormatExpr))
@@ -2426,11 +2424,12 @@
 }
 
 /// Handles the checks for format strings, non-POD arguments to vararg
-/// functions, and NULL arguments passed to non-NULL parameters.
+/// functions, NULL arguments passed to non-NULL parameters, and diagnose_if
+/// attributes.
 void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
                      ArrayRef<const Expr *> Args, bool IsMemberFunction,
                      SourceLocation Loc, SourceRange Range,
-                     VariadicCallType CallType) {
+                     VariadicCallType CallType, const Expr *ThisArg) {
   // FIXME: We should check as much as we can in the template definition.
   if (CurContext->isDependentContext())
     return;
@@ -2477,6 +2476,9 @@
         CheckArgumentWithTypeTag(I, Args.data());
     }
   }
+
+  if (FD)
+    diagnoseArgDependentDiagnoseIfAttrs(FD, ThisArg, Args, Loc);
 }
 
 /// CheckConstructorCall - Check a constructor call for correctness and safety
@@ -2487,32 +2489,59 @@
                                 SourceLocation Loc) {
   VariadicCallType CallType =
     Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
-  checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(), 
-            CallType);
+  checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(),
+            CallType, /*ThisArg=*/nullptr);
 }
 
-/// CheckFunctionCall - Check a direct function call for various correctness
-/// and safety properties not strictly enforced by the C type system.
-bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
-                             const FunctionProtoType *Proto) {
+static std::tuple<Expr *, ArrayRef<Expr *>, bool>
+unwrapCallExpr(const FunctionDecl *FDecl, CallExpr *TheCall) {
   bool IsMemberOperatorCall = isa<CXXOperatorCallExpr>(TheCall) &&
                               isa<CXXMethodDecl>(FDecl);
   bool IsMemberFunction = isa<CXXMemberCallExpr>(TheCall) ||
                           IsMemberOperatorCall;
-  VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
-                                                  TheCall->getCallee());
-  Expr** Args = TheCall->getArgs();
+
+  Expr **Args = TheCall->getArgs();
+  Expr *ImplicitThis = nullptr;
   unsigned NumArgs = TheCall->getNumArgs();
   if (IsMemberOperatorCall) {
     // If this is a call to a member operator, hide the first argument
     // from checkCall.
     // FIXME: Our choice of AST representation here is less than ideal.
+    ImplicitThis = Args[0];
     ++Args;
     --NumArgs;
-  }
-  checkCall(FDecl, Proto, llvm::makeArrayRef(Args, NumArgs), 
-            IsMemberFunction, TheCall->getRParenLoc(),
-            TheCall->getCallee()->getSourceRange(), CallType);
+  } else if (IsMemberFunction)
+    ImplicitThis =
+        cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
+  return std::make_tuple(ImplicitThis, llvm::makeArrayRef(Args, NumArgs),
+                         IsMemberFunction);
+}
+
+// TODO: Call can technically be a const CallExpr, but const_casting feels ugly,
+// and I really don't want to duplicate unwrapCallExpr's logic. No caller really
+// cares about its constness at the moment, though.
+bool Sema::checkDiagnoseIfAttrsOnCall(const FunctionDecl *Function,
+                                      CallExpr *Call) {
+  const Expr *ImplicitThis;
+  ArrayRef<const Expr *> Args;
+  std::tie(ImplicitThis, Args, std::ignore) = unwrapCallExpr(Function, Call);
+  return diagnoseArgDependentDiagnoseIfAttrs(Function, ImplicitThis,
+                                             Args, Call->getExprLoc());
+}
+
+/// CheckFunctionCall - Check a direct function call for various correctness
+/// and safety properties not strictly enforced by the C type system.
+bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
+                             const FunctionProtoType *Proto) {
+  Expr *ThisArg;
+  ArrayRef<Expr *> Args;
+  bool IsMemberFunction;
+  std::tie(ThisArg, Args, IsMemberFunction) = unwrapCallExpr(FDecl, TheCall);
+
+  VariadicCallType CallType = getVariadicCallType(FDecl, Proto,
+                                                  TheCall->getCallee());
+  checkCall(FDecl, Proto, Args, IsMemberFunction, TheCall->getRParenLoc(),
+            TheCall->getCallee()->getSourceRange(), CallType, ThisArg);
 
   IdentifierInfo *FnInfo = FDecl->getIdentifier();
   // None of the checks below are needed for functions that don't have
@@ -2524,7 +2553,7 @@
   CheckMaxUnsignedZero(TheCall, FDecl);
 
   if (getLangOpts().ObjC1)
-    DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
+    DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args);
 
   unsigned CMId = FDecl->getMemoryFunctionKind();
   if (CMId == 0)
@@ -2547,8 +2576,8 @@
       Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply;
 
   checkCall(Method, nullptr, Args,
-            /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(), 
-            CallType);
+            /*IsMemberFunction=*/false, lbrac, Method->getSourceRange(),
+            CallType, /*ThisArg=*/nullptr);
 
   return false;
 }
@@ -2579,7 +2608,8 @@
   checkCall(NDecl, Proto,
             llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
             /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
-            TheCall->getCallee()->getSourceRange(), CallType);
+            TheCall->getCallee()->getSourceRange(), CallType,
+            /*ThisArg=*/nullptr);
 
   return false;
 }
@@ -2592,7 +2622,8 @@
   checkCall(/*FDecl=*/nullptr, Proto,
             llvm::makeArrayRef(TheCall->getArgs(), TheCall->getNumArgs()),
             /*IsMemberFunction=*/false, TheCall->getRParenLoc(),
-            TheCall->getCallee()->getSourceRange(), CallType);
+            TheCall->getCallee()->getSourceRange(), CallType,
+            /*ThisArg=*/nullptr);
 
   return false;
 }
@@ -11930,4 +11961,3 @@
       rhs, std::bind(&Sema::AddPotentialMisalignedMembers, std::ref(*this), _1,
                      _2, _3, _4));
 }
-
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2544,14 +2544,14 @@
   void AddMethodCandidate(DeclAccessPair FoundDecl,
                           QualType ObjectType,
                           Expr::Classification ObjectClassification,
-                          Expr *ThisArg, ArrayRef<Expr *> Args,
+                          ArrayRef<Expr *> Args,
                           OverloadCandidateSet& CandidateSet,
                           bool SuppressUserConversion = false);
   void AddMethodCandidate(CXXMethodDecl *Method,
                           DeclAccessPair FoundDecl,
                           CXXRecordDecl *ActingContext, QualType ObjectType,
                           Expr::Classification ObjectClassification,
-                          Expr *ThisArg, ArrayRef<Expr *> Args,
+                          ArrayRef<Expr *> Args,
                           OverloadCandidateSet& CandidateSet,
                           bool SuppressUserConversions = false,
                           bool PartialOverloading = false,
@@ -2562,7 +2562,6 @@
                                  TemplateArgumentListInfo *ExplicitTemplateArgs,
                                   QualType ObjectType,
                                   Expr::Classification ObjectClassification,
-                                  Expr *ThisArg,
                                   ArrayRef<Expr *> Args,
                                   OverloadCandidateSet& CandidateSet,
                                   bool SuppressUserConversions = false,
@@ -2636,37 +2635,26 @@
   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.
+  /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
+  /// non-ArgDependent DiagnoseIfAttrs.
   ///
-  /// 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,
-                              Expr *ThisArg = nullptr);
+  /// Returns true if any errors were emitted.
+  bool diagnoseArgDependentDiagnoseIfAttrs(const FunctionDecl *Function,
+                                           const Expr *ThisArg,
+                                           ArrayRef<const Expr *> Args,
+                                           SourceLocation Loc);
 
-  /// 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);
+  /// Calls diagnoseArgDependentDiagnoseIfAttrs with information from the given
+  /// CallExpr.
+  bool checkDiagnoseIfAttrsOnCall(const FunctionDecl *Function,
+                                  CallExpr *Call);
 
-  /// 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);
+  /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
+  /// ArgDependent DiagnoseIfAttrs.
+  ///
+  /// Returns true if any errors were emitted.
+  bool diagnoseArgIndependentDiagnoseIfAttrs(const FunctionDecl *Function,
+                                             SourceLocation Loc);
 
   /// Returns whether the given function's address can be taken or not,
   /// optionally emitting a diagnostic if the address can't be taken.
@@ -9934,7 +9922,7 @@
   void checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
                  ArrayRef<const Expr *> Args, bool IsMemberFunction,
                  SourceLocation Loc, SourceRange Range,
-                 VariadicCallType CallType);
+                 VariadicCallType CallType, const Expr *ThisArg);
 
   bool CheckObjCString(Expr *Arg);
   ExprResult CheckOSLogFormatStringArg(Expr *Arg);
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -675,26 +675,6 @@
     /// 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 a 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;
       
@@ -759,9 +739,8 @@
     SmallVector<OverloadCandidate, 16> Candidates;
     llvm::SmallPtrSet<Decl *, 16> Functions;
 
-    // Allocator for ConversionSequenceLists and DiagnoseIfAttr* arrays.
-    // We store the first few of each of these inline to avoid allocation for
-    // small sets.
+    // Allocator for ConversionSequenceLists. We store the first few of these
+    // inline to avoid allocation for small sets.
     llvm::BumpPtrAllocator SlabAllocator;
 
     SourceLocation Loc;
@@ -776,6 +755,8 @@
     /// from the slab allocator.
     /// FIXME: It would probably be nice to have a SmallBumpPtrAllocator
     /// instead.
+    /// FIXME: Now that this only allocates ImplicitConversionSequences, do we
+    /// want to un-generalize this?
     template <typename T>
     T *slabAllocate(unsigned N) {
       // It's simpler if this doesn't need to consider alignment.
@@ -809,11 +790,6 @@
     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) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to