編譯:登鏈翻譯計畫;來源:soliditylang.org
Solidity編譯器的最新版本v0.8.22的發布。 0.8.22版本的編譯器包含了一系列的語言和編譯器改進,例如檔案級事件定義、unchecked 循環增量最佳化、支援導入EVM彙編JSON 等等。
重要提示
本次發布廢棄了對低於Constantinople(君士坦丁堡) 版本的EVM 的支持,這些版本越來越難以維護。這些古老的版本在以太坊主網和測試網路上早已過時,我們懷疑它們對其他網路也不再相關。複雜的程式碼路徑和解決方案會減慢針對新版本的功能開發和測試的速度,因此我們希望在未來的編譯器版本中停止支援它們。
新功能亮點
unchecked 循環增量
在增加循環計數器時使用unchecked 算術運算是為了gas 優化[7]的常見做法。讓我們以以下循環和計數器i 的範例來說明:
for (uint i = 0; i < array.length; ++i) {
acc += array[i]; // i is not modified by the loop body
}
在許多情況下(詳見下文的精確條件),比較操作將確保i 永遠不會達到其類型的最大值,因此可以安全地假設在達到最大值之前循環將停止。在這種情況下,對計數器進行安全檢查將是多餘的,也是一種gas 的浪費。這會鼓勵使用者使用冗長的unchecked 模式,將計數器增量包裝在循環體內的unchecked算術區塊中,繞過檢查:
for (uint i = 0; i < array.length;) {
acc += array[i];
unchecked { i++; } // i gets incremented without overflow checks — less gas used
}
Solidity 0.8.22 引入了一種溢位檢查最佳化,自動產生了對於循環計數器的unchecked 算術增量。這個新的最佳化消除了在循環體中使用類似前面範例中的冗長unchecked 增量模式的需要。
相比之下,新的優化使用戶能夠在不犧牲gas效率的情況下返回原始的、更易讀的程式碼。
新優化避免溢出檢查的精確條件如下:
-
循環條件是形如 i < ... 的比較,其中i 是局部變數(從現在開始稱為「循環計數器」)。
-
此比較必須在與循環計數器相同的類型上執行,即右側的類型必須可以隱式轉換為循環計數器的類型,以使循環計數器在比較之前不會被隱式擴展。
-
循環計數器必須是內建整數類型的局部變數。
-
循環表達式必須是循環計數器的前綴或後綴遞增,即i++ 或 ++i。
-
循環計數器不能在循環條件或循環體中被修改。
為了澄清第二個條件,請考慮以下程式碼片段:
for (uint8 i = 0; i < uint16(1000); ++i) {
// loop body
}
在這種情況下, i 在比較之前被轉換為uint16,而且條件實際上永遠不會為假,因此無法刪除遞增的溢位檢查。
另外,請注意, < 是唯一會觸發該最佳化的比較運算子。故意排除了運算子 <= 和其他運算子。此外,該運算子必須是內建的- 使用者定義的 < 不符合條件。
此最佳化是直接的且總是有益的,因此即使使用通用設定 settings.optimizer.enabled 停用了優化器的其餘部分,它也會啟用。可以透過在標準JSON 輸入中將 settings.optimizer.details.simpleCounterForLoopUncheckedIncrement 設為 false 來明確關閉它。無法使用命令列介面停用它。
調整Yul優化器以重新產生零字面量
新版本在0.8.20 版本引入的 PUSH0 操作碼的支援基礎上進行了構建,透過將 Rematerialiser[8] 最佳化步驟擴展為始終重新產生零字面量而不是將其儲存為變數引用,從而允許使用PUSH0 而不是DUP 來降低gas成本。為確保有效執行此操作,請將Rematerialiser 和 UnusedPruner[9] 步驟加入了Yul優化器的預設清理序列中。
增加對導入EVM 彙編JSON 的支援(實驗性)
這個新版本添加了對導入EVM 彙編的實驗性支持,為外部工具在字節碼生成之前執行超級優化提供了可能性。此功能的主要目的是定義一種低階EVM 彙編的序列化格式,使編譯器產生的彙編可以被匯出、修改和重新匯入,從而恢復正常的編譯過程。
重要提示:這是一個實驗性功能,目前不適用於生產環境。我們在此版本中提供此功能,以便你嘗試並提供回饋。
允許在檔案層級定義事件
Solidity 0.8.22 允許你在檔案層級定義事件。現在,事件定義可以放在合約範圍之外。這為程式碼組織提供了另一種選擇,無需人為地將事件包裝在庫中。
此外,此版本還修正了一個錯誤,該錯誤導致在為程式碼發出在外部合約或介面中定義的事件時產生NatSpec 時出錯。在上一個版本(0.8.21)中,Solidity編譯器新增了對在目前合約未繼承的合約和介面中定義的事件的限定存取的支持,但該錯誤阻止了該功能的完全使用。
透過此錯誤修復和允許檔案層級事件定義,Solidity的最新版本使用戶能夠編譯以下範例而不會出現任何錯誤:
interface I {
event ForeignEvent();
}
contract C {
event ForeignEvent();
}
event E();
contract D {
function f() public {
// Emitting a foreign event would trigger an internal error on 0.8.21
emit I.ForeignEvent();
emit C.ForeignEvent();
// Emitting a file-level event. New feature.
emit E();
}
}
完整的更改日誌
語言特性
-
允許在檔案層級定義事件。
編譯器特性
-
程式碼產生器:當計數變數不會溢位時,刪除某些for迴圈的冗餘溢位檢查。
-
命令列介面:新增–no-import-callback選項,防止編譯器載入未在CLI 或標準JSON 輸入中明確給出的來源檔案。
-
命令列介面:新增實驗性的–import-asm-json選項,可以匯入以–asm-json使用的格式的EVM 彙編。
-
命令列介面:對於在編譯pipeline 之外產生的錯誤訊息,使用適當的嚴重性和著色。
-
EVM:棄用對「homestead」、「tangerineWhistle」、「spuriousDragon」和「byzantium」 EVM 版本的支援。
-
解析器(Parser):刪除實驗性的錯誤恢復模式(–error-recovery / settings.parserErrorRecovery)。
-
SMTChecker:支援使用者定義的運算子。
-
Yul 優化器:如果支援PUSH0,優先使用零字面量而不是將零值儲存在變數中。
-
Yul 優化器:在預設的清理序列的末端運行Rematerializer和UnusedPruner步驟。
Bug 修復
-
程式碼產生器:修復透過via-IR 程式碼產生器輸出的結果依賴於匯入回呼中發現的檔案的問題。在某些情況下,不同的AST ID 分配會改變內部調度中函數的順序,導致表面上不同但在語義上等效的字節碼。
-
NatSpec:修正在要求合約的使用者文件或開發文件時出現內部錯誤的問題,該合約發出了在外部合約或介面中定義的事件。
-
SMTChecker:修復編碼錯誤,導致循環在完成後展開。
-
SMTChecker:修正常數條件檢查的不一致性,當while 或for 迴圈在條件檢查前展開。
-
Yul 最佳化器:修正在CSE 期間受編譯器產生的Yul變數名稱影響替換決策的問題,在某些情況下導致不同(但等效)的字節碼。