c++/modules: Handle redefinitions of using-decls

This fixes an ICE exposed by supporting exported non-function
using-decls.  Sometimes when preparing to define a class, xref_tag will
find a using-decl belonging to a different namespace, which triggers the
checking_assert in modules handling.

Ideally I feel that 'lookup_and_check_tag' should be told whether we're
about to define the type and handle erroring on redefinitions itself to
avoid this issue (and provide better diagnostics by acknowledging the
using-declaration), but this is complicated with the current
fragmentation of definition checking.  So for this patch we just fixup
the assertion and ensure that pushdecl properly errors on the
conflicting declaration later.

gcc/cp/ChangeLog:

	* decl.cc (xref_tag): Move assertion into condition.
	* name-lookup.cc (check_module_override): Check for conflicting
	types and using-decls.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/using-19_a.C: New test.
	* g++.dg/modules/using-19_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
This commit is contained in:
Nathaniel Shead 2024-07-05 13:52:01 +10:00
parent d6bf4b1c93
commit 1f7a21c6e8
4 changed files with 54 additions and 12 deletions

View file

@ -16737,12 +16737,14 @@ xref_tag (enum tag_types tag_code, tree name,
if (CLASS_TYPE_P (t) && CLASSTYPE_IS_TEMPLATE (t))
maybe_tmpl = CLASSTYPE_TI_TEMPLATE (t);
/* FIXME: we should do a more precise check for redefinitions
of a conflicting using-declaration here, as these diagnostics
are not ideal. */
if (DECL_LANG_SPECIFIC (decl)
&& DECL_MODULE_IMPORT_P (decl)
&& TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
&& CP_DECL_CONTEXT (decl) == current_namespace)
{
/* Push it into this TU's symbol slot. */
gcc_checking_assert (current_namespace == CP_DECL_CONTEXT (decl));
if (maybe_tmpl != decl)
/* We're in the template parm binding level.
Pushtag has logic to slide under that, but we're

View file

@ -3782,18 +3782,30 @@ check_module_override (tree decl, tree mvec, bool hiding,
/* Errors could cause there to be nothing. */
continue;
tree type = NULL_TREE;
if (STAT_HACK_P (bind))
/* We do not have to check STAT_TYPE here, the xref_tag
machinery deals with that problem. */
bind = STAT_VISIBLE (bind);
{
/* If there was a matching STAT_TYPE here then xref_tag
should have found it, but we need to check anyway because
a conflicting using-declaration may exist. */
if (STAT_TYPE_VISIBLE_P (bind))
type = STAT_TYPE (bind);
bind = STAT_VISIBLE (bind);
}
for (ovl_iterator iter (bind); iter; ++iter)
if (!iter.using_p ())
{
match = duplicate_decls (decl, *iter, hiding);
if (match)
goto matched;
}
if (type)
{
match = duplicate_decls (decl, strip_using_decl (type), hiding);
if (match)
goto matched;
}
for (ovl_iterator iter (strip_using_decl (bind)); iter; ++iter)
{
match = duplicate_decls (decl, *iter, hiding);
if (match)
goto matched;
}
}
if (TREE_PUBLIC (scope) && TREE_PUBLIC (STRIP_TEMPLATE (decl))

View file

@ -0,0 +1,18 @@
// { dg-additional-options "-fmodules-ts -Wno-global-module" }
// { dg-module-cmi M }
module;
namespace hidden {
struct S {};
enum E { e };
void f();
}
export module M;
export namespace exposed {
using hidden::S;
using hidden::E;
using hidden::e;
using hidden::f;
}

View file

@ -0,0 +1,10 @@
// { dg-additional-options "-fmodules-ts" }
import M;
namespace exposed {
struct S {}; // { dg-error "redefinition" }
enum E { x }; // { dg-error "multiple definition" }
int e(); // { dg-error "redeclared" }
int f; // { dg-error "redeclared" }
}