Skip to content

Commit

Permalink
c++: Implement P2615 'Meaningful Exports' [PR107688]
Browse files Browse the repository at this point in the history
This clarifies which kinds of declarations may and may not be exported
in various contexts. The patch additionally fixes up some small issues
that were clarified by the paper.

Most of the changes are with regards to export-declarations, which are
applied for all standards modes that we support '-fmodules-ts' for.
However there are also a couple of changes made to linkage specifiers
('extern "C"'); I've applied these as since C++20, to line up with when
modules were actually introduced.

	PR c++/107688

gcc/cp/ChangeLog:

	* name-lookup.cc (push_namespace): Error when exporting
	namespace with internal linkage.
	* parser.h (struct cp_parser): Add new flag
	'in_unbraced_export_declaration_p'.
	* parser.cc (cp_debug_parser): Print the new flag.
	(cp_parser_new): Initialise the new flag.
	(cp_parser_module_export): Set the new flag.
	(cp_parser_class_specifier): Clear and restore the new flag.
	(cp_parser_import_declaration): Imports can now appear directly
	in a linkage specification.
	(cp_parser_declaration): Categorise declarations as "name" or
	"special"; error on the later in contexts where the former is
	required.
	(cp_parser_class_head): Error when exporting a partial
	specialisation.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/contracts-1_a.C: Avoid now-illegal syntax.
	* g++.dg/modules/contracts-2_a.C: Likewise.
	* g++.dg/modules/contracts-3_a.C: Likewise.
	* g++.dg/modules/contracts-4_a.C: Likewise.
	* g++.dg/modules/lang-1_c.C: Clarify now-legal syntax.
	* g++.dg/modules/pr101582-1.C: Remove now-legal XFAILS.
	* g++.dg/template/crash71.C: Update error messages.
	* g++.dg/cpp2a/linkage-spec1.C: New test.
	* g++.dg/modules/export-3.C: New test.
	* g++.dg/modules/export-4_a.C: New test.
	* g++.dg/modules/export-4_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
  • Loading branch information
wreien committed May 1, 2024
1 parent 610415b commit 79420dd
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 34 deletions.
10 changes: 8 additions & 2 deletions gcc/cp/name-lookup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9143,8 +9143,14 @@ push_namespace (tree name, bool make_inline)
{
/* A public namespace is exported only if explicitly marked, or
it contains exported entities. */
if (TREE_PUBLIC (ns) && module_exporting_p ())
DECL_MODULE_EXPORT_P (ns) = true;
if (module_exporting_p ())
{
if (TREE_PUBLIC (ns))
DECL_MODULE_EXPORT_P (ns) = true;
else if (!header_module_p ())
error_at (input_location,
"exporting namespace with internal linkage");
}
if (module_purview_p ())
DECL_MODULE_PURVIEW_P (ns) = true;

Expand Down
105 changes: 83 additions & 22 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ cp_debug_parser (FILE *file, cp_parser *parser)
& THIS_FORBIDDEN));
cp_debug_print_flag (file, "In unbraced linkage specification",
parser->in_unbraced_linkage_specification_p);
cp_debug_print_flag (file, "In unbraced export declaration",
parser->in_unbraced_export_declaration_p);
cp_debug_print_flag (file, "Parsing a declarator",
parser->in_declarator_p);
cp_debug_print_flag (file, "In template argument list",
Expand Down Expand Up @@ -4425,6 +4427,9 @@ cp_parser_new (cp_lexer *lexer)
/* We are not processing an `extern "C"' declaration. */
parser->in_unbraced_linkage_specification_p = false;

/* We aren't parsing an export-declaration. */
parser->in_unbraced_export_declaration_p = false;

/* We are not processing a declarator. */
parser->in_declarator_p = false;

Expand Down Expand Up @@ -15249,10 +15254,6 @@ cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,
goto skip_eol;
cp_parser_require_pragma_eol (parser, token);

if (parser->in_unbraced_linkage_specification_p)
error_at (token->location, "import cannot appear directly in"
" a linkage-specification");

if (mp_state == MP_PURVIEW_IMPORTS || mp_state == MP_PRIVATE_IMPORTS)
{
/* Module-purview imports must not be from source inclusion
Expand All @@ -15273,7 +15274,7 @@ cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,

/* export-declaration.

export declaration
export name-declaration
export { declaration-seq-opt } */

static void
Expand Down Expand Up @@ -15315,7 +15316,13 @@ cp_parser_module_export (cp_parser *parser)
|| cp_lexer_next_token_is_keyword (parser->lexer, RID__EXPORT))
error_at (token->location, "%<export%> not part of following"
" module-directive");

bool saved_in_unbraced_export_declaration_p
= parser->in_unbraced_export_declaration_p;
parser->in_unbraced_export_declaration_p = true;
cp_parser_declaration (parser, NULL_TREE);
parser->in_unbraced_export_declaration_p
= saved_in_unbraced_export_declaration_p;
}

module_kind = mk;
Expand Down Expand Up @@ -15346,27 +15353,29 @@ cp_parser_declaration_seq_opt (cp_parser* parser)
}
}

/* Parse a declaration.
/* Parse a declaration. The distinction between name-declaration
and special-declaration is only since C++20.

declaration:
name-declaration
special-declaration

name-declaration:
block-declaration
nodeclspec-function-declaration
function-definition
template-declaration
explicit-instantiation
explicit-specialization
deduction-guide (C++17)
linkage-specification
namespace-definition
empty-declaration
attribute-declaration
module-import-declaration (modules)

C++17:
deduction-guide

modules:
(all these are only allowed at the outermost level, check
that semantically, for better diagnostics)
module-declaration
module-export-declaration
module-import-declaration
export-declaration
special-declaration:
explicit-instantiation
explicit-specialization
export-declaration (modules)

GNU extension:

Expand All @@ -15389,6 +15398,13 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
return;
}

/* P2615: Determine if we're parsing a name-declaration specifically,
or if special-declarations are OK too. */
bool require_name_decl_p
= (parser->in_unbraced_export_declaration_p
|| (parser->in_unbraced_linkage_specification_p
&& cxx_dialect >= cxx20));

/* Try to figure out what kind of declaration is present. */
cp_token *token1 = cp_lexer_peek_token (parser->lexer);
cp_token *token2 = (token1->type == CPP_EOF
Expand Down Expand Up @@ -15496,13 +15512,30 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
/* `template <>' indicates a template specialization. */
if (token2->type == CPP_LESS
&& cp_lexer_peek_nth_token (parser->lexer, 3)->type == CPP_GREATER)
cp_parser_explicit_specialization (parser);
{
if (require_name_decl_p)
{
auto_diagnostic_group d;
cp_token *token3 = cp_lexer_peek_nth_token (parser->lexer, 3);
location_t loc = make_location (token1, token1, token3);
error_at (loc, "explicit specializations are not permitted here");
if (parser->in_unbraced_export_declaration_p)
inform (loc, "a specialization is always exported alongside "
"its primary template");
}
cp_parser_explicit_specialization (parser);
}
/* `template <' indicates a template declaration. */
else if (token2->type == CPP_LESS)
cp_parser_template_declaration (parser, /*member_p=*/false);
/* Anything else must be an explicit instantiation. */
else
cp_parser_explicit_instantiation (parser);
{
if (require_name_decl_p)
error_at (token1->location,
"explicit instantiations are not permitted here");
cp_parser_explicit_instantiation (parser);
}
}
/* If the next token is `export', it's new-style modules or
old-style template. */
Expand All @@ -15511,7 +15544,14 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
if (!modules_p ())
cp_parser_template_declaration (parser, /*member_p=*/false);
else
cp_parser_module_export (parser);
{
/* We check for nested exports in cp_parser_module_export. */
if (require_name_decl_p
&& !parser->in_unbraced_export_declaration_p)
error_at (token1->location,
"export-declarations are not permitted here");
cp_parser_module_export (parser);
}
}
else if (cp_token_is_module_directive (token1))
{
Expand Down Expand Up @@ -16836,7 +16876,7 @@ cp_parser_function_specifier_opt (cp_parser* parser,

linkage-specification:
extern string-literal { declaration-seq [opt] }
extern string-literal declaration */
extern string-literal name-declaration */

static void
cp_parser_linkage_specification (cp_parser* parser, tree prefix_attr)
Expand Down Expand Up @@ -26795,6 +26835,7 @@ cp_parser_class_specifier (cp_parser* parser)
unsigned char in_statement;
bool in_switch_statement_p;
bool saved_in_unbraced_linkage_specification_p;
bool saved_in_unbraced_export_declaration_p;
tree old_scope = NULL_TREE;
tree scope = NULL_TREE;
cp_token *closing_brace;
Expand Down Expand Up @@ -26846,6 +26887,10 @@ cp_parser_class_specifier (cp_parser* parser)
saved_in_unbraced_linkage_specification_p
= parser->in_unbraced_linkage_specification_p;
parser->in_unbraced_linkage_specification_p = false;
/* Or in an export-declaration. */
saved_in_unbraced_export_declaration_p
= parser->in_unbraced_export_declaration_p;
parser->in_unbraced_export_declaration_p = false;
/* 'this' from an enclosing non-static member function is unavailable. */
tree saved_ccp = current_class_ptr;
tree saved_ccr = current_class_ref;
Expand Down Expand Up @@ -27228,6 +27273,8 @@ cp_parser_class_specifier (cp_parser* parser)
= saved_num_template_parameter_lists;
parser->in_unbraced_linkage_specification_p
= saved_in_unbraced_linkage_specification_p;
parser->in_unbraced_export_declaration_p
= saved_in_unbraced_export_declaration_p;
current_class_ptr = saved_ccp;
current_class_ref = saved_ccr;

Expand Down Expand Up @@ -27515,6 +27562,20 @@ cp_parser_class_head (cp_parser* parser,
permerror (nested_name_specifier_token_start->location,
"extra qualification not allowed");
}
/* The name-declaration of an export-declaration shall not declare
a partial specialization. */
if (template_id_p
&& parser->in_unbraced_export_declaration_p
&& !processing_specialization
&& !processing_explicit_instantiation)
{
auto_diagnostic_group d;
location_t loc = type_start_token->location;
error_at (loc, "declaration of partial specialization in unbraced "
"export-declaration");
inform (loc, "a specialization is always exported alongside its "
"primary template");
}
/* An explicit-specialization must be preceded by "template <>". If
it is not, try to recover gracefully. */
if (at_namespace_scope_p ()
Expand Down
6 changes: 5 additions & 1 deletion gcc/cp/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,13 @@ struct GTY(()) cp_parser {

/* TRUE if the declaration we are parsing is part of a
linkage-specification of the form `extern string-literal
declaration'. */
name-declaration'. */
bool in_unbraced_linkage_specification_p;

/* TRUE if the declaration we are parsing is part of an
export-declaration of the form 'export name-declaration'. */
bool in_unbraced_export_declaration_p;

/* TRUE if we are presently parsing a declarator, after the
direct-declarator. */
bool in_declarator_p;
Expand Down
22 changes: 22 additions & 0 deletions gcc/testsuite/g++.dg/cpp2a/linkage-spec1.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// PR c++/107688
// P2615R1: Meaningful exports: Newly invalid declarations
// { dg-do compile { target c++11 } }

extern "C++" template <typename T> struct A {};

extern "C++" template <typename T> struct A<T*> {};

extern "C++" template <> struct A<int*> {};
// { dg-error "explicit specializations are not permitted here" "" { target c++20 } .-1 }

extern "C++" template struct A<int>;
// { dg-error "explicit instantiations are not permitted here" "" { target c++20 } .-1 }


// These should all still be valid, though
extern "C++" {
template <typename T> struct B {};
template <typename T> struct B<T*> {};
template <> struct B<int*> {};
template struct B<int>;
}
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/contracts-1_a.C
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export module foo;
// { dg-module-cmi foo }

export int violation_count{0};
extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
export extern "C++" void handle_contract_violation(const std::experimental::contract_violation &violation)
{
violation_count++;
printf("violation_count: %d\n", violation_count);
Expand Down
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/contracts-2_a.C
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export module foo;

export int violation_count{0};
export int violation_line_sum{0};
extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
export extern "C++" void handle_contract_violation(const std::experimental::contract_violation &violation)
{
violation_count++;
violation_line_sum += violation.line_number () * violation_count;
Expand Down
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/contracts-3_a.C
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export module foo;
// { dg-module-cmi foo }

export int violation_count{0};
extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
export extern "C++" void handle_contract_violation(const std::experimental::contract_violation &violation)
{
violation_count++;
printf("violation_count: %d\n", violation_count);
Expand Down
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/contracts-4_a.C
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export module foo;
// { dg-module-cmi foo }

export int violation_count{0};
extern "C++" export void handle_contract_violation(const std::experimental::contract_violation &violation)
export extern "C++" void handle_contract_violation(const std::experimental::contract_violation &violation)
{
violation_count++;
printf("violation_count: %d\n", violation_count);
Expand Down
30 changes: 30 additions & 0 deletions gcc/testsuite/g++.dg/modules/export-3.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// P2615R1 invalid declarations
// PR c++/107688
// { dg-additional-options "-fmodules-ts" }
// { dg-module-cmi !bad }

export module bad;

extern "C++" export void foo(); // { dg-error "export-declarations are not permitted here" "" { target c++20 } }

export template <typename T> struct S {};

export template <typename T> struct S<T*> {}; // { dg-error "partial specialization in unbraced export-declaration" }

export template <> struct S<int*> {}; // { dg-error "explicit specializations are not permitted here" }

export template struct S<int>; // { dg-error "explicit instantiations are not permitted here" }

template <> export struct S<double>; // { dg-error "expected unqualified-id" }

export export int x; // { dg-error ".export. may only occur once" }

export { export int y; } // { dg-error ".export. may only occur once" }

namespace {
export namespace ns {} // { dg-error "internal linkage" }
}

export namespace {} // { dg-error "internal linkage" }

// { dg-prune-output "not writing module" }
23 changes: 23 additions & 0 deletions gcc/testsuite/g++.dg/modules/export-4_a.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// P2615R1 valid declarations
// PR c++/107688
// { dg-additional-options "-fmodules-ts" }
// { dg-module-cmi M }

export module M;

export {}
export { static_assert(true); }

export namespace Empty {}
export using namespace Empty;

export {
template <typename T> struct S {};
template <typename T> struct S<T*> { using a = int; };
template <> struct S<int*> { using b = int; };
template struct S<int>;
}

extern "C++" {
export void foo();
}
13 changes: 13 additions & 0 deletions gcc/testsuite/g++.dg/modules/export-4_b.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// PR c++/107688
// { dg-additional-options "-fmodules-ts" }

import M;

using namespace Empty;

int main() {
S<int> x;
S<int*>::b y;
S<int**>::a z;
foo();
}
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/modules/lang-1_c.C
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ extern "C++"
}

extern "C"
import "lang-1_a.H"; // { dg-error "cannot appear directly" }
import "lang-1_a.H"; // OK since p2615r1

extern "C" int cfunc (int); // { dg-error "conflicting declaration" }
extern "C" int cxxfunc (int);
8 changes: 5 additions & 3 deletions gcc/testsuite/g++.dg/modules/pr101582-1.C
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// { dg-additional-options "-fmodules-ts" }
export module pr101582;
// { dg-module-cmi "pr101582" }
export ; // { dg-error "export declaration does not declare anything" "" { xfail *-*-* } }
export [[]]; // { dg-error "export declaration does not declare anything" "" { xfail *-*-* } }
export // { dg-error "export declaration does not declare anything" "" { xfail *-*-* } }

// These are all legal since P2615R1.
export ;
export [[]];
export
{
}
Loading

0 comments on commit 79420dd

Please sign in to comment.