jank reimagines C++ errors and gets an official native package repo
jank reimagines C++ errors and gets an official native package repo
jank 重构 C++ 错误报告并获得官方原生包仓库
Sep 04, 2026 · Jeaye Wilkerson 2026年9月4日 · Jeaye Wilkerson
Welcome back! It’s been three months since my last post and the amount of improvements to jank are staggering. Let me tell you all about it! Before jumping into the details, though, I want to say thank you to my Github sponsors and to Clojurists Together for sponsoring me this whole year. Thank you! 欢迎回来!距离我上一篇文章已经过去三个月了,jank 的改进程度令人震惊。让我来详细介绍一下!不过在进入细节之前,我想感谢我的 Github 赞助者以及 Clojurists Together 在这一整年里对我的资助。谢谢你们!
Error reporting
错误报告
Last year, I published a post showcasing how jank has reimagined Clojure errors. In every way, I aim for jank compiler errors to be more helpful, provide more context, and be more visually appealing than the Clojure JVM counterpart. Even in complex scenarios involving macros, jank excels. 去年,我发表了一篇文章,展示了 jank 如何重构 Clojure 的错误报告。我致力于让 jank 的编译器错误在各个方面都比 Clojure JVM 版本更有帮助、提供更多上下文,并且在视觉上更美观。即使在涉及宏的复杂场景中,jank 也表现出色。
However, I hadn’t yet tackled a couple of other areas of error reporting: runtime exceptions and C++ interop errors. 然而,我还没有解决错误报告中的另外两个领域:运行时异常和 C++ 互操作错误。
Uncaught runtime exceptions
未捕获的运行时异常
In today’s modern C++ compilers, there’s no standard, portable way to get a stack trace. Coming from the JVM, this may sound surprising, but it’s par for course in the native world. Even worse, jank is JIT compiling C++ code and we want to get accurate stack traces which include those frames as well. Even worse, we need to map some of that C++ back to actual jank code. So, in order to get beautiful, accurate stack traces for jank’s uncaught runtime exceptions, there was a lot of work to be done. Check out the results! 在当今现代 C++ 编译器中,没有一种标准且可移植的方法来获取堆栈跟踪。对于来自 JVM 背景的人来说,这听起来可能很惊讶,但在原生开发领域这很常见。更糟糕的是,jank 正在对 C++ 代码进行 JIT 编译,我们希望获得包含这些帧的准确堆栈跟踪。更进一步,我们需要将部分 C++ 代码映射回实际的 jank 代码。因此,为了给 jank 的未捕获运行时异常提供美观、准确的堆栈跟踪,我们需要做大量工作。来看看结果吧!
As you can see in the figure above, an exception was thrown from the C++ code backing clojure.core/subs. jank properly reports the error by pointing at the nearest user’s Clojure call, skipping over the one in clojure.core which calls the C++ function. In the stack trace, we can see two Clojure-specific frames, numbered as #1 and #3. First, we see the frame for clojure.core/subs. Then we see the frame for user/foo, which actually does the call to subs. Note that both of these frames include the exact arity that was used, as well as the precise source location in their respective jank files.
正如上图所示,异常是从支持 clojure.core/subs 的 C++ 代码中抛出的。jank 通过指向最近的用户 Clojure 调用来正确报告错误,跳过了 clojure.core 中调用该 C++ 函数的部分。在堆栈跟踪中,我们可以看到两个 Clojure 特有的帧,编号为 #1 和 #3。首先是 clojure.core/subs 的帧,然后是实际调用 subs 的 user/foo 帧。请注意,这两个帧都包含了所使用的确切元数(arity),以及它们在各自 jank 文件中的精确源代码位置。
What you’re not seeing here is that this stack trace is pulling debug info from three separate places: 你在这里看不到的是,这个堆栈跟踪是从三个不同的地方提取调试信息的:
- The current executable, for all of the non-Clojure frames.
- 当前的可执行文件,用于所有非 Clojure 帧。
- An AOT-compiled object file, which was loaded when clojure.core was required. This is equivalent to Clojure JVM’s .class files.
- 一个 AOT 编译的目标文件,在加载
clojure.core时被载入。这相当于 Clojure JVM 的.class文件。 - A JIT-compiled object file, which was added to the LLVM JIT runtime when the user/foo function was compiled.
- 一个 JIT 编译的目标文件,在编译
user/foo函数时被添加到 LLVM JIT 运行时中。
After my recent efforts, jank now weaves all of these together seamlessly to provide you a lovely error report. This works reliably on macOS and on Linux. 经过我最近的努力,jank 现在可以将所有这些信息无缝地编织在一起,为您提供美观的错误报告。这在 macOS 和 Linux 上都能可靠运行。
Error pages
错误页面
Building on the error output above, you may also notice the URL that’s tucked into the bottom of the code snippet. Since the original error reporting design last year, I have intended for jank to have a dedicated error page for each error. Each page should provide more information about the error, common causes, and suggested fixes. All of these pages have now been created and are part of the jank book. Some of them are more bare-bones than others and I plan to continue filling them in over time. Getting them created sooner will start aiding in SEO, though, which will help ensure that if you search for any jank errors you hit, the right resources will be shown to you. 基于上述错误输出,您可能还会注意到代码片段底部嵌入的 URL。自去年最初的错误报告设计以来,我就打算为每个错误提供一个专门的错误页面。每个页面都应提供有关错误的更多信息、常见原因和建议的修复方法。所有这些页面现在都已经创建完成,并成为 jank 手册的一部分。其中一些页面目前还比较简陋,我计划随着时间的推移继续完善它们。尽早创建这些页面将有助于 SEO,确保当您搜索遇到的任何 jank 错误时,能够显示正确的资源。
C++ candidates
C++ 候选方案
I have saved the best for last, as far as error messages go. We know that Clojure is infamous for its error messages and I hope to have shown how jank addresses that. However, C++ is also infamous for its error messages and jank is just as much C++ as it is Clojure. C++ is a much scarier beast when it comes to all of the possible things that can go wrong, though. So how can we reimagine C++ error messages? Well, I gave it my best shot. Take a look. :) 在错误消息方面,我把最好的留到了最后。我们知道 Clojure 的错误消息以“臭名昭著”著称,我希望我已经展示了 jank 是如何解决这个问题的。然而,C++ 的错误消息同样以“臭名昭著”著称,而 jank 既是 Clojure 也是 C++。当涉及到所有可能出错的情况时,C++ 是一个更可怕的野兽。那么,我们该如何重构 C++ 错误消息呢?好吧,我尽了最大努力。来看看吧。:)
The call is ambiguous because the second argument is an int, which directly matches neither long nor short but can implicitly be converted to either of them. jank’s AST is intertwined with Clang’s AST, so we can extract all of the necessary information to render this neatly. Unlike Clang, or GCC, jank renders these in a table format which I find to be incredibly succinct and appealing.
该调用存在歧义,因为第二个参数是 int,它既不直接匹配 long 也不匹配 short,但可以隐式转换为两者中的任何一个。jank 的 AST 与 Clang 的 AST 交织在一起,因此我们可以提取所有必要的信息来整齐地呈现这一点。与 Clang 或 GCC 不同,jank 以表格格式呈现这些信息,我觉得这非常简洁且美观。
Also, as a bonus, the signature and source information for these bar functions is correct, even when they’re declared inside of a cpp/raw in a jank file. Let’s take a look at another one.
此外,作为额外奖励,这些 bar 函数的签名和源信息是正确的,即使它们是在 jank 文件的 cpp/raw 中声明的。让我们再看一个例子。
When there are many candidates to report, jank optimizes useful output by ranking the candidates based on argument count, required conversions, as well as access levels. By default, jank will only show the top three candidates. 当有许多候选方案需要报告时,jank 会根据参数数量、所需的转换以及访问级别对候选方案进行排名,从而优化有用的输出。默认情况下,jank 只会显示前三个候选方案。
Finally, I’ll show one more image, which is of a special kind of ambiguity with some jank-specific behavior. On top of normal C++ overloading, implicit conversions, etc, jank also supports automatic trait conversions, which use a well-known trait to convert to/from jank objects and native values. If an argument to a native function is a jank object, the compiler will consider whether or not a trait conversion can be used. However, this can result in ambiguities, too, if multiple candidates are viable. Here’s an example. 最后,我再展示一张图片,它展示了一种具有 jank 特定行为的特殊歧义。除了正常的 C++ 重载、隐式转换等之外,jank 还支持自动特征(trait)转换,它使用众所周知的特征在 jank 对象和原生值之间进行转换。如果原生函数的参数是 jank 对象,编译器会考虑是否可以使用特征转换。然而,如果多个候选方案都可行,这也可能导致歧义。这是一个例子。
There’s a lot more that jank can already do with these C++ candidate failures, but I can only show so much in a blog post. I’m sure you’ll see more next time you’re writing some jank code! jank 在处理这些 C++ 候选失败方面已经能做更多事情了,但我只能在博客文章中展示这么多。我相信下次你编写 jank 代码时会看到更多!
Why bother with all of this?
为什么要费这些功夫?
You may not be as excited as I am about these images of error reports. That’s understandable. It’s partly a compiler nerd thing, since effective error reporting can be quite tricky. However, it’s also partly a huge usability win over not only Clojure JVM, not only Clang and GCC, but also the status quo in a lot of developer tooling. I am trying to build a language, and tooling ecosystem, that is the best it can be. That’s the language I want to use. I want to entice others to try it by dedicating this time to usability, too. 你可能不像我那样对这些错误报告的图片感到兴奋。这是可以理解的。这在一定程度上是编译器极客的事情,因为有效的错误报告可能非常棘手。然而,这在很大程度上也是可用性的巨大胜利,不仅超越了 Clojure JVM、Clang 和 GCC,也超越了许多开发工具的现状。我正在努力构建一种语言及其工具生态系统,使其达到最佳状态。这就是我想使用的语言。我也想通过投入时间来提升可用性,从而吸引其他人尝试它。
To me, this is incredibly important. 对我来说,这极其重要。
Interlude
插曲
Before we move on to the next topic, please consider subscribing to jank’s mailing list. This is a great way to make sure you stay up to date. 在进入下一个主题之前,请考虑订阅 jank 的邮件列表。这是确保您随时了解最新动态的好方法。