{"id":2215606,"url":"http://patchwork.ozlabs.org/api/patches/2215606/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/patch/acLrujgOt2w8gd5_@redhat.com/","project":{"id":17,"url":"http://patchwork.ozlabs.org/api/projects/17/?format=json","name":"GNU Compiler Collection","link_name":"gcc","list_id":"gcc-patches.gcc.gnu.org","list_email":"gcc-patches@gcc.gnu.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<acLrujgOt2w8gd5_@redhat.com>","list_archive_url":null,"date":"2026-03-24T19:53:30","name":"[v2] c++/reflection: ICE with substitute and undeduced auto [PR123613]","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"b6ac09e132916693a25f6251e422e66ad612ff01","submitter":{"id":14370,"url":"http://patchwork.ozlabs.org/api/people/14370/?format=json","name":"Marek Polacek","email":"polacek@redhat.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/gcc/patch/acLrujgOt2w8gd5_@redhat.com/mbox/","series":[{"id":497341,"url":"http://patchwork.ozlabs.org/api/series/497341/?format=json","web_url":"http://patchwork.ozlabs.org/project/gcc/list/?series=497341","date":"2026-03-24T19:53:30","name":"[v2] c++/reflection: ICE with substitute and undeduced auto [PR123613]","version":2,"mbox":"http://patchwork.ozlabs.org/series/497341/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2215606/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2215606/checks/","tags":{},"related":[],"headers":{"Return-Path":"<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>","X-Original-To":["incoming@patchwork.ozlabs.org","gcc-patches@gcc.gnu.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","gcc-patches@gcc.gnu.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=JzSSYXyJ;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=JzSSYXyJ","sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","sourceware.org; spf=pass smtp.mailfrom=redhat.com","server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.133.124"],"Received":["from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgLMk0K4Cz1y1K\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 06:54:08 +1100 (AEDT)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id AE90F4BB3BF5\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 24 Mar 2026 19:54:06 +0000 (GMT)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by sourceware.org (Postfix) with ESMTP id 5030D4BB3BC9\n for <gcc-patches@gcc.gnu.org>; Tue, 24 Mar 2026 19:53:36 +0000 (GMT)","from mail-qt1-f198.google.com (mail-qt1-f198.google.com\n [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-306-AA9Y5m4fO6GUFpjX-n7MTA-1; Tue, 24 Mar 2026 15:53:34 -0400","by mail-qt1-f198.google.com with SMTP id\n d75a77b69052e-50b4b81c632so60600981cf.1\n for <gcc-patches@gcc.gnu.org>; Tue, 24 Mar 2026 12:53:34 -0700 (PDT)","from redhat.com ([2603:7000:9500:10::1db4])\n by smtp.gmail.com with ESMTPSA id\n 6a1803df08f44-89c852181b3sm136686126d6.9.2026.03.24.12.53.32\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 24 Mar 2026 12:53:32 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org AE90F4BB3BF5","OpenDKIM Filter v2.11.0 sourceware.org 5030D4BB3BC9"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 5030D4BB3BC9","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 5030D4BB3BC9","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1774382016; cv=none;\n b=D5H8aqxEalSu0f+K/1QjP8D1VJMHQxfDZvlw11QT9B0psJ7z7LXnajLpKvavqU6u5DxPgqX7a3uDjqEzWG4IoATJm+RimqZ83LPq7vGCBdmFaEyo5SK35SxDSm8qM5WrlpCS2xjclfpHRj7i1QiFLyhSBUIfWnP5X9Rv0ly/r6Q=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1774382016; c=relaxed/simple;\n bh=rFZQZy4IPn6IeoxCyOx+12CECvPi33QD8iT0E6r978c=;\n h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version;\n b=kLC+RzACJW9LUCmxGQC6QRSpmlWt0laMEG0RfENuqoFNO3/jWz1ZQF9p6L+cq5ZlocA4GTvsu/O9qz/vSfDW7FZ2PDIvouzJ2FyVO14cQWrtxrWg+ywKgErofRpgjlJnojMuN+8LyNjcyUpgUgmDsguUK9swlM89PRv5FSPCBf0=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1774382016;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n in-reply-to:in-reply-to:references:references;\n bh=r8EvdnUJoxnteqgopV1/aG3qaoGHY6pU3cuYUrCdOCc=;\n b=JzSSYXyJUGf2EDjOBXx/4UGiUjlF7rcPAcZ2fFJlYWHzKUpWofxkowZJZfb91DmKY+iydz\n iHhDw0rX7oiuSN+Oe2it4f7gRFvlnDNSnnbsP7EBQ4wHfXMFPSZpfYxZQ0v3FI/TwYLcVp\n PWjmIgd7D0pPtL5nrwN7gvobJaS/dCk=","X-MC-Unique":"AA9Y5m4fO6GUFpjX-n7MTA-1","X-Mimecast-MFC-AGG-ID":"AA9Y5m4fO6GUFpjX-n7MTA_1774382014","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774382014; x=1774986814;\n h=user-agent:in-reply-to:content-disposition:mime-version:references\n :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=r8EvdnUJoxnteqgopV1/aG3qaoGHY6pU3cuYUrCdOCc=;\n b=U+esUddPereEL6sMHjWlFjth9Ohn8C+HZyl4jQERDnd6TJPbktQXofJ6CtNM/snsuh\n mZjFFsgHKrKPnzTC/iatzYGC39Wk8zXjeiS0y9e2mINvjnBLheu3IKOl0hq3e1edHZV0\n SclhVU3XxZzl8yIEqxvMk6GtJ6ayqFIIQ0qCR28bOrrbq/FO6+zoItl34ITD9kig4FR+\n ZXxvuEJy1Zlf0jXwpaop4MIdv445rQoirmmUlpKxnEdyqMWjhxTKYzHoRDJmSkjjPiMp\n jV/tU4vbJn/hjruZ52znj/1jFazOQ97tRnYUY/mL+DnM938KNFCJNJuxR0gaGYJGxNC2\n wznA==","X-Gm-Message-State":"AOJu0Yzd6Z0Kd5MRdLgRg4bQX/Xyh7fZbLddot8n1Rp0sVUCHaxWpaVL\n BCybbStNQ3So8AE/FVXswhvYixbzQcfRuT+5LSJTV3+yqkDnDbmRneJ1eU7pvVa8A86GZtlgzxk\n ERiCII2KOzV8gLE9k5RfVF5kNcisD8wWLs/LqMyB3HWEQEVCfUQk+3UYD7j8=","X-Gm-Gg":"ATEYQzx4beU7glBGZ7ZzbsXLX572e8rlU257TTiOnG0nxKTMk/2vXK0k9imXcMMVKrX\n jMBtYJVCvf5qECUPjtUaYDSvTGN0xnFdjZB35oiS+LQJ7GYRBnrfRJLlp1wxONzrbbj3b+6y1Bl\n a7YlZjv6jCl8NTGsbkZWl1bDPJQd4yAK6bHZnvPW245IpGm27FJ45AqhF0oGWLFRedUKMWFj4Eq\n qErMiaf+iPArWBnAQisdt2a1X372mUU5OMoG1+QoZ7+yXYlOtpSzm1BUmFNGfPYGzxIKL7lJv1s\n 0cfyZEaMNA+R8C0nfF7wLe6RFtsL7cplbWnmZU1Iu7JA00b3rZA3fHpDgue/bCF/H/yIlnN7bfK\n rGw==","X-Received":["by 2002:ac8:4542:0:b0:50b:4be1:7c95 with SMTP id\n d75a77b69052e-50b80d581b4mr10960361cf.30.1774382013873;\n Tue, 24 Mar 2026 12:53:33 -0700 (PDT)","by 2002:ac8:4542:0:b0:50b:4be1:7c95 with SMTP id\n d75a77b69052e-50b80d581b4mr10960131cf.30.1774382013305;\n Tue, 24 Mar 2026 12:53:33 -0700 (PDT)"],"Date":"Tue, 24 Mar 2026 15:53:30 -0400","From":"Marek Polacek <polacek@redhat.com>","To":"Jason Merrill <jason@redhat.com>","Cc":"GCC Patches <gcc-patches@gcc.gnu.org>, Jakub Jelinek <jakub@redhat.com>","Subject":"[PATCH v2] c++/reflection: ICE with substitute and undeduced auto\n [PR123613]","Message-ID":"<acLrujgOt2w8gd5_@redhat.com>","References":"<20260319180156.1398725-1-polacek@redhat.com>\n <5d918cf0-da0a-4043-8946-7e70566ede9c@redhat.com>","MIME-Version":"1.0","In-Reply-To":"<5d918cf0-da0a-4043-8946-7e70566ede9c@redhat.com>","User-Agent":"Mutt/2.3.0 (2026-01-25)","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"r-1Gac-ZtxO0fj-pc95tgpMCw5h48NUu09ikPm64Alk_1774382014","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","X-BeenThere":"gcc-patches@gcc.gnu.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Gcc-patches mailing list <gcc-patches.gcc.gnu.org>","List-Unsubscribe":"<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>","List-Archive":"<https://gcc.gnu.org/pipermail/gcc-patches/>","List-Post":"<mailto:gcc-patches@gcc.gnu.org>","List-Help":"<mailto:gcc-patches-request@gcc.gnu.org?subject=help>","List-Subscribe":"<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>","Errors-To":"gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"},"content":"On Fri, Mar 20, 2026 at 06:19:18PM -0400, Jason Merrill wrote:\n> On 3/19/26 2:01 PM, Marek Polacek wrote:\n> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?\n> > \n> > -- >8 --\n> > In substitute7.C we have\n> > \n> >    template <typename T, auto ... Vs>\n> >    constexpr auto construct_from = T{Vs...}; // #1\n> >    struct Inner {};\n> >    struct Outer { Inner m; };\n> >    constexpr auto r = substitute(^^construct_from,\n> >                                 { ^^Outer, ^^construct_from<Inner> });\n> > \n> > which crashes because the auto in #1 hadn't been deduced when we\n> > called eval_can_substitute -> lookup_template_variable when processing\n> > the substitute call.\n> \n> Hmm, why wasn't it already deduced when we took the reflection? Likewise for\n> the resolve_, actually.\n\nIt seems like I can do the instantiation earlier, in get_reflection.  We\nalready perform the resolve_ there, so eval_can_substitute probably does\nnot need to do it again.\n \nThus, v2:\n\nBootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?\n\n-- >8 --\nIn substitute7.C we have\n\n  template <typename T, auto ... Vs>\n  constexpr auto construct_from = T{Vs...}; // #1\n  struct Inner {};\n  struct Outer { Inner m; };\n  constexpr auto r = substitute(^^construct_from,\n                               { ^^Outer, ^^construct_from<Inner> });\n\nwhich crashes because the auto in #1 hadn't been deduced when we\ncalled eval_can_substitute -> lookup_template_variable when processing\nthe substitute call.  We can call mark_used to resolve this, because\nit has:\n\n  if (undeduced_auto_decl (decl))\n    maybe_instantiate_decl (decl);\n\nand to make the json-parser.C test work, we have to do something\nsimilar in eval_substitute, otherwise we crash due to another\nundeduced auto.\n\n\tPR c++/123613\n\ngcc/cp/ChangeLog:\n\n\t* reflect.cc (get_reflection): Call mark_used.\n\t(eval_can_substitute): Don't resolve_nondeduced_context here.\n\t(eval_substitute): Call lookup_and_finish_template_variable instead\n\tof lookup_template_variable and finish_template_variable.\n\ngcc/testsuite/ChangeLog:\n\n\t* g++.dg/reflect/json-parser.C: New test.\n\t* g++.dg/reflect/substitute7.C: New test.\n\t* g++.dg/reflect/test.json: New test.\n---\n gcc/cp/reflect.cc                          |  16 +-\n gcc/testsuite/g++.dg/reflect/json-parser.C | 161 +++++++++++++++++++++\n gcc/testsuite/g++.dg/reflect/substitute7.C |  14 ++\n gcc/testsuite/g++.dg/reflect/test.json     |   4 +\n 4 files changed, 188 insertions(+), 7 deletions(-)\n create mode 100644 gcc/testsuite/g++.dg/reflect/json-parser.C\n create mode 100644 gcc/testsuite/g++.dg/reflect/substitute7.C\n create mode 100644 gcc/testsuite/g++.dg/reflect/test.json\n\n\nbase-commit: 5cd3889135d77bf951e4ffe169868b453c36257d","diff":"diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc\nindex 0b1d3a36f37..e4313ae5e98 100644\n--- a/gcc/cp/reflect.cc\n+++ b/gcc/cp/reflect.cc\n@@ -212,8 +212,14 @@ get_reflection (location_t loc, tree t, reflect_kind kind/*=REFLECT_UNDEF*/)\n      overload resolution for the expression &S with no target shall\n      select a unique function; R represents that function.  */\n   else if (!processing_template_decl && t != unknown_type_node)\n-    /* Resolve all TEMPLATE_ID_EXPRs here.  */\n-    t = resolve_nondeduced_context_or_error (t, tf_warning_or_error);\n+    {\n+      /* Resolve all TEMPLATE_ID_EXPRs here.  */\n+      t = resolve_nondeduced_context_or_error (t, tf_warning_or_error);\n+      /* The argument could have a deduced return type, so we need to\n+\t instantiate it now to find out its type.  */\n+      if (!mark_used (t))\n+\treturn error_mark_node;\n+    }\n \n   /* For injected-class-name, use the main variant so that comparing\n      reflections works (cf. compare3.C).  */\n@@ -5359,7 +5365,6 @@ eval_can_substitute (location_t loc, const constexpr_ctx *ctx,\n \treturn throw_exception (loc, ctx,\n \t\t\t\t\"invalid argument to can_substitute\",\n \t\t\t\tfun, non_constant_p, jump_target);\n-      a = resolve_nondeduced_context (a, tf_warning_or_error);\n       a = convert_from_reference (a);\n       TREE_VEC_ELT (rvec, i) = a;\n     }\n@@ -5432,10 +5437,7 @@ eval_substitute (location_t loc, const constexpr_ctx *ctx,\n       return get_reflection_raw (loc, ret, REFLECT_VALUE);\n     }\n   else if (variable_template_p (r))\n-    {\n-      ret = lookup_template_variable (r, rvec, tf_none);\n-      ret = finish_template_variable (ret, tf_none);\n-    }\n+    ret = lookup_and_finish_template_variable (r, rvec, tf_none);\n   else\n     {\n       if (DECL_FUNCTION_TEMPLATE_P (r))\ndiff --git a/gcc/testsuite/g++.dg/reflect/json-parser.C b/gcc/testsuite/g++.dg/reflect/json-parser.C\nnew file mode 100644\nindex 00000000000..6c54bb51704\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/reflect/json-parser.C\n@@ -0,0 +1,161 @@\n+// PR c++/123613\n+// { dg-do compile { target c++26 } }\n+// { dg-additional-options \"-freflection\" }\n+\n+#include <meta>\n+#include <charconv>\n+#include <string>\n+#include <algorithm>\n+\n+template <std::meta::info ...Ms>\n+struct Outer {\n+    struct Inner;\n+    consteval {\n+        define_aggregate(^^Inner, {Ms...});\n+    }\n+};\n+\n+template <std::meta::info ...Ms>\n+using Cls = Outer<Ms...>::Inner;\n+\n+template <typename T, auto ... Vs>\n+constexpr auto construct_from = T{Vs...};\n+\n+consteval std::meta::info parse_json(std::string_view json) {\n+  auto cursor = json.begin();\n+  auto end = json.end();\n+\n+  auto is_whitespace = [](char c) {\n+    return c == ' ' || c == '\\n' || c == '\\t';\n+  };\n+\n+  auto skip_whitespace = [&] -> void {\n+    while (is_whitespace(*cursor)) cursor++;\n+  };\n+\n+  auto expect_consume = [&] (char c) -> void {\n+    skip_whitespace();\n+    if (cursor == end || *(cursor++) != c) throw \"unexpected character\";\n+  };\n+\n+  auto parse_until = [&](std::vector<char> delims, std::string &out) -> void {\n+    skip_whitespace();\n+    while (cursor != end &&\n+           !std::ranges::any_of(delims, [&](char c) { return c == *cursor; }))\n+      out += *(cursor++);\n+  };\n+\n+  auto parse_delimited = [&](char lhs, std::string &out, char rhs) -> void {\n+    skip_whitespace();\n+    expect_consume(lhs);\n+    parse_until({rhs}, out);\n+    expect_consume(rhs);\n+  };\n+\n+  auto parse_value = [&](std::string &out) -> void {\n+    skip_whitespace();\n+\n+    bool quoted = false;\n+    unsigned depth = 0;\n+    while (true) {\n+      if (cursor == end) throw \"unexpected end of stream\";\n+      if (is_whitespace(*cursor) && !quoted && depth == 0)\n+        break;\n+\n+      if (depth == 0 && (*cursor == ',' || *cursor == '}'))\n+        break;\n+      out += *(cursor++);\n+\n+      if (out.back() == '{')\n+        ++depth;\n+      else if (out.back() == '}')\n+        --depth;\n+      else if (out.back() == '\"') {\n+        if (quoted && depth == 0)\n+          break;\n+        quoted = true;\n+      }\n+    };\n+  };\n+\n+  skip_whitespace();\n+  expect_consume('{');\n+\n+  std::vector<std::meta::info> members;\n+  std::vector<std::meta::info> values = {^^void};\n+\n+  using std::meta::reflect_constant, std::meta::reflect_constant_string;\n+  while (cursor != end && *cursor != '}') {\n+    std::string field_name;\n+    std::string value;\n+\n+    std::meta::info parsed_type;\n+\n+    parse_delimited('\"', field_name, '\"');\n+    expect_consume(':');\n+    parse_value(value);\n+\n+    if (value.empty()) throw \"expected value\";\n+    if (cursor == end) throw \"unexpected end of stream\";\n+\n+    if (value[0] == '\"') {\n+      if (value.back() == '}' && value[1] == 'f') throw \"hmm\";\n+      if (value.back() != '\"') throw \"expected end of string\";\n+      std::string_view contents(&value[1], value.size() - 2);\n+\n+      auto dms = data_member_spec(^^char const*, {.name=field_name});\n+      members.push_back(reflect_constant(dms));\n+      values.push_back(reflect_constant_string(contents));\n+    } else if (value[0] >= '0' && value[0] <= '9') {\n+      int contents = [](std::string_view in) {\n+        int out;\n+        std::from_chars(in.data(), in.data() + in.size(), out);\n+        return out;\n+      }(value);\n+\n+      auto dms = data_member_spec(^^int, {.name=field_name});\n+      members.push_back(reflect_constant(dms));\n+      values.push_back(reflect_constant(contents));\n+    } else if (value[0] == '{') {\n+      std::meta::info parsed = parse_json(value);\n+\n+      auto dms = data_member_spec(type_of(parsed), {.name=field_name});\n+      members.push_back(reflect_constant(dms));\n+      values.push_back(parsed);\n+    }\n+    skip_whitespace();\n+    if (cursor != end && *cursor == ',')\n+      ++cursor;\n+  }\n+  if (cursor == end) throw \"hmm\";\n+  expect_consume('}');\n+\n+  values[0] = substitute(^^Cls, members);\n+  return substitute(^^construct_from, values);\n+}\n+\n+struct JSONString {\n+    std::meta::info Rep;\n+    consteval JSONString(const char *Json) : Rep{parse_json(Json)} {}\n+};\n+\n+template <JSONString json>\n+consteval auto operator\"\"_json() {\n+    return [:json.Rep:];\n+}\n+\n+template <JSONString json>\n+inline constexpr auto json_to_object = [: json.Rep :];\n+\n+constexpr const char data[] = {\n+    #embed \"test.json\"\n+    , 0\n+};\n+\n+int main() {\n+  constexpr auto v = json_to_object<data>;\n+  static_assert(std::string_view(v.outer) == \"text\");\n+  static_assert(v.inner.number == 2996);\n+  static_assert(std::string_view(v.inner.field) == \"yes\");\n+  static_assert(R\"({\"field\": \"yes\", \"number\": 2996})\"_json.number == 2996);\n+}\ndiff --git a/gcc/testsuite/g++.dg/reflect/substitute7.C b/gcc/testsuite/g++.dg/reflect/substitute7.C\nnew file mode 100644\nindex 00000000000..15bacd09371\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/reflect/substitute7.C\n@@ -0,0 +1,14 @@\n+// PR c++/123613\n+// { dg-do compile { target c++26 } }\n+// { dg-additional-options \"-freflection\" }\n+\n+#include <meta>\n+\n+template <typename T, auto ... Vs>\n+constexpr auto construct_from = T{Vs...};\n+\n+struct Inner {};\n+struct Outer { Inner m; };\n+\n+constexpr auto r = substitute(^^construct_from,\n+                             { ^^Outer, ^^construct_from<Inner> });\ndiff --git a/gcc/testsuite/g++.dg/reflect/test.json b/gcc/testsuite/g++.dg/reflect/test.json\nnew file mode 100644\nindex 00000000000..b95cd80b88e\n--- /dev/null\n+++ b/gcc/testsuite/g++.dg/reflect/test.json\n@@ -0,0 +1,4 @@\n+{ \n+    \"outer\": \"text\", \n+    \"inner\": { \"field\": \"yes\", \"number\": 2996 }\n+} \n","prefixes":["v2"]}