Index: include/clang/Basic/Builtins.def =================================================================== --- include/clang/Basic/Builtins.def +++ include/clang/Basic/Builtins.def @@ -500,6 +500,7 @@ BUILTIN(__builtin_vsnprintf, "ic*zcC*a", "nFP:2:") BUILTIN(__builtin_thread_pointer, "v*", "nc") BUILTIN(__builtin_launder, "v*v*", "nt") +LANGBUILTIN(__builtin_is_constant_evaluated, "b", "n", CXX_LANG) // GCC exception builtins BUILTIN(__builtin_eh_return, "vzv*", "r") // FIXME: Takes intptr_t, not size_t! Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -8279,6 +8279,9 @@ return Success(false, E); } + case Builtin::BI__builtin_is_constant_evaluated: + return Success(Info.InConstantContext, E); + case Builtin::BI__builtin_ctz: case Builtin::BI__builtin_ctzl: case Builtin::BI__builtin_ctzll: @@ -11139,6 +11142,7 @@ EvalInfo::EvaluationMode EM = EvalInfo::EM_ConstantExpression; EvalInfo Info(Ctx, Result, EM); Info.InConstantContext = true; + if (!::Evaluate(Result.Val, Info, this)) return false; Index: lib/Basic/Builtins.cpp =================================================================== --- lib/Basic/Builtins.cpp +++ lib/Basic/Builtins.cpp @@ -75,9 +75,12 @@ bool OclCUnsupported = !LangOpts.OpenCL && (BuiltinInfo.Langs & ALL_OCLC_LANGUAGES); bool OpenMPUnsupported = !LangOpts.OpenMP && BuiltinInfo.Langs == OMP_LANG; + bool CPlusPlusUnsupported = + !LangOpts.CPlusPlus && BuiltinInfo.Langs == CXX_LANG; return !BuiltinsUnsupported && !MathBuiltinsUnsupported && !OclCUnsupported && !OclC1Unsupported && !OclC2Unsupported && !OpenMPUnsupported && - !GnuModeUnsupported && !MSModeUnsupported && !ObjCUnsupported; + !GnuModeUnsupported && !MSModeUnsupported && !ObjCUnsupported && + !CPlusPlusUnsupported; } /// initializeBuiltins - Mark the identifiers for all the builtins with their Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -1783,7 +1783,8 @@ } llvm::Constant *constant = nullptr; - if (emission.IsConstantAggregate || D.isConstexpr()) { + if (emission.IsConstantAggregate || D.isConstexpr() || + D.isUsableInConstantExpressions(getContext())) { assert(!capturedByInit && "constant init contains a capturing block?"); constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D); if (constant && trivialAutoVarInit != Index: test/CodeGenCXX/builtin-is-constant-evaluated.cpp =================================================================== --- test/CodeGenCXX/builtin-is-constant-evaluated.cpp +++ test/CodeGenCXX/builtin-is-constant-evaluated.cpp @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm %s -std=c++2a -o %t.ll +// RUN: FileCheck -check-prefix=CHECK-FN-CG -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-STATIC -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-DYN -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-ARR -input-file=%t.ll %s +// RUN: FileCheck -check-prefix=CHECK-FOLD -input-file=%t.ll %s + +using size_t = decltype(sizeof(int)); + +#define CONSTINIT __attribute__((require_constant_initialization)) + +extern "C" [[noreturn]] void BOOM(); +extern "C" void OK(); +extern "C" size_t RANDU(); + +namespace std { +inline constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} // namespace std + +// CHECK-FN-CG-LABEL: define zeroext i1 @_Z3foov() +// CHECK-FN-CG: ret i1 false +bool foo() { + return __builtin_is_constant_evaluated(); +} + +// CHECK-FN-CG-LABEL: define linkonce_odr i32 @_Z1fv() +constexpr int f() { + // CHECK-FN-CG: store i32 13, i32* %n, align 4 + // CHECK-FN-CG: store i32 17, i32* %m, align 4 + // CHECK-FN-CG: %1 = load i32, i32* %m, align 4 + // CHECK-FN-CG: %add = add nsw i32 %1, 13 + // CHECK-FN-CG: ret i32 %add + const int n = __builtin_is_constant_evaluated() && std::is_constant_evaluated() ? 13 : 17; // n == 13 + int m = __builtin_is_constant_evaluated() ? 13 : 17; // m might be 13 or 17 (see below) + char arr[n] = {}; // char[13] + return m + int(sizeof(arr)); +} + +// CHECK-STATIC-DAG: @p = global i32 26, +CONSTINIT int p = f(); // f().m == 13; initialized to 26 +// CHECK-STATIC-DAG: @p2 = global i32 26, +int p2 = f(); // same result without CONSTINIT + +// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init() +// CHECK-DYN: %0 = load i32, i32* @p, align 4 +// CHECK-DYN-NEXT: %call = call i32 @_Z1fv() +// CHECK-DYN-NEXT: %add = add nsw i32 %0, %call +// CHECK-DYN-NEXT: store i32 %add, i32* @q, align 4 +// CHECK-DYN-NEXT: ret void +int q = p + f(); // m == 17 for this call; initialized to 56 + +int y; + +// CHECK-STATIC-DAG: @b = global i32 2, +CONSTINIT int b = __builtin_is_constant_evaluated() ? 2 : y; // static initialization to 2 + +// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init.1() +// CHECK-DYN: %0 = load i32, i32* @y, align 4 +// CHECK-DYN: %1 = load i32, i32* @y, align 4 +// CHECK-DYN-NEXT: %add = add +// CHECK-DYN-NEXT: store i32 %add, i32* @c, +int c = y + (__builtin_is_constant_evaluated() ? 2 : y); // dynamic initialization to y+y + +// CHECK-DYN-LABEL: define internal void @__cxx_global_var_init.2() +// CHECK-DYN: store i32 1, i32* @_ZL1a, align 4 +// CHECK-DYN-NEXT: ret void +const int a = __builtin_is_constant_evaluated() ? y : 1; // dynamic initialization to 1 +const int *a_sink = &a; + +// CHECK-ARR-LABEL: define void @_Z13test_arr_exprv +void test_arr_expr() { + // CHECK-ARR: %x1 = alloca [101 x i8], + char x1[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 101 : 1]; + + // CHECK-ARR: %x2 = alloca [42 x i8], + char x2[std::is_constant_evaluated() && __builtin_is_constant_evaluated() ? 42 : RANDU()]; + + // CHECK-ARR: call i8* @llvm.stacksave() + // CHECK-ARR: %vla = alloca i8, i64 13, + char x3[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? RANDU() : 13]; +} + +// CHECK-ARR-LABEL: define void @_Z17test_new_arr_exprv +void test_new_arr_expr() { + // CHECK-ARR: call i8* @_Znam(i64 17) + new char[std::is_constant_evaluated() || __builtin_is_constant_evaluated() ? 1 : 17]; +} + +// CHECK-FOLD-LABEL: @_Z31test_constant_initialized_locali( +bool test_constant_initialized_local(int k) { + // CHECK-FOLD: store i8 1, i8* %n, + // CHECK-FOLD: store volatile i8* %n, i8** %p, + const bool n = __builtin_is_constant_evaluated() && std::is_constant_evaluated(); + const bool *volatile p = &n; + return *p; +} + +// CHECK-FOLD-LABEL: define void @_Z21test_ir_constant_foldv() +void test_ir_constant_fold() { + // CHECK-FOLD-NEXT: entry: + // CHECK-FOLD-NEXT: call void @OK() + // CHECK-FOLD-NEXT: call void @OK() + // CHECK-FOLD-NEXT: ret void + if (std::is_constant_evaluated()) { + BOOM(); + } else { + OK(); + } + std::is_constant_evaluated() ? BOOM() : OK(); +} + +// CHECK-STATIC-DAG: @ir = constant i32* @i_constant, +int i_constant; +int i_not_constant; +int &ir = __builtin_is_constant_evaluated() ? i_constant : i_not_constant; + +// CHECK-FOLD-LABEL: @_Z35test_ref_initialization_local_scopev() +void test_ref_initialization_local_scope() { + const int i_constant = 42; + const int i_non_constant = 101; + // CHECK-FOLD: store i32* %i_non_constant, i32** %r, + const int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant; +} + +// CHECK-FOLD-LABEL: @_Z22test_ref_to_static_varv() +void test_ref_to_static_var() { + static int i_constant = 42; + static int i_non_constant = 101; + // CHECK-FOLD: store i32* @_ZZ22test_ref_to_static_varvE10i_constant, i32** %r, + int &r = __builtin_is_constant_evaluated() ? i_constant : i_non_constant; +} \ No newline at end of file Index: test/Sema/builtins.c =================================================================== --- test/Sema/builtins.c +++ test/Sema/builtins.c @@ -314,3 +314,9 @@ memcpy(buf, src, 11); // expected-warning{{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}} my_memcpy(buf, src, 11); // expected-warning{{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}} } + +// Test that __builtin_is_constant_evaluated() is not allowed in C +int test_cxx_builtin() { + // expected-error@+1 {{use of unknown builtin '__builtin_is_constant_evaluated'}} + return __builtin_is_constant_evaluated(); +} Index: test/SemaCXX/builtin-is-constant-evaluated.cpp =================================================================== --- test/SemaCXX/builtin-is-constant-evaluated.cpp +++ test/SemaCXX/builtin-is-constant-evaluated.cpp @@ -0,0 +1,121 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu + +using size_t = decltype(sizeof(int)); + +namespace std { +inline constexpr bool is_constant_evaluated() noexcept { + return __builtin_is_constant_evaluated(); +} +} // namespace std + +extern int dummy; // expected-note 1+ {{declared here}} + +static_assert(__builtin_is_constant_evaluated()); +static_assert(noexcept(__builtin_is_constant_evaluated())); + +constexpr bool b = __builtin_is_constant_evaluated(); +static_assert(b); + +const int n = __builtin_is_constant_evaluated() ? 4 : dummy; +static_assert(n == 4); +constexpr int cn = __builtin_is_constant_evaluated() ? 11 : dummy; +static_assert(cn == 11); +// expected-error@+1 {{'bn' must be initialized by a constant expression}} +constexpr int bn = __builtin_is_constant_evaluated() ? dummy : 42; // expected-note {{non-const variable 'dummy' is not allowed}} + +const int n2 = __builtin_is_constant_evaluated() ? dummy : 42; // expected-note {{declared here}} +static_assert(n2 == 42); // expected-error {{static_assert expression is not an integral constant}} +// expected-note@-1 {{initializer of 'n2' is not a constant expression}} + +template +struct Templ { static_assert(V); static_assert(Default); }; +Templ<__builtin_is_constant_evaluated()> x; // type X + +template +void test_if_constexpr() { + if constexpr (__builtin_is_constant_evaluated()) { + static_assert(__is_same(T, int)); + } else { + using Test = typename T::DOES_NOT_EXIST; + } +} +template void test_if_constexpr(); + +void test_array_decl() { + char x[__builtin_is_constant_evaluated() + std::is_constant_evaluated()]; + static_assert(sizeof(x) == 2, ""); +} + +void test_case_stmt(int x) { + switch (x) { + case 0: // OK + case __builtin_is_constant_evaluated(): // expected-note {{previous case}} + case std::is_constant_evaluated() + __builtin_is_constant_evaluated(): // expected-note {{previous case}} + case 1: // expected-error {{duplicate case value '1'}} + case 2: // expected-error {{duplicate case value '2'}} + break; + } +} + +constexpr size_t good_array_size() { + return std::is_constant_evaluated() ? 42 : static_cast(-1); +} + +constexpr size_t bad_array_size() { + return std::is_constant_evaluated() ? static_cast(-1) : 13; +} + +template +constexpr T require_constexpr(T v) { + if (!std::is_constant_evaluated()) + throw "BOOM"; + return v; +} + +void test_new_expr() { + constexpr size_t TooLarge = -1; + auto *x = new int[std::is_constant_evaluated() ? 1 : TooLarge]; // expected-error {{array is too large}} + auto *x2 = new int[std::is_constant_evaluated() ? TooLarge : 1]; // OK + auto *y = new int[1][std::is_constant_evaluated() ? TooLarge : 1]{}; // expected-error {{array is too large}} + auto *y2 = new int[1][require_constexpr(42)]; +} + +void test_alignas_operand() { + alignas(std::is_constant_evaluated() ? 8 : 2) char dummy; + static_assert(__alignof(dummy) == 8); +} + +void test_static_assert_operand() { + static_assert(std::is_constant_evaluated(), ""); +} + +void test_enumerator() { + enum MyEnum { + ZERO = 0, + ONE = std::is_constant_evaluated() + }; + static_assert(ONE == 1, ""); +} + +struct TestBitfieldWidth { + unsigned Bits : std::is_constant_evaluated(); +}; + +void test_operand_of_noexcept_fn() noexcept(std::is_constant_evaluated()); +static_assert(noexcept(test_operand_of_noexcept_fn()), ""); + + +namespace test_ref_initialization { +int x; +int y; +int &r = __builtin_is_constant_evaluated() ? x : y; +static_assert(&r == &x); + +} // namespace test_ref_initialization + +#if defined(__cpp_conditional_explicit) +struct TestConditionalExplicit { + explicit(__builtin_is_constant_evaluated()) TestConditionalExplicit(int) {} +}; +TestConditionalExplicit e = 42; +#endif