On Tue, Sep 09, 2025 at 07:38:46PM +0200, Jason Merrill wrote:
> On 9/9/25 4:49 PM, Nathaniel Shead wrote:
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > (If you prefer I could also split up each fix into separate commits.)
> > 
> > -- >8 --
> > 
> > This patch fixes some issues with import handling.
> > 
> > The main functional change is to allow importing a module's interface
> > file into its implementation file indirectly.  [module.import] p9
> > forbids an explicit 'import M;' declaration, but there's no provision
> > against having 'import X;' where module X itself imports M, so this
> > patch splits up the detection of circular imports to handle this better.
> > I also updated the errors to be closer to the standard wording.
> > 
> > A second issue I found while testing this is that we don't properly
> > handle name visibility when a partition implementation unit imports its
> > primary module interface (g++.dg/modules/part-10).  This is resolved by
> > setting 'module_p' on the primary interface when it gets imported.
> > 
> > Solving this I incidentally removed the assertion that PR121808 was
> > failing on, which was never valid: we can enter import_module for a
> > module previously seen as a module-declaration if parsing bails before
> > declare_module is called.  I experimented with guaranteeing that
> > declare_module always gets called for a module_state generated during
> > preprocessing if at all possible, but the resulting errors didn't seem a
> > lot better so I've left it as-is for now.
> > 
> > I did make a small adjustment so that a direct import of a module
> > doesn't overwrite the location of a module-declaration from earlier in
> > the file; this is important because preprocessing (and thus the
> > assigning of these locations) seems to happen for the whole file before
> > parsing begins, which can otherwise cause confusing locations referring
> > to later in the file than parsing would otherwise indicate.
> > 
> >     PR c++/99682
> >     PR c++/121808
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * module.cc (class module_state): Rename check_not_purview.
> >     (module_state::check_not_purview): Rename to...
> >     (module_state::check_circular_import): ...this.  Only handle
> >     interface dependencies, adjust diagnostic message.
> >     (module_state::read_imports): Use new function.  Pass location
> >     of import as from_loc instead of the module location.
> >     (module_state::do_import): Set module_p when importing the
> >     primary interface for the current module.
> >     (import_module): Split out check for imports in own unit.
> >     Remove incorrect assertion.
> >     (preprocess_module): Don't overwrite module-decl location with
> >     later import.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/modules/circ-1_c.C: Adjust diagnostic.
> >     * g++.dg/modules/mod-decl-1.C: Likewise.
> >     * g++.dg/modules/mod-decl-2_b.C: Likewise.
> >     * g++.dg/modules/pr99174.H: Likewise.
> >     * g++.dg/modules/import-3_a.C: New test.
> >     * g++.dg/modules/import-3_b.C: New test.
> >     * g++.dg/modules/import-3_c.C: New test.
> >     * g++.dg/modules/mod-decl-9.C: New test.
> >     * g++.dg/modules/part-10_a.C: New test.
> >     * g++.dg/modules/part-10_b.C: New test.
> >     * g++.dg/modules/part-10_c.C: New test.
> >     * g++.dg/modules/part-10_d.C: New test.
> > 
> > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > ---
> >   gcc/cp/module.cc                            | 44 +++++++++++++++------
> >   gcc/testsuite/g++.dg/modules/circ-1_c.C     |  2 +-
> >   gcc/testsuite/g++.dg/modules/import-3_a.C   |  6 +++
> >   gcc/testsuite/g++.dg/modules/import-3_b.C   |  8 ++++
> >   gcc/testsuite/g++.dg/modules/import-3_c.C   | 11 ++++++
> >   gcc/testsuite/g++.dg/modules/mod-decl-1.C   |  4 +-
> >   gcc/testsuite/g++.dg/modules/mod-decl-2_b.C |  2 +-
> >   gcc/testsuite/g++.dg/modules/mod-decl-9.C   | 13 ++++++
> >   gcc/testsuite/g++.dg/modules/part-10_a.C    |  5 +++
> >   gcc/testsuite/g++.dg/modules/part-10_b.C    |  9 +++++
> >   gcc/testsuite/g++.dg/modules/part-10_c.C    | 10 +++++
> >   gcc/testsuite/g++.dg/modules/part-10_d.C    | 10 +++++
> >   gcc/testsuite/g++.dg/modules/pr99174.H      |  2 +-
> >   13 files changed, 108 insertions(+), 18 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/modules/import-3_a.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/import-3_b.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/import-3_c.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/mod-decl-9.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-10_a.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-10_b.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-10_c.C
> >   create mode 100644 gcc/testsuite/g++.dg/modules/part-10_d.C
> > 
> > diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
> > index 50fc1af0354..ec9d84f11cc 100644
> > --- a/gcc/cp/module.cc
> > +++ b/gcc/cp/module.cc
> > @@ -3782,7 +3782,7 @@ class GTY((chain_next ("%h.parent"), for_user)) 
> > module_state {
> >     }
> >    public:
> > -  bool check_not_purview (location_t loc);
> > +  bool check_circular_import (location_t loc);
> >    public:
> >     void mangle (bool include_partition);
> > @@ -15996,20 +15996,19 @@ recursive_lazy (unsigned snum = ~0u)
> >     return false;
> >   }
> > -/* If THIS is the current purview, issue an import error and return false. 
> >  */
> > +/* If THIS has an interface dependency on itself, report an error and
> > +   return false.  */
> >   bool
> > -module_state::check_not_purview (location_t from)
> > +module_state::check_circular_import (location_t from)
> >   {
> > -  module_state *imp = this_module ();
> > -  if (imp && !imp->name)
> > -    imp = imp->parent;
> > -  if (imp == this)
> > +  if (this == this_module ())
> >       {
> >         /* Cannot import the current module.  */
> >         auto_diagnostic_group d;
> > -      error_at (from, "cannot import module in its own purview");
> > -      inform (loc, "module %qs declared here", get_flatname ());
> > +      error_at (from, "module %qs depends on itself", get_flatname ());
> > +      if (!header_module_p ())
> > +   inform (loc, "module %qs declared here", get_flatname ());
> >         return false;
> >       }
> >     return true;
> > @@ -16320,7 +16319,7 @@ module_state::read_imports (bytes_in &sec, 
> > cpp_reader *reader, line_maps *lmaps)
> >       if (sec.get_overrun ())
> >         break;
> > -     if (!imp->check_not_purview (loc))
> > +     if (!imp->check_circular_import (floc))
> >         continue;
> >       if (imp->loadedness == ML_NONE)
> > @@ -21535,6 +21534,12 @@ module_state::do_import (cpp_reader *reader, bool 
> > outermost)
> >   {
> >     gcc_assert (global_namespace == current_scope () && loadedness == 
> > ML_NONE);
> > +  /* If this TU is a partition of the module we're importing,
> > +     that module is the primary module interface.  */
> > +  if (this_module ()->is_partition ()
> > +      && this == get_primary (this_module ()))
> > +    module_p = true;
> > +
> >     loc = linemap_module_loc (line_table, loc, get_flatname ());
> >     if (lazy_open >= lazy_limit)
> > @@ -21807,7 +21812,17 @@ void
> >   import_module (module_state *import, location_t from_loc, bool 
> > exporting_p,
> >            tree, cpp_reader *reader)
> >   {
> > -  if (!import->check_not_purview (from_loc))
> > +  /* A non-partition implementation unit has no name.  */
> > +  if (!this_module ()->name && this_module ()->parent == import)
> 
> The patch is OK, but could you add a comment to module_state::parent? It's
> not immediately obvious what it means.
> 
> Jason
> 

Thanks, this is the comment I've added:

--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -3667,8 +3667,12 @@ class GTY((chain_next ("%h.parent"), for_user)) 
module_state {
   bitmap imports;      /* Transitive modules we're importing.  */
   bitmap exports;      /* Subset of that, that we're exporting.  */
 
+  /* For a named module interface A.B, parent is A and name is B.
+     For a partition M:P, parent is M and name is P.
+     For an implementation unit I, parent is I's interface and name is NULL.
+     Otherwise parent is NULL and name will be the flatname.  */
   module_state *parent;
-  tree name;           /* Name of the module.  */
+  tree name;
 
   slurping *slurp;     /* Data for loading.  */

Reply via email to