Solidity 编译器 v0.8.31 正式发布,默认 EVM 版本升级为 Fusaka (osaka),新增对 CLZ 操作码(EIP-7939)的支持,并扩展了存储布局规范器,允许使用常量变量作为基槽表达式。
我们很高兴地宣布 Solidity 编译器 v0.8.31 版本发布!
此版本的编译器支持 Fusaka 网络升级引入的新 EVM 功能,扩展了存储布局指定器的功能,并弃用了计划在 0.9.0 破坏性版本中移除的第一批功能。
我们还增加了官方 ARM Linux 构建。
随着 Fusaka 计划在主网上线,我们将其设为编译器的默认目标。
和往常一样,你仍然可以通过 --evm-version/settings.evmVersion 选择更早的版本。
此版本 Solidity 编译器包含对 CLZ 操作码(EIP-7939) 的支持。
该功能已在最近的预发布版本中可用。
该操作码用于计算一个 256 位字中前导零位的数量。
它是位操作、压缩算法以及某些数据结构的基本构建块。
在当前编译器的实现中,此操作码的应用有限,但我们正在探索其未来优化的潜力。
它的优势将在应用层得到充分体现。
诸如 solady 之类的库对它有很多用途,并且它将能够替代 OpenZeppelin 中现有的工具,例如 Math.clz()。
从此版本开始,我们将提供针对 ARM 架构的官方 Linux 二进制文件。
我们预计这不仅对使用 Linux 作为主要操作系统的开发者有用,也对在 ARM 硬件上通过 Linux 容器(例如使用 Docker)运行编译器二进制文件的其他系统用户有用。
请注意,编译器中的 ARM 支持并非新事物——我们早已为基于 ARM 的 Mac 提供二进制文件,并且一直可以在 ARM 上的 Linux 上从源代码构建编译器。
我们在此引入的是将这些二进制文件集成到我们的 CI 和发布基础设施中。
虽然非官方的 ARM 构建已从第三方来源提供一段时间,但现在你可以从所有其他官方编译器二进制文件的同一来源获取它们。
这也意味着它们会经过我们完整的测试流程,包括检查所有支持的平台产生的字节码和元数据是否相同。
到目前为止,我们只提供完整的编译器版本和夜间构建。
通过去年十月分享的 v0.8.31-pre.1,我们引入了一个更轻量的流程来填补空缺,并让我们能够更早地分享实验性功能。
预发布已经包含了我们在此展示的某些功能,例如对 CLZ 操作码的支持。
查看 Twitter 公告 获取更多详情!
每次发布,我们通过多个渠道提供编译器二进制文件,其中一些使用并不广泛。
为了减少维护负担,我们一直在计划停止其中一些渠道。
作为第一步,从此版本开始,我们正式停止我们的 Ubuntu PPA 作为二进制分发渠道。
我们打算至少再维持一个版本的 Docker 版本,但如果我们判断使用率很低,它们也很可能被停止。
不过请注意,它们已经从 DockerHub 迁移到了 Github 的容器注册表,新的二进制文件也将出现在那里。
Solidity 编译器 0.8.31 版本进一步扩展了存储布局指定器的功能。
现在可以在基础槽表达式中使用常量变量:
uint constant OFFSET = 0x10 + 1;
contract C layout at 0xAAAA + OFFSET {
uint[3] x; // 占据槽位 0xAABB..0xAABD
}
请记住,Solidity 中的编译时求值能力仍然非常有限,仅涵盖涉及有理数字面量的算术表达式。
这些计算始终以无限精度执行,因此将具有有限精度整数类型的值引入表达式(通过显式类型转换、使用某些内置函数、使用三元运算符等)将导致编译错误。
此限制同样适用于用于初始化常量的表达式。
我们计划在未来的版本中进一步扩展允许的表达式范围,特别是通过为常见表达式定义特殊的内置函数来绕过这些限制,但除此之外的任何事情都需要为语言添加一个更健壮的编译时求值系统。
0.9.0 破坏性版本将专注于抛弃旧的包袱,使编译器更精简。
为此,我们开始为将要移除的功能引入弃用警告。
目前包括:
这些函数最初引入是为了允许以太币转账,同时不让被调用者执行任何复杂操作。
由于 EVM 没有提供任何直接的方式来仅转账以太币而不执行目标合约,因此总是需要转发一些 gas,但这些函数将其限制为固定的 2300 gas 津贴。
以前这刚好足够读取存储和发出事件,但不能进行重入调用和写入存储。
不幸的是,这隐含了一个假设,即操作码定价永远不会改变,而这一假设很快被证明是错误的。
如今,它们的使用被广泛认为是反模式,<address>.call() 并携带空负载是推荐的替代方案。
请记住,这并不提供任何重入保护,因为默认情况下转发给被调用者的 gas 量没有限制。
如果你正是依赖 send() 和 transfer() 的这一特性,你应该考虑一种不同的机制,例如瞬态存储中的重入保护,或者重构代码以使用检查-效果-交互模式。
简单的以太币转账仍然有用,但编译器无法以面向未来的方式引入这样的机制。
这需要在 EVM 层面解决,例如 EIP-5920: PAY 操作码,该操作码可以在可用时以 <address>.pay() 的形式暴露。
ABI 编码器 v2 早在 0.4.19 中作为实验性功能引入。
最初它必须通过 ABI 编码器 pragma(当时是一个实验性的 pragma)显式启用。
我们在 0.7 发布周期开始将其视为稳定版本,并在 0.8.0 中成为默认设置。
v2 是 v1 的直接替代品,提供其所有功能,同时增加了额外能力:支持结构体、支持任意嵌套数组以及更广泛的输入验证。
重写对于使 ABI 编码器与 IR 管道一起使用是必要的。
ABI 编码器 v1 仅适用于传统的 evmasm 管道。
实际上,当使用 --via-ir 编译时,编译器会忽略 pragma 并始终使用 v2。
v2 从一开始就在 Yul 中实现,并且两种管道都可以使用。
ABI 编码器 v1 现在已经完全过时,最终将被移除。
就像普通函数一样,修饰符可以被声明为 virtual 并在派生合约中被覆盖。
我们普遍发现这一功能让用户感到惊讶且难以理解,主要成为编译器实现中棘手边缘情况的来源。
由于继承本身已经是语言中许多不必要复杂性的来源,而虚拟修饰符并非必不可少的功能(请注意,我们不会移除从修饰符内调用虚拟函数的能力),因此我们决定移除它们。
由于合约类型的变量本质上只是 address 的薄抽象,历史上语言允许对地址有意义的操作也可以在它们上执行。
实际上,直到 0.5.0,所有 address 成员也可用于合约类型。
随着对更显式转换的推动,这种情况已经改变,如今地址和合约类型之间有更清晰的区分。
然而,旧系统的一个残留是你可以在合约上使用比较运算符。
而且不仅仅是 == 和 !=。
像 c < d 这样的不等式在 c 和 d 代表合约时仍然是一个有效的表达式。
这里比较的是什么可能不清楚,并且由于合约在语法上与结构体非常相似,用户完全有理由认为结果以某种方式由合约的成员决定。
在 0.9.0 中,我们将开始要求对此类操作进行显式转换到 address,以使语义清晰。
内联汇编允许代码以与 Solidity 内存模型不兼容的方式使用内存。
因此,默认情况下,如果存在包含内存操作或分配给 Solidity 内存变量的内联汇编块,某些内存优化会在全局范围内被禁用。
为了绕过这个问题,语言允许程序员使用 memory-safe 注解 声明某个汇编块实际上是完全安全的:
assembly ("memory-safe") {
...
}
然而,由于该注解无法在它引入之前的版本中使用,我们还提供了一种向后兼容的临时替代机制,使用特殊的 NatSpec 注释来注解此类块:
/// @solidity memory-safe-assembly
assembly {
...
}
由于 0.9.0 无论如何都不会向后兼容以前的版本,我们将移除此替代机制。
要升级到 Solidity 编译器的最新版本,请遵循我们文档中的安装说明。
你可以在此处下载 Solidity 的新版本:v0.8.31。
如果你想从源代码构建,请不要使用 GitHub 自动生成的源代码存档。
请改用 solidity_0.8.31.tar.gz 源代码 tarball 或通过 git 检出 v0.8.31 标签。
最后但同样重要的是,我们要向所有帮助实现此版本的贡献者表示衷心的感谢!
- 原文链接: soliditylang.org/blog/20...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!
作者暂未设置收款二维码