1. carbon-lang介绍
自去年(2020.7)Chandler Carruth 作为 Google 的 tech leader 官方宣布 Carbon 语言,这个项目在 github 上热度一度飙升,至今已有30k+的 star。
Carbon 作为一个实验性的通用编程语言,旨在成为“C++的后继语言”[1],目前(2023.10)仍在项目孵化期中,预计2-3年内结束实验[2],Carbon 由于它的设计和目标,实现编译器会具有非常大难度与挑战性,因而 Chandler 将它作为一个 long-term 的开发项目,预期在2026年发布1.0版本。
但即使是可能要在比较长的时间后才能看到生产可用的版本,目前的 Carbon 仓库代码也仍然值得学习,包含非常多足够现代的 C++ 实现方式,本系列文章就从 Carbon 开始,介绍并分析一个完整的现代语言编译器的实现过程,以及它背后的设计思想。
Carbon 官方项目选择 Bazel 构建工具,一方面是因为 Google 内部工具链的高可用性,相比于 Go 语言早期手搓 Makefile 的构建方式,Carbon 语言选用更加现代的工具链作为构建工具,另一方面也由于 Google 在LLVM之上的积累和贡献[3],LLVM 目前仓库中 Bazel 构建方式由 Google 团队完成及合并(不过存在一些 build 的问题[4],并且随着 llvm 的版本迭代常需要对 Bazel 进行一些 patch)。
我提供了一个 Fork 于 Carbon 所修改的仓库:https://github.com/CanftIn/cocktail-lang,为深入剖析现代编译器前端实现,一步一步进行其中的构建,项目按照LLVM仓库主流构建方式和代码结构组织,后续的系列文章也按这个仓库和 Carbon 官方仓库共同进行讲解。
该项目在 Ubuntu 22.04 系统环境下测试完成,其他环境暂未测试。
2. C++后继语言
2022年被称为 C++ 后继语言(successor languages)元年。三种 C++ 后继语言被宣布。
首先,Dave Abrahams 和 Dimitri Racordon 在 C++ Now 上宣布了 Val 语言(后更名为hylo-lang)。Val 的核心思想是,人们可以使用可变的值语义来构建安全、高效的程序。
两个月后,Chandler Carruth 在 CppNorth 上宣布了 Carbon 语言。Carbon 语言试图解决 C++ 的几个问题:数十年的技术债务,对向后兼容性的优先考虑以及 C++ 的演变效率的问题。
再过两个月,在 CppCon 上,Herb Sutter 宣布了 CppFront,作为 C++ 的可能后继者。宣称的目标是使 C++ 更安全50倍,更简单10倍。
而早在更早之前 github 上的项目 circle (闭源)就已经完成了大量 C++ 之上的现代语言特性。
为什么 C++ 存在近40年仍然经久不衰?
C++ 是一种特殊的编程语言,具有多种编程范式。它是最常用的编程语言之一,但也是最受批评的语言之一。热爱 C++ 的人备受喜爱,但更多的人都抱怨这门语言太大、太复杂,既有不必要的功能,又缺乏足够的功能。过于概括地说,C++ 似乎是一系列随机的功能组合,没有一个清晰连贯的主线。
Bjarne Stroustrup 为了辩护这门语言说:“within C++, there is a much smaller and cleaner language struggling to get out”(在 C++ 中,有一个更小、更干净的语言努力地想要脱颖而出)。尽管这句话旨在为 C++ 辩护,但仔细回想后会发现它也是一种隐含的批评:C++ 仍然没有成为人们期望的那种更小、更干净的语言。
C++ 的标准委员会一直在不断地改进这门语言。但是这也意味着 C++ 积累了大量的技术债务。这一部分是因为标准委员会一直在试图修复这门语言的错误,另部分是因为技术进步导致了新的需求。为了避免破坏现有的代码,C++ 标准委员会非常重视向后兼容性。这意味着新版本的 C++ 通常不能删除旧的或过时的功能。此外,C++ 标准委员会的工作方式导致了 C++ 形成了一个缓慢的演变过程。新功能必须经过多年的实验和讨论才能被添加到标准中。
那么什么是后继语言?C++的后继者应该是什么样子的?
- 相较于 C++,这是一种具有更少缺陷/错误的语言。
- 与 C++ 相比,这是一种具有友好特性的语言。
- 这是一种会比 C++ 更安全、更清晰、更高效的语言。
- 这是一种与现有 C++ 代码广泛兼容的语言(兼容方式可能是Subset/Superset/Overlap):
后继语言这个目标对于 C++ 来说是很值得尝试的目标,上面这张图也表示了去实现 C++ 后继语言的方式:新语言作为 C++ 的子集或者超集,又或者它们之间互有交叉。
引发出来的一个问题是,为了实现新语言,发明新的编译器是否富有成效与价值?Carbon 就是这样一个实验性项目,通过发展现有的 C++ 工具链如 LLVM 和 Clang 来实现新语言。Carbon 提出了新的目标:
- 默认快速:与 C++ 相比,Carbon 代码应该至少同样快。
- 默认安全:与Rust不同,Carbon 的设计师没有试图创造一个绝对安全的语言。而是创造一个默认安全的语言。
- 简单:删除了一些已经过时的功能,例如宏、模板、继承和异常,并引入了新的功能,例如类型推导和类型类。
尽管 Carbon 有许多有趣的想法,但它还需要经过时间的考验。特别是其简化 C++ 的目标,因为 C++ 是一个非常复杂的语言,这会是一个巨大的挑战。
Carbon 作为 C++ 后继语言采用的策略并非上面的 intersection 而是 C++ interop,即和现有的 C++ 代码进行交互,Carbon 与 C++ 之间具有互操作能力,类似于 TypeScript 之于 JavaScript、Kotlin 之于 Java、Swift 之于 Object-C,一个新语言的发展离不开一个性能优秀的编译器或解释器,但更重要的是语言之上的生态,为了成功,任何希望替代 C++ 的语言都必须与 C++ 有很好的互操作性,这也是 Carbon 的目标。Carbon 期望能利用现有 C++ 的生态的同时,去实践更多语言上的目标。
3. Cocktail CMake项目结构
首先从0到1构建CMake项目结构,参照clang的项目结构,初版结构链接:project_structure。
其中 include 作为Cocktail的头文件目录,lib 作为 Cocktail 的库文件目录,其中 C++ 文件一律以 .cc
作为后缀,unittests 作为 Cocktail 单元测试文件目录,并且其中单元测试一律以 .t.cc
作为后缀,单元测试依赖头文件以 .t.h
为后缀。
在主目录下 CMakeList.txt
文件中可以看到这条 add_compile_options(-fno-rtti)
编译选项,这里表明禁用 C++ 的 RTTI 特性,由于 LLVM 实现了自己的一套 RTTI 机制,此处加入该选项禁用。
项目依赖于 LLVM,CMake 中 set(LLVM_DIR /usr/lib/llvm-15/lib/cmake/llvm)
设置 LLVM 路径,需要按 readme 介绍使用 apt 包管理安装 llvm-15-dev
(项目开始暂不手动构建 LLVM),LLVM 默认 CMake 路径为 /usr/lib/llvm-15/lib/cmake/llvm
。
同时需要加入如下条件使得项目完成 LLVM 的引入:
|
|
关于内存泄漏检查,使用 valgrind 工具,可将其加入 ctest 中,对编译出的二进制目标进行测试:
|
|
3.1 clang-format使用
clang-format 作为 LLVM 官方提供的自动格式化工具,能够格式化排版 C++ 代码,统一代码样式,本仓库代码一律使用官方.clang-format[5]配置。
3.2 clang-tidy使用
clang-tidy 作为 C++ 的静态检查工具,因为它基于AST,比基于正则表达式的静态检查工具更为精准。本仓库代码一律使用官方.clang-tidy[6]配置。
3.3 项目调试的前置知识
由于本项目基于LLVM,需要用到诸如ADT、Support等 LLVM 基础库中的一些工具,ADT 中容器在使用 lldb vscode 前端调试时存在难以打印的问题,这里需要引入LLVM官方仓库中lldbDataFormatters插件[7],得以直观显示 LLVM 数据结构。
4. 引用
[1] : “C++的后继语言”
[2] : 实验
[3] : 贡献
[4] : 年久失修的问题
[5] : .clang-format
[6] : .clang-tidy
[7] : lldbDataFormatters插件