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:
parent
512429a885
commit
584731eced
12 changed files with 278 additions and 37 deletions
|
@ -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);
|
||||
|
|
|
@ -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 } }
|
||||
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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 } }
|
||||
|
|
|
@ -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 } }
|
||||
|
|
23
gcc/testsuite/g++.dg/modules/pr99283-6.h
Normal file
23
gcc/testsuite/g++.dg/modules/pr99283-6.h
Normal 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;
|
||||
};
|
33
gcc/testsuite/g++.dg/modules/pr99283-6_a.H
Normal file
33
gcc/testsuite/g++.dg/modules/pr99283-6_a.H
Normal 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;
|
||||
};
|
164
gcc/testsuite/g++.dg/modules/pr99283-6_b.H
Normal file
164
gcc/testsuite/g++.dg/modules/pr99283-6_b.H
Normal 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";
|
10
gcc/testsuite/g++.dg/modules/pr99283-6_c.C
Normal file
10
gcc/testsuite/g++.dg/modules/pr99283-6_c.C
Normal 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>;
|
||||
};
|
||||
|
|
@ -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 } }
|
||||
|
||||
|
|
|
@ -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 } }
|
||||
|
|
Loading…
Add table
Reference in a new issue