-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[clang] add -fimplicit-constexpr
flag
#136436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-driver Author: Hana Dusíková (hanickadot) ChangesSame as GCC's flag of the same name. If a function (constructor / destructor) doesn't have a This change doesn't touch variables. Only functions. Full diff: https://github.com/llvm/llvm-project/pull/136436.diff 7 Files Affected:
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index 930c1c06d1a76..97527f7a0d1f3 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")
+LANGOPT(ImplicitConstexpr, 1, 0, "evaluate everything as it is a constexpr enabled")
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/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 96d81e618494a..603a44bbba910 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2826,6 +2826,7 @@ class Sema final : public SemaBase {
bool buildCoroutineParameterMoves(SourceLocation Loc);
VarDecl *buildCoroutinePromise(SourceLocation Loc);
void CheckCompletedCoroutineBody(FunctionDecl *FD, Stmt *&Body);
+ void AnalyseCoroutineBody(const CoroutineBodyStmt *);
// As a clang extension, enforces that a non-coroutine function must be marked
// with [[clang::coro_wrapper]] if it returns a type marked with
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index f2f5231933c88..27baf602e2703 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -6612,6 +6612,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ);
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ);
+ 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/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 4b87004e4b8ea..c0e916440a5fd 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -46,6 +46,7 @@ add_clang_library(clangSema
SemaConcept.cpp
SemaConsumer.cpp
SemaCoroutine.cpp
+ SemaCoroutineFlow.cpp
SemaCUDA.cpp
SemaDirectX.cpp
SemaDecl.cpp
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 46933c5c43168..1d4328db4dc41 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -10163,6 +10163,16 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
+
+ // Clang's option -fimplicit-constexpr will make functions without constexpr
+ // or consteval specifier implicitly constexpr (compatibility with GCC.)
+ if (ConstexprKind == ConstexprSpecKind::Unspecified &&
+ getLangOpts().ImplicitConstexpr) {
+
+ NewFD->setConstexprKind(ConstexprSpecKind::Constexpr);
+ ConstexprKind = ConstexprSpecKind::Constexpr;
+ }
+
if (ConstexprKind != ConstexprSpecKind::Unspecified) {
// C++11 [dcl.constexpr]p2: constexpr functions and constexpr constructors
// are implicitly inline.
diff --git a/clang/test/Sema/implicit-constexpr.cpp b/clang/test/Sema/implicit-constexpr.cpp
new file mode 100644
index 0000000000000..4b1314c342361
--- /dev/null
+++ b/clang/test/Sema/implicit-constexpr.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -verify=normal,both -std=c++23 %s
+// RUN: %clang_cc1 -verify=implicit,both -fimplicit-constexpr -std=c++23 %s
+
+bool function_test() { // normal-note+ {{declared here}}
+ return true;
+}
+
+// const variables should work as before
+const auto cvar = function_test();
+
+constexpr auto constexpr_var = function_test(); // normal-error {{constexpr variable 'constexpr_var' must be initialized by a constant expression}}
+// normal-note@-1 {{non-constexpr function 'function_test' cannot be used in a constant expression}}
+
+static_assert(function_test()); // normal-error {{static assertion expression is not an integral constant expression}}
+// normal-note@-1 {{non-constexpr function 'function_test' cannot be used in a constant expression}}
+
+
+struct full_nonconstexpr_type {
+ full_nonconstexpr_type() { } // normal-note {{declared here}}
+ ~full_nonconstexpr_type() { }
+};
+
+constexpr bool constructor_destructor_test() {
+ full_nonconstexpr_type var{}; // normal-note {{non-constexpr constructor 'full_nonconstexpr_type' cannot be used in a constant expression}}
+ return true;
+}
+
+static_assert(constructor_destructor_test()); // normal-error {{static assertion expression is not an integral constant expression}}
+// normal-note@-1 {{in call to 'constructor_destructor_test()'}}
+
+
+struct only_destructor_type {
+ constexpr only_destructor_type() { }
+ ~only_destructor_type() { } // normal-note {{declared here}}
+};
+
+constexpr bool destructor_test() {
+ only_destructor_type var{}; // normal-note {{non-constexpr function '~only_destructor_type' cannot be used in a constant expression}}
+ return true;
+}
+
+static_assert(destructor_test()); // normal-error {{static assertion expression is not an integral constant expression}}
+// normal-note@-1 {{in call to 'destructor_test()'}}
+
+
+bool undefined_test(); // both-note {{declared here}}
+
+static_assert(undefined_test()); // both-error {{static assertion expression is not an integral constant expression}}
+// normal-note@-1 {{non-constexpr function 'undefined_test' cannot be used in a constant expressio}}
+// implicit-note@-2 {{undefined function 'undefined_test' cannot be used in a constant expression}}
+
|
697ec9a
to
b8865c8
Compare
IMO this kind of flag requires an RFC. This is a non-conforming extension and I don't see any rationale for why we'd want to add it. |
My motivation is get better ability to experiment with constexpr-ification of stdlib and other libraries. This proved very useful in GCC. Where quickly check something for constexpr-ness is much easier. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this unfortunately isn’t this simple: I can think of a few problems this runs into and I the solutions I’ve managed to think of are:
First, we could use Sema::CheckConstexprFunctionDefinition()
(see also the call to that function in Sema::BuildLambda()
, which handles making lambdas implicitly constexpr
) to make sure a function is even allowed to be constexpr
; however, that needs to happen after we parse the function body.
This, however, might have some unfortunate consequences for function declarations without a body; it would be a bit janky to have to remove the constexpr
specifier again once we see the definition and realise it can’t actually be constexpr
, but not making it constexpr
if there is no body runs into problems with code like:
int foo();
int bar() { return foo(); }
int foo() { return 3; }
static_assert(bar() == 3);
When we encounter bar()
here, we can only make it implicitly constexpr
if we tread foo()
as constexpr
as well even though we haven’t seen its body yet and hence can’t actually check whether it can actually be constexpr
. GCC accepts this code if we declare foo()
and bar()
as inline
.
An alternative approach might be to just not make functions constexpr
in the AST and also never call CheckConstexprFunctionDefinition()
for such functions in this mode and instead just allow calling functions that are not declared constexpr
if the lang opt is set. That however runs into the problem that the constant evaluator needs to be able to diagnose any invalid constructs it runs into w/o crashing, and I’m not sure it’s currently set up for that given that CheckConstexprFunctionDefinitions()
rejects e.g. goto
s before we ever get to the constant evaluator. So that definitely requires more tests for all things we might run into there.
@@ -6612,6 +6612,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, | |||
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_depth_EQ); | |||
Args.AddLastArg(CmdArgs, options::OPT_fconstexpr_steps_EQ); | |||
|
|||
Args.AddLastArg(CmdArgs, options::OPT_fimplicit_constexpr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should issue a driver diagnostic somewhere if someone tries to pass -fimplicit-constexpr
if we’re not compiling C++. And we’d need tests for that too then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added check for c++, but it would be goot to enable for 14+ only (but not sure how to do it in this file, will look at it tomorrow)
Oh and one more thing: this is currently missing a release note. |
I feel like GCC compatibility might be an argument? Imo it depends a bit on how difficult it is to implement the flag, but an RFC to discuss the exact behaviour of it might be warranted (e.g. do we require |
Oh yeah, and one more thing I forgot about earlier: this definitely need some tests involving templates to make sure that instantiations are also treated as implicitly |
CC @AaronBallman regarding whether this needs an RFC |
3853a24
to
f37eb75
Compare
426c4f7
to
a486edc
Compare
This at least shouldn't be a problem - the constant evaluator already needs to do that when e.g. the |
@Sirraide we could do the check at the point of call - and cache the result, which would require storing some state in FunctionDecl. Or just not care. GCC is explicit about not supporting this scenario https://compiler-explorer.com/z/Mdva4f1a1 - and i don't see the value in diverging from GCC I do agree we should call We also should be careful about the whole consteval propagation thing, which would be affected by this feature. |
|
||
if (LangOpts.ImplicitConstexpr) // same value as GCC | ||
Builder.defineMacro("__cpp_implicit_constexpr", "20211111"); |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
+1, this sort of extension requires an RFC. Please be sure to mention how the RFC addresses our usual criteria for new extensions, too. |
An RFC has been posted on Discourse: https://discourse.llvm.org/t/rfc-fimplicit-constexpr/85963 |
e5bc297
to
f2d5bfa
Compare
f2d5bfa
to
5f388d0
Compare
Same as GCC's flag of the same name. If a function (constructor / destructor) doesn't have a
constexpr
norconsteval
specifier. It will implicitly addconstexpr
.This change doesn't touch variables. Only functions.