From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id BF6BA3858D37 for ; Wed, 6 Apr 2022 20:55:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org BF6BA3858D37 Received: from mail-qt1-f197.google.com (mail-qt1-f197.google.com [209.85.160.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-499-hRJFAe9kMbGVH8AEFIkitw-1; Wed, 06 Apr 2022 16:55:58 -0400 X-MC-Unique: hRJFAe9kMbGVH8AEFIkitw-1 Received: by mail-qt1-f197.google.com with SMTP id u8-20020ac858c8000000b002e1b3ceb167so4066276qta.10 for ; Wed, 06 Apr 2022 13:55:58 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:message-id:date:mime-version:user-agent:subject :content-language:to:references:from:in-reply-to :content-transfer-encoding; bh=fej9IzgyIG/7TacdiyZE+AePZ87xiPpL0XFfAMYSRbY=; b=tim9MVFroIbSGpbP6ACe5D+XbQuadFiQYVF+nQTqslBQexic/+PpFa53kg04VPRZ97 /ncXPqlgKWddwIGPgUUdJ6lz8Z1H1klm4eoa0v5aVwFJCP4++BC4yjpomb3v5xjF3MhQ zIu2mr9N168iXJL6Zpy6W62wYRuLX/V9WQrHMOMT3+G4+EypHH8Njnhkhs+On1ZVArJO RCiyssI2GUXgcmLWoOklQrmwwe7ZlR8nW8fhVaBnZhe/8/sVgO+FSAfNkgI36Vkvvxm/ Evve5lCmWTynJJ6oEiwRAov4jlBA8B9veZOaJd+nHVRE6lQ+LV310SBXRSFGDSj1MCEm e3aA== X-Gm-Message-State: AOAM533dul986G2mng+wLNaLXEDX1ZYZ9pE4DiaMfUxlP3JfAl8LC9dk aZeBLAVb3r/BN5B2x21SyXb2BqMbBu5HRp4n7OA2ulvFx4I37NpyBn50luZqfTo3zIweQzNvMx8 3zSKoxkIloYqAAPW96Q== X-Received: by 2002:ac8:7c54:0:b0:2e1:d4b6:b991 with SMTP id o20-20020ac87c54000000b002e1d4b6b991mr9155700qtv.134.1649278557615; Wed, 06 Apr 2022 13:55:57 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwSDElkr+i8hbblYrLBxgKJY1wP2Zv6sFaq+rU6Dd7hvv4m0QvdXTEdw/VT+bwt7XGFCXrRfg== X-Received: by 2002:ac8:7c54:0:b0:2e1:d4b6:b991 with SMTP id o20-20020ac87c54000000b002e1d4b6b991mr9155676qtv.134.1649278557064; Wed, 06 Apr 2022 13:55:57 -0700 (PDT) Received: from [192.168.1.149] (130-44-159-43.s15913.c3-0.arl-cbr1.sbo-arl.ma.cable.rcncustomer.com. [130.44.159.43]) by smtp.gmail.com with ESMTPSA id h8-20020ac87d48000000b002e1c6faae9csm14347810qtb.28.2022.04.06.13.55.55 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 06 Apr 2022 13:55:56 -0700 (PDT) Message-ID: Date: Wed, 6 Apr 2022 16:55:54 -0400 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Thunderbird/91.7.0 Subject: Re: [PATCH] c, c++: attribute format on a ctor with a vbase [PR101833, PR47634] To: Marek Polacek , Joseph Myers , GCC Patches References: <20220401191454.464924-1-polacek@redhat.com> From: Jason Merrill In-Reply-To: <20220401191454.464924-1-polacek@redhat.com> X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-28.0 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, NICE_REPLY_A, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 06 Apr 2022 20:56:02 -0000 On 4/1/22 15:14, Marek Polacek wrote: > Attribute format takes three arguments: archetype, string-index, and > first-to-check. The last two specify the position in the function > parameter list. r63030 clarified that "Since non-static C++ methods have > an implicit this argument, the arguments of such methods should be counted > from two, not one, when giving values for string-index and first-to-check." > Therefore one has to write > > struct D { > D(const char *, ...) __attribute__((format(printf, 2, 3))); > }; > > However -- and this is the problem in this PR -- ctors with virtual > bases also get two additional parameters: the in-charge parameter and > the VTT parameter (added in maybe_retrofit_in_chrg). In fact we'll end up > with two clones of the ctor: an in-charge and a not-in-charge version (see > build_cdtor_clones). That means that the argument position the user > specified in the attribute argument will refer to different arguments, > depending on which constructor we're currently dealing with. This can > cause a range of problems: wrong errors, confusing warnings, or crashes. > > This patch corrects that; for C we don't have to do anything, and in C++ > we can use num_artificial_parms_for. It would be wrong to rewrite the > attributes the user supplied, so I've added an extra parameter called > adjust_pos. > > Attribute format_arg is not affected, because it requires that the > function returns "const char *" which will never be the case for cdtors. > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > PR c++/101833 > PR c++/47634 > > gcc/c-family/ChangeLog: > > * c-attribs.cc (positional_argument): Add new argument adjust_pos, > use it. > * c-common.cc (check_function_arguments): Pass fndecl to > check_function_format. > * c-common.h (check_function_format): Adjust declaration. > (maybe_adjust_arg_pos_for_attribute): Add. > (positional_argument): Adjust declaration. > * c-format.cc (decode_format_attr): Add fndecl argument. Pass it to > maybe_adjust_arg_pos_for_attribute. Adjust calls to get_constant. I wonder about, instead of adding another parameter, allowing the current fntype parameter to be the fndecl when we have one. And then that gets passed down into positional_argument, so we can call maybe_adjust_arg_pos_for_attribute there, and adjust the return value appropriately so we don't need the extra adjustment in get_constant? > (handle_format_arg_attribute): Pass 0 to get_constant. > (get_constant): Add new argument adjust_pos, use it. > (check_function_format): Add fndecl argument. Pass it to > decode_format_attr. > (handle_format_attribute): Get the fndecl from node[2]. Pass it to > decode_format_attr. > > gcc/c/ChangeLog: > > * c-objc-common.cc (maybe_adjust_arg_pos_for_attribute): New. > > gcc/cp/ChangeLog: > > * tree.cc (maybe_adjust_arg_pos_for_attribute): New. > > gcc/testsuite/ChangeLog: > > * g++.dg/ext/attr-format-arg1.C: New test. > * g++.dg/ext/attr-format1.C: New test. > * g++.dg/ext/attr-format2.C: New test. > * g++.dg/ext/attr-format3.C: New test. > --- > gcc/c-family/c-attribs.cc | 14 ++++--- > gcc/c-family/c-common.cc | 4 +- > gcc/c-family/c-common.h | 5 ++- > gcc/c-family/c-format.cc | 46 +++++++++++++-------- > gcc/c/c-objc-common.cc | 9 ++++ > gcc/cp/tree.cc | 19 +++++++++ > gcc/testsuite/g++.dg/ext/attr-format-arg1.C | 26 ++++++++++++ > gcc/testsuite/g++.dg/ext/attr-format1.C | 32 ++++++++++++++ > gcc/testsuite/g++.dg/ext/attr-format2.C | 38 +++++++++++++++++ > gcc/testsuite/g++.dg/ext/attr-format3.C | 15 +++++++ > 10 files changed, 182 insertions(+), 26 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/ext/attr-format-arg1.C > create mode 100644 gcc/testsuite/g++.dg/ext/attr-format1.C > create mode 100644 gcc/testsuite/g++.dg/ext/attr-format2.C > create mode 100644 gcc/testsuite/g++.dg/ext/attr-format3.C > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc > index 111a33f405a..6e17847ec9e 100644 > --- a/gcc/c-family/c-attribs.cc > +++ b/gcc/c-family/c-attribs.cc > @@ -599,12 +599,15 @@ attribute_takes_identifier_p (const_tree attr_id) > matching all C integral types except bool. If successful, return > POS after default conversions, if any. Otherwise, issue appropriate > warnings and return null. A non-zero 1-based ARGNO should be passed > - in by callers only for attributes with more than one argument. */ > + in by callers only for attributes with more than one argument. > + ADJUST_POS is used and non-zero in C++ when the function type has > + invisible parameters generated by the compiler, such as the in-charge > + or VTT parameters. */ > > tree > positional_argument (const_tree fntype, const_tree atname, tree pos, > tree_code code, int argno /* = 0 */, > - int flags /* = posargflags () */) > + int flags /* = posargflags () */, int adjust_pos /* = 0 */) > { > if (pos && TREE_CODE (pos) != IDENTIFIER_NODE > && TREE_CODE (pos) != FUNCTION_DECL) > @@ -690,7 +693,7 @@ positional_argument (const_tree fntype, const_tree atname, tree pos, > if (!nargs > || !tree_fits_uhwi_p (pos) > || ((flags & POSARG_ELLIPSIS) == 0 > - && !IN_RANGE (tree_to_uhwi (pos), 1, nargs))) > + && !IN_RANGE (tree_to_uhwi (pos) + adjust_pos, 1, nargs))) > { > > if (argno < 1) > @@ -707,8 +710,9 @@ positional_argument (const_tree fntype, const_tree atname, tree pos, > } > > /* Verify that the type of the referenced formal argument matches > - the expected type. */ > - unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos); > + the expected type. Invisible parameters may have been added by > + the compiler, so adjust the position accordingly. */ > + unsigned HOST_WIDE_INT ipos = tree_to_uhwi (pos) + adjust_pos; > > /* Zero was handled above. */ > gcc_assert (ipos != 0); > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc > index d034837bb5b..6f08b55d4a7 100644 > --- a/gcc/c-family/c-common.cc > +++ b/gcc/c-family/c-common.cc > @@ -6069,8 +6069,8 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype, > /* Check for errors in format strings. */ > > if (warn_format || warn_suggest_attribute_format) > - check_function_format (fntype, TYPE_ATTRIBUTES (fntype), nargs, argarray, > - arglocs); > + check_function_format (fntype, fndecl, TYPE_ATTRIBUTES (fntype), nargs, > + argarray, arglocs); > > if (warn_format) > check_function_sentinel (fntype, nargs, argarray); > diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h > index 52a85bfb783..db6ff07db37 100644 > --- a/gcc/c-family/c-common.h > +++ b/gcc/c-family/c-common.h > @@ -857,7 +857,7 @@ extern void check_function_arguments_recurse (void (*) > opt_code); > extern bool check_builtin_function_arguments (location_t, vec, > tree, tree, int, tree *); > -extern void check_function_format (const_tree, tree, int, tree *, > +extern void check_function_format (const_tree, const_tree, tree, int, tree *, > vec *); > extern bool attribute_fallthrough_p (tree); > extern tree handle_format_attribute (tree *, tree, tree, int, bool *); > @@ -1049,6 +1049,7 @@ extern tree finish_label_address_expr (tree, location_t); > extern tree lookup_label (tree); > extern tree lookup_name (tree); > extern bool lvalue_p (const_tree); > +extern int maybe_adjust_arg_pos_for_attribute (const_tree); > > extern bool vector_targets_convertible_p (const_tree t1, const_tree t2); > extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note); > @@ -1494,7 +1495,7 @@ enum posargflags { > }; > > extern tree positional_argument (const_tree, const_tree, tree, tree_code, > - int = 0, int = posargflags ()); > + int = 0, int = posargflags (), int = 0); > > extern enum flt_eval_method > excess_precision_mode_join (enum flt_eval_method, enum flt_eval_method); > diff --git a/gcc/c-family/c-format.cc b/gcc/c-family/c-format.cc > index 98f28c0dcc6..87ae7bb73b0 100644 > --- a/gcc/c-family/c-format.cc > +++ b/gcc/c-family/c-format.cc > @@ -70,8 +70,8 @@ static GTY(()) tree local_gimple_ptr_node; > static GTY(()) tree local_cgraph_node_ptr_node; > static GTY(()) tree locus; > > -static bool decode_format_attr (const_tree, tree, tree, function_format_info *, > - bool); > +static bool decode_format_attr (const_tree, const_tree, tree, tree, > + function_format_info *, bool); > static format_type decode_format_type (const char *, bool * = NULL); > > static bool check_format_string (const_tree argument, > @@ -80,7 +80,7 @@ static bool check_format_string (const_tree argument, > int expected_format_type); > static tree get_constant (const_tree fntype, const_tree atname, tree expr, > int argno, unsigned HOST_WIDE_INT *value, > - int flags, bool validated_p); > + int flags, bool validated_p, int adjust_pos); > static const char *convert_format_name_to_system_name (const char *attr_name); > > static int first_target_format_type; > @@ -177,7 +177,7 @@ handle_format_arg_attribute (tree *node, tree atname, > unsigned HOST_WIDE_INT format_num = 0; > > if (tree val = get_constant (type, atname, *format_num_expr, 0, &format_num, > - 0, false)) > + 0, false, 0)) > *format_num_expr = val; > else > { > @@ -305,18 +305,24 @@ check_format_string (const_tree fntype, unsigned HOST_WIDE_INT format_num, > If valid, store the constant's integer value in *VALUE and return > the value. > If VALIDATED_P is true assert the validation is successful. > - Returns the converted constant value on success, null otherwise. */ > + Returns the converted constant value on success, null otherwise. > + ADJUST_POS is used and non-zero in C++ when the function type has > + invisible parameters generated by the compiler, such as the in-charge > + or VTT parameters. */ > > static tree > get_constant (const_tree fntype, const_tree atname, tree expr, int argno, > - unsigned HOST_WIDE_INT *value, int flags, bool validated_p) > + unsigned HOST_WIDE_INT *value, int flags, bool validated_p, > + int adjust_pos) > { > /* Require the referenced argument to have a string type. For targets > like Darwin, also accept pointers to struct CFString. */ > if (tree val = positional_argument (fntype, atname, expr, STRING_CST, > - argno, flags)) > + argno, flags, adjust_pos)) > { > - *value = TREE_INT_CST_LOW (val); > + /* Invisible parameters may have been added by the compiler, so adjust > + the position accordingly. */ > + *value = TREE_INT_CST_LOW (val) + adjust_pos; > return val; > } > > @@ -332,8 +338,8 @@ get_constant (const_tree fntype, const_tree atname, tree expr, int argno, > attributes are successfully decoded, false otherwise. */ > > static bool > -decode_format_attr (const_tree fntype, tree atname, tree args, > - function_format_info *info, bool validated_p) > +decode_format_attr (const_tree fntype, const_tree fndecl, tree atname, > + tree args, function_format_info *info, bool validated_p) > { > tree format_type_id = TREE_VALUE (args); > /* Note that TREE_VALUE (args) is changed in place below. Ditto > @@ -372,15 +378,19 @@ decode_format_attr (const_tree fntype, tree atname, tree args, > } > } > > + int adjust_pos = maybe_adjust_arg_pos_for_attribute (fndecl); > + > if (tree val = get_constant (fntype, atname, *format_num_expr, > - 2, &info->format_num, 0, validated_p)) > + 2, &info->format_num, 0, validated_p, > + adjust_pos)) > *format_num_expr = val; > else > return false; > > if (tree val = get_constant (fntype, atname, *first_arg_num_expr, > 3, &info->first_arg_num, > - (POSARG_ZERO | POSARG_ELLIPSIS), validated_p)) > + (POSARG_ZERO | POSARG_ELLIPSIS), validated_p, > + adjust_pos)) > *first_arg_num_expr = val; > else > return false; > @@ -1160,8 +1170,8 @@ decode_format_type (const char *s, bool *is_raw /* = NULL */) > attribute themselves. */ > > void > -check_function_format (const_tree fntype, tree attrs, int nargs, > - tree *argarray, vec *arglocs) > +check_function_format (const_tree fntype, const_tree fndecl, tree attrs, > + int nargs, tree *argarray, vec *arglocs) > { > tree a; > > @@ -1174,7 +1184,7 @@ check_function_format (const_tree fntype, tree attrs, int nargs, > { > /* Yup; check it. */ > function_format_info info; > - decode_format_attr (fntype, atname, TREE_VALUE (a), &info, > + decode_format_attr (fntype, fndecl, atname, TREE_VALUE (a), &info, > /*validated=*/true); > if (warn_format) > { > @@ -5150,10 +5160,11 @@ convert_format_name_to_system_name (const char *attr_name) > /* Handle a "format" attribute; arguments as in > struct attribute_spec.handler. */ > tree > -handle_format_attribute (tree *node, tree atname, tree args, > +handle_format_attribute (tree node[3], tree atname, tree args, > int flags, bool *no_add_attrs) > { > const_tree type = *node; > + const_tree fndecl = node[2]; > function_format_info info; > > #ifdef TARGET_FORMAT_TYPES > @@ -5179,7 +5190,8 @@ handle_format_attribute (tree *node, tree atname, tree args, > if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE) > TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args)); > > - if (!decode_format_attr (type, atname, args, &info, /* validated_p = */false)) > + if (!decode_format_attr (type, fndecl, atname, args, &info, > + /* validated_p = */false)) > { > *no_add_attrs = true; > return NULL_TREE; > diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc > index 97850ada2c8..70e10a98e33 100644 > --- a/gcc/c/c-objc-common.cc > +++ b/gcc/c/c-objc-common.cc > @@ -394,3 +394,12 @@ c_get_alias_set (tree t) > > return c_common_get_alias_set (t); > } > + > +/* In C there are no invisible parameters like in C++ (this, in-charge, VTT, > + etc.). */ > + > +int > +maybe_adjust_arg_pos_for_attribute (const_tree) > +{ > + return 0; > +} > diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc > index 780a8d89165..ddfeb8ce428 100644 > --- a/gcc/cp/tree.cc > +++ b/gcc/cp/tree.cc > @@ -6101,6 +6101,25 @@ maybe_warn_zero_as_null_pointer_constant (tree expr, location_t loc) > } > return false; > } > + > +/* FNDECL is a function declaration whose type may have been altered by > + adding extra parameters such as this, in-charge, or VTT. When this > + takes place, the positional arguments supplied by the user (as in the > + 'format' attribute arguments) may refer to the wrong argument. This > + function returns an integer indicating how many arguments should be > + skipped. */ > + > +int > +maybe_adjust_arg_pos_for_attribute (const_tree fndecl) > +{ > + if (!fndecl) > + return 0; > + int n = num_artificial_parms_for (fndecl); > + /* The manual states that it's the user's responsibility to account > + for the implicit this parameter. */ > + return n > 0 ? n - 1 : 0; > +} > + > > /* Release memory we no longer need after parsing. */ > void > diff --git a/gcc/testsuite/g++.dg/ext/attr-format-arg1.C b/gcc/testsuite/g++.dg/ext/attr-format-arg1.C > new file mode 100644 > index 00000000000..a7ad0f9ca33 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/attr-format-arg1.C > @@ -0,0 +1,26 @@ > +// PR c++/101833 > +// { dg-do compile } > +// { dg-options "-Wall" } > + > +struct B { }; > + > +struct V : virtual B { > + const char *fmt (int, const char *) __attribute__((format_arg(3))); > +}; > + > +struct D : B { > + const char *fmt (int, const char *) __attribute__((format_arg(3))); > +}; > + > +extern void fmt (const char *, ...) __attribute__((format(printf, 1, 2))); > + > +void > +g () > +{ > + V v; > + fmt (v.fmt (1, "%d"), 1); > + fmt (v.fmt (1, "%d"), 1lu); // { dg-warning "expects argument of type" } > + D d; > + fmt (d.fmt (1, "%d"), 1); > + fmt (d.fmt (1, "%d"), 1lu); // { dg-warning "expects argument of type" } > +} > diff --git a/gcc/testsuite/g++.dg/ext/attr-format1.C b/gcc/testsuite/g++.dg/ext/attr-format1.C > new file mode 100644 > index 00000000000..1b8464ed6ac > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/attr-format1.C > @@ -0,0 +1,32 @@ > +// PR c++/47634 > +// { dg-do compile } > + > +class Base { > +public: > + Base() { } > +}; > + > +class VDerived : public virtual Base { > +public: > + VDerived(int x, const char * f, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +class Derived : public Base { > +public: > + Derived(int x, const char * f, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +VDerived::VDerived(int, const char *, ...) > +{ > +} > + > +Derived::Derived(int, const char *, ...) > +{ > +} > + > +int > +main(int, char **) > +{ > + throw VDerived(1, "%s %d", "foo", 1); > + throw Derived(1, "%s %d", "bar", 1); > +} > diff --git a/gcc/testsuite/g++.dg/ext/attr-format2.C b/gcc/testsuite/g++.dg/ext/attr-format2.C > new file mode 100644 > index 00000000000..7e6eec58047 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/attr-format2.C > @@ -0,0 +1,38 @@ > +// PR c++/101833 > +// { dg-do compile } > +// { dg-options "-Wall" } > + > +struct B { }; > + > +struct V : virtual B { > + V(int, const char *, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +struct D : B { > + D(int, const char *, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +struct D2 : B { > + template > + D2(T, const char *, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +struct V2 : virtual B { > + template > + V2(T, const char *, ...) __attribute__((format(printf, 3, 4))); > +}; > + > +struct X { > + template > + X(T, ...) __attribute__((format(printf, 2, 3))); > +}; > + > +V v(1, "%s %d", "foo", 1); > +D d(1, "%s %d", "foo", 1); > +D2 d2(1, "%s %d", "foo", 1); > +V2 v2(1, "%s %d", "foo", 1); > + > +// Test that it actually works. > +V e1(1, "%d", 1L); // { dg-warning "expects argument of type" } > +D e2(1, "%d", 1L); // { dg-warning "expects argument of type" } > +X e3("%d", 1L); // { dg-warning "expects argument of type" } > diff --git a/gcc/testsuite/g++.dg/ext/attr-format3.C b/gcc/testsuite/g++.dg/ext/attr-format3.C > new file mode 100644 > index 00000000000..d6c9e40756f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/ext/attr-format3.C > @@ -0,0 +1,15 @@ > +// PR c++/101833 > +// { dg-do compile } > +// { dg-options "-Wall" } > + > +class Base {}; > + > +struct VDerived : virtual Base { > + VDerived(int, int, const char *, ...) __attribute__((format(printf, 2, 3))); // { dg-error ".format. attribute argument 2 value .2. refers to parameter type .int." } > + VDerived(int, const char *, ...) __attribute__((format(printf, 5, 6))); // { dg-warning ".format. attribute argument 2 value .5. exceeds" } > +} a(1, "%s %d", "foo", 1); > + > +struct Derived : Base { > + Derived(int, int, const char *, ...) __attribute__((format(printf, 2, 3))); // { dg-error ".format. attribute argument 2 value .2. refers to parameter type .int." } > + Derived(int, const char *, ...) __attribute__((format(printf, 5, 6))); // { dg-warning ".format. attribute argument 2 value .5. exceeds" } > +} b(1, "%s %d", "foo", 1);; > > base-commit: 95533fe4f014c10dd18de649927668aba6117daf