From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id OM+rCKt6NWrHnQ8AWB0awg (envelope-from ) for ; Fri, 19 Jun 2026 13:21:47 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=gFkv7L1h; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 11D061E070; Fri, 19 Jun 2026 13:21:47 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [IPv6:2620:52:6:3111::32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 0F0F71E070 for ; Fri, 19 Jun 2026 13:21:45 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2827E4BAE7D8 for ; Fri, 19 Jun 2026 17:21:43 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2827E4BAE7D8 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=gFkv7L1h Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 156164BAE7D1 for ; Fri, 19 Jun 2026 17:21:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 156164BAE7D1 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 156164BAE7D1 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1781889672; cv=none; b=FOIOwt73kfIKM+HHoKQXrfWl9P5TA50KzP0PkPX3/yWURxhMnt9mcOnL2fMW+sxfgXyWYlus5olSKPaj0A7tigxQ1T3RefzlL6c6naKEqRzUev4OpZ3cujJ7DFqPo43UpW/156UREEmOwO/6vksOBnNA4DACO/pZ5rw5NFL3Kdo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1781889672; c=relaxed/simple; bh=l0I6fzT9Vkjf6eaxE+PxKbni9t/RiYnlIoDto6T1uxQ=; h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:From:To; b=DZU5Fm2ww2WyCkBVCam8HpawTteCChcIXzb66Ufgn1/JhrDhEnQ8dPKT5FcmUeTwLdZsHEz1j/65ZMVV6jXLV6tuec4nMTGRaQo6NlFKhFeWyvy97Ls21bvjr/TNqHsBgO69+9tTpNrhLolqq2qy8FrSRSdeJZbGHOev/ysdYm8= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=gFkv7L1h DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 156164BAE7D1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1781889671; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=SY7HLxCovjL9q++KoEasRvm4xV7fKGfjrDiDHHHlA9w=; b=gFkv7L1h5blTur1ALMCzBRWB5BpUsAuXSBbDE43ELyzBjnbuhgIHVWakQKHivvQjdiomp2 GMAtSXDyp7cVIM/VO5Q5u8bh+vO1tjqjWN4/8h/7TyinOYJPqjG4ALmKhSqPZo6A08uLKs cHN4yLtj+19BHoWz4B2dNxmiE81uoyA= Received: from mail-qt1-f199.google.com (mail-qt1-f199.google.com [209.85.160.199]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-529-coBAeMaNPiuNNDHSvV_aLA-1; Fri, 19 Jun 2026 13:21:09 -0400 X-MC-Unique: coBAeMaNPiuNNDHSvV_aLA-1 X-Mimecast-MFC-AGG-ID: coBAeMaNPiuNNDHSvV_aLA_1781889669 Received: by mail-qt1-f199.google.com with SMTP id d75a77b69052e-51a05146054so1887121cf.3 for ; Fri, 19 Jun 2026 10:21:09 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781889669; x=1782494469; h=content-transfer-encoding:in-reply-to:content-language:references :to:from:subject:user-agent:mime-version:date:message-id:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=SY7HLxCovjL9q++KoEasRvm4xV7fKGfjrDiDHHHlA9w=; b=o3Al1lcjFchHxg1Jav7nWBWPLc+RgzOk+zugw8q4+a6Z1LF5x3Mg+Fn2dTylR50XXE 1bRehViuuL251Tkgo2AU+t+HMUs562hgk+eCT9sEXPCJkKHISKLJXnMPzrMH4xZ2lC9z QSNuJU8jFwrCvkC6zErLRirtJaih0NBw7fQMGrUt9FhLJTQVuv+eZ9XJB/1XAlu/ijZ3 oQ7hkBd1aJXeWaV18TICtoyPuyckwlh3lk4l7zJts/qt5zDqdzR/OoNuxqcCTkT8y5f+ PMi7ckyLBC9kwncMyNXtbEp+QNes4cwPLGhYnjPjSfH7Uhf7fFbKY07+5eCo0YGXJlvm p7GQ== X-Gm-Message-State: AOJu0YzMSLPTVeekNsQb9JfjVNqXg1ZTWYqpcta4tqpc4IWAP3Rm0em7 ySvX7gfE+GAhRkyDkSSpNOYVNwIu68GCN2ywwtvDQh7amjnpUtejbm8agjt8VJ0S47TSt4MKgrs 6hejBDS7M1qyMG0V+6rqO6sRq+dU1UWOTehTXznIJF73Fcg9czxXh5A0QU2cRIC6/upZDqKSJfD GdXmGwHx8matF188QFZFsGwe++MhSEh/pXLOT81h0gc12a X-Gm-Gg: AfdE7cmASUvz+RSWmo+29Eq+iITEllIjyLp81xGDFlH5Acy5OzVFuK6euYpG7dU7k45 nHQwIgGIco4OMpt0XxvgTnbYcsXCM1ZlvU9mIEYxp9z8VVWCEZ9+h60JVH/D2uITLotBy2+DImr rbco8zB/lMOIEC/N3rnAv2gxjJnBSD6zbQW/C+JjAU4TvWsbY247S3javpK5SjC8cjUc/hrwoQA mJPldSryajTnz4GtZxGeQhS3EeV3nQFMNp2TGAtSFAMSPNMDXhpnXws+woFIzMUoVI7AfiaPQvQ GcytStHZvN4R3tWmZk/zNH7FrUQ38yAVfjrZxxyhuz754vPm6lXGFwCd0I6WzE4/lMAoWSAbFkv ssrlnRaGCZCszOZkhSg== X-Received: by 2002:a05:622a:420d:b0:517:61d5:2f7b with SMTP id d75a77b69052e-519f06520cfmr44515981cf.56.1781889667935; Fri, 19 Jun 2026 10:21:07 -0700 (PDT) X-Received: by 2002:a05:622a:420d:b0:517:61d5:2f7b with SMTP id d75a77b69052e-519f06520cfmr44514771cf.56.1781889666635; Fri, 19 Jun 2026 10:21:06 -0700 (PDT) Received: from [150.1.200.157] ([172.56.105.224]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-519e60ee870sm26205751cf.20.2026.06.19.10.21.05 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Fri, 19 Jun 2026 10:21:06 -0700 (PDT) Message-ID: Date: Fri, 19 Jun 2026 10:21:04 -0700 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH v2] Add infcall support for C++ constructor-style expressions From: Keith Seitz To: gdb-patches@sourceware.org References: In-Reply-To: X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: aVLDz1mzr6ogLxrFQc2FOK0W3pwJQmPEPH0AElkOxAY_1781889669 X-Mimecast-Originator: redhat.com Content-Language: en-US Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Ping. On 4/27/26 9:43 AM, Keith Seitz wrote: > Changes in v2: > - Formatting fixes > - Use c_isspace to skip whitespace in typename_token_for() > - Merged rules for typename_for_ctor to type_exp > [This adds ~20 shift/reduce conflicts.] > - Updated error message when overload-resolution is off > - Handle static constructors and add new tests for this case > - Added test case for when ctor is not found > - Test early return: remove integer return status > > Thanks, > Keith > > --------- > > This patch adds an initial try at teaching the expression parser/evaluator > to construct temporary objects requiring construction during an inferior > function call. > > To accomplish this, I've chosen the route of modifying the parser to > teach it that `Type(args)' is a function call when `Type' names a > class/struct/union and is immediately followed by '(', that is, via look- > ahead). A new parser token and grammar rule have been added to deal > with this new production. > > The real work is dispatched to `type_operation::evaluate_funcall', > allocating memory for the temporary and finding the most suitable constructor > with `find_overload_match'. It then runs the inferior call, returning > the newly constructed object. > > I've included many tests covering as many corner cases as I could invent, > and these tests are clang clean. They also introduce no regressions on > x86-64 Fedora 43 with GCC 15.2.1 and RHEL 9.4 with GCC 11.5.0. > > Example: > Consider a C++ frame where 'struct S { int x; S(int); ... }' is in scope > > Before: > (gdb) print S(42) > ❌️ A syntax error in expression, near `10)'. > > After: > (gdb) print S(42) > $1 = {x = 42} > > Note that no attempt has been made to deal with templates. Hopefully > a follow-on patch can address that. > --- > gdb/NEWS | 5 + > gdb/c-exp.y | 85 +++++++- > gdb/eval.c | 60 ++++++ > gdb/expop.h | 5 + > gdb/testsuite/gdb.cp/infcall-ctors.cc | 141 +++++++++++++ > gdb/testsuite/gdb.cp/infcall-ctors.exp | 198 +++++++++++++++++++ > gdb/testsuite/gdb.cp/static-ctor-infcall.c | 33 ++++ > gdb/testsuite/gdb.cp/static-ctor-infcall.exp | 87 ++++++++ > 8 files changed, 605 insertions(+), 9 deletions(-) > create mode 100644 gdb/testsuite/gdb.cp/infcall-ctors.cc > create mode 100644 gdb/testsuite/gdb.cp/infcall-ctors.exp > create mode 100644 gdb/testsuite/gdb.cp/static-ctor-infcall.c > create mode 100644 gdb/testsuite/gdb.cp/static-ctor-infcall.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index e233906153a..24d26d29ea5 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -75,6 +75,11 @@ > * The Windows native target now supports scheduler-locking. E.g., > "set scheduler-locking on" now works. Previously it gave an error. > > +* In C++ GDB now accepts constructor-style expressions "TYPE (ARGS)" > + when TYPE names a class, struct, or union in the current expression > + context. This allows objects to be constructed directly during > + expression evaluation. > + > * New targets > > GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux* > diff --git a/gdb/c-exp.y b/gdb/c-exp.y > index 2829d8bccba..73f88bd84f1 100644 > --- a/gdb/c-exp.y > +++ b/gdb/c-exp.y > @@ -231,7 +231,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value); > %token NAME /* BLOCKNAME defined below to give it higher precedence. */ > %token UNKNOWN_CPP_NAME > %token COMPLETE > -%token TYPENAME > +%token TYPENAME TYPENAME_CTOR > %token CLASSNAME /* ObjC Class name */ > %type name > %type qual_field_name field_name field_name_or_complete > @@ -314,6 +314,13 @@ type_exp: type > { > pstate->push_new ($1); > } > + | TYPENAME_CTOR > + { > + /* Constructor-style call: lexer returns TYPENAME_CTOR > + only when '(' follows the type name (see > + typename_token_for). */ > + pstate->push_new ($1.type); > + } > | TYPEOF '(' exp ')' > { > pstate->wrap (); > @@ -534,6 +541,26 @@ msgarg : name ':' exp > { add_msglist(0, 0); } > ; > > +exp : type_exp '(' > + { pstate->start_arglist (); } > + arglist ')' %prec ARROW > + { > + std::vector args > + = pstate->pop_vector (pstate->end_arglist ()); > + operation_up type_op = pstate->pop (); > + pstate->push_new > + (std::move (type_op), std::move (args)); > + } > + ; > + > +exp : type_exp '(' ')' %prec ARROW > + { > + operation_up type_op = pstate->pop (); > + pstate->push_new > + (std::move (type_op), std::vector ()); > + } > + ; > + > exp : exp '(' > /* This is to save the value of arglist_len > being accumulated by an outer function call. */ > @@ -3118,6 +3145,34 @@ static int popping; > built up. */ > static auto_obstack name_obstack; > > +/* Return TYPENAME_CTOR only when the next token is '(', so this > + token is used solely for constructor calls. Otherwise return TYPENAME. > + NAME_END is the character just past the name (e.g. yylval.sval.ptr + > + yylval.sval.length). */ > + > +static int > +typename_token_for (struct parser_state *par_state, struct type *type, > + const char *name_end) > +{ > + if (type == nullptr > + || par_state->language ()->la_language != language_cplus) > + return TYPENAME; > + type = check_typedef (type); > + if (type->code () != TYPE_CODE_STRUCT && type->code () != TYPE_CODE_UNION) > + return TYPENAME; > + /* Only return TYPENAME_CTOR when followed by '('. */ > + if (name_end == nullptr) > + return TYPENAME; > + { > + const char *p = name_end; > + while (c_isspace (*p)) > + ++p; > + if (*p != '(') > + return TYPENAME; > + } > + return TYPENAME_CTOR; > +} > + > /* Classify a NAME token. The contents of the token are in `yylval'. > Updates yylval and returns the new token type. BLOCK is the block > in which lookups start; this can be NULL to mean the global scope. > @@ -3161,7 +3216,8 @@ classify_name (struct parser_state *par_state, const struct block *block, > if (bsym.symbol != NULL) > { > yylval.tsym.type = bsym.symbol->type (); > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > + yylval.sval.ptr + yylval.sval.length); > } > } > > @@ -3188,7 +3244,8 @@ classify_name (struct parser_state *par_state, const struct block *block, > if (bsym.symbol && bsym.symbol->loc_class () == LOC_TYPEDEF) > { > yylval.tsym.type = bsym.symbol->type (); > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > + yylval.sval.ptr + yylval.sval.length); > } > > /* See if it's an ObjC classname. */ > @@ -3274,7 +3331,9 @@ classify_inner_name (struct parser_state *par_state, > if (base_type != NULL) > { > yylval.tsym.type = base_type; > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > + yylval.ssym.stoken.ptr > + + yylval.ssym.stoken.length); > } > > return ERROR; > @@ -3294,14 +3353,18 @@ classify_inner_name (struct parser_state *par_state, > if (base_type != NULL) > { > yylval.tsym.type = base_type; > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > + yylval.ssym.stoken.ptr > + + yylval.ssym.stoken.length); > } > } > return ERROR; > > case LOC_TYPEDEF: > yylval.tsym.type = yylval.ssym.sym.symbol->type (); > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > + yylval.ssym.stoken.ptr > + + yylval.ssym.stoken.length); > > default: > return NAME; > @@ -3336,7 +3399,7 @@ handle_qualified_field_name (qualified_name_token token) > int kind = classify_inner_name (pstate, > pstate->expression_context_block, > type); > - if (kind != TYPENAME) > + if (kind != TYPENAME && kind != TYPENAME_CTOR) > error (_("could not find type '%s'"), accum.c_str ()); > > type = yylval.tsym.type; > @@ -3388,7 +3451,8 @@ yylex (void) > current.token = classify_name (pstate, pstate->expression_context_block, > is_quoted_name, last_lex_was_structop); > if (pstate->language ()->la_language != language_cplus > - || (current.token != TYPENAME && current.token != COLONCOLON > + || (current.token != TYPENAME && current.token != TYPENAME_CTOR > + && current.token != COLONCOLON > && current.token != FILENAME > && (cpstate->assume_classification == TYPE_CODE_UNDEF > || current.token != NAME)) > @@ -3434,6 +3498,7 @@ yylex (void) > else > { > gdb_assert (current.token == TYPENAME > + || current.token == TYPENAME_CTOR > || cpstate->assume_classification != TYPE_CODE_UNDEF); > search_block = pstate->expression_context_block; > obstack_grow (&name_obstack, current.value.sval.ptr, > @@ -3464,7 +3529,8 @@ yylex (void) > context_type); > /* We keep going until we either run out of names, or until > we have a qualified name which is not a type. */ > - if (classification != TYPENAME && classification != NAME) > + if (classification != TYPENAME && classification != TYPENAME_CTOR > + && classification != NAME) > break; > > /* Accept up to this token. */ > @@ -3595,6 +3661,7 @@ c_print_token (FILE *file, int type, YYSTYPE value) > break; > > case TYPENAME: > + case TYPENAME_CTOR: > parser_fprintf (file, "tsym", > value.tsym.type->safe_name (), > copy_name (value.tsym.stoken).c_str ()); > diff --git a/gdb/eval.c b/gdb/eval.c > index a00774bda3d..933415ce028 100644 > --- a/gdb/eval.c > +++ b/gdb/eval.c > @@ -1872,6 +1872,66 @@ type_operation::evaluate (struct type *expect_type, struct expression *exp, > error (_("Attempt to use a type name as an expression")); > } > > +value * > +type_operation::evaluate_funcall (struct type *expect_type, > + struct expression *exp, > + enum noside noside, > + const std::vector &args) > +{ > + struct type *type = std::get<0> (m_storage); > + type = check_typedef (type); > + > + /* Constructor-style call Type(args) is only for C++ aggregate types. */ > + gdb_assert (exp->language_defn->la_language == language_cplus); > + > + const char *name = type->name (); > + if (name == nullptr) > + error (_("Cannot call constructor of unnamed type")); > + > + /* Get the constructor name from the type name. */ > + gdb::unique_xmalloc_ptr ctor_name_ptr = cp_func_name (name); > + const char *ctor_name = > + (ctor_name_ptr != nullptr) ? ctor_name_ptr.get () : name; > + > + if (!overload_resolution) > + error (_("Constructor calls require 'overload-resolution' to be on")); > + > + std::vector argvec (1 + args.size ()); > + value *this_ptr; > + if (noside == EVAL_AVOID_SIDE_EFFECTS) > + this_ptr = value::zero (lookup_pointer_type (type), lval_memory); > + else > + { > + value *alloc_val = value_allocate_space_in_inferior (type->length ()); > + this_ptr = value_from_pointer (lookup_pointer_type (type), > + value_as_long (alloc_val)); > + } > + argvec[0] = this_ptr; > + for (size_t i = 0; i < args.size (); ++i) > + argvec[i + 1] = args[i]->evaluate_with_coercion (exp, noside); > + gdb::array_view arg_view = argvec; > + > + value *callee = nullptr; > + int static_memfuncp; > + find_overload_match (arg_view, ctor_name, METHOD, > + &argvec[0], nullptr, &callee, nullptr, > + &static_memfuncp, 0, noside); > + /* If find_overload_match fails, it will have already thrown an error. */ > + gdb_assert (callee != nullptr); > + > + /* C++ constructors are methods. If the found method is marked static, > + treat it as an explicit failure until we have a real case. */ > + if (static_memfuncp) > + error (_("Constructor %s is unexpectedly marked static"), name); > + > + if (noside == EVAL_AVOID_SIDE_EFFECTS) > + return value::zero (type, not_lval); > + > + evaluate_subexp_do_call (exp, noside, callee, arg_view, > + nullptr, expect_type); > + return value_ind (this_ptr); > +} > + > } > > /* A helper function for BINOP_ASSIGN_MODIFY. */ > diff --git a/gdb/expop.h b/gdb/expop.h > index c58a8d7ac37..9839e3bffa9 100644 > --- a/gdb/expop.h > +++ b/gdb/expop.h > @@ -1597,6 +1597,11 @@ class type_operation > struct expression *exp, > enum noside noside) override; > > + value *evaluate_funcall (struct type *expect_type, > + struct expression *exp, > + enum noside noside, > + const std::vector &args) override; > + > enum exp_opcode opcode () const override > { return OP_TYPE; } > > diff --git a/gdb/testsuite/gdb.cp/infcall-ctors.cc b/gdb/testsuite/gdb.cp/infcall-ctors.cc > new file mode 100644 > index 00000000000..5177f5de5da > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/infcall-ctors.cc > @@ -0,0 +1,141 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright (C) 2026 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +struct S > +{ > + int x; > + explicit S (int n = 0) : x (n) {} > + S operator+ (int n) const > + { > + return S (x + n); > + } > +}; > + > +typedef S S_td; > +using S_u = S; > + > +static int > +add (const struct S &s1, const struct S &s2) > +{ > + return s1.x + s2.x; > +} > + > +/* Non-trivially copyable: copy-constructing swaps the two members. */ > +struct swapcopy > +{ > + int lo; > + int hi; > + swapcopy (int l, int h) : lo (l), hi (h) {} > + swapcopy (const swapcopy &o) : lo (o.hi), hi (o.lo) {} > +}; > + > +/* Pass swapcopy by value so the call must copy-construct the argument. */ > + > +static int > +swapcopy_first_byval (swapcopy c) > +{ > + return c.lo; > +} > + > +struct Base > +{ > + int x; > + Base () : x (0) {} > + explicit Base (int n) : x (n) {} > + Base (const Base &other) : x (other.x) {} > +}; > + > +typedef Base Base_td; > +using Base_u = Base; > + > +namespace NS > +{ > +class Derived : public Base > +{ > +public: > + int y; > + Derived () : Base (), y (0) {} > + Derived (int a, int b) : Base (a), y (b) {} > +}; > + > +typedef Base NsBaseTd; > +using NsBaseU = Base; > +typedef Derived Derived_td; > +using Derived_u = Derived; > + > +union U > +{ > + int a; > + U () : a (0) {} > + explicit U (int n) : a (n) {} > +}; > + > +typedef U Nu_td; > +} > + > +union U > +{ > + int x; > + U () : x (0) {} > + explicit U (int n) : x (n) {} > +}; > + > +typedef U U_td; > + > +static int > +plus_one (U u) > +{ > + return u.x + 1; > +} > + > +int > +main (void) > +{ > + S s0; /* default: x = 0 */ > + S s1 (42); /* x = 42 */ > + S s2 (s1 + 2); /* x = 44 */ > + S s3 = s1; > + NS::Derived d; /* Base part x=0, Derived part y=0 */ > + NS::Derived d1 (10, 20); /* Base part x=10, Derived part y=20 */ > + Base b; /* x=0 */ > + Base b1 (5); /* x=5 */ > + Base b2 (d1); /* x=10 */ > + U u0; /* default: x = 0 */ > + U u1 (42); /* x = 42 */ > + NS::U uv0; /* default: a = 0 */ > + NS::U uv1 (7); /* a = 7 */ > + S_td s_td = S_td (11); > + S_u s_u = S_u (12); > + Base_td b_td = Base_td (8); > + Base_u b_u = Base_u (9); > + NS::NsBaseTd nsb_td = NS::NsBaseTd (13); > + NS::NsBaseU nsb_u = NS::NsBaseU (14); > + NS::Derived_td d_td = NS::Derived_td (2, 3); > + NS::Derived_u d_u = NS::Derived_u (4, 5); > + U_td u_td = U_td (15); > + NS::Nu_td nu_u = NS::Nu_td (16); > + swapcopy swp (30, 40); > + int result = add (s1, s2); > + return result + d.x + d.y + b.x + b1.x + b2.x + d.x + d.y \ > + + d1.x + d1.y + plus_one (u0) + u1.x + uv0.a + uv1.a \ > + + s_td.x + s_u.x + b_td.x + b_u.x + nsb_td.x + nsb_u.x \ > + + d_td.x + d_td.y + d_u.x + d_u.y + u_td.x + nu_u.a \ > + + swapcopy_first_byval (swapcopy (100, 200)) \ > + + swapcopy_first_byval (swp); /* stop-here */ > +} > diff --git a/gdb/testsuite/gdb.cp/infcall-ctors.exp b/gdb/testsuite/gdb.cp/infcall-ctors.exp > new file mode 100644 > index 00000000000..2a92266ccfe > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/infcall-ctors.exp > @@ -0,0 +1,198 @@ > +# Copyright (C) 2026 Free Software Foundation, Inc. > + > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > + > +# This file is part of the gdb testsuite. > + > +# Test constructor calls and casting via inferior function calls: > +# - Simple struct S with constructor (int, default 0). > +# - Base and Derived; cast derived to base, construct Base from Derived. > +# - Typedef and using aliases: ctor resolution must use the class ctor even > +# when the expression names a typedef or alias (DWARF may name types > +# differently from the underlying class tag). > +# - swapcopy: two ints with a user-defined copy ctor that swaps them > +# (non-trivially copyable); pass-by-value in inferior calls must run it. > +# - Constructor-style calls require overload resolution; verify the error > +# when it is disabled. > + > +require allow_cplus_tests > + > +standard_testfile .cc > + > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} { > + return > +} > + > +if {![runto_main]} { > + return > +} > + > +# Run to stop-here to ensure all locals are initialized. > +gdb_breakpoint [gdb_get_line_number "stop-here"] > +gdb_continue_to_breakpoint "stop-here" > + > +# Simple tests involving "struct S". > +gdb_test "ptype S" [multi_line \ > + {type = struct S \{} \ > + { int x;} \ > + "" \ > + { S\(int\);} \ > + { S operator\+\(int\) const;} \ > + {\}}] > + > +gdb_test "print s0" " = \\{x = 0\\}" "print s0 default ctor" > +gdb_test "print s1" " = \\{x = 42\\}" "print s1 with 42" > +gdb_test "print S(99)" " = \\{x = 99\\}" "construct S(99) via inferior function call" > + > +gdb_test "ptype swapcopy" [multi_line \ > + {type = struct swapcopy \{} \ > + { int lo;} \ > + { int hi;} \ > + "" \ > + { swapcopy\(int, int\);} \ > + { swapcopy\(const swapcopy ?&\);} \ > + {\}}] > +gdb_test "print swapcopy(1, 2)" { = \{lo = 1, hi = 2\}} \ > + "construct swapcopy via inferior function call" > +gdb_test {print swapcopy($)} {= \{lo = 2, hi = 1\}} \ > + "call copy ctor on object via history" > +gdb_test "print swapcopy_first_byval(swapcopy(10, 20))" "= 20" \ > + "pass-by-value copy ctor swaps members" > +gdb_test "print swapcopy_first_byval(swp)" "= 40" \ > + "swapcopy local passed by value uses copy ctor" > + > +gdb_test "print S_td(33)" " = \\{x = 33\\}" \ > + "construct S via typedef name (not underlying struct tag)" > +gdb_test "print S_u(34)" " = \\{x = 34\\}" "construct S via using alias" > +gdb_test "print Base_td(8)" { = \{x = 8\}} "construct Base via typedef name" > +gdb_test "print Base_u(9)" { = \{x = 9\}} "construct Base via using alias" > +gdb_test "print NS::NsBaseTd(5)" { = \{x = 5\}} \ > + "construct Base via typedef in namespace" > +gdb_test "print NS::NsBaseU(6)" { = \{x = 6\}} \ > + "construct Base via using alias in namespace" > +gdb_test "print NS::Derived_td(1, 2)" { = \{ = \{x = 1\}, y = 2\}} \ > + "construct NS::Derived via typedef name" > +gdb_test "print NS::Derived_u(3, 4)" { = \{ = \{x = 3\}, y = 4\}} \ > + "construct NS::Derived via using alias" > +gdb_test "print U_td(55)" { = \{x = 55\}} \ > + "construct global union U via typedef name" > +gdb_test "print NS::Nu_td(66)" { = \{a = 66\}} \ > + "construct NS::U via typedef name" > + > +# Tests involving "Base" and "Derived". > +set base_re [multi_line \ > + {type = struct Base \{} \ > + { int x;} \ > + "" \ > + { Base\(void\);} \ > + { Base\(int\);} \ > + { Base\(const Base ?&\);} \ > + {\}}] > +gdb_test "ptype Base" $base_re > + > +set derived_re [multi_line \ > + {type = class NS::Derived : public Base \{} \ > + { public:} \ > + { int y;} \ > + "" \ > + { Derived\(void\);} \ > + { Derived\(int, int\);} \ > + {\}}] > +gdb_test "ptype NS::Derived" $derived_re > + > +gdb_test "print d" { = \{ = \{x = 0\}, y = 0\}} > +gdb_test "print d1" { = \{ = \{x = 10\}, y = 20\}} > +gdb_test "print b" { = \{x = 0\}} > +gdb_test "print b1" { = \{x = 5\}} > +gdb_test "print b2" { = \{x = 10\}} > +gdb_test "print (Base)(d1)" { = \{x = 10\}} "cast (Base)(d1) slices to Base" > +gdb_test "print Base(d1)" { = \{x = 10\}} "construct Base(d1) from Derived" > +gdb_test "print Base()" { = \{x = 0\}} "construct Base() default" > +gdb_test "print Base(7)" { = \{x = 7\}} "construct Base(7) with argument" > +gdb_test "print NS::Derived()" { = \{ = \{x = 0\}, y = 0\}} \ > + "construct NS::Derived() via inferior function call" > +gdb_test "print NS::Derived().y" " = 0" "construct NS::Derived() and access .y" > +gdb_test "print NS::Derived(1, 2)" { = \{ = \{x = 1\}, y = 2\}} \ > + "construct NS::Derived(1, 2) via inferior function call" > +gdb_test "print NS::Derived(1, 2).y" " = 2" \ > + "construct NS::Derived(1, 2) and access .y" > +gdb_test "print ((Base)d1).x" " = 10" "cast ((Base)d1).x" > + > +# Print the types of these "temporary" objects. > +gdb_test "ptype Base()" $base_re "ptype of Base temporary" > +gdb_test "ptype NS::Derived(15, 25)" $derived_re \ > + "ptype of NS::Derived temporary" > + > +# Tests involving unions. > +gdb_test "ptype U" [multi_line \ > + {type = union U \{} \ > + { int x;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] \ > + "ptype U" > + > +gdb_test "ptype NS::U" [multi_line \ > + {type = union NS::U \{} \ > + { int a;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] \ > + "ptype NS::U" > + > +gdb_test "print u0" { = \{x = 0\}} > +gdb_test "print u1" { = \{x = 42\}} > +gdb_test "print uv0" { = \{a = 0\}} > +gdb_test "print uv1" { = \{a = 7\}} > +gdb_test "print U()" { = \{x = 0\}} "construct U() via inferior function call" > +gdb_test "print U(99)" { = \{x = 99\}} "construct U(99) via inferior function call" > +gdb_test "print U(99).x" " = 99" "construct U(99) and access .x" > +gdb_test "print NS::U()" { = \{a = 0\}} \ > + "construct NS::U() via inferior function call" > +gdb_test "print NS::U(13)" { = \{a = 13\}} \ > + "construct NS::U(13) via inferior function call" > +gdb_test "print NS::U(13).a" " = 13" \ > + "construct NS::U(13) and access .a" > + > +set u_re [multi_line \ > + {type = union U \{} \ > + { int x;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] > +set ns_u_re [multi_line \ > + {type = union NS::U \{} \ > + { int a;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] > +gdb_test "ptype U()" $u_re "ptype of U temporary" > +gdb_test "ptype NS::U(99)" $ns_u_re "ptype of NS::U temporary" > + > +gdb_test "p plus_one(U(42))" "= 43" "temporary in function call" > +gdb_test "p add(S(1), S(20))" "= 21" "add two temporaries of S" > +gdb_test "p plus_one(U(add (S(4), S(6))))" "= 11" \ > + "nested function call using temporaries" > + > +gdb_test_no_output "set overload-resolution off" > +gdb_test "print S(1)" \ > + {Constructor calls require 'overload-resolution' to be on} \ > + "constructor call fails when overload-resolution is off" > +gdb_test_no_output "set overload-resolution on" > +gdb_test "print S(1)" " = \\{x = 1\\}" \ > + "constructor call works after overload-resolution turned back on" > diff --git a/gdb/testsuite/gdb.cp/static-ctor-infcall.c b/gdb/testsuite/gdb.cp/static-ctor-infcall.c > new file mode 100644 > index 00000000000..0516196e6d8 > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/static-ctor-infcall.c > @@ -0,0 +1,33 @@ > +/* This testcase is part of GDB, the GNU debugger. > + > + Copyright 2026 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + This program is free software; you can redistribute it and/or modify > + it under the terms of the GNU General Public License as published by > + the Free Software Foundation; either version 3 of the License, or > + (at your option) any later version. > + > + This program is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + GNU General Public License for more details. > + > + You should have received a copy of the GNU General Public License > + along with this program. If not, see . */ > + > +/* Address range for hand-written DWARF describing a static constructor. */ > + > +extern "C" void > +dwarf_static_ctor_stub (void) > +{ > + asm ("dwarf_static_ctor_stub_label: .globl dwarf_static_ctor_stub_label"); > +} > + > +int > +main (void) > +{ > + asm ("main_label: .globl main_label"); > + return 0; > +} > diff --git a/gdb/testsuite/gdb.cp/static-ctor-infcall.exp b/gdb/testsuite/gdb.cp/static-ctor-infcall.exp > new file mode 100644 > index 00000000000..cdc2db170d6 > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/static-ctor-infcall.exp > @@ -0,0 +1,87 @@ > +# Copyright 2026 Free Software Foundation, Inc. > +# > +# This program is free software; you can redistribute it and/or modify > +# it under the terms of the GNU General Public License as published by > +# the Free Software Foundation; either version 3 of the License, or > +# (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program. If not, see . > +# > +# Exercise the "Constructor %s is unexpectedly marked static" error in > +# type_operation::evaluate_funcall. > + > +load_lib dwarf.exp > + > +require dwarf2_support > +require allow_cplus_tests > + > +standard_testfile .c -dw.S > + > +set asm_file [standard_output_file $srcfile2] > +Dwarf::assemble $asm_file { > + declare_labels int_label struct_label ptr_label ctor_subr_label > + > + get_func_info dwarf_static_ctor_stub {nodebug c++} > + > + cu {} { > + compile_unit { > + DW_AT_language @DW_LANG_C_plus_plus > + DW_AT_name dw2-static-ctor-infcall.cc > + DW_AT_comp_dir /tmp > + } { > + int_label: base_type { > + DW_AT_name int > + DW_AT_encoding @DW_ATE_signed > + DW_AT_byte_size 4 DW_FORM_sdata > + } > + > + struct_label: structure_type { > + DW_AT_name dwarf_static_ctor > + DW_AT_byte_size 4 DW_FORM_sdata > + } { > + member { > + DW_AT_name x > + DW_AT_type :$int_label > + DW_AT_data_member_location 0 data1 > + } > + ptr_label: pointer_type { > + DW_AT_type :$struct_label > + } > + ctor_subr_label: subroutine_type { > + DW_AT_type :$int_label > + } { > + formal_parameter { > + DW_AT_type :$ptr_label > + } > + } > + subprogram { > + DW_AT_name dwarf_static_ctor > + DW_AT_external 1 flag > + DW_AT_low_pc $dwarf_static_ctor_stub_start addr > + DW_AT_high_pc $dwarf_static_ctor_stub_end addr > + DW_AT_type :$ctor_subr_label > + } > + } > + } > + } > +} > + > +if {[prepare_for_testing "failed to prepare" ${testfile} \ > + [list $srcfile $asm_file] {nodebug c++}]} { > + return > +} > + > +if {![runto_main]} { > + return > +} > + > +gdb_test_no_output "set language c++" > + > +gdb_test "print dwarf_static_ctor()" \ > + "Constructor dwarf_static_ctor is unexpectedly marked static" > > base-commit: 8e78d43dbcd122e852d59abe29c8d6aeb6c04925