Skip to content

Commit f37eb75

Browse files
committed
[clang] add -fimplicit-constexpr flag (with same behaviour as GCC)
1 parent 6196b4e commit f37eb75

13 files changed

+339
-7
lines changed

clang/include/clang/AST/Decl.h

+3
Original file line numberDiff line numberDiff line change
@@ -2418,6 +2418,9 @@ class FunctionDecl : public DeclaratorDecl,
24182418
bool isConstexpr() const {
24192419
return getConstexprKind() != ConstexprSpecKind::Unspecified;
24202420
}
2421+
/// Support for `-fimplicit-constexpr`
2422+
bool isConstexprOrImplicitlyCanBe(const LangOptions &LangOpts,
2423+
bool MustBeInlined = true) const;
24212424
void setConstexprKind(ConstexprSpecKind CSK) {
24222425
FunctionDeclBits.ConstexprKind = static_cast<uint64_t>(CSK);
24232426
}

clang/include/clang/Basic/DiagnosticASTKinds.td

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def note_constexpr_lshift_discards : Note<"signed left shift discards bits">;
3232
def note_constexpr_invalid_function : Note<
3333
"%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot "
3434
"be used in a constant expression">;
35+
def note_constexpr_implicit_constexpr_must_be_inlined
36+
: Note<"non-inline function %0 is not implicitly constexpr">;
3537
def note_constexpr_invalid_inhctor : Note<
3638
"constructor inherited from base class %0 cannot be used in a "
3739
"constant expression; derived class cannot be implicitly initialized">;

clang/include/clang/Basic/Features.def

+1
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ EXTENSION(matrix_types, LangOpts.MatrixTypes)
316316
EXTENSION(matrix_types_scalar_division, true)
317317
EXTENSION(cxx_attributes_on_using_declarations, LangOpts.CPlusPlus11)
318318
EXTENSION(datasizeof, LangOpts.CPlusPlus)
319+
EXTENSION(cxx_implicit_constexpr, LangOpts.ImplicitConstexpr)
319320

320321
FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVTables)
321322

clang/include/clang/Basic/LangOptions.def

+1
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ BENIGN_LANGOPT(ArrowDepth, 32, 256,
413413
"maximum number of operator->s to follow")
414414
BENIGN_LANGOPT(InstantiationDepth, 32, 1024,
415415
"maximum template instantiation depth")
416+
COMPATIBLE_LANGOPT(ImplicitConstexpr, 1, 0, "make functions implicitly 'constexpr'")
416417
BENIGN_LANGOPT(ConstexprCallDepth, 32, 512,
417418
"maximum constexpr call depth")
418419
BENIGN_LANGOPT(ConstexprStepLimit, 32, 1048576,

clang/include/clang/Driver/Options.td

+6
Original file line numberDiff line numberDiff line change
@@ -1991,6 +1991,12 @@ defm constant_cfstrings : BoolFOption<"constant-cfstrings",
19911991
"Disable creation of CodeFoundation-type constant strings">,
19921992
PosFlag<SetFalse>>;
19931993
def fconstant_string_class_EQ : Joined<["-"], "fconstant-string-class=">, Group<f_Group>;
1994+
def fimplicit_constexpr
1995+
: Joined<["-"], "fimplicit-constexpr">,
1996+
Group<f_Group>,
1997+
Visibility<[ClangOption, CC1Option]>,
1998+
HelpText<"All function declarations will be implicitly constexpr.">,
1999+
MarshallingInfoFlag<LangOpts<"ImplicitConstexpr">>;
19942000
def fconstexpr_depth_EQ : Joined<["-"], "fconstexpr-depth=">, Group<f_Group>,
19952001
Visibility<[ClangOption, CC1Option]>,
19962002
HelpText<"Set the maximum depth of recursive constexpr function calls">,

clang/lib/AST/Decl.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -3242,6 +3242,27 @@ bool FunctionDecl::isDefined(const FunctionDecl *&Definition,
32423242
return false;
32433243
}
32443244

3245+
bool FunctionDecl::isConstexprOrImplicitlyCanBe(const LangOptions &LangOpts,
3246+
bool MustBeInlined) const {
3247+
if (isConstexpr())
3248+
return true;
3249+
3250+
if (!LangOpts.ImplicitConstexpr)
3251+
return false;
3252+
3253+
// Constexpr function in C++11 couldn't contain anything other then return
3254+
// expression. It wouldn't make sense to allow it (GCC doesn't do it neither).
3255+
if (!LangOpts.CPlusPlus14)
3256+
return false;
3257+
3258+
// Free functions must be inlined, but sometimes we want to skip this check.
3259+
// And in order to keep logic on one place, the check is here.
3260+
if (MustBeInlined)
3261+
return isInlined();
3262+
3263+
return true;
3264+
}
3265+
32453266
Stmt *FunctionDecl::getBody(const FunctionDecl *&Definition) const {
32463267
if (!hasBody(Definition))
32473268
return nullptr;

clang/lib/AST/ExprConstant.cpp

+19-5
Original file line numberDiff line numberDiff line change
@@ -5968,8 +5968,9 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
59685968

59695969
// Can we evaluate this function call?
59705970
if (Definition && Body &&
5971-
(Definition->isConstexpr() || (Info.CurrentCall->CanEvalMSConstexpr &&
5972-
Definition->hasAttr<MSConstexprAttr>())))
5971+
(Definition->isConstexprOrImplicitlyCanBe(Info.Ctx.getLangOpts()) ||
5972+
(Info.CurrentCall->CanEvalMSConstexpr &&
5973+
Definition->hasAttr<MSConstexprAttr>())))
59735974
return true;
59745975

59755976
if (Info.getLangOpts().CPlusPlus11) {
@@ -5987,12 +5988,25 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
59875988
// FIXME: If DiagDecl is an implicitly-declared special member function
59885989
// or an inheriting constructor, we should be much more explicit about why
59895990
// it's not constexpr.
5990-
if (CD && CD->isInheritingConstructor())
5991+
if (CD && CD->isInheritingConstructor()) {
59915992
Info.FFDiag(CallLoc, diag::note_constexpr_invalid_inhctor, 1)
59925993
<< CD->getInheritedConstructor().getConstructor()->getParent();
5993-
else
5994+
5995+
} else if (Definition && !DiagDecl->isInlined() &&
5996+
Info.Ctx.getLangOpts().ImplicitConstexpr) {
5997+
Info.FFDiag(CallLoc,
5998+
diag::note_constexpr_implicit_constexpr_must_be_inlined)
5999+
<< DiagDecl;
6000+
6001+
} else {
6002+
// Using implicit constexpr check here, so we see a missing body as main
6003+
// problem and not missing constexpr with -fimplicit-constexpr.
59946004
Info.FFDiag(CallLoc, diag::note_constexpr_invalid_function, 1)
5995-
<< DiagDecl->isConstexpr() << (bool)CD << DiagDecl;
6005+
<< DiagDecl->isConstexprOrImplicitlyCanBe(Info.Ctx.getLangOpts(),
6006+
false)
6007+
<< (bool)CD << DiagDecl;
6008+
}
6009+
59966010
Info.Note(DiagDecl->getLocation(), diag::note_declared_at);
59976011
} else {
59986012
Info.FFDiag(CallLoc, diag::note_invalid_subexpr_in_const_expr);

clang/lib/Driver/ToolChains/Clang.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -6612,6 +6612,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
66126612
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ);
66136613
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ);
66146614

6615+
if (types::isCXX(InputType))
6616+
Args.AddLastArg(CmdArgs, options::OPT_fimplicit_constexpr);
6617+
66156618
Args.AddLastArg(CmdArgs, options::OPT_fexperimental_library);
66166619

66176620
if (Args.hasArg(options::OPT_fexperimental_new_constant_interpreter))

clang/lib/Frontend/InitPreprocessor.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,9 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
779779

780780
// TODO: Final number?
781781
Builder.defineMacro("__cpp_type_aware_allocators", "202500L");
782+
783+
if (LangOpts.ImplicitConstexpr) // same value as GCC
784+
Builder.defineMacro("__cpp_implicit_constexpr", "20211111");
782785
}
783786

784787
/// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target

clang/lib/Sema/SemaExpr.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -18384,16 +18384,18 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
1838418384
}
1838518385

1838618386
if (FirstInstantiation || TSK != TSK_ImplicitInstantiation ||
18387-
Func->isConstexpr()) {
18387+
Func->isConstexprOrImplicitlyCanBe(getLangOpts())) {
1838818388
if (isa<CXXRecordDecl>(Func->getDeclContext()) &&
1838918389
cast<CXXRecordDecl>(Func->getDeclContext())->isLocalClass() &&
1839018390
CodeSynthesisContexts.size())
1839118391
PendingLocalImplicitInstantiations.push_back(
1839218392
std::make_pair(Func, PointOfInstantiation));
18393-
else if (Func->isConstexpr())
18393+
else if (Func->isConstexprOrImplicitlyCanBe(getLangOpts()))
1839418394
// Do not defer instantiations of constexpr functions, to avoid the
1839518395
// expression evaluator needing to call back into Sema if it sees a
1839618396
// call to such a function.
18397+
// (When -fimplicit-instantiation is enabled, all functions are
18398+
// implicitly constexpr)
1839718399
InstantiateFunctionDefinition(PointOfInstantiation, Func);
1839818400
else {
1839918401
Func->setInstantiationIsPending(true);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL14,BOTH14,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++14 %s -fcolor-diagnostics
2+
// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_PRE20,BOTH14,ALL_PRE20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++14 %s -fcolor-diagnostics
3+
4+
// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL17,BOTH17,ALL_PRE20,ALLNORMAL,NORMAL_PRE20,ALL -std=c++17 %s -fcolor-diagnostics
5+
// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_PRE20,BOTH17,ALL_PRE20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++17 %s -fcolor-diagnostics
6+
7+
// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL20,BOTH20,ALLNORMAL,ALL -std=c++20 %s -fcolor-diagnostics
8+
// RUN: %clang_cc1 -verify=IMPLICIT20,BOTH20,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++20 %s -fcolor-diagnostics
9+
10+
// RUN: %clang_cc1 -verify=ALL_NORMAL,NORMAL23,BOTH23,ALLNORMAL,ALL -std=c++23 %s -fcolor-diagnostics
11+
// RUN: %clang_cc1 -verify=IMPLICIT23,BOTH23,ALLIMPLICIT,ALL -fimplicit-constexpr -std=c++23 %s -fcolor-diagnostics
12+
13+
14+
15+
16+
// =============================================
17+
// 1) simple uninlined function
18+
19+
bool noinline_fnc() {
20+
// ALL-note@-1 {{declared here}}
21+
return true;
22+
}
23+
24+
constexpr bool result_noinline_fnc = noinline_fnc();
25+
// ALL-error@-1 {{constexpr variable 'result_noinline_fnc' must be initialized by a constant expression}}
26+
// ALLNORMAL-note@-2 {{non-constexpr function 'noinline_fnc' cannot be used in a constant expression}}
27+
// ALLIMPLICIT-note@-3 {{non-inline function 'noinline_fnc' is not implicitly constexpr}}
28+
29+
30+
// =============================================
31+
// 2) simple inlined function
32+
33+
inline bool inline_fnc() {
34+
// ALLNORMAL-note@-1 {{declared here}}
35+
return true;
36+
}
37+
38+
constexpr bool result_inline_fnc = inline_fnc();
39+
// ALLNORMAL-error@-1 {{constexpr variable 'result_inline_fnc' must be initialized by a constant expression}}
40+
// ALLNORMAL-note@-2 {{non-constexpr function 'inline_fnc' cannot be used in a constant expression}}
41+
42+
43+
// =============================================
44+
// 3) undefined uninlined function
45+
46+
bool noinline_undefined_fnc();
47+
// ALL-note@-1 {{declared here}}
48+
49+
constexpr bool result_noinline_undefined_fnc = noinline_undefined_fnc();
50+
// ALL-error@-1 {{constexpr variable 'result_noinline_undefined_fnc' must be initialized by a constant expression}}
51+
// ALLNORMAL-note@-2 {{non-constexpr function 'noinline_undefined_fnc' cannot be used in a constant expression}}
52+
// ALLIMPLICIT-note@-3 {{undefined function 'noinline_undefined_fnc' cannot be used in a constant expression}}
53+
54+
55+
// =============================================
56+
// 4) undefined inline function
57+
58+
inline bool inline_undefined_fnc();
59+
// ALL-note@-1 {{declared here}}
60+
61+
constexpr bool result_inline_undefined_fnc = inline_undefined_fnc();
62+
// ALL-error@-1 {{constexpr variable 'result_inline_undefined_fnc' must be initialized by a constant expression}}
63+
// ALLNORMAL-note@-2 {{non-constexpr function 'inline_undefined_fnc' cannot be used in a constant expression}}
64+
// ALLIMPLICIT-note@-3 {{undefined function 'inline_undefined_fnc' cannot be used in a constant expression}}
65+
66+
// =============================================
67+
// 5) lambda function
68+
69+
auto lambda = [](int x) { return x > 0; };
70+
// NORMAL14-note@-1 {{declared here}}
71+
72+
constexpr bool result_lambda = lambda(10);
73+
// NORMAL14-error@-1 {{constexpr variable 'result_lambda' must be initialized by a constant expression}}
74+
// NORMAL14-note@-2 {{non-constexpr function 'operator()' cannot be used in a constant expression}}
75+
76+
77+
// =============================================
78+
// 6) virtual functions
79+
80+
struct type {
81+
virtual bool dispatch() const noexcept {
82+
return false;
83+
}
84+
};
85+
86+
struct child_of_type: type {
87+
bool dispatch() const noexcept override {
88+
// NORMAL20-note@-1 {{declared here}}
89+
// NORMAL23-note@-2 {{declared here}}
90+
return true;
91+
}
92+
};
93+
94+
constexpr bool result_virtual = static_cast<const type &>(child_of_type{}).dispatch();
95+
// ALL_NORMAL-error@-1 {{constexpr variable 'result_virtual' must be initialized by a constant expression}}
96+
// NORMAL_PRE20-note@-2 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}}
97+
// IMPLICIT_PRE20-error@-3 {{constexpr variable 'result_virtual' must be initialized by a constant expression}}
98+
// IMPLICIT_PRE20-note@-4 {{cannot evaluate call to virtual function in a constant expression in C++ standards before C++20}}
99+
// NORMAL20-note@-5 {{non-constexpr function 'dispatch' cannot be used in a constant expression}}
100+
// NORMAL20-note@-6 {{declared here}}
101+
// NORMAL23-note@-7 {{non-constexpr function 'dispatch' cannot be used in a constant expression}}
102+
// NORMAL23-note@-8 {{declared here}}
103+
104+
105+
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201907L
106+
static_assert(result_virtual == true, "virtual should work");
107+
// ALL_NORMAL-error@-1 {{static assertion expression is not an integral constant expression}}
108+
// ALL_NORMAL-note@-2 {{initializer of 'result_virtual' is not a constant expression}}
109+
// IMPLICIT_PRE20-note@-3 {{initializer of 'result_virtual' is not a constant expression}}
110+
#endif
111+
112+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %clang_cc1 -verify=NORMAL14,NORMAL_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
2+
// RUN: %clang_cc1 -verify=NORMAL17,NORMAL_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
3+
// RUN: %clang_cc1 -verify=NORMAL20,NORMAL_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
4+
// RUN: %clang_cc1 -verify=NORMAL23,NORMAL_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
5+
// RUN: %clang_cc1 -verify=NORMAL26,NORMAL_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=
6+
7+
// RUN: %clang_cc1 -verify=IMPLICIT14,IMPLICIT_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
8+
// RUN: %clang_cc1 -verify=IMPLICIT17,IMPLICIT_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
9+
// RUN: %clang_cc1 -verify=IMPLICIT20,IMPLICIT_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
10+
// RUN: %clang_cc1 -verify=IMPLICIT23,IMPLICIT_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
11+
// RUN: %clang_cc1 -verify=IMPLICIT26,IMPLICIT_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR= -fimplicit-constexpr
12+
13+
// RUN: %clang_cc1 -verify=CONSTEXPR14,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++14 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
14+
// RUN: %clang_cc1 -verify=CONSTEXPR17,CONSTEXPR_BEFORE23,CONSTEXPR_BEFORE20,CONSTEXPR_ALL -std=c++17 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
15+
// RUN: %clang_cc1 -verify=CONSTEXPR20,CONSTEXPR_BEFORE23,CONSTEXPR_ALL -std=c++20 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
16+
// RUN: %clang_cc1 -verify=CONSTEXPR23,CONSTEXPR_ALL -std=c++23 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
17+
// RUN: %clang_cc1 -verify=CONSTEXPR26,CONSTEXPR_ALL -std=c++26 %s -fcolor-diagnostics -fcxx-exceptions -DCONSTEXPR=constexpr
18+
19+
// Objective is to make sure features like allocation / throwing won't fail code by just adding implicit constexpr
20+
// in an unevaluated code.
21+
22+
// NORMAL_ALL-no-diagnostics
23+
// IMPLICIT_ALL-no-diagnostics
24+
// CONSTEXPR23-no-diagnostics
25+
// CONSTEXPR26-no-diagnostics
26+
27+
CONSTEXPR inline bool function_with_goto(int v) {
28+
if (v == 0) {
29+
return true;
30+
}
31+
32+
goto label;
33+
// CONSTEXPR_BEFORE23-warning@-1 {{use of this statement in a constexpr function is a C++23 extension}}
34+
35+
label:
36+
return false;
37+
}
38+
39+
CONSTEXPR inline bool function_with_label(int v) {
40+
label:
41+
// CONSTEXPR_BEFORE23-warning@-1 {{use of this statement in a constexpr function is a C++23 extension}}
42+
if (v > 0) {
43+
return true;
44+
}
45+
v++;
46+
goto label;
47+
}
48+
49+
CONSTEXPR inline bool function_with_try_catch(int v) {
50+
try {
51+
// CONSTEXPR_BEFORE20-warning@-1 {{use of this statement in a constexpr function is a C++20 extension}}
52+
return v;
53+
} catch (int) {
54+
return -v;
55+
}
56+
}
57+
58+
CONSTEXPR inline bool function_with_inline_asm(int v) {
59+
if (v > 0) {
60+
asm("");
61+
// CONSTEXPR_BEFORE20-warning@-1 {{use of this statement in a constexpr function is a C++20 extension}}
62+
}
63+
64+
return v;
65+
}
66+
67+
struct easy_type {
68+
// CONSTEXPR_BEFORE20-note@-1 {{declared here}}
69+
int * x;
70+
};
71+
72+
CONSTEXPR inline bool function_with_no_initializer_variable(int v) {
73+
// CONSTEXPR_BEFORE20-error@-1 {{constexpr function never produces a constant expression}}
74+
easy_type easy;
75+
// CONSTEXPR_BEFORE20-note@-1 {{non-constexpr constructor 'easy_type' cannot be used in a constant expression}}
76+
return v;
77+
}
78+
79+
80+
81+

0 commit comments

Comments
 (0)