Skip to content

Commit e5f7123

Browse files
authored
Disable constexpr function body checking in more situations (#94347)
Before C++23, we would check a constexpr function body to diagnose if the function can never be evaluated in a constant expression context. This was previously required standards behavior, but C++23 relaxed the restrictions with P2448R2. While this checking is useful, it is also quite expensive, especially in pathological cases (see #92924 for an example), because it means the mere presence of a constexpr function definition will require constant evaluation even if the function is not used within the TU. Clang suppresses diagnostics in system headers by default and system headers (like STL implementations) can be full of constexpr function bodies. Now we suppress the check for a diagnostic if the function definition is in a system header or if the `-Winvalid-constexpr` diagnostic is disabled. This should have some mild compile time performance improvements. Also, the previous implementation would disable the diagnostic in C++23 mode entirely. Due to the benefit of the check, this patch now makes it possible to enable the diagnostic explicitly in C++23 mode.
1 parent 7dc84e2 commit e5f7123

File tree

6 files changed

+80
-4
lines changed

6 files changed

+80
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,9 @@ C++23 Feature Support
207207
- Implemented `P1774R8: Portable assumptions <https://wg21.link/P1774R8>`_.
208208

209209
- Implemented `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/P2448R2>`_.
210+
Note, the ``-Winvalid-constexpr`` diagnostic is now disabled in C++23 mode,
211+
but can be explicitly specified to retain the old diagnostic checking
212+
behavior.
210213

211214
- Added a ``__reference_converts_from_temporary`` builtin, completing the necessary compiler support for
212215
`P2255R2: Type trait to determine if a reference binds to a temporary <https://wg21.link/P2255R2>`_.
@@ -323,6 +326,17 @@ Non-comprehensive list of changes in this release
323326
- Builtins ``__builtin_shufflevector()`` and ``__builtin_convertvector()`` may
324327
now be used within constant expressions.
325328

329+
- When compiling a constexpr function, Clang will check to see whether the
330+
function can *never* be used in a constant expression context and issues a
331+
diagnostic under the ``-Winvalid-constexpr`` diagostic flag (which defaults
332+
to an error). This check can be expensive because the mere presence of a
333+
function marked ``constexpr`` will cause us to undergo constant expression
334+
evaluation, even if the function is not called within the translation unit
335+
being compiled. Due to the expense, Clang no longer checks constexpr function
336+
bodies when the function is defined in a system header file or when
337+
``-Winvalid-constexpr`` is not enabled for the function definition, which
338+
should result in mild compile-time performance improvements.
339+
326340
New Compiler Flags
327341
------------------
328342
- ``-fsanitize=implicit-bitfield-conversion`` checks implicit truncation and

clang/include/clang/Basic/LangOptions.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,14 @@ COMPATIBLE_LANGOPT(IncrementalExtensions, 1, 0, " True if we want to process sta
505505

506506
BENIGN_LANGOPT(CheckNew, 1, 0, "Do not assume C++ operator new may not return NULL")
507507

508+
// FIXME: It would be better for us to find a way to encode the state of this
509+
// diagnostic in tablegen so that we can specify a particular diagnostic option
510+
// is disabled or enabled based on other language options or made it easier to
511+
// do this from the compiler invocation without hitting option round-tripping
512+
// issues.
513+
BENIGN_LANGOPT(CheckConstexprFunctionBodies, 1, 1,
514+
"Emit diagnostics for a constexpr function body that can never "
515+
"be used in a constant expression.")
508516
#undef LANGOPT
509517
#undef COMPATIBLE_LANGOPT
510518
#undef BENIGN_LANGOPT

clang/include/clang/Driver/Options.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,17 @@ multiclass BoolMOption<string flag_base, KeyPathAndMacro kpm,
557557
Group<m_Group>;
558558
}
559559

560+
/// Creates a BoolOption where both of the flags are prefixed with "W", are in
561+
/// the Group<W_Group>.
562+
/// Used for -cc1 frontend options. Driver-only options do not map to
563+
/// CompilerInvocation.
564+
multiclass BoolWOption<string flag_base, KeyPathAndMacro kpm,
565+
Default default, FlagDef flag1, FlagDef flag2,
566+
BothFlags both = BothFlags<[]>> {
567+
defm NAME : BoolOption<"W", flag_base, kpm, default, flag1, flag2, both>,
568+
Group<W_Group>;
569+
}
570+
560571
// Works like BoolOption except without marshalling
561572
multiclass BoolOptionWithoutMarshalling<string prefix = "", string spelling_base,
562573
FlagDef flag1_base, FlagDef flag2_base,
@@ -606,6 +617,7 @@ defvar cpp11 = LangOpts<"CPlusPlus11">;
606617
defvar cpp14 = LangOpts<"CPlusPlus14">;
607618
defvar cpp17 = LangOpts<"CPlusPlus17">;
608619
defvar cpp20 = LangOpts<"CPlusPlus20">;
620+
defvar cpp23 = LangOpts<"CPlusPlus23">;
609621
defvar c99 = LangOpts<"C99">;
610622
defvar c23 = LangOpts<"C23">;
611623
defvar lang_std = LangOpts<"LangStd">;
@@ -961,6 +973,12 @@ def Wdeprecated : Flag<["-"], "Wdeprecated">, Group<W_Group>,
961973
HelpText<"Enable warnings for deprecated constructs and define __DEPRECATED">;
962974
def Wno_deprecated : Flag<["-"], "Wno-deprecated">, Group<W_Group>,
963975
Visibility<[ClangOption, CC1Option]>;
976+
defm invalid_constexpr : BoolWOption<"invalid-constexpr",
977+
LangOpts<"CheckConstexprFunctionBodies">,
978+
Default<!strconcat("!", cpp23.KeyPath)>,
979+
NegFlag<SetFalse, [], [ClangOption, CC1Option], "Disable">,
980+
PosFlag<SetTrue, [], [ClangOption, CC1Option], "Enable">,
981+
BothFlags<[], [ClangOption, CC1Option], " checking of constexpr function bodies for validity within a constant expression context">>;
964982
def Wl_COMMA : CommaJoined<["-"], "Wl,">, Visibility<[ClangOption, FlangOption]>,
965983
Flags<[LinkerInput, RenderAsInput]>,
966984
HelpText<"Pass the comma separated arguments in <arg> to the linker">,

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2407,6 +2407,9 @@ void CompilerInvocationBase::GenerateDiagnosticArgs(
24072407
// This option is automatically generated from UndefPrefixes.
24082408
if (Warning == "undef-prefix")
24092409
continue;
2410+
// This option is automatically generated from CheckConstexprFunctionBodies.
2411+
if (Warning == "invalid-constexpr" || Warning == "no-invalid-constexpr")
2412+
continue;
24102413
Consumer(StringRef("-W") + Warning);
24112414
}
24122415

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,11 +2469,18 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
24692469
// base class sub-objects shall be a constexpr constructor.
24702470
//
24712471
// Note that this rule is distinct from the "requirements for a constexpr
2472-
// function", so is not checked in CheckValid mode.
2472+
// function", so is not checked in CheckValid mode. Because the check for
2473+
// constexpr potential is expensive, skip the check if the diagnostic is
2474+
// disabled, the function is declared in a system header, or we're in C++23
2475+
// or later mode (see https://wg21.link/P2448).
2476+
bool SkipCheck =
2477+
!SemaRef.getLangOpts().CheckConstexprFunctionBodies ||
2478+
SemaRef.getSourceManager().isInSystemHeader(Dcl->getLocation()) ||
2479+
SemaRef.getDiagnostics().isIgnored(
2480+
diag::ext_constexpr_function_never_constant_expr, Dcl->getLocation());
24732481
SmallVector<PartialDiagnosticAt, 8> Diags;
2474-
if (Kind == Sema::CheckConstexprKind::Diagnose &&
2475-
!Expr::isPotentialConstantExpr(Dcl, Diags) &&
2476-
!SemaRef.getLangOpts().CPlusPlus23) {
2482+
if (Kind == Sema::CheckConstexprKind::Diagnose && !SkipCheck &&
2483+
!Expr::isPotentialConstantExpr(Dcl, Diags)) {
24772484
SemaRef.Diag(Dcl->getLocation(),
24782485
diag::ext_constexpr_function_never_constant_expr)
24792486
<< isa<CXXConstructorDecl>(Dcl) << Dcl->isConsteval()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -fcxx-exceptions %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Winvalid-constexpr -verify -fcxx-exceptions %s
3+
// Note: for a diagnostic that defaults to an error, -Wno-foo -Wfoo will
4+
// disable the diagnostic and then re-enable it *as a warning* rather than as
5+
// an error. So we manually enable it as an error again with -Werror to keep
6+
// the diagnostic checks consistent.
7+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Wno-invalid-constexpr -Winvalid-constexpr -Werror=invalid-constexpr -verify -fcxx-exceptions %s
8+
9+
// RUN: %clang_cc1 -fsyntax-only -Wno-invalid-constexpr -verify=good -fcxx-exceptions %s
10+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -verify=good -fcxx-exceptions %s
11+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Wno-invalid-constexpr -verify=good -fcxx-exceptions %s
12+
// RUN: %clang_cc1 -fsyntax-only -std=c++23 -Winvalid-constexpr -Wno-invalid-constexpr -verify=good -fcxx-exceptions %s
13+
// RUN: %clang_cc1 -fsyntax-only -Wno-invalid-constexpr -verify=good -fcxx-exceptions %s
14+
// good-no-diagnostics
15+
16+
constexpr void func() { // expected-error {{constexpr function never produces a constant expression}}
17+
throw 12; // expected-note {{subexpression not valid in a constant expression}}
18+
}
19+
20+
#pragma clang diagnostic push
21+
#pragma clang diagnostic ignored "-Winvalid-constexpr"
22+
constexpr void other_func() {
23+
#pragma clang diagnostic pop
24+
25+
throw 12;
26+
}

0 commit comments

Comments
 (0)