george.burgess.iv created this revision.
As it turns out, emitting diagnostics from places where you're not meant to
emit them from is a very bad idea. :)
After some looking around, it seems that it's less insane to check for
`diagnose_if` attributes in code that's already checking for e.g. nullness
warnings. As a result, we get to rip out the `diagnose_if`-induced changes to
overloading. Woohoo!
This also includes a slight change to how `diagnose_if` works: for "error"
calls, we'll always assume that overload resolution chose the overload the user
wanted. So, regardless of whetehr a `diagnose_if` attribute emits a diag or
not, we'll continue building the AST/etc as usual.
This patch aims to fix PR31638, PR31639, and PR31640.
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 *
@@ -6261,84 +6228,107 @@
}
static bool gatherDiagnoseIfAttrs(FunctionDecl *Function, bool ArgDependent,
- SmallVectorImpl<DiagnoseIfAttr *> &Errors,
- SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal) {
+ SmallVectorImpl<DiagnoseIfAttr *> &Diags) {
for (auto *DIA : Function->specific_attrs<DiagnoseIfAttr>())
- if (ArgDependent == DIA->getArgDependent()) {
- if (DIA->isError())
- Errors.push_back(DIA);
- else
- Nonfatal.push_back(DIA);
- }
-
- return !Errors.empty() || !Nonfatal.empty();
+ if (ArgDependent == DIA->getArgDependent())
+ Diags.push_back(DIA);
+ return !Diags.empty();
}
template <typename CheckFn>
static DiagnoseIfAttr *
-checkDiagnoseIfAttrsWith(const SmallVectorImpl<DiagnoseIfAttr *> &Errors,
- SmallVectorImpl<DiagnoseIfAttr *> &Nonfatal,
+checkDiagnoseIfAttrsWith(SmallVectorImpl<DiagnoseIfAttr *> &Diags,
CheckFn &&IsSuccessful) {
+ auto ErrorBegin = std::stable_partition(
+ Diags.begin(), Diags.end(),
+ [](const DiagnoseIfAttr *DIA) { return DIA->isWarning(); });
+
// 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())
+ auto ErrAttr =
+ llvm::find_if(llvm::make_range(ErrorBegin, Diags.end()), IsSuccessful);
+ if (ErrAttr != Diags.end())
return *ErrAttr;
- llvm::erase_if(Nonfatal, [&](DiagnoseIfAttr *A) { return !IsSuccessful(A); });
+ Diags.erase(ErrorBegin, Diags.end());
+ llvm::erase_if(Diags, [&](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;
+static void emitDiagnoseIfDiagnostic(Sema &S, SourceLocation Loc,
+ const DiagnoseIfAttr *DIA) {
+ auto Code = DIA->isError() ? diag::err_diagnose_if_succeeded
+ : diag::warn_diagnose_if_succeeded;
+ S.Diag(Loc, Code) << DIA->getMessage();
+ S.Diag(DIA->getLocation(), diag::note_from_diagnose_if)
+ << DIA->getParent() << DIA->getCond()->getSourceRange();
+}
- SFINAETrap Trap(*this);
- SmallVector<Expr *, 16> ConvertedArgs;
- Expr *ConvertedThis;
- if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap,
- MissingImplicitThis, ConvertedThis,
- ConvertedArgs))
- return nullptr;
+bool Sema::diagnoseArgDependentDiagnoseIfAttrs(FunctionDecl *Function,
+ Expr *ThisArg,
+ ArrayRef<Expr *> Args,
+ SourceLocation Loc) {
+ SmallVector<DiagnoseIfAttr *, 8> Attrs;
+ if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/true, Attrs))
+ return false;
- 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))
+ DiagnoseIfAttr *FirstError;
+ {
+ SFINAETrap Trap(*this);
+ SmallVector<Expr *, 16> ConvertedArgs;
+ Expr *ConvertedThis;
+ if (!convertArgsForAvailabilityChecks(*this, Function, ThisArg, Args, Trap,
+ /*MissingImplicitThis=*/false,
+ ConvertedThis, 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;
+ FirstError =
+ checkDiagnoseIfAttrsWith(Attrs, [&](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(), ConvertedArgs,
+ ConvertedThis))
+ return false;
+ return Result.isInt() && Result.getInt().getBoolValue();
+ });
+ }
- return checkDiagnoseIfAttrsWith(Errors, Nonfatal, [&](DiagnoseIfAttr *DIA) {
- bool Result;
- return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
- Result;
- });
+ if (FirstError) {
+ emitDiagnoseIfDiagnostic(*this, Loc, FirstError);
+ return true;
+ }
+ for (const DiagnoseIfAttr *DIA : Attrs)
+ emitDiagnoseIfDiagnostic(*this, Loc, DIA);
+
+ return false;
}
-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(FunctionDecl *Function,
+ SourceLocation Loc) {
+ SmallVector<DiagnoseIfAttr *, 4> Attrs;
+ if (!gatherDiagnoseIfAttrs(Function, /*ArgDependent=*/false, Attrs))
+ return false;
+
+ const DiagnoseIfAttr *FirstError =
+ checkDiagnoseIfAttrsWith(Attrs, [&](const DiagnoseIfAttr *DIA) {
+ bool Result;
+ return DIA->getCond()->EvaluateAsBooleanCondition(Result, Context) &&
+ Result;
+ });
+
+ if (FirstError) {
+ emitDiagnoseIfDiagnostic(*this, Loc, FirstError);
+ return true;
+ }
+
+ for (const DiagnoseIfAttr *DIA : Attrs)
+ emitDiagnoseIfDiagnostic(*this, Loc, DIA);
+
+ return false;
}
/// \brief Add all of the function declarations in the given function set to
@@ -6356,8 +6346,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 +6359,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 +6375,6 @@
void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions) {
@@ -6399,15 +6388,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 +6409,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 +6531,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 +6543,6 @@
TemplateArgumentListInfo *ExplicitTemplateArgs,
QualType ObjectType,
Expr::Classification ObjectClassification,
- Expr *ThisArg,
ArrayRef<Expr *> Args,
OverloadCandidateSet& CandidateSet,
bool SuppressUserConversions,
@@ -6613,9 +6596,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 +6925,6 @@
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None, false, From);
}
/// \brief Adds a conversion function template specialization
@@ -7096,8 +7077,6 @@
Candidate.DeductionFailure.Data = FailedAttr;
return;
}
-
- initDiagnoseIfComplaint(*this, CandidateSet, Candidate, Conversion, None);
}
/// \brief Add overload candidates for overloaded operators that are
@@ -7146,7 +7125,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 +9157,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 +9235,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 +10124,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 +12067,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 +12297,22 @@
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,
+ checkCall(FnDecl, nullptr, ArgsArray, isa<CXXMethodDecl>(FnDecl), OpLoc,
TheCall->getSourceRange(), VariadicDoesNotApply);
+ diagnoseArgDependentDiagnoseIfAttrs(FnDecl, ImplicitThis, ArgsArray,
+ OpLoc);
return MaybeBindToTemporary(TheCall);
} else {
@@ -12561,6 +12521,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 +12688,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 +12708,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 +12831,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 +12909,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 +13185,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 +13259,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))
@@ -2482,38 +2480,65 @@
/// CheckConstructorCall - Check a constructor call for correctness and safety
/// properties not enforced by the C type system.
void Sema::CheckConstructorCall(FunctionDecl *FDecl,
- ArrayRef<const Expr *> Args,
+ ArrayRef<Expr *> Args,
const FunctionProtoType *Proto,
SourceLocation Loc) {
VariadicCallType CallType =
Proto->isVariadic() ? VariadicConstructor : VariadicDoesNotApply;
- checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(),
+ checkCall(FDecl, Proto, Args, /*IsMemberFunction=*/true, Loc, SourceRange(),
CallType);
+ diagnoseArgDependentDiagnoseIfAttrs(FDecl, /*ThisArg=*/nullptr, Args, Loc);
}
-/// 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(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(),
+ } else if (IsMemberFunction)
+ ImplicitThis =
+ cast<CXXMemberCallExpr>(TheCall)->getImplicitObjectArgument();
+ return std::make_tuple(ImplicitThis, llvm::makeArrayRef(Args, NumArgs),
+ IsMemberFunction);
+}
+
+bool Sema::checkDiagnoseIfAttrsOnCall(FunctionDecl *Function, CallExpr *Call) {
+ Expr *ImplicitThis;
+ ArrayRef<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);
+ diagnoseArgDependentDiagnoseIfAttrs(FDecl, ThisArg, Args,
+ TheCall->getExprLoc());
+
IdentifierInfo *FnInfo = FDecl->getIdentifier();
// None of the checks below are needed for functions that don't have
// simple names (e.g., C++ conversion functions).
@@ -2524,7 +2549,7 @@
CheckMaxUnsignedZero(TheCall, FDecl);
if (getLangOpts().ObjC1)
- DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
+ DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args);
unsigned CMId = FDecl->getMemoryFunctionKind();
if (CMId == 0)
@@ -2542,7 +2567,7 @@
}
bool Sema::CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation lbrac,
- ArrayRef<const Expr *> Args) {
+ ArrayRef<Expr *> Args) {
VariadicCallType CallType =
Method->isVariadic() ? VariadicMethod : VariadicDoesNotApply;
@@ -11930,4 +11955,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,25 @@
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(FunctionDecl *Function,
+ Expr *ThisArg,
+ ArrayRef<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(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(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.
@@ -9922,12 +9909,12 @@
bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
const FunctionProtoType *Proto);
bool CheckObjCMethodCall(ObjCMethodDecl *Method, SourceLocation loc,
- ArrayRef<const Expr *> Args);
+ ArrayRef<Expr *> Args);
bool CheckPointerCall(NamedDecl *NDecl, CallExpr *TheCall,
const FunctionProtoType *Proto);
bool CheckOtherCall(CallExpr *TheCall, const FunctionProtoType *Proto);
void CheckConstructorCall(FunctionDecl *FDecl,
- ArrayRef<const Expr *> Args,
+ ArrayRef<Expr *> Args,
const FunctionProtoType *Proto,
SourceLocation Loc);
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 it only alloates 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