tbaeder updated this revision to Diff 449669.
tbaeder added a comment.
Ignore what I said, they are supported now though.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D130894/new/
https://reviews.llvm.org/D130894
Files:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/Sema/SemaDeclCXX.cpp
clang/test/CXX/dcl.decl/dcl.meaning/dcl.array/p3.cpp
clang/test/CXX/drs/dr7xx.cpp
clang/test/CXX/temp/temp.decls/temp.variadic/init-capture.cpp
clang/test/Lexer/cxx1z-trigraphs.cpp
clang/test/PCH/cxx-templates.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/instantiate-var-template.cpp
clang/test/SemaTemplate/instantiation-dependence.cpp
clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
Index: clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
===================================================================
--- clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
+++ clang/test/SemaTemplate/temp_arg_nontype_cxx20.cpp
@@ -184,7 +184,8 @@
namespace Diags {
struct A { int n, m; };
- template<A a> struct X { static_assert(a.n == a.m); }; // expected-error {{static assertion failed due to requirement 'Diags::A{1, 2}.n == Diags::A{1, 2}.m'}}
+ template<A a> struct X { static_assert(a.n == a.m); }; // expected-error {{static assertion failed due to requirement 'Diags::A{1, 2}.n == Diags::A{1, 2}.m'}} \
+ // expected-note {{evaluates to 1 == 2}}
template struct X<A{1, 2}>; // expected-note {{in instantiation of template class 'Diags::X<{1, 2}>' requested here}}
}
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/SemaCXX/static-assert.cpp
===================================================================
--- clang/test/SemaCXX/static-assert.cpp
+++ clang/test/SemaCXX/static-assert.cpp
@@ -22,7 +22,8 @@
T<2> t2;
template<typename T> struct S {
- static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static assertion failed due to requirement 'sizeof(char) > sizeof(char)': Type not big enough!}}
+ static_assert(sizeof(T) > sizeof(char), "Type not big enough!"); // expected-error {{static assertion failed due to requirement 'sizeof(char) > sizeof(char)': Type not big enough!}} \
+ // expected-note {{1 > 1}}
};
S<char> s1; // expected-note {{in instantiation of template class 'S<char>' requested here}}
@@ -215,3 +216,40 @@
static_assert(constexprNotBool, "message"); // expected-error {{value of type 'const NotBool' is not contextually convertible to 'bool'}}
static_assert(1 , "") // expected-error {{expected ';' after 'static_assert'}}
+
+
+namespace Diagnostics {
+ /// No notes for literals.
+ static_assert(false, ""); // expected-error {{failed}}
+ static_assert(1.0 > 2.0, ""); // expected-error {{failed}}
+ static_assert('c' == 'd', ""); // expected-error {{failed}}
+ static_assert(1 == 2, ""); // expected-error {{failed}}
+
+ /// Simple things are ignored.
+ static_assert(1 == (-(1)), ""); //expected-error {{failed}}
+
+ /// Chars are printed as chars.
+ constexpr char getChar() {
+ return 'c';
+ }
+ static_assert(getChar() == 'a', ""); // expected-error {{failed}} \
+ // expected-note {{left-hand side of operator == evaluates to 'c'}}
+
+ /// Bools are printed as bools.
+ constexpr bool invert(bool b) {
+ return !b;
+ }
+ static_assert(invert(true) == invert(false), ""); // expected-error {{failed}} \
+ // expected-note {{evaluates to false == true}}
+
+ /// No notes here since we compare a bool expression with a bool literal.
+ static_assert(invert(true) == true, ""); // expected-error {{failed}}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc99-extensions"
+ constexpr _Complex float com = {5,6};
+ constexpr _Complex float com2 = {1, 9};
+ static_assert(com == com2, ""); // expected-error {{failed}} \
+ // expected-note {{evaluates to (5 + 6i) == (1 + 9i)}}
+#pragma clang diagnostic pop
+}
Index: clang/test/SemaCXX/static-assert-cxx17.cpp
===================================================================
--- clang/test/SemaCXX/static-assert-cxx17.cpp
+++ clang/test/SemaCXX/static-assert-cxx17.cpp
@@ -88,7 +88,8 @@
static_assert(typename T::T(0));
// expected-error@-1{{static assertion failed due to requirement 'int(0)'}}
static_assert(sizeof(X<typename T::T>) == 0);
- // expected-error@-1{{static assertion failed due to requirement 'sizeof(X<int>) == 0'}}
+ // expected-error@-1{{static assertion failed due to requirement 'sizeof(X<int>) == 0'}} \
+ // expected-note@-1 {{evaluates to 8}}
static_assert((const X<typename T::T> *)nullptr);
// expected-error@-1{{static assertion failed due to requirement '(const X<int> *)nullptr'}}
static_assert(static_cast<const X<typename T::T> *>(nullptr));
@@ -96,7 +97,8 @@
static_assert((const X<typename T::T>[]){} == nullptr);
// expected-error@-1{{static assertion failed due to requirement '(const X<int>[0]){} == nullptr'}}
static_assert(sizeof(X<decltype(X<typename T::T>().X<typename T::T>::~X())>) == 0);
- // expected-error@-1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}}
+ // expected-error@-1{{static assertion failed due to requirement 'sizeof(X<void>) == 0'}} \
+ // expected-note@-1 {{evaluates to 8}}
static_assert(constexpr_return_false<typename T::T, typename T::U>());
// expected-error@-1{{static assertion 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
@@ -1913,11 +1913,13 @@
// cxx11-error@-1 {{not an integral constant expression}}
// cxx11-note@-2 {{call to virtual function}}
// cxx20_2b-error@-3 {{static assertion failed}}
+ // cxx20_2b-note@-4 {{8 == 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 assertion failed}}
+ static_assert(q->f() == sizeof(S2), ""); // cxx20_2b-error {{static assertion failed}} \
+ // cxx20_2b-note {{16 == 8}}
}
namespace ConstexprConstructorRecovery {
Index: clang/test/Sema/static-assert.c
===================================================================
--- clang/test/Sema/static-assert.c
+++ clang/test/Sema/static-assert.c
@@ -55,6 +55,7 @@
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 assertion failed due to requirement 'sizeof(char) == sizeof(short)': type size mismatch}} \
+ // expected-note{{evaluates to 1 == 2}} \
// ext-warning 3 {{'_Static_assert' is a C11 extension}}
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,8 @@
static_assert(a, ""); // expected-error {{static assertion expression is not an integral constant expression}}
static_assert(sizeof(a) == 4, "");
- static_assert(sizeof(a) == 3, ""); // expected-error {{static assertion failed}}
+ static_assert(sizeof(a) == 3, ""); // expected-error {{static assertion failed}} \
+ // expected-note {{evaluates to 4}}
}
static_assert(1, "");
@@ -40,7 +41,8 @@
static_assert(1, "");
_Static_assert(1, "");
static_assert(sizeof(b) == 4, "");
- static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}}
+ static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \
+ // expected-note {{evaluates to 4}}
}
static_assert(1, "");
@@ -56,7 +58,8 @@
@interface B () {
int b;
static_assert(sizeof(b) == 4, "");
- static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}}
+ static_assert(sizeof(b) == 3, ""); // expected-error {{static assertion failed}} \
+ // expected-note {{evaluates to 4}}
}
@end
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/Lexer/cxx1z-trigraphs.cpp
===================================================================
--- clang/test/Lexer/cxx1z-trigraphs.cpp
+++ clang/test/Lexer/cxx1z-trigraphs.cpp
@@ -21,7 +21,7 @@
#if !ENABLED_TRIGRAPHS
// expected-error@11 {{}} expected-warning@11 {{trigraph ignored}}
-// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}}
+// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}} expected-note@13 {{evaluates to '?'}}
// expected-error@16 {{}}
// expected-error@20 {{}}
#else
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/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,8 @@
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 {{evaluates to 12}}
}
void g() { f<int[3]>(); } // expected-note {{in instantiation of}}
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -16554,6 +16554,166 @@
AssertMessage, RParenLoc, false);
}
+/// Convert \V to a string we can present to the user in a diagnostic
+/// \T is the type of the expression that has been evaluated into \V
+static bool ConvertAPValueToString(const APValue &V, QualType T,
+ SmallVectorImpl<char> &Str) {
+ if (!V.hasValue())
+ return false;
+
+ switch (V.getKind()) {
+ case APValue::ValueKind::Int:
+ if (T->isBooleanType()) {
+ // Bools are reduced to ints during evaluation, but for
+ // diagnostic purposes we want to print them as
+ // true or false.
+ int64_t BoolValue = V.getInt().getExtValue();
+ assert((BoolValue == 0 || BoolValue == 1) &&
+ "Bool type, but value is not 0 or 1");
+ if (BoolValue) {
+ Str.push_back('t');
+ Str.push_back('r');
+ Str.push_back('u');
+ Str.push_back('e');
+ } else {
+ Str.push_back('f');
+ Str.push_back('a');
+ Str.push_back('l');
+ Str.push_back('s');
+ Str.push_back('e');
+ }
+ } else if (T->isCharType()) {
+ // Same is true for chars.
+ Str.push_back('\'');
+ Str.push_back(V.getInt().getExtValue());
+ Str.push_back('\'');
+ } else
+ V.getInt().toString(Str);
+
+ break;
+
+ case APValue::ValueKind::Float:
+ V.getFloat().toString(Str);
+ break;
+
+ case APValue::ValueKind::LValue:
+ if (V.isNullPointer()) {
+ Str.push_back('n');
+ Str.push_back('u');
+ Str.push_back('l');
+ Str.push_back('l');
+ Str.push_back('p');
+ Str.push_back('t');
+ Str.push_back('r');
+ } else
+ return false;
+ break;
+
+ case APValue::ValueKind::ComplexFloat:
+ Str.push_back('(');
+ V.getComplexFloatReal().toString(Str);
+ Str.push_back(' ');
+ Str.push_back('+');
+ Str.push_back(' ');
+ V.getComplexFloatImag().toString(Str);
+ Str.push_back('i');
+ Str.push_back(')');
+ break;
+
+ case APValue::ValueKind::ComplexInt:
+ Str.push_back('(');
+ V.getComplexIntReal().toString(Str);
+ Str.push_back(' ');
+ Str.push_back('+');
+ Str.push_back(' ');
+ V.getComplexIntImag().toString(Str);
+ Str.push_back('i');
+ Str.push_back(')');
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/// Some Expression types are not useful to print notes about,
+/// e.g. literals and values that have already been expanded
+/// before such as int-valued template parameters.
+static bool UsefulToPrintExpr(const Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ // Literals are pretty easy for humans to understand.
+ if (isa<IntegerLiteral>(E) || isa<FloatingLiteral>(E) ||
+ isa<CharacterLiteral>(E) || isa<CXXBoolLiteralExpr>(E) ||
+ isa<CXXNullPtrLiteralExpr>(E))
+ return false;
+
+ // These have been substituted from template parameters
+ // and appear as literals in the static assert error.
+ if (isa<SubstNonTypeTemplateParmExpr>(E))
+ return false;
+
+ // -5 is also simple to understand.
+ if (const UnaryOperator *UnaryOp = dyn_cast_or_null<UnaryOperator>(E))
+ return UsefulToPrintExpr(UnaryOp->getSubExpr());
+
+ // Ignore nested binary operators. This could be a FIXME for improvements
+ // to the diagnostics in the future.
+ if (isa<BinaryOperator>(E))
+ return false;
+
+ return true;
+}
+
+/// 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)) {
+ const Expr *LHS = Op->getLHS()->IgnoreParenImpCasts();
+ const Expr *RHS = Op->getRHS()->IgnoreParenImpCasts();
+
+ // Ignore comparisons of boolean expressions with a boolean literal.
+ if ((isa<CXXBoolLiteralExpr>(LHS) && RHS->getType()->isBooleanType()) ||
+ (isa<CXXBoolLiteralExpr>(RHS) && LHS->getType()->isBooleanType()))
+ return;
+
+ struct {
+ const Expr *Expr;
+ Expr::EvalResult Result;
+ SmallString<12> ValueString;
+ bool Print;
+ } DiagSide[2] = {{LHS, Expr::EvalResult(), {}, false},
+ {RHS, Expr::EvalResult(), {}, false}};
+ for (unsigned I = 0; I < 2; I++) {
+ const Expr *Side = DiagSide[I].Expr;
+
+ if (!UsefulToPrintExpr(Side))
+ continue;
+
+ Side->EvaluateAsRValue(DiagSide[I].Result, Context, true);
+
+ DiagSide[I].Print = ConvertAPValueToString(
+ DiagSide[I].Result.Val, Side->getType(), DiagSide[I].ValueString);
+ }
+ 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();
+ } else if (DiagSide[0].Print) {
+ // Only print LHS.
+ Diag(DiagSide[0].Expr->getExprLoc(), diag::note_bin_op_evaluates_to)
+ << 0 << Op->getOpcodeStr() << DiagSide[0].ValueString
+ << DiagSide[0].Expr->getSourceRange();
+ } else if (DiagSide[1].Print) {
+ // Only print RHS.
+ Diag(DiagSide[1].Expr->getExprLoc(), diag::note_bin_op_evaluates_to)
+ << 1 << Op->getOpcodeStr() << DiagSide[1].ValueString
+ << DiagSide[1].Expr->getSourceRange();
+ }
+ }
+}
+
Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
Expr *AssertExpr,
StringLiteral *AssertMessage,
@@ -16612,6 +16772,7 @@
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
@@ -7479,6 +7479,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,10 @@
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_to : Note<
+ "%select{left-hand|right-hand}0 side of operator %1 evaluates to %2">;
+def note_expr_evaluates_to : Note<
+ "expression evaluates to %0 %1 %2">;
def warn_consteval_if_always_true : Warning<
"consteval if is always true in an %select{unevaluated|immediate}0 context">,
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -64,6 +64,9 @@
enum without a fixed underlying type is set to a value outside the range of
the enumeration's values. Fixes
`Issue 50055: <https://github.com/llvm/llvm-project/issues/50055>`_.
+- Clang will now print more information about failed static assertions. In
+ particular, simple static assertion expressions are evaluated to their
+ compile-time value and printed out if the assertion fails.
Non-comprehensive list of changes in this release
-------------------------------------------------
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits