Skip to content

[Clang][Sema] Print more static_assert exprs #74852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,37 @@ Improvements to Clang's diagnostics
operands, distinguishing it from potential typographical errors or unintended
bitwise operations. Fixes #GH77601.

- Clang will now print ``static_assert`` failure details for binary operators on
structs, vectors, or arrays. The diagnostic is limited in size (the limit may
be adjusted with `-fconstexpr-print-value-size-limit=N`), and is not emitted
when it would be redundant with the existing "requirement" diagnostic.
Example:

.. code-block:: cpp

struct S {
int a, b;
bool operator==(const S &) const = default;
};

static_assert(S{1, 2} == S{3, 4});
constexpr auto f = [] { return S{3, 4}; };
static_assert(S{1, 2} == f());

will now print:

.. code-block:: text

error: static assertion failed due to requirement 'S{1, 2} == S{3, 4}'
85 | static_assert(S{1, 2} == S{3, 4});
| ^~~~~~~~~~~~~~~~~~
error: static assertion failed due to requirement 'S{1, 2} == f()'
87 | static_assert(S{1, 2} == f());
| ^~~~~~~~~~~~~~
note: expression evaluates to 'S{1, 2} == S{3, 4}'
87 | static_assert(S{1, 2} == f());
| ~~~~~~~~^~~~~~

Improvements to Clang's time-trace
----------------------------------

Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/Basic/Diagnostic.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
// Cap on depth of constexpr evaluation backtrace stack, 0 -> no limit.
unsigned ConstexprBacktraceLimit = 0;

// Cap on size of value in a constexpr diagnostic without ellipsis, 0 -> no
// limit.
unsigned ConstexprValueSizeLimit = 0;

IntrusiveRefCntPtr<DiagnosticIDs> Diags;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
DiagnosticConsumer *Client = nullptr;
Expand Down Expand Up @@ -644,6 +648,16 @@ class DiagnosticsEngine : public RefCountedBase<DiagnosticsEngine> {
return ConstexprBacktraceLimit;
}

/// Specify the maximum size of a value to print without ellipsis.
void setConstexprValueSizeLimit(unsigned Limit) {
ConstexprValueSizeLimit = Limit;
}

/// Retrieve the maximum size of a value to print without ellipsis.
unsigned getConstexprValueSizeLimit() const {
return ConstexprValueSizeLimit;
}

/// When set to true, any unmapped warnings are ignored.
///
/// If this and WarningsAsErrors are both set, then this one wins.
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ VALUE_DIAGOPT(MacroBacktraceLimit, 32, DefaultMacroBacktraceLimit)
VALUE_DIAGOPT(TemplateBacktraceLimit, 32, DefaultTemplateBacktraceLimit)
/// Limit depth of constexpr backtrace.
VALUE_DIAGOPT(ConstexprBacktraceLimit, 32, DefaultConstexprBacktraceLimit)
/// Limit size of constexpr diagnostic value.
VALUE_DIAGOPT(ConstexprValueSizeLimit, 32, DefaultConstexprValueSizeLimit)
/// Limit number of times to perform spell checking.
VALUE_DIAGOPT(SpellCheckingLimit, 32, DefaultSpellCheckingLimit)
/// Limit number of lines shown in a snippet.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DiagnosticOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class DiagnosticOptions : public RefCountedBase<DiagnosticOptions>{
DefaultMacroBacktraceLimit = 6,
DefaultTemplateBacktraceLimit = 10,
DefaultConstexprBacktraceLimit = 10,
DefaultConstexprValueSizeLimit = 320,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

320?

DefaultSpellCheckingLimit = 50,
DefaultSnippetLineLimit = 16,
DefaultShowLineNumbers = 1,
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,10 @@ def fconstexpr_backtrace_limit_EQ : Joined<["-"], "fconstexpr-backtrace-limit=">
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set the maximum number of entries to print in a constexpr evaluation backtrace (0 = no limit)">,
MarshallingInfoInt<DiagnosticOpts<"ConstexprBacktraceLimit">, "DiagnosticOptions::DefaultConstexprBacktraceLimit">;
def fconstexpr_print_value_size_limit_EQ : Joined<["-"], "fconstexpr-print-value-size-limit=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set the maximum size of a value in a constexpr diagnostic to be printed without ellipsis (0 = no limit)">,
MarshallingInfoInt<DiagnosticOpts<"ConstexprValueSizeLimit">, "DiagnosticOptions::DefaultConstexprValueSizeLimit">;
def fcrash_diagnostics_EQ : Joined<["-"], "fcrash-diagnostics=">, Group<f_clang_Group>,
Flags<[NoArgumentUnused]>, Visibility<[ClangOption, CLOption, DXCOption]>,
HelpText<"Set level of crash diagnostic reporting, (option: off, compiler, all)">;
Expand Down
50 changes: 47 additions & 3 deletions clang/lib/AST/APValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
Expand Down Expand Up @@ -711,11 +712,54 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy,
case APValue::Indeterminate:
Out << "<uninitialized>";
return;
case APValue::Int:
case APValue::Int: {
const APSInt &Val = getInt();
if (const EnumType *ET = Ty->getAs<EnumType>()) {
// 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);
Comment on lines +722 to +725
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

return;
}
}
}

// otherwise, we print it as a cast from `Val`
if (ET->hasUnnamedOrLocalType()) {
// e.g. `(unnamed enum at ...)7`, unless...
if (const EnumDecl *Defn = ET->getDecl()->getDefinition()) {
// ... can identify the defn somehow
if (const IdentifierInfo *II = Defn->getIdentifier())
Out << '(' << II->getName() << ')';
else if (const TypedefNameDecl *Typedef =
Defn->getTypedefNameForAnonDecl()) {
assert(Typedef->getIdentifier() && "Typedef without identifier?");
Out << '(' << Typedef->getIdentifier()->getName() << ')';
} else if (const NamedDecl *ND = dyn_cast_if_present<NamedDecl>(
Defn->getNextDeclInContext())) {
// if it's part of a declaration, then use `(decltype(...))7`
Out << "(decltype(";
ND->printQualifiedName(Out);
Out << "))";
Comment on lines +742 to +747
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it really make sense to support the unnamed case?

} else
Defn->printQualifiedName(Out);
} else
ET->getDecl()->printQualifiedName(Out);
} else {
// e.g. `(E)7` for some `enum E {};`
Out << '(' << Ty << ')';
}
}

if (Ty->isBooleanType())
Out << (getInt().getBoolValue() ? "true" : "false");
Out << (Val.getBoolValue() ? "true" : "false");
else
Out << getInt();
Out << Val;
}
return;
case APValue::Float:
Out << GetApproxValue(getFloat());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/Warnings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ void clang::ProcessWarningOptions(DiagnosticsEngine &Diags,
Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit);
if (Opts.ConstexprBacktraceLimit)
Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit);
if (Opts.ConstexprValueSizeLimit)
Diags.setConstexprValueSizeLimit(Opts.ConstexprValueSizeLimit);

// If -pedantic or -pedantic-errors was specified, then we want to map all
// extension diagnostics onto WARNING or ERROR unless the user has futz'd
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6325,6 +6325,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("19");

Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_backtrace_limit_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_print_value_size_limit_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fmacro_backtrace_limit_EQ);
Args.AddLastArg(CmdArgs, options::OPT_ftemplate_backtrace_limit_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fspell_checking_limit_EQ);
Expand Down
Loading