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?= <hani...@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@-1 {{declared here}} + return true; +} + +constexpr bool result_noinline_fnc = noinline_fnc(); +// ALL-error@-1 {{constexpr variable 'result_noinline_fnc' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'noinline_fnc' cannot be used in a constant expression}} +// ALLIMPLICIT-note@-3 {{non-inline function 'noinline_fnc' is not implicitly constexpr}} + + +// ============================================= +// 2) simple inlined function + +inline bool inline_fnc() { +// ALLNORMAL-note@-1 {{declared here}} + return true; +} + +constexpr bool result_inline_fnc = inline_fnc(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_inline_fnc' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'inline_fnc' cannot be used in a constant expression}} + + +// ============================================= +// 3) undefined uninlined function + +bool noinline_undefined_fnc(); +// ALL-note@-1 {{declared here}} + +constexpr bool result_noinline_undefined_fnc = noinline_undefined_fnc(); +// ALL-error@-1 {{constexpr variable 'result_noinline_undefined_fnc' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'noinline_undefined_fnc' cannot be used in a constant expression}} +// ALLIMPLICIT-note@-3 {{undefined function 'noinline_undefined_fnc' cannot be used in a constant expression}} + + +// ============================================= +// 4) undefined inline function + +inline bool inline_undefined_fnc(); +// ALL-note@-1 {{declared here}} + +constexpr bool result_inline_undefined_fnc = inline_undefined_fnc(); +// ALL-error@-1 {{constexpr variable 'result_inline_undefined_fnc' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'inline_undefined_fnc' cannot be used in a constant expression}} +// ALLIMPLICIT-note@-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@-1 {{declared here}} + +constexpr bool result_lambda = lambda(10); +// NORMAL14-error@-1 {{constexpr variable 'result_lambda' must be initialized by a constant expression}} +// NORMAL14-note@-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@-1 {{declared here}} +// NORMAL23-note@-2 {{declared here}} + return true; + } +}; + +constexpr bool result_virtual = static_cast<const type &>(child_of_type{}).dispatch(); +// ALL_NORMAL-error@-1 {{constexpr variable 'result_virtual' must be initialized by a constant expression}} +// NORMAL_PRE20-note@-2 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}} +// IMPLICIT_PRE20-error@-3 {{constexpr variable 'result_virtual' must be initialized by a constant expression}} +// IMPLICIT_PRE20-note@-4 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}} +// NORMAL20-note@-5 {{non-constexpr function 'dispatch' cannot be used in a constant expression}} +// NORMAL20-note@-6 {{declared here}} +// NORMAL23-note@-7 {{non-constexpr function 'dispatch' cannot be used in a constant expression}} +// NORMAL23-note@-8 {{declared here}} + + +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L +static_assert(result_virtual == true, "virtual should work"); +// ALL_NORMAL-error@-1 {{static assertion expression is not an integral constant expression}} +// ALL_NORMAL-note@-2 {{initializer of 'result_virtual' is not a constant expression}} +// IMPLICIT_PRE20-note@-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@-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@-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@-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@-1 {{use of this statement in a constexpr function is a C++20 extension}} + } + + return v; +} + +struct easy_type { + // CONSTEXPR_BEFORE20-note@-1 {{declared here}} + int * x; +}; + +CONSTEXPR inline bool function_with_no_initializer_variable(int v) { + // CONSTEXPR_BEFORE20-error@-1 {{constexpr function never produces a constant expression}} + easy_type easy; + // CONSTEXPR_BEFORE20-note@-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@-1 {{declared here}} + return true; + } +}; + +constexpr bool result_simple_type = simple_type{}.test(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_simple_type' must be initialized by a constant expression}} +// ALLNORMAL-note@-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@-1 {{declared here}} + return true; + } +}; + +constexpr bool result_template_type = template_type<int>{}.test(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_template_type' must be initialized by a constant expression}} +// ALLNORMAL-note@-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@-1 {{declared here}} + return true; + } +}; + +constexpr bool result_template_template_type = template_template_type<int>{}.template test<long>(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_template_template_type' must be initialized by a constant expression}} +// ALLNORMAL-note@-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@-1 {{declared here}} + return self.ok; + } +}; + +struct child: explicit_this { + static constexpr bool ok = true; +}; + +constexpr bool result_explicit_this = child{}.test(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_explicit_this' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'test' cannot be used in a constant expression}} + +#endif From a486edc7562ba44e67a50eee3217c39fd9405d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hani...@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..be8a7e164e529 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 and defined functions ``constexpr``. + Deprecated Compiler Flags ------------------------- _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits