Skip to content

Commit f281d34

Browse files
committed
[Clang][Sema] Print more static_assert exprs
This change introspects more values involved in a static_assert, and extends the supported set of operators for introspection to include binary operator method calls. It's intended to address the use-case where a small static_assert helper looks something like this (via `constexpr-builtin-bit-cast.cpp`): ```c++ struct int_splicer { unsigned x; unsigned y; constexpr bool operator==(const int_splicer &other) const { return other.x == x && other.y == y; } }; ``` When used like so: ```c++ constexpr int_splicer got{1, 2}; constexpr int_splicer want{3, 4}; static_assert(got == want); ``` Then we'd expect to get the error: ``` Static assertion failed due to requirement 'got == want' ``` And this change adds the helpful note: ``` Expression evaluates to '{1, 2} == {3, 4}' ```
1 parent db3bc49 commit f281d34

File tree

5 files changed

+40
-25
lines changed

5 files changed

+40
-25
lines changed

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T,
1721917219
OS << "i)";
1722017220
} break;
1722117221

17222+
case APValue::ValueKind::Array:
17223+
case APValue::ValueKind::Vector:
17224+
case APValue::ValueKind::Struct: {
17225+
llvm::raw_svector_ostream OS(Str);
17226+
V.printPretty(OS, Context, T);
17227+
} break;
17228+
1722217229
default:
1722317230
return false;
1722417231
}
@@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) {
1725617263
/// Try to print more useful information about a failed static_assert
1725717264
/// with expression \E
1725817265
void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
17259-
if (const auto *Op = dyn_cast<BinaryOperator>(E);
17260-
Op && Op->getOpcode() != BO_LOr) {
17261-
const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts();
17262-
const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts();
17263-
17266+
const auto Diagnose = [&](const Expr *LHS, const Expr *RHS,
17267+
const llvm::StringRef &OpStr) {
17268+
LHS = LHS->IgnoreParenImpCasts();
17269+
RHS = RHS->IgnoreParenImpCasts();
1726417270
// Ignore comparisons of boolean expressions with a boolean literal.
1726517271
if ((isa<CXXBoolLiteralExpr>(LHS) && RHS->getType()->isBooleanType()) ||
1726617272
(isa<CXXBoolLiteralExpr>(RHS) && LHS->getType()->isBooleanType()))
@@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
1728717293
DiagSide[I].ValueString, Context);
1728817294
}
1728917295
if (DiagSide[0].Print && DiagSide[1].Print) {
17290-
Diag(Op->getExprLoc(), diag::note_expr_evaluates_to)
17291-
<< DiagSide[0].ValueString << Op->getOpcodeStr()
17292-
<< DiagSide[1].ValueString << Op->getSourceRange();
17296+
Diag(E->getExprLoc(), diag::note_expr_evaluates_to)
17297+
<< DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString
17298+
<< E->getSourceRange();
1729317299
}
17300+
};
17301+
17302+
if (const auto *Op = dyn_cast<BinaryOperator>(E);
17303+
Op && Op->getOpcode() != BO_LOr) {
17304+
Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr());
17305+
} else if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(E);
17306+
Op && Op->isInfixBinaryOp()) {
17307+
Diagnose(Op->getArg(0), Op->getArg(1),
17308+
getOperatorSpelling(Op->getOperator()));
1729417309
}
1729517310
}
1729617311

clang/test/CXX/class/class.compare/class.eq/p3.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ struct A {
66
};
77

88
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5});
9-
static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}}
10-
static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}}
11-
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}}
12-
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}}
13-
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}}
9+
static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
10+
static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
11+
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
12+
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
13+
static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} expected-note {{evaluates to}}
1414

1515
struct B {
1616
int a, b[3], c;
1717
friend bool operator==(B, B) = default;
1818
};
1919

2020
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 3, 4, 5});
21-
static_assert(B{1, 2, 3, 4, 5} == B{0, 2, 3, 4, 5}); // expected-error {{failed}}
22-
static_assert(B{1, 2, 3, 4, 5} == B{1, 0, 3, 4, 5}); // expected-error {{failed}}
23-
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 0, 4, 5}); // expected-error {{failed}}
24-
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 3, 0, 5}); // expected-error {{failed}}
25-
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 3, 4, 0}); // expected-error {{failed}}
21+
static_assert(B{1, 2, 3, 4, 5} == B{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
22+
static_assert(B{1, 2, 3, 4, 5} == B{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
23+
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
24+
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}}
25+
static_assert(B{1, 2, 3, 4, 5} == B{1, 2, 3, 4, 0}); // expected-error {{failed}} expected-note {{evaluates to}}

clang/test/CXX/class/class.compare/class.rel/p2.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ namespace Rel {
1010
friend bool operator>=(const A&, const A&) = default;
1111
};
1212
static_assert(A{0} < A{1});
13-
static_assert(A{1} < A{1}); // expected-error {{failed}}
13+
static_assert(A{1} < A{1}); // expected-error {{failed}} expected-note {{'{1} < {1}'}}
1414
static_assert(A{0} <= A{1});
1515
static_assert(A{1} <= A{1});
16-
static_assert(A{2} <= A{1}); // expected-error {{failed}}
16+
static_assert(A{2} <= A{1}); // expected-error {{failed}} expected-note {{'{2} <= {1}'}}
1717
static_assert(A{1} > A{0});
18-
static_assert(A{1} > A{1}); // expected-error {{failed}}
18+
static_assert(A{1} > A{1}); // expected-error {{failed}} expected-note {{'{1} > {1}'}}
1919
static_assert(A{1} >= A{0});
2020
static_assert(A{1} >= A{1});
21-
static_assert(A{1} >= A{2}); // expected-error {{failed}}
21+
static_assert(A{1} >= A{2}); // expected-error {{failed}} expected-note {{'{1} >= {2}'}}
2222

2323
struct B {
2424
bool operator<=>(B) const = delete; // expected-note 4{{deleted here}} expected-note-re 8{{candidate {{.*}} deleted}}
@@ -49,7 +49,7 @@ namespace NotEqual {
4949
friend bool operator!=(const A&, const A&) = default;
5050
};
5151
static_assert(A{1} != A{2});
52-
static_assert(A{1} != A{1}); // expected-error {{failed}}
52+
static_assert(A{1} != A{1}); // expected-error {{failed}} expected-note {{'{1} != {1}'}}
5353

5454
struct B {
5555
bool operator==(B) const = delete; // expected-note {{deleted here}} expected-note-re 2{{candidate {{.*}} deleted}}

clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct Y {};
3333
constexpr bool operator==(X x, Y) { return x.equal; }
3434

3535
static_assert(X{true} == Y{});
36-
static_assert(X{false} == Y{}); // expected-error {{failed}}
36+
static_assert(X{false} == Y{}); // expected-error {{failed}} expected-note{{'{false} == {}'}}
3737

3838
// x == y -> y == x
3939
static_assert(Y{} == X{true});

clang/test/SemaCXX/static-assert-cxx17.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ void foo6() {
9494
// expected-error@-1{{static assertion failed due to requirement '(const X<int> *)nullptr'}}
9595
static_assert(static_cast<const X<typename T::T> *>(nullptr));
9696
// expected-error@-1{{static assertion failed due to requirement 'static_cast<const X<int> *>(nullptr)'}}
97-
static_assert((const X<typename T::T>[]){} == nullptr);
97+
static_assert((const X<typename T::T>[]){} == nullptr); // expected-note{{expression evaluates to '{} == nullptr'}}
9898
// expected-error@-1{{static assertion failed due to requirement '(const X<int>[0]){} == nullptr'}}
9999
static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
100100
// expected-error@-1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}} \

0 commit comments

Comments
 (0)