Skip to content

Commit 014873f

Browse files
committed
c++/modules: Fix exported using-directive of imported namespace [PR121702]
Currently we represent exported using-directives as a list of indices into the namespace array that we stream. However this list of namespaces doesn't include any namespaces that we don't expose in this module's purview, and so we ICE. This patch reworks the handling to instead use the existing depset tracking for namespaces directly. This means that we don't need to build up a second lookup map when streaming, and we can reuse the logic in {read,write}_namespace. We do need to make sure that we create a depset for namespaces only referenced by a using-directive, though. I don't expect to be exporting large numbers of using-directives from a namespace, so for simplicity we stream the names as {parent, target} pairs. This also adjusts read handling so that we load the using-directives for any import (including indirect) if it's in the import list for the current TU. Otherwise we run into issues if the using-directive is in a namespace that is otherwise never referenced in the 'export import'ing module, because we never walk this namespace and so never know that we need to emit it. To do this the patch ensures that we calculate the import list before read_language is called. As a drive-by fix, I noticed that with modules 'add_using_namespace' will add duplicate using-directives because we compare usings against the target namespace, but we then push a wrapping USING_DECL instead. This reworks so that the contents of the structure is equivalent between modules and non-modules code. PR c++/121702 gcc/cp/ChangeLog: * module.cc (enum module_state_counts): New counter. (depset::hash::add_namespace_entities): Seed using-directive targets for later streaming. (module_state::write_namespaces): Don't handle using-directives here. (module_state::read_namespaces): Likewise. (module_state::write_using_directives): New function. (module_state::read_using_directives): New function. (module_state::write_counts): Log using-directives. (module_state::read_counts): Likewise. (module_state::write_begin): Stream using-directives. (module_state::read_language): Read using-directives if directly importing. (module_state::direct_import): Update current TU import list before calling read_language. * name-lookup.cc (add_using_namespace): Fix lookup of previous using-directives. * parser.cc (cp_parser_import_declaration): Don't set MK_EXPORTING when performing import_module. gcc/testsuite/ChangeLog: * g++.dg/modules/namespace-10_c.C: Add check for log dump. * g++.dg/modules/namespace-13_a.C: New test. * g++.dg/modules/namespace-13_b.C: New test. * g++.dg/modules/namespace-13_c.C: New test. Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
1 parent c39dbb6 commit 014873f

File tree

7 files changed

+193
-90
lines changed

7 files changed

+193
-90
lines changed

gcc/cp/module.cc

Lines changed: 102 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -3633,6 +3633,7 @@ enum module_state_counts
36333633
MSC_pendings,
36343634
MSC_entities,
36353635
MSC_namespaces,
3636+
MSC_using_directives,
36363637
MSC_bindings,
36373638
MSC_macros,
36383639
MSC_inits,
@@ -3854,6 +3855,10 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state {
38543855
unsigned, unsigned *crc_ptr);
38553856
bool read_namespaces (unsigned);
38563857

3858+
unsigned write_using_directives (elf_out *to, depset::hash &,
3859+
vec<depset *> spaces, unsigned *crc_ptr);
3860+
bool read_using_directives (unsigned);
3861+
38573862
void intercluster_seed (trees_out &sec, unsigned index, depset *dep);
38583863
unsigned write_cluster (elf_out *to, depset *depsets[], unsigned size,
38593864
depset::hash &, unsigned *counts, unsigned *crc_ptr);
@@ -14691,17 +14696,22 @@ depset::hash::add_namespace_entities (tree ns, bitmap partitions)
1469114696
data.partitions = partitions;
1469214697
data.hash = this;
1469314698

14694-
hash_table<named_decl_hash>::iterator end
14695-
(DECL_NAMESPACE_BINDINGS (ns)->end ());
14696-
for (hash_table<named_decl_hash>::iterator iter
14697-
(DECL_NAMESPACE_BINDINGS (ns)->begin ()); iter != end; ++iter)
14699+
for (tree binding : *DECL_NAMESPACE_BINDINGS (ns))
1469814700
{
1469914701
data.binding = nullptr;
1470014702
data.met_namespace = false;
14701-
if (walk_module_binding (*iter, partitions, add_binding_entity, &data))
14703+
if (walk_module_binding (binding, partitions, add_binding_entity, &data))
1470214704
count++;
1470314705
}
1470414706

14707+
/* Seed any using-directives so that we emit the relevant namespaces. */
14708+
for (tree udir : NAMESPACE_LEVEL (ns)->using_directives)
14709+
if (TREE_CODE (udir) == USING_DECL && DECL_MODULE_EXPORT_P (udir))
14710+
{
14711+
make_dependency (USING_DECL_DECLS (udir), depset::EK_NAMESPACE);
14712+
count++;
14713+
}
14714+
1470514715
if (count)
1470614716
dump () && dump ("Found %u entries", count);
1470714717
dump.outdent ();
@@ -17128,15 +17138,11 @@ module_state::write_namespaces (elf_out *to, vec<depset *> spaces,
1712817138
bytes_out sec (to);
1712917139
sec.begin ();
1713017140

17131-
hash_map<tree, unsigned> ns_map;
17132-
1713317141
for (unsigned ix = 0; ix != num; ix++)
1713417142
{
1713517143
depset *b = spaces[ix];
1713617144
tree ns = b->get_entity ();
1713717145

17138-
ns_map.put (ns, ix);
17139-
1714017146
/* This could be an anonymous namespace even for a named module,
1714117147
since we can still emit no-linkage decls. */
1714217148
gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
@@ -17178,31 +17184,6 @@ module_state::write_namespaces (elf_out *to, vec<depset *> spaces,
1717817184
}
1717917185
}
1718017186

17181-
/* Now write exported using-directives, as a sequence of 1-origin indices in
17182-
the spaces array (not entity indices): First the using namespace, then the
17183-
used namespaces. And then a zero terminating the list. :: is
17184-
represented as index -1. */
17185-
auto emit_one_ns = [&](unsigned ix, tree ns) {
17186-
for (auto udir: NAMESPACE_LEVEL (ns)->using_directives)
17187-
{
17188-
if (TREE_CODE (udir) != USING_DECL || !DECL_MODULE_EXPORT_P (udir))
17189-
continue;
17190-
tree ns2 = USING_DECL_DECLS (udir);
17191-
dump() && dump ("Writing using-directive in %N for %N",
17192-
ns, ns2);
17193-
sec.u (ix);
17194-
sec.u (*ns_map.get (ns2) + 1);
17195-
}
17196-
};
17197-
emit_one_ns (-1, global_namespace);
17198-
for (unsigned ix = 0; ix != num; ix++)
17199-
{
17200-
depset *b = spaces[ix];
17201-
tree ns = b->get_entity ();
17202-
emit_one_ns (ix + 1, ns);
17203-
}
17204-
sec.u (0);
17205-
1720617187
sec.end (to, to->name (MOD_SNAME_PFX ".nms"), crc_p);
1720717188
dump.outdent ();
1720817189
}
@@ -17221,8 +17202,6 @@ module_state::read_namespaces (unsigned num)
1722117202
dump () && dump ("Reading namespaces");
1722217203
dump.indent ();
1722317204

17224-
tree *ns_map = XALLOCAVEC (tree, num);
17225-
1722617205
for (unsigned ix = 0; ix != num; ix++)
1722717206
{
1722817207
unsigned entity_index = sec.u ();
@@ -17284,8 +17263,6 @@ module_state::read_namespaces (unsigned num)
1728417263
DECL_ATTRIBUTES (inner)
1728517264
= tree_cons (get_identifier ("abi_tag"), tags, DECL_ATTRIBUTES (inner));
1728617265

17287-
ns_map[ix] = inner;
17288-
1728917266
/* Install the namespace. */
1729017267
(*entity_ary)[entity_lwm + entity_index] = inner;
1729117268
if (DECL_MODULE_IMPORT_P (inner))
@@ -17301,41 +17278,79 @@ module_state::read_namespaces (unsigned num)
1730117278
}
1730217279
}
1730317280

17304-
/* Read the exported using-directives. */
17305-
while (unsigned ix = sec.u ())
17281+
dump.outdent ();
17282+
if (!sec.end (from ()))
17283+
return false;
17284+
return true;
17285+
}
17286+
17287+
unsigned
17288+
module_state::write_using_directives (elf_out *to, depset::hash &table,
17289+
vec<depset *> spaces, unsigned *crc_p)
17290+
{
17291+
dump () && dump ("Writing using-directives");
17292+
dump.indent ();
17293+
17294+
bytes_out sec (to);
17295+
sec.begin ();
17296+
17297+
unsigned num = 0;
17298+
auto emit_one_ns = [&](depset *parent_dep)
1730617299
{
17307-
tree ns;
17308-
if (ix == (unsigned)-1)
17309-
ns = global_namespace;
17310-
else
17311-
{
17312-
if (--ix >= num)
17313-
{
17314-
sec.set_overrun ();
17315-
break;
17316-
}
17317-
ns = ns_map [ix];
17318-
}
17319-
unsigned ix2 = sec.u ();
17320-
if (--ix2 >= num)
17300+
tree parent = parent_dep->get_entity ();
17301+
for (auto udir : NAMESPACE_LEVEL (parent)->using_directives)
1732117302
{
17322-
sec.set_overrun ();
17323-
break;
17324-
}
17325-
tree ns2 = ns_map [ix2];
17326-
if (directness)
17327-
{
17328-
dump() && dump ("Reading using-directive in %N for %N",
17329-
ns, ns2);
17330-
/* In an export import this will mark the using-directive as
17331-
exported, so it will be emitted again. */
17332-
add_using_namespace (ns, ns2);
17303+
if (TREE_CODE (udir) != USING_DECL || !DECL_MODULE_EXPORT_P (udir))
17304+
continue;
17305+
tree target = USING_DECL_DECLS (udir);
17306+
depset *target_dep = table.find_dependency (target);
17307+
gcc_checking_assert (target_dep);
17308+
17309+
dump () && dump ("Writing using-directive in %N for %N",
17310+
parent, target);
17311+
write_namespace (sec, parent_dep);
17312+
write_namespace (sec, target_dep);
17313+
++num;
1733317314
}
17334-
else
17335-
/* Ignore using-directives from indirect imports, we only want them
17336-
from our own imports. */
17337-
dump() && dump ("Ignoring using-directive in %N for %N",
17338-
ns, ns2);
17315+
};
17316+
17317+
emit_one_ns (table.find_dependency (global_namespace));
17318+
for (depset *parent_dep : spaces)
17319+
emit_one_ns (parent_dep);
17320+
17321+
sec.end (to, to->name (MOD_SNAME_PFX ".udi"), crc_p);
17322+
dump.outdent ();
17323+
17324+
return num;
17325+
}
17326+
17327+
bool
17328+
module_state::read_using_directives (unsigned num)
17329+
{
17330+
if (!bitmap_bit_p ((*modules)[0]->imports, mod))
17331+
{
17332+
dump () && dump ("Ignoring using-directives because module %M "
17333+
"is not visible in this TU", this);
17334+
return true;
17335+
}
17336+
17337+
bytes_in sec;
17338+
17339+
if (!sec.begin (loc, from (), MOD_SNAME_PFX ".udi"))
17340+
return false;
17341+
17342+
dump () && dump ("Reading using-directives");
17343+
dump.indent ();
17344+
17345+
for (unsigned ix = 0; ix != num; ++ix)
17346+
{
17347+
tree parent = read_namespace (sec);
17348+
tree target = read_namespace (sec);
17349+
if (sec.get_overrun ())
17350+
break;
17351+
17352+
dump () && dump ("Read using-directive in %N for %N", parent, target);
17353+
add_using_namespace (parent, target);
1733917354
}
1734017355

1734117356
dump.outdent ();
@@ -19857,6 +19872,7 @@ module_state::write_counts (elf_out *to, unsigned counts[MSC_HWM],
1985719872
dump ("Pendings %u", counts[MSC_pendings]);
1985819873
dump ("Entities %u", counts[MSC_entities]);
1985919874
dump ("Namespaces %u", counts[MSC_namespaces]);
19875+
dump ("Using-directives %u", counts[MSC_using_directives]);
1986019876
dump ("Macros %u", counts[MSC_macros]);
1986119877
dump ("Initializers %u", counts[MSC_inits]);
1986219878
}
@@ -19883,6 +19899,7 @@ module_state::read_counts (unsigned counts[MSC_HWM])
1988319899
dump ("Pendings %u", counts[MSC_pendings]);
1988419900
dump ("Entities %u", counts[MSC_entities]);
1988519901
dump ("Namespaces %u", counts[MSC_namespaces]);
19902+
dump ("Using-directives %u", counts[MSC_using_directives]);
1988619903
dump ("Macros %u", counts[MSC_macros]);
1988719904
dump ("Initializers %u", counts[MSC_inits]);
1988819905
}
@@ -20165,7 +20182,8 @@ ool_cmp (const void *a_, const void *b_)
2016520182
qualified-names : binding value(s)
2016620183
MOD_SNAME_PFX.README : human readable, strings
2016720184
MOD_SNAME_PFX.ENV : environment strings, strings
20168-
MOD_SNAME_PFX.nms : namespace hierarchy
20185+
MOD_SNAME_PFX.nms : namespace hierarchy
20186+
MOD_SNAME_PFX.udi : namespace using-directives
2016920187
MOD_SNAME_PFX.bnd : binding table
2017020188
MOD_SNAME_PFX.spc : specialization table
2017120189
MOD_SNAME_PFX.imp : import table
@@ -20411,6 +20429,11 @@ module_state::write_begin (elf_out *to, cpp_reader *reader,
2041120429
if (counts[MSC_namespaces])
2041220430
write_namespaces (to, spaces, counts[MSC_namespaces], &crc);
2041320431

20432+
/* Write any using-directives. */
20433+
if (counts[MSC_namespaces])
20434+
counts[MSC_using_directives]
20435+
= write_using_directives (to, table, spaces, &crc);
20436+
2041420437
/* Write the bindings themselves. */
2041520438
counts[MSC_bindings] = write_bindings (to, sccs, &crc);
2041620439

@@ -20662,11 +20685,16 @@ module_state::read_language (bool outermost)
2066220685
counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
2066320686
ok = false;
2066420687

20665-
/* Read the namespace hierarchy. */
20688+
/* Read the namespace hierarchy. */
2066620689
if (ok && counts[MSC_namespaces]
2066720690
&& !read_namespaces (counts[MSC_namespaces]))
2066820691
ok = false;
2066920692

20693+
/* Read any using-directives. */
20694+
if (ok && counts[MSC_using_directives]
20695+
&& !read_using_directives (counts[MSC_using_directives]))
20696+
ok = false;
20697+
2067020698
if (ok && !read_bindings (counts[MSC_bindings],
2067120699
counts[MSC_sec_lwm], counts[MSC_sec_hwm]))
2067220700
ok = false;
@@ -21753,15 +21781,15 @@ direct_import (module_state *import, cpp_reader *reader)
2175321781
if (!import->do_import (reader, true))
2175421782
gcc_unreachable ();
2175521783

21784+
(*modules)[0]->set_import (import, import->exported_p);
21785+
2175621786
if (import->loadedness < ML_LANGUAGE)
2175721787
{
2175821788
if (!keyed_table)
2175921789
keyed_table = new keyed_map_t (EXPERIMENT (1, 400));
2176021790
import->read_language (true);
2176121791
}
2176221792

21763-
(*modules)[0]->set_import (import, import->exported_p);
21764-
2176521793
dump.pop (n);
2176621794
timevar_stop (TV_MODULE_IMPORT);
2176721795
}

gcc/cp/name-lookup.cc

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8957,20 +8957,34 @@ pop_nested_namespace (tree ns)
89578957
static void
89588958
add_using_namespace (vec<tree, va_gc> *&usings, tree target)
89598959
{
8960+
/* Find if this using already exists. */
8961+
tree old = NULL_TREE;
89608962
if (usings)
8961-
for (unsigned ix = usings->length (); ix--;)
8962-
if ((*usings)[ix] == target)
8963-
return;
8963+
for (tree t : *usings)
8964+
if (USING_DECL_DECLS (t) == target)
8965+
{
8966+
old = t;
8967+
break;
8968+
}
89648969

8970+
tree decl = old;
8971+
if (!decl)
8972+
{
8973+
decl = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE);
8974+
USING_DECL_DECLS (decl) = target;
8975+
}
8976+
8977+
/* Update purviewness and exportedness in case that has changed. */
89658978
if (modules_p ())
89668979
{
8967-
tree u = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE);
8968-
USING_DECL_DECLS (u) = target;
8969-
DECL_MODULE_EXPORT_P (u) = module_exporting_p ();
8970-
DECL_MODULE_PURVIEW_P (u) = module_purview_p ();
8971-
target = u;
8980+
if (module_purview_p ())
8981+
DECL_MODULE_PURVIEW_P (decl) = true;
8982+
if (module_exporting_p ())
8983+
DECL_MODULE_EXPORT_P (decl) = true;
89728984
}
8973-
vec_safe_push (usings, target);
8985+
8986+
if (!old)
8987+
vec_safe_push (usings, decl);
89748988
}
89758989

89768990
/* Convenience overload for the above, taking the user as its first

gcc/cp/parser.cc

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16267,13 +16267,7 @@ cp_parser_import_declaration (cp_parser *parser, module_parse mp_state,
1626716267
" must not be from header inclusion");
1626816268
}
1626916269

16270-
auto mk = module_kind;
16271-
if (exporting)
16272-
module_kind |= MK_EXPORTING;
16273-
1627416270
import_module (mod, token->location, exporting, attrs, parse_in);
16275-
16276-
module_kind = mk;
1627716271
}
1627816272
}
1627916273

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
// { dg-additional-options -fmodules }
1+
// { dg-additional-options "-fmodules -fdump-lang-module" }
22

33
import M2;
44

55
A::AT var1;
66
AT var2; // { dg-error "AT" }
7+
8+
// { dg-final { scan-lang-dump {Ignoring using-directives because module M1 is not visible} module } }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// PR c++/121702
2+
// { dg-additional-options "-fmodules -fdump-lang-module" }
3+
// { dg-module-cmi a }
4+
5+
export module a;
6+
export namespace a {
7+
constexpr int f() { return 42; }
8+
}
9+
10+
namespace x {}
11+
namespace y {
12+
export using namespace x;
13+
}
14+
15+
// { dg-final { scan-lang-dump {Using-directives 1} module } }
16+
// { dg-final { scan-lang-dump {Writing using-directive in '::y' for '::x'} module } }

0 commit comments

Comments
 (0)