[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-04-30 Thread via cfe-commits

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)

2024-03-19 Thread via cfe-commits

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)

2024-03-19 Thread via cfe-commits

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)

2024-03-19 Thread via cfe-commits

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)

2024-03-19 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-03-15 Thread via cfe-commits

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)

2024-03-15 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-03-07 Thread Timm Baeder via cfe-commits

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)

2024-02-22 Thread Timm Baeder via cfe-commits

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)

2024-02-09 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang-tools-extra] [llvm] [clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-02-05 Thread via cfe-commits

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)

2024-01-19 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-18 Thread Erich Keane via cfe-commits

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)

2024-01-18 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-17 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 2, 3, 4, 5} 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-16 Thread via cfe-commits

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)

2024-01-16 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-16 Thread via cfe-commits


@@ -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)

2024-01-16 Thread Erich Keane via cfe-commits

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)

2024-01-16 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-14 Thread via cfe-commits

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)

2024-01-14 Thread via cfe-commits


@@ -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 ) {
+  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(v4si)];

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-14 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-10 Thread via cfe-commits


@@ -711,11 +712,54 @@ void APValue::printPretty(raw_ostream , const 
PrintingPolicy ,
   case APValue::Indeterminate:
 Out << "";
 return;
-  case APValue::Int:
+  case APValue::Int: {
+const APSInt  = 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)

2024-01-10 Thread via cfe-commits


@@ -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)

2024-01-10 Thread via cfe-commits

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)

2024-01-10 Thread via cfe-commits


@@ -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)

2024-01-10 Thread via cfe-commits


@@ -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)

2024-01-10 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-09 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-09 Thread via cfe-commits


@@ -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)

2024-01-09 Thread via cfe-commits


@@ -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)

2024-01-09 Thread via cfe-commits

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)

2024-01-09 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-08 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-08 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-06 Thread Craig Topper via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-06 Thread via cfe-commits


@@ -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)

2024-01-05 Thread via cfe-commits


@@ -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)

2024-01-05 Thread Erich Keane via cfe-commits

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)

2024-01-05 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-05 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-05 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-05 Thread via cfe-commits


@@ -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)

2024-01-05 Thread via cfe-commits

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 ) 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 , 
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 ) {
+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, 4, 5} == 

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2024-01-05 Thread via cfe-commits


@@ -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)

2024-01-03 Thread Erich Keane via cfe-commits


@@ -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)

2024-01-03 Thread Erich Keane via cfe-commits

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)

2024-01-03 Thread Erich Keane via cfe-commits

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)

2023-12-18 Thread via cfe-commits

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)

2023-12-15 Thread Timm Baeder via cfe-commits

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)

2023-12-12 Thread Tom Honermann via cfe-commits

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)

2023-12-08 Thread Shafik Yaghmour via cfe-commits

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)

2023-12-08 Thread via cfe-commits

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 , 
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 ) {
+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}}
 
 struct B {
   

[clang] [Clang][Sema] Print more static_assert exprs (PR #74852)

2023-12-08 Thread via cfe-commits

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 ) 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 ) 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 , 
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 ) {
+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,