c++: lambda mangling alias issues [PR107897]

In 107897, by the time we are looking at the mangling clash, the
alias has already been removed from the symbol table by analyze_functions,
so we can't look at n->cpp_implicit_alias.  So just assume that it's an
alias if it's internal.

In 108887 the problem is that removing the mangling alias from the symbol
table confuses analyze_functions, because it ended up as first_analyzed
somehow, so it becomes a dangling pointer.  So instead we call reset()
to neutralize the alias.  To make this work for variables, I needed to move
reset() from cgraph_node to symtab_node.

	PR c++/107897
	PR c++/108887

gcc/ChangeLog:

	* cgraph.h: Move reset() from cgraph_node to symtab_node.
	* cgraphunit.cc (symtab_node::reset): Adjust.  Also call
	remove_from_same_comdat_group.

gcc/cp/ChangeLog:

	* decl2.cc (record_mangling): Use symtab_node::reset.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-lambda3.C: Use -flto if supported.
	* g++.dg/cpp0x/lambda/lambda-mangle7.C: New test.
This commit is contained in:
Jason Merrill 2023-03-06 15:33:45 -05:00
parent 04b0a7b1a6
commit a23b33a1bd
5 changed files with 108 additions and 25 deletions

View file

@ -152,6 +152,9 @@ public:
/* Remove symbol from symbol table. */
void remove (void);
/* Undo any definition or use of the symbol. */
void reset (void);
/* Dump symtab node to F. */
void dump (FILE *f);
@ -1066,14 +1069,6 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node
/* Expand function specified by node. */
void expand (void);
/* As an GCC extension we allow redefinition of the function. The
semantics when both copies of bodies differ is not well defined.
We replace the old body with new body so in unit at a time mode
we always use new body, while in normal mode we may end up with
old body inlined into some functions and new body expanded and
inlined in others. */
void reset (void);
/* Creates a wrapper from cgraph_node to TARGET node. Thunk is used for this
kind of wrapper method. */
void create_wrapper (cgraph_node *target);

View file

@ -378,21 +378,15 @@ symbol_table::process_new_functions (void)
inlined in others.
??? It may make more sense to use one body for inlining and other
body for expanding the function but this is difficult to do. */
body for expanding the function but this is difficult to do.
This is also used to cancel C++ mangling aliases, which can be for
functions or variables. */
void
cgraph_node::reset (void)
symtab_node::reset (void)
{
/* If process is set, then we have already begun whole-unit analysis.
This is *not* testing for whether we've already emitted the function.
That case can be sort-of legitimately seen with real function redefinition
errors. I would argue that the front end should never present us with
such a case, but don't enforce that for now. */
gcc_assert (!process);
/* Reset our data structures so we can analyze the function again. */
inlined_to = NULL;
memset (&rtl, 0, sizeof (rtl));
analyzed = false;
definition = false;
alias = false;
@ -400,8 +394,22 @@ cgraph_node::reset (void)
weakref = false;
cpp_implicit_alias = false;
remove_callees ();
remove_all_references ();
remove_from_same_comdat_group ();
if (cgraph_node *cn = dyn_cast <cgraph_node *> (this))
{
/* If process is set, then we have already begun whole-unit analysis.
This is *not* testing for whether we've already emitted the function.
That case can be sort-of legitimately seen with real function
redefinition errors. I would argue that the front end should never
present us with such a case, but don't enforce that for now. */
gcc_assert (!cn->process);
memset (&cn->rtl, 0, sizeof (cn->rtl));
cn->inlined_to = NULL;
cn->remove_callees ();
}
}
/* Return true when there are references to the node. INCLUDE_SELF is

View file

@ -4742,15 +4742,24 @@ record_mangling (tree decl, bool need_warning)
= mangled_decls->find_slot_with_hash (id, IDENTIFIER_HASH_VALUE (id),
INSERT);
/* If this is already an alias, remove the alias, because the real
/* If this is already an alias, cancel the alias, because the real
decl takes precedence. */
if (*slot && DECL_ARTIFICIAL (*slot) && DECL_IGNORED_P (*slot))
if (symtab_node *n = symtab_node::get (*slot))
if (n->cpp_implicit_alias)
{
if (symtab_node *n = symtab_node::get (*slot))
{
n->remove ();
*slot = NULL_TREE;
if (n->cpp_implicit_alias)
/* Actually removing the node isn't safe if other code is already
holding a pointer to it, so just neutralize it. */
n->reset ();
}
else
/* analyze_functions might have already removed the alias from the
symbol table if it's internal. */
gcc_checking_assert (!TREE_PUBLIC (*slot));
*slot = NULL_TREE;
}
if (!*slot)
*slot = decl;

View file

@ -0,0 +1,70 @@
// PR c++/108887
// { dg-do compile { target c++11 } }
template <int __v> struct integral_constant {
static constexpr int value = __v;
};
using false_type = integral_constant<false>;
template <bool, bool, typename...> struct __result_of_impl;
template <typename _Functor, typename... _ArgTypes>
struct __result_of_impl<false, false, _Functor, _ArgTypes...> {
typedef decltype(0) type;
};
template <typename... _ArgTypes>
struct __invoke_result
: __result_of_impl<false_type::value, false_type::value, _ArgTypes...> {};
template <typename, typename _Fn, typename... _Args>
void __invoke_impl(_Fn __f, _Args... __args) {
__f(__args...);
}
template <typename, typename _Callable, typename... _Args>
void __invoke_r(_Callable __fn, _Args... __args) {
using __result = __invoke_result<_Args...>;
using __type = typename __result::type;
__invoke_impl<__type>(__fn, __args...);
}
struct QString {
QString(const char *);
};
template <typename> class function;
template <typename _Functor> struct _Base_manager {
static _Functor _M_get_pointer(int) { __builtin_abort (); }
};
template <typename, typename> class _Function_handler;
template <typename _Res, typename _Functor, typename... _ArgTypes>
struct _Function_handler<_Res(_ArgTypes...), _Functor> {
using _Base = _Base_manager<_Functor>;
static _Res _M_invoke(const int &__functor, _ArgTypes &&...__args) {
auto __trans_tmp_1 = _Base::_M_get_pointer(__functor);
__invoke_r<_Res>(__trans_tmp_1, __args...);
}
};
template <typename _Res, typename... _ArgTypes>
struct function<_Res(_ArgTypes...)> {
template <typename _Functor>
using _Handler = _Function_handler<_Res(_ArgTypes...), _Functor>;
template <typename _Functor> function(_Functor) {
using _My_handler = _Handler<_Functor>;
_M_invoker = _My_handler::_M_invoke;
}
using _Invoker_type = _Res (*)(const int &, _ArgTypes &&...);
_Invoker_type _M_invoker;
};
struct QRegularExpression {
QRegularExpression(QString);
};
struct AbstractAccount {
void get(function<void(AbstractAccount *)>,
function<void(AbstractAccount *)>);
};
struct AbstractTimelineModel {
AbstractAccount m_account;
};
struct LinkPaginationTimelineModel : AbstractTimelineModel {
void fillTimeline();
};
void LinkPaginationTimelineModel::fillTimeline() {
[] {};
m_account.get([](AbstractAccount *) { static QRegularExpression re(""); },
[](AbstractAccount *) {});
}

View file

@ -1,6 +1,7 @@
// { dg-do run { target c++20 } }
// { dg-do compile { target c++17_down } }
// { dg-excess-errors "" { target { c++17_down } } } (PR108972)
// { dg-additional-options "-flto" { target lto } } (PR107897)
template<typename T>
concept C1 = __is_same_as(T, int)