diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c60d0ac014e..6de8f64b5ee 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7531,6 +7531,7 @@ extern void set_originating_module (tree, bool friend_p = false); extern tree get_originating_module_decl (tree) ATTRIBUTE_PURE; extern int get_originating_module (tree, bool for_mangle = false) ATTRIBUTE_PURE; extern unsigned get_importing_module (tree, bool = false) ATTRIBUTE_PURE; +extern void check_module_decl_linkage (tree); /* Where current instance of the decl got declared/defined/instantiated. */ extern void set_instantiating_module (tree); diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index bc3a9d0e922..b6e93c1dfca 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -1019,6 +1019,7 @@ finish_static_data_member_decl (tree decl, } cp_finish_decl (decl, init, init_const_expr_p, asmspec_tree, flags); + check_module_decl_linkage (decl); } /* DECLARATOR and DECLSPECS correspond to a class member. The other diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index c1886e6c3b6..b15f5b2496f 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -20153,11 +20153,34 @@ set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNUSED) DECL_MODULE_ATTACH_P (decl) = true; } - if (!module_exporting_p ()) + /* It is ill-formed to export a declaration with internal linkage. However, + at the point this function is called we don't yet always know whether this + declaration has internal linkage; instead we defer this check for callers + to do once visibility has been determined. */ + if (module_exporting_p ()) + DECL_MODULE_EXPORT_P (decl) = true; +} + +/* Checks whether DECL within a module unit has valid linkage for its kind. + Must be called after visibility for DECL has been finalised. */ + +void +check_module_decl_linkage (tree decl) +{ + if (!module_has_cmi_p ()) return; - // FIXME: Check ill-formed linkage - DECL_MODULE_EXPORT_P (decl) = true; + /* An internal-linkage declaration cannot be generally be exported. + But it's OK to export any declaration from a header unit, including + internal linkage declarations. */ + if (!header_module_p () + && DECL_MODULE_EXPORT_P (decl) + && decl_linkage (decl) == lk_internal) + { + error_at (DECL_SOURCE_LOCATION (decl), + "exporting declaration %qD with internal linkage", decl); + DECL_MODULE_EXPORT_P (decl) = false; + } } /* DECL is keyed to CTX for odr purposes. */ diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 7737b0fbf73..9c1be9e2371 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -6606,7 +6606,7 @@ pop_decl_namespace (void) /* Process a namespace-alias declaration. */ void -do_namespace_alias (tree alias, tree name_space) +do_namespace_alias (location_t loc, tree alias, tree name_space) { if (name_space == error_mark_node) return; @@ -6616,7 +6616,7 @@ do_namespace_alias (tree alias, tree name_space) name_space = ORIGINAL_NAMESPACE (name_space); /* Build the alias. */ - alias = build_lang_decl (NAMESPACE_DECL, alias, void_type_node); + alias = build_lang_decl_loc (loc, NAMESPACE_DECL, alias, void_type_node); DECL_NAMESPACE_ALIAS (alias) = name_space; DECL_EXTERNAL (alias) = 1; DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ()); @@ -6628,6 +6628,7 @@ do_namespace_alias (tree alias, tree name_space) return; set_originating_module (alias); + check_module_decl_linkage (alias); /* Emit debug info for namespace alias. */ if (!building_stmt_list_p ()) @@ -8569,6 +8570,7 @@ pushtag (tree name, tree type, TAG_how how) /* Set type visibility now if this is a forward declaration. */ TREE_PUBLIC (decl) = 1; determine_visibility (decl); + check_module_decl_linkage (decl); return type; } @@ -9274,8 +9276,18 @@ push_namespace (tree name, bool make_inline) 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 (name) + { + auto_diagnostic_group d; + error_at (input_location, "exporting namespace %qD with " + "internal linkage", ns); + inform (input_location, "%qD has internal linkage because " + "it was declared in an unnamed namespace", ns); + } + else + error_at (input_location, "exporting unnamed namespace"); + } } if (module_purview_p ()) DECL_MODULE_PURVIEW_P (ns) = true; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 54edadeed7f..08b0cac137d 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -444,7 +444,7 @@ extern tree cp_namespace_decls (tree); extern void set_decl_namespace (tree, tree, bool); extern void push_decl_namespace (tree); extern void pop_decl_namespace (void); -extern void do_namespace_alias (tree, tree); +extern void do_namespace_alias (location_t, tree, tree); extern tree do_class_using_decl (tree, tree); extern tree lookup_arg_dependent (tree, tree, vec *); extern tree search_anon_aggr (tree, tree, bool = false); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 51c34cebe6c..23c6a2fd30e 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -22668,6 +22668,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser) /* Look for the `namespace' keyword. */ cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); /* Look for the identifier. */ + location_t id_location = cp_lexer_peek_token (parser->lexer)->location; identifier = cp_parser_identifier (parser); if (identifier == error_mark_node) return; @@ -22691,7 +22692,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser) cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); /* Register the alias in the symbol table. */ - do_namespace_alias (identifier, namespace_specifier); + do_namespace_alias (id_location, identifier, namespace_specifier); } /* Parse a qualified-namespace-specifier. @@ -23140,6 +23141,8 @@ cp_parser_alias_declaration (cp_parser* parser) check_member_template (decl); } + check_module_decl_linkage (decl); + return decl; } @@ -24202,6 +24205,7 @@ cp_parser_init_declarator (cp_parser* parser, `explicit' constructor cannot be used. */ ((is_direct_init || !is_initialized) ? LOOKUP_NORMAL : LOOKUP_IMPLICIT)); + check_module_decl_linkage (decl); } else if ((cxx_dialect != cxx98) && friend_p && decl && TREE_CODE (decl) == FUNCTION_DECL) @@ -33489,6 +33493,7 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, /* Finish the function. */ fn = finish_function (inline_p); + check_module_decl_linkage (fn); if (modules_p () && !inline_p @@ -34143,6 +34148,8 @@ cp_parser_save_member_function_body (cp_parser* parser, /* Add FN to the queue of functions to be parsed later. */ vec_safe_push (unparsed_funs_with_definitions, fn); + check_module_decl_linkage (fn); + return fn; } diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index a8d0d8c0296..7fa286698ef 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -29915,11 +29915,13 @@ finish_concept_definition (cp_expr id, tree init, tree attrs) tree decl = build_lang_decl_loc (loc, CONCEPT_DECL, *id, boolean_type_node); DECL_CONTEXT (decl) = current_scope (); DECL_INITIAL (decl) = init; + TREE_PUBLIC (decl) = true; if (attrs) cplus_decl_attributes (&decl, attrs, 0); set_originating_module (decl, false); + check_module_decl_linkage (decl); /* Push the enclosing template. */ return push_template_decl (decl); diff --git a/gcc/testsuite/g++.dg/modules/export-3.C b/gcc/testsuite/g++.dg/modules/export-3.C index 6af314b9519..5a001d7cff2 100644 --- a/gcc/testsuite/g++.dg/modules/export-3.C +++ b/gcc/testsuite/g++.dg/modules/export-3.C @@ -25,4 +25,4 @@ namespace { export namespace ns {} // { dg-error "internal linkage" } } -export namespace {} // { dg-error "internal linkage" } +export namespace {} // { dg-error "exporting unnamed namespace" } diff --git a/gcc/testsuite/g++.dg/modules/export-6.C b/gcc/testsuite/g++.dg/modules/export-6.C new file mode 100644 index 00000000000..c59944aa688 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/export-6.C @@ -0,0 +1,36 @@ +// { dg-additional-options "-fmodules-ts" } +// { dg-module-cmi !bad } + +export module bad; +namespace global {} + +export static int x = 123; // { dg-error "internal linkage" } +export static void f(); // { dg-error "internal linkage" } +export static void g() {} // { dg-error "internal linkage" } +export template static void t(); // { dg-error "internal linkage" } +export template static void u() {} // { dg-error "internal linkage" } + +namespace { + export int y = 456; // { dg-error "internal linkage" } + export void h(); // { dg-error "internal linkage" } + export void i() {} // { dg-error "internal linkage" } + export template void v(); // { dg-error "internal linkage" } + export template void w() {} // { dg-error "internal linkage" } + + export namespace ns {} // { dg-error "internal linkage" } + export namespace alias = global; // { dg-error "internal linkage" } + + export struct A {}; // { dg-error "internal linkage" } + export template struct B {}; // { dg-error "internal linkage" } + + export enum E {}; // { dg-error "internal linkage" } + export enum class F {}; // { dg-error "internal linkage" } + + export template using U = int; // { dg-error "internal linkage" } + +#if __cplusplus >= 202002L + export template concept C = true; // { dg-error "internal linkage" "" { target c++20 } } +#endif +} + +export namespace {} // { dg-error "exporting unnamed namespace" } diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc index da68c5d0ac1..97877ad9138 100644 --- a/libcc1/libcp1plugin.cc +++ b/libcc1/libcp1plugin.cc @@ -799,7 +799,7 @@ plugin_add_namespace_alias (cc1_plugin::connection *, tree name = get_identifier (id); tree target = convert_in (target_in); - do_namespace_alias (name, target); + do_namespace_alias (input_location, name, target); return 1; }