mbox series

[v3,0/5] c++/modules: Implement P1815 "Translation-unit-local entities"

Message ID 6709282a.a70a0220.1dd501.9a14@mx.google.com
Headers show
Series c++/modules: Implement P1815 "Translation-unit-local entities" | expand

Message

Nathaniel Shead Oct. 11, 2024, 1:29 p.m. UTC
This patch series implements most of the changes made by P1815.  It also
cleans up a few bugs found along the way that impacted tests I wrote.

The whole patch series was bootstrapped on x86_64-pc-linux-gnu and
aarch64-unknown-linux-gnu with no regressions.

A range-diff against v2 is attached; the main changes are responses to
comments left on previous versions of this patch series.

Nathaniel Shead (5):
  c++/modules: Detect exposures of TU-local entities
  c++/modules: Ignore TU-local entities where necessary
  c++/modules: Support unnamed namespaces in header units
  c++/modules: Check linkage for exported declarations
  c++/modules: Validate external linkage definitions in header units
    [PR116401]

 gcc/c-family/c.opt                            |   4 +
 gcc/cp/cp-objcp-common.cc                     |   1 +
 gcc/cp/cp-tree.def                            |   6 +
 gcc/cp/cp-tree.h                              |  22 +-
 gcc/cp/decl.cc                                |   4 +
 gcc/cp/decl2.cc                               |   1 +
 gcc/cp/module.cc                              | 749 +++++++++++++++---
 gcc/cp/name-lookup.cc                         |  30 +-
 gcc/cp/name-lookup.h                          |   2 +-
 gcc/cp/parser.cc                              |   9 +-
 gcc/cp/pt.cc                                  | 100 ++-
 gcc/doc/invoke.texi                           |  19 +-
 gcc/testsuite/g++.dg/modules/block-decl-2.C   |   2 +-
 gcc/testsuite/g++.dg/modules/export-3.C       |   2 +-
 gcc/testsuite/g++.dg/modules/export-6.C       |  35 +
 gcc/testsuite/g++.dg/modules/hdr-2.H          | 172 ++++
 gcc/testsuite/g++.dg/modules/internal-1.C     |  15 +-
 gcc/testsuite/g++.dg/modules/internal-3.C     |  18 +
 gcc/testsuite/g++.dg/modules/internal-4_a.H   |   4 +
 gcc/testsuite/g++.dg/modules/internal-4_b.C   | 124 +++
 gcc/testsuite/g++.dg/modules/internal-5_a.C   | 110 +++
 gcc/testsuite/g++.dg/modules/internal-5_b.C   |  30 +
 gcc/testsuite/g++.dg/modules/internal-6.C     |  24 +
 gcc/testsuite/g++.dg/modules/internal-7_a.C   |  75 ++
 gcc/testsuite/g++.dg/modules/internal-7_b.C   |  21 +
 gcc/testsuite/g++.dg/modules/internal-8_a.C   |  35 +
 gcc/testsuite/g++.dg/modules/internal-9_a.H   |  28 +
 gcc/testsuite/g++.dg/modules/internal-9_b.C   |  29 +
 gcc/testsuite/g++.dg/modules/linkage-2.C      |   5 +-
 gcc/testsuite/g++.dg/modules/macro-4_c.H      |   2 +-
 gcc/testsuite/g++.dg/modules/pr106761.h       |   2 +-
 gcc/testsuite/g++.dg/modules/pr98843_b.H      |   2 +-
 gcc/testsuite/g++.dg/modules/pr99468.H        |   2 +-
 gcc/testsuite/g++.dg/modules/pragma-1_a.H     |   2 +-
 gcc/testsuite/g++.dg/modules/tpl-ary-1.h      |   2 +-
 .../g++.dg/modules/xtreme-header-8.C          |   8 +
 libcc1/libcp1plugin.cc                        |   2 +-
 37 files changed, 1555 insertions(+), 143 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/export-6.C
 create mode 100644 gcc/testsuite/g++.dg/modules/hdr-2.H
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-3.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_a.H
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-6.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_a.H
 create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/xtreme-header-8.C

Range-diff against v2:
1:  4a032de3f30 ! 1:  36b944bfe8c c++/modules: Detect exposures of TU-local entities
    @@ Commit message
         follow-up patch for easier review, as it has broader implications for
         streaming.
     
    +    TU-local lambdas are also not yet properly implemented, due to other
    +    bugs with regards to LAMBDA_TYPE_EXTRA_SCOPE not being set in all cases
    +    that it probably should be (see also PR c++/116568).  We can revisit
    +    this once that issue has been fixed.
    +
         Finally, this patch makes a couple of small adjustments to the modules
         streaming logic to prune any leftover TU-local deps (that aren't
         erroneous exposures).  This is required for this patch to ensure that
    @@ Commit message
     
         gcc/cp/ChangeLog:
     
    -            * cp-tree.h (TYPE_DEPENDENT_P_VALID): Fix whitespace.
    -            (TYPE_DEFINED_IN_INITIALIZER_P): New accessor.
                 * module.cc (DB_IS_INTERNAL_BIT): Rename to...
                 (DB_TU_LOCAL_BIT): ...this.
                 (DB_REFS_INTERNAL_BIT): Rename to...
    @@ Commit message
                 diagnostic messages to report exposures of TU-local entities.
                 (depset::tarjan::connect): Don't include any TU-local depsets.
                 (depset::hash::connect): Likewise.
    -            (trees_out::core_bools): Stream TYPE_LANG_FLAG_7.
    -            (trees_in::core_bools): Read it.
    -            * parser.h (struct cp_parser::in_initializer_p): New flag.
    -            * parser.cc (cp_debug_parser): Print the new flag.
    -            (cp_parser_new): Set the new flag to false.
    -            (cp_parser_lambda_expression): Mark whether the lambda was
    -            defined in an initializer.
    -            (cp_parser_initializer): Set the new flag to true while parsing.
    -            (cp_parser_class_head): Mark whether the class was defined in an
    -            initializer.
    -            (cp_parser_concept_definition): Set the new flag to true while
    -            parsing.
     
         gcc/testsuite/ChangeLog:
     
    @@ Commit message
                 * g++.dg/modules/linkage-2.C: Adjust messages, remove XFAILS.
                 * g++.dg/modules/internal-3.C: New test.
                 * g++.dg/modules/internal-4_a.H: New test.
    -            * g++.dg/modules/internal-4_a.C: New test.
    +            * g++.dg/modules/internal-4_b.C: New test.
     
         Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
    -
    - ## gcc/cp/cp-tree.h ##
    -@@ gcc/cp/cp-tree.h: extern GTY(()) tree cp_global_trees[CPTI_MAX];
    -       AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM)
    -       TEMPLATE_TEMPLATE_PARM_SIMPLE_P (in TEMPLATE_TEMPLATE_PARM)
    -    6: TYPE_DEPENDENT_P_VALID
    -+   7: TYPE_DEFINED_IN_INITIALIZER_P
    - 
    -    Usage of DECL_LANG_FLAG_?:
    -    0: DECL_TEMPLATE_PARM_P (in PARM_DECL, CONST_DECL, TYPE_DECL, or TEMPLATE_DECL)
    -@@ gcc/cp/cp-tree.h: enum languages { lang_c, lang_cplusplus };
    - 
    - /* True if dependent_type_p has been called for this type, with the
    -    result that TYPE_DEPENDENT_P is valid.  */
    --#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6(NODE)
    -+#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6 (NODE)
    -+
    -+/* True if this type was defined in an initializer.  Used for determining
    -+   whether an entity is TU-local.  */
    -+#define TYPE_DEFINED_IN_INITIALIZER_P(NODE) TYPE_LANG_FLAG_7 (NODE)
    - 
    - /* Nonzero if this type is const-qualified.  */
    - #define CP_TYPE_CONST_P(NODE)				\
    +    Reviewed-by: Jason Merrill <jason@redhat.com>
     
      ## gcc/cp/module.cc ##
     @@ gcc/cp/module.cc: private:
    @@ gcc/cp/module.cc: public:
        private:
          static bool add_binding_entity (tree, WMB_Flags, void *);
      
    -@@ gcc/cp/module.cc: trees_out::core_bools (tree t, bits_out& bits)
    -       WB (t->type_common.lang_flag_4);
    -       WB (t->type_common.lang_flag_5);
    -       WB (t->type_common.lang_flag_6);
    -+      WB (t->type_common.lang_flag_7);
    -       WB (t->type_common.typeless_storage);
    -     }
    - 
    -@@ gcc/cp/module.cc: trees_in::core_bools (tree t, bits_in& bits)
    -       RB (t->type_common.lang_flag_4);
    -       RB (t->type_common.lang_flag_5);
    -       RB (t->type_common.lang_flag_6);
    -+      RB (t->type_common.lang_flag_7);
    -       RB (t->type_common.typeless_storage);
    -     }
    - 
     @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
        return slot ? *slot : NULL;
      }
    @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
     +     is used to declare only TU-local entities.
     +
     +     We consider types with names for linkage purposes as having names, since
    -+     these aren't really TU-local, and also consider constraint-expressions
    -+     as initializers.  */
    ++     these aren't really TU-local.  */
     +  if (TREE_CODE (decl) == TYPE_DECL
     +      && TYPE_ANON_P (type)
     +      && !DECL_SELF_REFERENCE_P (decl)
    @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
     +      && !(UNSCOPED_ENUM_P (type) && TYPE_VALUES (type)))
     +    {
     +      tree main_decl = TYPE_MAIN_DECL (type);
    -+      if (!TYPE_DEFINED_IN_INITIALIZER_P (type)
    -+	  && !DECL_CLASS_SCOPE_P (main_decl)
    -+	  && !decl_function_context (main_decl))
    ++      if (!DECL_CLASS_SCOPE_P (main_decl)
    ++	  && !decl_function_context (main_decl)
    ++	  /* FIXME: Lambdas defined outside initializers.  We'll need to more
    ++	     thoroughly set LAMBDA_TYPE_EXTRA_SCOPE to check this.  */
    ++	  && !LAMBDA_TYPE_P (type))
     +	{
     +	  if (explain)
     +	    inform (loc, "%qT has no name and is not defined within a class, "
    @@ gcc/cp/module.cc: module_state::write_begin (elf_out *to, cpp_reader *reader,
      #endif
      
     -  /* Determine Strongy Connected Components.  */
    -+  /* Determine Strongy Connected Components.  This will also strip any
    ++  /* Determine Strongly Connected Components.  This will also strip any
     +     unnecessary dependencies on imported or TU-local entities.  */
        vec<depset *> sccs = table.connect ();
      
        vec_alloc (ool, modules->length ());
     
    - ## gcc/cp/parser.cc ##
    -@@ gcc/cp/parser.cc: cp_debug_parser (FILE *file, cp_parser *parser)
    - 			      parser->in_unbraced_export_declaration_p);
    -   cp_debug_print_flag (file, "Parsing a declarator",
    - 			      parser->in_declarator_p);
    -+  cp_debug_print_flag (file, "Parsing an initializer",
    -+			      parser->in_initializer_p);
    -   cp_debug_print_flag (file, "In template argument list",
    - 			      parser->in_template_argument_list_p);
    -   cp_debug_print_flag (file, "Parsing an iteration statement",
    -@@ gcc/cp/parser.cc: cp_parser_new (cp_lexer *lexer)
    -   /* We are not processing a declarator.  */
    -   parser->in_declarator_p = false;
    - 
    -+  /* We are not processing an initializer.  */
    -+  parser->in_initializer_p = false;
    -+
    -   /* We are not processing a template-argument-list.  */
    -   parser->in_template_argument_list_p = false;
    - 
    -@@ gcc/cp/parser.cc: cp_parser_lambda_expression (cp_parser* parser)
    - 
    -   record_lambda_scope (lambda_expr);
    -   record_lambda_scope_discriminator (lambda_expr);
    -+  TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
    - 
    -   /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set.  */
    -   determine_visibility (TYPE_NAME (type));
    -@@ gcc/cp/parser.cc: cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/,
    -   if (non_constant_p)
    -     *non_constant_p = false;
    - 
    -+  bool saved_in_initializer_p = parser->in_initializer_p;
    -+  parser->in_initializer_p = true;
    -+
    -   if (token->type == CPP_EQ)
    -     {
    -       /* Consume the `='.  */
    -@@ gcc/cp/parser.cc: cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/,
    -   if (!subexpression_p && check_for_bare_parameter_packs (init))
    -     init = error_mark_node;
    - 
    -+  parser->in_initializer_p = saved_in_initializer_p;
    -+
    -   return init;
    - }
    - 
    -@@ gcc/cp/parser.cc: cp_parser_class_head (cp_parser* parser,
    -     }
    -   else if (type == error_mark_node)
    -     type = NULL_TREE;
    -+  else
    -+    TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
    - 
    -   if (type)
    -     {
    -@@ gcc/cp/parser.cc: cp_parser_concept_definition (cp_parser *parser)
    -     }
    - 
    -   processing_constraint_expression_sentinel parsing_constraint;
    -+  parser->in_initializer_p = true;
    -+
    -   tree init = cp_parser_constraint_expression (parser);
    -   if (init == error_mark_node)
    -     cp_parser_skip_to_end_of_statement (parser);
    -@@ gcc/cp/parser.cc: cp_parser_concept_definition (cp_parser *parser)
    -      but continue as if it were.  */
    -   cp_parser_consume_semicolon_at_end_of_statement (parser);
    - 
    -+  parser->in_initializer_p = false;
    -   return finish_concept_definition (id, init, attrs);
    - }
    - 
    -
    - ## gcc/cp/parser.h ##
    -@@ gcc/cp/parser.h: struct GTY(()) cp_parser {
    -      direct-declarator.  */
    -   bool in_declarator_p;
    - 
    -+  /* TRUE if we are parsing an initializer.  */
    -+  bool in_initializer_p;
    -+
    -   /* TRUE if we are presently parsing a template-argument-list.  */
    -   bool in_template_argument_list_p;
    - 
    -
      ## gcc/testsuite/g++.dg/modules/block-decl-2.C ##
     @@ gcc/testsuite/g++.dg/modules/block-decl-2.C: export extern "C++" auto foo() {
        struct X {
    @@ gcc/testsuite/g++.dg/modules/internal-4_b.C (new)
     +auto in_initializer = []{};  // OK
     +
     +#if __cplusplus >= 202002L
    -+decltype([]{}) d_lambda;  // { dg-error "exposes TU-local entity" "" { target c++20 } }
    ++decltype([]{}) d_lambda;  // { dg-error "exposes TU-local entity" "" { xfail *-*-* } }
     +
     +template <typename T>
     +concept in_constraint_expression = requires {
     +  // Strictly by the standard this is currently ill-formed
     +  // (this is a constraint-expression not an initializer)
     +  // but I don't think that is intended.
    -+  []{};  // OK?
    ++  []{};  // { dg-bogus "exposes TU-local entity" }
     +};
     +#endif
     +
2:  597f0448531 ! 2:  b4e1e0e150f c++/modules: Implement ignored TU-local exposures
    @@ Metadata
     Author: Nathaniel Shead <nathanieloshead@gmail.com>
     
      ## Commit message ##
    -    c++/modules: Implement ignored TU-local exposures
    +    c++/modules: Ignore TU-local entities where necessary
     
         [basic.link] p14 lists a number of circumstances where a declaration
         naming a TU-local entity is not an exposure, notably the bodies of
    @@ Commit message
         prevent complicating the logic; I imagine this shouldn't ever come up
         though.
     
    -    We also add a new warning, '-Wignored-exposures', to handle the case
    -    where someone accidentally refers to a TU-local value from within a
    +    We also add a new warning, '-Wtemplate-names-tu-local', to handle the
    +    case where someone accidentally refers to a TU-local value from within a
         non-inline function template.  This will compile without errors as-is,
         but any attempt to instantiate the decl will fail; this warning can be
         used to ensure that this doesn't happen.  Unfortunately the warning has
    @@ Commit message
         instead just keeps the current (wrong) behaviour of non-exported
         entities not being visible to ADL at all.
     
    +    Additionally, this patch doesn't attempt to ignore non-ODR uses of
    +    constants in constexpr functions or templates.  The obvious approach of
    +    folding them early in 'mark_use' doesn't seem to work (for a variety of
    +    reasons), so this leaves this to a later patch to implement, as it's at
    +    least no worse than the current behaviour and easy enough to workaround.
    +
    +    For completeness this patch adds a new xtreme-header testcase to ensure
    +    that we have no regressions with regards to exposures of TU-local
    +    declarations in the standard library header files.  A more restrictive
    +    test would be to do 'export extern "C++"' here, but unfortunately the
    +    system headers on some targets declare TU-local entities, so we'll make
    +    do with checking that at least the C++ standard library headers don't
    +    refer to such entities.
    +
         gcc/c-family/ChangeLog:
     
    -            * c.opt: New warning '-Wignored-exposures'.
    +            * c.opt: New warning '-Wtemplate-names-tu-local'.
     
         gcc/cp/ChangeLog:
     
    @@ Commit message
                 (TU_LOCAL_ENTITY_LOCATION): New accessor.
                 (enum cp_tree_node_structure_enum): Add TS_CP_TU_LOCAL_ENTITY.
                 (union GTY): Add tu_local_entity field.
    -            * module.cc (enum tree_tag): New flag DB_IGNORED_EXPOSURE_BIT.
    -            (depset::is_ignored_exposure): New accessor.
    +            * module.cc (enum tree_tag): New flag DB_REFS_TU_LOCAL_BIT.
                 (depset::has_defn): Override for TU-local entities.
    -            (depset::hash::ignore_exposure): New field.
    +            (depset::refs_tu_local): New accessor.
    +            (depset::hash::ignore_tu_local): New field.
                 (depset::hash::hash): Initialize it.
                 (trees_out::tree_tag::tt_tu_local): New flag.
                 (trees_out::writing_local_entities): New field.
    @@ Commit message
                 (trees_out::find_tu_local_decl): New function.
                 (trees_out::tree_node): Intercept TU-local entities and write
                 placeholder values for them instead of normal streaming.
    -            (trees_in::tree_node): Read TU-local entities; handle TU-local
    +            (trees_in::tree_node): Handle TU-local entities and TU-local
                 template results.
                 (trees_out::write_function_def): Ignore exposures in non-inline
                 function bodies.
    @@ Commit message
                 (trees_in::read_class_def): Skip TU-local friends.
                 (trees_out::write_definition): Record whether we're writing a
                 decl which refers to TU-local entities.
    -            (depset::hash::add_dependency): Handle ignored exposures.
    +            (depset::hash::add_dependency): Only mark as exposure if we're not
    +            ignoring TU-local entities.
                 (depset::hash::find_dependencies): Use depset's own is_key_order
                 function rather than delegating via walker.  Pass whether the
                 decl has ignored TU-local entities in its definition.
                 (depset::hash::finalize_dependencies): Implement new warning
    -            Wignored-exposures.
    +            Wtemplate-names-tu-local.
                 (module_state::intercluster_seed): Don't seed TU-local deps.
                 (module_state::write_cluster): Pass whether the decl has ignored
                 TU-local entities in its definition.
    @@ Commit message
     
         gcc/ChangeLog:
     
    -            * doc/invoke.texi: Document -Wignored-exposures.
    +            * doc/invoke.texi: Document -Wtemplate-names-tu-local.
     
         gcc/testsuite/ChangeLog:
     
    @@ Commit message
                 * g++.dg/modules/internal-6.C: New test.
                 * g++.dg/modules/internal-7_a.C: New test.
                 * g++.dg/modules/internal-7_b.C: New test.
    +            * g++.dg/modules/internal-8_a.C: New test.
    +            * g++.dg/modules/xtreme-header-8.C: New test.
     
         Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
    +    Reviewed-by: Jason Merrill <jason@redhat.com>
     
      ## gcc/c-family/c.opt ##
    -@@ gcc/c-family/c.opt: Wif-not-aligned
    - C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning
    - Warn when the field in a struct is not aligned.
    +@@ gcc/c-family/c.opt: Wtemplate-id-cdtor
    + C++ ObjC++ Var(warn_template_id_cdtor) Warning
    + Warn about simple-template-id in a constructor or destructor.
      
    -+Wignored-exposures
    -+C++ ObjC++ Var(warn_ignored_exposures) Warning EnabledBy(Wextra)
    -+Warn about ignored exposures of TU-local entities in a module.
    ++Wtemplate-names-tu-local
    ++C++ ObjC++ Var(warn_template_names_tu_local) Warning EnabledBy(Wextra)
    ++Warn about templates naming TU-local entities in a module.
     +
    - Wignored-qualifiers
    - C C++ Var(warn_ignored_qualifiers) Warning EnabledBy(Wextra)
    - Warn whenever type qualifiers are ignored.
    + Wterminate
    + C++ ObjC++ Warning Var(warn_terminate) Init(1)
    + Warn if a throw expression will always result in a call to terminate().
     
      ## gcc/cp/cp-objcp-common.cc ##
     @@ gcc/cp/cp-objcp-common.cc: cp_tree_size (enum tree_code code)
    @@ gcc/cp/cp-tree.h: check_constraint_info (tree t)
         it for unscoped enums.  */
      #define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE)
      
    -+/* Represents a streamed-in translation-unit-local entity. Any use of this
    -+   should emit an error.  */
    ++/* Represents a streamed-in translation-unit-local entity.  Any use of
    ++   this node when instantiating a template should emit an error.  */
     +struct GTY(()) tree_tu_local_entity {
     +  struct tree_base base;
     +  tree name;
     +  location_t loc;
     +};
     +
    -+/* The name of a translation-unit local-entity.  */
    ++/* The name of a translation-unit-local entity.  */
     +#define TU_LOCAL_ENTITY_NAME(NODE) \
     +  (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name)
     +
    @@ gcc/cp/cp-tree.h: union GTY((desc ("cp_tree_node_structure (&%h)"),
     
      ## gcc/cp/module.cc ##
     @@ gcc/cp/module.cc: private:
    +     DB_KIND_BITS = EK_BITS,
    +     DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
          DB_IS_MEMBER_BIT,		/* Is an out-of-class member.  */
    -     DB_TU_LOCAL_BIT,		/* It is a TU-local entity.  */
    +-    DB_TU_LOCAL_BIT,		/* It is a TU-local entity.  */
    ++    DB_TU_LOCAL_BIT,		/* Is a TU-local entity.  */
    ++    DB_REFS_TU_LOCAL_BIT,	/* Refers to a TU-local entity (but is not
    ++				   necessarily an exposure.)  */
          DB_EXPOSURE_BIT,		/* Exposes a TU-local entity.  */
    -+    DB_IGNORED_EXPOSURE_BIT,	/* Will stream a TU-local entity.  */
          DB_IMPORTED_BIT,		/* An imported entity.  */
          DB_UNREACHED_BIT,		/* A yet-to-be reached entity.  */
    -     DB_HIDDEN_BIT,		/* A hidden binding.  */
     @@ gcc/cp/module.cc: public:
      public:
        bool has_defn () const
    @@ gcc/cp/module.cc: public:
      public:
     @@ gcc/cp/module.cc: public:
        {
    -     return get_flag_bit<DB_EXPOSURE_BIT> ();
    +     return get_flag_bit<DB_TU_LOCAL_BIT> ();
        }
    -+  bool is_ignored_exposure () const
    ++  bool refs_tu_local () const
     +  {
    -+    return get_flag_bit<DB_IGNORED_EXPOSURE_BIT> ();
    ++    return get_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
     +  }
    -   bool is_import () const
    +   bool is_exposure () const
        {
    -     return get_flag_bit<DB_IMPORTED_BIT> ();
    +     return get_flag_bit<DB_EXPOSURE_BIT> ();
     @@ gcc/cp/module.cc: public:
          depset *current;         /* Current depset being depended.  */
          unsigned section;	     /* When writing out, the section.  */
          bool reached_unreached;  /* We reached an unreached entity.  */
    -+    bool ignore_exposure;    /* In a context where exposures are ignored.  */
    ++    bool ignore_tu_local;    /* In a context where referencing a TU-local
    ++				entity is not an exposure.  */
      
        public:
          hash (size_t size, hash *c = NULL)
            : parent (size), chain (c), current (NULL), section (0),
     -	reached_unreached (false)
    -+	reached_unreached (false), ignore_exposure (false)
    ++	reached_unreached (false), ignore_tu_local (false)
          {
            worklist.create (size);
          }
    @@ gcc/cp/module.cc: public:
      public:
        /* Serialize various definitions. */
     -  void write_definition (tree decl);
    -+  void write_definition (tree decl, bool has_tu_local = false);
    ++  void write_definition (tree decl, bool refs_tu_local = false);
        void mark_declaration (tree decl, bool do_defn);
      
      private:
    @@ gcc/cp/module.cc: trees_out::tree_node (tree t)
          goto done;
      
     +  /* Find TU-local entities and intercept streaming to instead write a
    -+     placeholder value; this way we don't need to expose such decls.
    ++     placeholder value; this way we don't need to emit such decls.
     +     We only need to do this when writing a definition of an entity
    -+     that we know is an ignored exposure.  */
    ++     that we know names a TU-local entity.  */
     +  if (!is_initial_scan () && writing_local_entities)
     +    {
     +      tree local_decl = NULL_TREE;
    @@ gcc/cp/module.cc: trees_in::tree_node (bool is_use)
      
     +    case tt_tu_local:
     +      {
    -+	/* A translation-unit local entity.  */
    ++	/* A translation-unit-local entity.  */
     +	res = make_node (TU_LOCAL_ENTITY);
     +	int tag = insert (res);
     +
    @@ gcc/cp/module.cc: void
     +       is ignored for determining exposures.  This should only matter
     +       for templates (we don't emit the bodies of non-inline functions
     +       to begin with).  */
    -+    auto ovr = make_temp_override (dep_hash->ignore_exposure,
    ++    auto ovr = make_temp_override (dep_hash->ignore_tu_local,
     +				   !DECL_DECLARED_INLINE_P (decl));
     +    tree_node (DECL_INITIAL (decl));
     +    tree_node (DECL_SAVED_TREE (decl));
    @@ gcc/cp/module.cc: trees_in::read_function_def (tree decl, tree maybe_template)
      {
     +  /* The initializer of a variable or variable template is ignored for
     +     determining exposures.  */
    -+  auto ovr = make_temp_override (dep_hash->ignore_exposure, VAR_P (decl));
    ++  auto ovr = make_temp_override (dep_hash->ignore_tu_local, VAR_P (decl));
     +
        tree init = DECL_INITIAL (decl);
        tree_node (init);
    @@ gcc/cp/module.cc: trees_out::write_class_def (tree defn)
     +      {
     +	/* Friend declarations in class definitions are ignored when
     +	   determining exposures.  */
    -+	auto ovr = make_temp_override (dep_hash->ignore_exposure, true);
    ++	auto ovr = make_temp_override (dep_hash->ignore_tu_local, true);
      
     -      /* Write the friend functions.  */
     -      for (tree friends = DECL_FRIENDLIST (defn);
    @@ gcc/cp/module.cc: trees_in::read_enum_def (tree defn, tree maybe_template)
      
      void
     -trees_out::write_definition (tree decl)
    -+trees_out::write_definition (tree decl, bool has_tu_local)
    ++trees_out::write_definition (tree decl, bool refs_tu_local)
      {
     +  auto ovr = make_temp_override (writing_local_entities,
    -+				 writing_local_entities || has_tu_local);
    ++				 writing_local_entities || refs_tu_local);
     +
        if (streaming_p ())
          {
    @@ gcc/cp/module.cc: depset::hash::add_dependency (depset *dep)
        if (dep->is_tu_local ())
     -    current->set_flag_bit<DB_EXPOSURE_BIT> ();
     +    {
    -+      if (ignore_exposure)
    -+	current->set_flag_bit<DB_IGNORED_EXPOSURE_BIT> ();
    -+      else
    ++      current->set_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
    ++      if (!ignore_tu_local)
     +	current->set_flag_bit<DB_EXPOSURE_BIT> ();
     +    }
      
    @@ gcc/cp/module.cc: depset::hash::find_dependencies (module_state *module)
      		  walker.decl_value (decl, current);
      		  if (current->has_defn ())
     -		    walker.write_definition (decl);
    -+		    walker.write_definition (decl,
    -+					     current->is_ignored_exposure ());
    ++		    walker.write_definition (decl, current->refs_tu_local ());
      		}
      	      walker.end ();
      
    @@ gcc/cp/module.cc: depset::hash::finalize_dependencies ()
      	  /* We should have emitted an error above.  */
      	  gcc_checking_assert (explained);
      	}
    -+      else if (warn_ignored_exposures && dep->is_ignored_exposure ())
    ++      else if (warn_template_names_tu_local
    ++	       && dep->refs_tu_local () && !dep->is_tu_local ())
     +	{
     +	  tree decl = dep->get_entity ();
     +
    @@ gcc/cp/module.cc: depset::hash::finalize_dependencies ()
     +	  for (depset *rdep : dep->deps)
     +	    if (!rdep->is_binding () && rdep->is_tu_local ())
     +	      {
    -+		tree exposed = rdep->get_entity ();
    ++		tree ref = rdep->get_entity ();
     +		auto_diagnostic_group d;
     +		if (warning_at (DECL_SOURCE_LOCATION (decl),
    -+				OPT_Wignored_exposures,
    ++				OPT_Wtemplate_names_tu_local,
     +				"%qD refers to TU-local entity %qD and cannot "
    -+				"be instantiated in other TUs", decl, exposed))
    -+		  is_tu_local_entity (exposed, /*explain=*/true);
    ++				"be instantiated in other TUs", decl, ref))
    ++		  is_tu_local_entity (ref, /*explain=*/true);
     +		break;
     +	      }
     +	}
    @@ gcc/cp/module.cc: module_state::write_cluster (elf_out *to, depset *scc[], unsig
      	      sec.tree_node (decl);
      	      dump () && dump ("Writing definition %N", decl);
     -	      sec.write_definition (decl);
    -+	      sec.write_definition (decl, b->is_ignored_exposure ());
    ++	      sec.write_definition (decl, b->refs_tu_local ());
      
      	      if (!namer->has_defn ())
      		namer = b;
    @@ gcc/cp/pt.cc: tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_d
     
      ## gcc/doc/invoke.texi ##
     @@ gcc/doc/invoke.texi: in the following sections.
    - -Weffc++ -Wno-elaborated-enum-base
    - -Wno-exceptions -Wextra-semi -Wno-global-module -Wno-inaccessible-base
    - -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime
    ---Winvalid-constexpr -Winvalid-imported-macros
    -+-Wignored-exposures -Winvalid-constexpr -Winvalid-imported-macros
    - -Wno-invalid-offsetof  -Wno-literal-suffix
    - -Wmismatched-new-delete -Wmismatched-tags
    - -Wmultiple-inheritance  -Wnamespaces  -Wnarrowing
    -@@ gcc/doc/invoke.texi: the variable declaration statement.
    + -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
    + -Wsized-deallocation  -Wsuggest-final-methods
    + -Wsuggest-final-types  -Wsuggest-override  -Wno-template-body
    +--Wno-template-id-cdtor
    ++-Wno-template-id-cdtor  -Wtemplate-names-tu-local
    + -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
    + -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
      
    - @end itemize
    +@@ gcc/doc/invoke.texi: template<typename T> struct S @{
    + @option{-Wtemplate-id-cdtor} is enabled by default with
    + @option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}.
      
    -+@opindex Wignored-exposures
    -+@opindex Wno-ignored-exposures
    -+@item -Wignored-exposures
    ++@opindex Wtemplate-names-tu-local
    ++@opindex Wno-template-names-tu-local
    ++@item -Wtemplate-names-tu-local
     +Warn when a template body hides an exposure of a translation-unit-local
     +entity.  In most cases, referring to a translation-unit-local entity
     +(such as an internal linkage declaration) within an entity that is
     +emitted into a module's CMI is an error.  However, within the
     +initializer of a variable, or in the body of a non-inline function,
    -+this error is suppressed.
    ++this is not an exposure and no error is emitted.
     +
     +This can cause variable or function templates to accidentally become
     +unusable if they reference such an entity, because other translation
     +units that import the template will never be able to instantiate it.
     +This warning attempts to detect cases where this might occur.
     +
    -+This warning is enabled by @option{-Wextra}.
    ++This flag is enabled by @option{-Wextra}.
     +
    - @opindex Winvalid-constexpr
    - @opindex Wno-invalid-constexpr
    - @item -Winvalid-constexpr
    + @opindex Wterminate
    + @opindex Wno-terminate
    + @item -Wno-terminate @r{(C++ and Objective-C++ only)}
     
      ## gcc/testsuite/g++.dg/modules/internal-5_a.C (new) ##
     @@
    -+// { dg-additional-options "-fmodules-ts -Wignored-exposures" }
    ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
     +// { dg-module-cmi M }
     +// Ignore exposures in these cases
     +
    @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
     +  internal_tmpl<T>();
     +}
     +template void function_tmpl<ok_inst_tag>();
    ++template <> void function_tmpl<ok_inst_tag*>() {}
     +
     +
     +// The initializer for a variable or variable template
    @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
     +     internal_ovl(internal_x), internal_tmpl<T*>(), 0);
     +
     +template int var_tmpl<ok_inst_tag>;
    -+template int var_tmpl<ok_inst_tag*>;
    ++template <> int var_tmpl<ok_inst_tag*> = 0;
     +
     +export int& constant_ref = internal_x;
     +static_assert (&constant_ref == &internal_x);
    @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
     +// Any reference to a non-volatile const object or reference with internal or
     +// no linkage initialized with a constant expression that is not an ODR-use
     +static const int value = 123;
    -+static const int& ref = value;
    ++static const int& ref = 456;
     +static const internal_t internal {};
    ++void f(int) {}
     +export inline void no_odr_use() {
     +  int x = value;
     +  int y = ref;
     +  int z = (internal, 0);
    ++
    ++  value;
    ++  bool b = value < value;
    ++  f(value);
     +}
     
      ## gcc/testsuite/g++.dg/modules/internal-5_b.C (new) ##
    @@ gcc/testsuite/g++.dg/modules/internal-5_b.C (new)
     +  no_odr_use();
     +
     +  function_tmpl<ok_inst_tag>();
    ++  function_tmpl<ok_inst_tag*>();
     +  int b = var_tmpl<ok_inst_tag>;
     +  int c = var_tmpl<ok_inst_tag*>;
     +
    @@ gcc/testsuite/g++.dg/modules/internal-6.C (new)
     
      ## gcc/testsuite/g++.dg/modules/internal-7_a.C (new) ##
     @@
    -+// { dg-additional-options "-fmodules-ts -Wignored-exposures" }
    ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
     +// Test streaming and instantiations of various kinds of exposures
     +
     +export module M;
    @@ gcc/testsuite/g++.dg/modules/internal-7_b.C (new)
     +}
     +
     +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } 0 }
    +
    + ## gcc/testsuite/g++.dg/modules/internal-8_a.C (new) ##
    +@@
    ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
    ++// Non-ODR usages of const variables currently are erroneously
    ++// reported in templates and constexpr functions; this test
    ++// XFAILS them until we can implement a fix.
    ++
    ++export module M;
    ++
    ++namespace { struct internal_t {}; };
    ++static const int value = 123;
    ++static const int& ref = 456;
    ++static const internal_t internal {};
    ++
    ++constexpr void f(int) {}
    ++
    ++export constexpr
    ++void no_odr_use_cexpr() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
    ++  int x = value;
    ++  int y = ref;
    ++  int z = (internal, 0);
    ++
    ++  value;
    ++  bool b = value < value;
    ++  f(value);
    ++}
    ++
    ++export template <typename T>
    ++void no_odr_use_templ() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
    ++  int x = value;
    ++  int y = ref;
    ++  int z = (internal, 0);
    ++
    ++  value;
    ++  bool b = value < value;
    ++  f(value);
    ++}
    +
    + ## gcc/testsuite/g++.dg/modules/xtreme-header-8.C (new) ##
    +@@
    ++// PR c++/115126
    ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
    ++// { dg-module-cmi xstd }
    ++
    ++export module xstd;
    ++extern "C++" {
    ++  #include "xtreme-header.h"
    ++}
3:  49e06cc4065 ! 3:  918c037a0a9 c++/modules: Support anonymous namespaces in header units
    @@ Metadata
     Author: Nathaniel Shead <nathanieloshead@gmail.com>
     
      ## Commit message ##
    -    c++/modules: Support anonymous namespaces in header units
    +    c++/modules: Support unnamed namespaces in header units
     
    -    A header unit may contain anonymous namespaces, and those declarations
    +    A header unit may contain unnamed namespaces, and those declarations
         are exported (as with any declaration in a header unit).  This patch
         ensures that such declarations are correctly handled.
     
    -    The change to 'make_namespace_finish' is required so that if an
    -    anonymous namespace is first seen by an import it is correctly handled
    -    within 'add_imported_namespace'.  I don't see any particular reason why
    -    handling of anonymous namespaces here had to be handled separately
    -    outside that function since these are the only two callers.
    +    The change to 'make_namespace_finish' is required so that if an unnamed
    +    namespace is first seen by an import it is correctly handled within
    +    'add_imported_namespace'.  I don't see any particular reason why
    +    handling of unnamed namespaces here had to be handled separately outside
    +    that function since these are the only two callers.
     
         gcc/cp/ChangeLog:
     
                 * module.cc (depset::hash::add_binding_entity): Also walk
    -            anonymous namespaces.
    +            unnamed namespaces.
                 (module_state::write_namespaces): Adjust assertion.
                 * name-lookup.cc (push_namespace): Move anon using-directive
                 handling to...
    @@ Commit message
     
         gcc/testsuite/ChangeLog:
     
    -            * g++.dg/modules/internal-8_a.H: New test.
    -            * g++.dg/modules/internal-8_b.C: New test.
    +            * g++.dg/modules/internal-9_a.H: New test.
    +            * g++.dg/modules/internal-9_b.C: New test.
     
         Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
    +    Reviewed-by: Jason Merrill <jason@redhat.com>
     
      ## gcc/cp/module.cc ##
     @@ gcc/cp/module.cc: depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
    @@ gcc/cp/name-lookup.cc: make_namespace_finish (tree ns, tree *slot, bool from_imp
        if (DECL_NAMESPACE_INLINE_P (ns) || !DECL_NAME (ns))
          emit_debug_info_using_namespace (ctx, ns, true);
     +
    ++  /* An unnamed namespace implicitly has a using-directive inserted so
    ++     that its contents are usable in the surrounding context.  */
     +  if (!DECL_NAMESPACE_INLINE_P (ns) && !DECL_NAME (ns))
     +    add_using_namespace (NAMESPACE_LEVEL (ctx)->using_directives, ns);
      }
    @@ gcc/cp/name-lookup.cc: push_namespace (tree name, bool make_inline)
          }
      
     
    - ## gcc/testsuite/g++.dg/modules/internal-8_a.H (new) ##
    + ## gcc/testsuite/g++.dg/modules/internal-9_a.H (new) ##
     @@
     +// { dg-additional-options "-fmodule-header" }
     +// { dg-module-cmi {} }
    @@ gcc/testsuite/g++.dg/modules/internal-8_a.H (new)
     +
     +namespace ns2 = ns;
     
    - ## gcc/testsuite/g++.dg/modules/internal-8_b.C (new) ##
    + ## gcc/testsuite/g++.dg/modules/internal-9_b.C (new) ##
     @@
     +// { dg-additional-options "-fmodules-ts" }
     +
    -+import "internal-8_a.H";
    ++import "internal-9_a.H";
     +
     +int main() {
     +  auto x2 = x;
4:  671a9de084e ! 4:  46d0ecf964c c++/modules: Check linkage for exported declarations
    @@ Commit message
                 * g++.dg/modules/export-6.C: New test.
     
         Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
    +    Reviewed-by: Jason Merrill <jason@redhat.com>
     
      ## gcc/cp/cp-tree.h ##
     @@ gcc/cp/cp-tree.h: extern void set_originating_module (tree, bool friend_p = false);
    @@ gcc/cp/module.cc: set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNU
          }
      
     -  if (!module_exporting_p ())
    -+  /* It is illegal to export a declaration with internal linkage.  However, at
    -+     the point this function is called we don't always know yet whether this
    ++  /* 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 ())
    @@ gcc/cp/name-lookup.cc: push_namespace (tree name, bool make_inline)
     -		      "exporting namespace with internal linkage");
     +	    {
     +	      if (name)
    -+		error_at (input_location,
    -+			  "exporting namespace %qD with internal linkage", ns);
    ++		{
    ++		  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 anonymous namespace");
    ++		error_at (input_location, "exporting unnamed namespace");
     +	    }
      	}
            if (module_purview_p ())
    @@ gcc/testsuite/g++.dg/modules/export-3.C: namespace {
      }
      
     -export namespace {}  // { dg-error "internal linkage" }
    -+export namespace {}  // { dg-error "exporting anonymous namespace" }
    ++export namespace {}  // { dg-error "exporting unnamed namespace" }
     
      ## gcc/testsuite/g++.dg/modules/export-6.C (new) ##
     @@
    @@ gcc/testsuite/g++.dg/modules/export-6.C (new)
     +#endif
     +}
     +
    -+export namespace {}  // { dg-error "exporting anonymous namespace" }
    ++export namespace {}  // { dg-error "exporting unnamed namespace" }
     +export namespace ns2 = ns;  // { dg-error "internal linkage" }
     
      ## libcc1/libcp1plugin.cc ##
5:  0923d54daf1 ! 5:  3470fe5b429 c++/modules: Validate external linkage definitions in header units [PR116401]
    @@ Commit message
                 * g++.dg/modules/hdr-2.H: New test.
     
         Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
    +    Reviewed-by: Jason Merrill <jason@redhat.com>
     
      ## gcc/cp/decl.cc ##
     @@ gcc/cp/decl.cc: grokfndecl (tree ctype,
      	 have one: the restriction that you can't repeat a deduction guide
      	 makes them more like a definition anyway.  */
            DECL_INITIAL (decl) = void_node;
    ++      /* But to ensure that external-linkage deduction guides in header units
    ++	 don't fall afoul of [module.import] p6, mark them as inline to give
    ++	 the same effect as them not having a definition.  */
     +      DECL_DECLARED_INLINE_P (decl) = true;
            break;
          default:
6:  0f78398918f < -:  ----------- c++/modules: Add testcase for standard-library exposures [PR115126]

Comments

Nathaniel Shead Oct. 31, 2024, 10:12 a.m. UTC | #1
Gentle ping for the first two patches in this series:
https://gcc.gnu.org/pipermail/gcc-patches/2024-October/665108.html

On Sat, Oct 12, 2024 at 12:29:09AM +1100, Nathaniel Shead wrote:
> This patch series implements most of the changes made by P1815.  It also
> cleans up a few bugs found along the way that impacted tests I wrote.
> 
> The whole patch series was bootstrapped on x86_64-pc-linux-gnu and
> aarch64-unknown-linux-gnu with no regressions.
> 
> A range-diff against v2 is attached; the main changes are responses to
> comments left on previous versions of this patch series.
> 
> Nathaniel Shead (5):
>   c++/modules: Detect exposures of TU-local entities
>   c++/modules: Ignore TU-local entities where necessary
>   c++/modules: Support unnamed namespaces in header units
>   c++/modules: Check linkage for exported declarations
>   c++/modules: Validate external linkage definitions in header units
>     [PR116401]
> 
>  gcc/c-family/c.opt                            |   4 +
>  gcc/cp/cp-objcp-common.cc                     |   1 +
>  gcc/cp/cp-tree.def                            |   6 +
>  gcc/cp/cp-tree.h                              |  22 +-
>  gcc/cp/decl.cc                                |   4 +
>  gcc/cp/decl2.cc                               |   1 +
>  gcc/cp/module.cc                              | 749 +++++++++++++++---
>  gcc/cp/name-lookup.cc                         |  30 +-
>  gcc/cp/name-lookup.h                          |   2 +-
>  gcc/cp/parser.cc                              |   9 +-
>  gcc/cp/pt.cc                                  | 100 ++-
>  gcc/doc/invoke.texi                           |  19 +-
>  gcc/testsuite/g++.dg/modules/block-decl-2.C   |   2 +-
>  gcc/testsuite/g++.dg/modules/export-3.C       |   2 +-
>  gcc/testsuite/g++.dg/modules/export-6.C       |  35 +
>  gcc/testsuite/g++.dg/modules/hdr-2.H          | 172 ++++
>  gcc/testsuite/g++.dg/modules/internal-1.C     |  15 +-
>  gcc/testsuite/g++.dg/modules/internal-3.C     |  18 +
>  gcc/testsuite/g++.dg/modules/internal-4_a.H   |   4 +
>  gcc/testsuite/g++.dg/modules/internal-4_b.C   | 124 +++
>  gcc/testsuite/g++.dg/modules/internal-5_a.C   | 110 +++
>  gcc/testsuite/g++.dg/modules/internal-5_b.C   |  30 +
>  gcc/testsuite/g++.dg/modules/internal-6.C     |  24 +
>  gcc/testsuite/g++.dg/modules/internal-7_a.C   |  75 ++
>  gcc/testsuite/g++.dg/modules/internal-7_b.C   |  21 +
>  gcc/testsuite/g++.dg/modules/internal-8_a.C   |  35 +
>  gcc/testsuite/g++.dg/modules/internal-9_a.H   |  28 +
>  gcc/testsuite/g++.dg/modules/internal-9_b.C   |  29 +
>  gcc/testsuite/g++.dg/modules/linkage-2.C      |   5 +-
>  gcc/testsuite/g++.dg/modules/macro-4_c.H      |   2 +-
>  gcc/testsuite/g++.dg/modules/pr106761.h       |   2 +-
>  gcc/testsuite/g++.dg/modules/pr98843_b.H      |   2 +-
>  gcc/testsuite/g++.dg/modules/pr99468.H        |   2 +-
>  gcc/testsuite/g++.dg/modules/pragma-1_a.H     |   2 +-
>  gcc/testsuite/g++.dg/modules/tpl-ary-1.h      |   2 +-
>  .../g++.dg/modules/xtreme-header-8.C          |   8 +
>  libcc1/libcp1plugin.cc                        |   2 +-
>  37 files changed, 1555 insertions(+), 143 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/modules/export-6.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/hdr-2.H
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-3.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_a.H
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-4_b.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_a.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-5_b.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-6.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_a.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-7_b.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-8_a.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_a.H
>  create mode 100644 gcc/testsuite/g++.dg/modules/internal-9_b.C
>  create mode 100644 gcc/testsuite/g++.dg/modules/xtreme-header-8.C
> 
> Range-diff against v2:
> 1:  4a032de3f30 ! 1:  36b944bfe8c c++/modules: Detect exposures of TU-local entities
>     @@ Commit message
>          follow-up patch for easier review, as it has broader implications for
>          streaming.
>      
>     +    TU-local lambdas are also not yet properly implemented, due to other
>     +    bugs with regards to LAMBDA_TYPE_EXTRA_SCOPE not being set in all cases
>     +    that it probably should be (see also PR c++/116568).  We can revisit
>     +    this once that issue has been fixed.
>     +
>          Finally, this patch makes a couple of small adjustments to the modules
>          streaming logic to prune any leftover TU-local deps (that aren't
>          erroneous exposures).  This is required for this patch to ensure that
>     @@ Commit message
>      
>          gcc/cp/ChangeLog:
>      
>     -            * cp-tree.h (TYPE_DEPENDENT_P_VALID): Fix whitespace.
>     -            (TYPE_DEFINED_IN_INITIALIZER_P): New accessor.
>                  * module.cc (DB_IS_INTERNAL_BIT): Rename to...
>                  (DB_TU_LOCAL_BIT): ...this.
>                  (DB_REFS_INTERNAL_BIT): Rename to...
>     @@ Commit message
>                  diagnostic messages to report exposures of TU-local entities.
>                  (depset::tarjan::connect): Don't include any TU-local depsets.
>                  (depset::hash::connect): Likewise.
>     -            (trees_out::core_bools): Stream TYPE_LANG_FLAG_7.
>     -            (trees_in::core_bools): Read it.
>     -            * parser.h (struct cp_parser::in_initializer_p): New flag.
>     -            * parser.cc (cp_debug_parser): Print the new flag.
>     -            (cp_parser_new): Set the new flag to false.
>     -            (cp_parser_lambda_expression): Mark whether the lambda was
>     -            defined in an initializer.
>     -            (cp_parser_initializer): Set the new flag to true while parsing.
>     -            (cp_parser_class_head): Mark whether the class was defined in an
>     -            initializer.
>     -            (cp_parser_concept_definition): Set the new flag to true while
>     -            parsing.
>      
>          gcc/testsuite/ChangeLog:
>      
>     @@ Commit message
>                  * g++.dg/modules/linkage-2.C: Adjust messages, remove XFAILS.
>                  * g++.dg/modules/internal-3.C: New test.
>                  * g++.dg/modules/internal-4_a.H: New test.
>     -            * g++.dg/modules/internal-4_a.C: New test.
>     +            * g++.dg/modules/internal-4_b.C: New test.
>      
>          Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
>     -
>     - ## gcc/cp/cp-tree.h ##
>     -@@ gcc/cp/cp-tree.h: extern GTY(()) tree cp_global_trees[CPTI_MAX];
>     -       AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM)
>     -       TEMPLATE_TEMPLATE_PARM_SIMPLE_P (in TEMPLATE_TEMPLATE_PARM)
>     -    6: TYPE_DEPENDENT_P_VALID
>     -+   7: TYPE_DEFINED_IN_INITIALIZER_P
>     - 
>     -    Usage of DECL_LANG_FLAG_?:
>     -    0: DECL_TEMPLATE_PARM_P (in PARM_DECL, CONST_DECL, TYPE_DECL, or TEMPLATE_DECL)
>     -@@ gcc/cp/cp-tree.h: enum languages { lang_c, lang_cplusplus };
>     - 
>     - /* True if dependent_type_p has been called for this type, with the
>     -    result that TYPE_DEPENDENT_P is valid.  */
>     --#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6(NODE)
>     -+#define TYPE_DEPENDENT_P_VALID(NODE) TYPE_LANG_FLAG_6 (NODE)
>     -+
>     -+/* True if this type was defined in an initializer.  Used for determining
>     -+   whether an entity is TU-local.  */
>     -+#define TYPE_DEFINED_IN_INITIALIZER_P(NODE) TYPE_LANG_FLAG_7 (NODE)
>     - 
>     - /* Nonzero if this type is const-qualified.  */
>     - #define CP_TYPE_CONST_P(NODE)				\
>     +    Reviewed-by: Jason Merrill <jason@redhat.com>
>      
>       ## gcc/cp/module.cc ##
>      @@ gcc/cp/module.cc: private:
>     @@ gcc/cp/module.cc: public:
>         private:
>           static bool add_binding_entity (tree, WMB_Flags, void *);
>       
>     -@@ gcc/cp/module.cc: trees_out::core_bools (tree t, bits_out& bits)
>     -       WB (t->type_common.lang_flag_4);
>     -       WB (t->type_common.lang_flag_5);
>     -       WB (t->type_common.lang_flag_6);
>     -+      WB (t->type_common.lang_flag_7);
>     -       WB (t->type_common.typeless_storage);
>     -     }
>     - 
>     -@@ gcc/cp/module.cc: trees_in::core_bools (tree t, bits_in& bits)
>     -       RB (t->type_common.lang_flag_4);
>     -       RB (t->type_common.lang_flag_5);
>     -       RB (t->type_common.lang_flag_6);
>     -+      RB (t->type_common.lang_flag_7);
>     -       RB (t->type_common.typeless_storage);
>     -     }
>     - 
>      @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
>         return slot ? *slot : NULL;
>       }
>     @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
>      +     is used to declare only TU-local entities.
>      +
>      +     We consider types with names for linkage purposes as having names, since
>     -+     these aren't really TU-local, and also consider constraint-expressions
>     -+     as initializers.  */
>     ++     these aren't really TU-local.  */
>      +  if (TREE_CODE (decl) == TYPE_DECL
>      +      && TYPE_ANON_P (type)
>      +      && !DECL_SELF_REFERENCE_P (decl)
>     @@ gcc/cp/module.cc: depset::hash::find_binding (tree ctx, tree name)
>      +      && !(UNSCOPED_ENUM_P (type) && TYPE_VALUES (type)))
>      +    {
>      +      tree main_decl = TYPE_MAIN_DECL (type);
>     -+      if (!TYPE_DEFINED_IN_INITIALIZER_P (type)
>     -+	  && !DECL_CLASS_SCOPE_P (main_decl)
>     -+	  && !decl_function_context (main_decl))
>     ++      if (!DECL_CLASS_SCOPE_P (main_decl)
>     ++	  && !decl_function_context (main_decl)
>     ++	  /* FIXME: Lambdas defined outside initializers.  We'll need to more
>     ++	     thoroughly set LAMBDA_TYPE_EXTRA_SCOPE to check this.  */
>     ++	  && !LAMBDA_TYPE_P (type))
>      +	{
>      +	  if (explain)
>      +	    inform (loc, "%qT has no name and is not defined within a class, "
>     @@ gcc/cp/module.cc: module_state::write_begin (elf_out *to, cpp_reader *reader,
>       #endif
>       
>      -  /* Determine Strongy Connected Components.  */
>     -+  /* Determine Strongy Connected Components.  This will also strip any
>     ++  /* Determine Strongly Connected Components.  This will also strip any
>      +     unnecessary dependencies on imported or TU-local entities.  */
>         vec<depset *> sccs = table.connect ();
>       
>         vec_alloc (ool, modules->length ());
>      
>     - ## gcc/cp/parser.cc ##
>     -@@ gcc/cp/parser.cc: cp_debug_parser (FILE *file, cp_parser *parser)
>     - 			      parser->in_unbraced_export_declaration_p);
>     -   cp_debug_print_flag (file, "Parsing a declarator",
>     - 			      parser->in_declarator_p);
>     -+  cp_debug_print_flag (file, "Parsing an initializer",
>     -+			      parser->in_initializer_p);
>     -   cp_debug_print_flag (file, "In template argument list",
>     - 			      parser->in_template_argument_list_p);
>     -   cp_debug_print_flag (file, "Parsing an iteration statement",
>     -@@ gcc/cp/parser.cc: cp_parser_new (cp_lexer *lexer)
>     -   /* We are not processing a declarator.  */
>     -   parser->in_declarator_p = false;
>     - 
>     -+  /* We are not processing an initializer.  */
>     -+  parser->in_initializer_p = false;
>     -+
>     -   /* We are not processing a template-argument-list.  */
>     -   parser->in_template_argument_list_p = false;
>     - 
>     -@@ gcc/cp/parser.cc: cp_parser_lambda_expression (cp_parser* parser)
>     - 
>     -   record_lambda_scope (lambda_expr);
>     -   record_lambda_scope_discriminator (lambda_expr);
>     -+  TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
>     - 
>     -   /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set.  */
>     -   determine_visibility (TYPE_NAME (type));
>     -@@ gcc/cp/parser.cc: cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/,
>     -   if (non_constant_p)
>     -     *non_constant_p = false;
>     - 
>     -+  bool saved_in_initializer_p = parser->in_initializer_p;
>     -+  parser->in_initializer_p = true;
>     -+
>     -   if (token->type == CPP_EQ)
>     -     {
>     -       /* Consume the `='.  */
>     -@@ gcc/cp/parser.cc: cp_parser_initializer (cp_parser *parser, bool *is_direct_init /*=nullptr*/,
>     -   if (!subexpression_p && check_for_bare_parameter_packs (init))
>     -     init = error_mark_node;
>     - 
>     -+  parser->in_initializer_p = saved_in_initializer_p;
>     -+
>     -   return init;
>     - }
>     - 
>     -@@ gcc/cp/parser.cc: cp_parser_class_head (cp_parser* parser,
>     -     }
>     -   else if (type == error_mark_node)
>     -     type = NULL_TREE;
>     -+  else
>     -+    TYPE_DEFINED_IN_INITIALIZER_P (type) = parser->in_initializer_p;
>     - 
>     -   if (type)
>     -     {
>     -@@ gcc/cp/parser.cc: cp_parser_concept_definition (cp_parser *parser)
>     -     }
>     - 
>     -   processing_constraint_expression_sentinel parsing_constraint;
>     -+  parser->in_initializer_p = true;
>     -+
>     -   tree init = cp_parser_constraint_expression (parser);
>     -   if (init == error_mark_node)
>     -     cp_parser_skip_to_end_of_statement (parser);
>     -@@ gcc/cp/parser.cc: cp_parser_concept_definition (cp_parser *parser)
>     -      but continue as if it were.  */
>     -   cp_parser_consume_semicolon_at_end_of_statement (parser);
>     - 
>     -+  parser->in_initializer_p = false;
>     -   return finish_concept_definition (id, init, attrs);
>     - }
>     - 
>     -
>     - ## gcc/cp/parser.h ##
>     -@@ gcc/cp/parser.h: struct GTY(()) cp_parser {
>     -      direct-declarator.  */
>     -   bool in_declarator_p;
>     - 
>     -+  /* TRUE if we are parsing an initializer.  */
>     -+  bool in_initializer_p;
>     -+
>     -   /* TRUE if we are presently parsing a template-argument-list.  */
>     -   bool in_template_argument_list_p;
>     - 
>     -
>       ## gcc/testsuite/g++.dg/modules/block-decl-2.C ##
>      @@ gcc/testsuite/g++.dg/modules/block-decl-2.C: export extern "C++" auto foo() {
>         struct X {
>     @@ gcc/testsuite/g++.dg/modules/internal-4_b.C (new)
>      +auto in_initializer = []{};  // OK
>      +
>      +#if __cplusplus >= 202002L
>     -+decltype([]{}) d_lambda;  // { dg-error "exposes TU-local entity" "" { target c++20 } }
>     ++decltype([]{}) d_lambda;  // { dg-error "exposes TU-local entity" "" { xfail *-*-* } }
>      +
>      +template <typename T>
>      +concept in_constraint_expression = requires {
>      +  // Strictly by the standard this is currently ill-formed
>      +  // (this is a constraint-expression not an initializer)
>      +  // but I don't think that is intended.
>     -+  []{};  // OK?
>     ++  []{};  // { dg-bogus "exposes TU-local entity" }
>      +};
>      +#endif
>      +
> 2:  597f0448531 ! 2:  b4e1e0e150f c++/modules: Implement ignored TU-local exposures
>     @@ Metadata
>      Author: Nathaniel Shead <nathanieloshead@gmail.com>
>      
>       ## Commit message ##
>     -    c++/modules: Implement ignored TU-local exposures
>     +    c++/modules: Ignore TU-local entities where necessary
>      
>          [basic.link] p14 lists a number of circumstances where a declaration
>          naming a TU-local entity is not an exposure, notably the bodies of
>     @@ Commit message
>          prevent complicating the logic; I imagine this shouldn't ever come up
>          though.
>      
>     -    We also add a new warning, '-Wignored-exposures', to handle the case
>     -    where someone accidentally refers to a TU-local value from within a
>     +    We also add a new warning, '-Wtemplate-names-tu-local', to handle the
>     +    case where someone accidentally refers to a TU-local value from within a
>          non-inline function template.  This will compile without errors as-is,
>          but any attempt to instantiate the decl will fail; this warning can be
>          used to ensure that this doesn't happen.  Unfortunately the warning has
>     @@ Commit message
>          instead just keeps the current (wrong) behaviour of non-exported
>          entities not being visible to ADL at all.
>      
>     +    Additionally, this patch doesn't attempt to ignore non-ODR uses of
>     +    constants in constexpr functions or templates.  The obvious approach of
>     +    folding them early in 'mark_use' doesn't seem to work (for a variety of
>     +    reasons), so this leaves this to a later patch to implement, as it's at
>     +    least no worse than the current behaviour and easy enough to workaround.
>     +
>     +    For completeness this patch adds a new xtreme-header testcase to ensure
>     +    that we have no regressions with regards to exposures of TU-local
>     +    declarations in the standard library header files.  A more restrictive
>     +    test would be to do 'export extern "C++"' here, but unfortunately the
>     +    system headers on some targets declare TU-local entities, so we'll make
>     +    do with checking that at least the C++ standard library headers don't
>     +    refer to such entities.
>     +
>          gcc/c-family/ChangeLog:
>      
>     -            * c.opt: New warning '-Wignored-exposures'.
>     +            * c.opt: New warning '-Wtemplate-names-tu-local'.
>      
>          gcc/cp/ChangeLog:
>      
>     @@ Commit message
>                  (TU_LOCAL_ENTITY_LOCATION): New accessor.
>                  (enum cp_tree_node_structure_enum): Add TS_CP_TU_LOCAL_ENTITY.
>                  (union GTY): Add tu_local_entity field.
>     -            * module.cc (enum tree_tag): New flag DB_IGNORED_EXPOSURE_BIT.
>     -            (depset::is_ignored_exposure): New accessor.
>     +            * module.cc (enum tree_tag): New flag DB_REFS_TU_LOCAL_BIT.
>                  (depset::has_defn): Override for TU-local entities.
>     -            (depset::hash::ignore_exposure): New field.
>     +            (depset::refs_tu_local): New accessor.
>     +            (depset::hash::ignore_tu_local): New field.
>                  (depset::hash::hash): Initialize it.
>                  (trees_out::tree_tag::tt_tu_local): New flag.
>                  (trees_out::writing_local_entities): New field.
>     @@ Commit message
>                  (trees_out::find_tu_local_decl): New function.
>                  (trees_out::tree_node): Intercept TU-local entities and write
>                  placeholder values for them instead of normal streaming.
>     -            (trees_in::tree_node): Read TU-local entities; handle TU-local
>     +            (trees_in::tree_node): Handle TU-local entities and TU-local
>                  template results.
>                  (trees_out::write_function_def): Ignore exposures in non-inline
>                  function bodies.
>     @@ Commit message
>                  (trees_in::read_class_def): Skip TU-local friends.
>                  (trees_out::write_definition): Record whether we're writing a
>                  decl which refers to TU-local entities.
>     -            (depset::hash::add_dependency): Handle ignored exposures.
>     +            (depset::hash::add_dependency): Only mark as exposure if we're not
>     +            ignoring TU-local entities.
>                  (depset::hash::find_dependencies): Use depset's own is_key_order
>                  function rather than delegating via walker.  Pass whether the
>                  decl has ignored TU-local entities in its definition.
>                  (depset::hash::finalize_dependencies): Implement new warning
>     -            Wignored-exposures.
>     +            Wtemplate-names-tu-local.
>                  (module_state::intercluster_seed): Don't seed TU-local deps.
>                  (module_state::write_cluster): Pass whether the decl has ignored
>                  TU-local entities in its definition.
>     @@ Commit message
>      
>          gcc/ChangeLog:
>      
>     -            * doc/invoke.texi: Document -Wignored-exposures.
>     +            * doc/invoke.texi: Document -Wtemplate-names-tu-local.
>      
>          gcc/testsuite/ChangeLog:
>      
>     @@ Commit message
>                  * g++.dg/modules/internal-6.C: New test.
>                  * g++.dg/modules/internal-7_a.C: New test.
>                  * g++.dg/modules/internal-7_b.C: New test.
>     +            * g++.dg/modules/internal-8_a.C: New test.
>     +            * g++.dg/modules/xtreme-header-8.C: New test.
>      
>          Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
>     +    Reviewed-by: Jason Merrill <jason@redhat.com>
>      
>       ## gcc/c-family/c.opt ##
>     -@@ gcc/c-family/c.opt: Wif-not-aligned
>     - C ObjC C++ ObjC++ Var(warn_if_not_aligned) Init(1) Warning
>     - Warn when the field in a struct is not aligned.
>     +@@ gcc/c-family/c.opt: Wtemplate-id-cdtor
>     + C++ ObjC++ Var(warn_template_id_cdtor) Warning
>     + Warn about simple-template-id in a constructor or destructor.
>       
>     -+Wignored-exposures
>     -+C++ ObjC++ Var(warn_ignored_exposures) Warning EnabledBy(Wextra)
>     -+Warn about ignored exposures of TU-local entities in a module.
>     ++Wtemplate-names-tu-local
>     ++C++ ObjC++ Var(warn_template_names_tu_local) Warning EnabledBy(Wextra)
>     ++Warn about templates naming TU-local entities in a module.
>      +
>     - Wignored-qualifiers
>     - C C++ Var(warn_ignored_qualifiers) Warning EnabledBy(Wextra)
>     - Warn whenever type qualifiers are ignored.
>     + Wterminate
>     + C++ ObjC++ Warning Var(warn_terminate) Init(1)
>     + Warn if a throw expression will always result in a call to terminate().
>      
>       ## gcc/cp/cp-objcp-common.cc ##
>      @@ gcc/cp/cp-objcp-common.cc: cp_tree_size (enum tree_code code)
>     @@ gcc/cp/cp-tree.h: check_constraint_info (tree t)
>          it for unscoped enums.  */
>       #define DECL_MODULE_EXPORT_P(NODE) TREE_LANG_FLAG_3 (NODE)
>       
>     -+/* Represents a streamed-in translation-unit-local entity. Any use of this
>     -+   should emit an error.  */
>     ++/* Represents a streamed-in translation-unit-local entity.  Any use of
>     ++   this node when instantiating a template should emit an error.  */
>      +struct GTY(()) tree_tu_local_entity {
>      +  struct tree_base base;
>      +  tree name;
>      +  location_t loc;
>      +};
>      +
>     -+/* The name of a translation-unit local-entity.  */
>     ++/* The name of a translation-unit-local entity.  */
>      +#define TU_LOCAL_ENTITY_NAME(NODE) \
>      +  (((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name)
>      +
>     @@ gcc/cp/cp-tree.h: union GTY((desc ("cp_tree_node_structure (&%h)"),
>      
>       ## gcc/cp/module.cc ##
>      @@ gcc/cp/module.cc: private:
>     +     DB_KIND_BITS = EK_BITS,
>     +     DB_DEFN_BIT = DB_KIND_BIT + DB_KIND_BITS,
>           DB_IS_MEMBER_BIT,		/* Is an out-of-class member.  */
>     -     DB_TU_LOCAL_BIT,		/* It is a TU-local entity.  */
>     +-    DB_TU_LOCAL_BIT,		/* It is a TU-local entity.  */
>     ++    DB_TU_LOCAL_BIT,		/* Is a TU-local entity.  */
>     ++    DB_REFS_TU_LOCAL_BIT,	/* Refers to a TU-local entity (but is not
>     ++				   necessarily an exposure.)  */
>           DB_EXPOSURE_BIT,		/* Exposes a TU-local entity.  */
>     -+    DB_IGNORED_EXPOSURE_BIT,	/* Will stream a TU-local entity.  */
>           DB_IMPORTED_BIT,		/* An imported entity.  */
>           DB_UNREACHED_BIT,		/* A yet-to-be reached entity.  */
>     -     DB_HIDDEN_BIT,		/* A hidden binding.  */
>      @@ gcc/cp/module.cc: public:
>       public:
>         bool has_defn () const
>     @@ gcc/cp/module.cc: public:
>       public:
>      @@ gcc/cp/module.cc: public:
>         {
>     -     return get_flag_bit<DB_EXPOSURE_BIT> ();
>     +     return get_flag_bit<DB_TU_LOCAL_BIT> ();
>         }
>     -+  bool is_ignored_exposure () const
>     ++  bool refs_tu_local () const
>      +  {
>     -+    return get_flag_bit<DB_IGNORED_EXPOSURE_BIT> ();
>     ++    return get_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
>      +  }
>     -   bool is_import () const
>     +   bool is_exposure () const
>         {
>     -     return get_flag_bit<DB_IMPORTED_BIT> ();
>     +     return get_flag_bit<DB_EXPOSURE_BIT> ();
>      @@ gcc/cp/module.cc: public:
>           depset *current;         /* Current depset being depended.  */
>           unsigned section;	     /* When writing out, the section.  */
>           bool reached_unreached;  /* We reached an unreached entity.  */
>     -+    bool ignore_exposure;    /* In a context where exposures are ignored.  */
>     ++    bool ignore_tu_local;    /* In a context where referencing a TU-local
>     ++				entity is not an exposure.  */
>       
>         public:
>           hash (size_t size, hash *c = NULL)
>             : parent (size), chain (c), current (NULL), section (0),
>      -	reached_unreached (false)
>     -+	reached_unreached (false), ignore_exposure (false)
>     ++	reached_unreached (false), ignore_tu_local (false)
>           {
>             worklist.create (size);
>           }
>     @@ gcc/cp/module.cc: public:
>       public:
>         /* Serialize various definitions. */
>      -  void write_definition (tree decl);
>     -+  void write_definition (tree decl, bool has_tu_local = false);
>     ++  void write_definition (tree decl, bool refs_tu_local = false);
>         void mark_declaration (tree decl, bool do_defn);
>       
>       private:
>     @@ gcc/cp/module.cc: trees_out::tree_node (tree t)
>           goto done;
>       
>      +  /* Find TU-local entities and intercept streaming to instead write a
>     -+     placeholder value; this way we don't need to expose such decls.
>     ++     placeholder value; this way we don't need to emit such decls.
>      +     We only need to do this when writing a definition of an entity
>     -+     that we know is an ignored exposure.  */
>     ++     that we know names a TU-local entity.  */
>      +  if (!is_initial_scan () && writing_local_entities)
>      +    {
>      +      tree local_decl = NULL_TREE;
>     @@ gcc/cp/module.cc: trees_in::tree_node (bool is_use)
>       
>      +    case tt_tu_local:
>      +      {
>     -+	/* A translation-unit local entity.  */
>     ++	/* A translation-unit-local entity.  */
>      +	res = make_node (TU_LOCAL_ENTITY);
>      +	int tag = insert (res);
>      +
>     @@ gcc/cp/module.cc: void
>      +       is ignored for determining exposures.  This should only matter
>      +       for templates (we don't emit the bodies of non-inline functions
>      +       to begin with).  */
>     -+    auto ovr = make_temp_override (dep_hash->ignore_exposure,
>     ++    auto ovr = make_temp_override (dep_hash->ignore_tu_local,
>      +				   !DECL_DECLARED_INLINE_P (decl));
>      +    tree_node (DECL_INITIAL (decl));
>      +    tree_node (DECL_SAVED_TREE (decl));
>     @@ gcc/cp/module.cc: trees_in::read_function_def (tree decl, tree maybe_template)
>       {
>      +  /* The initializer of a variable or variable template is ignored for
>      +     determining exposures.  */
>     -+  auto ovr = make_temp_override (dep_hash->ignore_exposure, VAR_P (decl));
>     ++  auto ovr = make_temp_override (dep_hash->ignore_tu_local, VAR_P (decl));
>      +
>         tree init = DECL_INITIAL (decl);
>         tree_node (init);
>     @@ gcc/cp/module.cc: trees_out::write_class_def (tree defn)
>      +      {
>      +	/* Friend declarations in class definitions are ignored when
>      +	   determining exposures.  */
>     -+	auto ovr = make_temp_override (dep_hash->ignore_exposure, true);
>     ++	auto ovr = make_temp_override (dep_hash->ignore_tu_local, true);
>       
>      -      /* Write the friend functions.  */
>      -      for (tree friends = DECL_FRIENDLIST (defn);
>     @@ gcc/cp/module.cc: trees_in::read_enum_def (tree defn, tree maybe_template)
>       
>       void
>      -trees_out::write_definition (tree decl)
>     -+trees_out::write_definition (tree decl, bool has_tu_local)
>     ++trees_out::write_definition (tree decl, bool refs_tu_local)
>       {
>      +  auto ovr = make_temp_override (writing_local_entities,
>     -+				 writing_local_entities || has_tu_local);
>     ++				 writing_local_entities || refs_tu_local);
>      +
>         if (streaming_p ())
>           {
>     @@ gcc/cp/module.cc: depset::hash::add_dependency (depset *dep)
>         if (dep->is_tu_local ())
>      -    current->set_flag_bit<DB_EXPOSURE_BIT> ();
>      +    {
>     -+      if (ignore_exposure)
>     -+	current->set_flag_bit<DB_IGNORED_EXPOSURE_BIT> ();
>     -+      else
>     ++      current->set_flag_bit<DB_REFS_TU_LOCAL_BIT> ();
>     ++      if (!ignore_tu_local)
>      +	current->set_flag_bit<DB_EXPOSURE_BIT> ();
>      +    }
>       
>     @@ gcc/cp/module.cc: depset::hash::find_dependencies (module_state *module)
>       		  walker.decl_value (decl, current);
>       		  if (current->has_defn ())
>      -		    walker.write_definition (decl);
>     -+		    walker.write_definition (decl,
>     -+					     current->is_ignored_exposure ());
>     ++		    walker.write_definition (decl, current->refs_tu_local ());
>       		}
>       	      walker.end ();
>       
>     @@ gcc/cp/module.cc: depset::hash::finalize_dependencies ()
>       	  /* We should have emitted an error above.  */
>       	  gcc_checking_assert (explained);
>       	}
>     -+      else if (warn_ignored_exposures && dep->is_ignored_exposure ())
>     ++      else if (warn_template_names_tu_local
>     ++	       && dep->refs_tu_local () && !dep->is_tu_local ())
>      +	{
>      +	  tree decl = dep->get_entity ();
>      +
>     @@ gcc/cp/module.cc: depset::hash::finalize_dependencies ()
>      +	  for (depset *rdep : dep->deps)
>      +	    if (!rdep->is_binding () && rdep->is_tu_local ())
>      +	      {
>     -+		tree exposed = rdep->get_entity ();
>     ++		tree ref = rdep->get_entity ();
>      +		auto_diagnostic_group d;
>      +		if (warning_at (DECL_SOURCE_LOCATION (decl),
>     -+				OPT_Wignored_exposures,
>     ++				OPT_Wtemplate_names_tu_local,
>      +				"%qD refers to TU-local entity %qD and cannot "
>     -+				"be instantiated in other TUs", decl, exposed))
>     -+		  is_tu_local_entity (exposed, /*explain=*/true);
>     ++				"be instantiated in other TUs", decl, ref))
>     ++		  is_tu_local_entity (ref, /*explain=*/true);
>      +		break;
>      +	      }
>      +	}
>     @@ gcc/cp/module.cc: module_state::write_cluster (elf_out *to, depset *scc[], unsig
>       	      sec.tree_node (decl);
>       	      dump () && dump ("Writing definition %N", decl);
>      -	      sec.write_definition (decl);
>     -+	      sec.write_definition (decl, b->is_ignored_exposure ());
>     ++	      sec.write_definition (decl, b->refs_tu_local ());
>       
>       	      if (!namer->has_defn ())
>       		namer = b;
>     @@ gcc/cp/pt.cc: tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_d
>      
>       ## gcc/doc/invoke.texi ##
>      @@ gcc/doc/invoke.texi: in the following sections.
>     - -Weffc++ -Wno-elaborated-enum-base
>     - -Wno-exceptions -Wextra-semi -Wno-global-module -Wno-inaccessible-base
>     - -Wno-inherited-variadic-ctor  -Wno-init-list-lifetime
>     ---Winvalid-constexpr -Winvalid-imported-macros
>     -+-Wignored-exposures -Winvalid-constexpr -Winvalid-imported-macros
>     - -Wno-invalid-offsetof  -Wno-literal-suffix
>     - -Wmismatched-new-delete -Wmismatched-tags
>     - -Wmultiple-inheritance  -Wnamespaces  -Wnarrowing
>     -@@ gcc/doc/invoke.texi: the variable declaration statement.
>     + -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
>     + -Wsized-deallocation  -Wsuggest-final-methods
>     + -Wsuggest-final-types  -Wsuggest-override  -Wno-template-body
>     +--Wno-template-id-cdtor
>     ++-Wno-template-id-cdtor  -Wtemplate-names-tu-local
>     + -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
>     + -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
>       
>     - @end itemize
>     +@@ gcc/doc/invoke.texi: template<typename T> struct S @{
>     + @option{-Wtemplate-id-cdtor} is enabled by default with
>     + @option{-std=c++20}; it is also enabled by @option{-Wc++20-compat}.
>       
>     -+@opindex Wignored-exposures
>     -+@opindex Wno-ignored-exposures
>     -+@item -Wignored-exposures
>     ++@opindex Wtemplate-names-tu-local
>     ++@opindex Wno-template-names-tu-local
>     ++@item -Wtemplate-names-tu-local
>      +Warn when a template body hides an exposure of a translation-unit-local
>      +entity.  In most cases, referring to a translation-unit-local entity
>      +(such as an internal linkage declaration) within an entity that is
>      +emitted into a module's CMI is an error.  However, within the
>      +initializer of a variable, or in the body of a non-inline function,
>     -+this error is suppressed.
>     ++this is not an exposure and no error is emitted.
>      +
>      +This can cause variable or function templates to accidentally become
>      +unusable if they reference such an entity, because other translation
>      +units that import the template will never be able to instantiate it.
>      +This warning attempts to detect cases where this might occur.
>      +
>     -+This warning is enabled by @option{-Wextra}.
>     ++This flag is enabled by @option{-Wextra}.
>      +
>     - @opindex Winvalid-constexpr
>     - @opindex Wno-invalid-constexpr
>     - @item -Winvalid-constexpr
>     + @opindex Wterminate
>     + @opindex Wno-terminate
>     + @item -Wno-terminate @r{(C++ and Objective-C++ only)}
>      
>       ## gcc/testsuite/g++.dg/modules/internal-5_a.C (new) ##
>      @@
>     -+// { dg-additional-options "-fmodules-ts -Wignored-exposures" }
>     ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
>      +// { dg-module-cmi M }
>      +// Ignore exposures in these cases
>      +
>     @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
>      +  internal_tmpl<T>();
>      +}
>      +template void function_tmpl<ok_inst_tag>();
>     ++template <> void function_tmpl<ok_inst_tag*>() {}
>      +
>      +
>      +// The initializer for a variable or variable template
>     @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
>      +     internal_ovl(internal_x), internal_tmpl<T*>(), 0);
>      +
>      +template int var_tmpl<ok_inst_tag>;
>     -+template int var_tmpl<ok_inst_tag*>;
>     ++template <> int var_tmpl<ok_inst_tag*> = 0;
>      +
>      +export int& constant_ref = internal_x;
>      +static_assert (&constant_ref == &internal_x);
>     @@ gcc/testsuite/g++.dg/modules/internal-5_a.C (new)
>      +// Any reference to a non-volatile const object or reference with internal or
>      +// no linkage initialized with a constant expression that is not an ODR-use
>      +static const int value = 123;
>     -+static const int& ref = value;
>     ++static const int& ref = 456;
>      +static const internal_t internal {};
>     ++void f(int) {}
>      +export inline void no_odr_use() {
>      +  int x = value;
>      +  int y = ref;
>      +  int z = (internal, 0);
>     ++
>     ++  value;
>     ++  bool b = value < value;
>     ++  f(value);
>      +}
>      
>       ## gcc/testsuite/g++.dg/modules/internal-5_b.C (new) ##
>     @@ gcc/testsuite/g++.dg/modules/internal-5_b.C (new)
>      +  no_odr_use();
>      +
>      +  function_tmpl<ok_inst_tag>();
>     ++  function_tmpl<ok_inst_tag*>();
>      +  int b = var_tmpl<ok_inst_tag>;
>      +  int c = var_tmpl<ok_inst_tag*>;
>      +
>     @@ gcc/testsuite/g++.dg/modules/internal-6.C (new)
>      
>       ## gcc/testsuite/g++.dg/modules/internal-7_a.C (new) ##
>      @@
>     -+// { dg-additional-options "-fmodules-ts -Wignored-exposures" }
>     ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
>      +// Test streaming and instantiations of various kinds of exposures
>      +
>      +export module M;
>     @@ gcc/testsuite/g++.dg/modules/internal-7_b.C (new)
>      +}
>      +
>      +// { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } 0 }
>     +
>     + ## gcc/testsuite/g++.dg/modules/internal-8_a.C (new) ##
>     +@@
>     ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
>     ++// Non-ODR usages of const variables currently are erroneously
>     ++// reported in templates and constexpr functions; this test
>     ++// XFAILS them until we can implement a fix.
>     ++
>     ++export module M;
>     ++
>     ++namespace { struct internal_t {}; };
>     ++static const int value = 123;
>     ++static const int& ref = 456;
>     ++static const internal_t internal {};
>     ++
>     ++constexpr void f(int) {}
>     ++
>     ++export constexpr
>     ++void no_odr_use_cexpr() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
>     ++  int x = value;
>     ++  int y = ref;
>     ++  int z = (internal, 0);
>     ++
>     ++  value;
>     ++  bool b = value < value;
>     ++  f(value);
>     ++}
>     ++
>     ++export template <typename T>
>     ++void no_odr_use_templ() {  // { dg-bogus "TU-local" "" { xfail *-*-* } }
>     ++  int x = value;
>     ++  int y = ref;
>     ++  int z = (internal, 0);
>     ++
>     ++  value;
>     ++  bool b = value < value;
>     ++  f(value);
>     ++}
>     +
>     + ## gcc/testsuite/g++.dg/modules/xtreme-header-8.C (new) ##
>     +@@
>     ++// PR c++/115126
>     ++// { dg-additional-options "-fmodules-ts -Wtemplate-names-tu-local" }
>     ++// { dg-module-cmi xstd }
>     ++
>     ++export module xstd;
>     ++extern "C++" {
>     ++  #include "xtreme-header.h"
>     ++}
> 3:  49e06cc4065 ! 3:  918c037a0a9 c++/modules: Support anonymous namespaces in header units
>     @@ Metadata
>      Author: Nathaniel Shead <nathanieloshead@gmail.com>
>      
>       ## Commit message ##
>     -    c++/modules: Support anonymous namespaces in header units
>     +    c++/modules: Support unnamed namespaces in header units
>      
>     -    A header unit may contain anonymous namespaces, and those declarations
>     +    A header unit may contain unnamed namespaces, and those declarations
>          are exported (as with any declaration in a header unit).  This patch
>          ensures that such declarations are correctly handled.
>      
>     -    The change to 'make_namespace_finish' is required so that if an
>     -    anonymous namespace is first seen by an import it is correctly handled
>     -    within 'add_imported_namespace'.  I don't see any particular reason why
>     -    handling of anonymous namespaces here had to be handled separately
>     -    outside that function since these are the only two callers.
>     +    The change to 'make_namespace_finish' is required so that if an unnamed
>     +    namespace is first seen by an import it is correctly handled within
>     +    'add_imported_namespace'.  I don't see any particular reason why
>     +    handling of unnamed namespaces here had to be handled separately outside
>     +    that function since these are the only two callers.
>      
>          gcc/cp/ChangeLog:
>      
>                  * module.cc (depset::hash::add_binding_entity): Also walk
>     -            anonymous namespaces.
>     +            unnamed namespaces.
>                  (module_state::write_namespaces): Adjust assertion.
>                  * name-lookup.cc (push_namespace): Move anon using-directive
>                  handling to...
>     @@ Commit message
>      
>          gcc/testsuite/ChangeLog:
>      
>     -            * g++.dg/modules/internal-8_a.H: New test.
>     -            * g++.dg/modules/internal-8_b.C: New test.
>     +            * g++.dg/modules/internal-9_a.H: New test.
>     +            * g++.dg/modules/internal-9_b.C: New test.
>      
>          Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
>     +    Reviewed-by: Jason Merrill <jason@redhat.com>
>      
>       ## gcc/cp/module.cc ##
>      @@ gcc/cp/module.cc: depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
>     @@ gcc/cp/name-lookup.cc: make_namespace_finish (tree ns, tree *slot, bool from_imp
>         if (DECL_NAMESPACE_INLINE_P (ns) || !DECL_NAME (ns))
>           emit_debug_info_using_namespace (ctx, ns, true);
>      +
>     ++  /* An unnamed namespace implicitly has a using-directive inserted so
>     ++     that its contents are usable in the surrounding context.  */
>      +  if (!DECL_NAMESPACE_INLINE_P (ns) && !DECL_NAME (ns))
>      +    add_using_namespace (NAMESPACE_LEVEL (ctx)->using_directives, ns);
>       }
>     @@ gcc/cp/name-lookup.cc: push_namespace (tree name, bool make_inline)
>           }
>       
>      
>     - ## gcc/testsuite/g++.dg/modules/internal-8_a.H (new) ##
>     + ## gcc/testsuite/g++.dg/modules/internal-9_a.H (new) ##
>      @@
>      +// { dg-additional-options "-fmodule-header" }
>      +// { dg-module-cmi {} }
>     @@ gcc/testsuite/g++.dg/modules/internal-8_a.H (new)
>      +
>      +namespace ns2 = ns;
>      
>     - ## gcc/testsuite/g++.dg/modules/internal-8_b.C (new) ##
>     + ## gcc/testsuite/g++.dg/modules/internal-9_b.C (new) ##
>      @@
>      +// { dg-additional-options "-fmodules-ts" }
>      +
>     -+import "internal-8_a.H";
>     ++import "internal-9_a.H";
>      +
>      +int main() {
>      +  auto x2 = x;
> 4:  671a9de084e ! 4:  46d0ecf964c c++/modules: Check linkage for exported declarations
>     @@ Commit message
>                  * g++.dg/modules/export-6.C: New test.
>      
>          Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
>     +    Reviewed-by: Jason Merrill <jason@redhat.com>
>      
>       ## gcc/cp/cp-tree.h ##
>      @@ gcc/cp/cp-tree.h: extern void set_originating_module (tree, bool friend_p = false);
>     @@ gcc/cp/module.cc: set_originating_module (tree decl, bool friend_p ATTRIBUTE_UNU
>           }
>       
>      -  if (!module_exporting_p ())
>     -+  /* It is illegal to export a declaration with internal linkage.  However, at
>     -+     the point this function is called we don't always know yet whether this
>     ++  /* 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 ())
>     @@ gcc/cp/name-lookup.cc: push_namespace (tree name, bool make_inline)
>      -		      "exporting namespace with internal linkage");
>      +	    {
>      +	      if (name)
>     -+		error_at (input_location,
>     -+			  "exporting namespace %qD with internal linkage", ns);
>     ++		{
>     ++		  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 anonymous namespace");
>     ++		error_at (input_location, "exporting unnamed namespace");
>      +	    }
>       	}
>             if (module_purview_p ())
>     @@ gcc/testsuite/g++.dg/modules/export-3.C: namespace {
>       }
>       
>      -export namespace {}  // { dg-error "internal linkage" }
>     -+export namespace {}  // { dg-error "exporting anonymous namespace" }
>     ++export namespace {}  // { dg-error "exporting unnamed namespace" }
>      
>       ## gcc/testsuite/g++.dg/modules/export-6.C (new) ##
>      @@
>     @@ gcc/testsuite/g++.dg/modules/export-6.C (new)
>      +#endif
>      +}
>      +
>     -+export namespace {}  // { dg-error "exporting anonymous namespace" }
>     ++export namespace {}  // { dg-error "exporting unnamed namespace" }
>      +export namespace ns2 = ns;  // { dg-error "internal linkage" }
>      
>       ## libcc1/libcp1plugin.cc ##
> 5:  0923d54daf1 ! 5:  3470fe5b429 c++/modules: Validate external linkage definitions in header units [PR116401]
>     @@ Commit message
>                  * g++.dg/modules/hdr-2.H: New test.
>      
>          Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
>     +    Reviewed-by: Jason Merrill <jason@redhat.com>
>      
>       ## gcc/cp/decl.cc ##
>      @@ gcc/cp/decl.cc: grokfndecl (tree ctype,
>       	 have one: the restriction that you can't repeat a deduction guide
>       	 makes them more like a definition anyway.  */
>             DECL_INITIAL (decl) = void_node;
>     ++      /* But to ensure that external-linkage deduction guides in header units
>     ++	 don't fall afoul of [module.import] p6, mark them as inline to give
>     ++	 the same effect as them not having a definition.  */
>      +      DECL_DECLARED_INLINE_P (decl) = true;
>             break;
>           default:
> 6:  0f78398918f < -:  ----------- c++/modules: Add testcase for standard-library exposures [PR115126]
> -- 
> 2.46.0
>