https://gcc.gnu.org/g:13920d004da35b536d6ef07b0a32c0d3acc4c889

commit r17-4959-g13920d004da35b536d6ef07b0a32c0d3acc4c889
Author: Jason Merrill <[email protected]>
Date:   Wed Oct 7 10:03:47 2026 -0400

    c++/modules: P1811: relaxing redefinition restrictions [PR99000]
    
    P1811 says it's OK to redefine an imported global module entity in a
    different translation unit.  The simplest (and fastest) way to handle that
    is skipping the tokens of the redundant definition.  At some later point we
    might want to optionally parse the new definition and compare it to the
    imported one for ODR checking, but it isn't necessary for supporting
    well-formed code.  This does allow wildly ill-formed code in the
    redefinitions for the sake of speed, on the assumption that the the
    definitions satisfy the ODR, probably because they are from the same source
    code.
    
    The core addition is redefinable_import_p; various places that have errored
    about redefinition now discard the redefinition if it is true.
    
    For convenience, I'm also adding -fno-module-include-translate to use to
    force textual inclusion in compile-std1.C.
    
    For redefinition of classes, I've gone back and forth between trying to
    merge reachable non-exports in lookup_elaborated_type (as with the
    __class_type_info hack removed by this patch) or waiting until
    check_module_override.  I eventually settled on the latter approach for
    consistency, but it does mean that we need to check for a previous
    definition after lookup and again after push.
    
    This patch wrongly allows multiple redefinitions after an import, as in
    p1811-5.  I expect that fixing this should mostly mean clarifying what
    DECL_MODULE_IMPORT_P means for global module entities, but additional
    machinery would be needed for default arguments.  And it doesn't seem
    necessary to fix in this patch, given that I'm deliberately allowing other
    ill-formed redefinitions; if a shared header has multiple definitions, we
    will diagnose it when building the imported module.
    
    Assisted-by: GPT-5.6 Sol (p1811* testcases)
    
            P1811
            PR c++/99000
    
    gcc/c-family/ChangeLog:
    
            * c.opt (fmodule-include-translate): New option.
    
    gcc/cp/ChangeLog:
    
            * module.cc (maybe_translate_include): Check
            -fmodule-include-translate.
            * name-lookup.cc (redefinable_import_p): New.
            (pushdecl): Check DECL_IS_UNDECLARED_BUILTIN instead of hidden_p.
            (lookup_elaborated_type): Don't special-case cxxabi classes.
            (pushtag): Handle pushdecl finding a previous definition.
            * decl.cc (merge_default_template_args): Check redefinable_import_p.
            (duplicate_decls, grokfndecl, start_enum): Likewise.
            (redeclaration_error_message): Likewise.
            * parser.cc (cp_parser_skip_to_end_of_balanced_tokens): New.
            (cp_parser_skip_function_definition): New.
            (cp_parser_skip_base_clause): New.
            (cp_parser_enum_specifier): Skip redundant definition.
            (cp_parser_class_specifier): Likewise.
            (cp_parser_class_head): Likewise.
            (cp_parser_function_definition_from_specifiers_and_declarator):
            Likewise.
            * pt.cc (redeclare_class_template): Adjust.
            * cp-tree.h: Adjust.
            * name-lookup.h: Adjust.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/compile-std1.C: Also test #include after import.
            * g++.dg/modules/hello-1_b.C: Likewise.
            * g++.dg/modules/default-arg-4_b.C: Remove xfails.
            * g++.dg/modules/pr122053_b.C: Likewise.
            * g++.dg/modules/gmf-3.h: New test.
            * g++.dg/modules/gmf-3_a.C: New test.
            * g++.dg/modules/gmf-3_b.C: New test.
            * g++.dg/modules/gmf-4_a.C: New test.
            * g++.dg/modules/gmf-4_b.C: New test.

Diff:
---
 gcc/c-family/c.opt                             |   4 +
 gcc/cp/cp-tree.h                               |   2 +-
 gcc/cp/name-lookup.h                           |   1 +
 gcc/testsuite/g++.dg/modules/gmf-3.h           |   2 +
 gcc/testsuite/g++.dg/modules/p1811-1.h         |   9 ++
 gcc/testsuite/g++.dg/modules/p1811-2.h         |   5 +
 gcc/testsuite/g++.dg/modules/p1811-3.h         |   1 +
 gcc/testsuite/g++.dg/modules/p1811-5.h         |   1 +
 gcc/testsuite/g++.dg/modules/p1811-6.h         |   2 +
 gcc/cp/decl.cc                                 | 116 ++++++++++++++++------
 gcc/cp/module.cc                               |  33 ++++++-
 gcc/cp/name-lookup.cc                          |  28 ++----
 gcc/cp/parser.cc                               | 128 ++++++++++++++++++++++---
 gcc/cp/pt.cc                                   |   2 +-
 gcc/testsuite/g++.dg/modules/compile-std1.C    |   9 +-
 gcc/testsuite/g++.dg/modules/default-arg-4_b.C |   9 +-
 gcc/testsuite/g++.dg/modules/gmf-3_a.C         |  14 +++
 gcc/testsuite/g++.dg/modules/gmf-3_b.C         |   7 ++
 gcc/testsuite/g++.dg/modules/gmf-4_a.C         |  13 +++
 gcc/testsuite/g++.dg/modules/gmf-4_b.C         |  12 +++
 gcc/testsuite/g++.dg/modules/hello-1_b.C       |   4 +-
 gcc/testsuite/g++.dg/modules/p1811-1_a.C       |  11 +++
 gcc/testsuite/g++.dg/modules/p1811-1_b.C       |   8 ++
 gcc/testsuite/g++.dg/modules/p1811-2_a.C       |  11 +++
 gcc/testsuite/g++.dg/modules/p1811-2_b.C       |   9 ++
 gcc/testsuite/g++.dg/modules/p1811-3_a.C       |  11 +++
 gcc/testsuite/g++.dg/modules/p1811-3_b.C       |   8 ++
 gcc/testsuite/g++.dg/modules/p1811-4_a.C       |   8 ++
 gcc/testsuite/g++.dg/modules/p1811-4_b.C       |   7 ++
 gcc/testsuite/g++.dg/modules/p1811-5_a.C       |  11 +++
 gcc/testsuite/g++.dg/modules/p1811-5_b.C       |   7 ++
 gcc/testsuite/g++.dg/modules/p1811-6_a.C       |  12 +++
 gcc/testsuite/g++.dg/modules/p1811-6_b.C       |   9 ++
 gcc/testsuite/g++.dg/modules/pr122053_b.C      |   4 +-
 34 files changed, 445 insertions(+), 73 deletions(-)

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 3ad96a5fe783..d5b973baa855 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -2153,6 +2153,10 @@ fmodule-implicit-inline
 C++ ObjC++ Var(flag_module_implicit_inline,0) Integer
 Member functions defined within their class are inline in module purview.
 
+fmodule-include-translate
+C++ ObjC++ Var(flag_module_include_translate) Init(1) Integer
+Implicitly translate #include to import when possible.
+
 fmodule-only
 C++ ObjC RejectNegative Var(flag_module_only) Integer
 Only emit Compiled Module Interface.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 947bf62ff982..7b461e4f5281 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7605,7 +7605,7 @@ extern bool fns_correspond                        (tree, 
tree);
 extern int decls_match                         (tree, tree, bool = true);
 extern bool maybe_version_functions            (tree, tree);
 extern bool validate_constexpr_redeclaration   (tree, tree);
-extern bool merge_default_template_args                (tree, tree, bool);
+extern bool merge_default_template_args                (tree, tree, tree);
 extern void merge_decl_arguments               (tree, tree, bool, bool, bool);
 extern tree duplicate_decls                    (tree, tree,
                                                 bool hiding = false,
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 3737ae0ebac1..48b4a363cf53 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -509,6 +509,7 @@ extern void finish_nonmember_using_decl (tree scope, tree 
name,
                                         bool typename_p = false);
 extern void finish_using_directive (tree target, tree attribs);
 void push_local_extern_decl_alias (tree decl);
+extern bool redefinable_import_p (tree decl);
 extern tree pushdecl (tree, bool hiding = false);
 extern tree pushdecl_outermost_localscope (tree);
 extern tree pushdecl_top_level (tree);
diff --git a/gcc/testsuite/g++.dg/modules/gmf-3.h 
b/gcc/testsuite/g++.dg/modules/gmf-3.h
new file mode 100644
index 000000000000..fc83c3d3fb5d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/gmf-3.h
@@ -0,0 +1,2 @@
+struct A { };
+template <class T> struct B { };
diff --git a/gcc/testsuite/g++.dg/modules/p1811-1.h 
b/gcc/testsuite/g++.dg/modules/p1811-1.h
new file mode 100644
index 000000000000..f29b80a747dc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-1.h
@@ -0,0 +1,9 @@
+inline int
+f () try
+{
+  return 1;
+}
+catch (...)
+{
+  return 2;
+}
diff --git a/gcc/testsuite/g++.dg/modules/p1811-2.h 
b/gcc/testsuite/g++.dg/modules/p1811-2.h
new file mode 100644
index 000000000000..433344d3b178
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-2.h
@@ -0,0 +1,5 @@
+struct C {
+  enum E : int;
+};
+
+enum C::E : int { e };
diff --git a/gcc/testsuite/g++.dg/modules/p1811-3.h 
b/gcc/testsuite/g++.dg/modules/p1811-3.h
new file mode 100644
index 000000000000..8ae11367b646
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-3.h
@@ -0,0 +1 @@
+struct A { };
diff --git a/gcc/testsuite/g++.dg/modules/p1811-5.h 
b/gcc/testsuite/g++.dg/modules/p1811-5.h
new file mode 100644
index 000000000000..8ae11367b646
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-5.h
@@ -0,0 +1 @@
+struct A { };
diff --git a/gcc/testsuite/g++.dg/modules/p1811-6.h 
b/gcc/testsuite/g++.dg/modules/p1811-6.h
new file mode 100644
index 000000000000..1b99bed4babd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-6.h
@@ -0,0 +1,2 @@
+template <class T> struct A { };
+template <class T> struct B { };
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a14e9a4cb719..fe210c48cfe5 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -1754,12 +1754,13 @@ duplicate_function_template_decls (tree newdecl, tree 
olddecl)
 /* OLD_PARMS is the innermost set of template parameters for some template
    declaration, and NEW_PARMS is the corresponding set of template parameters
    for a redeclaration of that template.  Merge the default arguments within
-   these two sets of parameters.  CLASS_P is true iff the template in
-   question is a class template.  */
+   these two sets of parameters.  OLDDECL is the origin of OLD_PARMS.  */
 
 bool
-merge_default_template_args (tree new_parms, tree old_parms, bool class_p)
+merge_default_template_args (tree new_parms, tree old_parms, tree olddecl)
 {
+  bool class_p = DECL_IMPLICIT_TYPEDEF_P (STRIP_TEMPLATE (olddecl));
+
   gcc_checking_assert (TREE_VEC_LENGTH (new_parms)
                       == TREE_VEC_LENGTH (old_parms));
   for (int i = 0; i < TREE_VEC_LENGTH (new_parms); i++)
@@ -1770,7 +1771,9 @@ merge_default_template_args (tree new_parms, tree 
old_parms, bool class_p)
       tree& old_default = TREE_PURPOSE (TREE_VEC_ELT (old_parms, i));
       if (error_operand_p (new_parm) || error_operand_p (old_parm))
        return false;
-      if (new_default != NULL_TREE && old_default != NULL_TREE)
+      if (new_default != NULL_TREE && old_default != NULL_TREE
+         && !(redefinable_import_p (olddecl)
+              && cp_tree_equal (new_default, old_default)))
        {
          auto_diagnostic_group d;
          error ("redefinition of default argument for %q+#D", new_parm);
@@ -1942,6 +1945,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
       || TREE_TYPE (olddecl) == error_mark_node)
     return error_mark_node;
 
+  bool mod_redefinable = redefinable_import_p (olddecl);
+
   /* Check for redeclaration and other discrepancies.  */
   if (TREE_CODE (olddecl) == FUNCTION_DECL
       && DECL_IS_UNDECLARED_BUILTIN (olddecl))
@@ -2228,6 +2233,14 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
          if (TREE_CODE (oldres) == TYPE_DECL
              || TREE_CODE (newres) == TYPE_DECL)
            {
+             /* E.g. cstddef __byte_operand.  Alternatively we could find a
+                mergeable definition in lookup_elaborated_type, but we'd still
+                need to push it into scope in the current TU.  */
+             if (mod_redefinable
+                 && (DECL_IMPLICIT_TYPEDEF_P (oldres)
+                     == DECL_IMPLICIT_TYPEDEF_P (newres)))
+               goto return_old;
+
              auto_diagnostic_group d;
              error_at (newdecl_loc,
                        "conflicting declaration of template %q#D", newdecl);
@@ -2283,6 +2296,18 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
          else
            return NULL_TREE;
        }
+      else if (mod_redefinable
+              && TREE_CODE (newdecl) == TYPE_DECL
+              && (DECL_IMPLICIT_TYPEDEF_P (newdecl)
+                  == DECL_IMPLICIT_TYPEDEF_P (olddecl)))
+       /* Can also get here with e.g. typedef class {...} foo.  */
+       goto return_old;
+      else if (mod_redefinable
+              && TREE_CODE (newdecl) == VAR_DECL
+              && undeduced_auto_decl (newdecl)
+              && !undeduced_auto_decl (olddecl))
+       /* We don't have fndecl_declared_return_type for vars.  */
+       goto return_old;
       else
        {
          auto_diagnostic_group d;
@@ -2478,7 +2503,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
                     of the member function within the class template.  */
                  || CLASSTYPE_TEMPLATE_INFO (CP_DECL_CONTEXT (olddecl))))
            {
-             check_redeclaration_no_default_args (newdecl);
+             if (!mod_redefinable)
+               check_redeclaration_no_default_args (newdecl);
 
              if (DECL_TEMPLATE_INFO (olddecl)
                  && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (olddecl)))
@@ -2488,8 +2514,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
                    : INNERMOST_TEMPLATE_PARMS (current_template_parms);
                  tree old_parms
                    = DECL_INNERMOST_TEMPLATE_PARMS (DECL_TI_TEMPLATE 
(olddecl));
-                 merge_default_template_args (new_parms, old_parms,
-                                              /*class_p=*/false);
+                 merge_default_template_args (new_parms, old_parms, olddecl);
                }
            }
          else
@@ -2502,18 +2527,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
                   t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2), i++)
                if (TREE_PURPOSE (t1) && TREE_PURPOSE (t2))
                  {
-                   if (simple_cst_equal (TREE_PURPOSE (t1),
-                                         TREE_PURPOSE (t2)) == 1)
-                     {
-                       auto_diagnostic_group d;
-                       if (permerror (newdecl_loc,
-                                      "default argument given for parameter "
-                                      "%d of %q#D", i, newdecl))
-                         inform (olddecl_loc,
-                                 "previous specification in %q#D here",
-                                 olddecl);
-                     }
-                   else
+                   if (!cp_tree_equal (TREE_PURPOSE (t1),
+                                       TREE_PURPOSE (t2)))
                      {
                        auto_diagnostic_group d;
                        error_at (newdecl_loc,
@@ -2523,6 +2538,16 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
                                "previous specification in %q#D here",
                                olddecl);
                      }
+                   else if (!mod_redefinable)
+                     {
+                       auto_diagnostic_group d;
+                       if (permerror (newdecl_loc,
+                                      "default argument given for parameter "
+                                      "%d of %q#D", i, newdecl))
+                         inform (olddecl_loc,
+                                 "previous specification in %q#D here",
+                                 olddecl);
+                     }
                  }
 
              /* C++17 11.3.6/4: "If a friend declaration specifies a default
@@ -2653,6 +2678,17 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
hiding, bool was_hidden)
          = DECL_OVERLOADED_OPERATOR_CODE_RAW (olddecl);
       new_defines_function = DECL_INITIAL (newdecl) != NULL_TREE;
 
+      if (new_defines_function && mod_redefinable
+         && DECL_INITIAL (olddecl))
+       {
+         if (GNU_INLINE_P (STRIP_TEMPLATE (olddecl))
+             && !GNU_INLINE_P (STRIP_TEMPLATE (newdecl)))
+           /* Normal out-of-line redefinition of gnu_inline.  */
+           mod_redefinable = false;
+         else
+           new_defines_function = false;
+       }
+
       check_redecl_contract (newdecl, olddecl);
 
       /* Optionally warn about more than one declaration for the same
@@ -2684,7 +2720,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
       if (!(DECL_TEMPLATE_INSTANTIATION (olddecl)
            && DECL_TEMPLATE_SPECIALIZATION (newdecl)))
        {
-         if (DECL_DELETED_FN (newdecl))
+         if (DECL_DELETED_FN (newdecl)
+             && !(mod_redefinable
+                  && DECL_DELETED_FN (olddecl)))
            {
              auto_diagnostic_group d;
              if (pedwarn (newdecl_loc, 0, "deleted definition of %qD "
@@ -2748,7 +2786,8 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
            {
              /* Per C++11 8.3.6/4, default arguments cannot be added in
                 later declarations of a function template.  */
-             check_redeclaration_no_default_args (newdecl);
+             if (!mod_redefinable)
+               check_redeclaration_no_default_args (newdecl);
              /* C++17 11.3.6/4: "If a friend declaration specifies a default
                 argument expression, that declaration... shall be the only
                 declaration of the function or function template in the
@@ -2758,8 +2797,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
 
              tree new_parms = DECL_INNERMOST_TEMPLATE_PARMS (newdecl);
              tree old_parms = DECL_INNERMOST_TEMPLATE_PARMS (olddecl);
-             merge_default_template_args (new_parms, old_parms,
-                                          /*class_p=*/false);
+             merge_default_template_args (new_parms, old_parms, olddecl);
            }
          if (!DECL_UNIQUE_FRIEND_P (new_result))
            DECL_UNIQUE_FRIEND_P (old_result) = false;
@@ -2769,6 +2807,7 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
          if (GNU_INLINE_P (old_result) != GNU_INLINE_P (new_result)
              && DECL_INITIAL (new_result))
            {
+             mod_redefinable = false;
              if (DECL_INITIAL (old_result))
                DECL_UNINLINABLE (old_result) = 1;
              else
@@ -2798,7 +2837,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
       /* If the new declaration is a definition, update the file and
         line information on the declaration, and also make
         the old declaration the same definition.  */
-      if (DECL_INITIAL (new_result) != NULL_TREE)
+      if (DECL_INITIAL (new_result) != NULL_TREE
+         && !(DECL_INITIAL (old_result)
+              && mod_redefinable))
        {
          DECL_SOURCE_LOCATION (olddecl)
            = DECL_SOURCE_LOCATION (old_result)
@@ -2982,8 +3023,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
        }
 
       /* Merge the initialization information.  */
-      if (DECL_INITIAL (newdecl) == NULL_TREE
-         && DECL_INITIAL (olddecl) != NULL_TREE)
+      if (DECL_INITIAL (olddecl) != NULL_TREE
+         && (DECL_INITIAL (newdecl) == NULL_TREE
+             || mod_redefinable))
        {
          DECL_INITIAL (newdecl) = DECL_INITIAL (olddecl);
          DECL_SOURCE_LOCATION (newdecl) = DECL_SOURCE_LOCATION (olddecl);
@@ -3611,6 +3653,9 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, 
bool was_hidden)
 static const char *
 redeclaration_error_message (tree newdecl, tree olddecl)
 {
+  if (redefinable_import_p (olddecl))
+    return NULL;
+
   if (TREE_CODE (newdecl) == TYPE_DECL)
     {
       /* Because C++ can put things into name space for free,
@@ -12871,7 +12916,9 @@ grokfndecl (tree ctype,
              error ("definition of implicitly-declared %qD", old_decl);
              return NULL_TREE;
            }
-         else if (DECL_DEFAULTED_FN (old_decl))
+         else if (DECL_DEFAULTED_FN (old_decl)
+                  && !(initialized == SD_DEFAULTED
+                       && redefinable_import_p (old_decl)))
            {
              auto_diagnostic_group d;
              error ("definition of explicitly-defaulted %q+D", decl);
@@ -18498,7 +18545,11 @@ lookup_and_check_tag (enum tag_types tag_code, tree 
name,
     {
       tree u = decl;
       decl = strip_using_decl (decl);
-      if (how == TAG_how::CURRENT_ONLY)
+      if (how == TAG_how::CURRENT_ONLY
+         && TREE_CODE (decl) == TYPE_DECL
+         && !(DECL_IMPLICIT_TYPEDEF_P (decl)
+              && CP_DECL_CONTEXT (decl) == CP_DECL_CONTEXT (u)
+              && redefinable_import_p (decl)))
        {
          auto_diagnostic_group _;
          error ("%qD conflicts with a previous declaration", name);
@@ -19095,6 +19146,9 @@ start_enum (tree name, tree enumtype, tree 
underlying_type,
          tree decl = TYPE_NAME (enumtype);
          if (!module_may_redeclare (decl))
            enumtype = error_mark_node;
+         else if (COMPLETE_TYPE_P (enumtype)
+                  && redefinable_import_p (decl))
+           return enumtype;
          else
            set_instantiating_module (decl);
        }
@@ -19163,6 +19217,12 @@ start_enum (tree name, tree enumtype, tree 
underlying_type,
 
          if (enumtype != error_mark_node)
            {
+             /* ??? We wouldn't need to repeat this if lookup_elaborated_type
+                found (and pushed) mergeable types.  */
+             if (COMPLETE_TYPE_P (enumtype)
+                 && redefinable_import_p (TYPE_NAME (enumtype)))
+               return enumtype;
+
              /* The enum is considered opaque until the opening '{' of the
                 enumerator list.  */
              SET_OPAQUE_ENUM_P (enumtype, true);
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 1e9e27a3ec20..6d7d650c638f 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -22220,6 +22220,37 @@ set_originating_module (tree decl, bool friend_p 
ATTRIBUTE_UNUSED)
     DECL_MODULE_EXPORT_P (decl) = true;
 }
 
+/* True if it's OK to redefine DECL under P1811 because it is attached to the
+   global module and its definition was imported; [basic.def.odr]/12 "there can
+   be more than one definition...provided that each definition appears in a
+   different translation unit"
+
+   This does not depend on whether DECL was already defined, so that for
+   instance it can affect redefinition of default arguments.  This should only
+   be used when we notice a redefinition.  */
+
+bool
+redefinable_import_p (tree decl)
+{
+  if (!modules_p () || named_module_attach_p ())
+    return false;
+
+  decl = STRIP_TEMPLATE (decl);
+
+  if (TREE_CODE (decl) == TYPE_DECL
+      && !DECL_IMPLICIT_TYPEDEF_P (decl)
+      && !TYPE_DECL_FOR_LINKAGE_PURPOSES_P (decl))
+    /* Typedefs are always redefinable, use normal handling.  */
+    return false;
+
+  return (DECL_LANG_SPECIFIC (decl)
+         && DECL_MODULE_ENTITY_P (decl)
+         /* Use this instead of DECL_MODULE_IMPORT_P so we don't get confused
+            by partitions or set_instantiating_module.  */
+         /* FIXME this also means we allow multiple redefinitions.  */
+         && get_importing_module (decl, /*flexible*/true) > 0);
+}
+
 /* Checks whether DECL within a module unit has valid linkage for its kind.
    Must be called after visibility for DECL has been finalised.  */
 
@@ -23171,7 +23202,7 @@ static char *
 maybe_translate_include (cpp_reader *reader, line_maps *lmaps, location_t loc,
                         _cpp_file *file, bool angle, const char **alternate)
 {
-  if (!modules_p ())
+  if (!modules_p () || !flag_module_include_translate)
     {
       /* Turn off.  */
       cpp_get_callbacks (reader)->translate_include = NULL;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 56d21bf50d61..e2fc89e6b32e 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -4071,7 +4071,7 @@ pushdecl (tree decl, bool hiding)
          for (ovl_iterator iter (oldi); iter; ++iter)
            if (iter.using_p ())
              ; /* Ignore using decls here.  */
-           else if (iter.hidden_p ()
+           else if (DECL_IS_UNDECLARED_BUILTIN (*iter)
                     && TREE_CODE (*iter) == FUNCTION_DECL
                     && DECL_LANG_SPECIFIC (*iter)
                     && DECL_MODULE_IMPORT_P (*iter))
@@ -8643,24 +8643,8 @@ lookup_elaborated_type (tree name, TAG_how how)
                  return bind;
              }
 
-         if (!module_purview_p ())
-           {
-             /* We're in the global module, perhaps there's a tag
-                there?  */
-
-             /* FIXME: In general we should probably merge global module
-                classes in check_module_override rather than here, but for
-                GCC14 let's just fix lazy declarations of __class_type_info in
-                build_dynamic_cast_1.  */
-             if (current_namespace == abi_node)
-               {
-                 tree g = (BINDING_VECTOR_CLUSTER (*slot, 0)
-                           .slots[BINDING_SLOT_GLOBAL]);
-                 for (ovl_iterator iter (g); iter; ++iter)
-                   if (qualify_lookup (*iter, LOOK_want::TYPE))
-                     return *iter;
-               }
-           }
+         /* We'll find mergeable types in check_module_override when we try to
+            push a new one, no need to handle them specially here.  */
        }
     }
 
@@ -8825,6 +8809,9 @@ pushtag (tree name, tree type, TAG_how how)
        (type, how == TAG_how::HIDDEN_FRIEND, b);
       if (decl == error_mark_node)
        return decl;
+      if (TREE_TYPE (decl) != type)
+       /* Found an imported version of the same type.  */
+       return TREE_TYPE (decl);
 
       if (b->kind == sk_class)
        {
@@ -8854,6 +8841,9 @@ pushtag (tree name, tree type, TAG_how how)
            (decl, b, /*hiding=*/(how == TAG_how::HIDDEN_FRIEND));
          if (decl == error_mark_node)
            return decl;
+         if (TREE_TYPE (decl) != type)
+           /* Found an imported version of the same type.  */
+           return TREE_TYPE (decl);
 
          if (DECL_CONTEXT (decl) == std_node
              && init_list_identifier == DECL_NAME (TYPE_NAME (type))
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 9c54b2f22e2d..48e3117a71e7 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4435,6 +4435,81 @@ cp_parser_consume_semicolon_at_end_of_statement 
(cp_parser *parser)
     }
 }
 
+/* Consume the entire balanced token sequence at point.  Returns true
+   if that was successful.  */
+
+static bool
+cp_parser_skip_to_end_of_balanced_tokens (cp_parser *parser)
+{
+  size_t n = cp_parser_skip_balanced_tokens (parser, 1);
+  if (n == 1)
+    return false;
+  while (--n)
+    cp_lexer_consume_token (parser->lexer);
+  return true;
+}
+
+/* Consume the entire function definition at point, including
+   function-try-block and mem-initializers.  */
+
+static void
+cp_parser_skip_function_definition (cp_parser *parser)
+{
+  bool fn_try = cp_lexer_next_token_is_keyword (parser->lexer, RID_TRY);
+  if (fn_try)
+    cp_lexer_consume_token (parser->lexer);
+  if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+    do
+      {
+       cp_lexer_consume_token (parser->lexer);
+       cp_parser_mem_initializer_id (parser);
+       cp_parser_skip_to_end_of_balanced_tokens (parser);
+      }
+    while (cp_lexer_next_token_is (parser->lexer, CPP_COMMA));
+  cp_parser_skip_to_end_of_block_or_statement (parser);
+  if (fn_try)
+    while (cp_lexer_next_token_is_keyword (parser->lexer, RID_CATCH))
+      {
+       cp_lexer_consume_token (parser->lexer);
+       cp_parser_skip_to_end_of_balanced_tokens (parser);
+       cp_parser_skip_to_end_of_block_or_statement (parser);
+      }
+  /* Parse error if what we skipped didn't end with }.  */
+  if (cp_token *prev = cp_lexer_safe_previous_token (parser->lexer))
+    if (!prev || prev->type != CPP_CLOSE_BRACE)
+      cp_parser_require (parser, CPP_CLOSE_BRACE, RT_CLOSE_BRACE);
+}
+
+/* Skip past any base clause to the open brace of the class definition.  */
+
+static void
+cp_parser_skip_base_clause (cp_parser *parser)
+{
+  while (true)
+    {
+      cp_token *t = cp_lexer_peek_token (parser->lexer);
+      switch (t->type)
+       {
+       case CPP_EOF:
+       case CPP_OPEN_BRACE:
+         goto done;
+       case CPP_OPEN_PAREN:
+       case CPP_OPEN_SPLICE:
+       case CPP_OPEN_SQUARE:
+         if (!cp_parser_skip_to_end_of_balanced_tokens (parser))
+           goto done;
+         break;
+       default:
+         cp_lexer_consume_token (parser->lexer);
+         break;
+       }
+    }
+ done:
+  /* Parse error if this didn't get us to the {.  */
+  if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
+    cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE);
+}
+
 /* Skip tokens until we have consumed an entire block, or until we
    have consumed a non-nested `;'.  */
 
@@ -24121,6 +24196,23 @@ cp_parser_enum_specifier (cp_parser* parser)
     type = start_enum (identifier, type, underlying_type,
                       attributes, scoped_enum_p, &is_new_type);
 
+  if (type != error_mark_node && COMPLETE_TYPE_P (type)
+      && redefinable_import_p (TYPE_NAME (type)))
+    {
+      /* We already have an imported definition of this enum; skip over
+        the redefinition and push any non-scoped enumerators.  */
+      if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+       {
+         cp_parser_skip_to_end_of_block_or_statement (parser);
+         if (!SCOPED_ENUM_P (type))
+           for (tree l = TYPE_VALUES (type); l; l = TREE_CHAIN (l))
+             pushdecl (TREE_VALUE (l));
+       }
+      if (cp_parser_allow_gnu_extensions_p (parser))
+       cp_parser_gnu_attributes_opt (parser);
+      goto done;
+    }
+
   /* If the next token is not '{' it is an opaque-enum-specifier or an
      elaborated-type-specifier.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -24269,6 +24361,7 @@ cp_parser_enum_specifier (cp_parser* parser)
        finish_enum (type);
     }
 
+ done:
   if (nested_name_specifier)
     {
       if (CLASS_TYPE_P (nested_name_specifier))
@@ -29854,11 +29947,17 @@ cp_parser_class_specifier (cp_parser* parser)
                               &nested_name_specifier_p);
   /* If the class-head was a semantic disaster, skip the entire body
      of the class.  */
-  if (!type)
+  if (!type
+      /* Or if the class is already complete from an import, skip the
+        redefinition.  */
+      || (CLASS_TYPE_P (type) && COMPLETE_TYPE_P (type)))
     {
       cp_parser_skip_to_end_of_block_or_statement (parser);
       pop_deferring_access_checks ();
-      return error_mark_node;
+      /* Consume trailing attributes like below.  */
+      if (cp_parser_allow_gnu_extensions_p (parser))
+       cp_parser_gnu_attributes_opt (parser);
+      return type ? type : error_mark_node;
     }
 
   /* Look for the `{'.  */
@@ -30835,12 +30934,19 @@ cp_parser_class_head (cp_parser* parser,
   if (type != error_mark_node
       && (COMPLETE_TYPE_P (type) || TYPE_BEING_DEFINED (type)))
     {
-      auto_diagnostic_group d;
-      error_at (type_start_token->location, "redefinition of %q#T",
-               type);
-      inform (location_of (type), "previous definition of %q#T",
-             type);
-      type = NULL_TREE;
+      if (redefinable_import_p (TYPE_NAME (type)))
+       /* Redefinition of an imported type is OK under P1811;
+          cp_parser_class_specifier will skip the body.  */
+       cp_parser_skip_base_clause (parser);
+      else
+       {
+         auto_diagnostic_group d;
+         error_at (type_start_token->location, "redefinition of %q#T",
+                   type);
+         inform (location_of (type), "previous definition of %q#T",
+                 type);
+         type = NULL_TREE;
+       }
       goto done;
     }
   else if (type == error_mark_node)
@@ -36265,13 +36371,13 @@ 
cp_parser_function_definition_from_specifiers_and_declarator
   if (!success_p)
     {
       /* Skip the entire function.  */
-      cp_parser_skip_to_end_of_block_or_statement (parser);
+      cp_parser_skip_function_definition (parser);
       fn = error_mark_node;
     }
   else if (DECL_INITIAL (current_function_decl) != error_mark_node)
     {
-      /* Seen already, skip it.  An error message has already been output.  */
-      cp_parser_skip_to_end_of_block_or_statement (parser);
+      /* start_preparsed_function exited early, also skip parsing.  */
+      cp_parser_skip_function_definition (parser);
       fn = current_function_decl;
       current_function_decl = NULL_TREE;
       /* If this is a function from a class, pop the nested class.  */
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index fd60878ef2a6..945554663a2c 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -6687,7 +6687,7 @@ redeclare_class_template (tree type, tree parms, tree 
cons)
        }
     }
 
-  if (!merge_default_template_args (parms, tmpl_parms, /*class_p=*/true))
+  if (!merge_default_template_args (parms, tmpl_parms, tmpl))
     return false;
 
   tree ci = get_constraints (tmpl);
diff --git a/gcc/testsuite/g++.dg/modules/compile-std1.C 
b/gcc/testsuite/g++.dg/modules/compile-std1.C
index 1de9ea789769..069ec32e8581 100644
--- a/gcc/testsuite/g++.dg/modules/compile-std1.C
+++ b/gcc/testsuite/g++.dg/modules/compile-std1.C
@@ -1,5 +1,9 @@
+// Test both that --compile-std-module works and that P1811 redefinition works
+// for all of libstdc++.
+
 // { dg-additional-options "-fmodules --compile-std-module -g -O" }
-// { dg-additional-options "-flang-info-include-translate" }
+// { dg-additional-options "-fno-module-include-translate" }
+// { dg-additional-options "-fno-module-lazy" }
 // { dg-do compile { target c++20 } }
 // { dg-module-cmi std }
 // { dg-module-cmi std.compat }
@@ -7,8 +11,7 @@
 
 import std;
 import std.compat;
-#include <vector>              // { dg-message "translated to import" }
-import <bits/stdc++.h>;
+#include <bits/stdc++.h>
 
 void f()
 {
diff --git a/gcc/testsuite/g++.dg/modules/default-arg-4_b.C 
b/gcc/testsuite/g++.dg/modules/default-arg-4_b.C
index 5800ea208072..fcc07d31aaa0 100644
--- a/gcc/testsuite/g++.dg/modules/default-arg-4_b.C
+++ b/gcc/testsuite/g++.dg/modules/default-arg-4_b.C
@@ -1,24 +1,23 @@
 // C++20 P1766R1 - Mitigating minor modules maladies
-// FIXME mark as run once we no longer xfail below.
-// { dg-module-do compile }
+// { dg-module-do run }
 // { dg-additional-options "-fmodules-ts" }
 
 import M;
 
 int
-foo (int i = 42)                       // { dg-bogus "default argument given 
for parameter 1 of 'int foo\\\(int\\\)'" "PR99000" { xfail *-*-* } }
+foo (int i = 42)                       // { dg-bogus "default argument given 
for parameter 1 of 'int foo\\\(int\\\)'" "PR99000" }
 {
   return i;
 }
 
-template <typename T, typename U = int>        // { dg-bogus "redefinition of 
default argument for 'class U'" "PR99000" { xfail *-*-* } }
+template <typename T, typename U = int>        // { dg-bogus "redefinition of 
default argument for 'class U'" "PR99000" }
 int
 bar ()
 {
   return sizeof (U);
 }
 
-template <typename T, int N = 42>      // { dg-bogus "redefinition of default 
argument for 'int N'" "PR99000" { xfail *-*-* } }
+template <typename T, int N = 42>      // { dg-bogus "redefinition of default 
argument for 'int N'" "PR99000" }
 int
 baz ()
 {
diff --git a/gcc/testsuite/g++.dg/modules/gmf-3_a.C 
b/gcc/testsuite/g++.dg/modules/gmf-3_a.C
new file mode 100644
index 000000000000..bb28144ea139
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/gmf-3_a.C
@@ -0,0 +1,14 @@
+// Test that a forward declaration of a class (template) in the global module
+// combines with a reachable imported definition.
+
+// { dg-additional-options -fmodules-ts }
+
+module;
+#include "gmf-3.h"
+
+// { dg-module-cmi M1 }
+export module M1;
+
+// Keep A and B from being discarded per [module.global.frag]
+export A f1();
+export B<int> f2();
diff --git a/gcc/testsuite/g++.dg/modules/gmf-3_b.C 
b/gcc/testsuite/g++.dg/modules/gmf-3_b.C
new file mode 100644
index 000000000000..be5de6b38623
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/gmf-3_b.C
@@ -0,0 +1,7 @@
+// { dg-additional-options -fmodules-ts }
+
+import M1;
+
+struct A a;
+template <class T> struct B;
+B<int> b;
diff --git a/gcc/testsuite/g++.dg/modules/gmf-4_a.C 
b/gcc/testsuite/g++.dg/modules/gmf-4_a.C
new file mode 100644
index 000000000000..c1e08da16ada
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/gmf-4_a.C
@@ -0,0 +1,13 @@
+// Test P1811 redefinition of class and class template
+
+// { dg-additional-options -fmodules-ts }
+
+module;
+#include "gmf-3.h"
+
+// { dg-module-cmi M1 }
+export module M1;
+
+// Keep A and B from being discarded per [module.global.frag]
+export A f1();
+export B<int> f2();
diff --git a/gcc/testsuite/g++.dg/modules/gmf-4_b.C 
b/gcc/testsuite/g++.dg/modules/gmf-4_b.C
new file mode 100644
index 000000000000..6fb01f83d94e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/gmf-4_b.C
@@ -0,0 +1,12 @@
+// { dg-additional-options -fmodules-ts }
+
+import M1;
+
+// Force lazy loading of A since gmf-3 does.
+auto x1 = f1();
+auto x2 = f2();
+
+#include "gmf-3.h"
+
+A a;
+B<int> b;
diff --git a/gcc/testsuite/g++.dg/modules/hello-1_b.C 
b/gcc/testsuite/g++.dg/modules/hello-1_b.C
index b7d78c476c66..624af70ed187 100644
--- a/gcc/testsuite/g++.dg/modules/hello-1_b.C
+++ b/gcc/testsuite/g++.dg/modules/hello-1_b.C
@@ -1,7 +1,7 @@
-// { dg-additional-options -fmodules-ts }
+// { dg-additional-options "-fmodules -fno-module-lazy" }
 
-#include <string_view>
 import hello;
+#include <string_view>
 int main (void)
 {
   greeter ("world");
diff --git a/gcc/testsuite/g++.dg/modules/p1811-1_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-1_a.C
new file mode 100644
index 000000000000..608ea10a6db7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-1_a.C
@@ -0,0 +1,11 @@
+// Test P1811 redefinition of a function-try-block.
+
+// { dg-additional-options "-fmodules" }
+
+module;
+#include "p1811-1.h"
+
+// { dg-module-cmi M }
+export module M;
+
+export inline auto pf = &f;
diff --git a/gcc/testsuite/g++.dg/modules/p1811-1_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-1_b.C
new file mode 100644
index 000000000000..46b18f19bb00
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-1_b.C
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+auto p = pf;
+#include "p1811-1.h"
+
+int after;
diff --git a/gcc/testsuite/g++.dg/modules/p1811-2_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-2_a.C
new file mode 100644
index 000000000000..6b816cb47e8f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-2_a.C
@@ -0,0 +1,11 @@
+// Test P1811 redefinition of a qualified enum.
+
+// { dg-additional-options "-fmodules" }
+
+module;
+#include "p1811-2.h"
+
+// { dg-module-cmi M }
+export module M;
+
+export C make_c ();
diff --git a/gcc/testsuite/g++.dg/modules/p1811-2_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-2_b.C
new file mode 100644
index 000000000000..5ea46095c35e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-2_b.C
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+auto imported_c = make_c ();
+using C = decltype (imported_c);
+enum C::E : int { e };
+
+int after;
diff --git a/gcc/testsuite/g++.dg/modules/p1811-3_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-3_a.C
new file mode 100644
index 000000000000..7444dd131234
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-3_a.C
@@ -0,0 +1,11 @@
+// Test recovery from an incomplete redundant class definition.
+
+// { dg-additional-options "-fmodules" }
+
+module;
+#include "p1811-3.h"
+
+// { dg-module-cmi M }
+export module M;
+
+export A make_a ();
diff --git a/gcc/testsuite/g++.dg/modules/p1811-3_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-3_b.C
new file mode 100644
index 000000000000..5985dedc5aa4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-3_b.C
@@ -0,0 +1,8 @@
+// { dg-additional-options "-fmodules" }
+// { dg-timeout-factor 0.05 }
+
+import M;
+
+auto imported_a = make_a ();
+struct A : ( // { dg-error "expected" }
+// { dg-prune-output "at end of input" }
diff --git a/gcc/testsuite/g++.dg/modules/p1811-4_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-4_a.C
new file mode 100644
index 000000000000..10c2a6ffdd4b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-4_a.C
@@ -0,0 +1,8 @@
+// Definitions attached to a named module cannot be repeated.
+
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi P:a }
+
+export module P:a;
+
+export struct A { };
diff --git a/gcc/testsuite/g++.dg/modules/p1811-4_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-4_b.C
new file mode 100644
index 000000000000..5d31bb1883bc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-4_b.C
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi !P:b }
+
+module P:b;
+import :a;
+
+struct A { }; // { dg-error "redefinition" }
diff --git a/gcc/testsuite/g++.dg/modules/p1811-5_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-5_a.C
new file mode 100644
index 000000000000..1b87579e8dc3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-5_a.C
@@ -0,0 +1,11 @@
+// Test that P1811 does not allow multiple definitions in one TU.
+
+// { dg-additional-options "-fmodules" }
+
+module;
+#include "p1811-5.h"
+
+// { dg-module-cmi M }
+export module M;
+
+export A make_a ();
diff --git a/gcc/testsuite/g++.dg/modules/p1811-5_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-5_b.C
new file mode 100644
index 000000000000..63c86631c7b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-5_b.C
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+auto imported_a = make_a ();
+struct A { };
+struct A { };                // { dg-error "redefinition" "" { xfail *-*-* } }
diff --git a/gcc/testsuite/g++.dg/modules/p1811-6_a.C 
b/gcc/testsuite/g++.dg/modules/p1811-6_a.C
new file mode 100644
index 000000000000..83180c3bcd6a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-6_a.C
@@ -0,0 +1,12 @@
+// Test that P1811 does not allow changing the kind of a template.
+
+// { dg-additional-options "-fmodules" }
+
+module;
+#include "p1811-6.h"
+
+// { dg-module-cmi M }
+export module M;
+
+export A<int> make_a ();
+export B<int> make_b ();
diff --git a/gcc/testsuite/g++.dg/modules/p1811-6_b.C 
b/gcc/testsuite/g++.dg/modules/p1811-6_b.C
new file mode 100644
index 000000000000..a815da5c9b9f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/p1811-6_b.C
@@ -0,0 +1,9 @@
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+auto imported_a = make_a ();
+auto imported_b = make_b ();
+
+template <class T> using A = T; // { dg-error "conflicting declaration" }
+template <class T> int B; // { dg-error "conflicting declaration" }
diff --git a/gcc/testsuite/g++.dg/modules/pr122053_b.C 
b/gcc/testsuite/g++.dg/modules/pr122053_b.C
index a7084b74040a..e7038fa07230 100644
--- a/gcc/testsuite/g++.dg/modules/pr122053_b.C
+++ b/gcc/testsuite/g++.dg/modules/pr122053_b.C
@@ -3,7 +3,7 @@
 // Test we don't ICE when redefining a type coming from an import.
 
 import M;
-struct mytime {  // { dg-bogus "conflicting" "PR99000" { xfail *-*-* } }
+struct mytime {  // { dg-bogus "conflicting" "PR99000" }
   long a, b;
 };
-mytime m = foo();  // { dg-bogus "" "PR99000" { xfail *-*-* } }
+mytime m = foo();  // { dg-bogus "" "PR99000" }

Reply via email to