Hi! The following patch attempts to implement the paper just by dropping the diagnostics. Because we don't differentiate between "C" types vs. "C++" types, this is less important. Or shall we go even further and not mark the templates or their specializations internally with C++ linkage? I think with reflection this is actually observable, one can call has_c_language_linkage on a reflection of function or variable template specialization. Guess then at least mangle.cc would need to be changed so that e.g. unmangled_name_p ignores DECL_EXTERN_C_FUNCTION_P for function template instantiations or DECL_EXTERN_C_P for variable template instantiations.
2026-09-09 Jakub Jelinek <[email protected]> P2243R0 PR c++/125823 * parser.cc: Implement C++29 P2243R0: Language linkage for templates. (cp_parser_explicit_specialization): Don't diagnose template specialization with C linkage. (cp_parser_explicit_template_declaration): Don't diagnose template with C linkage. * g++.dg/diagnostic/unclosed-extern-c.C: Don't expect error and inform about template with C linkage. * g++.dg/parse/bitfield10.C: Likewise. * g++.dg/template/extern-c.C: Likewise. * g++.dg/cpp29/tmpl-extern-c1.C: New test. * g++.dg/cpp29/tmpl-extern-c2.C: New test. --- a/gcc/cp/parser.cc 2026-09-08 22:18:28.484021504 +0200 +++ b/gcc/cp/parser.cc 2026-09-09 12:47:00.081357116 +0200 @@ -22353,21 +22353,11 @@ cp_parser_explicit_specialization (cp_pa /* We have processed another parameter list. */ ++parser->num_template_parameter_lists; - /* [temp] - - A template ... explicit specialization ... shall not have C - linkage. */ bool need_lang_pop = current_lang_name == lang_name_c; if (need_lang_pop) - { - auto_diagnostic_group d; - error_at (token->location, "template specialization with C linkage"); - maybe_show_extern_c_location (); - - /* Give it C++ linkage to avoid confusing other parts of the - front end. */ - push_lang_context (lang_name_cplusplus); - } + /* Give it C++ linkage to avoid confusing other parts of the + front end. */ + push_lang_context (lang_name_cplusplus); /* Let the front end know that we are beginning a specialization. */ if (begin_specialization ()) @@ -36559,14 +36549,8 @@ cp_parser_explicit_template_declaration cp_parser_skip_to_end_of_block_or_statement (parser); return; } - /* [temp] - - A template ... shall not have C linkage. */ if (current_lang_name == lang_name_c) { - auto_diagnostic_group d; - error_at (location, "template with C linkage"); - maybe_show_extern_c_location (); /* Give it C++ linkage to avoid confusing other parts of the front end. */ push_lang_context (lang_name_cplusplus); --- a/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C 2026-03-27 10:17:15.754304304 +0100 +++ b/gcc/testsuite/g++.dg/diagnostic/unclosed-extern-c.C 2026-09-09 12:59:24.653915865 +0200 @@ -4,9 +4,9 @@ extern "C" { // { dg-line open_extern_c /* Missing close-brace for the extern "C" here. */ -template <typename T> // { dg-error "template with C linkage" } +template <typename T> // { dg-bogus "template with C linkage" } void bar (void); -// { dg-message "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c } +// { dg-bogus "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c } void test (void); // { dg-message "12: to match this '.'" "" { target *-*-* } open_extern_c } --- a/gcc/testsuite/g++.dg/parse/bitfield10.C 2026-03-27 10:17:16.066299212 +0100 +++ b/gcc/testsuite/g++.dg/parse/bitfield10.C 2026-09-09 12:55:14.741084080 +0200 @@ -2,7 +2,7 @@ /* { dg-do "compile" } */ extern "C" { - template <int> class b; /* { dg-error "with C linkage" } */ + template <int> class b; /* { dg-bogus "with C linkage" } */ struct { typedef b: /* { dg-error "at end of input" } */ --- a/gcc/testsuite/g++.dg/template/extern-c.C 2026-03-27 10:17:16.181297336 +0100 +++ b/gcc/testsuite/g++.dg/template/extern-c.C 2026-09-09 12:53:53.393115354 +0200 @@ -2,9 +2,9 @@ template <typename T> void specializable /* Invalid template: within "extern C". */ -extern "C" { // { dg-message "1: 'extern .C.' linkage started here" } +extern "C" { // { dg-bogus "1: 'extern .C.' linkage started here" } -template <typename T> // { dg-error "template with C linkage" } +template <typename T> // { dg-bogus "template with C linkage" } void within_extern_c_braces (void); } @@ -17,9 +17,9 @@ void not_within_extern_c (void); /* Invalid specialization: within "extern C". */ -extern "C" { // { dg-message "1: 'extern .C.' linkage started here" } +extern "C" { // { dg-bogus "1: 'extern .C.' linkage started here" } -template <> // { dg-error "template specialization with C linkage" } +template <> // { dg-bogus "template specialization with C linkage" } void specializable (int); } @@ -34,8 +34,8 @@ void specializable (char); extern "C" template <typename T> // { dg-line open_extern_c_no_braces } void within_extern_c_no_braces (void); -// { dg-error "12: template with C linkage" "" { target *-*-* } open_extern_c_no_braces } -// { dg-message "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c_no_braces } +// { dg-bogus "12: template with C linkage" "" { target *-*-* } open_extern_c_no_braces } +// { dg-bogus "1: 'extern .C.' linkage started here" "" { target *-*-* } open_extern_c_no_braces } /* Nested extern "C" specifications. @@ -46,9 +46,9 @@ extern "C" { extern "C" { } - template <typename T> // { dg-error "template with C linkage" } + template <typename T> // { dg-bogus "template with C linkage" } void within_nested_extern_c (void); - // { dg-message "3: 'extern .C.' linkage started here" "" { target *-*-* } middle_open_extern_c } + // { dg-bogus "3: 'extern .C.' linkage started here" "" { target *-*-* } middle_open_extern_c } extern "C++" { /* Valid template: within extern "C++". */ @@ -57,9 +57,9 @@ extern "C" { extern "C" { // { dg-line last_open_extern_c } /* Invalid template: within "extern C". */ - template <typename T> // { dg-error "template with C linkage" } + template <typename T> // { dg-bogus "template with C linkage" } void within_extern_c_within_extern_cpp (void); - // { dg-message "7: 'extern .C.' linkage started here" "" { target *-*-* } last_open_extern_c } + // { dg-bogus "7: 'extern .C.' linkage started here" "" { target *-*-* } last_open_extern_c } } } } --- a/gcc/testsuite/g++.dg/cpp29/tmpl-extern-c1.C 2026-09-09 12:56:00.477504267 +0200 +++ b/gcc/testsuite/g++.dg/cpp29/tmpl-extern-c1.C 2026-09-09 13:04:14.858236867 +0200 @@ -0,0 +1,35 @@ +// C++29 P2243R0: Language linkage for templates +// { dg-do compile } + +extern "C" { +template <int> struct A {}; // { dg-bogus "template with C linkage" } +template <int> void foo (); // { dg-bogus "template with C linkage" } +#if __cpp_variable_templates >= 201304 +template <int> int v; // { dg-bogus "template with C linkage" } +#endif +#if __cpp_alias_templates >= 200704 +template <int N> using B = A <N>; // { dg-bogus "template with C linkage" } +#endif +#if __cpp_concepts >= 201907 +template <int> concept C = requires { true; }; // { dg-bogus "template with C linkage" } +#endif +template <int, int> struct D {}; // { dg-bogus "template with C linkage" } +template <> struct D <42, 42> {}; // { dg-bogus "template specialization with C linkage" } +template <int N> struct D <N, N + 1> {}; // { dg-bogus "template with C linkage" } +} +extern "C++" { +template <int> struct E {}; +template <int> void bar (); +#if __cpp_variable_templates >= 201304 +template <int> int w; +#endif +#if __cpp_alias_templates >= 200704 +template <int N> using F = E <N>; +#endif +#if __cpp_concepts >= 201907 +template <int> concept G = requires { true; }; +#endif +template <int, int> struct H {}; +template <> struct H <42, 42> {}; +template <int N> struct H <N, N + 1> {}; +} --- a/gcc/testsuite/g++.dg/cpp29/tmpl-extern-c2.C 2026-09-09 12:56:03.679463676 +0200 +++ b/gcc/testsuite/g++.dg/cpp29/tmpl-extern-c2.C 2026-09-09 13:09:33.902183045 +0200 @@ -0,0 +1,39 @@ +// C++29 P2243R0: Language linkage for templates +// { dg-do compile } + +template <int> extern "C" struct A {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-1 } +template <int> extern "C" void foo (); // { dg-error "expected unqualified-id before string constant" } +#if __cpp_variable_templates >= 201304 +template <int> extern "C" int v; // { dg-error "expected unqualified-id before string constant" "" { target c++14 } } +#endif +#if __cpp_alias_templates >= 200704 +template <int N> extern "C" using B = A <N>; // { dg-error "expected unqualified-id before string constant" "" { target c++11 } } +#endif +#if __cpp_concepts >= 201907 +template <int> extern "C" concept C = requires { true; }; // { dg-error "expected unqualified-id before string constant" "" { target c++20 } } +#endif +template <int, int> struct D {}; +template <> extern "C" struct D <42, 42> {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "explicit template specialization cannot have a storage class" "" { target *-*-* } .-1 } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-2 } +template <int N> extern "C" struct D <N, N + 1> {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-1 } +template <int> extern "C++" struct E {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-1 } +template <int> extern "C++" void bar (); // { dg-error "expected unqualified-id before string constant" } +#if __cpp_variable_templates >= 201304 +template <int> extern "C++" int w; // { dg-error "expected unqualified-id before string constant" "" { target c++14 } } +#endif +#if __cpp_alias_templates >= 200704 +template <int N> extern "C++" using F = E <N>; // { dg-error "expected unqualified-id before string constant" "" { target c++11 } } +#endif +#if __cpp_concepts >= 201907 +template <int> extern "C++" concept G = requires { true; }; // { dg-error "expected unqualified-id before string constant" "" { target c++20 } } +#endif +template <int, int> struct H {}; +template <> extern "C++" struct H <42, 42> {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "explicit template specialization cannot have a storage class" "" { target *-*-* } .-1 } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-2 } +template <int N> extern "C++" struct H <N, N + 1> {}; // { dg-error "expected unqualified-id before string constant" } + // { dg-error "extra ';' outside of a function only allowed in C\\\+\\\+11" "" { target c++98_only } .-1 } Jakub
