From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id u3QCLMF+52mWVjAAWB0awg (envelope-from ) for ; Tue, 21 Apr 2026 09:42:25 -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=WwgkcCoT; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id A20441E067; Tue, 21 Apr 2026 09:42:25 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.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,RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.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 79D291E067 for ; Tue, 21 Apr 2026 09:42:23 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 0560C4B9DB72 for ; Tue, 21 Apr 2026 13:42:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0560C4B9DB72 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=WwgkcCoT 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 78C674BA900E for ; Tue, 21 Apr 2026 13:41:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 78C674BA900E 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 78C674BA900E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776778912; cv=none; b=c/tvR1ijtKEqVfeAz0tmLCGRvZ/BuKYo5gY3+VNW/sTF3mwEPYWRF28MVWSB8UweVd+s+wDEYUtxI2yTjUENxD+gp0q63JYjPnQsudjsy9+EneyC+iauIQ6YnaU9mrNBtMDpGjgq6oFAiOQTcTtoeGomimt7JxUP7vtHUi6MJbQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776778912; c=relaxed/simple; bh=/5X10sWU3NOJdbY3iFW9YgpQ6DXl0Igw7C4ZhQKqMw8=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=ODGAr4VCxGOWMR1fo//zVlx2EicUBCiF30JoELpDVxFRADtURLY8535AYFQCTojtUreL311g2Fkide0alkam2l5G9J83muDBrJvsVlX1bbU6bwBM8IMcO2X0LnzecRqzt3VWTsVG/3TlfMOhPguFPy7/XUYqI8hs5wSf76/EyBU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 78C674BA900E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1776778912; 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=CfPoMZ0FxQGfwZH0JCRZ6GXJuOzNBb4YNG7ElD7Zyqo=; b=WwgkcCoTLWjkMyw4t4tcPFbtbfVEGioTh6+vJvhvqpg4iyNRPsBnsm0Lly4IoL+Nnu9j7g pAdl8AOUghqfocGG7V674cqzvj1Yu9ZOgAnTtQLMWm71e7ly5CjbD1HZUy0BEUzD/tiBPB aWWOdmh2lz/kCMafXW4j8GPQGUNZk4Y= Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-543-5_1phzRSOKKg8sZmzkKLGw-1; Tue, 21 Apr 2026 09:41:49 -0400 X-MC-Unique: 5_1phzRSOKKg8sZmzkKLGw-1 X-Mimecast-MFC-AGG-ID: 5_1phzRSOKKg8sZmzkKLGw_1776778908 Received: by mail-wm1-f72.google.com with SMTP id 5b1f17b1804b1-488d3eec9bcso27735755e9.3 for ; Tue, 21 Apr 2026 06:41:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1776778908; x=1777383708; h=content-transfer-encoding:mime-version:message-id:date:references :in-reply-to:subject:to:from:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=kALI6JXWKB6+vuHandHTqusQCt6nrM7rdWQDxiBJBe0=; b=ryphzZg+/C7PwG0MZC37ZS5pU0eEbcCBeYqhU0Xvv4npjbXWfUOvUguAejKWEPHLnD AeLcgKLCqPYDctWCQjIVlpX87pYGbB+PI49diVUozUmhSxXBq7RaFHGuRy2TgcVlYAau PLey0jb9GeSlta0w8RpzCbgJxaLc+gKCOpG2uR550XbX9sVRTDfHVP6Pg0rpuyaIgE4C HpD776vjxn2aHF1rnRzCYRc8T83kKF2INqWMvXCZjzVraA2/TwI3Bn7K3mVQ+Ri0H/D+ w35o2h7JHkpoEu3lP0R8nD80vQMjG9Iyz+AsbGl74om63TQmCR0+3thTczuEJ7OUX3fM uvPQ== X-Forwarded-Encrypted: i=1; AFNElJ+/Po3ejjRLSOqYfcDLTqEs1UNpECn/3HMqBlKcHTdg3UAX6HazQLbHfKDtK9hnMaoL7vhpgPG4vIo4tQ==@sourceware.org X-Gm-Message-State: AOJu0Yxe0TTV0k3TGxmm9N+i/scRKKgfhXsVsHa5tpkseP9e+nEQwBme +9UN/TPvqq0Xo/nwU1KcAE49x6L1RWxzVGex2j4SLaWWq7I8Dt2AnACPPNQduwOh6/SiDO9dAvJ x14L/eaoRrbki9CSIyq9Ynn2VfBfLwB/MQQj22ef2qV7okjOIEVjdVaImuxVeWzZSoIg/jHU= X-Gm-Gg: AeBDieucMnyHTd1x9haCdYpMxztNFwyawWdmeLTVniu7HMRZoqg1r5zhDlQ0IQMiUAB aHDGQYC1ZxuBz4GodZkMshc1TaZTRVmEwbg7LZ0PYhGQxOublemSavqBh8+5RMUcyKx2LhbfVP/ zHtqoWcWIB0vdZ98xG7Nau5Xkhj5pHBJyHRR/5EEtSQxg4lKKMIXxAUu4hiTndlYR+4GlgTYh1i rMK56/tj/HJvBs1iGEuBH8kRtEgaProZa3R1AYRo8Y1a9jwtNHAs8PjG7kqjGU26RHeXCYTiuzY SKYrRWG8NB+fPspVyHokirfj6dIq+/Oc3UFFhMO5D4dfD8ifEe4z5vdElxHTAvj1kzf8+c4s3mm M+r+76TbmCXWxOfAjFCv5+JsF668= X-Received: by 2002:a05:600c:4c08:b0:489:1c1f:35f1 with SMTP id 5b1f17b1804b1-4891c1f3850mr74571075e9.4.1776778907456; Tue, 21 Apr 2026 06:41:47 -0700 (PDT) X-Received: by 2002:a05:600c:4c08:b0:489:1c1f:35f1 with SMTP id 5b1f17b1804b1-4891c1f3850mr74570575e9.4.1776778906664; Tue, 21 Apr 2026 06:41:46 -0700 (PDT) Received: from localhost ([31.111.84.232]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-43fe4cb1176sm38868140f8f.3.2026.04.21.06.41.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Apr 2026 06:41:45 -0700 (PDT) From: Andrew Burgess To: Keith Seitz , gdb-patches@sourceware.org Subject: Re: [PATCH] Add infcall support for C++ constructor-style expressions In-Reply-To: References: Date: Tue, 21 Apr 2026 14:41:44 +0100 Message-ID: <87cxzs2zxj.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: FHaz_J14VcjbvA7IwXsCICjtHVQYxKTH68gGo38ypro_1776778908 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable 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 Keith Seitz writes: > This patch adds an initial try at teaching the expression parser/evaluato= r > 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 constru= ctor > 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) > =E2=9D=8C=EF=B8=8F A syntax error in expression, near `10)'. > > After: > (gdb) print S(42) > $1 =3D {x =3D 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 | 86 +++++++++-- > gdb/eval.c | 55 ++++++++ > gdb/expop.h | 5 + > gdb/testsuite/gdb.cp/infcall-ctors.cc | 128 +++++++++++++++++ > gdb/testsuite/gdb.cp/infcall-ctors.exp | 188 +++++++++++++++++++++++++ > 6 files changed, 457 insertions(+), 10 deletions(-) > create mode 100644 gdb/testsuite/gdb.cp/infcall-ctors.cc > create mode 100644 gdb/testsuite/gdb.cp/infcall-ctors.exp > > diff --git a/gdb/NEWS b/gdb/NEWS > index 03f46df5400..c04727d342d 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -66,6 +66,11 @@ > automatically set to UTF-8. (Users can use the Windows 'chcp' > command to change the output codepage of the console.) > =20 > +* 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 evalution. typo: evalution -> evaluation > + > * New targets > =20 > GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux* > diff --git a/gdb/c-exp.y b/gdb/c-exp.y > index a4a910df712..e17ee2d3c12 100644 > --- a/gdb/c-exp.y > +++ b/gdb/c-exp.y > @@ -199,7 +199,7 @@ static void c_print_token (FILE *file, int type, YYST= YPE value); > #endif > %} > =20 > -%type exp exp1 type_exp start variable qualified_name lcurly f= unction_method > +%type exp exp1 type_exp start variable qualified_name lcurly f= unction_method typename_for_ctor > %type rcurly > %type type typebase scalar_type tag_name_or_complete > %type nonempty_typelist func_mod parameter_typelist > @@ -230,7 +230,7 @@ static void c_print_token (FILE *file, int type, YYST= YPE value); > %token NAME /* BLOCKNAME defined below to give it higher preceden= ce. */ > %token UNKNOWN_CPP_NAME > %token COMPLETE > -%token TYPENAME > +%token TYPENAME TYPENAME_CTOR > %token CLASSNAME=09/* ObjC Class name */ > %type name > %type qual_field_name field_name field_name_or_complete > @@ -533,6 +533,26 @@ msgarg=09:=09name ':' exp > =09=09=09{ add_msglist(0, 0); } > =09; > =20 > +exp=09:=09typename_for_ctor '(' > +=09=09=09{ pstate->start_arglist (); } > +=09=09arglist ')'=09%prec ARROW > +=09=09=09{ > +=09=09=09 std::vector args > +=09=09=09 =3D pstate->pop_vector (pstate->end_arglist ()); > +=09=09=09 operation_up type_op =3D pstate->pop (); > +=09=09=09 pstate->push_new > +=09=09=09 (std::move (type_op), std::move (args)); > +=09=09=09} > +=09; > + > +exp=09:=09typename_for_ctor '(' ')'=09%prec ARROW > +=09=09=09{ > +=09=09=09 operation_up type_op =3D pstate->pop (); > +=09=09=09 pstate->push_new > +=09=09=09 (std::move (type_op), std::vector ()); > +=09=09=09} > +=09; > + > exp=09:=09exp '(' > =09=09=09/* This is to save the value of arglist_len > =09=09=09 being accumulated by an outer function call. */ > @@ -1471,6 +1491,12 @@ scalar_type: > =09=09=09=09=09=09 "int"); } > =09; > =20 > +/* Constructor-style calls. */ > +typename_for_ctor > +=09:=09TYPENAME_CTOR > +=09=09=09{ pstate->push_new ($1.type); } > +=09; > + > /* Implements (approximately): (type-qualifier)* type-specifier. > =20 > When type-specifier is only ever a single word, like 'float' then the= se > @@ -3114,6 +3140,34 @@ static int popping; > built up. */ > static auto_obstack name_obstack; > =20 > +/* Return TYPENAME_CTOR only when the next token is '(', so this > + token is used solely for constructor calls. Otherwise return TYPENAM= E. > + 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, > +=09=09 const char *name_end) > +{ > + if (type =3D=3D nullptr > + || par_state->language ()->la_language !=3D language_cplus) > + return TYPENAME; > + type =3D check_typedef (type); > + if (type->code () !=3D TYPE_CODE_STRUCT && type->code () !=3D TYPE_COD= E_UNION) > + return TYPENAME; > + /* Only return TYPENAME_CTOR when followed by '('. */ > + if (name_end =3D=3D nullptr) > + return TYPENAME; > + { > + const char *p =3D name_end; > + while (*p =3D=3D ' ' || *p =3D=3D '\t' || *p =3D=3D '\n' || *p =3D= =3D '\r') Would 'c_isspace (*p)' work here? It's what we use in similar cases within this file. > + ++p; > + if (*p !=3D '(') > + 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. > @@ -3157,7 +3211,8 @@ classify_name (struct parser_state *par_state, cons= t struct block *block, > =09 if (bsym.symbol !=3D NULL) > =09 { > =09 yylval.tsym.type =3D bsym.symbol->type (); > -=09 return TYPENAME; > +=09 return typename_token_for (par_state, yylval.tsym.type, > +=09=09=09=09=09 yylval.sval.ptr + yylval.sval.length); > =09 } > =09} > =20 > @@ -3184,7 +3239,8 @@ classify_name (struct parser_state *par_state, cons= t struct block *block, > if (bsym.symbol && bsym.symbol->loc_class () =3D=3D LOC_TYPEDEF) > { > yylval.tsym.type =3D bsym.symbol->type (); > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > +=09=09=09=09 yylval.sval.ptr + yylval.sval.length); > } > =20 > /* See if it's an ObjC classname. */ > @@ -3270,7 +3326,9 @@ classify_inner_name (struct parser_state *par_state= , > if (base_type !=3D NULL) > =09{ > =09 yylval.tsym.type =3D base_type; > -=09 return TYPENAME; > +=09 return typename_token_for (par_state, yylval.tsym.type, > +=09=09=09=09 yylval.ssym.stoken.ptr > +=09=09=09=09 + yylval.ssym.stoken.length); > =09} > =20 > return ERROR; > @@ -3290,14 +3348,18 @@ classify_inner_name (struct parser_state *par_sta= te, > =09if (base_type !=3D NULL) > =09 { > =09 yylval.tsym.type =3D base_type; > -=09 return TYPENAME; > +=09 return typename_token_for (par_state, yylval.tsym.type, > +=09=09=09=09 yylval.ssym.stoken.ptr > +=09=09=09=09 + yylval.ssym.stoken.length); > =09 } > } > return ERROR; > =20 > case LOC_TYPEDEF: > yylval.tsym.type =3D yylval.ssym.sym.symbol->type (); > - return TYPENAME; > + return typename_token_for (par_state, yylval.tsym.type, > +=09=09=09=09 yylval.ssym.stoken.ptr > +=09=09=09=09 + yylval.ssym.stoken.length); > =20 > default: > return NAME; > @@ -3332,7 +3394,7 @@ handle_qualified_field_name (qualified_name_token t= oken) > int kind =3D classify_inner_name (pstate, > =09=09=09=09 pstate->expression_context_block, > =09=09=09=09 type); > - if (kind !=3D TYPENAME) > + if (kind !=3D TYPENAME && kind !=3D TYPENAME_CTOR) > =09error (_("could not find type '%s'"), accum.c_str ()); > =20 > type =3D yylval.tsym.type; > @@ -3384,7 +3446,8 @@ yylex (void) > current.token =3D classify_name (pstate, pstate->expression_context_= block, > =09=09=09=09 is_quoted_name, last_lex_was_structop); > if (pstate->language ()->la_language !=3D language_cplus > - || (current.token !=3D TYPENAME && current.token !=3D COLONCOLON > + || (current.token !=3D TYPENAME && current.token !=3D TYPENAME_CTO= R > +=09 && current.token !=3D COLONCOLON > =09 && current.token !=3D FILENAME > =09 && (cpstate->assume_classification =3D=3D TYPE_CODE_UNDEF > =09 || current.token !=3D NAME)) > @@ -3430,6 +3493,7 @@ yylex (void) > else > { > gdb_assert (current.token =3D=3D TYPENAME > +=09=09 || current.token =3D=3D TYPENAME_CTOR > =09=09 || cpstate->assume_classification !=3D TYPE_CODE_UNDEF); > search_block =3D pstate->expression_context_block; > obstack_grow (&name_obstack, current.value.sval.ptr, > @@ -3460,7 +3524,8 @@ yylex (void) > =09=09=09=09=09=09 context_type); > =09 /* We keep going until we either run out of names, or until > =09 we have a qualified name which is not a type. */ > -=09 if (classification !=3D TYPENAME && classification !=3D NAME) > +=09 if (classification !=3D TYPENAME && classification !=3D TYPENAME_CT= OR > +=09 && classification !=3D NAME) > =09 break; > =20 > =09 /* Accept up to this token. */ > @@ -3591,6 +3656,7 @@ c_print_token (FILE *file, int type, YYSTYPE value) > break; > =20 > case TYPENAME: > + case TYPENAME_CTOR: > parser_fprintf (file, "tsym", > =09=09 value.tsym.type->safe_name (), > =09=09 copy_name (value.tsym.stoken).c_str ()); > diff --git a/gdb/eval.c b/gdb/eval.c > index 7beff554ed4..e988b954059 100644 > --- a/gdb/eval.c > +++ b/gdb/eval.c > @@ -1869,6 +1869,61 @@ type_operation::evaluate (struct type *expect_type= , struct expression *exp, > error (_("Attempt to use a type name as an expression")); > } > =20 > +value * > +type_operation::evaluate_funcall (struct type *expect_type, > +=09=09=09=09 struct expression *exp, > +=09=09=09=09 enum noside noside, > +=09=09=09=09 const std::vector &args) > +{ > + struct type *type =3D std::get<0> (m_storage); > + type =3D check_typedef (type); > + > + /* Constructor-style call Type(args) is only for C++ aggregate types. = */ > + gdb_assert (exp->language_defn->la_language =3D=3D language_cplus); > + > + const char *name =3D type->name (); > + if (name =3D=3D nullptr) > + error (_("Cannot call constructor of unnamed type")); Thinking about this error was interesting. I was wondering how we might test this case, as I don't think this is tested right now. I tried writing some C++ like: struct { int a; int b; } global_var =3D { 1, 2 }; Then in GDB I tried: (gdb) p decltype(global_var) (3) Which I know makes no sense, the anonymous struct doesn't even have a constructor, but I wondered if we'd hit the anonymous type error before we even searched for a constructor. We didn't though, as GDB doesn't know how to parse the expression: (gdb) p decltype(global_var)(3) A syntax error in expression, near `(3)'. (gdb) the decltype part is handled in c-exp.y by the rule: type_exp: =09... snip ... =09|=09DECLTYPE '(' exp ')' =09=09=09{ =09=09=09 pstate->wrap (); =09=09=09} And the '(3)' part of the expression is handled by your new rule: > +exp=09:=09typename_for_ctor '(' > +=09=09=09{ pstate->start_arglist (); } > +=09=09arglist ')'=09%prec ARROW > +=09=09=09{ But, I wondered, what if, instead of typename_for_ctor, we just used 'type_exp', like this: exp=09:=09type_exp '(' =09=09=09{ =09=09=09 pstate->start_arglist (); =09=09=09} =09=09arglist ')'=09%prec ARROW =09=09=09{ Then I extended the type_exp rule like: type_exp: =09... snip ... =09|=09TYPENAME_CTOR =09=09=09{ =09=09=09 pstate->push_new ($1.type); =09=09=09} All of the new tests still pass... ... but the decltype example still doesn't work. But by this time I was curious, clearly my actual example with global_var is never going to work, the global_var type doesn't have a constructor, but what if global_var was a type that did have a constructor? The decltype trick should be workable, right? So, I guess the question I'm circling around here is, instead of adding typename_for_ctor, did you try just using the existing type_exp rule? Even if we didn't get the decltype trick working in the original commit, it feels like going through type_exp would leave that as a possibility for the future, but going with typename_for_ctor feels like it will make that harder. > + > + /* Get the constructor name from the type name. */ > + gdb::unique_xmalloc_ptr ctor_name_ptr =3D cp_func_name (name); > + const char *ctor_name =3D (ctor_name_ptr !=3D nullptr) ? ctor_name_ptr= .get () : name; This line seems a little long, maybe wrap at the '=3D' ? > + > + if (!overload_resolution) > + return operation::evaluate_funcall (expect_type, exp, noside, args); I've pretty sure that this path, calling operation::evaluate_funcall, will always result in an error like: (gdb) set overload-resolution off=20 (gdb) p U(34) Attempt to use a type name as an expression (gdb)=20 I wonder if it would be clearer to the user to just do: if (!overload_resolution) error (_("Constructor calls require 'set overload-resolution on'")); I didn't see any tests with 'set overload-resolution off' in use, so it wasn't clear if there's maybe some path where the function call as you have it can do anything other than throw an error? > + > + std::vector argvec (1 + args.size ()); > + value *this_ptr; > + if (noside =3D=3D EVAL_AVOID_SIDE_EFFECTS) > + this_ptr =3D value::zero (lookup_pointer_type (type), lval_memory); > + else > + { > + value *alloc_val =3D value_allocate_space_in_inferior (type->lengt= h ()); > + this_ptr =3D value_from_pointer (lookup_pointer_type (type), > +=09=09=09=09 value_as_long (alloc_val)); > + } > + argvec[0] =3D this_ptr; > + for (size_t i =3D 0; i < args.size (); ++i) > + argvec[i + 1] =3D args[i]->evaluate_with_coercion (exp, noside); > + gdb::array_view arg_view =3D argvec; > + > + value *callee =3D nullptr; > + int static_memfuncp; > + find_overload_match (arg_view, ctor_name, METHOD, > +=09=09 &argvec[0], nullptr, &callee, nullptr, > +=09=09 &static_memfuncp, 0, noside); It would not be usual for constructors to be static, but we should probably handle the case where static_memfuncp is true, even if it is just: if (static_memfuncp) error (_("Constructor %s is unexpectedly marked static"), name); Bonus points for using the DWARF assembler to exercise this error case, but I don't think that's a hard requirement, I see this error more as a glorified todo marker -- if we ever find a case where this is triggers then we can understand it, and fix GDB to match. > + if (callee =3D=3D nullptr) > + error (_("Cannot resolve constructor %s to any overloaded instance")= , > +=09 name); I don't think this error case is being tested. Can you add a test case for this? > + > + if (noside =3D=3D EVAL_AVOID_SIDE_EFFECTS) > + return value::zero (type, not_lval); > + > + evaluate_subexp_do_call (exp, noside, callee, arg_view, > +=09=09=09 nullptr, expect_type); > + return value_ind (this_ptr); > +} > + > } > =20 > /* 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 > =09=09 struct expression *exp, > =09=09 enum noside noside) override; > =20 > + value *evaluate_funcall (struct type *expect_type, > +=09=09=09 struct expression *exp, > +=09=09=09 enum noside noside, > +=09=09=09 const std::vector &args) override; > + > enum exp_opcode opcode () const override > { return OP_TYPE; } > =20 > diff --git a/gdb/testsuite/gdb.cp/infcall-ctors.cc b/gdb/testsuite/gdb.cp= /infcall-ctors.cc > new file mode 100644 > index 00000000000..053542ec5ea > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/infcall-ctors.cc > @@ -0,0 +1,128 @@ > +/* 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 { The '{' should be on a new line. This mistake is repeated throughout this test, I'll not point them all out. > + int x; > + explicit S (int n =3D 0) : x (n) {} > + S operator+ (int n) const { return S (x + n); } > +}; > + > +typedef S S_td; > +using S_u =3D S; > + > +static int add (const struct S &s1, const struct S &s2) { Newline before add, opening '{' on its own line. Unless this layout is needed for the test? In which case a comment is needed. > + 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 =3D 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 =3D Base; > +typedef Derived Derived_td; > +using Derived_u =3D 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 =3D 0 */ > + S s1 (42); /* x =3D 42 */ > + S s2 (s1 + 2); /* x =3D 44 */ > + S s3 =3D s1; > + NS::Derived d;=09 /* Base part x=3D0, Derived part y=3D0 */ > + NS::Derived d1 (10, 20); /* Base part x=3D10, Derived part y=3D20 *= / > + Base b;=09=09 /* x=3D0 */ > + Base b1 (5); /* x=3D5 */ > + Base b2 (d1);=09=09 /* x=3D10 */ > + U u0; /* default: x =3D 0 */ > + U u1 (42); /* x =3D 42 */ > + NS::U uv0; /* default: a =3D 0 */ > + NS::U uv1 (7); /* a =3D 7 */ > + S_td s_td =3D S_td (11); > + S_u s_u =3D S_u (12); > + Base_td b_td =3D Base_td (8); > + Base_u b_u =3D Base_u (9); > + NS::NsBaseTd nsb_td =3D NS::NsBaseTd (13); > + NS::NsBaseU nsb_u =3D NS::NsBaseU (14); > + NS::Derived_td d_td =3D NS::Derived_td (2, 3); > + NS::Derived_u d_u =3D NS::Derived_u (4, 5); > + U_td u_td =3D U_td (15); > + NS::Nu_td nu_u =3D NS::Nu_td (16); > + swapcopy swp (30, 40); > + int result =3D add (s1, s2); > + return result + d.x + d.y + b.x + b1.x + b2.x + d.x + d.y \ > +=09+ d1.x + d1.y + plus_one (u0) + u1.x + uv0.a + uv1.a \ > +=09+ s_td.x + s_u.x + b_td.x + b_u.x + nsb_td.x + nsb_u.x \ > +=09+ d_td.x + d_td.y + d_u.x + d_u.y + u_td.x + nu_u.a \ > +=09+ swapcopy_first_byval (swapcopy (100, 200)) \ > +=09+ swapcopy_first_byval (swp); /* stop-here */ > +} > diff --git a/gdb/testsuite/gdb.cp/infcall-ctors.exp b/gdb/testsuite/gdb.c= p/infcall-ctors.exp > new file mode 100644 > index 00000000000..88297018b28 > --- /dev/null > +++ b/gdb/testsuite/gdb.cp/infcall-ctors.exp > @@ -0,0 +1,188 @@ > +# 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 e= ven > +# 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 i= t. > + > +require allow_cplus_tests > + > +standard_testfile .cc > + > +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c= ++}]} { > + return -1 The -1 is not needed. Thanks, Andrew > +} > + > +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 =3D struct S \{} \ > + { int x;} \ > + "" \ > + { S\(int\);} \ > + { S operator\+\(int\) const;} \ > + {\}}] > + > +gdb_test "print s0" " =3D \\{x =3D 0\\}" "print s0 default ctor" > +gdb_test "print s1" " =3D \\{x =3D 42\\}" "print s1 with 42" > +gdb_test "print S(99)" " =3D \\{x =3D 99\\}" "construct S(99) via inferi= or function call" > + > +gdb_test "ptype swapcopy" [multi_line \ > + {type =3D struct swapcopy \{} \ > + { int lo;} \ > + { int hi;} \ > + "" \ > + { swapcopy\(int, int\);} \ > + { swapcopy\(const swapcopy ?&\);} \ > + {\}}] > +gdb_test "print swapcopy(1, 2)" { =3D \{lo =3D 1, hi =3D 2\}} \ > + "construct swapcopy via inferior function call" > +gdb_test {print swapcopy($)} {=3D \{lo =3D 2, hi =3D 1\}} \ > + "call copy ctor on object via history" > +gdb_test "print swapcopy_first_byval(swapcopy(10, 20))" "=3D 20" \ > + "pass-by-value copy ctor swaps members" > +gdb_test "print swapcopy_first_byval(swp)" "=3D 40" \ > + "swapcopy local passed by value uses copy ctor" > + > +gdb_test "print S_td(33)" " =3D \\{x =3D 33\\}" \ > + "construct S via typedef name (not underlying struct tag)" > +gdb_test "print S_u(34)" " =3D \\{x =3D 34\\}" "construct S via using al= ias" > +gdb_test "print Base_td(8)" { =3D \{x =3D 8\}} "construct Base via typed= ef name" > +gdb_test "print Base_u(9)" { =3D \{x =3D 9\}} "construct Base via using = alias" > +gdb_test "print NS::NsBaseTd(5)" { =3D \{x =3D 5\}} \ > + "construct Base via typedef in namespace" > +gdb_test "print NS::NsBaseU(6)" { =3D \{x =3D 6\}} \ > + "construct Base via using alias in namespace" > +gdb_test "print NS::Derived_td(1, 2)" { =3D \{ =3D \{x =3D 1\}, y = =3D 2\}} \ > + "construct NS::Derived via typedef name" > +gdb_test "print NS::Derived_u(3, 4)" { =3D \{ =3D \{x =3D 3\}, y = =3D 4\}} \ > + "construct NS::Derived via using alias" > +gdb_test "print U_td(55)" { =3D \{x =3D 55\}} \ > + "construct global union U via typedef name" > +gdb_test "print NS::Nu_td(66)" { =3D \{a =3D 66\}} \ > + "construct NS::U via typedef name" > + > +# Tests involving "Base" and "Derived". > +set base_re [multi_line \ > + {type =3D struct Base \{} \ > + { int x;} \ > + "" \ > + { Base\(void\);} \ > + { Base\(int\);} \ > + { Base\(const Base ?&\);} \ > + {\}}] > +gdb_test "ptype Base" $base_re > + > +set derived_re [multi_line \ > + {type =3D class NS::Derived : public Base \{} \ > + { public:} \ > + { int y;} \ > + "" \ > + { Derived\(void\);} \ > + { Derived\(int, int\);} \ > + {\}}] > +gdb_test "ptype NS::Derived" $derived_re > + > +gdb_test "print d" { =3D \{ =3D \{x =3D 0\}, y =3D 0\}} > +gdb_test "print d1" { =3D \{ =3D \{x =3D 10\}, y =3D 20\}} > +gdb_test "print b" { =3D \{x =3D 0\}} > +gdb_test "print b1" { =3D \{x =3D 5\}} > +gdb_test "print b2" { =3D \{x =3D 10\}} > +gdb_test "print (Base)(d1)" { =3D \{x =3D 10\}} "cast (Base)(d1) slices = to Base" > +gdb_test "print Base(d1)" { =3D \{x =3D 10\}} "construct Base(d1) from D= erived" > +gdb_test "print Base()" { =3D \{x =3D 0\}} "construct Base() default" > +gdb_test "print Base(7)" { =3D \{x =3D 7\}} "construct Base(7) with argu= ment" > +gdb_test "print NS::Derived()" { =3D \{ =3D \{x =3D 0\}, y =3D 0\}= } \ > + "construct NS::Derived() via inferior function call" > +gdb_test "print NS::Derived().y" " =3D 0" "construct NS::Derived() and a= ccess .y" > +gdb_test "print NS::Derived(1, 2)" { =3D \{ =3D \{x =3D 1\}, y =3D= 2\}} \ > + "construct NS::Derived(1, 2) via inferior function call" > +gdb_test "print NS::Derived(1, 2).y" " =3D 2" \ > + "construct NS::Derived(1, 2) and access .y" > +gdb_test "print ((Base)d1).x" " =3D 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 \ > +=09"ptype of NS::Derived temporary" > + > +# Tests involving unions. > +gdb_test "ptype U" [multi_line \ > + {type =3D union U \{} \ > + { int x;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] \ > + "ptype U" > + > +gdb_test "ptype NS::U" [multi_line \ > + {type =3D union NS::U \{} \ > + { int a;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] \ > + "ptype NS::U" > + > +gdb_test "print u0" { =3D \{x =3D 0\}} > +gdb_test "print u1" { =3D \{x =3D 42\}} > +gdb_test "print uv0" { =3D \{a =3D 0\}} > +gdb_test "print uv1" { =3D \{a =3D 7\}} > +gdb_test "print U()" { =3D \{x =3D 0\}} "construct U() via inferior func= tion call" > +gdb_test "print U(99)" { =3D \{x =3D 99\}} "construct U(99) via inferior= function call" > +gdb_test "print U(99).x" " =3D 99" "construct U(99) and access .x" > +gdb_test "print NS::U()" { =3D \{a =3D 0\}} \ > + "construct NS::U() via inferior function call" > +gdb_test "print NS::U(13)" { =3D \{a =3D 13\}} \ > + "construct NS::U(13) via inferior function call" > +gdb_test "print NS::U(13).a" " =3D 13" \ > + "construct NS::U(13) and access .a" > + > +set u_re [multi_line \ > + {type =3D union U \{} \ > + { int x;} \ > + "" \ > + { U\(void\);} \ > + { U\(int\);} \ > + {\}}] > +set ns_u_re [multi_line \ > + {type =3D 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))" "=3D 43" "temporary in function call" > +gdb_test "p add(S(1), S(20))" "=3D 21" "add two temporaries of S" > +gdb_test "p plus_one(U(add (S(4), S(6))))" "=3D 11" \ > + "nested function call using temporaries" > > base-commit: 07519d531b1e858f665ff011d7f1002f38111ec8 > --=20 > 2.53.0