Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------------------

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">,
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
24 changes: 19 additions & 5 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Frontend/InitPreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Comment on lines +782 to +784
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't usually define macros for non-standard feature. Instead users should use __has_extension(cxx_implicit_constexpr)

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 to mirror GCC's behavior, but I'm happy to remove it

}

/// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
112 changes: 112 additions & 0 deletions clang/test/Sema/implicit-constexpr-basic.cpp
Original file line number Diff line number Diff line change
@@ -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


81 changes: 81 additions & 0 deletions clang/test/Sema/implicit-constexpr-features.cpp
Original file line number Diff line number Diff line change
@@ -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;
}




Loading
Loading