2008-03-25 Jakub Jelinek PR c++/35546 * pt.c (apply_late_template_attributes): Don't call tsubst on first attribute argument if it is IDENTIFIER_NODE. * g++.dg/ext/attrib33.C: New test. --- gcc/cp/pt.c.jj 2008-03-10 17:11:48.000000000 +0100 +++ gcc/cp/pt.c 2008-03-25 21:32:17.000000000 +0100 @@ -6717,9 +6717,29 @@ apply_late_template_attributes (tree *de { *p = TREE_CHAIN (t); TREE_CHAIN (t) = NULL_TREE; - TREE_VALUE (t) - = tsubst_expr (TREE_VALUE (t), args, complain, in_decl, - /*integral_constant_expression_p=*/false); + /* If the first attribute argument is an identifier, don't + pass it through tsubst. Attributes like mode, format, + cleanup and several target specific attributes expect it + unmodified. */ + if (TREE_VALUE (t) + && TREE_CODE (TREE_VALUE (t)) == TREE_LIST + && TREE_VALUE (TREE_VALUE (t)) + && (TREE_CODE (TREE_VALUE (TREE_VALUE (t))) + == IDENTIFIER_NODE)) + { + tree chain + = tsubst_expr (TREE_CHAIN (TREE_VALUE (t)), args, complain, + in_decl, + /*integral_constant_expression_p=*/false); + if (chain != TREE_CHAIN (TREE_VALUE (t))) + TREE_VALUE (t) + = tree_cons (NULL_TREE, TREE_VALUE (TREE_VALUE (t)), + chain); + } + else + TREE_VALUE (t) + = tsubst_expr (TREE_VALUE (t), args, complain, in_decl, + /*integral_constant_expression_p=*/false); *q = t; q = &TREE_CHAIN (t); } --- gcc/testsuite/g++.dg/ext/attrib33.C.jj 2008-03-25 23:05:51.000000000 +0100 +++ gcc/testsuite/g++.dg/ext/attrib33.C 2008-03-25 23:06:15.000000000 +0100 @@ -0,0 +1,18 @@ +// PR c++/35546 +// { dg-do compile } + +template +struct T +{ + void foo (char const * ...) __attribute__ ((format (printf,2,3))); +}; + +template struct T<3>; + +template +struct U +{ + typedef T __attribute__((mode (SI))) V; +}; + +U::V v;