From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id QTGEKFlB2mp3GAwAWB0awg (envelope-from ) for ; Thu, 22 Oct 2026 13:01:13 -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=TUMu/Jb6; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 9AA0D378002; Thu, 22 Oct 2026 13:01:13 -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.1 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_SBL_CSS 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 5A4511E007 for ; Thu, 22 Oct 2026 13:01:11 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B7B374BA23E8 for ; Thu, 22 Oct 2026 17:01:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B7B374BA23E8 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=TUMu/Jb6 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 CC9114BA2E24 for ; Thu, 22 Oct 2026 17:00:34 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CC9114BA2E24 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 CC9114BA2E24 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=1792688434; cv=none; b=r1zTkWTk6GLPjqNOEn2ly2iYlCuVV/1BWCWCA3wTvCSk5lD+c3YHbK7g5zDG7QDgCDBF2ipBtPvzsmPZwrsJSCZ4xD8d0hllWeJWAEkktysG7asj6hpjr6abWUc3I5rUbArxSoHqVpUnIf0UlinQsqyec63A5fEEsliNRj0TO+8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1792688434; c=relaxed/simple; bh=MZRA0Zt5ZFoPvHpECwz/XpKAfX1q8oXLB+1wiQJeUX0=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=VjkTWW/wgbsZOJQ33gI4PtWqVeQJl8VCxeP2lmxcGAS2eShvVLYRI4to9VfKvbkbjyux3OzTAnWXsawTSURFBA87asB/UxdNEAXPIyyknC5+K/ghTULcqrVWF3zcNa2gxRI6SnOLdJzTCtWeovGPhNORiO/p2JO+iR2jeuOlk1o= 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=TUMu/Jb6 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CC9114BA2E24 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1792688434; 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=9hYG+oC19VNU9FZAVN24NUOYYu4LFo2b8HHva7/H3KU=; b=TUMu/Jb6g4462X40aCdAHYArkfF/5GwouE8/uxXya9NpwEHKN8KPH8g7nDKyUe5UTdv+cO dbFraigoMuFysFXZFFSDucQSR0axtpPgHes//rP0pdRzb4nEeEyJKaD0egqP8IsqgHzQkF W0wNr0e94P6EYIMTDf2g0Z3/ZsLbx0Y= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-29-9WIAxnTZN9y55IEU3iaDNg-1; Thu, 22 Oct 2026 13:00:32 -0400 X-MC-Unique: 9WIAxnTZN9y55IEU3iaDNg-1 X-Mimecast-MFC-AGG-ID: 9WIAxnTZN9y55IEU3iaDNg_1792688431 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 596E419541AF for ; Thu, 22 Oct 2026 17:00:31 +0000 (UTC) Received: from keiths-thinkpadp1gen7.redhat.corp (headnet04.pony-001.prod.iad2.dc.redhat.com [10.2.32.116]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 8D9651800495 for ; Thu, 22 Oct 2026 17:00:30 +0000 (UTC) From: Keith Seitz To: gdb-patches@sourceware.org Subject: [PATCH v3] Add infcall support for C++ constructor-style expressions/casts Date: Thu, 22 Oct 2026 10:00:29 -0700 Message-ID: <04cf7b0e8ecb77bf2ab49e6854542273c85ebcba.1792687980.git.keiths@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: Vet-d-OYAFBfoG-gE53DGxkaYTv-UL5XDRMRO9U4krI_1792688431 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 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 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 `42)'. 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. Changes since v2: - Rework parser rules to eliminate previously added SR/RR conflicts. - Add TYPEOF_CTOR with lexer lookahead to handle typeof(T)(args) and typeof(expr)(args) - Implement functional-cast / value-init semantics (Type(arg), Type()) - Address misc review comments from Kevin and Andrew --- gdb/NEWS | 5 + gdb/c-exp-parser.c | 88 ++++++- gdb/c-exp-parser.y | 26 ++- gdb/eval.c | 93 ++++++++ gdb/expop.h | 10 + gdb/testsuite/gdb.base/typeof-ctor-style.c | 30 +++ gdb/testsuite/gdb.base/typeof-ctor-style.exp | 52 +++++ gdb/testsuite/gdb.cp/infcall-ctors.cc | 148 ++++++++++++ gdb/testsuite/gdb.cp/infcall-ctors.exp | 230 +++++++++++++++++++ gdb/testsuite/gdb.cp/static-ctor-infcall.c | 33 +++ gdb/testsuite/gdb.cp/static-ctor-infcall.exp | 86 +++++++ 11 files changed, 792 insertions(+), 9 deletions(-) create mode 100644 gdb/testsuite/gdb.base/typeof-ctor-style.c create mode 100644 gdb/testsuite/gdb.base/typeof-ctor-style.exp 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 7303ec0e460..8eb791454de 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -198,6 +198,11 @@ info locals intent to remove it in a future release. The s390 64-bit target (s390x-*) remains supported. +* 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. + * Configure changes ** --with-babeltrace has been removed. The babeltrace library was diff --git a/gdb/c-exp-parser.c b/gdb/c-exp-parser.c index 64bf455e722..b31d98352df 100644 --- a/gdb/c-exp-parser.c +++ b/gdb/c-exp-parser.c @@ -1089,6 +1089,60 @@ static int popping; built up. */ static auto_obstack name_obstack; +/* Return true if the next non-whitespace character at P is '('. */ + +static bool +followed_by_open_paren (const char *p) +{ + p = skip_spaces (p); + return *p == '('; +} + +/* 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. c_yylval.sval.ptr + + c_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; + if (!followed_by_open_paren (name_end)) + return TYPENAME; + return TYPENAME_CTOR; +} + +/* If the input is typeof ( ... ) followed by '(', return TYPEOF_CTOR so the + grammar can parse the typeof operand and the following call. Otherwise + return TYPEOF. Lexptr is left unchanged. */ + +static int +typeof_ctor_token_for (struct parser_state *par_state) +{ + const char *p = skip_spaces (par_state->lexptr); + + if (*p != '(') + return TYPEOF; + + p = find_toplevel_char (p + 1, ')'); + if (p == nullptr) + return TYPEOF; + + if (!followed_by_open_paren (p + 1)) + return TYPEOF; + + return TYPEOF_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. @@ -1132,7 +1186,9 @@ classify_name (struct parser_state *par_state, const struct block *block, if (bsym.symbol != NULL) { c_yylval.tsym.type = bsym.symbol->type (); - return TYPENAME; + return typename_token_for (par_state, c_yylval.tsym.type, + c_yylval.sval.ptr + + c_yylval.sval.length); } } @@ -1159,7 +1215,8 @@ classify_name (struct parser_state *par_state, const struct block *block, if (bsym.symbol && bsym.symbol->loc_class () == LOC_TYPEDEF) { c_yylval.tsym.type = bsym.symbol->type (); - return TYPENAME; + return typename_token_for (par_state, c_yylval.tsym.type, + c_yylval.sval.ptr + c_yylval.sval.length); } /* See if it's an ObjC classname. */ @@ -1245,7 +1302,9 @@ classify_inner_name (struct parser_state *par_state, if (base_type != NULL) { c_yylval.tsym.type = base_type; - return TYPENAME; + return typename_token_for (par_state, c_yylval.tsym.type, + c_yylval.ssym.stoken.ptr + + c_yylval.ssym.stoken.length); } return ERROR; @@ -1265,14 +1324,18 @@ classify_inner_name (struct parser_state *par_state, if (base_type != NULL) { c_yylval.tsym.type = base_type; - return TYPENAME; + return typename_token_for (par_state, c_yylval.tsym.type, + c_yylval.ssym.stoken.ptr + + c_yylval.ssym.stoken.length); } } return ERROR; case LOC_TYPEDEF: c_yylval.tsym.type = c_yylval.ssym.sym.symbol->type (); - return TYPENAME; + return typename_token_for (par_state, c_yylval.tsym.type, + c_yylval.ssym.stoken.ptr + + c_yylval.ssym.stoken.length); default: return NAME; @@ -1304,7 +1367,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 = c_yylval.tsym.type; @@ -1345,8 +1408,15 @@ c_yylex () && current.token == NAME) current.token = classify_name (pstate, pstate->expression_context_block, is_quoted_name, last_lex_was_structop); + if (current.token == TYPEOF) + { + int tok = typeof_ctor_token_for (pstate); + if (tok == TYPEOF_CTOR) + return TYPEOF_CTOR; + } 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)) @@ -1392,6 +1462,7 @@ c_yylex () 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, @@ -1422,7 +1493,8 @@ c_yylex () 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. */ diff --git a/gdb/c-exp-parser.y b/gdb/c-exp-parser.y index b24fee0058b..cbc71005475 100644 --- a/gdb/c-exp-parser.y +++ b/gdb/c-exp-parser.y @@ -127,7 +127,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 @@ -157,6 +157,7 @@ static void c_print_token (FILE *file, int type, YYSTYPE value); %token REINTERPRET_CAST DYNAMIC_CAST STATIC_CAST CONST_CAST %token ENTRY %token TYPEOF +%token TYPEOF_CTOR %token DECLTYPE %token TYPEID @@ -430,6 +431,28 @@ msgarg : name ':' exp { add_msglist(0, 0); } ; +/* Constructor-style Type(args) / typeof(T)(args): + TYPENAME_CTOR and TYPEOF_CTOR are selected by lexer lookahead when a + call '(' follows. The insides of typeof are parsed by these rules; + the outer call uses the existing exp '(' ... ')' productions. */ +exp : TYPENAME_CTOR + { + pstate->push_new ($1.type); + } + ; + +exp : TYPEOF_CTOR '(' type ')' %prec UNARY + { + pstate->push_new ($3); + } + ; + +exp : TYPEOF_CTOR '(' exp ')' %prec UNARY + { + pstate->wrap (); + } + ; + exp : exp '(' /* This is to save the value of arglist_len being accumulated by an outer function call. */ @@ -1833,6 +1856,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 17fb6723413..ad22c395400 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -1871,6 +1871,99 @@ 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); + struct type *resolved_type = check_typedef (type); + + /* Constructor-style Type(args) is C++-only. */ + if (exp->language_defn->la_language != language_cplus) + error (_("Constructor-style casts are only valid in C++")); + + /* For non-class types, Type(arg) is a cast and Type() value-inits. */ + if (resolved_type->code () != TYPE_CODE_STRUCT + && resolved_type->code () != TYPE_CODE_UNION) + { + if (args.size () > 1) + error (_("Functional cast requires at most one argument")); + + if (noside == EVAL_AVOID_SIDE_EFFECTS) + return value::zero (type, not_lval); + + if (args.empty ()) + return value::zero (type, not_lval); + + value *arg = args[0]->evaluate_with_coercion (exp, noside); + return value_cast (type, arg); + } + + type = resolved_type; + + 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); +} + +value * +typeof_operation::evaluate_funcall (struct type *expect_type, + struct expression *exp, + enum noside noside, + const std::vector &args) +{ + value *type_val + = std::get<0> (m_storage)->evaluate (nullptr, exp, + EVAL_AVOID_SIDE_EFFECTS); + type_operation type_op (type_val->type ()); + return type_op.evaluate_funcall (expect_type, exp, noside, args); +} + } /* A helper function for BINOP_ASSIGN_MODIFY. */ diff --git a/gdb/expop.h b/gdb/expop.h index ce175afbc3d..8be26069b58 100644 --- a/gdb/expop.h +++ b/gdb/expop.h @@ -1603,6 +1603,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; } @@ -1632,6 +1637,11 @@ class typeof_operation error (_("Attempt to use a type as an expression")); } + 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_TYPEOF; } diff --git a/gdb/testsuite/gdb.base/typeof-ctor-style.c b/gdb/testsuite/gdb.base/typeof-ctor-style.c new file mode 100644 index 00000000000..58c94cb5a09 --- /dev/null +++ b/gdb/testsuite/gdb.base/typeof-ctor-style.c @@ -0,0 +1,30 @@ +/* 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; +}; + +int +main (void) +{ + struct S s = {1}; + return s.x; +} diff --git a/gdb/testsuite/gdb.base/typeof-ctor-style.exp b/gdb/testsuite/gdb.base/typeof-ctor-style.exp new file mode 100644 index 00000000000..432585afde8 --- /dev/null +++ b/gdb/testsuite/gdb.base/typeof-ctor-style.exp @@ -0,0 +1,52 @@ +# 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 . + +# typeof / cast forms around struct types in C. +# +# typeof(T)(x) is not valid C (GCC rejects it); constructor-style Type(args) +# is C++-only. Compound literals are the valid C99 form. + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} { + return +} + +if {![runto_main]} { + return +} + +# Compound literals (valid C99 / GNU C). +gdb_test "print (struct S){42}" " = \\{x = 42\\}" \ + "compound literal (struct S){42}" +gdb_test "print (typeof(struct S)){42}" " = \\{x = 42\\}" \ + "compound literal (typeof(struct S)){42}" + +gdb_test "print (typeof(int))(42)" " = 42" \ + "parenthesized cast (typeof(int))(42)" + +# Constructor-style Type(args) / Type() is only valid in C++. +gdb_test "print typeof(struct S)(42)" \ + "Constructor-style casts are only valid in C\\+\\+" \ + "typeof(struct S)(42) rejected in C" +gdb_test "print typeof(int)(42)" \ + "Constructor-style casts are only valid in C\\+\\+" \ + "typeof(int)(42) rejected in C" +gdb_test "print typeof(struct S)()" \ + "Constructor-style casts are only valid in C\\+\\+" \ + "typeof(struct S)() rejected in C" +gdb_test "print typeof(int)()" \ + "Constructor-style casts are only valid in C\\+\\+" \ + "typeof(int)() rejected in C" diff --git a/gdb/testsuite/gdb.cp/infcall-ctors.cc b/gdb/testsuite/gdb.cp/infcall-ctors.cc new file mode 100644 index 00000000000..505f115d13e --- /dev/null +++ b/gdb/testsuite/gdb.cp/infcall-ctors.cc @@ -0,0 +1,148 @@ +/* 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); + } +}; + +/* Aggregate with no user-declared constructors. */ +struct Agg +{ + int x; +}; + +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; + Agg agg = {7}; + 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) + agg.x; /* 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..07efd084ca8 --- /dev/null +++ b/gdb/testsuite/gdb.cp/infcall-ctors.exp @@ -0,0 +1,230 @@ +# 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. + +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" + +# typeof / __typeof__ constructor-style calls and related casts. + +gdb_test "print typeof(S)(42)" " = \\{x = 42\\}" \ + "construct S via typeof(S)(42)" +gdb_test "print typeof(struct S)(42)" " = \\{x = 42\\}" \ + "construct S via typeof(struct S)(42)" +gdb_test "print __typeof__(S)(42)" " = \\{x = 42\\}" \ + "construct S via __typeof__(S)(42)" +gdb_test "print __typeof(S)(42)" " = \\{x = 42\\}" \ + "construct S via __typeof(S)(42)" +gdb_test "print typeof(S_td)(33)" " = \\{x = 33\\}" \ + "construct S via typeof of typedef name" +gdb_test "print typeof(s1)(99)" " = \\{x = 99\\}" \ + "construct S via typeof(expression)" + +# Parenthesized casts of a class type with a converting constructor. +gdb_test "print (S)(42)" " = \\{x = 42\\}" "cast (S)(42)" +gdb_test "print (typeof(S))(42)" " = \\{x = 42\\}" \ + "cast (typeof(S))(42)" + +# Functional cast to a scalar via typeof. +gdb_test "print typeof(int)(42)" " = 42" \ + "functional cast typeof(int)(42)" +gdb_test "print typeof(int)()" " = 0" \ + "value-initialization via typeof(int)()" +gdb_test "print typeof(Base)()" { = \{x = 0\}} \ + "default construct Base via typeof(Base)()" +gdb_test "print typeof(int)(1, 2)" \ + "Functional cast requires at most one argument" \ + "functional cast rejects multiple arguments" +gdb_test "print (typeof(int))(42)" " = 42" \ + "parenthesized cast (typeof(int))(42)" + +# Aggregate with no user-declared ctor: Type(args) has no matching +# constructor. +gdb_test "print agg" " = \\{x = 7\\}" +gdb_test "print Agg(42)" "Couldn't find method Agg::Agg" \ + "Agg(42) with no user-declared constructor" +gdb_test "print typeof(Agg)(42)" "Couldn't find method Agg::Agg" \ + "typeof(Agg)(42) with no user-declared constructor" 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..503e3bb98f3 --- /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 (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 . */ + +/* 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..08b1ca22f43 --- /dev/null +++ b/gdb/testsuite/gdb.cp/static-ctor-infcall.exp @@ -0,0 +1,86 @@ +# 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 . +# +# Exercise the "Constructor %s is unexpectedly marked static" error. + +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: 21cffe7c0955c6bdb0c14ded96939d409f3c21f0 -- 2.56.0