tbaeder created this revision.
tbaeder added a reviewer: aaron.ballman.
Herald added a project: All.
tbaeder requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

For

  c++
  static constexpr m = 10;
  static_assert(m == 11);

the output is before:

  ./test.cpp:2:1: error: static_assert failed due to requirement 'm == 11'
  static_assert(m == 11);
  ^             ~~~~~~~
  1 error generated.

and after:

  ./test.cpp:2:1: error: static assertion failed due to requirement 'm == 11'
  static_assert(m == 11);
  ^             ~~~~~~~
  ./test.cpp:2:15: note: left-hand side of operator '==' evaluates to '10'
  static_assert(m == 11);
                ^
  1 error generated.

The patch adds a new function `Sema::DiagnoseStaticAssertDetails()`, which 
currently only handles binary operators with an integer literal on one side and 
a non-integer literal on the other side. This is intentionally kept simple, but 
can be improved incrementally over time of course.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D130894

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/CXX/drs/dr7xx.cpp
  clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
  clang/test/PCH/cxx-templates.cpp
  clang/test/SemaTemplate/instantiate-var-template.cpp
  clang/test/SemaTemplate/instantiation-dependence.cpp

Index: clang/test/SemaTemplate/instantiation-dependence.cpp
===================================================================
--- clang/test/SemaTemplate/instantiation-dependence.cpp
+++ clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -68,8 +68,10 @@
   struct D : B, C {};
 
   static_assert(trait<A>::specialization == 0);
-  static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}}
-  static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}}
+  static_assert(trait<B>::specialization == 1); // FIXME expected-error {{failed}} \
+                                                // expected-note {{evaluates to '0'}}
+  static_assert(trait<C>::specialization == 2); // FIXME expected-error {{failed}} \
+                                                // expected-note {{evaluates to '0'}}
   static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous partial specialization}}
 }
 
Index: clang/test/SemaTemplate/instantiate-var-template.cpp
===================================================================
--- clang/test/SemaTemplate/instantiate-var-template.cpp
+++ clang/test/SemaTemplate/instantiate-var-template.cpp
@@ -31,7 +31,8 @@
   static_assert(b<char> == 1, ""); // expected-note {{in instantiation of}} expected-error {{not an integral constant}}
 
   template<typename T> void f() {
-    static_assert(a<sizeof(sizeof(f(T())))> == 0, ""); // expected-error {{static assertion failed due to requirement 'a<sizeof (sizeof (f(type-parameter-0-0())))> == 0'}}
+    static_assert(a<sizeof(sizeof(f(T())))> == 0, ""); // expected-error {{static assertion failed due to requirement 'a<sizeof (sizeof (f(type-parameter-0-0())))> == 0'}} \
+                                                       // expected-note {{evaluates to '1'}}
   }
 }
 
Index: clang/test/PCH/cxx-templates.cpp
===================================================================
--- clang/test/PCH/cxx-templates.cpp
+++ clang/test/PCH/cxx-templates.cpp
@@ -167,7 +167,8 @@
   // This used to mark 'f' invalid without producing any diagnostic. That's a
   // little hard to detect, but we can make sure that constexpr evaluation
   // fails when it should.
-  static_assert(A<int>().f() == 1); // expected-error {{static assertion failed}}
+  static_assert(A<int>().f() == 1); // expected-error {{static assertion failed}} \
+                                    // expected-note {{left-hand side of operator '==' evaluates to '0'}}
 #endif
 }
 
Index: clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
===================================================================
--- clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
+++ clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
@@ -40,8 +40,10 @@
 
 template<int ...a> constexpr auto x = [...z = a] (auto F) { return F(z...); };
 static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 123);
-static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}}
+static_assert(x<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}} \
+                                                                                          // expected-note {{evaluates to '123'}}
 
 template<int ...a> constexpr auto y = [z = a...] (auto F) { return F(z...); }; // expected-error {{must appear before the name of the capture}}
 static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 123);
-static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}}
+static_assert(y<1,2,3>([](int a, int b, int c) { return 100 * a + 10 * b + c; }) == 124); // expected-error {{failed}} \
+                                                                                          // expected-note {{evaluates to '123'}}
Index: clang/test/CXX/drs/dr7xx.cpp
===================================================================
--- clang/test/CXX/drs/dr7xx.cpp
+++ clang/test/CXX/drs/dr7xx.cpp
@@ -178,7 +178,8 @@
     static_assert(B<0>().v<1> == 3, "");
     static_assert(B<0>().v<0> == 4, "");
 #if __cplusplus < 201702L
-    // expected-error@-2 {{failed}}
+    // expected-error@-2 {{failed}} \
+    // expected-note@-2 {{left-hand side of operator '==' evaluates to '2'}}
 #endif
 
     static_assert(B<1>().w<1> == 1, "");
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -16554,6 +16554,50 @@
                                       AssertMessage, RParenLoc, false);
 }
 
+/// Try to print more useful information about a failed static_assert
+/// with expression \E
+void Sema::DiagnoseStaticAssertDetails(const Expr *E) {
+  if (const BinaryOperator *Op = dyn_cast_or_null<BinaryOperator>(E)) {
+    unsigned ExprIsRHS = true;
+    const Expr *ExprSide;
+
+    if (isa<IntegerLiteral>(Op->getRHS())) {
+      ExprIsRHS = false;
+      ExprSide = Op->getLHS();
+    } else if (isa<IntegerLiteral>(Op->getLHS())) {
+      ExprIsRHS = true;
+      ExprSide = Op->getRHS();
+    } else {
+      return;
+    }
+
+    // We already print enough info about these earlier,
+    // e.g. "requirement '2 == 1' failed" where 2 is
+    // a substituted template parameter.
+    if (isa<SubstNonTypeTemplateParmExpr>(ExprSide))
+      return;
+
+    if (isa<IntegerLiteral>(ExprSide))
+      return;
+
+    // Evaluate the expression again
+    Expr::EvalResult EvalResult;
+    SmallVector<PartialDiagnosticAt, 0> Notes;
+    EvalResult.Diag = &Notes;
+    ExprSide->EvaluateAsRValue(EvalResult, Context, true);
+
+    SmallString<12> ValueString;
+    if (EvalResult.Val.isInt()) {
+      EvalResult.Val.getInt().toString(ValueString);
+    } else {
+      assert(false);
+    }
+
+    Diag(ExprSide->getExprLoc(), diag::note_bin_op_evaluates)
+        << (unsigned)ExprIsRHS << Op->getOpcodeStr() << ValueString;
+  }
+}
+
 Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
                                          Expr *AssertExpr,
                                          StringLiteral *AssertMessage,
@@ -16609,9 +16653,11 @@
           DiagnoseUnsatisfiedConstraint(Satisfaction);
       } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond)
                            && !isa<IntegerLiteral>(InnerCond)) {
+
         Diag(StaticAssertLoc, diag::err_static_assert_requirement_failed)
           << InnerCondDescription << !AssertMessage
           << Msg.str() << InnerCond->getSourceRange();
+        DiagnoseStaticAssertDetails(InnerCond);
       } else {
         Diag(StaticAssertLoc, diag::err_static_assert_failed)
           << !AssertMessage << Msg.str() << AssertExpr->getSourceRange();
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -7482,6 +7482,7 @@
                                      StringLiteral *AssertMessageExpr,
                                      SourceLocation RParenLoc,
                                      bool Failed);
+  void DiagnoseStaticAssertDetails(const Expr *E);
 
   FriendDecl *CheckFriendTypeDecl(SourceLocation LocStart,
                                   SourceLocation FriendLoc,
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1532,6 +1532,8 @@
 def err_static_assert_failed : Error<"static assertion failed%select{: %1|}0">;
 def err_static_assert_requirement_failed : Error<
   "static assertion failed due to requirement '%0'%select{: %2|}1">;
+def note_bin_op_evaluates : Note<
+  "%select{left-hand|right-hand}0 side of operator '%1' evaluates to '%2'">;
 
 def warn_consteval_if_always_true : Warning<
   "consteval if is always true in an %select{unevaluated|immediate}0 context">,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to