https://gcc.gnu.org/g:179e01085b0aed111ef1f7908c4b87c800f880e9

commit r15-7870-g179e01085b0aed111ef1f7908c4b87c800f880e9
Author: Jakub Jelinek <ja...@redhat.com>
Date:   Thu Mar 6 18:26:37 2025 +0100

    c++: Update TYPE_FIELDS of variant types if 
cp_parser_late_parsing_default_args etc. modify it [PR98533]
    
    The following testcases ICE during type verification, because TYPE_FIELDS
    of e.g. S RECORD_TYPE in pr119123.C is different from TYPE_FIELDS of const 
S.
    Various decls are added to S's TYPE_FIELDS first, then finish_struct
    indirectly calls fixup_type_variants to sync the variant copies.
    But later on cp_parser_class_specifier calls
    cp_parser_late_parsing_default_args and that apparently adds a lambda
    type (from default argument) to TYPE_FIELDS of S.
    Dunno if that is right or not, assuming it is right, the following
    patch fixes it by updating TYPE_FIELDS of variant types if there were
    any changes in the various functions cp_parser_class_specifier defers and
    calls on the outermost enclosing class.
    There was quite a lot of code repetition already before, so the patch
    uses a lambda to avoid the repetitions.
    To my surprise, in some of the contract testcases (
    g++.dg/contracts/contracts-friend1.C
    g++.dg/contracts/contracts-nested-class1.C
    g++.dg/contracts/contracts-nested-class2.C
    g++.dg/contracts/contracts-redecl7.C
    g++.dg/contracts/contracts-redecl8.C
    ) it is actually setting class_type and pushing TRANSLATION_UNIT_DECL
    rather than some class types in some cases.
    
    Or should the lambda pushing into the containing class be somehow avoided?
    
    2025-03-06  Jakub Jelinek  <ja...@redhat.com>
    
            PR c++/98533
            PR c++/119123
            * parser.cc (cp_parser_class_specifier): Update TYPE_FIELDS of
            variant types in case cp_parser_late_parsing_default_args etc. 
change
            TYPE_FIELDS on the main variant.  Add switch_to_class lambda and
            use it to simplify repeated class switching code.
    
            * g++.dg/cpp0x/pr98533.C: New test.
            * g++.dg/cpp0x/pr119123.C: New test.

Diff:
---
 gcc/cp/parser.cc                      | 63 ++++++++++++++++++-----------------
 gcc/testsuite/g++.dg/cpp0x/pr119123.C | 10 ++++++
 gcc/testsuite/g++.dg/cpp0x/pr98533.C  | 25 ++++++++++++++
 3 files changed, 68 insertions(+), 30 deletions(-)

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 69c27aa7b6e3..489c00e05227 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -27718,6 +27718,7 @@ cp_parser_class_specifier (cp_parser* parser)
     {
       tree decl;
       tree class_type = NULL_TREE;
+      tree class_type_fields = NULL_TREE;
       tree pushed_scope = NULL_TREE;
       unsigned ix;
       cp_default_arg_entry *e;
@@ -27731,6 +27732,33 @@ cp_parser_class_specifier (cp_parser* parser)
          vec_safe_truncate (unparsed_funs_with_definitions, 0);
        }
 
+      auto switch_to_class = [&] (tree t)
+       {
+         if (class_type != t)
+           {
+             /* cp_parser_late_parsing_default_args etc. could have changed
+                TYPE_FIELDS (class_type), propagate that to all variants.  */
+             if (class_type
+                 && RECORD_OR_UNION_TYPE_P (class_type)
+                 && TYPE_FIELDS (class_type) != class_type_fields)
+               for (tree variant = TYPE_NEXT_VARIANT (class_type);
+                    variant; variant = TYPE_NEXT_VARIANT (variant))
+                 TYPE_FIELDS (variant) = TYPE_FIELDS (class_type);
+             if (pushed_scope)
+               pop_scope (pushed_scope);
+             class_type = t;
+             class_type_fields = NULL_TREE;
+             if (t)
+               {
+                 if (RECORD_OR_UNION_TYPE_P (class_type))
+                   class_type_fields = TYPE_FIELDS (class_type);
+                 pushed_scope = push_scope (class_type);
+               }
+             else
+               pushed_scope = NULL_TREE;
+           }
+       };
+
       /* In a first pass, parse default arguments to the functions.
         Then, in a second pass, parse the bodies of the functions.
         This two-phased approach handles cases like:
@@ -27746,13 +27774,7 @@ cp_parser_class_specifier (cp_parser* parser)
          decl = e->decl;
          /* If there are default arguments that have not yet been processed,
             take care of them now.  */
-         if (class_type != e->class_type)
-           {
-             if (pushed_scope)
-               pop_scope (pushed_scope);
-             class_type = e->class_type;
-             pushed_scope = push_scope (class_type);
-           }
+         switch_to_class (e->class_type);
          /* Make sure that any template parameters are in scope.  */
          maybe_begin_member_template_processing (decl);
          /* Parse the default argument expressions.  */
@@ -27768,13 +27790,7 @@ cp_parser_class_specifier (cp_parser* parser)
       FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
        {
          tree ctx = DECL_CONTEXT (decl);
-         if (class_type != ctx)
-           {
-             if (pushed_scope)
-               pop_scope (pushed_scope);
-             class_type = ctx;
-             pushed_scope = push_scope (class_type);
-           }
+         switch_to_class (ctx);
 
          tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
          def_parse = TREE_PURPOSE (def_parse);
@@ -27830,13 +27846,7 @@ cp_parser_class_specifier (cp_parser* parser)
       FOR_EACH_VEC_SAFE_ELT (unparsed_nsdmis, ix, decl)
        {
          tree ctx = type_context_for_name_lookup (decl);
-         if (class_type != ctx)
-           {
-             if (pushed_scope)
-               pop_scope (pushed_scope);
-             class_type = ctx;
-             pushed_scope = push_scope (class_type);
-           }
+         switch_to_class (ctx);
          inject_this_parameter (class_type, TYPE_UNQUALIFIED);
          cp_parser_late_parsing_nsdmi (parser, decl);
        }
@@ -27846,13 +27856,7 @@ cp_parser_class_specifier (cp_parser* parser)
       FOR_EACH_VEC_SAFE_ELT (unparsed_contracts, ix, decl)
        {
          tree ctx = DECL_CONTEXT (decl);
-         if (class_type != ctx)
-           {
-             if (pushed_scope)
-               pop_scope (pushed_scope);
-             class_type = ctx;
-             pushed_scope = push_scope (class_type);
-           }
+         switch_to_class (ctx);
 
          temp_override<tree> cfd(current_function_decl, decl);
 
@@ -27893,8 +27897,7 @@ cp_parser_class_specifier (cp_parser* parser)
 
       current_class_ptr = NULL_TREE;
       current_class_ref = NULL_TREE;
-      if (pushed_scope)
-       pop_scope (pushed_scope);
+      switch_to_class (NULL_TREE);
 
       /* Now parse the body of the functions.  */
       if (flag_openmp)
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr119123.C 
b/gcc/testsuite/g++.dg/cpp0x/pr119123.C
new file mode 100644
index 000000000000..bbc1ca0cf21d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr119123.C
@@ -0,0 +1,10 @@
+// PR c++/119123
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -g" }
+
+struct S {
+  template <typename> void foo (int = [] {}) const;
+};
+struct T {
+  static void bar (const S &);
+};
diff --git a/gcc/testsuite/g++.dg/cpp0x/pr98533.C 
b/gcc/testsuite/g++.dg/cpp0x/pr98533.C
new file mode 100644
index 000000000000..c279d021e198
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/pr98533.C
@@ -0,0 +1,25 @@
+// PR c++/98533
+// { dg-do compile { target c++11 } }
+// { dg-options "-g" }
+
+class IR;
+struct Pass {
+  explicit Pass(IR *ir) : ir_(ir) {}
+  virtual ~Pass() = default;
+  IR *ir_ {nullptr};
+};
+struct PassManager {
+  template <typename T> void RunPass() { T pass(ir_); }
+  IR *ir_ {nullptr};
+};
+struct IR final {
+  template <typename T> void RunPass() { pass_manager_.RunPass<T>(); }
+  PassManager pass_manager_;
+};
+struct ThePass : Pass {
+  explicit ThePass(IR *ir) : Pass(ir) {}
+  ThePass(const ThePass &) = delete;
+  template <typename Func = bool (*)(void *)> void Bar(void *inst, Func func = 
[](void *) {});
+};
+
+void foo(IR *ir) { ir->RunPass<ThePass>(); }

Reply via email to