Solidity v0.8.8 引入了用户定义的值类型作为主要功能。该override 关键字现在对于接口函数是可选的,immutable 变量可以在构造函数中读取,支持检索枚举的最小值和最大值,可以指定包含目录,并且命令行界面已清理。此外,我们修复了几个错误,并且 SMTChecker 提高了语言覆盖率。
值得注意的新功能
用户定义的值类型
用户定义的值类型允许在基本值类型上创建零成本抽象,这也提高了类型安全性并改善了可读性。有关详细信息,请参见此深入研究博客文章.
接口函数的无重写
仅重写单个接口函数的函数不需要 override 说明符。
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; interface IMinimalERC20 { function transfer(address to, uint value) external; } contract MinimalERC20 is IMinimalERC20 { // Before version 0.8.8, this function declaration required // specifying the override keyword, i.e., // `function transfer(address to, uint value) external override ...` function transfer(address to, uint value) external { // ... } }
min 和 max 用于枚举
现在可以使用 type(E).min 和 type(E).max 分别访问 枚举类型 的最小值和最大值。
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; enum AuctionState {Created, InProgress, Completed} // returns AuctionState.Created function min() pure returns (AuctionState) { return type(AuctionState).min; } // returns AuctionState.Completed function max() pure returns (AuctionState) { return type(AuctionState).max; }
从不可变变量读取
不可变变量可以在初始化后在构造时间读取。
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.8; interface IMockInterface { function getValue() external returns(uint); } contract ImmutableExample { uint immutable value; event setValue(uint); constructor(IMockInterface mockContract) { // Sets the immutable variable. value = mockContract.getValue(); // Reads the immutable variable. emit setValue(value); } }
正如您可能知道的那样,也可以在声明时初始化不可变变量,例如 uint immutable deployTime = block.timestamp;。由于初始化顺序并不总是很明显,因此此类不可变变量仅在执行构造函数后才被视为已初始化。这意味着您可以在构造函数内部从这些变量中读取,但不能使用它们在声明时初始化其他不可变变量。
导入包含路径
我们添加了新的命令行选项 --include-path,它有望让使用多个库编译更复杂的项目变得更加容易。它在 solcjs 和 solc 中可用,无论是在传统模式还是标准 JSON 模式下。您可以使用它来指定在解析 import 语句时查找源文件的目录。如果您使用 npm 安装 Solidity 包,它们通常放置在 node_modules 下的子目录中。如果您使用 import X from "@packagename/contracts/x.sol"; 形式的导入,则如下调用编译器应该能够正确地解析所有导入,而无需进一步的操作(基路径和包含路径将被添加到“允许的路径”中,例如)
solc contract.sol --base-path . --include-path node_modules/
之前介绍的选项 --base-path 提供了一个路径,在此路径中首先执行查找。如果失败,将搜索包含路径以查找该文件。
Solidity 编译器将源文件的完整路径存储在元数据中,这通常会让人感到沮丧,因为它有时会使在不使用标准 JSON 模式的情况下重现编译变得更加困难。
如果您使用 --base-path 和 --include-path,则仅存储位于其中的文件的相对路径,因此应该更容易在不同的系统上将合约重新编译为完全相同的二进制文件。
另一方面,如果您在命令行中提供任何要编译的文件,而这些文件不在基路径或包含路径中,则这些文件将以系统上的绝对路径命名,因此最终会以这种方式出现在元数据中。
目前,一些项目使用重新映射来解决依赖关系,我们认为“导入路径”功能是解决同一问题的更好方法。重新映射实际上只用于更改编译器如何查看其自己的内部文件系统,以及允许在同一个合约或项目中使用同一个库的两个不同版本。
完整变更日志
语言功能
- 继承:仅重写单个接口函数的函数不需要 override 说明符。
- 类型系统:支持 type(E).min 和 type(E).max 用于枚举。
- 用户定义的值类型:允许在具有更严格类型要求的值类型上创建零成本抽象。
编译器功能
- 命令行界面:添加 --include-path 选项,用于指定可能包含可导入代码的额外目录(例如,打包的第三方库)。
- 命令行界面:除非请求的输出需要,否则不要隐式运行 EVM 字节码生成。
- 命令行界面:规范化命令行中指定的路径,并使它们相对于位于基路径和/或包含路径中的文件。
- 不可变变量可以在初始化后在构造时间读取。
- SMTChecker:添加约束以更好地关联 address(this).balance 和 msg.value。
- SMTChecker:通过模块支持常量。
- SMTChecker:支持低级 call 作为对未知代码的外部调用。
- SMTChecker:支持外部函数调用的 value 选项。
- SMTChecker:支持用户定义的值类型。
错误修复
- 代码生成器:修复在内联汇编中将 calldata 结构和静态大小的 calldata 数组分配给 calldata 结构和静态大小的 calldata 数组时的 ICE。
- 代码生成器:对 ABI 函数使用稳定的源顺序。
- 命令行界面:在标准 JSON、汇编器和链接器模式下禁止 --experimental-via-ir 选项。
- 命令行界面:修复使用 --allowed-paths 显式列入白名单或由于基路径、重新映射和要编译的文件而隐式列入白名单的路径的解析。正确处理由于是相对路径、未规范化或为空而与导入不完全匹配的路径。
- 命令行界面:将优化器选项报告为标准 JSON 和链接器模式中的无效选项,而不是忽略它们。
- 名称解析器:修复使用 import {AliasedName} from "a.sol" 导入别名符号时,它将使用符号的原始名称,而不是别名名称。
- 操作码优化器:防止优化器多次运行,以避免引用代码的潜在字节码差异。
- 解析器:正确检查彼此相邻的多个 SPDX 许可证标识符并验证它们。
- SMTChecker:修复有关内部函数的 BMC 约束。
- SMTChecker:修复由内部函数返回的存储数组引用上的 push 引起的误报。
- SMTChecker:修复构造函数中外部调用的误报。
- SMTChecker:修复在 abi.*、加密函数和常量的某些多源使用上的内部错误。
- 标准 JSON:修复 Yul 模式中非致命错误在致命错误之后被丢弃。
- 类型检查器:纠正内联汇编中关于 .slot 或 .offset` 无效的错误消息,而实际上使用了 .length``。
- 类型检查器:禁止在接口中声明和定义修饰符。
- Yul 优化器:修复 LoadResolver 中的崩溃,当 keccak256 具有特定的非标识符参数时。
感谢所有为使本版本发布成为可能而做出贡献的人!
在此处下载 Solidity 的新版本 here.