diff options
author | Corentin Jabot <corentinjabot@gmail.com> | 2021-10-05 23:33:31 +0200 |
---|---|---|
committer | Corentin Jabot <corentinjabot@gmail.com> | 2022-03-22 19:51:19 +0100 |
commit | 683e83c56f98df6fe42d506a04dda44309ca758f (patch) | |
tree | 425747d6753617f8a8f10b6df4de06805135143b /clang/test | |
parent | bafbae238aa1948aa511c734a613a95d89a72546 (diff) | |
download | llvm-683e83c56f98df6fe42d506a04dda44309ca758f.zip llvm-683e83c56f98df6fe42d506a04dda44309ca758f.tar.gz llvm-683e83c56f98df6fe42d506a04dda44309ca758f.tar.bz2 |
[Clang][C++2b] P2242R3: Non-literal variables [...] in constexpr
Allow goto, labelled statements as well as `static`, `thread_local`, and
non-literal variables in `constexpr` functions.
As specified. for all of the above (except labelled statements) constant
evaluation of the construct still fails.
For `constexpr` bodies, the proposal is implemented with diagnostics as
a language extension in older language modes. For determination of
whether a lambda body satisfies the requirements for a constexpr
function, the proposal is implemented only in C++2b mode to retain the
semantics of older modes for programs conforming to them.
Reviewed By: aaron.ballman, hubert.reinterpretcast, erichkeane
Differential Revision: https://reviews.llvm.org/D111400
Diffstat (limited to 'clang/test')
-rw-r--r-- | clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp | 26 | ||||
-rw-r--r-- | clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp | 55 | ||||
-rw-r--r-- | clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp | 64 | ||||
-rw-r--r-- | clang/test/Lexer/cxx-features.cpp | 2 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx14.cpp | 10 | ||||
-rw-r--r-- | clang/test/SemaCXX/constant-expression-cxx2b.cpp | 240 |
6 files changed, 366 insertions, 31 deletions
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp index 3d45949..dcb9f60 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a %s +// RUN: %clang_cc1 -std=c++2b -verify=expected %s // p3: if the function is a constructor or destructor, its class shall not have // any virtual base classes; @@ -9,40 +10,43 @@ namespace vbase { }; } -// p3: its function-body shall not enclose -// -- a goto statement -// -- an identifier label -// -- a variable of non-literal type or of static or thread storage duration namespace contents { struct A { constexpr ~A() { - goto x; // expected-error {{statement not allowed in constexpr function}} + return; + goto x; // cxx2a-warning {{use of this statement in a constexpr function is a C++2b extension}} x: ; } }; struct B { constexpr ~B() { - x: ; // expected-error {{statement not allowed in constexpr function}} + x:; // cxx2a-warning {{use of this statement in a constexpr function is a C++2b extension}} } }; - struct Nonlit { Nonlit(); }; // expected-note {{not literal}} + struct Nonlit { // cxx2a-note {{'Nonlit' is not literal because}} + Nonlit(); + }; struct C { constexpr ~C() { - Nonlit nl; // expected-error {{non-literal}} + return; + Nonlit nl; // cxx2a-error {{variable of non-literal type 'contents::Nonlit' cannot be defined in a constexpr function before C++2b}} } }; struct D { constexpr ~D() { - static int a; // expected-error {{static variable}} + return; + static int a; // cxx2a-warning {{definition of a static variable in a constexpr function is a C++2b extension}} } }; struct E { constexpr ~E() { - thread_local int e; // expected-error {{thread_local variable}} + return; + thread_local int e; // cxx2a-warning {{definition of a thread_local variable in a constexpr function is a C++2b extension}} } }; struct F { constexpr ~F() { + return; extern int f; } }; diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp new file mode 100644 index 0000000..671895b --- /dev/null +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -verify -std=c++2b -Wpre-c++2b-compat %s + +constexpr int h(int n) { + if (!n) + return 0; + static const int m = n; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int i(int n) { + if (!n) + return 0; + thread_local const int m = n; // expected-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int g() { // expected-error {{constexpr function never produces a constant expression}} + goto test; // expected-note {{subexpression not valid in a constant expression}} \ + // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} +test: + return 0; +} + +constexpr void h() { +label:; // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} +} + +struct NonLiteral { + NonLiteral() {} +}; + +constexpr void non_literal() { // expected-error {{constexpr function never produces a constant expression}} + NonLiteral n; // expected-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} +} + +constexpr void non_literal2(bool b) { + if (!b) + NonLiteral n; +} + +constexpr int c_thread_local(int n) { + if (!n) + return 0; + static _Thread_local int a; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + _Thread_local int b; // // expected-error {{'_Thread_local' variables must have global storage}} + return 0; +} + +constexpr int gnu_thread_local(int n) { + if (!n) + return 0; + static __thread int a; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + __thread int b; // expected-error {{'__thread' variables must have global storage}} + return 0; +} diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp index 9b0e7cc..3dbe3e9 100644 --- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp +++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp @@ -1,6 +1,7 @@ -// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++14-extensions -Werror=c++20-extensions %s -// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++14 -DCXX14 -Werror=c++20-extensions %s -// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++20 -DCXX14 -DCXX20 %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++14-extensions -Werror=c++20-extensions -Werror=c++2b-extensions %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++14 -DCXX14 -Werror=c++20-extensions -Werror=c++2b-extensions %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++20 -DCXX14 -DCXX20 -Werror=c++2b-extensions %s +// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++2b -DCXX14 -DCXX20 -DCXX2b %s namespace N { typedef char C; @@ -10,7 +11,7 @@ namespace M { typedef double D; } -struct NonLiteral { // expected-note 3{{no constexpr constructors}} +struct NonLiteral { // expected-note 2{{no constexpr constructors}} NonLiteral() {} NonLiteral(int) {} }; @@ -150,16 +151,26 @@ constexpr int DisallowedStmtsCXX14_1(bool b) { } constexpr int DisallowedStmtsCXX14_2() { // - a goto statement - goto x; // expected-error {{statement not allowed in constexpr function}} -x: + try { + } catch (...) { + goto x; + x:; + } +#ifndef CXX2b + // expected-error@-4 {{use of this statement in a constexpr function is a C++2b extension}} +#endif return 0; } constexpr int DisallowedStmtsCXX14_2_1() { try { - return 0; } catch (...) { - merp: goto merp; // expected-error {{statement not allowed in constexpr function}} + merp: + goto merp; +#ifndef CXX2b + // expected-error@-3 {{use of this statement in a constexpr function is a C++2b extension}} +#endif } + return 0; } constexpr int DisallowedStmtsCXX14_3() { // - a try-block, @@ -171,18 +182,35 @@ constexpr int DisallowedStmtsCXX14_3() { } constexpr int DisallowedStmtsCXX14_4() { // - a definition of a variable of non-literal type - NonLiteral nl; // expected-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function}} return 0; + NonLiteral nl; +#ifndef CXX2b + // expected-error@-2 {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++2b}} + // expected-note@14 {{'NonLiteral' is not literal}} +#endif } + constexpr int DisallowedStmtsCXX14_5() { + return 0; // - a definition of a variable of static storage duration - static constexpr int n = 123; // expected-error {{static variable not permitted in a constexpr function}} - return n; + static constexpr int n = 123; +#ifndef CXX2b + // expected-error@-2 {{definition of a static variable in a constexpr function is a C++2b extension}} +#endif +#if !defined(CXX14) + // expected-error@-5 {{variable declaration in a constexpr function is a C++14 extension}} +#endif } constexpr int DisallowedStmtsCXX14_6() { // - a definition of a variable of thread storage duration - thread_local constexpr int n = 123; // expected-error {{thread_local variable not permitted in a constexpr function}} - return n; + return 0; + thread_local constexpr int n = 123; +#ifndef CXX2b + // expected-error@-2 {{definition of a thread_local variable in a constexpr function is a C++2b extension}} +#endif +#if !defined(CXX14) + // expected-error@-5 {{variable declaration in a constexpr function is a C++14 extension}} +#endif } constexpr int DisallowedStmtsCXX14_7() { // - a definition of a variable for which no initialization is performed @@ -317,8 +345,14 @@ namespace std_example { return x; } constexpr int first(int n) { - static int value = n; // expected-error {{static variable not permitted}} - return value; + return 0; + static int value = n; +#ifndef CXX2b + // expected-error@-2 {{definition of a static variable in a constexpr function is a C++2b extension}} +#endif +#ifndef CXX14 + // expected-error@-5 {{variable declaration in a constexpr function is a C++14 extension}} +#endif } constexpr int uninit() { int a; diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp index 2847273..6b6729d 100644 --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -277,7 +277,7 @@ #error "wrong value for __cpp_lambdas" #endif -#if check(constexpr, 0, 200704, 201304, 201603, 201907, 201907) +#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202110) #error "wrong value for __cpp_constexpr" #endif diff --git a/clang/test/SemaCXX/constant-expression-cxx14.cpp b/clang/test/SemaCXX/constant-expression-cxx14.cpp index 84ffad3..9f8d8f7 100644 --- a/clang/test/SemaCXX/constant-expression-cxx14.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp @@ -44,12 +44,14 @@ constexpr int g(int k) { return 3 * k3 + 5 * k2 + n * k - 20; } static_assert(g(2) == 42, ""); -constexpr int h(int n) { - static const int m = n; // expected-error {{static variable not permitted in a constexpr function}} +constexpr int h(int n) { // expected-error {{constexpr function never produces a constant expression}} + static const int m = n; // expected-note {{control flows through the definition of a static variable}} \ + // cxx14_20-warning {{definition of a static variable in a constexpr function is a C++2b extension}} return m; } -constexpr int i(int n) { - thread_local const int m = n; // expected-error {{thread_local variable not permitted in a constexpr function}} +constexpr int i(int n) { // expected-error {{constexpr function never produces a constant expression}} + thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx14_20-warning {{definition of a thread_local variable in a constexpr function is a C++2b extension}} return m; } diff --git a/clang/test/SemaCXX/constant-expression-cxx2b.cpp b/clang/test/SemaCXX/constant-expression-cxx2b.cpp new file mode 100644 index 0000000..0657a35 --- /dev/null +++ b/clang/test/SemaCXX/constant-expression-cxx2b.cpp @@ -0,0 +1,240 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx2a %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wno-c++2b-extensions +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wpre-c++2b-compat + +struct NonLiteral { // cxx2a-note {{'NonLiteral' is not literal}} + NonLiteral() {} +}; + +#if __cplusplus > 202002L + +constexpr int f(int n) { // expected-error {{constexpr function never produces a constant expression}} + static const int m = n; // expected-note {{control flows through the definition of a static variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} +constexpr int g(int n) { // expected-error {{constexpr function never produces a constant expression}} + thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int c_thread_local(int n) { // expected-error {{constexpr function never produces a constant expression}} + static _Thread_local int m = 0; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int gnu_thread_local(int n) { // expected-error {{constexpr function never produces a constant expression}} + static __thread int m = 0; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int h(int n) { // expected-error {{constexpr function never produces a constant expression}} + static const int m = n; // expected-note {{control flows through the definition of a static variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return &m - &m; +} +constexpr int i(int n) { // expected-error {{constexpr function never produces a constant expression}} + thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + return &m - &m; +} + +constexpr int j(int n) { + if (!n) + return 0; + static const int m = n; // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} +constexpr int j0 = j(0); + +constexpr int k(int n) { + if (!n) + return 0; + thread_local const int m = n; // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + + return m; +} +constexpr int k0 = k(0); + +constexpr int j_evaluated(int n) { + if (!n) + return 0; + static const int m = n; // expected-note {{control flows through the definition of a static variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int je = j_evaluated(1); // expected-error {{constexpr variable 'je' must be initialized by a constant expression}} \ + // expected-note {{in call}} + +constexpr int k_evaluated(int n) { + if (!n) + return 0; + thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + + return m; +} + +constexpr int ke = k_evaluated(1); // expected-error {{constexpr variable 'ke' must be initialized by a constant expression}} \ + // expected-note {{in call}} + +constexpr int static_constexpr() { // expected-error {{constexpr function never produces a constant expression}} + static constexpr int m = 42; // expected-note {{control flows through the definition of a static variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int thread_local_constexpr() { // expected-error {{constexpr function never produces a constant expression}} + thread_local constexpr int m = 42; // expected-note {{control flows through the definition of a thread_local variable}} \ + // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; +} + +constexpr int non_literal(bool b) { + if (!b) + return 0; + NonLiteral n; +} + +constexpr int non_literal_1 = non_literal(false); + +namespace eval_goto { + +constexpr int f(int x) { + if (x) { + return 0; + } else { + goto test; // expected-note {{subexpression not valid in a constant expression}} \ + // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} + } +test: + return 0; +} + +int a = f(0); +constexpr int b = f(0); // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'f(0)'}} +constexpr int c = f(1); + +constexpr int label() { + +test: // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} + return 0; +} + +constexpr int d = label(); + +} // namespace eval_goto + +#endif + +// Test that explicitly constexpr lambdas behave correctly, +// This is to be contrasted with the test for implicitly constexpr lambdas below. +int test_in_lambdas() { + auto a = []() constexpr { // expected-error{{constexpr function never produces a constant expression}} + static const int m = 32; // expected-note {{control flows through the definition of a static variable}} \ + // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; + }; + + auto b = [](int n) constexpr { + if (!n) + return 0; + static const int m = n; // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; + } + (1); + + auto c = [](int n) constexpr { + if (!n) + return 0; + else + goto test; // expected-note {{subexpression not valid in a constant expression}} \ + // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} + test: + return 1; + }; + c(0); + constexpr auto c_error = c(1); // expected-error {{constexpr variable 'c_error' must be initialized by a constant expression}} \ + // expected-note {{in call to}} + + auto non_literal = [](bool b) constexpr { + if (!b) + NonLiteral n; // cxx2b-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} \ + // cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++2b}} + return 0; + }; + +#if __cplusplus > 202002L + constexpr auto non_literal_ko = non_literal(false); // cxx2b-error {{constexpr variable 'non_literal_ko' must be initialized by a constant expression}} \ + // cxx2b-note {{in call}} + + constexpr auto non_literal_ok = non_literal(true); +#endif +} + +// Test whether lambdas are correctly treated as implicitly constexpr under the +// relaxed C++23 rules (and similarly as not implicitly constexpr under the +// C++20 rules). +int test_lambdas_implicitly_constexpr() { + + auto b = [](int n) { // cxx2a-note 2{{declared here}} + if (!n) + return 0; + static const int m = n; // cxx2b-note {{control flows through the definition of a static variable}} + return m; + }; + + auto b1 = b(1); + constexpr auto b2 = b(0); // cxx2a-error {{must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} + + constexpr auto b3 = b(1); // expected-error{{constexpr variable 'b3' must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} \ + // cxx2b-note {{in call}} + + auto c = [](int n) { // cxx2a-note 2{{declared here}} + if (!n) + return 0; + else + goto test; // cxx2b-note {{subexpression not valid in a constant expression}} + test: + return 1; + }; + c(0); + constexpr auto c_ok = c(0); // cxx2a-error {{must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} + + constexpr auto c_error = c(1); // expected-error {{constexpr variable 'c_error' must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} \ + // cxx2b-note {{in call to}} + + auto non_literal = [](bool b) { // cxx2a-note 2{{declared here}} + if (b) + NonLiteral n; // cxx2b-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} + return 0; + }; + + constexpr auto non_literal_ko = non_literal(true); // expected-error {{constexpr variable 'non_literal_ko' must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} \ + // cxx2b-note {{in call}} + + constexpr auto non_literal_ok = non_literal(false); // cxx2a-error {{must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} +} + +template <typename T> +constexpr auto dependent_var_def_lambda(void) { + return [](bool b) { // cxx2a-note {{declared here}} + if (!b) + T t; + return 0; + }; +} + +constexpr auto non_literal_valid_in_cxx2b = dependent_var_def_lambda<NonLiteral>()(true); // \ + // cxx2a-error {{constexpr variable 'non_literal_valid_in_cxx2b' must be initialized by a constant expression}} \ + // cxx2a-note {{non-constexpr function}} |