arichardson created this revision.
arichardson added reviewers: courbet, Quuxplusone, aaron.ballman.
arichardson requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Trying to debug a static_assert(sizeof(foo) == ...) failure can be rather
awkward since there is no easy way to print the value of the sizeof() at
compile-time without another compiler diagnostic involving an array size.
This patch emits additional notes when a static_assert fails for any
UnaryExprOrTypeTraitExpr expression inside the static_assert.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D108211

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaTemplate.cpp
  clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
  clang/test/Parser/objc-static-assert.mm
  clang/test/Sema/static-assert.c
  clang/test/SemaCXX/constant-expression-cxx11.cpp
  clang/test/SemaCXX/static-assert-cxx17.cpp
  clang/test/SemaCXX/static-assert.cpp
  clang/test/SemaTemplate/overload-candidates.cpp

Index: clang/test/SemaTemplate/overload-candidates.cpp
===================================================================
--- clang/test/SemaTemplate/overload-candidates.cpp
+++ clang/test/SemaTemplate/overload-candidates.cpp
@@ -62,6 +62,7 @@
 
 template<typename T> struct NonTemplateFunction {
   typename boost::enable_if<sizeof(T) == 4, int>::type f(); // expected-error{{failed requirement 'sizeof(char) == 4'; 'enable_if' cannot be used to disable this declaration}}
+  // expected-note@-1{{with 'sizeof(char)' equal to 1}}
 };
 NonTemplateFunction<char> NTFC; // expected-note{{here}}
 
Index: clang/test/SemaCXX/static-assert.cpp
===================================================================
--- clang/test/SemaCXX/static-assert.cpp
+++ clang/test/SemaCXX/static-assert.cpp
@@ -21,8 +21,10 @@
 T<1> t1; // expected-note {{in instantiation of template class 'T<1>' requested here}}
 T<2> t2;
 
-template<typename T> struct S {
-    static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed due to requirement 'sizeof(char) > sizeof(char)' "Type not big enough!"}}
+template <typename T> struct S {
+  static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static_assert failed due to requirement 'sizeof(char) > sizeof(char)' "Type not big enough!"}}
+                                                                   // expected-note@-1{{with 'sizeof(char)' equal to 1}}
+                                                                   // expected-note@-2{{with 'sizeof(char)' equal to 1}}
 };
 
 S<char> s1; // expected-note {{in instantiation of template class 'S<char>' requested here}}
@@ -199,3 +201,18 @@
 constexpr NotBool constexprNotBool;
 static_assert(notBool, "message");          // expected-error {{value of type 'struct NotBool' is not contextually convertible to 'bool'}}
 static_assert(constexprNotBool, "message"); // expected-error {{value of type 'const NotBool' is not contextually convertible to 'bool'}}
+
+struct IntAndPointer {
+  int i;
+  void *p;
+};
+static_assert(sizeof(IntAndPointer) == 4, "message");
+// expected-error@-1{{static_assert failed due to requirement 'sizeof(IntAndPointer) == 4' "message"}}
+// expected-note@-2{{with 'sizeof(IntAndPointer)' equal to 16}}
+static_assert(alignof(IntAndPointer) == 4, "message");
+// expected-error@-1{{static_assert failed due to requirement 'alignof(IntAndPointer) == 4' "message"}}
+// expected-note@-2{{with 'alignof(IntAndPointer)' equal to 8}}
+static_assert(alignof(IntAndPointer) == sizeof(IntAndPointer), "message");
+// expected-error@-1{{static_assert failed due to requirement 'alignof(IntAndPointer) == sizeof(IntAndPointer)' "message"}}
+// expected-note@-2{{with 'alignof(IntAndPointer)' equal to 8}}
+// expected-note@-3{{with 'sizeof(IntAndPointer)' equal to 16}}
Index: clang/test/SemaCXX/static-assert-cxx17.cpp
===================================================================
--- clang/test/SemaCXX/static-assert-cxx17.cpp
+++ clang/test/SemaCXX/static-assert-cxx17.cpp
@@ -89,6 +89,7 @@
   // expected-error@-1{{static_assert failed due to requirement 'int(0)'}}
   static_assert(sizeof(X<typename T::T>) == 0);
   // expected-error@-1{{static_assert failed due to requirement 'sizeof(X<int>) == 0'}}
+  // expected-note@-2{{with 'sizeof(X<int>)' equal to 8}}
   static_assert((const X<typename T::T> *)nullptr);
   // expected-error@-1{{static_assert failed due to requirement '(const X<int> *)nullptr'}}
   static_assert(static_cast<const X<typename T::T> *>(nullptr));
@@ -97,6 +98,7 @@
   // expected-error@-1{{static_assert failed due to requirement '(X<int> const[0]){} == nullptr'}}
   static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
   // expected-error@-1{{static_assert failed due to requirement 'sizeof(X<void>) == 0'}}
+  // expected-note@-2{{with 'sizeof(X<void>)' equal to 8}}
   static_assert(constexpr_return_false<typename T::T, typename T::U>());
   // expected-error@-1{{static_assert failed due to requirement 'constexpr_return_false<int, float>()'}}
 }
Index: clang/test/SemaCXX/constant-expression-cxx11.cpp
===================================================================
--- clang/test/SemaCXX/constant-expression-cxx11.cpp
+++ clang/test/SemaCXX/constant-expression-cxx11.cpp
@@ -1908,11 +1908,13 @@
   // cxx11-error@-1    {{not an integral constant expression}}
   // cxx11-note@-2     {{call to virtual function}}
   // cxx20_2b-error@-3 {{static_assert failed}}
+  // cxx20_2b-note@-4  {{with 'sizeof(VirtualFromBase::X<VirtualFromBase::S1>)' equal to 16}}
 
   // Non-virtual f(), OK.
   constexpr X<X<S2>> xxs2;
   constexpr X<S2> *q = const_cast<X<X<S2>>*>(&xxs2);
   static_assert(q->f() == sizeof(S2), ""); // cxx20_2b-error {{static_assert failed}}
+  // cxx20_2b-note@-1 {{with 'sizeof(VirtualFromBase::S2)' equal to 8}}
 }
 
 namespace ConstexprConstructorRecovery {
Index: clang/test/Sema/static-assert.c
===================================================================
--- clang/test/Sema/static-assert.c
+++ clang/test/Sema/static-assert.c
@@ -55,7 +55,9 @@
 typedef UNION(unsigned, struct A) U1; // ext-warning 3 {{'_Static_assert' is a C11 extension}}
 UNION(char[2], short) u2 = { .one = { 'a', 'b' } }; // ext-warning 3 {{'_Static_assert' is a C11 extension}} cxx-warning {{designated initializers are a C++20 extension}}
 typedef UNION(char, short) U3; // expected-error {{static_assert failed due to requirement 'sizeof(char) == sizeof(short)' "type size mismatch"}} \
-                               // ext-warning 3 {{'_Static_assert' is a C11 extension}}
+                               // ext-warning 3 {{'_Static_assert' is a C11 extension}} \
+                               // expected-note {{with 'sizeof(char)' equal to 1}} \
+                               // expected-note {{with 'sizeof(short)' equal to 2}}
 typedef UNION(float, 0.5f) U4; // expected-error {{expected a type}} \
                                // ext-warning 3 {{'_Static_assert' is a C11 extension}}
 
Index: clang/test/Parser/objc-static-assert.mm
===================================================================
--- clang/test/Parser/objc-static-assert.mm
+++ clang/test/Parser/objc-static-assert.mm
@@ -26,7 +26,7 @@
 
   static_assert(a, ""); // expected-error {{static_assert expression is not an integral constant expression}}
   static_assert(sizeof(a) == 4, "");
-  static_assert(sizeof(a) == 3, ""); // expected-error {{static_assert failed}}
+  static_assert(sizeof(a) == 3, ""); // expected-error {{static_assert failed}} expected-note{{with 'sizeof (a)' equal to 4}}
 }
 
 static_assert(1, "");
@@ -40,7 +40,7 @@
   static_assert(1, "");
   _Static_assert(1, "");
   static_assert(sizeof(b) == 4, "");
-  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}}
+  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} expected-note{{with 'sizeof (b)' equal to 4}}
 }
 
 static_assert(1, "");
@@ -56,7 +56,7 @@
 @interface B () {
   int b;
   static_assert(sizeof(b) == 4, "");
-  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}}
+  static_assert(sizeof(b) == 3, ""); // expected-error {{static_assert failed}} expected-note{{with 'sizeof (b)' equal to 4}}
 }
 @end
 
Index: clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
===================================================================
--- clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
+++ clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
@@ -98,7 +98,7 @@
     static_assert(sizeof(arr2) == 12, "");
 
     // Use a failing test to ensure the type isn't considered dependent.
-    static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}}
+    static_assert(sizeof(arr2) == 13, ""); // expected-error {{failed}} expected-note{{with 'sizeof (arr2)' equal to 12}}
   }
 
   void g() { f<int[3]>(); } // expected-note {{in instantiation of}}
Index: clang/lib/Sema/SemaTemplate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplate.cpp
+++ clang/lib/Sema/SemaTemplate.cpp
@@ -3574,8 +3574,10 @@
 // for actual types.
 class FailedBooleanConditionPrinterHelper : public PrinterHelper {
 public:
-  explicit FailedBooleanConditionPrinterHelper(const PrintingPolicy &P)
-      : Policy(P) {}
+  explicit FailedBooleanConditionPrinterHelper(
+      const PrintingPolicy &P, Sema &S,
+      SmallVectorImpl<PartialDiagnosticAt> &Notes)
+      : Policy(P), S(S), Notes(Notes) {}
 
   bool handledStmt(Stmt *E, raw_ostream &OS) override {
     const auto *DR = dyn_cast<DeclRefExpr>(E);
@@ -3593,18 +3595,34 @@
             IV->getSpecializedTemplate()->getTemplateParameters());
       }
       return true;
+    } else if (auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E)) {
+      Expr::EvalResult Result;
+      if (UE->EvaluateAsConstantExpr(Result, S.Context) && Result.Val.isInt()) {
+        std::string ExprStr;
+        llvm::raw_string_ostream ExprStream(ExprStr);
+        UE->printPretty(ExprStream, nullptr, Policy);
+        ExprStream.flush();
+        Notes.push_back(PartialDiagnosticAt(
+            UE->getExprLoc(),
+            S.PDiag(diag::note_static_assert_requirement_context)
+                << ExprStr << toString(Result.Val.getInt(), 10)
+                << UE->getSourceRange()));
+      }
     }
     return false;
   }
 
 private:
   const PrintingPolicy Policy;
+  Sema &S;
+  SmallVectorImpl<PartialDiagnosticAt> &Notes;
 };
 
 } // end anonymous namespace
 
 std::pair<Expr *, std::string>
-Sema::findFailedBooleanCondition(Expr *Cond) {
+Sema::findFailedBooleanCondition(Expr *Cond,
+                                 SmallVectorImpl<PartialDiagnosticAt> &Notes) {
   Cond = lookThroughRangesV3Condition(PP, Cond);
 
   // Separate out all of the terms in a conjunction.
@@ -3641,7 +3659,7 @@
     llvm::raw_string_ostream Out(Description);
     PrintingPolicy Policy = getPrintingPolicy();
     Policy.PrintCanonicalTypes = true;
-    FailedBooleanConditionPrinterHelper Helper(Policy);
+    FailedBooleanConditionPrinterHelper Helper(Policy, *this, Notes);
     FailedCond->printPretty(Out, &Helper, Policy, 0, "\n", nullptr);
   }
   return { FailedCond, Description };
@@ -3729,8 +3747,10 @@
                 == TemplateArgument::Expression) {
             Expr *FailedCond;
             std::string FailedDescription;
+            SmallVector<PartialDiagnosticAt, 2> Notes;
             std::tie(FailedCond, FailedDescription) =
-              findFailedBooleanCondition(TemplateArgs[0].getSourceExpression());
+                findFailedBooleanCondition(
+                    TemplateArgs[0].getSourceExpression(), Notes);
 
             // Remove the old SFINAE diagnostic.
             PartialDiagnosticAt OldDiag =
@@ -3744,6 +3764,8 @@
               PDiag(diag::err_typename_nested_not_found_requirement)
                 << FailedDescription
                 << FailedCond->getSourceRange());
+            for (const PartialDiagnosticAt &Note : Notes)
+              (*DeductionInfo)->addSFINAEDiagnostic(Note.first, Note.second);
           }
         }
       }
@@ -10608,13 +10630,16 @@
       if (Cond) {
         Expr *FailedCond;
         std::string FailedDescription;
+        SmallVector<PartialDiagnosticAt, 2> Notes;
         std::tie(FailedCond, FailedDescription) =
-          findFailedBooleanCondition(Cond);
+            findFailedBooleanCondition(Cond, Notes);
 
         Diag(FailedCond->getExprLoc(),
              diag::err_typename_nested_not_found_requirement)
           << FailedDescription
           << FailedCond->getSourceRange();
+        for (const PartialDiagnosticAt &Note : Notes)
+          Diag(Note.first, Note.second);
         return QualType();
       }
 
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -16366,8 +16366,9 @@
 
       Expr *InnerCond = nullptr;
       std::string InnerCondDescription;
+      SmallVector<PartialDiagnosticAt, 2> Notes;
       std::tie(InnerCond, InnerCondDescription) =
-        findFailedBooleanCondition(Converted.get());
+          findFailedBooleanCondition(Converted.get(), Notes);
       if (InnerCond && isa<ConceptSpecializationExpr>(InnerCond)) {
         // Drill down into concept specialization expressions to see why they
         // weren't satisfied.
@@ -16385,6 +16386,8 @@
         Diag(StaticAssertLoc, diag::err_static_assert_failed)
           << !AssertMessage << Msg.str() << AssertExpr->getSourceRange();
       }
+      for (const PartialDiagnosticAt &Note : Notes)
+        Diag(Note.first, Note.second);
       Failed = true;
     }
   } else {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -3739,7 +3739,9 @@
 
   /// Find the failed Boolean condition within a given Boolean
   /// constant expression, and describe it with a string.
-  std::pair<Expr *, std::string> findFailedBooleanCondition(Expr *Cond);
+  std::pair<Expr *, std::string>
+  findFailedBooleanCondition(Expr *Cond,
+                             SmallVectorImpl<PartialDiagnosticAt> &Notes);
 
   /// Emit diagnostics for the diagnose_if attributes on Function, ignoring any
   /// non-ArgDependent DiagnoseIfAttrs.
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1501,6 +1501,7 @@
 def err_static_assert_failed : Error<"static_assert failed%select{ %1|}0">;
 def err_static_assert_requirement_failed : Error<
   "static_assert failed due to requirement '%0'%select{ %2|}1">;
+def note_static_assert_requirement_context : Note<"with '%0' equal to %1">;
 
 def ext_inline_variable : ExtWarn<
   "inline variables are a C++17 extension">, InGroup<CXX17>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D108211: Emit... Alexander Richardson via Phabricator via cfe-commits

Reply via email to