[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp created https://github.com/llvm/llvm-project/pull/74852 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}' ``` >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
llvmbot wrote: @llvm/pr-subscribers-clang Author: None (sethp) Changes 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}' ``` --- Full diff: https://github.com/llvm/llvm-project/pull/74852.diff 5 Files Affected: - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+23-8) - (modified) clang/test/CXX/class/class.compare/class.eq/p3.cpp (+10-10) - (modified) clang/test/CXX/class/class.compare/class.rel/p2.cpp (+5-5) - (modified) clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp (+1-1) - (modified) clang/test/SemaCXX/static-assert-cxx17.cpp (+1-1) ``diff diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aece..e3d46c3140741 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe7302..53c4dda133301 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} expected-note {{evaluates to}} struc
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/shafik commented: I don't feel like the changes are an improvement to the diagnostic but I would like to hear from others who focus on clang front-end reviews. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to '{0, {0, 3, 4}, 5} == {1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// TODO[seth] syntactically sort of valid but almost entirely unusuable +// (it's an int *, not an int [3] ) +// constexpr int _[3] = {...}; would work, but that's not piecewise substitutable +// maybe it's ok? I mean, not like we can do better really... +constexpr auto _ = (int[3]){2, 3, 4}; + +// output: '{{2, 3, 4}} == {0, 3, 4}' (the `{{` breaks VerifyDiagnosticConsumer::ParseDirective) sethp wrote: Well, my bad idea [worked out easily enough](https://github.com/sethp/llvm-project/commit/153d36338a73d8fbbb6087b2cf07e671b7aa660a). I'm happy to roll that in to (or out of) this change: whatever y'all think is best. (I also seem to have confused the 'hub with an immaculately timed interrupt to the `git push`; that commit is on the appropriate branch in my fork, but hasn't shown up here on the PR yet. Maybe it will eventually? ¯\\_(ツ)_/¯ ) https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/4] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to '{0, {0, 3, 4}, 5} == {1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// TODO[seth] syntactically sort of valid but almost entirely unusuable +// (it's an int *, not an int [3] ) +// constexpr int _[3] = {...}; would work, but that's not piecewise substitutable +// maybe it's ok? I mean, not like we can do better really... +constexpr auto _ = (int[3]){2, 3, 4}; + +// output: '{{2, 3, 4}} == {0, 3, 4}' (the `{{` breaks VerifyDiagnosticConsumer::ParseDirective) erichkeane wrote: Please put that 'wide delimiters' thing as a separate pull request that we can review, I suspect it'll go quickly, but i want a few folks to chime in who have experience on the consumer. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/6] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/7] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: In playing around with this some more, I found it was pretty effective for me to have an intializer-like expression (as in [e769f15](https://github.com/llvm/llvm-project/pull/74852/commits/e769f158c8ee001e95299ce4c83e5250e2a8f963) ), because then I could copy/paste the output from clang directly into a `static_assert()` in the source file and start cutting it down interactively with clangd's guidance and my editor identifying common subexpressions: ```c++ // evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}' -> static_assert(A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}); // -> static_assert(A{0, {0}, 5} == A{1, {2}, 5}); // -> static_assert(A{0, {0}} == A{1, {2}}); ``` gets me to a minimal "diff" of the complex struct plus some nice paths to the different elements: ![image](https://github.com/llvm/llvm-project/assets/241129/3abe2c1b-1a19-4b3a-9fc6-32029c25ab40) I could reproduce that in the textual output, but I worry that'd be too noisy without the dimming/color text effects. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} sethp wrote: NB: ```c++ static_assert(C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}); // ^ ^ ``` doesn't work as an example of two fully initialized `C`s because the unnamed enum can't be initialized from the `int`s indicated above; if we like this idea, I'll look into how to print those out as `C::E1` and `C::E2`. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} sethp wrote: this also doesn't quite work as advertised, either. The closest I could come was something like: ``` constexpr int v[3] = {0, 3, 4}; static_assert(_arr{{2, 3, 4}} == v); // -> constexpr int v[3] = {0}; static_assert(_arr{{2}} == v); ``` Because anything else and the evaluator complained about invalid `constexpr` subexpressions in: ``` In call to '_arr{{2}}.operator==(&(int[3]){0}[0])' ``` https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} erichkeane wrote: I really like the idea of printing enum values (though we obviously have to revert to the number in cases where there isn't a value). However, these longer ones make me wonder if we should be printing JUST the differences here instead of all of this. I realize this is a divergence in the direction of this patch, but feels like it would be a MUCH nicer experience. MAYBE it is a second note? And perhaps it should only happen when there is no user-defined == (and maybe the printing everythign should only happen in that case?). Consider: ``` struct S { int a,b,c; }; constexpr bool operator==(const S&, const S&) { return false;} static_assert(S{1,2,3} == S{1,2,3}); ``` This diagnostic would be REALLY confusing as is, right? 'static-assert-failed' followed by 'S{1,2,3} != S{1,2,3}`. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/8] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} sethp wrote: Ah, happily this is resolved by doing less work: the `const` qualifier from `(const int [3]){0}` is semantically important here, and the type printer already "knows" how to print qualified types appropriately. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} sethp wrote: It is confusing, but I don't think this change makes it any worse; the output I see from clang with my change is: ```c++ clang/test/SemaCXX/static-assert-diagnostics.cpp:177:15: error: static assertion failed due to requirement 'S{1, 2, 3} == S{1, 2, 3}' 177 | static_assert(S{1,2,3} == S{1,2,3}); | ^~~~ clang/test/SemaCXX/static-assert-diagnostics.cpp:177:24: note: expression evaluates to 'S{1, 2, 3} == S{1, 2, 3}' 177 | static_assert(S{1,2,3} == S{1,2,3}); | ~^~~ ``` The `note` this change adds is redundant, sure, but I don't think it's more confusing than omitting it: either way, I'm going to go hunt for an `operator==` between two `S`s and see if I can figure out why two things that sure feel like they _ought_ to be `==` aren't. A different example with the same `operator==` overload there might be something like this: ``` struct alignas(int) S { unsigned char a, b, c; }; constexpr bool operator==(const S&, const S&) { return false; } static_assert(S{1, 2, 3} == std::bit_cast(0x030201)); ``` Which now outputs: ``` clang/test/SemaCXX/static-assert-diagnostics.cpp:177:15: error: static assertion failed due to requirement 'S{1, 2, 3} == std::bit_cast(197121)' 177 | static_assert(S{1, 2, 3} == std::bit_cast(0x030201)); | ^~~~ clang/test/SemaCXX/static-assert-diagnostics.cpp:177:26: note: expression evaluates to 'S{1, 2, 3} == struct S{1, 2, 3}' 177 | static_assert(S{1, 2, 3} == std::bit_cast(0x030201)); | ~~~^ ``` Here the note is, I think, quite helpful: to me it very strongly suggests to me that the issue isn't the `bit_cast` or a endian-ness mismatch or anything like that, but the `operator==` implementation. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp edited https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} sethp wrote: Oh, I also wanted to add: I like the idea of adding extra `note`s here when possible to be more helpful in diagnosing the problem. To me, that's what this PR does: adds more context that will help in at least a few situations that I've found. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -711,11 +712,54 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, case APValue::Indeterminate: Out << ""; return; - case APValue::Int: + case APValue::Int: { +const APSInt &Val = getInt(); +if (const EnumType *ET = Ty->getAs()) { + // print the enumerator name if requested (and one exists) + if (Policy.UseEnumerators) { +for (const EnumConstantDecl *ECD : ET->getDecl()->enumerators()) { + if (APSInt::isSameValue(ECD->getInitVal(), Val)) { +if (ECD->isCXXClassMember()) + ECD->printQualifiedName(Out, Policy); +else + ECD->printName(Out, Policy); sethp wrote: This is a weird heuristic: without it, we were trying to print a function-local enum constant as `fn_name()::Constant`. It might be better to go back to just always using the QualifiedName though, I'm not sure. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
tahonermann wrote: This looks like a good change to me since coercing a compiler to reveal the values of compile-time objects can be frustrating. We could really use something like `constexpr_print` from [P2758 (Emitting messages at compile time)](https://wg21.link/p2758). https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
tbaederr wrote: I like this change, the only thing I'm a little afraid of is that the output will deteriorate for large structs https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Thanks for the feedback! I've definitely found it helpful trying to puzzle out why one of my static_asserts is failing, though it's certainly a far less complete solution than the document @tahonermann shared. The output from `printPretty` will definitely fall apart for a complex structure (especially one with e.g. multiple bases, or lots of similar fields). Still, this change seems like an overall improvement to me: it's still quite possible to cut a complex struct comparison up into multiple `static_assert`s. Right now I feel more or less compelled to cut every comparison down to asserts on built-in types, and this output was intended to raise that floor up to "a struct with a handful of fields and an `operator==`." That said, I'm receiving approximately $0 from Big `printPretty` lobbying groups, so I'm happy to walk the structure however we feel `ConvertAPValueToString` ought to do the conversion. I do quite like how `clangd` does its inline initializer hints, so maybe something like: ``` Expression evaluates to '{x: 1, y: 2} == {x: 3, y: 4}' ``` for the original example, or for a `int[2]`: ``` Expression evaluates to '{[0]=1, [1]=2} == ...' ``` What do you think? https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/erichkeane edited https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/erichkeane commented: I too would like to see some more complicated dumps for 'complicated' structs (inheritance, multiple inheritance, etc), and vectors/arrays of complicated structs. I am really concerned with the 'depth' that this will show and be unwieldy as a result. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -6,20 +6,20 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} erichkeane wrote: Since the point of this patch is that we're changing what this message shows, please fill in the 'notes' as expected here. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -6,20 +6,20 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} sethp wrote: I can, though I was worried it'd over-specify advisory output to do so. What do you think about waiting until we reached loose consensus around the format for printing the struct? If you're looking for a sample here's a snippet of output from my `build/bin/clang clang/test/CXX/class/class.compare/class.eq/p3.cpp` : ``` clang/test/CXX/class/class.compare/class.eq/p3.cpp:9:15: error: static assertion failed due to requirement 'A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}' 9 | static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} | ^~~~ clang/test/CXX/class/class.compare/class.eq/p3.cpp:9:32: note: expression evaluates to '{1, {2, 3, 4}, 5} == {0, {2, 3, 4}, 5}' 9 | static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} | ~^~~ clang/test/CXX/class/class.compare/class.eq/p3.cpp:10:15: error: static assertion failed due to requirement 'A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}' 10 | static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} | ^~~~ clang/test/CXX/class/class.compare/class.eq/p3.cpp:10:32: note: expression evaluates to '{1, {2, 3, 4}, 5} == {1, {0, 3, 4}, 5}' 10 | static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} ``` That's for: ```c++ struct A { int a, b[3], c; bool operator==(const A&) const = default; }; ``` Which, to my eye, is a strict improvement over the only alternative (without this patch) that I see to get diagnostics: ```c++ // instead of static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); constexpr A lhs = {1, 2, 3, 4, 5}; constexpr A rhs = {0, 2, 3, 4, 5}; static_assert(lhs.a == rhs.a); static_assert(lhs.b[0] == rhs.b[0]); static_assert(lhs.b[1] == rhs.b[1]); static_assert(lhs.b[2] == rhs.b[2]); static_assert(lhs.c == rhs.c); ``` https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/2] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -6,20 +6,20 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} sethp wrote: Actually, on second thought, I think I'd prefer to leave these under-specified (since they're really checking the behavior of the default `operator==`). I did go ahead and add a test specifically for some more complicated types in [2fd5fc4](https://github.com/llvm/llvm-project/pull/74852/commits/2fd5fc464868a682cb4b66ea4e97a6a7100ab120) , though, to give us a few specific examples to focus on for discussion https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -6,20 +6,20 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} erichkeane wrote: The additional test is sufficient to me I think. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/3] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to '{0, {0, 3, 4}, 5} == {1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// TODO[seth] syntactically sort of valid but almost entirely unusuable +// (it's an int *, not an int [3] ) +// constexpr int _[3] = {...}; would work, but that's not piecewise substitutable +// maybe it's ok? I mean, not like we can do better really... +constexpr auto _ = (int[3]){2, 3, 4}; + +// output: '{{2, 3, 4}} == {0, 3, 4}' (the `{{` breaks VerifyDiagnosticConsumer::ParseDirective) erichkeane wrote: IIRC, you can escape a { with \. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/erichkeane edited https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,117 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to '{0, {0, 3, 4}, 5} == {1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// TODO[seth] syntactically sort of valid but almost entirely unusuable +// (it's an int *, not an int [3] ) +// constexpr int _[3] = {...}; would work, but that's not piecewise substitutable +// maybe it's ok? I mean, not like we can do better really... +constexpr auto _ = (int[3]){2, 3, 4}; + +// output: '{{2, 3, 4}} == {0, 3, 4}' (the `{{` breaks VerifyDiagnosticConsumer::ParseDirective) sethp wrote: It didn't seem to like it; when I tried ``` // expected-note@+1 {{evaluates to '\{{2, 3, 4}\} == {0, 3, 4}'}} ``` here I got this output from `-verify`: ``` error: 'expected-error' diagnostics seen but not expected: File /home/seth/Code/src/github.com/llvm/llvm-project/clang/test/SemaCXX/static-assert-diagnostics.cpp Line 30: cannot find end ('}}') of expected string error: 'expected-note' diagnostics seen but not expected: File /home/seth/Code/src/github.com/llvm/llvm-project/clang/test/SemaCXX/static-assert-diagnostics.cpp Line 31: expression evaluates to '{{2, 3, 4}} == {0, 3, 4}' 2 errors generated. ``` I *think* I could use the `-re` mode, but then `{{` has _meaning_ so the output would be really obscured, something like `{{[{][{]}}` would be how to match `{{`, I think. My bad idea is to teach the parser to count opening braces instead, so a `expected-error {{{` looks for a `}}}` and ignores any `{{` or `}}` sequences, but I wanted to get this up before dealing with that particular sub-problem. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/12] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Ping @cor3ntin @cjdb —this one definitely needs a rebase from me, but I don't want to do that if y'all have a review in progress. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/13] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
yronglin wrote: FYI, Does this PR have some overlapping functions with `__builtin_dump_struct` in structure printing? https://github.com/llvm/llvm-project/blob/db4170a4f3a701a62f5c1ef2e6a30f490f107f7d/clang/lib/Sema/SemaChecking.cpp#L453-L695 https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: @yronglin Thanks for introducing me to `__builtin_dump_struct`, that looks useful! I'm not sure I see much in the way of overlap with this PR, though: would you say more about what you mean? Is there maybe some commonality that you're concerned might drift apart? https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
yronglin wrote: > @yronglin Thanks for introducing me to `__builtin_dump_struct`, that looks > useful! > > I'm not sure I see much in the way of overlap with this PR, though: would you > say more about what you mean? Is there maybe some commonality that you're > concerned might drift apart? Sorry for not describing it clearly before, I really like this improvements. `__builtin_dump_struct` has the dilemma for complicated dumps for 'complicated' structs too. Can they have similar restricts and output formats in the future (not in this PR). https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
tbaederr wrote: Ping @cor3ntin @cjdb https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
tbaederr wrote: Ping @cor3ntin @cjdb https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/12] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 1/9] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,205 @@ +// RUN: %clang_cc1 -std=c++2a -verify -fsyntax-only -triple wasm32 %s +// RUN: %clang_cc1 -std=c++2a -verify -fsyntax-only -triple aarch64_be-linux-gnu %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to '(const A){0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +// `operator==` wrapper type +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-extensions" +static_assert(_arr{{2, 3, 4}} == (const int[3]){2, 3, 4}); +#pragma clang diagnostic pop + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (const int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to '(const C){{1, {2, 3, 4}, 5}, {7, 6}, C::E1} == C{{0, {0, 3, 4}, 5}, {5, 0}, C::E2}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} + +enum E { numerator }; +constexpr E e = E::numerator; +static_assert(numerator == ((E)0)); +static_assert(((E)0) == ((E)7)); // expected-error {{failed}} +// expected-note@-1 {{{evaluates to 'numerator == (E)7'}}} + +typedef enum { something } MyEnum; +static_assert(MyEnum::something == ((MyEnum)7)); // expected-error {{failed}} +// expected-note@-1 {{{evaluates to 'something == (MyEnum)7'}}} + +// unnamed enums +static_assert(C::E1 == (decltype(C::e))0); +// expected-note@+1 {{{evaluates to 'C::E1 == C::E2'}}} +static_assert(C::E1 == (decltype(C::e))1); // expected-error {{failed}} +static_assert(C::E1 == (decltype(C::e))7); // expected-error {{failed}} +// expected-note@-1 {{{evaluates to 'C::E1 == (decltype(C::e))7'}}} + +constexpr enum { declLocal } ee = declLocal; +static_assert(((decltype(ee))0) == ee); +static_assert(((decltype(ee))0) == ((decltype(ee))7)); // expected-error {{failed}} +// expected-note@-1 {{{evaluates to 'declLocal == (decltype(ee))7'}}} + +struct TU { + enum { S, U } Tag; + union { +signed int s; +unsigned int u; + }; + constexpr bool operator==(const TU& rhs) const { +if (Tag != rhs.Tag) return false; +switch (Tag) { + case S: +return s == rhs.s; + case U: +return u == rhs.u; +} + }; +}; +static_assert(TU{TU::S, {7}} == TU{TU::S, {.s=7}}); +static_assert(TU{TU::U, {.u=9}} == TU{TU::U, {.u=9}}); + +// expected-note@+1 {{{evaluates to 'TU{TU::S, {.s = 7}} == TU{TU::S, {.s = 6}}'}}} +static_assert(TU{TU::S, {.s=7}} == TU{TU::S, {.s=6}}); // expected-error {{failed}} +static_assert(TU{TU::U, {.u=7}} == TU{TU::U, {.u=9}}); // expected-error {{failed}} +// expected-note@-1 {{{evaluates to 'TU{TU::U, {.u = 7}} == TU{TU::U, {.u = 9}}'}}} + +struct EnumArray { + const E nums[3]; + constexpr bool operator==(const E rhs[3]) const { +for (unsigned i = 0; i < sizeof(nums) / sizeof(E); i++) + if (nums[i] != rhs[i]) +return false; +return true; + + }; +}; +static_assert(EnumArray{} == (const E[3]){numerator}); + +// expected-note@+1 {{{evaluates to 'EnumArray{{}} == (const E[3]){numerator, (const E)1, (const E)2}'}}} +static_assert(EnumArray{} == (const E[3]){(E)0, (E)1, (E)2}); // expected-error {{failed}} + +// define `std::bit_cast` +namespace std { +template +constexpr To bit_cast(const From &from) { + static_assert(sizeof(To) == sizeof(From)); + return __builtin_bit_cast(To, from); +} +} // namespace std + +namespace vector { +typedef int v4si __attribute__((__vector_size__(16))); + +struct V { + v4si v; + + // doesn't work + // vectors are not contextually convertable to `bool`, and + // `==` on vectors produces a vector of element-wise results + // bool operator==(const V&) const = default; + + constexpr bool operator==(const V& rhs) const { +// doesn't work +// __builtin_reduce_and is not valid in a constant expression +// return __builtin_reduce_and(b == rhs.b) && __builtin_reduce_and(v == rhs.v); + +// also doesn't work +// surprisingly, b[0] is also not valid in a constant expression (nor v[0]) +// return b[0] == rhs.b[0] && ... + +// cmp an array of bytes that does element-wise comparisons that's the same size as v +struct cmp { + unsigned char v [sizeof(v4s
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: I think this change is useful enough on its own that I'd like us to consider merging it: to me, teaching the compiler to do additional diffing or further clarifying the structure of complicated sub-objects feel like they'd benefit from having this work as a fallback. We could then build up from concrete examples of "it would be nice if the compiler diff'd here", and rely on the much more capable (and domain-aware) programmers to use the complete evaluation result for solving the general case. That said, I'm curious if there's an upper limit we ought to set here on the size of the string: copy & paste work fine for hundreds or even thousands of characters, but if someone `static_assert`s on a multi-GB `const char*`, they might be in for a bit of trouble. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} erichkeane wrote: IMO, adding notes when it doesn't add additional information is a net-loss. Folks already complain about the number of our error/notes when trying to figure something out, so I'm concerned this is actually making it worse when we don't print something 'different' in the note. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
erichkeane wrote: > I think this change is useful enough on its own that I'd like us to consider > merging it: to me, teaching the compiler to do additional diffing or further > clarifying the structure of complicated sub-objects feel like they'd benefit > from having this work as a fallback. We could then build up from concrete > examples of "it would be nice if the compiler diff'd here", and rely on the > much more capable (and domain-aware) programmers to use the complete > evaluation result for solving the general case. > As I said before, I'm somewhat concerned this makes some situations worse by adding more noise, and is 'useless' in cases where the size is long enough to make the 'difference' invisible. There is a judgement of value here that I'd probably like to hear the opinion of @AaronBallman and @cor3ntin . > That said, I'm curious if there's an upper limit we ought to set here on the > size of the string: copy & paste work fine for hundreds or even thousands of > characters, but if someone `static_assert`s on a multi-GB `const char*`, they > might be in for a bit of trouble. We absolutely should limit the size of the notes, another concern I had though I thought we auto-snipped when printing large structures. If that doesn't happen, we need to NOT do this patch, crashing a user's terminal is worse than giving them insufficient information. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} sethp wrote: I hear your frustration there: it's a very goldilocks problem to get the feedback "just right," and not one made easier by the, ah, breadth of individual preferences (and the cartesian product thereof with the standards). In this case, would it make sense to mirror what clang does with e.g. integer constants, and skip the note if they evaluate identically to as-written? So, we'd avoid noting the redundant info for `static_assert(S{1, 2, 3} == S{1, 2, 3})`, but still emit the result of the `bit_cast` for `static_assert(S{1, 2, 3} == std::bit_cast(0x030201))`? https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
@@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s + +struct A { + int a, b[3], c; + bool operator==(const A&) const = default; +}; + +constexpr auto a0 = A{0, 0, 3, 4, 5}; + +// expected-note@+1 {{evaluates to 'A{0, {0, 3, 4}, 5} == A{1, {2, 3, 4}, 5}'}} +static_assert(a0 == A{1, {2, 3, 4}, 5}); // expected-error {{failed}} + +struct _arr { + const int b[3]; + constexpr bool operator==(const int rhs[3]) const { +for (unsigned i = 0; i < sizeof(b) / sizeof(int); i++) + if (b[i] != rhs[i]) +return false; +return true; + } +}; + +// expected-note@+1 {{{evaluates to '_arr{{2, 3, 4}} == (int[3]){0, 3, 4}'}}} +static_assert(_arr{2, 3, 4} == a0.b); // expected-error {{failed}} + +struct B { + int a, c; // named the same just to keep things fresh + bool operator==(const B&) const = default; +}; + +// expected-note@+1 {{evaluates to 'B{7, 6} == B{8, 6}'}} +static_assert(B{7, 6} == B{8, 6}); // expected-error {{failed}} + +typedef int v4si __attribute__((__vector_size__(16))); + +struct C: A, B { + enum { E1, E2 } e; + bool operator==(const C&) const = default; +}; + +constexpr auto cc = C{A{1, {2, 3, 4}, 5}, B{7, 6}, C::E1}; + +// expected-note@+1 {{{evaluates to 'C{{1, {2, 3, 4}, 5}, {7, 6}, 0} == C{{0, {0, 3, 4}, 5}, {5, 0}, 1}'}}} +static_assert(cc == C{a0, {5}, C::E2}); // expected-error {{failed}} erichkeane wrote: I think that would be a sensible improvement here. I think mixed with making sure we don't print arbitrarily sized structs/arrays would make this 'net improvement'. https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Yeah, definitely agree about adding no new issues like https://github.com/llvm/llvm-project/issues/71675 . The good news is that ought to make for an easy test case: something like `struct { _BitInt(__BITINT_MAXWIDTH__ >> 6) F }` should do it. I'll take a look! https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/10] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecece..e3d46c3140741be 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe730217..53c4dda133301b4 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/11] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/erichkeane commented: I think this needs a 'merge' to get rid of the }}} patch changes from its diff, but I'm in favor of the patch otherwise. Probably could use a release note, and a look over @cor3ntin or @cjdb perhaps? https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Ping @cor3ntin @cjdb https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Ping @cor3ntin @cjdb https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [llvm] [clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
sethp wrote: Looks like I missed the branch for v18 with this one, hence the conflict (now resolved), too bad. The test that's failing is looking for clang to emit some options that it's not; it looks like the same test is failing on main, so I haven't dug too deep into it. I believe I'm just waiting on one more reviewer's approval (possibly @cor3ntin or @cjdb) for this change, and then I'll rebase/squash the history & it will be ready. Does that sound right to y'all? https://github.com/llvm/llvm-project/pull/74852 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [llvm] [clang] [Clang][Sema] Print more static_assert exprs (PR #74852)
https://github.com/sethp updated https://github.com/llvm/llvm-project/pull/74852 >From f281d34a51f662c934f158e4770774b0dc3588a2 Mon Sep 17 00:00:00 2001 From: Seth Pellegrino Date: Thu, 7 Dec 2023 08:45:51 -0800 Subject: [PATCH 01/12] [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}' ``` --- clang/lib/Sema/SemaDeclCXX.cpp| 31 ++- .../CXX/class/class.compare/class.eq/p3.cpp | 20 ++-- .../CXX/class/class.compare/class.rel/p2.cpp | 10 +++--- .../over.match.oper/p9-2a.cpp | 2 +- clang/test/SemaCXX/static-assert-cxx17.cpp| 2 +- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index c6218a491aecec..e3d46c3140741b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -17219,6 +17219,13 @@ static bool ConvertAPValueToString(const APValue &V, QualType T, OS << "i)"; } break; + case APValue::ValueKind::Array: + case APValue::ValueKind::Vector: + case APValue::ValueKind::Struct: { +llvm::raw_svector_ostream OS(Str); +V.printPretty(OS, Context, T); + } break; + default: return false; } @@ -17256,11 +17263,10 @@ static bool UsefulToPrintExpr(const Expr *E) { /// Try to print more useful information about a failed static_assert /// with expression \E void Sema::DiagnoseStaticAssertDetails(const Expr *E) { - if (const auto *Op = dyn_cast(E); - Op && Op->getOpcode() != BO_LOr) { -const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts(); -const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts(); - + const auto Diagnose = [&](const Expr *LHS, const Expr *RHS, +const llvm::StringRef &OpStr) { +LHS = LHS->IgnoreParenImpCasts(); +RHS = RHS->IgnoreParenImpCasts(); // Ignore comparisons of boolean expressions with a boolean literal. if ((isa(LHS) && RHS->getType()->isBooleanType()) || (isa(RHS) && LHS->getType()->isBooleanType())) @@ -17287,10 +17293,19 @@ void Sema::DiagnoseStaticAssertDetails(const Expr *E) { DiagSide[I].ValueString, Context); } if (DiagSide[0].Print && DiagSide[1].Print) { - Diag(Op->getExprLoc(), diag::note_expr_evaluates_to) - << DiagSide[0].ValueString << Op->getOpcodeStr() - << DiagSide[1].ValueString << Op->getSourceRange(); + Diag(E->getExprLoc(), diag::note_expr_evaluates_to) + << DiagSide[0].ValueString << OpStr << DiagSide[1].ValueString + << E->getSourceRange(); } + }; + + if (const auto *Op = dyn_cast(E); + Op && Op->getOpcode() != BO_LOr) { +Diagnose(Op->getLHS(), Op->getRHS(), Op->getOpcodeStr()); + } else if (const auto *Op = dyn_cast(E); + Op && Op->isInfixBinaryOp()) { +Diagnose(Op->getArg(0), Op->getArg(1), + getOperatorSpelling(Op->getOperator())); } } diff --git a/clang/test/CXX/class/class.compare/class.eq/p3.cpp b/clang/test/CXX/class/class.compare/class.eq/p3.cpp index 04db022fe73021..53c4dda133301b 100644 --- a/clang/test/CXX/class/class.compare/class.eq/p3.cpp +++ b/clang/test/CXX/class/class.compare/class.eq/p3.cpp @@ -6,11 +6,11 @@ struct A { }; static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 5}); -static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} -static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 4, 0}); // expected-error {{failed}} +static_assert(A{1, 2, 3, 4, 5} == A{0, 2, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 0, 3, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 0, 4, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2, 3, 4, 5} == A{1, 2, 3, 0, 5}); // expected-error {{failed}} expected-note {{evaluates to}} +static_assert(A{1, 2,