Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Keith Seitz <keiths@redhat.com>
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	[thread overview]
Message-ID: <04cf7b0e8ecb77bf2ab49e6854542273c85ebcba.1792687980.git.keiths@redhat.com> (raw)
In-Reply-To: <a65096b4d774c86d53d976b1bb2c39b2c6fc67ce.1774552598.git.keiths@redhat.com>

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 <ssym> NAME /* BLOCKNAME defined below to give it higher precedence. */
 %token <ssym> UNKNOWN_CPP_NAME
 %token <voidval> COMPLETE
-%token <tsym> TYPENAME
+%token <tsym> TYPENAME TYPENAME_CTOR
 %token <theclass> CLASSNAME	/* ObjC Class name */
 %type <sval> name
 %type <qval> 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<type_operation> ($1.type);
+			}
+	;
+
+exp	:	TYPEOF_CTOR '(' type ')' %prec UNARY
+			{
+			  pstate->push_new<type_operation> ($3);
+			}
+	;
+
+exp	:	TYPEOF_CTOR '(' exp ')' %prec UNARY
+			{
+			  pstate->wrap<typeof_operation> ();
+			}
+	;
+
 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<type=%s, name=%s>",
 		      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<operation_up> &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<char> 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<value *> 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<value *> 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<operation_up> &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<operation_up> &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<operation_up> &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 <http://www.gnu.org/licenses/>.  */
+
+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 <http://www.gnu.org/licenses/>.
+
+# 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 <http://www.gnu.org/licenses/>.  */
+
+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 <http://www.gnu.org/licenses/>.
+
+# 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)" { = \{<Base> = \{x = 1\}, y = 2\}} \
+    "construct NS::Derived via typedef name"
+gdb_test "print NS::Derived_u(3, 4)" { = \{<Base> = \{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" { = \{<Base> = \{x = 0\}, y = 0\}}
+gdb_test "print d1" { = \{<Base> = \{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()" { = \{<Base> = \{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)" { = \{<Base> = \{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 <http://www.gnu.org/licenses/>.  */
+
+/* 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 <http://www.gnu.org/licenses/>.
+#
+# 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


      parent reply	other threads:[~2026-10-22 17:01 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-26 19:17 [PATCH] Add infcall support for C++ constructor-style expressions Keith Seitz
2026-03-27  5:50 ` Eli Zaretskii
2026-04-21 13:41 ` Andrew Burgess
2026-04-21 18:13   ` Keith Seitz
2026-04-27 16:43 ` [PATCH v2] " Keith Seitz
2026-06-19 17:21   ` Keith Seitz
2026-06-27  0:06   ` Kevin Buettner
2026-07-29 17:44     ` Keith Seitz
2026-09-17 14:41       ` Andrew Burgess
2026-09-17 16:05         ` Keith Seitz
2026-09-17 15:10   ` Andrew Burgess
2026-10-22 17:00 ` Keith Seitz [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=04cf7b0e8ecb77bf2ab49e6854542273c85ebcba.1792687980.git.keiths@redhat.com \
    --to=keiths@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox