Hi! When working on unsequenced/reproducible attributes, I've noticed that on templates for some attributes decl_attributes isn't called at all, so they are kept in TYPE_ATTRIBUTES without any verification/transformations and also without argument substitution.
The following patch fixes that for FUNCTION/METHOD_TYPE attributes. The included testcase ICEs without the pt.cc changes. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2024-08-01 Jakub Jelinek <ja...@redhat.com> PR c++/116175 * pt.cc (apply_late_template_attributes): For function/method types call cp_build_type_attribute_variant on the non-dependent attributes. (rebuild_function_or_method_type): Add ARGS argument. Use apply_late_template_attributes rather than cp_build_type_attribute_variant. (maybe_rebuild_function_decl_type): Add ARGS argument, pass it to rebuild_function_or_method_type. (tsubst_function_decl): Adjust caller. (tsubst_function_type): Adjust rebuild_function_or_method_type caller. * g++.dg/ext/attr-format4.C: New test. --- gcc/cp/pt.cc.jj 2024-07-31 14:38:54.405628645 +0200 +++ gcc/cp/pt.cc 2024-08-01 16:29:59.671779469 +0200 @@ -12219,6 +12219,8 @@ apply_late_template_attributes (tree *de to our attributes parameter. */ gcc_assert (*p == attributes); } + else if (FUNC_OR_METHOD_TYPE_P (*decl_p)) + p = NULL; else { p = &TYPE_ATTRIBUTES (*decl_p); @@ -12237,7 +12239,10 @@ apply_late_template_attributes (tree *de tree nondep = t; /* Apply any non-dependent attributes. */ - *p = nondep; + if (p) + *p = nondep; + else if (nondep) + *decl_p = cp_build_type_attribute_variant (*decl_p, nondep); if (nondep == attributes) return true; @@ -14375,8 +14380,9 @@ lookup_explicit_specifier (tree v) identical to T. */ static tree -rebuild_function_or_method_type (tree t, tree return_type, tree arg_types, - tree raises, tsubst_flags_t complain) +rebuild_function_or_method_type (tree t, tree args, tree return_type, + tree arg_types, tree raises, + tsubst_flags_t complain) { gcc_assert (FUNC_OR_METHOD_TYPE_P (t)); @@ -14409,7 +14415,9 @@ rebuild_function_or_method_type (tree t, new_type = build_method_type_directly (r, return_type, TREE_CHAIN (arg_types)); } - new_type = cp_build_type_attribute_variant (new_type, TYPE_ATTRIBUTES (t)); + if (!apply_late_template_attributes (&new_type, TYPE_ATTRIBUTES (t), 0, + args, complain, NULL_TREE)) + return error_mark_node; cp_ref_qualifier rqual = type_memfn_rqual (t); bool late_return_type_p = TYPE_HAS_LATE_RETURN_TYPE (t); @@ -14422,7 +14430,7 @@ rebuild_function_or_method_type (tree t, resolution for Core issues 1001/1322. */ static void -maybe_rebuild_function_decl_type (tree decl) +maybe_rebuild_function_decl_type (tree decl, tree args) { bool function_type_needs_rebuilding = false; if (tree parm_list = FUNCTION_FIRST_USER_PARM (decl)) @@ -14474,7 +14482,7 @@ maybe_rebuild_function_decl_type (tree d *q = void_list_node; TREE_TYPE (decl) - = rebuild_function_or_method_type (fntype, + = rebuild_function_or_method_type (fntype, args, TREE_TYPE (fntype), new_parm_type_list, TYPE_RAISES_EXCEPTIONS (fntype), tf_none); } @@ -14657,7 +14665,7 @@ tsubst_function_decl (tree t, tree args, DECL_ARGUMENTS (r) = parms; DECL_RESULT (r) = NULL_TREE; - maybe_rebuild_function_decl_type (r); + maybe_rebuild_function_decl_type (r, args); TREE_STATIC (r) = 0; TREE_PUBLIC (r) = TREE_PUBLIC (t); @@ -15925,7 +15933,7 @@ tsubst_function_type (tree t, } /* Construct a new type node and return it. */ - return rebuild_function_or_method_type (t, return_type, arg_types, + return rebuild_function_or_method_type (t, args, return_type, arg_types, /*raises=*/NULL_TREE, complain); } --- gcc/testsuite/g++.dg/ext/attr-format4.C.jj 2024-08-01 17:44:35.492269816 +0200 +++ gcc/testsuite/g++.dg/ext/attr-format4.C 2024-08-01 17:43:27.340127120 +0200 @@ -0,0 +1,12 @@ +// PR c++/116175 +// { dg-do compile { target c++11 } } +// { dg-options "-Wformat" } + +template <typename ...T> +int foo (T ...args, const char *fmt, ...) +[[gnu::format (printf, 1 + sizeof... (T), 2 + sizeof... (T))]]; + +int a = foo <> ("%d", 1); +int b = foo <int, int, int, int, int> (1, 2, 3, 4, 5, "%d", 1); +int c = foo <> ("%f", 1); // { dg-warning "format '%f' expects argument of type 'double', but argument 2 has type 'int'" } +int d = foo <int, int, int, int, int> (1, 2, 3, 4, 5, "%f", 1); // { dg-warning "format '%f' expects argument of type 'double', but argument 7 has type 'int'" } Jakub