https://gcc.gnu.org/g:38a13ea4117b96e467f78b3f86d737ecbe326935
commit r15-6760-g38a13ea4117b96e467f78b3f86d737ecbe326935 Author: Jakub Jelinek <ja...@redhat.com> Date: Fri Jan 10 10:32:36 2025 +0100 c++: Fix up ICEs on constexpr inline asm strings in templates [PR118277] The following patch fixes ICEs when the new inline asm syntax to use C++26 static_assert-like constant expressions in place of string literals is used in templates. As finish_asm_stmt doesn't do any checking for processing_template_decl, this patch also just defers handling those strings in templates rather than say trying fold_non_dependent_expr and if the result is non-dependent and usable, try to extract. The patch also reverts changes to cp_parser_asm_specification_opt which allowed something like void foo () asm ((std::string_view ("bar"))); but it would be really hard to support template <int N> void baz () asm ((std::string_view ("qux"))); (especially with dependent constant expression). And the patch adds extensive test coverage for the various errors. 2025-01-10 Jakub Jelinek <ja...@redhat.com> PR c++/118277 * cp-tree.h (finish_asm_string_expression): Declare. * semantics.cc (finish_asm_string_expression): New function. (finish_asm_stmt): Use it. * parser.cc (cp_parser_asm_string_expression): Likewise. Wrap string into PAREN_EXPR in the ("") case. (cp_parser_asm_definition): Don't ICE if finish_asm_stmt returns error_mark_node. (cp_parser_asm_specification_opt): Revert 2024-06-24 changes. * pt.cc (tsubst_stmt): Don't ICE if finish_asm_stmt returns error_mark_node. * g++.dg/cpp1z/constexpr-asm-4.C: New test. * g++.dg/cpp1z/constexpr-asm-5.C: New test. Diff: --- gcc/cp/cp-tree.h | 1 + gcc/cp/parser.cc | 21 +- gcc/cp/pt.cc | 9 +- gcc/cp/semantics.cc | 43 ++++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C | 83 ++++++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C | 367 +++++++++++++++++++++++++++ 6 files changed, 509 insertions(+), 15 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c08494705e9f..b65a2677b4ec 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7947,6 +7947,7 @@ enum { extern tree begin_compound_stmt (unsigned int); extern void finish_compound_stmt (tree); +extern tree finish_asm_string_expression (location_t, tree); extern tree finish_asm_stmt (location_t, int, tree, tree, tree, tree, tree, bool, bool); extern tree finish_label_stmt (tree); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index f548dc31c2b8..80bc2d8e9e1e 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -23107,15 +23107,11 @@ cp_parser_asm_string_expression (cp_parser *parser) matching_parens parens; parens.consume_open (parser); tree string = cp_parser_constant_expression (parser); - if (string != error_mark_node) - string = cxx_constant_value (string, tf_error); - cexpr_str cstr (string); - if (!cstr.type_check (tok->location)) - return error_mark_node; - if (!cstr.extract (tok->location, string)) - string = error_mark_node; parens.require_close (parser); - return string; + if (TREE_CODE (string) == STRING_CST) + string = build1_loc (tok->location, PAREN_EXPR, TREE_TYPE (string), + string); + return finish_asm_string_expression (tok->location, string); } else if (!cp_parser_is_string_literal (tok)) { @@ -23396,7 +23392,7 @@ cp_parser_asm_definition (cp_parser* parser) inputs, clobbers, labels, inline_p, false); /* If the extended syntax was not used, mark the ASM_EXPR. */ - if (!extended_p) + if (!extended_p && asm_stmt != error_mark_node) { tree temp = asm_stmt; if (TREE_CODE (temp) == CLEANUP_POINT_EXPR) @@ -30044,7 +30040,7 @@ cp_parser_yield_expression (cp_parser* parser) /* Parse an (optional) asm-specification. asm-specification: - asm ( asm-string-expr ) + asm ( string-literal ) If the asm-specification is present, returns a STRING_CST corresponding to the string-literal. Otherwise, returns @@ -30067,8 +30063,9 @@ cp_parser_asm_specification_opt (cp_parser* parser) parens.require_open (parser); /* Look for the string-literal. */ - tree asm_specification = cp_parser_asm_string_expression (parser); - + tree asm_specification = cp_parser_string_literal (parser, + /*translate=*/false, + /*wide_ok=*/false); /* Look for the `)'. */ parens.require_close (parser); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 19df9ccb6afd..67964d41ab8b 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -19168,9 +19168,12 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) outputs, inputs, clobbers, labels, ASM_INLINE_P (t), false); tree asm_expr = tmp; - if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR) - asm_expr = TREE_OPERAND (asm_expr, 0); - ASM_BASIC_P (asm_expr) = ASM_BASIC_P (t); + if (asm_expr != error_mark_node) + { + if (TREE_CODE (asm_expr) == CLEANUP_POINT_EXPR) + asm_expr = TREE_OPERAND (asm_expr, 0); + ASM_BASIC_P (asm_expr) = ASM_BASIC_P (t); + } } break; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 15840e10620c..2039f5266042 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -2133,6 +2133,29 @@ finish_compound_stmt (tree stmt) add_stmt (stmt); } +/* Finish an asm string literal, which can be a string literal + or parenthesized constant expression. Extract the string literal + from the latter. */ + +tree +finish_asm_string_expression (location_t loc, tree string) +{ + if (string == error_mark_node + || TREE_CODE (string) == STRING_CST + || processing_template_decl) + return string; + string = cxx_constant_value (string, tf_error); + if (TREE_CODE (string) == STRING_CST) + string = build1_loc (loc, PAREN_EXPR, TREE_TYPE (string), + string); + cexpr_str cstr (string); + if (!cstr.type_check (loc)) + return error_mark_node; + if (!cstr.extract (loc, string)) + string = error_mark_node; + return string; +} + /* Finish an asm-statement, whose components are a STRING, some OUTPUT_OPERANDS, some INPUT_OPERANDS, some CLOBBERS and some LABELS. Also note whether the asm-statement should be @@ -2159,6 +2182,26 @@ finish_asm_stmt (location_t loc, int volatile_p, tree string, oconstraints = XALLOCAVEC (const char *, noutputs); + string = finish_asm_string_expression (cp_expr_loc_or_loc (string, loc), + string); + if (string == error_mark_node) + return error_mark_node; + for (int i = 0; i < 2; ++i) + for (t = i ? input_operands : output_operands; t; t = TREE_CHAIN (t)) + { + tree s = TREE_VALUE (TREE_PURPOSE (t)); + s = finish_asm_string_expression (cp_expr_loc_or_loc (s, loc), s); + if (s == error_mark_node) + return error_mark_node; + TREE_VALUE (TREE_PURPOSE (t)) = s; + } + for (t = clobbers; t; t = TREE_CHAIN (t)) + { + tree s = TREE_VALUE (t); + s = finish_asm_string_expression (cp_expr_loc_or_loc (s, loc), s); + TREE_VALUE (t) = s; + } + string = resolve_asm_operand_names (string, output_operands, input_operands, labels); diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C new file mode 100644 index 000000000000..4beea612761c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C @@ -0,0 +1,83 @@ +// PR c++/118277 +// { dg-do compile { target c++14 } } +// { dg-options "" } + +using size_t = decltype (sizeof (0)); +struct string_view { + size_t s; + const char *d; + constexpr string_view () : s (0), d (nullptr) {} + constexpr string_view (const char *p) : s (__builtin_strlen (p)), d (p) {} + constexpr string_view (size_t l, const char *p) : s (l), d (p) {} + constexpr size_t size () const noexcept { return s; } + constexpr const char *data () const noexcept { return d; } +}; + +template <typename T> +constexpr T +gen (int n) +{ + switch (n) + { + case 0: return "foo %3,%2,%1,%0"; + case 1: return "=r"; + case 2: return "r"; + case 3: return "memory"; + case 4: return "cc"; + case 5: return "goo %3,%2,%1,%0"; + case 6: return "hoo %3,%2,%1,%0"; + case 7: return "ioo"; + case 8: return "joo"; + case 9: return "koo"; + default: return ""; + } +} + +int +bar () +{ + int a, b; + asm ((gen <string_view> (0)) + : (gen <string_view> (1)) (a), (gen <string_view> (1)) (b) + : (gen <string_view> (2)) (1), (gen <string_view> (2)) (2) + : (gen <string_view> (3)), (gen <string_view> (4))); + asm ((gen <string_view> (7))); + return a + b; +} + +template <typename T, typename U> +U +baz () +{ + U a, b; + asm ((gen <T> (5)) + : (gen <T> (1)) (a), (gen <T> (1)) (b) + : (gen <T> (2)) (U(1)), (gen <T> (2)) (U(2)) + : (gen <T> (3)), (gen <T> (4))); + asm ((gen <string_view> (8))); + return a + b; +} + +template <typename T, typename U> +U +qux () +{ + U a, b; + asm ((gen <T> (6)) + : (gen <T> (1)) (a), (gen <T> (1)) (b) + : (gen <T> (2)) (U(1)), (gen <T> (2)) (U(2)) + : (gen <T> (3)), (gen <T> (4))); + asm ((gen <string_view> (9))); + return a + b; +} + +int +corge () +{ + return qux <string_view, int> (); +} + +/* { dg-final { scan-assembler "foo" } } */ +/* { dg-final { scan-assembler "hoo" } } */ +/* { dg-final { scan-assembler "ioo" } } */ +/* { dg-final { scan-assembler "koo" } } */ diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C new file mode 100644 index 000000000000..1c20b9dfec1e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C @@ -0,0 +1,367 @@ +// PR c++/118277 +// { dg-do compile { target c++11 } } +// { dg-options "" } +// Override any default-'-fno-exceptions': +// { dg-additional-options -fexceptions } + +struct A {}; +struct B { int size; }; +struct C { constexpr int size () const { return 0; } }; +struct D { constexpr int size () const { return 0; } int data; }; +struct E { int size = 0; + constexpr const char *data () const { return ""; } }; +struct F { constexpr const char *size () const { return ""; } + constexpr const char *data () const { return ""; } }; +struct G { constexpr long size () const { return 0; } + constexpr float data () const { return 0.0f; } }; +struct H { short size () const { return 0; } + constexpr const char *data () const { return ""; } }; +struct I { constexpr signed char size () const { return 0; } + const char *data () const { return ""; } }; +struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" } + constexpr const char *data () const { return ""; }; + constexpr J (int x) : j (x) {} + int j; }; +struct K { constexpr operator int () { return 4; } }; +struct L { constexpr operator const char * () { return "test"; } }; +struct M { constexpr K size () const { return {}; } + constexpr L data () const { return {}; } }; +#if __cpp_constexpr_dynamic_alloc >= 201907L +struct N { constexpr int size () const { return 3; } + constexpr const char *data () const { return new char[3] { 'b', 'a', 'd' }; } }; // { dg-error "'\\\* N\\\(\\\).N::data\\\(\\\)' is not a constant expression because allocated storage has not been deallocated" "" { target c++20 } } +#endif +constexpr const char a[] = { 't', 'e', 's', 't' }; +struct O { constexpr int size () const { return 4; } + constexpr const char *data () const { return a; } }; +struct P { constexpr int size () const { return 4 - p; } + constexpr const char *data () const { return &a[p]; } + constexpr P (int x) : p (x) {} + int p; }; +struct Q { constexpr int size () const { return 4 - q; } + constexpr const char *data () const { return &"test"[q]; } + constexpr Q (int x) : q (x) {} + int q; }; +struct R { constexpr int size () const { return 4 - r; } + constexpr const char *d () const { return "test"; } + constexpr const char *data () const { return d () + r; } + constexpr R (int x) : r (x) {} + int r; }; +struct S { constexpr float size (float) const { return 42.0f; } + constexpr int size (void * = nullptr) const { return 4; } + constexpr double data (double) const { return 42.0; } + constexpr const char *data (int = 0) const { return "test"; } }; +using size_t = decltype (sizeof (0)); +struct string_view { + size_t s; + const char *d; + constexpr string_view () : s (0), d (nullptr) {} + constexpr string_view (const char *p) : s (__builtin_strlen (p)), d (p) {} + constexpr string_view (size_t l, const char *p) : s (l), d (p) {} + constexpr size_t size () const noexcept { return s; } + constexpr const char *data () const noexcept { return d; } +}; +template <typename T, size_t N> +struct array { + constexpr size_t size () const { return N; } + constexpr const T *data () const { return a; } + const T a[N]; +}; +struct U { constexpr operator const char * () const { return u; } + char u[5] = "test"; }; +#if __cplusplus >= 201402L +struct V { constexpr auto size () const { return K {}; } + constexpr auto data () const { return U {}; } }; +#endif +struct W { constexpr int size (int) const { return 4; } + constexpr const char *data () const { return "test"; } }; +struct X { constexpr int size () const { return 4; } + constexpr const char *data (int) const { return "test"; } }; +struct Y { constexpr int size () { return 4; } + constexpr const char *data (int) { return "test"; } }; +#if __cpp_concepts >= 201907L +struct Z { constexpr int size (auto...) const { return 4; } + constexpr const char *data (auto...) const { return "test"; } }; +#endif + +void +foo () +{ + int v1, v2; + asm (""); + asm (("")); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '\\\(\\\"\\\"\\\)', which is of non-class type 'const char \\\[1\\\]'" "" { target *-*-* } .-1 } + asm (("" + 0)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '\\\(const char\\\*\\\)\\\"\\\"', which is of non-class type 'const char\\\*'" "" { target *-*-* } .-1 } + asm ((0) ::); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '0', which is of non-class type 'int'" "" { target *-*-* } .-1 } + asm ("" : (A {}) (v1)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct A' has no member named 'size'" "" { target *-*-* } .-1 } + asm ("" : : (B {}) (1)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct B' has no member named 'data'" "" { target *-*-* } .-1 } + asm ("" ::: (C {})); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct C' has no member named 'data'" "" { target *-*-* } .-1 } + asm ((D {})); // { dg-error "'D\\\(\\\).D::data' cannot be used as a function" } + asm ("" : (E {}) (v1)); // { dg-error "'E\\\{0\\\}.E::size' cannot be used as a function" } + __asm ("" :: (F {}) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be implicitly convertible to 'std::size_t'" } + // { dg-error "could not convert 'F\\\(\\\).F::size\\\(\\\)' from 'const char\\\*' to '\[^']*'" "" { target *-*-* } .-1 } + // { dg-error "conversion from 'const char\\\*' to '\[^']*' in a converted constant expression" "" { target *-*-* } .-2 } + asm ("" : : : (G {})); // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" } + // { dg-error "could not convert 'G\\\(\\\).G::data\\\(\\\)' from 'float' to 'const char\\\*'" "" { target *-*-* } .-1 } + asm ("" : "=r" (v1), (H {}) (v2)); // { dg-error "call to non-'constexpr' function 'short int H::size\\\(\\\) const'" } + // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 } + asm ((I {})); // { dg-error "call to non-'constexpr' function 'const char\\\* I::data\\\(\\\) const'" } + // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target *-*-* } .-1 } + + asm ((J (0))); + asm ("" :: (J (1)) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" } + asm ((M {})); +#if __cpp_constexpr_dynamic_alloc >= 201907L + asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } } +#endif + asm ((O {})); + asm ((P (0))); + asm ((P (2))); + asm ((Q (0))); + asm ((Q (1))); + asm ((R (0))); + asm ((R (2))); + asm ((S {})); + + asm ((string_view {})); + asm ((string_view ("test"))); + asm ((string_view ("א"))); + asm ((string_view (0, nullptr))); + asm ((string_view (4, "testwithextrachars"))); + asm ((string_view (42, "test"))); // { dg-error "array subscript value '41' is outside the bounds of array type 'const char \\\[5\\\]'" } + // { dg-error "constexpr string 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-1 } + + asm ((array<char, 2> { 'O', 'K' })); + asm ((array<wchar_t, 2> { L'O', L'K' })); // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" } + // { dg-error "could not convert 'array<wchar_t, 2>{const wchar_t \\\[2\\\]{\[0-9]+, \[0-9]+}}.array<wchar_t, 2>::data\\\(\\\)' from 'const wchar_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-1 } + asm ((array<char, 4> { 't', 'e', 's', 't' })); + { + constexpr auto a = array<char, 4> { 't', 'e', 's', 't' }; + asm ((a)); + } + +#if __cplusplus >= 201402L + asm ((V {})); +#endif + asm ((W {})); // { dg-error "no matching function for call to 'W::size\\\(\\\)'" } + asm ((X {})); // { dg-error "no matching function for call to 'X::data\\\(\\\)'" } + asm ((Y {})); // { dg-error "no matching function for call to 'Y::data\\\(\\\)'" } +#if __cpp_concepts >= 201907L + asm ((Z {}) :::); +#endif +} + +template <typename TT> +void +bar () +{ + int v1, v2; + asm (""); + asm (("")); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '\\\(\\\"\\\"\\\)', which is of non-class type 'const char \\\[1\\\]'" "" { target *-*-* } .-1 } + asm (("" + 0)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '\\\(const char\\\*\\\)\\\"\\\"', which is of non-class type 'const char\\\*'" "" { target *-*-* } .-1 } + asm ((0) ::); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '0', which is of non-class type 'int'" "" { target *-*-* } .-1 } + asm ("" : (A {}) (v1)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct A' has no member named 'size'" "" { target *-*-* } .-1 } + asm ("" : : (B {}) (1)); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct B' has no member named 'data'" "" { target *-*-* } .-1 } + asm ("" ::: (C {})); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "'struct C' has no member named 'data'" "" { target *-*-* } .-1 } + asm ((D {})); // { dg-error "'D\\\(\\\).D::data' cannot be used as a function" } + asm ("" : (E {}) (v1)); // { dg-error "'E\\\{0\\\}.E::size' cannot be used as a function" } + __asm ("" :: (F {}) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be implicitly convertible to 'std::size_t'" } + // { dg-error "could not convert 'F\\\(\\\).F::size\\\(\\\)' from 'const char\\\*' to '\[^']*'" "" { target *-*-* } .-1 } + // { dg-error "conversion from 'const char\\\*' to '\[^']*' in a converted constant expression" "" { target *-*-* } .-2 } + asm ("" : : : (G {})); // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" } + // { dg-error "could not convert 'G\\\(\\\).G::data\\\(\\\)' from 'float' to 'const char\\\*'" "" { target *-*-* } .-1 } + asm ("" : "=r" (v1), (H {}) (v2)); // { dg-error "call to non-'constexpr' function 'short int H::size\\\(\\\) const'" } + // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 } + asm ((I {})); // { dg-error "call to non-'constexpr' function 'const char\\\* I::data\\\(\\\) const'" } + // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target *-*-* } .-1 } + + asm ((J (0))); + asm ("" :: (J (1)) (1)); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" } + asm ((M {})); +#if __cpp_constexpr_dynamic_alloc >= 201907L + asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } } +#endif + asm ((O {})); + asm ((P (0))); + asm ((P (2))); + asm ((Q (0))); + asm ((Q (1))); + asm ((R (0))); + asm ((R (2))); + asm ((S {})); + + asm ((string_view {})); + asm ((string_view ("test"))); + asm ((string_view ("א"))); + asm ((string_view (0, nullptr))); + asm ((string_view (4, "testwithextrachars"))); + asm ((string_view (42, "test"))); // { dg-error "array subscript value '41' is outside the bounds of array type 'const char \\\[5\\\]'" } + // { dg-error "constexpr string 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-1 } + + asm ((array<char, 2> { 'O', 'K' })); + asm ((array<wchar_t, 2> { L'O', L'K' })); // { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" } + // { dg-error "could not convert 'array<wchar_t, 2>{const wchar_t \\\[2\\\]{\[0-9]+, \[0-9]+}}.array<wchar_t, 2>::data\\\(\\\)' from 'const wchar_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-1 } + asm ((array<char, 4> { 't', 'e', 's', 't' })); + { + constexpr auto a = array<char, 4> { 't', 'e', 's', 't' }; + asm ((a)); + } + +#if __cplusplus >= 201402L + asm ((V {})); +#endif + asm ((W {})); // { dg-error "no matching function for call to 'W::size\\\(\\\)'" } + asm ((X {})); // { dg-error "no matching function for call to 'X::data\\\(\\\)'" } + asm ((Y {})); // { dg-error "no matching function for call to 'Y::data\\\(\\\)'" } +#if __cpp_concepts >= 201907L + asm ((Z {}) :::); +#endif +} + +void +baz () +{ + bar<int> (); +} + +namespace NN +{ + template <typename T> + struct A { + constexpr int size () const = delete; + constexpr const char *data () const { return "test"; } }; +#if __cpp_concepts >= 201907L + template <typename T> + struct B { + constexpr int size () const { return 4; } + constexpr const char *data () const requires false { return "test"; } }; +#endif + class C { + constexpr int size () const = delete; + constexpr const char *data () const { return "test"; } }; +#if __cplusplus >= 201402L + struct D { + constexpr int size () { return 4; } + constexpr int size () const { return 3; } + constexpr const char *data () { return "test"; } + constexpr const char *data () const { return "ehlo"; } }; +#endif + struct E { + constexpr int size () const { return 4; } + constexpr const char *data () const { return "test"; } }; + constexpr E operator ""_myd (const char *, size_t) { return E {}; } + constexpr E operator + (const char *, const E &) { return E {}; } + struct H { + static constexpr int size () { return 7; } + static constexpr const char *data () { return "message"; } }; + struct I { + static constexpr int size () { return 0; } + static constexpr const char *data () { return nullptr; } }; +#if __cplusplus >= 201402L + struct J { + static constexpr int size () { return 0; } + static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } } +#endif +#if __cpp_if_consteval >= 202106L + struct K { + static constexpr int size () { if consteval { return 4; } else { throw 1; } } + static constexpr const char *data () { return "test"; } + }; + struct L { + static constexpr int size () { return 4; } + static constexpr const char *data () { if consteval { return "test"; } else { throw 1; } } + }; + struct M { + static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } } + static constexpr const char *data () { return "test"; } + }; + struct N { + static constexpr int size () { return 4; } + static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } } + }; +#endif + struct O { constexpr int operator () () const { return 12; } }; + struct P { constexpr const char *operator () () const { return "another test"; } }; + struct Q { O size; P data; }; + constexpr int get_size () { return 16; } + constexpr const char *get_data () { return "yet another test"; } + struct R { int (*size) () = NN::get_size; + const char *(*data) () = NN::get_data; }; + + void + bar () + { + asm ((A<int> {})); // { dg-error "use of deleted function 'constexpr int NN::A<T>::size\\\(\\\) const \\\[with T = int\\\]'" } +#if __cpp_concepts >= 201907L + asm ((B<short> {})); // { dg-error "no matching function for call to 'NN::B<short int>::data\\\(\\\)'" "" { target c++20 } } +#endif + asm ((C {})); // { dg-error "use of deleted function 'constexpr int NN::C::size\\\(\\\) const'" } + // { dg-error "'constexpr const char\\\* NN::C::data\\\(\\\) const' is private within this context" "" { target *-*-* } .-1 } +#if __cplusplus >= 201402L + asm ((D {})); +#endif + asm (("foo"_myd)); + asm (("foo" + E {})); + asm ((H {})); + asm ((I {})); +#if __cplusplus >= 201402L + asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } } +#endif +#if __cpp_if_consteval >= 202106L + asm ((K {})); + asm ((L {})); + asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } } + asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } } +#endif + asm ((Q {})); + asm ((R {})); + } + + template <typename TT, typename UU, typename VV, typename WW> + void + baz () + { + asm ((A<VV> {})); // { dg-error "use of deleted function 'constexpr int NN::A<T>::size\\\(\\\) const \\\[with T = int\\\]'" } +#if __cpp_concepts >= 201907L + asm ((B<WW> {})); // { dg-error "no matching function for call to 'NN::B<short int>::data\\\(\\\)'" "" { target c++20 } } +#endif + asm ((C {})); // { dg-error "use of deleted function 'constexpr int NN::C::size\\\(\\\) const'" } + // { dg-error "'constexpr const char\\\* NN::C::data\\\(\\\) const' is private within this context" "" { target *-*-* } .-1 } +#if __cplusplus >= 201402L + asm ((D {})); +#endif + asm (("foo"_myd)); + asm (("foo" + E {})); + asm ((H {})); + asm ((I {})); +#if __cplusplus >= 201402L + asm ((J {})); // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } } +#endif +#if __cpp_if_consteval >= 202106L + asm ((K {})); + asm ((L {})); + asm ((M {})); // { dg-error "constexpr string 'size\\\(\\\)' must be a constant expression" "" { target c++23 } } + asm ((N {})); // { dg-error "constexpr string 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } } +#endif + asm ((Q {})); + asm ((R {})); + asm ((TT {})); + asm ((UU {})); // { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" } + // { dg-error "request for member 'size' in '0', which is of non-class type 'long int'" "" { target *-*-* } .-1 } + } + void + qux () + { + baz<E, long, int, short> (); + } +}