[clang] [clang] add `-fimplicit-constexpr` flag (PR #136436)
Hana Dusíková via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 19 15:33:06 PDT 2025
https://github.com/hanickadot updated https://github.com/llvm/llvm-project/pull/136436
>From f37eb75b71d2aa6433bc528b7b4532138ae4e62a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sun, 20 Apr 2025 00:24:46 +0200
Subject: [PATCH 1/2] [clang] add -fimplicit-constexpr flag (with same
behaviour as GCC)
---
clang/include/clang/AST/Decl.h | 3 +
.../include/clang/Basic/DiagnosticASTKinds.td | 2 +
clang/include/clang/Basic/Features.def | 1 +
clang/include/clang/Basic/LangOptions.def | 1 +
clang/include/clang/Driver/Options.td | 6 +
clang/lib/AST/Decl.cpp | 21 ++++
clang/lib/AST/ExprConstant.cpp | 24 +++-
clang/lib/Driver/ToolChains/Clang.cpp | 3 +
clang/lib/Frontend/InitPreprocessor.cpp | 3 +
clang/lib/Sema/SemaExpr.cpp | 6 +-
clang/test/Sema/implicit-constexpr-basic.cpp | 112 ++++++++++++++++++
.../test/Sema/implicit-constexpr-features.cpp | 81 +++++++++++++
.../test/Sema/implicit-constexpr-members.cpp | 83 +++++++++++++
13 files changed, 339 insertions(+), 7 deletions(-)
create mode 100644 clang/test/Sema/implicit-constexpr-basic.cpp
create mode 100644 clang/test/Sema/implicit-constexpr-features.cpp
create mode 100644 clang/test/Sema/implicit-constexpr-members.cpp
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 3faf63e395a08..be37cb115b24b 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -2418,6 +2418,9 @@ class FunctionDecl : public DeclaratorDecl,
bool isConstexpr() const {
return getConstexprKind() != ConstexprSpecKind::Unspecified;
}
+ /// Support for `-fimplicit-constexpr`
+ bool isConstexprOrImplicitlyCanBe(const LangOptions &LangOpts,
+ bool MustBeInlined = true) const;
void setConstexprKind(ConstexprSpecKind CSK) {
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(CSK);
}
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index f73963752bb67..57d58a735db85 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -32,6 +32,8 @@ def note_constexpr_lshift_discards : Note<"signed left shift discards bits">;
def note_constexpr_invalid_function : Note<
"%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot "
"be used in a constant expression">;
+def note_constexpr_implicit_constexpr_must_be_inlined
+ : Note<"non-inline function %0 is not implicitly constexpr">;
def note_constexpr_invalid_inhctor : Note<
"constructor inherited from base class %0 cannot be used in a "
"constant expression; derived class cannot be implicitly initialized">;
diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index 14bff8a68846d..2fe9ed3419ee9 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -316,6 +316,7 @@ EXTENSION(matrix_types, LangOpts.MatrixTypes)
EXTENSION(matrix_types_scalar_division, true)
EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11)
EXTENSION(datasizeof, LangOpts.CPlusPlus)
+EXTENSION(cxx_implicit_constexpr, LangOpts.ImplicitConstexpr)
FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables)
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 930c1c06d1a76..97a271b200976 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -413,6 +413,7 @@ BENIGN_LANGOPT(ArrowDepth, 32, 256,
"maximum number of operator->s to follow")
BENIGN_LANGOPT(InstantiationDepth, 32, 1024,
"maximum template instantiation depth")
+COMPATIBLE_LANGOPT(ImplicitConstexpr, 1, 0, "make functions implicitly 'constexpr'")
BENIGN_LANGOPT(ConstexprCallDepth, 32, 512,
"maximum constexpr call depth")
BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576,
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 919c1c643d080..877235147a044 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1991,6 +1991,12 @@ defm constant_cfstrings : BoolFOption<"constant-cfstrings",
"Disable creation of CodeFoundation-type constant strings">,
PosFlag<SetFalse>>;
def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group<f_Group>;
+def fimplicit_constexpr
+ : Joined<["-"], "fimplicit-constexpr">,
+ Group<f_Group>,
+ Visibility<[ClangOption, CC1Option]>,
+ HelpText<"All function declarations will be implicitly constexpr.">,
+ MarshallingInfoFlag<LangOpts<"ImplicitConstexpr">>;
def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group<f_Group>,
Visibility<[ClangOption, CC1Option]>,
HelpText<"Set the maximum depth of recursive constexpr function calls">,
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index ad1cb01592e9b..1a595dfc3daeb 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -3242,6 +3242,27 @@ bool FunctionDecl::isDefined(const FunctionDecl *&Definition,
return false;
}
+bool FunctionDecl::isConstexprOrImplicitlyCanBe(const LangOptions &LangOpts,
+ bool MustBeInlined) const {
+ if (isConstexpr())
+ return true;
+
+ if (!LangOpts.ImplicitConstexpr)
+ return false;
+
+ // Constexpr function in C++11 couldn't contain anything other then return
+ // expression. It wouldn't make sense to allow it (GCC doesn't do it neither).
+ if (!LangOpts.CPlusPlus14)
+ return false;
+
+ // Free functions must be inlined, but sometimes we want to skip this check.
+ // And in order to keep logic on one place, the check is here.
+ if (MustBeInlined)
+ return isInlined();
+
+ return true;
+}
+
Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
if (!hasBody(Definition))
return nullptr;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index b14ff21a8ebc2..6cfb67ad1d719 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5968,8 +5968,9 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
// Can we evaluate this function call?
if (Definition && Body &&
- (Definition->isConstexpr() || (Info.CurrentCall->CanEvalMSConstexpr &&
- Definition->hasAttr<MSConstexprAttr>())))
+ (Definition->isConstexprOrImplicitlyCanBe(Info.Ctx.getLangOpts()) ||
+ (Info.CurrentCall->CanEvalMSConstexpr &&
+ Definition->hasAttr<MSConstexprAttr>())))
return true;
if (Info.getLangOpts().CPlusPlus11) {
@@ -5987,12 +5988,25 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
// FIXME: If DiagDecl is an implicitly-declared special member function
// or an inheriting constructor, we should be much more explicit about why
// it's not constexpr.
- if (CD && CD->isInheritingConstructor())
+ if (CD && CD->isInheritingConstructor()) {
Info.FFDiag(CallLoc, diag::note_constexpr_invalid_inhctor, 1)
<< CD->getInheritedConstructor().getConstructor()->getParent();
- else
+
+ } else if (Definition && !DiagDecl->isInlined() &&
+ Info.Ctx.getLangOpts().ImplicitConstexpr) {
+ Info.FFDiag(CallLoc,
+ diag::note_constexpr_implicit_constexpr_must_be_inlined)
+ << DiagDecl;
+
+ } else {
+ // Using implicit constexpr check here, so we see a missing body as main
+ // problem and not missing constexpr with -fimplicit-constexpr.
Info.FFDiag(CallLoc, diag::note_constexpr_invalid_function, 1)
- << DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
+ << DiagDecl->isConstexprOrImplicitlyCanBe(Info.Ctx.getLangOpts(),
+ false)
+ << (bool)CD << DiagDecl;
+ }
+
Info.Note(DiagDecl->getLocation(), diag::note_declared_at);
} else {
Info.FFDiag(CallLoc, diag::note_invalid_subexpr_in_const_expr);
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f2f5231933c88..e33d0b72ed04d 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6612,6 +6612,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ);
+ if (types::isCXX(InputType))
+ Args.AddLastArg(CmdArgs, options::OPT_fimplicit_constexpr);
+
Args.AddLastArg(CmdArgs, options::OPT_fexperimental_library);
if (Args.hasArg(options::OPT_fexperimental_new_constant_interpreter))
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 1f297f228fc1b..f0fdf81272fb3 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -779,6 +779,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
// TODO: Final number?
Builder.defineMacro("__cpp_type_aware_allocators", "202500L");
+
+ if (LangOpts.ImplicitConstexpr) // same value as GCC
+ Builder.defineMacro("__cpp_implicit_constexpr", "20211111");
}
/// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 01a021443c94f..82c2c61363e3b 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18384,16 +18384,18 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
}
if (FirstInstantiation || TSK != TSK_ImplicitInstantiation ||
- Func->isConstexpr()) {
+ Func->isConstexprOrImplicitlyCanBe(getLangOpts())) {
if (isa<CXXRecordDecl>(Func->getDeclContext()) &&
cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() &&
CodeSynthesisContexts.size())
PendingLocalImplicitInstantiations.push_back(
std::make_pair(Func, PointOfInstantiation));
- else if (Func->isConstexpr())
+ else if (Func->isConstexprOrImplicitlyCanBe(getLangOpts()))
// Do not defer instantiations of constexpr functions, to avoid the
// expression evaluator needing to call back into Sema if it sees a
// call to such a function.
+ // (When -fimplicit-instantiation is enabled, all functions are
+ // implicitly constexpr)
InstantiateFunctionDefinition(PointOfInstantiation, Func);
else {
Func->setInstantiationIsPending(true);
diff --git a/clang/test/Sema/implicit-constexpr-basic.cpp b/clang/test/Sema/implicit-constexpr-basic.cpp
new file mode 100644
index 0000000000000..9f17a344125dc
--- /dev/null
+++ b/clang/test/Sema/implicit-constexpr-basic.cpp
@@ -0,0 +1,112 @@
+// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL14,BOTH14,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++14 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_PRE20,BOTH14,ALL_PRE20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++14 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL17,BOTH17,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++17 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_PRE20,BOTH17,ALL_PRE20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++17 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL20,BOTH20,ALLNORMAL,ALL -std=c++20 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++20 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL23,BOTH23,ALLNORMAL,ALL -std=c++23 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++23 %s -fcolor-diagnostics
+
+
+
+
+// =============================================
+// 1) simple uninlined function
+
+bool noinline_fnc() {
+// ALL-note at -1 {{declared here}}
+ return true;
+}
+
+constexpr bool result_noinline_fnc = noinline_fnc();
+// ALL-error at -1 {{constexpr variable 'result_noinline_fnc' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'noinline_fnc' cannot be used in a constant expression}}
+// ALLIMPLICIT-note at -3 {{non-inline function 'noinline_fnc' is not implicitly constexpr}}
+
+
+// =============================================
+// 2) simple inlined function
+
+inline bool inline_fnc() {
+// ALLNORMAL-note at -1 {{declared here}}
+ return true;
+}
+
+constexpr bool result_inline_fnc = inline_fnc();
+// ALLNORMAL-error at -1 {{constexpr variable 'result_inline_fnc' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'inline_fnc' cannot be used in a constant expression}}
+
+
+// =============================================
+// 3) undefined uninlined function
+
+bool noinline_undefined_fnc();
+// ALL-note at -1 {{declared here}}
+
+constexpr bool result_noinline_undefined_fnc = noinline_undefined_fnc();
+// ALL-error at -1 {{constexpr variable 'result_noinline_undefined_fnc' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'noinline_undefined_fnc' cannot be used in a constant expression}}
+// ALLIMPLICIT-note at -3 {{undefined function 'noinline_undefined_fnc' cannot be used in a constant expression}}
+
+
+// =============================================
+// 4) undefined inline function
+
+inline bool inline_undefined_fnc();
+// ALL-note at -1 {{declared here}}
+
+constexpr bool result_inline_undefined_fnc = inline_undefined_fnc();
+// ALL-error at -1 {{constexpr variable 'result_inline_undefined_fnc' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'inline_undefined_fnc' cannot be used in a constant expression}}
+// ALLIMPLICIT-note at -3 {{undefined function 'inline_undefined_fnc' cannot be used in a constant expression}}
+
+// =============================================
+// 5) lambda function
+
+auto lambda = [](int x) { return x > 0; };
+// NORMAL14-note at -1 {{declared here}}
+
+constexpr bool result_lambda = lambda(10);
+// NORMAL14-error at -1 {{constexpr variable 'result_lambda' must be initialized by a constant expression}}
+// NORMAL14-note at -2 {{non-constexpr function 'operator()' cannot be used in a constant expression}}
+
+
+// =============================================
+// 6) virtual functions
+
+struct type {
+ virtual bool dispatch() const noexcept {
+ return false;
+ }
+};
+
+struct child_of_type: type {
+ bool dispatch() const noexcept override {
+// NORMAL20-note at -1 {{declared here}}
+// NORMAL23-note at -2 {{declared here}}
+ return true;
+ }
+};
+
+constexpr bool result_virtual = static_cast<const type &>(child_of_type{}).dispatch();
+// ALL_NORMAL-error at -1 {{constexpr variable 'result_virtual' must be initialized by a constant expression}}
+// NORMAL_PRE20-note at -2 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}}
+// IMPLICIT_PRE20-error at -3 {{constexpr variable 'result_virtual' must be initialized by a constant expression}}
+// IMPLICIT_PRE20-note at -4 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}}
+// NORMAL20-note at -5 {{non-constexpr function 'dispatch' cannot be used in a constant expression}}
+// NORMAL20-note at -6 {{declared here}}
+// NORMAL23-note at -7 {{non-constexpr function 'dispatch' cannot be used in a constant expression}}
+// NORMAL23-note at -8 {{declared here}}
+
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L
+static_assert(result_virtual == true, "virtual should work");
+// ALL_NORMAL-error at -1 {{static assertion expression is not an integral constant expression}}
+// ALL_NORMAL-note at -2 {{initializer of 'result_virtual' is not a constant expression}}
+// IMPLICIT_PRE20-note at -3 {{initializer of 'result_virtual' is not a constant expression}}
+#endif
+
+
diff --git a/clang/test/Sema/implicit-constexpr-features.cpp b/clang/test/Sema/implicit-constexpr-features.cpp
new file mode 100644
index 0000000000000..ab8a50ecdb147
--- /dev/null
+++ b/clang/test/Sema/implicit-constexpr-features.cpp
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -verify=NORMAL14,NORMAL_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
+// RUN: %clang_cc1 -verify=NORMAL17,NORMAL_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
+// RUN: %clang_cc1 -verify=NORMAL20,NORMAL_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
+// RUN: %clang_cc1 -verify=NORMAL23,NORMAL_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
+// RUN: %clang_cc1 -verify=NORMAL26,NORMAL_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
+
+// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
+// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
+// RUN: %clang_cc1 -verify=IMPLICIT20,IMPLICIT_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
+// RUN: %clang_cc1 -verify=IMPLICIT23,IMPLICIT_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
+// RUN: %clang_cc1 -verify=IMPLICIT26,IMPLICIT_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
+
+// RUN: %clang_cc1 -verify=CONSTEXPR14,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
+// RUN: %clang_cc1 -verify=CONSTEXPR17,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
+// RUN: %clang_cc1 -verify=CONSTEXPR20,CONSTEXPR_BEFORE23,CONSTEXPR_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
+// RUN: %clang_cc1 -verify=CONSTEXPR23,CONSTEXPR_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
+// RUN: %clang_cc1 -verify=CONSTEXPR26,CONSTEXPR_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
+
+// Objective is to make sure features like allocation / throwing won't fail code by just adding implicit constexpr
+// in an unevaluated code.
+
+// NORMAL_ALL-no-diagnostics
+// IMPLICIT_ALL-no-diagnostics
+// CONSTEXPR23-no-diagnostics
+// CONSTEXPR26-no-diagnostics
+
+CONSTEXPR inline bool function_with_goto(int v) {
+ if (v == 0) {
+ return true;
+ }
+
+ goto label;
+ // CONSTEXPR_BEFORE23-warning at -1 {{use of this statement in a constexpr function is a C++23 extension}}
+
+ label:
+ return false;
+}
+
+CONSTEXPR inline bool function_with_label(int v) {
+ label:
+ // CONSTEXPR_BEFORE23-warning at -1 {{use of this statement in a constexpr function is a C++23 extension}}
+ if (v > 0) {
+ return true;
+ }
+ v++;
+ goto label;
+}
+
+CONSTEXPR inline bool function_with_try_catch(int v) {
+ try {
+ // CONSTEXPR_BEFORE20-warning at -1 {{use of this statement in a constexpr function is a C++20 extension}}
+ return v;
+ } catch (int) {
+ return -v;
+ }
+}
+
+CONSTEXPR inline bool function_with_inline_asm(int v) {
+ if (v > 0) {
+ asm("");
+ // CONSTEXPR_BEFORE20-warning at -1 {{use of this statement in a constexpr function is a C++20 extension}}
+ }
+
+ return v;
+}
+
+struct easy_type {
+ // CONSTEXPR_BEFORE20-note at -1 {{declared here}}
+ int * x;
+};
+
+CONSTEXPR inline bool function_with_no_initializer_variable(int v) {
+ // CONSTEXPR_BEFORE20-error at -1 {{constexpr function never produces a constant expression}}
+ easy_type easy;
+ // CONSTEXPR_BEFORE20-note at -1 {{non-constexpr constructor 'easy_type' cannot be used in a constant expression}}
+ return v;
+}
+
+
+
+
diff --git a/clang/test/Sema/implicit-constexpr-members.cpp b/clang/test/Sema/implicit-constexpr-members.cpp
new file mode 100644
index 0000000000000..c68df52677dff
--- /dev/null
+++ b/clang/test/Sema/implicit-constexpr-members.cpp
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 -verify=NORMAL14,BOTH14,ALLNORMAL,ALL -std=c++14 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT14,BOTH14,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++14 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=NORMAL17,BOTH20,ALLNORMAL,ALL -std=c++17 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT17,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++17 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=NORMAL20,BOTH20,ALLNORMAL,ALL -std=c++20 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++20 %s -fcolor-diagnostics
+
+// RUN: %clang_cc1 -verify=NORMAL23,BOTH23,ALLNORMAL,ALL -std=c++23 %s -fcolor-diagnostics
+// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++23 %s -fcolor-diagnostics
+
+
+// =============================================
+// 1) simple member function
+
+struct simple_type {
+ bool test() const {
+ // ALLNORMAL-note at -1 {{declared here}}
+ return true;
+ }
+};
+
+constexpr bool result_simple_type = simple_type{}.test();
+// ALLNORMAL-error at -1 {{constexpr variable 'result_simple_type' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'test' cannot be used in a constant expression}}
+
+#ifdef __cpp_implicit_constexpr
+static_assert(result_simple_type == true, "simple member function must work");
+#endif
+
+
+// =============================================
+// 2) simple member function inside a template
+
+template <typename T> struct template_type {
+ bool test() const {
+ // ALLNORMAL-note at -1 {{declared here}}
+ return true;
+ }
+};
+
+constexpr bool result_template_type = template_type<int>{}.test();
+// ALLNORMAL-error at -1 {{constexpr variable 'result_template_type' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'test' cannot be used in a constant expression}}
+
+
+// =============================================
+// 3) template member function inside a template
+
+template <typename T> struct template_template_type {
+ template <typename Y> bool test() const {
+ // ALLNORMAL-note at -1 {{declared here}}
+ return true;
+ }
+};
+
+constexpr bool result_template_template_type = template_template_type<int>{}.template test<long>();
+// ALLNORMAL-error at -1 {{constexpr variable 'result_template_template_type' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'test<long>' cannot be used in a constant expression}}
+
+
+#if defined(__cpp_explicit_this_parameter) && __cpp_explicit_this_parameter >= 202110L
+
+// =============================================
+// 3) explicit "this" function
+
+struct explicit_this {
+ template <typename Self> bool test(this const Self & self) const {
+ // ALLNORMAL-note at -1 {{declared here}}
+ return self.ok;
+ }
+};
+
+struct child: explicit_this {
+ static constexpr bool ok = true;
+};
+
+constexpr bool result_explicit_this = child{}.test();
+// ALLNORMAL-error at -1 {{constexpr variable 'result_explicit_this' must be initialized by a constant expression}}
+// ALLNORMAL-note at -2 {{non-constexpr function 'test' cannot be used in a constant expression}}
+
+#endif
>From 426c4f754f887dd7a4e2b93eacfe1255e2351ce8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sun, 20 Apr 2025 00:33:05 +0200
Subject: [PATCH 2/2] [clang] `-fimplicit-constexpr` release notes.
---
clang/docs/ReleaseNotes.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5cd1fbeabcfe..062de2d23f038 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -211,6 +211,8 @@ New Compiler Flags
The feature has `existed <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program>`_)
for a while and this is just a user facing option.
+- New option ``-fimplicit-constexpr`` which implicitly makes all inlined functions implicitly ``constexpr``.
+
Deprecated Compiler Flags
-------------------------
More information about the cfe-commits
mailing list