On 3/6/25 11:14 AM, Jakub Jelinek wrote:
Hi!
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.
Odd, but I wouldn't worry about contracts bugs at this point.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK.
Or should the lambda pushing into the containing class be somehow avoided?
That's a question that could be considered in the context of the
question of linkage of lambdas in e.g. class-scope alias templates, but
not for GCC 15.
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.
--- gcc/cp/parser.cc.jj 2025-02-27 22:04:36.007028112 +0100
+++ gcc/cp/parser.cc 2025-03-06 16:35:12.770694734 +0100
@@ -27718,6 +27718,7 @@ cp_parser_class_specifier (cp_parser* pa
{
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* pa
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* pa
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* pa
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* pa
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* pa
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* pa
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)
--- gcc/testsuite/g++.dg/cpp0x/pr98533.C.jj 2025-03-06 10:21:25.456716285
+0100
+++ gcc/testsuite/g++.dg/cpp0x/pr98533.C 2025-03-06 10:20:56.561122855
+0100
@@ -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>(); }
--- gcc/testsuite/g++.dg/cpp0x/pr119123.C.jj 2025-03-06 10:22:02.723191937
+0100
+++ gcc/testsuite/g++.dg/cpp0x/pr119123.C 2025-03-06 10:22:52.957485126
+0100
@@ -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 &);
+};
Jakub