c++: inter-cluster import order [PR 99283]

I finally managed to reduce the testcase without hitting other bugs.
This problem is caused by discovering a duplicate in the middle of
reading in the entity in question.  I had thougt the import seeding at
the beginning of a cluster prevented that, but it is insufficient.
Specifically an earlier cluster in the same module can cause the
import of a duplicate.  Although clusters within a module are
well-ordered, there is no ordering between clusters of one module and
clusters of another module.  And thus we can get duplicate declaration
loops.  This prevents the problem by also seeding references to
earlier clusters in the same module.  As the FIXME notes, it is
sufficient to reference a single entity in any particular earlier
cluster, plus, we also could determine the implicit dependencies and
prune that seeding even further.  I do not do that -- it decrease the
loading that will happen, but would reduce the serialization size.  As
ever, let's get correctness first.

	PR c++/99283
	gcc/cp/
	* module.cc (trees_out::decl_node): Adjust importedness reference
	assert.
	(module_state::intercluster_seed): New.  Seed both imports and
	inter-cluster references.  Broken out of ...
	(module_state::write_cluster): ... here.  Call it.
	gcc/testsuite/
	* g++.dg/modules/pr99283-6.h: New.
	* g++.dg/modules/pr99283-6_a.H: New.
	* g++.dg/modules/pr99283-6_b.H: New.
	* g++.dg/modules/pr99283-6_c.C: New.
	* g++.dg/modules/hdr-init-1_c.C: Adjust scan.
	* g++.dg/modules/indirect-3_c.C: Adjust scan.
	* g++.dg/modules/indirect-4_c.C: Adjust scan.
	* g++.dg/modules/lambda-3_b.C: Adjust scan.
	* g++.dg/modules/late-ret-3_c.C: Adjust scan.
	* g++.dg/modules/pr99425-1_b.H: Adjust scan.
	* g++.dg/modules/pr99425-1_c.C: Adjust scan.
This commit is contained in:
Nathan Sidwell 2021-04-01 05:25:53 -07:00
parent 512429a885
commit 584731eced
12 changed files with 278 additions and 37 deletions

View file

@ -3570,6 +3570,7 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state {
unsigned, unsigned *crc_ptr);
bool read_namespaces (unsigned);
void intercluster_seed (trees_out &sec, unsigned index, depset *dep);
unsigned write_cluster (elf_out *to, depset *depsets[], unsigned size,
depset::hash &, unsigned *counts, unsigned *crc_ptr);
bool read_cluster (unsigned snum);
@ -8548,8 +8549,7 @@ trees_out::decl_node (tree decl, walk_kind ref)
gcc_checking_assert (index == ~import_entity_index (decl));
#if CHECKING_P
if (importedness)
gcc_assert (!import == (importedness < 0));
gcc_assert (!import || importedness >= 0);
#endif
i (tt_entity);
u (import);
@ -14419,7 +14419,33 @@ enum ct_bind_flags
cbf_wrapped = 0x8, /* ... that is wrapped. */
};
/* Write the cluster of depsets in SCC[0-SIZE). */
/* DEP belongs to a different cluster, seed it to prevent
unfortunately timed duplicate import. */
// FIXME: QOI For inter-cluster references we could just only pick
// one entity from an earlier cluster. Even better track
// dependencies between earlier clusters
void
module_state::intercluster_seed (trees_out &sec, unsigned index_hwm, depset *dep)
{
if (dep->is_import ()
|| dep->cluster < index_hwm)
{
tree ent = dep->get_entity ();
if (!TREE_VISITED (ent))
{
sec.tree_node (ent);
dump (dumper::CLUSTER)
&& dump ("Seeded %s %N",
dep->is_import () ? "import" : "intercluster", ent);
}
}
}
/* Write the cluster of depsets in SCC[0-SIZE).
dep->section -> section number
dep->cluster -> entity number
*/
unsigned
module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
@ -14431,6 +14457,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
trees_out sec (to, this, table, table.section);
sec.begin ();
unsigned index_lwm = counts[MSC_entities];
/* Determine entity numbers, mark for writing. */
dump (dumper::CLUSTER) && dump ("Cluster members:") && (dump.indent (), true);
@ -14484,10 +14511,10 @@ 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. */
/* Ensure every out-of-cluster decl is referenced before we start
streaming. We must do both imports *and* earlier clusters,
because the latter could reach into the former and cause a
duplicate loop. */
sec.set_importing (+1);
for (unsigned ix = 0; ix != size; ix++)
{
@ -14505,30 +14532,14 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
depset *bind = dep->deps[ix];
if (bind->get_entity_kind () == depset::EK_USING)
bind = bind->deps[1];
if (bind->is_import ())
{
tree import = bind->get_entity ();
if (!TREE_VISITED (import))
{
sec.tree_node (import);
dump (dumper::CLUSTER)
&& dump ("Seeded import %N", import);
}
}
intercluster_seed (sec, index_lwm, bind);
}
/* Also check the namespace itself. */
dep = dep->deps[0];
}
if (dep->is_import ())
{
tree import = dep->get_entity ();
if (!TREE_VISITED (import))
{
sec.tree_node (import);
dump (dumper::CLUSTER) && dump ("Seeded import %N", import);
}
}
intercluster_seed (sec, index_lwm, dep);
}
}
sec.tree_node (NULL_TREE);

View file

@ -17,8 +17,8 @@ int main ()
// { dg-final { scan-lang-dump-times {Reading 1 initializers} 2 module } }
// { dg-final { scan-lang-dump {Read:-1's named merge key \(new\) var_decl:'::var'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key \(new\) var_decl:'::var'} module } }
// { dg-final { scan-lang-dump-times {Reading definition var_decl '::var@[^\n]*/hdr-init-1_a.H:1'} 2 module } }
// { dg-final { scan-lang-dump {Read:-1's named merge key \(matched\) var_decl:'::var'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key \(matched\) var_decl:'::var'} module } }

View file

@ -19,6 +19,6 @@ int main ()
// { dg-final { scan-lang-dump-not {Instantiation:-[0-9]* function_decl:'::foo::X@foo:.::frob@.()<0x0>'} module } }
// { dg-final { scan-lang-dump {Lazily binding '::bar@bar:.::toto'@'bar' section:} module } }
// { dg-final { scan-lang-dump {>Loading entity foo\[1\] section:1} module } }
// { dg-final { scan-lang-dump {>Loading entity foo\[.\] section:1} module } }
// { dg-final { scan-lang-dump {Imported:-[0-9]* template_decl:'::foo@foo:.::template TPL@foo:.'@foo} module } }
// { dg-final { scan-lang-dump {Reading definition type_decl '::foo@foo:.::TPL@bar:.<0x0>'} module } }

View file

@ -10,7 +10,7 @@ int main ()
}
// { dg-final { scan-lang-dump {Lazily binding '::bar@bar:.::quux'@'bar' section:} module } }
// { dg-final { scan-lang-dump {>Loading entity foo\[2\] section:1} module } }
// { dg-final { scan-lang-dump {>Loading entity foo\[.\] section:1} module } }
// { dg-final { scan-lang-dump {Imported:-[0-9]* template_decl:'::foo@foo:.::template TPL@foo:.'@foo} module } }
// { dg-final { scan-lang-dump {Reading definition function_decl '::foo@foo:.::TPL@bar:.<0x1>::frob@bar:.<0x2>'} module } }

View file

@ -4,6 +4,6 @@
import "lambda-3_a.H";
// { dg-final { scan-lang-dump-not {merge key \(new\)} module } }
// { dg-final { scan-lang-dump {Read -1\[0\] matched attached decl '::template ._anon_0<#unnamed#>'} module } }
// { dg-final { scan-lang-dump {Read -1\[0\] matched attached decl '::._anon_2'} module } }
// { dg-final { scan-lang-dump {Read -1\[0\] matched attached decl '::._anon_1'} module } }
// { dg-final { scan-lang-dump {Read -[0-9]*\[0\] matched attached decl '::template ._anon_0<#unnamed#>'} module } }
// { dg-final { scan-lang-dump {Read -[0-9]*\[0\] matched attached decl '::._anon_2'} module } }
// { dg-final { scan-lang-dump {Read -[0-9]*\[0\] matched attached decl '::._anon_1'} module } }

View file

@ -19,4 +19,4 @@ int main ()
return 0;
}
// { dg-final { scan-lang-dump {Read:-1's named merge key \(matched\) template_decl:'::template Foo'\n Deduping '::template Foo@[^\n]*/late-ret-3_a.H:.'\n} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key \(matched\) template_decl:'::template Foo'\n Deduping '::template Foo@[^\n]*/late-ret-3_a.H:.'\n} module } }

View file

@ -0,0 +1,23 @@
template<bool, typename, typename>
struct conditional;
template<typename> struct incrementable_traits;
template<typename _Iter, typename _Tp>
struct __iter_traits_impl;
class __max_diff_type;
template<typename _Iter>
concept weakly_incrementable
= __is_same (__iter_traits_impl<_Iter, incrementable_traits<_Iter>>,
__max_diff_type);
template<typename _Iterator>
class reverse_iterator
{
public:
using iterator_concept
= typename conditional<weakly_incrementable<_Iterator>,
int, int>::type;
};

View file

@ -0,0 +1,33 @@
// { dg-additional-options {-std=c++2a -fmodule-header} }
// { dg-module-cmi {}
#include "pr99283-6.h"
template<typename _IteratorL, typename _IteratorR>
constexpr bool
operator<(const reverse_iterator<_IteratorL>& __x,
const reverse_iterator<_IteratorR>& __y);
template<typename _Tp>
struct numeric_limits;
class __max_size_type
{
public:
template<typename _Tp>
constexpr
__max_size_type(_Tp __i) noexcept
: _M_val(__i), _M_msb(__i < 0)
{ }
using __rep = __UINT64_TYPE__;
private:
__rep _M_val = 0;
unsigned _M_msb:1 = 0;
};
class __max_diff_type
{
private:
__max_size_type _M_rep = 0;
};

View file

@ -0,0 +1,164 @@
// { dg-additional-options {-std=c++2a -fmodule-header} }
// { dg-module-cmi {}
#include "pr99283-6.h"
template<typename _Tp>
void __addressof(_Tp& __r) ;
template<typename _Tp, _Tp __v>
struct integral_constant
{
static constexpr _Tp value = __v;
};
template<typename _Tp, _Tp __v>
constexpr _Tp integral_constant<_Tp, __v>::value;
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template<typename _B1, typename _B2>
struct __and_
: public conditional<_B1::value, _B2, _B1>::type
{ };
template<typename _From, typename _To>
struct is_convertible
: public true_type
{ };
template<bool, typename _Tp = void>
struct enable_if
{ };
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };
template<bool _Cond, typename _Tp = void>
using __enable_if_t = typename enable_if<_Cond, _Tp>::type;
template<typename A, typename B>
using _Require = __enable_if_t<__and_<A, B>::value>;
template<bool _Cond, typename _Iftrue, typename _Iffalse>
struct conditional
{ typedef _Iftrue type; };
template<typename> struct iterator_traits;
template<typename _IteratorL, typename _IteratorR>
constexpr bool
operator!=(const reverse_iterator<_IteratorL>& __x,
const reverse_iterator<_IteratorR>& __y);
typedef __INT64_TYPE__ int64_t;
typedef int64_t intmax_t;
template<intmax_t _Num>
struct ratio
{
};
namespace chrono
{
template<typename _Rep>
struct duration;
template<typename _ToDur, typename _Rep>
constexpr _ToDur
duration_cast(const duration<_Rep>& __d)
{
typedef typename _ToDur::rep __to_rep;
return _ToDur(static_cast<__to_rep>(static_cast<intmax_t>(__d.count())));
}
template<typename _Rep>
struct duration
{
public:
using rep = _Rep;
constexpr duration() = default;
duration(const duration&) = default;
template<typename _Rep2, typename
= _Require<is_convertible<const _Rep2&, rep>,
true_type>>
constexpr explicit duration(const _Rep2& __rep)
: __r (__rep) {}
~duration() = default;
duration& operator=(const duration&) = default;
rep count() const;
private:
rep __r;
};
using seconds = duration<int64_t>;
template<typename _Clock, typename _Dur>
struct time_point
{
typedef _Dur duration;
duration time_since_epoch() const;
private:
duration __d;
};
struct system_clock
{
typedef chrono::seconds duration;
typedef chrono::time_point<system_clock, duration> time_point;
static void
to_time_t(const time_point& __t) noexcept
{
duration_cast<chrono::seconds>
(__t.time_since_epoch()).count();
}
};
}
template<typename>
class allocator;
template<typename _ForwardIterator>
constexpr void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
{
for (; __first != __last; ++__first)
__addressof (*__first);
}
template<typename _Alloc>
struct allocator_traits
{
private:
template<typename _Alloc2, typename _Tp>
static constexpr void
_S_destroy(_Alloc2&, _Tp* __p, ...)
noexcept
{
__destroy(__p);
}
};
template<typename _Tp>
struct allocator_traits<allocator<_Tp>>
{
using value_type = _Tp;
using pointer = _Tp*;
};
import "pr99283-6_a.H";

View file

@ -0,0 +1,10 @@
// { dg-additional-options {-std=c++2a -fmodules-ts} }
import "pr99283-6_b.H";
template<typename _Alloc>
struct __allocated_ptr
{
using value_type = allocator_traits<_Alloc>;
};

View file

@ -15,5 +15,5 @@ inline void widget (Cont parm)
ssize (parm);
}
// { dg-final { scan-lang-dump {Read:-[0-9]*'s alias spec merge key \(new\) type_decl:'::make_signed_t'\n ... Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::make_signed'\n Read:-1's named merge key \(matched\) template_decl:'::template ssize'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s alias spec merge key \(new\) type_decl:'::make_signed_t'\n ... Read:-[0-9]*'s type spec merge key \(new\) type_decl:'::make_signed'\n Read:-[0-9]*'s named merge key \(matched\) template_decl:'::template ssize'} module } }

View file

@ -7,5 +7,5 @@ void frob (Cont parm)
ssize (parm);
}
// { dg-final { scan-lang-dump {Read:-1's named merge key \(new\) template_decl:'::template ssize'} module } }
// { dg-final { scan-lang-dump {Read:-1's named merge key \(matched\) template_decl:'::template ssize'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key \(new\) template_decl:'::template ssize'} module } }
// { dg-final { scan-lang-dump {Read:-[0-9]*'s named merge key \(matched\) template_decl:'::template ssize'} module } }