llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: None (Sirraide) <details> <summary>Changes</summary> --- Patch is 35.53 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169686.diff 4 Files Affected: - (modified) clang/lib/AST/ExprConstant.cpp (+40) - (modified) clang/lib/CodeGen/CGDecl.cpp (+7) - (modified) clang/lib/Sema/SemaDeclCXX.cpp (+3) - (added) clang/test/SemaCXX/cxx2c-expansion-stmts.cpp (+1042) ``````````diff diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index d93f87a27e68d..2de1641f6b46a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -6037,6 +6037,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, const VarDecl *VD = dyn_cast_or_null<VarDecl>(D); if (VD && !CheckLocalVariableDeclaration(Info, VD)) return ESR_Failed; + + if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) { + assert(ESD->getInstantiations() && "not expanded?"); + return EvaluateStmt(Result, Info, ESD->getInstantiations(), Case); + } + // Each declaration initialization is its own full-expression. FullExpressionRAII Scope(Info); if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) && @@ -6309,6 +6315,40 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info, return Scope.destroy() ? ESR_Succeeded : ESR_Failed; } + case Stmt::CXXExpansionStmtInstantiationClass: { + BlockScopeRAII Scope(Info); + const auto *Expansion = cast<CXXExpansionStmtInstantiation>(S); + for (const Stmt *Shared : Expansion->getSharedStmts()) { + EvalStmtResult ESR = EvaluateStmt(Result, Info, Shared); + if (ESR != ESR_Succeeded) { + if (ESR != ESR_Failed && !Scope.destroy()) + return ESR_Failed; + return ESR; + } + } + + // No need to push an extra scope for these since they're already + // CompoundStmts. + EvalStmtResult ESR = ESR_Succeeded; + for (const Stmt *Instantiation : Expansion->getInstantiations()) { + ESR = EvaluateStmt(Result, Info, Instantiation); + if (ESR == ESR_Failed || + ShouldPropagateBreakContinue(Info, Expansion, &Scope, ESR)) + return ESR; + if (ESR != ESR_Continue) { + // Succeeded here actually means we encountered a 'break'. + assert(ESR == ESR_Succeeded || ESR == ESR_Returned); + break; + } + } + + // Map Continue back to Succeeded if we fell off the end of the loop. + if (ESR == ESR_Continue) + ESR = ESR_Succeeded; + + return Scope.destroy() ? ESR : ESR_Failed; + } + case Stmt::SwitchStmtClass: return EvaluateSwitch(Result, Info, cast<SwitchStmt>(S)); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 8b1cd83af2396..678d2e9fa743a 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -143,6 +143,13 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { // None of these decls require codegen support. return; + case Decl::CXXExpansionStmt: { + const auto *ESD = cast<CXXExpansionStmtDecl>(&D); + assert(ESD->getInstantiations() && "expansion statement not expanded?"); + EmitStmt(ESD->getInstantiations()); + return; + } + case Decl::NamespaceAlias: if (CGDebugInfo *DI = getDebugInfo()) DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(D)); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 8030aac3d8771..e398710ace63b 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2027,6 +2027,9 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl, // - using-enum-declaration continue; + case Decl::CXXExpansionStmt: + continue; + case Decl::Typedef: case Decl::TypeAlias: { // - typedef declarations and alias-declarations that do not define diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp new file mode 100644 index 0000000000000..71ce71c4f69fe --- /dev/null +++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp @@ -0,0 +1,1042 @@ +// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -verify +namespace std { +template <typename T> +struct initializer_list { + const T* a; + const T* b; + initializer_list(T* a, T* b): a{a}, b{b} {} +}; +} + +struct S { + int x; + constexpr S(int x) : x{x} {} +}; + +void g(int); // #g +template <int n> constexpr int tg() { return n; } + +void f1() { + template for (auto x : {}) static_assert(false, "discarded"); + template for (constexpr auto x : {}) static_assert(false, "discarded"); + template for (auto x : {1}) g(x); + template for (auto x : {1, 2, 3}) g(x); + template for (constexpr auto x : {1}) g(x); + template for (constexpr auto x : {1, 2, 3}) g(x); + template for (constexpr auto x : {1}) tg<x>(); + template for (constexpr auto x : {1, 2, 3}) + static_assert(tg<x>()); + + template for (int x : {1, 2, 3}) g(x); + template for (S x : {1, 2, 3}) g(x.x); + template for (constexpr S x : {1, 2, 3}) tg<x.x>(); + + template for (int x : {"1", S(1), {1, 2}}) { // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'const char[2]'}} \ + expected-error {{no viable conversion from 'S' to 'int'}} \ + expected-error {{excess elements in scalar initializer}} \ + expected-note 3 {{in instantiation of expansion statement requested here}} + g(x); + } + + template for (constexpr auto x : {1, 2, 3, 4}) { // expected-note 3 {{in instantiation of expansion statement requested here}} + static_assert(tg<x>() == 4); // expected-error 3 {{static assertion failed due to requirement 'tg<x>() == 4'}} \ + expected-note {{expression evaluates to '1 == 4'}} \ + expected-note {{expression evaluates to '2 == 4'}} \ + expected-note {{expression evaluates to '3 == 4'}} + } + + + template for (constexpr auto x : {1, 2}) { // expected-note 2 {{in instantiation of expansion statement requested here}} + static_assert(false, "not discarded"); // expected-error 2 {{static assertion failed: not discarded}} + } +} + +template <typename T> +void t1() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (T x : {T(1), T(2)}) g(x); + template for (auto x : {T(1), T(2)}) g(x); + template for (constexpr T x : {T(1), T(2)}) static_assert(tg<x>()); + template for (constexpr auto x : {T(1), T(2)}) static_assert(tg<x>()); +} + +template <typename U> +struct s1 { + template <typename T> + void tf() { + template for (T x : {}) g(x); + template for (constexpr T x : {}) g(x); + template for (U x : {}) g(x); + template for (constexpr U x : {}) g(x); + template for (auto x : {}) g(x); + template for (constexpr auto x : {}) g(x); + template for (T x : {1, 2}) g(x); + template for (U x : {1, 2}) g(x); + template for (U x : {T(1), T(2)}) g(x); + template for (T x : {U(1), U(2)}) g(x); + template for (auto x : {T(1), T(2)}) g(x); + template for (auto x : {U(1), T(2)}) g(x); + template for (constexpr U x : {T(1), T(2)}) static_assert(tg<x>()); + template for (constexpr T x : {U(1), U(2)}) static_assert(tg<x>()); + template for (constexpr auto x : {T(1), U(2)}) static_assert(tg<x>()); + } +}; + +template <typename T> +void t2() { + template for (T x : {}) g(x); +} + +void f2() { + t1<int>(); + t1<long>(); + s1<long>().tf<long>(); + s1<int>().tf<int>(); + s1<int>().tf<long>(); + s1<long>().tf<int>(); + t2<S>(); + t2<S[1231]>(); + t2<S***>(); +} + +template <__SIZE_TYPE__ size> +struct String { + char data[size]; + + template <__SIZE_TYPE__ n> + constexpr String(const char (&str)[n]) { __builtin_memcpy(data, str, n); } + + constexpr const char* begin() const { return data; } + constexpr const char* end() const { return data + size - 1; } +}; + +template <__SIZE_TYPE__ n> +String(const char (&str)[n]) -> String<n>; + +constexpr int f3() { + static constexpr String s{"abcd"}; + int count = 0; + template for (constexpr auto x : s) count++; + return count; +} + +template <String s> +constexpr int tf3() { + int count = 0; + template for (constexpr auto x : s) count++; + return count; +} + +static_assert(f3() == 4); +static_assert(tf3<"1">() == 1); +static_assert(tf3<"12">() == 2); +static_assert(tf3<"123">() == 3); +static_assert(tf3<"1234">() == 4); + +void f4() { + static constexpr String empty{""}; + static constexpr String s{"abcd"}; + template for (auto x : empty) static_assert(false, "not expanded"); + template for (constexpr auto x : s) g(x); + template for (auto x : s) g(x); +} + +struct NegativeSize { + static constexpr const char* str = "123"; + constexpr const char* begin() const { return str + 3; } + constexpr const char* end() const { return str; } +}; + +void negative_size() { + static constexpr NegativeSize n; + template for (auto x : n) g(x); // expected-error {{expansion size must not be negative (was -3)}} + template for (constexpr auto x : n) g(x); // expected-error {{expansion size must not be negative (was -3)}} +} + +template <typename T, __SIZE_TYPE__ size> +struct Array { + T data[size]{}; + constexpr const T* begin() const { return data; } + constexpr const T* end() const { return data + size; } +}; + +struct NotInt { + struct iterator {}; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +void not_int() { + static constexpr NotInt ni; + template for (auto x : ni) g(x); // expected-error {{invalid operands to binary expression}} +} + +static constexpr Array<int, 3> integers{1, 2, 3}; + +constexpr int friend_func(); + +struct Private { + friend constexpr int friend_func(); + +private: + constexpr const int* begin() const { return integers.begin(); } // expected-note 2 {{declared private here}} + constexpr const int* end() const { return integers.end(); } // expected-note 2 {{declared private here}} + +public: + static constexpr int member_func() { + int sum = 0; + static constexpr Private p1; + template for (auto x : p1) sum += x; + return sum; + } +}; + +struct Protected { + friend constexpr int friend_func(); + +protected: + constexpr const int* begin() const { return integers.begin(); } // expected-note 2 {{declared protected here}} + constexpr const int* end() const { return integers.end(); } // expected-note 2 {{declared protected here}} + +public: + static constexpr int member_func() { + int sum = 0; + static constexpr Protected p1; + template for (auto x : p1) sum += x; + return sum; + } +}; + +void access_control() { + static constexpr Private p1; + template for (auto x : p1) g(x); // expected-error 2 {{'begin' is a private member of 'Private'}} expected-error 2 {{'end' is a private member of 'Private'}} + + static constexpr Protected p2; + template for (auto x : p2) g(x); // expected-error 2 {{'begin' is a protected member of 'Protected'}} expected-error 2 {{'end' is a protected member of 'Protected'}} +} + +constexpr int friend_func() { + int sum = 0; + static constexpr Private p1; + template for (auto x : p1) sum += x; + + static constexpr Protected p2; + template for (auto x : p2) sum += x; + return sum; +} + +static_assert(friend_func() == 12); +static_assert(Private::member_func() == 6); +static_assert(Protected::member_func() == 6); + +struct SizeNotICE { + struct iterator { + friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; } + int constexpr operator*() const { return 7; } + + // NOT constexpr! + friend int operator-(iterator, iterator) { return 7; } // expected-note {{declared here}} + friend int operator!=(iterator, iterator) { return 7; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +struct PlusMissing { + struct iterator { + int constexpr operator*() const { return 7; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +struct DerefMissing { + struct iterator { + friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; } + }; + constexpr iterator begin() const { return {}; } + constexpr iterator end() const { return {}; } +}; + +void missing_funcs() { + static constexpr SizeNotICE s1; + static constexpr PlusMissing s2; + static constexpr DerefMissing s3; + + // TODO: This message should start complaining about '!=' once we support the + // proper way of computing the size. + template for (auto x : s1) g(x); // expected-error {{expansion size is not a constant expression}} \ + expected-note {{non-constexpr function 'operator-' cannot be used in a constant expression}} + + template for (auto x : s2) g(x); // expected-error {{invalid operands to binary expression}} + template for (auto x : s3) g(x); // expected-error {{indirection requires pointer operand ('iterator' invalid)}} +} + +namespace adl { +struct ADL { + +}; + +constexpr const int* begin(const ADL&) { return integers.begin(); } +constexpr const int* end(const ADL&) { return integers.end(); } +} + +namespace adl_error { +struct ADLError1 { + constexpr const int* begin() const { return integers.begin(); } +}; + +struct ADLError2 { + constexpr const int* end() const { return integers.end(); } +}; + +constexpr const int* begin(const ADLError2&) { return integers.begin(); } +constexpr const int* end(const ADLError1&) { return integers.end(); } +} + +namespace adl_both { +static constexpr Array<int, 5> integers2{1, 2, 3, 4, 5}; +struct ADLBoth { + // Test that member begin/end are preferred over ADl begin/end. These return + // pointers to a different array. + constexpr const int* begin() const { return integers2.begin(); } + constexpr const int* end() const { return integers2.end(); } +}; + +constexpr const int* begin(const ADLBoth&) { return integers.begin(); } +constexpr const int* end(const ADLBoth&) { return integers.end(); } +} + +constexpr int adl_begin_end() { + static constexpr adl::ADL a; + int sum = 0; + template for (auto x : a) sum += x; + template for (constexpr auto x : a) sum += x; + return sum; +} + +static_assert(adl_begin_end() == 12); + +void adl_mixed() { + static constexpr adl_error::ADLError1 a1; + static constexpr adl_error::ADLError2 a2; + + // These are actually destructuring because there is no + // valid begin/end pair. + template for (auto x : a1) g(x); + template for (auto x : a2) g(x); +} + +constexpr int adl_both_test() { + static constexpr adl_both::ADLBoth a; + int sum = 0; + template for (auto x : a) sum += x; + return sum; +} + +static_assert(adl_both_test() == 15); + +struct A {}; +struct B { int x = 1; }; +struct C { int a = 1, b = 2, c = 3; }; +struct D { + int a = 1; + int* b = nullptr; + const char* c = "3"; +}; + +struct Nested { + A a; + B b; + C c; +}; + +struct PrivateDestructurable { + friend void destructurable_friend(); +private: + int a, b; // expected-note 4 {{declared private here}} +}; + +struct ProtectedDestructurable { + friend void destructurable_friend(); +protected: + int a, b; // expected-note 4 {{declared protected here}} +}; + +void destructuring() { + static constexpr A a; + static constexpr B b; + static constexpr C c; + static constexpr D d; + + template for (auto x : a) static_assert(false, "not expanded"); + template for (constexpr auto x : a) static_assert(false, "not expanded"); + + template for (auto x : b) g(x); + template for (constexpr auto x : b) g(x); + + template for (auto x : c) g(x); + template for (constexpr auto x : c) g(x); + + template for (auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}} + // expected-note@#g {{candidate function not viable: no known conversion from 'int *' to 'int' for 1st argument}} + // expected-note@#g {{candidate function not viable: no known conversion from 'const char *' to 'int' for 1st argument}} + g(x); // expected-error 2 {{no matching function for call to 'g'}} + + } + + template for (constexpr auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}} + // expected-note@#g {{candidate function not viable: no known conversion from 'int *const' to 'int' for 1st argument}} + // expected-note@#g {{candidate function not viable: no known conversion from 'const char *const' to 'int' for 1st argument}} + g(x); // expected-error 2 {{no matching function for call to 'g'}} + } +} + +constexpr int array() { + static constexpr int x[4]{1, 2, 3, 4}; + int sum = 0; + template for (auto y : x) sum += y; + template for (constexpr auto y : x) sum += y; + return sum; +} + +static_assert(array() == 20); + +template <auto v> +constexpr int destructure() { + int sum = 0; + template for (auto x : v) sum += x; + template for (constexpr auto x : v) sum += x; + return sum; +} + +static_assert(destructure<B{10}>() == 20); +static_assert(destructure<C{}>() == 12); +static_assert(destructure<C{3, 4, 5}>() == 24); + +constexpr int nested() { + static constexpr Nested n; + int sum = 0; + template for (constexpr auto x : n) { + static constexpr auto val = x; + template for (auto y : val) { + sum += y; + } + } + template for (constexpr auto x : n) { + static constexpr auto val = x; + template for (constexpr auto y : val) { + sum += y; + } + } + return sum; +} + +static_assert(nested() == 14); + +void access_control_destructurable() { + template for (auto x : PrivateDestructurable()) {} // expected-error 2 {{cannot bind private member 'a' of 'PrivateDestructurable'}} \ + expected-error 2 {{cannot bind private member 'b' of 'PrivateDestructurable'}} + + template for (auto x : ProtectedDestructurable()) {} // expected-error 2 {{cannot bind protected member 'a' of 'ProtectedDestructurable'}} \ + expected-error 2 {{cannot bind protected member 'b' of 'ProtectedDestructurable'}} +} + +void destructurable_friend() { + template for (auto x : PrivateDestructurable()) {} + template for (auto x : ProtectedDestructurable()) {} +} + +struct Placeholder { + A get_value() const { return {}; } + __declspec(property(get = get_value)) A a; +}; + +void placeholder() { + template for (auto x: Placeholder().a) {} +} + +union Union { int a; long b;}; + +struct MemberPtr { + void f() {} +}; + +void overload_set(int); // expected-note 2 {{possible target for call}} +void overload_set(long); // expected-note 2 {{possible target for call}} + +void invalid_types() { + template for (auto x : void()) {} // expected-error {{cannot expand expression of type 'void'}} + template for (auto x : 1) {} // expected-error {{cannot expand expression of type 'int'}} + template for (auto x : 1.f) {} // expected-error {{cannot expand expression of type 'float'}} + template for (auto x : 'c') {} // expected-error {{cannot expand expression of type 'char'}} + template for (auto x : invalid_types) {} // expected-error {{cannot expand expression of type 'void ()'}} + template for (auto x : &invalid_types) {} // expected-error {{cannot expand expression of type 'void (*)()'}} + template for (auto x : &MemberPtr::f) {} // expected-error {{cannot expand expression of type 'void (MemberPtr::*)()'}} + template for (auto x : overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} + template for (auto x : &overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}} + template for (auto x : nullptr) {} // expected-error {{cannot expand expression of type 'std::nullptr_t'}} + template for (auto x : __builtin_strlen) {} // expected-error {{builtin functions must be directly called}} + template for (auto x : Union()) {} // expected-error {{cannot expand expression of type 'Union'}} + template for (auto x : (char*)nullptr) {} // expected-error {{cannot expand expression of type 'char *'}} + template for (auto x : []{}) {} // expected-error {{cannot expand lambda closure type}} + template for (auto x : [x=3]{}) {} // expected-error {{cannot expand lambda closure type}} +} + +struct BeginOnly { + int x{1}; + constexpr const int* begin() const { r... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/169686 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
