2025-01-08 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.
--- gcc/cp/cp-tree.h.jj 2025-01-07 13:13:31.042023537 +0100
+++ gcc/cp/cp-tree.h 2025-01-08 12:17:32.349673711 +0100
@@ -7946,6 +7946,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);
--- gcc/cp/semantics.cc.jj 2025-01-07 13:13:31.079023023 +0100
+++ gcc/cp/semantics.cc 2025-01-08 14:51:52.583352223 +0100
@@ -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 vol
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);
--- gcc/cp/parser.cc.jj 2025-01-07 13:13:31.061023273 +0100
+++ gcc/cp/parser.cc 2025-01-08 13:15:05.515288631 +0100
@@ -23107,15 +23107,11 @@ cp_parser_asm_string_expression (cp_pars
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* par
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* p
/* 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_pars
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);
--- gcc/cp/pt.cc.jj 2025-01-07 13:13:31.077023050 +0100
+++ gcc/cp/pt.cc 2025-01-08 12:21:18.154510158 +0100
@@ -19168,9 +19168,12 @@ tsubst_stmt (tree t, tree args, tsubst_f
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;
--- gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C.jj 2025-01-08 12:17:32.781667659 +0100
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-4.C 2025-01-08
13:29:48.266922987 +0100
@@ -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" } } */
--- gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C.jj 2025-01-08
12:38:38.430935760 +0100
+++ gcc/testsuite/g++.dg/cpp1z/constexpr-asm-5.C 2025-01-08
15:09:14.522912468 +0100
@@ -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> ();
+ }
+}
Jakub