c++: Redesign pending entity handling [PR 99170]

This patch addresses 99170.  with modules (and in particular header
units), one module can provide a (maybe nested) class or template and
another module can provide a definition or (maybe partial)
specialization of said entity, or member thereof.  when both are
imported into a 3rd TU, and that TU instantiates or uses the class, it
needs to stream in those entities (in general).  But how does it key
those entities to the original?  It can't /just/ use the entity index,
because, when header-units and/or partitions are in play, the entity
index /is not unique/.  I had two complicated schemes that tried to
unify that, but it failed.  Here's a simpler scheme.  Such pending
entities are keyed to the namespace and identifier of the
namespace-scope entity that contains them.  Thus the final TU needs to
find that entity and look in a hash table for lists of sections that
need loading just before instantiating a template or looking inside a
class.

I would like to make this more efficient, but given the complex scheme
failed, I'm shooting for correctness right now.  There will be a
follow up patch to complete the cleanup this enables.

	PR c++/99170
	gcc/cp/
	* cp-tree.h
	* lex.c (cxx_dup_lang_specific_decl): Adjust for module_attached_p
	rename.
	* module.cc (class pending_key): New.
	(default_hash_traits<pending_key>): New specialization.
	(pending_map_t): New typedef.
	(pending_table): Replace old table.
	(trees_out::lang_decl_bools): Adjust.
	(trees_in::lang_decl_bools): Adjust.
	(trees_in::install_entity): Drop pending member and specialization
	handling.
	(find_pending_key): New.
	(depset:#️⃣:fiund_dependencies): Use it.
	(pendset_lazy_load): Delete.
	(module_state::write_cluster): Don't count pendings here.  Bye
	Duff's device-like thing.
	(module_state::write_pendings): Reimplement.
	(module_state::read_pendings): Reimplement.
	(lazy_specializations_p): Delete.
	(module_state::write): Adjust write_pendings call.
	(lazy_load_pendings): New.
	(lazy_load_specializations): Delete.
	(lazy_load_members): Delete.
	(init_modules):	Adjust.
	* name-lookup.c (maybe_lazily_declare):	Call lazy_load_pendings
	not lazy_load_members.
	(note_pending_specializations): Delete.
	(load_pending_specializations): Delete.
	* name-lookup.h	(BINDING_VECTR_PENDING_SPECIALIZATIONS_P): Delete.
	(BINDING_VECTOR_PENDING_MEMBERS_P): Delete.
	(BINDING_VECTR_PENDING_MEMBERS_P): Delete.
	(note_pending_specializations): Delete.
	(load_pending_specializations): Delete.
	* pt.c (lookup_template_class_1): Call lazy_load_pendings not
	lazy_load_specializations.
	(instantiate_template_class_1): Likewise.
	(instantiate_decl): Call lazy_load_pendings.
	* typeck.c (complete_type): Likewise.
	gcc/testsuite/
	* g++.dg/modules/pr99170-1_a.H: New.
	* g++.dg/modules/pr99170-1_b.C: New.
	* g++.dg/modules/pr99170-2.h: New.
	* g++.dg/modules/pr99170-2_a.C: New.
	* g++.dg/modules/pr99170-2_b.C: New.
	* g++.dg/modules/pr99170-3_a.H: New.
	* g++.dg/modules/pr99170-3_b.C: New.
	* g++.dg/modules/inst-2_b.C: Adjust scan.
	* g++.dg/modules/inst-4_a.C: Adjust scan.
	* g++.dg/modules/inst-4_b.C: Adjust scan.
	* g++.dg/modules/member-def-1_b.C: Adjust scan.
	* g++.dg/modules/member-def-1_c.C: Adjust scan.
	* g++.dg/modules/tpl-spec-1_a.C: Adjust scan.
	* g++.dg/modules/tpl-spec-1_b.C: Adjust scan.
	* g++.dg/modules/tpl-spec-2_b.C: Adjust scan.
	* g++.dg/modules/tpl-spec-2_c.C: Adjust scan.
	* g++.dg/modules/tpl-spec-2_d.C: Adjust scan.
	* g++.dg/modules/tpl-spec-3_a.C: Adjust scan.
	* g++.dg/modules/tpl-spec-3_b.C: Adjust scan.
	* g++.dg/modules/tpl-spec-4_a.C: Adjust scan.
	* g++.dg/modules/tpl-spec-4_b.C: Adjust scan.
	* g++.dg/modules/tpl-spec-5_a.C: Adjust scan.
	* g++.dg/modules/tpl-spec-5_b.C: Adjust scan.
This commit is contained in:
Nathan Sidwell 2021-03-03 10:09:41 -08:00
parent 4c955b4ad3
commit c778a237c1
30 changed files with 483 additions and 445 deletions

View File

@ -1678,21 +1678,10 @@ check_constraint_info (tree t)
#define DECL_MODULE_ENTITY_P(NODE) \
(DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_entity_p)
/* True if there are unloaded specializations keyed to this template. */
#define DECL_MODULE_PENDING_SPECIALIZATIONS_P(NODE) \
(DECL_LANG_SPECIFIC (TEMPLATE_DECL_CHECK (NODE)) \
->u.base.module_pending_p)
/* True if this class has unloaded members. These should be loaded
before we do member lookups. */
#define DECL_MODULE_PENDING_MEMBERS_P(NODE) \
(DECL_LANG_SPECIFIC (TYPE_DECL_CHECK (NODE)) \
->u.base.module_pending_p)
/* DECL that has attached decls for ODR-relatedness. */
#define DECL_MODULE_ATTACHMENTS_P(NODE) \
(DECL_LANG_SPECIFIC (TREE_CHECK2(NODE,FUNCTION_DECL,VAR_DECL))\
->u.base.module_pending_p)
->u.base.module_attached_p)
/* Whether this is an exported DECL. Held on any decl that can appear
at namespace scope (function, var, type, template, const or
@ -2771,10 +2760,8 @@ struct GTY(()) lang_decl_base {
unsigned module_import_p : 1; /* from an import */
unsigned module_entity_p : 1; /* is in the entitity ary &
hash. */
/* TEMPLATE_DECL has specializations or,
TYPE_DECL has class members yet to load, or
VAR_DECL or FUNCTION_DECL has attached decls. */
unsigned module_pending_p : 1;
/* VAR_DECL or FUNCTION_DECL has attached decls. */
unsigned module_attached_p : 1;
/* 12 spare bits. */
};
@ -7025,9 +7012,7 @@ extern void mangle_module (int m, bool include_partition);
extern void mangle_module_fini ();
extern void lazy_load_binding (unsigned mod, tree ns, tree id,
binding_slot *bslot);
extern void lazy_load_specializations (tree tmpl);
extern void lazy_load_members (tree decl);
extern bool lazy_specializations_p (unsigned, bool, bool);
extern void lazy_load_pendings (tree decl);
extern module_state *preprocess_module (module_state *, location_t,
bool in_purview,
bool is_import, bool export_p,

View File

@ -1010,7 +1010,7 @@ cxx_dup_lang_specific_decl (tree node)
(module_purview_p still does). */
ld->u.base.module_entity_p = false;
ld->u.base.module_import_p = false;
ld->u.base.module_pending_p = false;
ld->u.base.module_attached_p = false;
if (GATHER_STATISTICS)
{

View File

@ -53,16 +53,13 @@ along with GCC; see the file COPYING3. If not see
the third indicates whether it was an import into this TU or not.
The more detailed flags are DECL_MODULE_PARTITION_P,
DECL_MODULE_ENTITY_P & DECL_MODULE_PENDING_SPECIALIZATIONS_P. The
first is set in a primary interface unit on decls that were read
from module partitions (these will have DECL_MODULE_IMPORT_P set
too). Such decls will be streamed out to the primary's CMI.
DECL_MODULE_ENTITY_P is set when an entity is imported, even if it
matched a non-imported entity. Such a decl will not have
DECL_MODULE_IMPORT_P set, even though it has an entry in the entity
map and array. DECL_MODULE_PENDING_SPECIALIZATIONS_P is set on a
primary template, and indicates there are specializations that
should be streamed in before trying to specialize this template.
DECL_MODULE_ENTITY_P. The first is set in a primary interface unit
on decls that were read from module partitions (these will have
DECL_MODULE_IMPORT_P set too). Such decls will be streamed out to
the primary's CMI. DECL_MODULE_ENTITY_P is set when an entity is
imported, even if it matched a non-imported entity. Such a decl
will not have DECL_MODULE_IMPORT_P set, even though it has an entry
in the entity map and array.
Header units are module-like.
@ -2644,6 +2641,58 @@ depset *depset::make_entity (tree entity, entity_kind ek, bool is_defn)
return r;
}
class pending_key
{
public:
tree ns;
tree id;
};
template<>
struct default_hash_traits<pending_key>
{
using value_type = pending_key;
static const bool empty_zero_p = false;
static hashval_t hash (const value_type &k)
{
hashval_t h = IDENTIFIER_HASH_VALUE (k.id);
h = iterative_hash_hashval_t (DECL_UID (k.ns), h);
return h;
}
static bool equal (const value_type &k, const value_type &l)
{
return k.ns == l.ns && k.id == l.id;
}
static void mark_empty (value_type &k)
{
k.ns = k.id = NULL_TREE;
}
static void mark_deleted (value_type &k)
{
k.ns = NULL_TREE;
gcc_checking_assert (k.id);
}
static bool is_empty (const value_type &k)
{
return k.ns == NULL_TREE && k.id == NULL_TREE;
}
static bool is_deleted (const value_type &k)
{
return k.ns == NULL_TREE && k.id != NULL_TREE;
}
static void remove (value_type &)
{
}
};
typedef hash_map<pending_key, auto_vec<unsigned>> pending_map_t;
/* Not-loaded entities that are keyed to a namespace-scope
identifier. See module_state::write_pendings for details. */
pending_map_t *pending_table;
/* Decls that need some post processing once a batch of lazy loads has
completed. */
vec<tree, va_heap, vl_embed> *post_load_decls;
@ -2804,20 +2853,6 @@ uintset<T>::hash::get (typename uintset<T>::hash::key_t key, bool extract)
return res;
}
/* Entities keyed to some other entity. When we load the other
entity, we mark it in some way to indicate there are further
entities to load when you start looking inside it. For instance
template specializations are keyed to their most general template.
When we instantiate that, we need to know all the partial
specializations (to pick the right template), and all the known
specializations (to avoid reinstantiating it, and/or whether it's
extern). The values split into two ranges. If !MSB set, indices
into the entity array. If MSB set, an indirection to another
pendset. */
typedef uintset<unsigned> pendset;
static pendset::hash *pending_table;
/* Some entities are attached to another entitity for ODR purposes.
For example, at namespace scope, 'inline auto var = []{};', that
lambda is attached to 'var', and follows its ODRness. */
@ -3709,8 +3744,8 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state {
bool read_inits (unsigned count);
private:
void write_pendings (elf_out *to, vec<depset *> depsets,
depset::hash &, unsigned count, unsigned *crc_ptr);
unsigned write_pendings (elf_out *to, vec<depset *> depsets,
depset::hash &, unsigned *crc_ptr);
bool read_pendings (unsigned count);
private:
@ -5644,7 +5679,7 @@ trees_out::lang_decl_bools (tree t)
WB (lang->u.base.dependent_init_p);
WB (lang->u.base.module_purview_p);
if (VAR_OR_FUNCTION_DECL_P (t))
WB (lang->u.base.module_pending_p);
WB (lang->u.base.module_attached_p);
switch (lang->u.base.selector)
{
default:
@ -5714,7 +5749,7 @@ trees_in::lang_decl_bools (tree t)
RB (lang->u.base.dependent_init_p);
RB (lang->u.base.module_purview_p);
if (VAR_OR_FUNCTION_DECL_P (t))
RB (lang->u.base.module_pending_p);
RB (lang->u.base.module_attached_p);
switch (lang->u.base.selector)
{
default:
@ -7572,12 +7607,7 @@ trees_in::install_entity (tree decl)
/* Insert the real decl into the entity ary. */
unsigned ident = state->entity_lwm + entity_index - 1;
binding_slot &elt = (*entity_ary)[ident];
/* See module_state::read_pendings for how this got set. */
int pending = elt.get_lazy () & 3;
elt = decl;
(*entity_ary)[ident] = decl;
/* And into the entity map, if it's not already there. */
if (!DECL_LANG_SPECIFIC (decl)
@ -7592,26 +7622,6 @@ trees_in::install_entity (tree decl)
gcc_checking_assert (!existed);
slot = ident;
}
else if (pending != 0)
{
unsigned key_ident = import_entity_index (decl);
if (pending & 1)
if (!pending_table->add (key_ident, ~ident))
pending &= ~1;
if (pending & 2)
if (!pending_table->add (~key_ident, ~ident))
pending &= ~2;
}
if (pending & 1)
DECL_MODULE_PENDING_SPECIALIZATIONS_P (decl) = true;
if (pending & 2)
{
DECL_MODULE_PENDING_MEMBERS_P (decl) = true;
gcc_checking_assert (TREE_CODE (decl) != TEMPLATE_DECL);
}
return true;
}
@ -10558,7 +10568,6 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
// FIXME: What if the return type is a voldemort?
key.ret = fndecl_declared_return_type (inner);
}
break;
case MK_field:
@ -13225,6 +13234,28 @@ depset::hash::add_mergeable (depset *mergeable)
dep->deps.safe_push (mergeable);
}
/* Find the innermost-namespace scope of DECL, and that
namespace-scope decl. */
tree
find_pending_key (tree decl, tree *decl_p = nullptr)
{
tree ns = decl;
do
{
decl = ns;
ns = CP_DECL_CONTEXT (ns);
if (TYPE_P (ns))
ns = TYPE_NAME (ns);
}
while (TREE_CODE (ns) != NAMESPACE_DECL);
if (decl_p)
*decl_p = decl;
return ns;
}
/* Iteratively find dependencies. During the walk we may find more
entries on the same binding that need walking. */
@ -13259,13 +13290,22 @@ depset::hash::find_dependencies (module_state *module)
walker.tree_node (OVL_FUNCTION (decl));
else if (TREE_VISITED (decl))
/* A global tree. */;
else if (TREE_CODE (decl) == NAMESPACE_DECL
&& !DECL_NAMESPACE_ALIAS (decl))
else if (item->get_entity_kind () == EK_NAMESPACE)
add_namespace_context (current, CP_DECL_CONTEXT (decl));
else
{
walker.mark_declaration (decl, current->has_defn ());
if (!walker.is_key_order ()
&& (item->get_entity_kind () == EK_SPECIALIZATION
|| item->get_entity_kind () == EK_PARTIAL
|| (item->get_entity_kind () == EK_DECL
&& item->is_member ())))
{
tree ns = find_pending_key (decl, nullptr);
add_namespace_context (item, ns);
}
// FIXME: Perhaps p1815 makes this redundant? Or at
// least simplifies it. Voldemort types are only
// ever emissable when containing (inline) function
@ -13709,43 +13749,6 @@ depset::hash::connect ()
return connector.result;
}
/* Load the entities referred to by this pendset. */
static bool
pendset_lazy_load (pendset *pendings, bool specializations_p)
{
bool ok = true;
for (unsigned ix = 0; ok && ix != pendings->num; ix++)
{
unsigned index = pendings->values[ix];
if (index & ~(~0u >> 1))
{
/* An indirection. */
if (specializations_p)
index = ~index;
pendset *other = pending_table->get (index, true);
if (!pendset_lazy_load (other, specializations_p))
ok = false;
}
else
{
module_state *module = import_entity_module (index);
binding_slot *slot = &(*entity_ary)[index];
if (!slot->is_lazy ())
dump () && dump ("Specialiation %M[%u] already loaded",
module, index - module->entity_lwm);
else if (!module->lazy_load (index - module->entity_lwm, slot))
ok = false;
}
}
/* We own set, so delete it now. */
delete pendings;
return ok;
}
/* Initialize location spans. */
void
@ -14665,12 +14668,8 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
break;
case depset::EK_DECL:
if (b->is_member ())
{
case depset::EK_SPECIALIZATION: /* Yowzer! */
case depset::EK_PARTIAL: /* Hey, let's do it again! */
counts[MSC_pendings]++;
}
case depset::EK_SPECIALIZATION:
case depset::EK_PARTIAL:
b->cluster = counts[MSC_entities]++;
sec.mark_declaration (b->get_entity (), b->has_defn ());
/* FALLTHROUGH */
@ -14688,10 +14687,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
dump (dumper::CLUSTER) && (dump.outdent (), true);
/* Ensure every imported decl is referenced before we start
streaming. This ensures that we never encounter the
situation where this cluster instantiates some implicit
member that importing some other decl causes to be
instantiated. */
streaming. This ensures that we never encounter the situation
where this cluster instantiates some implicit member that
importing some other decl causes to be instantiated. */
sec.set_importing (+1);
for (unsigned ix = 0; ix != size; ix++)
{
@ -14704,14 +14702,14 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
if (dep->is_binding ())
{
/* A cross-module using decl could be here. */
for (unsigned ix = dep->deps.length (); --ix;)
{
depset *bind = dep->deps[ix];
if (bind->get_entity_kind () == depset::EK_USING
&& bind->deps[1]->is_import ())
if (bind->get_entity_kind () == depset::EK_USING)
bind = bind->deps[1];
if (bind->is_import ())
{
tree import = bind->deps[1]->get_entity ();
tree import = bind->get_entity ();
if (!TREE_VISITED (import))
{
sec.tree_node (import);
@ -15397,98 +15395,208 @@ module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm)
/* Write the pending table to MOD_SNAME_PFX.pnd
Specializations & partials are keyed to their primary template.
Members are keyed to their context.
The pending table holds information about clusters that need to be
loaded because they contain information about something that is not
found by namespace-scope lookup.
For specializations & partials, primary templates are keyed to the
(namespace name) of their originating decl (because that's the only
handle we have). */
The three cases are:
void
(a) Template (maybe-partial) specializations that we have
instantiated or defined. When an importer needs to instantiate
that template, they /must have/ the partial, explicit & extern
specializations available. If they have the other specializations
available, they'll have less work to do. Thus, when we're about to
instantiate FOO, we have to be able to ask 'are there any
specialization of FOO in our imports?'.
(b) (Maybe-implicit) member functions definitions. A class could
be defined in one header, and an inline member defined in a
different header (this occurs in the STL). Similarly, like the
specialization case, an implicit member function could have been
'instantiated' in one module, and it'd be nice to not have to
reinstantiate it in another.
(c) A member classes completed elsewhere. A member class could be
declared in one header and defined in another. We need to know to
load the class definition before looking in it. This turns out to
be a specific case of #b, so we can treat these the same. But it
does highlight an issue -- there could be an intermediate import
between the outermost containing namespace-scope class and the
innermost being-defined member class. This is actually possible
with all of these cases, so be aware -- we're not just talking of
one level of import to get to the innermost namespace.
This gets complicated fast, it took me multiple attempts to even
get something remotely working. Partially because I focussed on
optimizing what I think turns out to be a smaller problem, given
the known need to do the more general case *anyway*. I document
the smaller problem, because it does appear to be the natural way
to do it. It's trap!
**** THE TRAP
Let's refer to the primary template or the containing class as the
KEY. And the specialization or member as the PENDING-ENTITY. (To
avoid having to say those mouthfuls all the time.)
In either case, we have an entity and we need some way of mapping
that to a set of entities that need to be loaded before we can
proceed with whatever processing of the entity we were going to do.
We need to link the key to the pending-entity in some way. Given a
key, tell me the pending-entities I need to have loaded. However
we tie the key to the pending-entity must not rely on the key being
loaded -- that'd defeat the lazy loading scheme.
As the key will be an import in we know its entity number (either
because we imported it, or we're writing it out too). Thus we can
generate a map of key-indices to pending-entities. The
pending-entity indices will be into our span of the entity table,
and thus allow them to be lazily loaded. The key index will be
into another slot of the entity table. Notice that this checking
could be expensive, we don't want to iterate over a bunch of
pending-entity indices (across multiple imports), every time we're
about do to the thing with the key. We need to quickly determine
'definitely nothing needed'.
That's almost good enough, except that key indices are not unique
in a couple of cases :( Specifically the Global Module or a module
partition can result in multiple modules assigning an entity index
for the key. The decl-merging on loading will detect that so we
only have one Key loaded, and in the entity hash it'll indicate the
entity index of first load. Which might be different to how we
know it. Notice this is restricted to GM entities or this-module
entities. Foreign imports cannot have this.
We can simply resolve this in the direction of how this module
referred to the key to how the importer knows it. Look in the
entity table slot that we nominate, maybe lazy load it, and then
lookup the resultant entity in the entity hash to learn how the
importer knows it.
But we need to go in the other direction :( Given the key, find all
the index-aliases of that key. We can partially solve that by
adding an alias hash table. Whenever we load a merged decl, add or
augment a mapping from the entity (or its entity-index) to the
newly-discovered index. Then when we look for pending entities of
a key, we also iterate over this aliases this mapping provides.
But that requires the alias to be loaded. And that's not
necessarily true.
*** THE SIMPLER WAY
The remaining fixed thing we have is the innermost namespace
containing the ultimate namespace-scope container of the key and
the name of that container (which might be the key itself). I.e. a
namespace-decl/identifier/module tuple. Let's call this the
top-key. We'll discover that the module is not important here,
because of cross-module possibilities mentioned in case #c above.
We can't markup namespace-binding slots. The best we can do is
mark the binding vector with 'there's something here', and have
another map from namespace/identifier pairs to a vector of pending
entity indices.
Maintain a pending-entity map. This is keyed by top-key, and
maps to a vector of pending-entity indices. On the binding vector
have flags saying whether the pending-name-entity map has contents.
(We might want to further extend the key to be GM-vs-Partition and
specialization-vs-member, but let's not get ahead of ourselves.)
For every key-like entity, find the outermost namespace-scope
name. Use that to lookup in the pending-entity map and then make
sure the specified entities are loaded.
An optimization might be to have a flag in each key-entity saying
that it's top key might be in the entity table. It's not clear to
me how to set that flag cheaply -- cheaper than just looking.
FIXME: It'd be nice to have a bit in decls to tell us whether to
even try this. We can have a 'already done' flag, that we set when
we've done KLASS's lazy pendings. When we import a module that
registers pendings on the same top-key as KLASS we need to clear
the flag. A recursive walk of the top-key clearing the bit will
suffice. Plus we only need to recurse on classes that have the bit
set. (That means we need to set the bit on parents of KLASS here,
don't forget.) However, first: correctness, second: efficiency. */
unsigned
module_state::write_pendings (elf_out *to, vec<depset *> depsets,
depset::hash &table,
unsigned count, unsigned *crc_p)
depset::hash &table, unsigned *crc_p)
{
dump () && dump ("Writing %u pendings", count);
dump () && dump ("Writing pending-entities");
dump.indent ();
trees_out sec (to, this, table);
sec.begin ();
unsigned count = 0;
tree cache_ns = NULL_TREE;
tree cache_id = NULL_TREE;
unsigned cache_section = ~0;
for (unsigned ix = 0; ix < depsets.length (); ix++)
{
depset *d = depsets[ix];
depset::entity_kind kind = d->get_entity_kind ();
tree key = NULL_TREE;
bool is_spec = false;
if (kind == depset::EK_SPECIALIZATION)
if (d->is_binding ())
continue;
if (d->is_import ())
continue;
if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION
|| d->get_entity_kind () == depset::EK_PARTIAL
|| (d->get_entity_kind () == depset::EK_DECL && d->is_member ())))
continue;
tree key_decl = nullptr;
tree key_ns = find_pending_key (d->get_entity (), &key_decl);
tree key_name = DECL_NAME (key_decl);
if (IDENTIFIER_ANON_P (key_name))
{
is_spec = true;
key = reinterpret_cast <spec_entry *> (d->deps[0])->tmpl;
}
else if (kind == depset::EK_PARTIAL)
{
is_spec = true;
key = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (d->get_entity ()));
}
else if (kind == depset::EK_DECL && d->is_member ())
{
tree ctx = DECL_CONTEXT (d->get_entity ());
key = TYPE_NAME (ctx);
if (tree ti = CLASSTYPE_TEMPLATE_INFO (ctx))
if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == key)
key = TI_TEMPLATE (ti);
}
// FIXME:OPTIMIZATION More than likely when there is one pending
// member, there will be others. All written in the same
// section and keyed to the same class. We only need to record
// one of them. The same is not true for specializations
if (key)
{
gcc_checking_assert (!d->is_import ());
{
/* Key the entity to its key. */
depset *key_dep = table.find_dependency (key);
if (key_dep->get_entity_kind () == depset::EK_REDIRECT)
key_dep = key_dep->deps[0];
unsigned key_origin
= key_dep->is_import () ? key_dep->section : 0;
sec.u (key_origin);
sec.u (key_dep->cluster);
sec.u (d->cluster);
dump () && dump ("%s %N entity:%u keyed to %M[%u] %N",
is_spec ? "Specialization" : "Member",
d->get_entity (),
d->cluster, (*modules)[key_origin],
key_dep->cluster, key);
}
if (is_spec)
{
/* Key the general template to the originating decl. */
tree origin = get_originating_module_decl (key);
sec.tree_node (CP_DECL_CONTEXT (origin));
sec.tree_node (DECL_NAME (origin));
unsigned origin_ident = import_entity_index (origin);
module_state *origin_from = this;
if (!(origin_ident & ~(~0u>>1)))
origin_from = import_entity_module (origin_ident);
sec.u (origin_from->remap);
}
gcc_checking_assert (IDENTIFIER_LAMBDA_P (key_name));
if (tree attached = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (key_decl)))
key_name = DECL_NAME (attached);
else
sec.tree_node (NULL);
count--;
{
/* There's nothing to attach it to. Must
always reinstantiate. */
dump ()
&& dump ("Unattached lambda %N[%u] section:%u",
d->get_entity_kind () == depset::EK_DECL
? "Member" : "Specialization", d->get_entity (),
d->cluster, d->section);
continue;
}
}
char const *also = "";
if (d->section == cache_section
&& key_ns == cache_ns
&& key_name == cache_id)
/* Same section & key as previous, no need to repeat ourselves. */
also = "also ";
else
{
cache_ns = key_ns;
cache_id = key_name;
cache_section = d->section;
gcc_checking_assert (table.find_dependency (cache_ns));
sec.tree_node (cache_ns);
sec.tree_node (cache_id);
sec.u (d->cluster);
count++;
}
dump () && dump ("Pending %s %N entity:%u section:%u %skeyed to %P",
d->get_entity_kind () == depset::EK_DECL
? "member" : "specialization", d->get_entity (),
d->cluster, cache_section, also, cache_ns, cache_id);
}
gcc_assert (!count);
sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p);
dump.outdent ();
return count;
}
bool
@ -15504,72 +15612,28 @@ module_state::read_pendings (unsigned count)
for (unsigned ix = 0; ix != count; ix++)
{
unsigned key_origin = slurp->remap_module (sec.u ());
unsigned key_index = sec.u ();
unsigned ent_index = sec.u ();
module_state *from = (*modules)[key_origin];
tree ns = sec.tree_node ();
pending_key key;
unsigned index;
if (!key_origin
|| key_index >= from->entity_num || ent_index >= entity_num
|| (ns && TREE_CODE (ns) != NAMESPACE_DECL))
key.ns = sec.tree_node ();
key.id = sec.tree_node ();
index = sec.u ();
if (!key.ns || !key.id
|| !(TREE_CODE (key.ns) == NAMESPACE_DECL
&& !DECL_NAMESPACE_ALIAS (key.ns))
|| !identifier_p (key.id)
|| index >= entity_num)
sec.set_overrun ();
if (sec.get_overrun ())
break;
bool loaded = false;
dump () && dump ("%s keyed to %M[%u] entity:%u",
ns ? "Specialization" : "Member",
from, key_index, ent_index);
unsigned key_ident = from->entity_lwm + key_index;
if (pending_table->add (ns ? key_ident : ~key_ident,
ent_index + entity_lwm))
{
binding_slot &slot = (*entity_ary)[key_ident];
if (slot.is_lazy ())
slot.or_lazy (ns ? 1 : 2);
else
{
tree key = slot;
dump () && dump ("Pending:%u keyed to %P", index, key.ns, key.id);
loaded = true;
if (ns)
{
if (key && TREE_CODE (key) == TEMPLATE_DECL)
DECL_MODULE_PENDING_SPECIALIZATIONS_P (key) = true;
else
sec.set_overrun ();
}
else
{
if (key && TREE_CODE (key) == TYPE_DECL)
DECL_MODULE_PENDING_MEMBERS_P (key) = true;
else
sec.set_overrun ();
}
}
}
if (ns)
{
/* We also need to mark the namespace binding of the
originating template, so we know to set its pending
specializations flag, when we load it. */
tree name = sec.tree_node ();
unsigned origin = slurp->remap_module (sec.u ());
if (!origin || !name || TREE_CODE (name) != IDENTIFIER_NODE)
sec.set_overrun ();
if (sec.get_overrun ())
break;
module_state *origin_from = (*modules)[origin];
if (!loaded
&& (origin_from->is_header ()
|| (origin_from->is_partition ()
|| origin_from->is_module ())))
note_pending_specializations (ns, name, origin_from->is_header ());
}
index += entity_lwm;
auto &vec = pending_table->get_or_insert (key);
vec.safe_push (index);
}
dump.outdent ();
@ -15578,23 +15642,6 @@ module_state::read_pendings (unsigned count)
return true;
}
/* Return true if module MOD cares about lazy specializations keyed to
possibly duplicated entity bindings. */
bool
lazy_specializations_p (unsigned mod, bool header_p, bool partition_p)
{
module_state *module = (*modules)[mod];
if (module->is_header ())
return header_p;
if (module->is_module () || module->is_partition ())
return partition_p;
return false;
}
/* Read & write locations. */
enum loc_kind {
LK_ORDINARY,
@ -17873,6 +17920,8 @@ module_state::write (elf_out *to, cpp_reader *reader)
}
}
/* depset::cluster - entity number (on entities)
depset::section - cluster number */
/* We'd better have written as many sections and found as many
namespaces as we predicted. */
gcc_assert (counts[MSC_sec_hwm] == to->get_section_limit ()
@ -17892,8 +17941,7 @@ module_state::write (elf_out *to, cpp_reader *reader)
counts[MSC_bindings] = write_bindings (to, sccs, &crc);
/* Write the unnamed. */
if (counts[MSC_pendings])
write_pendings (to, sccs, table, counts[MSC_pendings], &crc);
counts[MSC_pendings] = write_pendings (to, sccs, table, &crc);
/* Write the import table. */
if (config.num_imports > 1)
@ -18928,14 +18976,19 @@ lazy_load_binding (unsigned mod, tree ns, tree id, binding_slot *mslot)
module->get_flatname ());
}
/* Load any pending specializations of TMPL. Called just before
instantiating TMPL. */
/* Load any pending entities keyed to the top-key of DECL. */
void
lazy_load_specializations (tree tmpl)
lazy_load_pendings (tree decl)
{
gcc_checking_assert (DECL_MODULE_PENDING_SPECIALIZATIONS_P (tmpl)
&& DECL_MODULE_ENTITY_P (tmpl));
tree key_decl;
pending_key key;
key.ns = find_pending_key (decl, &key_decl);
key.id = DECL_NAME (key_decl);
auto *pending_vec = pending_table ? pending_table->get (key) : nullptr;
if (!pending_vec)
return;
int count = errorcount + warningcount;
@ -18943,21 +18996,32 @@ lazy_load_specializations (tree tmpl)
bool ok = !recursive_lazy ();
if (ok)
{
unsigned ident = import_entity_index (tmpl);
if (pendset *set = pending_table->get (ident, true))
function_depth++; /* Prevent GC */
unsigned n = dump.push (NULL);
dump () && dump ("Reading %u pending entities keyed to %P",
pending_vec->length (), key.ns, key.id);
for (unsigned ix = pending_vec->length (); ix--;)
{
function_depth++; /* Prevent GC */
unsigned n = dump.push (NULL);
dump ()
&& dump ("Reading %u pending specializations keyed to %M[%u] %N",
set->num, import_entity_module (ident),
ident - import_entity_module (ident)->entity_lwm, tmpl);
if (!pendset_lazy_load (set, true))
ok = false;
dump.pop (n);
unsigned index = (*pending_vec)[ix];
binding_slot *slot = &(*entity_ary)[index];
function_depth--;
if (slot->is_lazy ())
{
module_state *import = import_entity_module (index);
if (!import->lazy_load (index - import->entity_lwm, slot))
ok = false;
}
else if (dump ())
{
module_state *import = import_entity_module (index);
dump () && dump ("Entity %M[%u] already loaded",
import, index - import->entity_lwm);
}
}
pending_table->remove (key);
dump.pop (n);
function_depth--;
lazy_snum = 0;
post_load_processing ();
}
@ -18965,47 +19029,12 @@ lazy_load_specializations (tree tmpl)
timevar_stop (TV_MODULE_IMPORT);
if (!ok)
fatal_error (input_location, "failed to load specializations keyed to %qD",
tmpl);
fatal_error (input_location, "failed to load pendings for %<%E%s%E%>",
key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
if (count != errorcount + warningcount)
inform (input_location,
"during load of specializations keyed to %qD", tmpl);
}
void
lazy_load_members (tree decl)
{
gcc_checking_assert (DECL_MODULE_PENDING_MEMBERS_P (decl));
if (!DECL_MODULE_ENTITY_P (decl))
{
// FIXME: I can't help feeling that DECL_TEMPLATE_RESULT should
// be inserted into the entity map, or perhaps have the same
// DECL_UID as the template, so I don't have to do this dance
// here and elsewhere. It also simplifies when DECL is a
// partial specialization. (also noted elsewhere as an issue)
tree ti = CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl));
tree tmpl = TI_TEMPLATE (ti);
gcc_checking_assert (DECL_TEMPLATE_RESULT (tmpl) == decl);
decl = tmpl;
}
timevar_start (TV_MODULE_IMPORT);
unsigned ident = import_entity_index (decl);
if (pendset *set = pending_table->get (~ident, true))
{
function_depth++; /* Prevent GC */
unsigned n = dump.push (NULL);
dump () && dump ("Reading %u pending members keyed to %M[%u] %N",
set->num, import_entity_module (ident),
ident - import_entity_module (ident)->entity_lwm, decl);
pendset_lazy_load (set, false);
post_load_processing ();
dump.pop (n);
function_depth--;
}
timevar_stop (TV_MODULE_IMPORT);
inform (input_location, "during load of pendings for %<%E%s%E%>",
key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
}
static void
@ -19532,7 +19561,7 @@ preprocess_module (module_state *module, location_t from_loc,
{
unsigned n = dump.push (NULL);
dump () && dump ("Reading %s preprocessor state", module);
dump () && dump ("Reading %M preprocessor state", module);
name_pending_imports (reader, false);
/* Preserve the state of the line-map. */
@ -19873,8 +19902,7 @@ init_modules (cpp_reader *reader)
if (!flag_preprocess_only)
{
pending_table = new pendset::hash (EXPERIMENT (1, 400));
pending_table = new pending_map_t (EXPERIMENT (1, 400));
entity_map = new entity_map_t (EXPERIMENT (1, 400));
vec_safe_reserve (entity_ary, EXPERIMENT (1, 400));
}

View File

@ -1916,10 +1916,10 @@ get_class_binding_direct (tree klass, tree name, bool want_type)
static void
maybe_lazily_declare (tree klass, tree name)
{
tree main_decl = TYPE_NAME (TYPE_MAIN_VARIANT (klass));
if (DECL_LANG_SPECIFIC (main_decl)
&& DECL_MODULE_PENDING_MEMBERS_P (main_decl))
lazy_load_members (main_decl);
/* See big comment anout module_state::write_pendings regarding adding a check
bit. */
if (modules_p ())
lazy_load_pendings (TYPE_NAME (klass));
/* Lazily declare functions, if we're going to search these. */
if (IDENTIFIER_CTOR_P (name))
@ -4100,57 +4100,6 @@ set_module_binding (tree ns, tree name, unsigned mod, int mod_glob,
return true;
}
void
note_pending_specializations (tree ns, tree name, bool is_header)
{
if (tree *slot = find_namespace_slot (ns, name, false))
if (TREE_CODE (*slot) == BINDING_VECTOR)
{
tree vec = *slot;
BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = true;
if (is_header)
BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = true;
else
BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = true;
}
}
void
load_pending_specializations (tree ns, tree name)
{
tree *slot = find_namespace_slot (ns, name, false);
if (!slot || TREE_CODE (*slot) != BINDING_VECTOR
|| !BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (*slot))
return;
tree vec = *slot;
BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = false;
bool do_header = BINDING_VECTOR_PENDING_IS_HEADER_P (vec);
bool do_partition = BINDING_VECTOR_PENDING_IS_PARTITION_P (vec);
BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = false;
BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = false;
gcc_checking_assert (do_header | do_partition);
binding_cluster *cluster = BINDING_VECTOR_CLUSTER_BASE (vec);
unsigned ix = BINDING_VECTOR_NUM_CLUSTERS (vec);
if (BINDING_VECTOR_SLOTS_PER_CLUSTER == BINDING_SLOTS_FIXED)
{
ix--;
cluster++;
}
for (; ix--; cluster++)
for (unsigned jx = 0; jx != BINDING_VECTOR_SLOTS_PER_CLUSTER; jx++)
if (cluster->indices[jx].span
&& cluster->slots[jx].is_lazy ()
&& lazy_specializations_p (cluster->indices[jx].base,
do_header, do_partition))
lazy_load_binding (cluster->indices[jx].base, ns, name,
&cluster->slots[jx]);
}
void
add_module_namespace_decl (tree ns, tree decl)
{

View File

@ -177,17 +177,6 @@ struct GTY(()) tree_binding_vec {
#define MODULE_BINDING_PARTITION_P(NODE) \
(OVERLOAD_CHECK (NODE)->base.volatile_flag)
/* There are specializations of a template keyed to this binding. */
#define BINDING_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
(BINDING_VECTOR_CHECK (NODE)->base.public_flag)
/* The key is in a header unit (not a named module partition or
primary). */
#define BINDING_VECTOR_PENDING_IS_HEADER_P(NODE) \
(BINDING_VECTOR_CHECK (NODE)->base.protected_flag)
/* The key is in a named module (primary or partition). */
#define BINDING_VECTOR_PENDING_IS_PARTITION_P(NODE) \
(BINDING_VECTOR_CHECK (NODE)->base.private_flag)
extern void set_identifier_type_value (tree, tree);
extern void push_binding (tree, tree, cp_binding_level*);
extern void pop_local_binding (tree, tree);
@ -507,8 +496,6 @@ extern unsigned walk_module_binding (tree binding, bitmap partitions,
extern tree add_imported_namespace (tree ctx, tree name, location_t,
unsigned module,
bool inline_p, bool visible_p);
extern void note_pending_specializations (tree ns, tree name, bool is_header);
extern void load_pending_specializations (tree ns, tree name);
extern const char *get_cxx_dialect_name (enum cxx_dialect dialect);
#endif /* GCC_CP_NAME_LOOKUP_H */

View File

@ -9796,13 +9796,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
gen_tmpl = most_general_template (templ);
if (modules_p ())
{
tree origin = get_originating_module_decl (gen_tmpl);
load_pending_specializations (CP_DECL_CONTEXT (origin),
DECL_NAME (origin));
if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
lazy_load_specializations (gen_tmpl);
}
lazy_load_pendings (gen_tmpl);
parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
parm_depth = TMPL_PARMS_DEPTH (parmlist);
@ -21001,6 +20995,9 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
if (modules_p ())
lazy_load_pendings (tmpl);
/* If this function is a clone, handle it specially. */
if (DECL_CLONED_FUNCTION_P (tmpl))
{
@ -21037,15 +21034,6 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
(DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl)),
targ_ptr));
if (modules_p ())
{
tree origin = get_originating_module_decl (gen_tmpl);
load_pending_specializations (CP_DECL_CONTEXT (origin),
DECL_NAME (origin));
if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
lazy_load_specializations (gen_tmpl);
}
/* It would be nice to avoid hashing here and then again in tsubst_decl,
but it doesn't seem to be on the hot path. */
spec = retrieve_specialization (gen_tmpl, targ_ptr, 0);
@ -25946,6 +25934,10 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
gcc_checking_assert (!DECL_FUNCTION_SCOPE_P (d));
if (modules_p ())
/* We may have a pending instantiation of D itself. */
lazy_load_pendings (d);
/* Variables are never deferred; if instantiation is required, they
are instantiated right away. That allows for better code in the
case that an expression refers to the value of the variable --

View File

@ -133,8 +133,15 @@ complete_type (tree type)
TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = has_nontrivial_dtor;
}
}
else if (CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type))
instantiate_class_template (TYPE_MAIN_VARIANT (type));
else if (CLASS_TYPE_P (type))
{
if (modules_p ())
/* TYPE could be a class member we've not loaded the definition of. */
lazy_load_pendings (TYPE_NAME (TYPE_MAIN_VARIANT (type)));
if (CLASSTYPE_TEMPLATE_INSTANTIATION (type))
instantiate_class_template (TYPE_MAIN_VARIANT (type));
}
return type;
}

View File

@ -1,4 +1,4 @@
// { dg-additional-options {-fmodules-ts -fdump-lang-module-uid-alias} }
// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias} }
import foo;
int main ()
@ -9,6 +9,6 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s decl spec merge key \(new\) function_decl:'::foo'} module } }

View File

@ -1,5 +1,5 @@
// { dg-module-do run }
// { dg-additional-options {-fmodules-ts -fdump-lang-module-graph-blocks-alias} }
// { dg-additional-options {-fmodules-ts -fdump-lang-module-graph} }
export module foo;
// { dg-module-cmi foo }
@ -16,5 +16,5 @@ export inline int user (int i)
return x.m;
}
// { dg-final { scan-lang-dump {Specialization '::TPL<int>' entity:. keyed to foo\[.\] '::template TPL'} module } }
// { dg-final { scan-lang-dump {Specialization '::TPL<int>::TPL<int>' entity:. keyed to foo\[.\] '::template TPL<T>::template TPL'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::TPL<int>' entity:. section:. keyed to '::TPL'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::TPL<int>::TPL<int>' entity:. section:. also keyed to '::TPL'} module } }

View File

@ -1,4 +1,4 @@
// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias-uid} }
// { dg-additional-options {-fmodules-ts -fdump-lang-module-alias} }
import foo;
int main ()
@ -9,5 +9,5 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to foo\[.\] '::template TPL@foo:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::TPL'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::TPL'} module } }

View File

@ -11,4 +11,4 @@ struct frob::inner
};
// { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1::inner'\n \[1\]=decl declaration '::frob@foo:part1:1::inner::inner'\n} module } }
// { dg-final { scan-lang-dump {Member '::frob@foo:part1:1::inner' entity:0 keyed to foo:part1\[0\] '::frob@foo:part1:1'} module } }
// { dg-final { scan-lang-dump {Pending member '::frob@foo:part1:1::inner' entity:0 section:. keyed to '::frob'} module } }

View File

@ -11,6 +11,6 @@ export auto foo ()
return frob::inner ();
}
// { dg-final { scan-lang-dump {Reading 1 pending members keyed to foo:part1\[0\] '::frob@foo:part1:1'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } }
// { dg-final { scan-lang-dump { Cluster members:\n \[0\]=decl definition '::frob@foo:part1:1'\n \[1\]=decl definition '::frob@foo:part1:1::inner@foo:part1:1'\n \[2\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__dt '\n( \[.\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::__ct '\n)* \[6\]=decl declaration '::frob@foo:part1:1::inner@foo:part1:1::inner@foo:part2:2'\n \[7\]=decl declaration '::frob@foo:part1:1::frob@foo:part1:1'\n \[8\]=decl declaration '::frob@foo:part1:1::__as_base @foo:part1:1'\n \[9\]=binding '::frob'\n} module } }
// { dg-final { scan-lang-dump {Pendings 0} module } }

View File

@ -0,0 +1,10 @@
// PR 99170, pending instantiation snafu
// { dg-additional-options {-fmodule-header} }
// { dg-module-cmi {} }
namespace STD {
class string {
public:
template <typename T>
string (const T *);
};
}

View File

@ -0,0 +1,7 @@
// { dg-additional-options {-fmodules-ts} }
export module test;
// { dg-module-cmi test }
import "pr99170-1_a.H";
export class A {
STD::string str{"ayyy"};
};

View File

@ -0,0 +1,44 @@
namespace std
{
typedef long unsigned int size_t;
}
namespace __gnu_cxx
{
template<typename _CharT>
struct char_traits
{
typedef _CharT char_type;
static constexpr std::size_t
length(const char_type* __s);
};
template<typename _CharT>
constexpr std::size_t
char_traits<_CharT>::
length(const char_type* __p)
{
std::size_t __i = 0;
return __i;
}
}
namespace std
{
template<class _CharT>
struct char_traits;
template<>
struct char_traits<char>
{
typedef char char_type;
static constexpr size_t
length(const char_type* __s)
{
return __gnu_cxx::char_traits<char_type>::length(__s);
}
};
}

View File

@ -0,0 +1,9 @@
// pr99170 pending instantiations
// { dg-additional-options -fmodules-ts }
module;
#include "pr99170-2.h"
export module hello;
// { dg-module-cmi hello }
export void greeter (__gnu_cxx::char_traits<char> const &name);

View File

@ -0,0 +1,4 @@
// { dg-additional-options -fmodules-ts }
#include "pr99170-2.h"
import hello;

View File

@ -0,0 +1,11 @@
// PR 99170
// { dg-module-do link }
// { dg-additional-options -fmodule-header }
// { dg-module-cmi {} }
struct Foo
{
Foo () {};
};
static Foo __ioinit;

View File

@ -0,0 +1,6 @@
// { dg-additional-options -fmodules-ts }
import "pr99170-3_a.H";
int main ()
{
}

View File

@ -17,6 +17,6 @@ template <> int foo<int> (int y)
// { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::foo<int>'} module } }
// { dg-final { scan-lang-dump-not {Depending definition function_decl:'::foo<int>'} module } }
// { dg-final { scan-lang-dump {Cluster members:\n \[0\]=specialization declaration '::foo<int>'} module } }
// { dg-final { scan-lang-dump {Specialization '::foo<int>' entity:[0-9]* keyed to TPL\[0\] '::template foo'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::foo<int>' entity:[0-9]* section:. keyed to '::foo'} module } }
// { dg-final { scan-assembler {_Z3fooIiEiT_:} } }

View File

@ -14,7 +14,6 @@ int main ()
}
// { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
// { dg-final { scan-lang-dump {Specialization keyed to TPL\[0\] entity:1} module } }
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:1'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
// { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }

View File

@ -13,6 +13,6 @@ template <> int foo<int> (int y)
// { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::foo<int>'} module } }
// { dg-final { scan-lang-dump-not {Depending definition function_decl:'::foo<int>'} module } }
// { dg-final { scan-lang-dump {Cluster members:\n \[0\]=specialization declaration '::foo<int>'} module } }
// { dg-final { scan-lang-dump {Specialization '::foo<int>' entity:[0-9]* keyed to TPL\[.\] '::template foo@TPL:.'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::foo<int>' entity:[0-9]* section:. keyed to '::foo'} module } }
// { dg-final { scan-assembler {_Z3fooIiEiT_:} } }

View File

@ -13,7 +13,7 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
// { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
// { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }

View File

@ -20,7 +20,7 @@ int two ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template foo@TPL:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::foo'} module } }
// { dg-final { scan-lang-dump-not {Reading definition function_decl '::foo@TPL:.<int>'} module } }
// { dg-final { scan-assembler-not {_Z3fooIiEiT_:} } }

View File

@ -22,6 +22,6 @@ template <> void frob::store (int i_)
// { dg-final { scan-lang-dump {Dependencies of specialization function_decl:'::frob::store<int>'} module } }
// { dg-final { scan-lang-dump-not {Depending definition function_decl:'::frob::store<int>'} module } }
// { dg-final { scan-lang-dump {Cluster members:\n \[0\]=specialization declaration '::frob::store<int>'} module } }
// { dg-final { scan-lang-dump {Specialization '::frob::store<int>' entity:[0-9]* keyed to TPL\[1\] '::frob::template store'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::frob::store<int>' entity:[0-9]* section:. keyed to '::frob'} module } }
// { dg-final { scan-assembler {_ZN4frob5storeIiEEvT_:} } }

View File

@ -17,7 +17,7 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[1\] '::frob@TPL:.::template store@TPL:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::frob'} module } }
// { dg-final { scan-lang-dump-not {Reading definition function_decl '::frob@TPL:.::store@TPL:.<int>'} module } }
// { dg-final { scan-assembler-not {_ZN4frob5storeIiEEvT_:} } }

View File

@ -16,4 +16,4 @@ template<> struct X<int>
// { dg-final { scan-lang-dump {Dependencies of specialization type_decl:'::X<int>'} module } }
// { dg-final { scan-lang-dump {Cluster members:\n( \[.\]=[^\n]*'\n)* \[.\]=specialization definition '::X<int>'} module } }
// { dg-final { scan-lang-dump {Specialization '::X<int>' entity:[0-9]* keyed to TPL\[0\] '::template X'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::X<int>' entity:[0-9]* section:. keyed to '::X'} module } }

View File

@ -14,4 +14,4 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template X@TPL:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } }

View File

@ -16,4 +16,4 @@ template<typename T> struct X<T,1>
// { dg-final { scan-lang-dump {Dependency on partial template_decl:'::template X<T,0x1>' found} module } }
// { dg-final { scan-lang-dump {Cluster members:\n( \[.\][^\n]*'\n)* \[.\]=partial definition '::template X<T,0x1>'} module } }
// { dg-final { scan-lang-dump {Specialization '::template X<T,0x1>' entity:[0-9]* keyed to TPL\[0\] '::template X'} module } }
// { dg-final { scan-lang-dump {Pending specialization '::template X<T,0x1>' entity:[0-9]* section:. keyed to '::X'} module } }

View File

@ -14,4 +14,4 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Reading 1 pending specializations keyed to TPL\[0\] '::template X@TPL:.'} module } }
// { dg-final { scan-lang-dump {Reading 1 pending entities keyed to '::X'} module } }