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 `_) 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 ------------------------- 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(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>; def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group; +def fimplicit_constexpr + : Joined<["-"], "fimplicit-constexpr">, + Group, + Visibility<[ClangOption, CC1Option]>, + HelpText<"All function declarations will be implicitly constexpr.">, + MarshallingInfoFlag>; def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group, Visibility<[ClangOption, CC1Option]>, HelpText<"Set the maximum depth of recursive constexpr function calls">, diff --git a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp index d91d5f16fc7a9..26ca0f6fb1b87 100644 --- a/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp +++ b/clang/lib/AST/ByteCode/ByteCodeEmitter.cpp @@ -70,9 +70,10 @@ void ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl, Func->setDefined(true); // Lambda static invokers are a special case that we emit custom code for. - bool IsEligibleForCompilation = Func->isLambdaStaticInvoker() || - FuncDecl->isConstexpr() || - FuncDecl->hasAttr(); + bool IsEligibleForCompilation = + Func->isLambdaStaticInvoker() || + FuncDecl->isConstexprOrImplicitlyCanBe(Ctx.getLangOpts()) || + FuncDecl->hasAttr(); // Compile the function body. if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) { diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index b755a072fec88..161cfeecf39a5 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -827,7 +827,8 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { return false; if (F->isConstexpr() && F->hasBody() && - (F->getDecl()->isConstexpr() || F->getDecl()->hasAttr())) + (F->getDecl()->isConstexprOrImplicitlyCanBe(S.getLangOpts()) || + F->getDecl()->hasAttr())) return true; // Implicitly constexpr. @@ -846,7 +847,7 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { const auto *CD = dyn_cast(DiagDecl); if (CD && CD->isInheritingConstructor()) { const auto *Inherited = CD->getInheritedConstructor().getConstructor(); - if (!Inherited->isConstexpr()) + if (!Inherited->isConstexprOrImplicitlyCanBe(S.getLangOpts())) DiagDecl = CD = Inherited; } @@ -868,19 +869,28 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { // for a constant expression. It might be defined at the point we're // actually calling it. bool IsExtern = DiagDecl->getStorageClass() == SC_Extern; - if (!DiagDecl->isDefined() && !IsExtern && DiagDecl->isConstexpr() && + if (!DiagDecl->isDefined() && !IsExtern && + DiagDecl->isConstexprOrImplicitlyCanBe(S.getLangOpts()) && S.checkingPotentialConstantExpression()) return false; // If the declaration is defined, declared 'constexpr' _and_ has a body, // the below diagnostic doesn't add anything useful. - if (DiagDecl->isDefined() && DiagDecl->isConstexpr() && + if (DiagDecl->isDefined() && + DiagDecl->isConstexprOrImplicitlyCanBe(S.getLangOpts()) && DiagDecl->hasBody()) return false; - S.FFDiag(S.Current->getLocation(OpPC), - diag::note_constexpr_invalid_function, 1) - << DiagDecl->isConstexpr() << (bool)CD << DiagDecl; + if (S.getLangOpts().ImplicitConstexpr && !F->getDecl()->isInlined()) { + S.FFDiag(S.Current->getLocation(OpPC), + diag::note_constexpr_implicit_constexpr_must_be_inlined, 1) + << DiagDecl; + } else { + S.FFDiag(S.Current->getLocation(OpPC), + diag::note_constexpr_invalid_function, 1) + << DiagDecl->isConstexprOrImplicitlyCanBe(S.getLangOpts()) + << (bool)CD << DiagDecl; + } if (DiagDecl->getDefinition()) S.Note(DiagDecl->getDefinition()->getLocation(), 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()))) + (Definition->isConstexprOrImplicitlyCanBe(Info.Ctx.getLangOpts()) || + (Info.CurrentCall->CanEvalMSConstexpr && + Definition->hasAttr()))) 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(Func->getDeclContext()) && cast(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..6166928097b24 --- /dev/null +++ b/clang/test/Sema/implicit-constexpr-basic.cpp @@ -0,0 +1,117 @@ + +// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL14,BOTH14,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++14 %s +// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_PRE20,BOTH14,ALL_PRE20,ALLIMPLICIT,ALLIMPLICITOLD,ALL -fimplicit-constexpr -std=c++14 %s +// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_PRE20,BOTH14,ALL_PRE20,ALLIMPLICIT,ALLIMPLICITNEW,ALL -fimplicit-constexpr -std=c++14 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL17,BOTH17,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++17 %s +// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_PRE20,BOTH17,ALL_PRE20,ALLIMPLICIT,ALLIMPLICITOLD,ALL -fimplicit-constexpr -std=c++17 %s +// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_PRE20,BOTH17,ALL_PRE20,ALLIMPLICIT,ALLIMPLICITNEW,ALL -fimplicit-constexpr -std=c++17 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL20,BOTH20,ALLNORMAL,ALL -std=c++20 %s +// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALLIMPLICITOLD,ALL -fimplicit-constexpr -std=c++20 %s +// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALLIMPLICITNEW,ALL -fimplicit-constexpr -std=c++20 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL23,BOTH23,ALLNORMAL,ALL -std=c++23 %s +// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALLIMPLICITOLD,ALL -fimplicit-constexpr -std=c++23 %s +// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALLIMPLICITNEW,ALL -fimplicit-constexpr -std=c++23 %s -fexperimental-new-constant-interpreter + + + + +// ============================================= +// 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}} +// ALLIMPLICITOLD-note@-3 {{undefined function 'noinline_undefined_fnc' cannot be used in a constant expression}} +// ALLIMPLICITNEW-note@-4 {{non-inline function 'noinline_undefined_fnc' is not implicitly constexpr}} + +// ============================================= +// 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(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-chain.cpp b/clang/test/Sema/implicit-constexpr-chain.cpp new file mode 100644 index 0000000000000..564c74af34839 --- /dev/null +++ b/clang/test/Sema/implicit-constexpr-chain.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -verify=BEFORE -std=c++23 %s +// RUN: %clang_cc1 -verify=AFTER -std=c++23 %s -fimplicit-constexpr + +// AFTER-no-diagnostics + +// FOLLOWING TWO EXAMPLES ESTABLISH THE `-fimplicit-constexpr` allows enter to constant evaluation for functions +// which would be disabled based on: +// [expr.const]#10.3 "an invocation of a non-constexpr function" (is not allowed) + +// ------------------- + +inline int normal_function() { + // BEFORE-note@-1 {{declared here}} + return 42; +} + +constinit auto cinit = normal_function(); +// BEFORE-error@-1 {{variable does not have a constant initializer}} +// BEFORE-note@-2 {{required by 'constinit' specifier here}} +// BEFORE-note@-3 {{non-constexpr function 'normal_function' cannot be used in a constant expression}} + +// ------------------- + +inline int still_normal_function() { + // BEFORE-note@-1 {{declared here}} + return 42; +} + +constexpr auto cxpr = still_normal_function(); +// BEFORE-error@-1 {{constexpr variable 'cxpr' must be initialized by a constant expression}} +// BEFORE-note@-2 {{non-constexpr function 'still_normal_function' cannot be used in a constant expression}} + +// ------------------- + +// Following example shows calling non-constexpr marked function in +// constant evaluated context is no longer an error. + +struct type_with_nonconstexpr_static_function { + static /* non-constexpr */ int square(int v) { + // BEFORE-note@-1 {{declared here}} + return v*v; + } + + int value; + constexpr type_with_nonconstexpr_static_function(int x): value{square(x)} { } + // BEFORE-note@-1 {{non-constexpr function 'square' cannot be used in a constant expression}} + // ^ (Hana's note: during evaluation) +}; + +constexpr auto force_ce = type_with_nonconstexpr_static_function{4}; +// BEFORE-error@-1 {{constexpr variable 'force_ce' must be initialized by a constant expression}} +// BEFORE-note@-2 {{in call to 'type_with_nonconstexpr_static_function(4)'}} + + +// this is fine: as it's in runtime, where the constructor +// is called in runtime, like a normal function, so it doesn't matter `square` is not constexpr +static auto static_var = type_with_nonconstexpr_static_function{4}; + + + +// ------------------- + +// Following example shows now you can call non-constexpr marked even +// from consteval function initiated constant evaluation. + +inline int runtime_only_function() { + // BEFORE-note@-1 {{declared here}} + return 11; +} + +constexpr int constexpr_function() { + return runtime_only_function(); + // BEFORE-note@-1 {{non-constexpr function 'runtime_only_function' cannot be used in a constant expression}} +} + +consteval int consteval_function() { + return constexpr_function(); + // BEFORE-note@-1 {{in call to 'constexpr_function()'}} +} + +static int noncalled_runtime_function() { + // we enter consteval context here to replace `consteval_function()` with a constant. + // this is happen during parsing!! + return consteval_function(); + // BEFORE-error@-1 {{call to consteval function 'consteval_function' is not a constant expression}} + // BEFORE-note@-2 {{in call to 'consteval_function()'}} +} + diff --git a/clang/test/Sema/implicit-constexpr-features.cpp b/clang/test/Sema/implicit-constexpr-features.cpp new file mode 100644 index 0000000000000..832f06aa174f4 --- /dev/null +++ b/clang/test/Sema/implicit-constexpr-features.cpp @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -verify=NORMAL14,NORMAL_ALL -std=c++14 %s -fcxx-exceptions -DCONSTEXPR= +// RUN: %clang_cc1 -verify=NORMAL17,NORMAL_ALL -std=c++17 %s -fcxx-exceptions -DCONSTEXPR= +// RUN: %clang_cc1 -verify=NORMAL20,NORMAL_ALL -std=c++20 %s -fcxx-exceptions -DCONSTEXPR= +// RUN: %clang_cc1 -verify=NORMAL23,NORMAL_ALL -std=c++23 %s -fcxx-exceptions -DCONSTEXPR= +// RUN: %clang_cc1 -verify=NORMAL26,NORMAL_ALL -std=c++26 %s -fcxx-exceptions -DCONSTEXPR= + +// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_ALL -std=c++14 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr +// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_ALL -std=c++17 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr +// RUN: %clang_cc1 -verify=IMPLICIT20,IMPLICIT_ALL -std=c++20 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr +// RUN: %clang_cc1 -verify=IMPLICIT23,IMPLICIT_ALL -std=c++23 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr +// RUN: %clang_cc1 -verify=IMPLICIT26,IMPLICIT_ALL -std=c++26 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr + +// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_ALL -std=c++14 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_ALL -std=c++17 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify=IMPLICIT20,IMPLICIT_ALL -std=c++20 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify=IMPLICIT23,IMPLICIT_ALL -std=c++23 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -verify=IMPLICIT26,IMPLICIT_ALL -std=c++26 %s -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=CONSTEXPR14,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++14 %s -fcxx-exceptions -DCONSTEXPR=constexpr +// RUN: %clang_cc1 -verify=CONSTEXPR17,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++17 %s -fcxx-exceptions -DCONSTEXPR=constexpr +// RUN: %clang_cc1 -verify=CONSTEXPR20,CONSTEXPR_BEFORE23,CONSTEXPR_ALL -std=c++20 %s -fcxx-exceptions -DCONSTEXPR=constexpr +// RUN: %clang_cc1 -verify=CONSTEXPR23,CONSTEXPR_ALL -std=c++23 %s -fcxx-exceptions -DCONSTEXPR=constexpr +// RUN: %clang_cc1 -verify=CONSTEXPR26,CONSTEXPR_ALL -std=c++26 %s -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..3263ddc1222cd --- /dev/null +++ b/clang/test/Sema/implicit-constexpr-members.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 -verify=NORMAL14,BOTH14,ALLNORMAL,ALL -std=c++14 %s +// RUN: %clang_cc1 -verify=IMPLICIT14,BOTH14,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++14 %s +// RUN: %clang_cc1 -verify=IMPLICIT14,BOTH14,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++14 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=NORMAL17,BOTH20,ALLNORMAL,ALL -std=c++17 %s +// RUN: %clang_cc1 -verify=IMPLICIT17,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++17 %s +// RUN: %clang_cc1 -verify=IMPLICIT17,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++17 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=NORMAL20,BOTH20,ALLNORMAL,ALL -std=c++20 %s +// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++20 %s +// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++20 %s -fexperimental-new-constant-interpreter + +// RUN: %clang_cc1 -verify=NORMAL23,BOTH23,ALLNORMAL,ALL -std=c++23 %s +// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++23 %s +// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++23 %s -fexperimental-new-constant-interpreter + +// ALLIMPLICIT-no-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 struct template_type { + bool test() const { + // ALLNORMAL-note@-1 {{declared here}} + return true; + } +}; + +constexpr bool result_template_type = template_type{}.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 struct template_template_type { + template bool test() const { + // ALLNORMAL-note@-1 {{declared here}} + return true; + } +}; + +constexpr bool result_template_template_type = template_template_type{}.template test(); +// ALLNORMAL-error@-1 {{constexpr variable 'result_template_template_type' must be initialized by a constant expression}} +// ALLNORMAL-note@-2 {{non-constexpr function 'test' 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 bool test(this const Self & self) { + // 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 diff --git a/clang/test/Sema/implicit-constexpr-ub.cpp b/clang/test/Sema/implicit-constexpr-ub.cpp new file mode 100644 index 0000000000000..18ff17c73ed74 --- /dev/null +++ b/clang/test/Sema/implicit-constexpr-ub.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -verify=BEFORE,BOTH -std=c++23 %s +// RUN: %clang_cc1 -verify=AFTER,BOTH -std=c++23 %s -fimplicit-constexpr + +inline char read_byte(const char * ptr, int offset) { + return *(ptr+offset); + // AFTER-note@-1 {{read of dereferenced one-past-the-end pointer is not allowed in a constant expression}} + +} + +inline char normal_function(int offset) { + // BEFORE-note@-1 {{declared here}} + char array[8] = {'a','b','c','d','e','f','g','h'}; + return read_byte(array, offset); + // AFTER-note@-1 {{read_byte(&array[0], 8)}} +} + +constexpr char off_by_one_error = normal_function(8); +// BOTH-error@-1 {{constexpr variable 'off_by_one_error' must be initialized by a constant expression}} +// BEFORE-note@-2 {{non-constexpr function 'normal_function' cannot be used in a constant expression}} +// AFTER-note@-3 {{in call to 'normal_function(8)'}} + + +