{"id":597658,"date":"2025-09-15T08:33:06","date_gmt":"2025-09-15T07:33:06","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=clion&#038;p=597658"},"modified":"2026-02-24T11:17:32","modified_gmt":"2026-02-24T10:17:32","slug":"introducing-constexpr-debugger","status":"publish","type":"clion","link":"https:\/\/blog.jetbrains.com\/zh-hans\/clion\/2025\/09\/introducing-constexpr-debugger","title":{"rendered":"Introducing the Constexpr Debugger"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\u201cNot a constant expression.\u201d You\u2019ve seen the diagnostics and the note trail, but never the actual state. Until now.<\/h3>\n\n\n\n<p>Modern C++ pushes more logic into <code>constexpr<\/code>\/<code>consteval<\/code>: parsers, tables, DSLs, hashing \u2013 real code with real branches. When code fails at compile time, you can either try to guess the reason from the compiler\u2019s notes or de\u2011<code>constexpr<\/code> it and hope the runtime reproduction matches what the compiler actually did.<\/p>\n\n\n\n<p>The new Constexpr Debugger available in the first <a href=\"https:\/\/www.jetbrains.com\/clion\/nextversion\/\" target=\"_blank\" rel=\"noopener\">CLion 2025.3 EAP build<\/a> allows you to stay in the compiler\u2019s world and see what really happens \u2013 by stepping through evaluation, inspecting values, and confirming which <code>if constexpr <\/code>branch fired. Using it helps you understand exactly what the compiler is doing and fix issues faster.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1817\" height=\"1314\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/intro_image-1.png\" alt=\"\" class=\"wp-image-599091\"\/><\/figure>\n\n\n\n<p>With reflection coming in C++26 (<a href=\"https:\/\/wg21.link\/p2996r13\" target=\"_blank\" rel=\"noopener\">P2996R13<\/a>), compile\u2011time code will increase even more. Although none of the compilers support reflection yet, the Constexpr Debugger in CLion provides a foundation that will allow for debugging this metacode in the future.<\/p>\n\n\n\n<p align=\"center\"><a class=\"jb-download-button\" href=\"https:\/\/www.jetbrains.com\/clion\/nextversion\/\" target=\"_blank\" rel=\"noopener\" data-test=\"blog-article-cta\"><i class=\"download-icon\"><\/i>DOWNLOAD CLION 2025.3 EAP<\/a><\/p>\n\n\n\n<p><strong>Updated:<\/strong> Refer to our <a href=\"https:\/\/www.jetbrains.com\/help\/clion\/constexpr-debugger.html\" target=\"_blank\" rel=\"noopener\">documentation<\/a> to learn more about working with the Constexpr Debugger in CLion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Features<\/h2>\n\n\n\n<p>Here is what you currently can do with the Constexpr Debugger:<\/p>\n\n\n\n<ul>\n<li>Start step-by-step debugging from the gutter by clicking the <em>Debug<\/em> button next to <code>static_assert(...) <\/code>or a <code>constexpr<\/code> declarator to check how it was evaluated or why it failed.<\/li>\n\n\n\n<li>Use the same actions as in the regular debugger: <em>Step Into,<\/em> <em>Step Over<\/em>, <em>Step Out<\/em>, and <em>Restart<\/em>. Additionally, you can use <em>Step Backward<\/em>, the compile-time reverse stepping feature.<\/li>\n\n\n\n<li>See what the compiler sees: the call stack, locals, last returns, and template arguments of the current instantiation.&nbsp;<\/li>\n\n\n\n<li>Hover over a variable to see its values, use <em>Evaluate Expression<\/em>, or navigate to the source code from the call stack.<\/li>\n\n\n\n<li>Inspect the entire context when constant evaluation fails to determine when and why it occurred.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">How to use the Constexpr Debugger<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Basic case<\/h3>\n\n\n\n<p>To provide you with an overview of the basic actions that you can perform when debugging <code>constexpr<\/code>, we\u2019ll use this compile-time Fibonacci cache implementation:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;array>\n\ntemplate&lt;std::size_t N>\nstruct FibCache {\n    std::array&lt;int, N + 1> memo{};\n\n    constexpr FibCache() {\n        memo[0] = 0; memo[1] = 1;\n        for (std::size_t i = 2; i &lt;= N; ++i)\n            memo[i] = memo[i - 1] + memo[i - 2];\n    }\n\n    constexpr int operator()(int n) const { return memo[n]; }\n};\n\nconstexpr int get_fibonacci(int n) {\n    FibCache&lt;8> cache;\n    auto result = cache(n);\n    return result;\n}\n\nconstexpr int k = get_fibonacci(6);          \/\/ gutter: step through operator()\nstatic_assert(get_fibonacci(6) == 8, \"ok\");  \/\/ gutter available here too<\/pre>\n\n\n\n<p><br>The following are some basic operations you can perform when debugging:<\/p>\n\n\n\n<ul>\n<li>Find the green <em>Debug<\/em> icon next to the <code>constexpr<\/code> declarator or <code>static_assert<\/code> (either will do) and click it to run a Constexpr Debugger session.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1228\" height=\"115\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/image4.png\" alt=\"\" class=\"wp-image-598995\"\/><\/figure>\n\n\n\n<ul>\n<li>Use the regular debugger actions, or try <em>Step Backward<\/em> to time\u2011travel one step back in your compile\u2011time evaluation.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n                                                <figure class=\"wp-block-image size-full\"><img decoding=\"async\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/stepping_new.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/stepping_new_preview.jpg\" alt=\"\" width=\"1200\" class=\"wp-image-599017\"\/><\/figure>\n                        \n\n\n<ul>\n<li>Inspect the state:\n<ul>\n<li>Watch how the <code>this-&gt;memo<\/code> array fills during construction by hovering over it in the editor (see the screenshot below).<\/li>\n\n\n\n<li>Navigate to the <a href=\"https:\/\/www.jetbrains.com\/help\/clion\/examining-suspended-program.html#variables\" target=\"_blank\" rel=\"noopener\"><em>Variables<\/em> pane<\/a> to see <code>this<\/code>, locals (like <code>i<\/code>), and recent returns.<\/li>\n\n\n\n<li>Hover over any variable to see its value.<\/li>\n\n\n\n<li>Navigate to the <em>Call Stack<\/em> to see frames mapped to source.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1230\" height=\"671\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/image1.png\" alt=\"\" class=\"wp-image-599031\"\/><\/figure>\n\n\n\n<ul>\n<li>Check the result in the <em>Variables<\/em> pane after the constructor has precomputed all the Fibonacci numbers:<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1032\" height=\"534\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/image6.png\" alt=\"\" class=\"wp-image-599043\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Edge case: non-constexpr call prevents evaluation<\/h3>\n\n\n\n<p>Consider the case when we have a function (<code>fail<\/code>) that is not constexpr \u2013 it\u2019s deliberately called to indicate invalid input. This means that the original expression (<code>parse_int(\u201chello\u201d)<\/code>) cannot be evaluated at compile time.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;string_view>\n\nvoid fail(); \/\/ note: non-constexpr\n\nconsteval int parse_digit(char c) {\n    if (c &lt; '0' || c > '9') {\n        fail();\n    }\n\n    return c - '0';\n}\n\nconsteval int parse_int(std::string_view s) {\n    int result = 0;\n    for (std::size_t i = 0; i &lt; s.size(); i++) {\n        result = result * 10 + parse_digit(s[i]);\n    }\n\n    return result;\n}\n\nconstexpr auto digit = parse_int(\"12345\");\nconstexpr auto bad_digit = parse_int(\"hello\"); \/\/ start from the gutter<\/pre>\n\n\n\n<p>Stepping here lets you see <code>c == 'x'<\/code>, the taken branch, the value of argument c, and the exact point at which the evaluation fails:<\/p>\n\n\n                                                <figure class=\"wp-block-image size-full\"><img decoding=\"async\" data-gif-src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/edge_case_cropped.gif\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2025\/09\/edge_case_preview.jpg\" alt=\"\" width=\"1200\" class=\"wp-image-599080\"\/><\/figure>\n                        \n\n\n<h2 class=\"wp-block-heading\">Current limitations<\/h2>\n\n\n\n<ul>\n<li>Breakpoints and <em>Run to Cursor \/ Force Run to Cursor<\/em> are not supported in <code>constexpr<\/code> evaluation.<\/li>\n\n\n\n<li><strong>Updated:<\/strong> We&#8217;ve added support for C++ 20 modules to the <a href=\"https:\/\/www.jetbrains.com\/clion\/nextversion\/\" target=\"_blank\" rel=\"noopener\">2025.3 EAP<\/a> (<a href=\"https:\/\/youtrack.jetbrains.com\/issue\/CPP-46489\" target=\"_blank\" rel=\"noopener\">CPP-46489<\/a>). <s>C++20 modules aren\u2019t yet supported: <code>constexpr<\/code> debugging does not navigate to entities located in imported modules.<\/s><\/li>\n\n\n\n<li><strong>Updated:<\/strong> The stepping logic has been improved. This update is available in the <a href=\"https:\/\/www.jetbrains.com\/clion\/nextversion\/\" target=\"_blank\" rel=\"noopener\">2025.3 EAP<\/a> (<a href=\"https:\/\/youtrack.jetbrains.com\/issue\/CPP-46841\" target=\"_blank\" rel=\"noopener\">CPP-46841<\/a>, <a href=\"https:\/\/youtrack.jetbrains.com\/issue\/CPP-46867\" target=\"_blank\" rel=\"noopener\">CPP-46867<\/a>). <s>Stepping may not work properly with some language constructs or compound statements.<\/s><\/li>\n\n\n\n<li>Some constructs aren\u2019t yet supported by our <code>constexpr<\/code> evaluator. We track these unsupported cases in <a href=\"https:\/\/youtrack.jetbrains.com\/issues?q=%23rscpp%20%23cpp-constexpr-evaluation%20&amp;u=1\" target=\"_blank\" rel=\"noopener\">YouTrack<\/a>.<\/li>\n<\/ul>\n\n\n\n<p>We don\u2019t have a definite timeline for when these missing bits of support might be added, but we\u2019re working on them, so stay tuned!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Share your feedback<\/h2>\n\n\n\n<p>Your feedback is essential for improving this feature and making it more robust. We encourage you to try the Constexpr Debugger and share your thoughts and suggestions by submitting a ticket to our <a href=\"https:\/\/youtrack.jetbrains.com\/issues\/CPP\/\" target=\"_blank\" rel=\"noopener\">issue tracker<\/a> or commenting below.<\/p>\n\n\n\n<p align=\"center\"><a class=\"jb-download-button\" href=\"https:\/\/www.jetbrains.com\/clion\/nextversion\/\" target=\"_blank\" rel=\"noopener\" data-test=\"blog-article-cta\"><i class=\"download-icon\"><\/i>DOWNLOAD CLION 2025.3 EAP<\/a><\/p>\n","protected":false},"author":1580,"featured_media":599105,"comment_status":"closed","ping_status":"closed","template":"","categories":[826,89],"tags":[811,7110,264,600],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/clion\/597658"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/clion"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/types\/clion"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/users\/1580"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/comments?post=597658"}],"version-history":[{"count":9,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/clion\/597658\/revisions"}],"predecessor-version":[{"id":683563,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/clion\/597658\/revisions\/683563"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media\/599105"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/media?parent=597658"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/categories?post=597658"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/tags?post=597658"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/zh-hans\/wp-json\/wp\/v2\/cross-post-tag?post=597658"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}