RFC-0024:強制原始碼相容性 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 為 FIDL 語言繫結建立原始碼相容性標準,以及該標準的演進程序。 |
作者 | |
提交日期 (年-月-日) | 2019-04-02 |
審查日期 (年-月-日) | 2019-04-11 |
摘要
為 FIDL 語言繫結建立原始碼相容性標準,以及該標準的演進程序。
提振精神
目前,針對語言繫結所產生的程式碼,幾乎沒有任何書面規則。這些類別應符合特定的 wire ABI,但除了這點之外,繫結作者可在 API 的塑造方式上有許多彈性。任何對 FIDL 定義所做的變更,都可能導致產生的繫結發生任意變更。
實際上,使用者會期待「常識」清單,列出應與來源相容的項目,例如定義新的頂層類型。不過,並沒有明確的規則指出這是事實。雖然這個案例似乎有點荒謬,但它說明瞭缺乏規格如何破壞使用者的期望。實際上發生過的實際例子包括在資料表中新增欄位、新增 xunion 變化版本,或是在結構體中新增預設欄位。使用者可以合理地預期這些變更不會造成來源中斷,但目前並沒有任何標準可明確指出這一點,而且所有這些變更都會導致目前一或多個語言繫結的來源層級中斷 (例如,由於 C++ 或 Go 中的定位初始化子,或 Rust 中的結構體模式)。
此外,許多 FIDL 語言繫結的擴充功能在過去曾因與來源相容性互動而遭拒。例如,將 copy
或 clone
函式新增至不含句柄的類型。包含任意句柄的類型無法複製,因此在類型中新增句柄會導致該類型無法提供 clone
函式 (或無法提供有效的複製函式)。由於這項變更會影響來源相容性,因此已多次遭到拒絕,因為這項變更會在生成的 Rust 繫結中,根據沒有句柄的情況引入條件式 clone
函式。因此,Fuchsia 開發人員必須手動回卷自己的 clone
函式,並為透過這些手動回卷方法 clone
的 FIDL 產生類型新增包裝函式類型。本文件提出一致的標準,讓我們能夠評估這類功能,希望能為開發人員提供更符合人體工學、更友善的體驗,並免除繁瑣的程式碼。
設計
程序
這個 FTP 會建立初始的來源相容性限制組合。這份清單會在 Fuchsia 來源樹狀結構的文件中追蹤。您必須使用 FTP 程序新增其他來源相容性限制。為方便新增與新功能相關的來源相容性規則,我們將修訂 FTP 範本中的「向下相容性」部分,加入建議,以便引入新的來源相容性限制 (適用時)。
定義:來源相容性和轉換性
下列變更必須與原始碼相容 (即不會破壞原始碼) 或可轉換。
原始碼相容性變更不得導致產生 FIDL 繫結公開 API 的任何有效 (編譯) 用途的原始碼中斷。針對限制哪些功能屬於「公開 API」,以及哪些功能不屬於「公開 API」的定義和可行性,我們有合理的論點;因此,為了符合本文的目的,我們將「公開 API」定義為任何使用產生的繫結,但不需使用非凡的語言技巧 (例如反射) 或開發人員明確意圖違反隱私權 (例如呼叫 __private_dont_use_me_function_2())。所有其他公開的 API (例如位置初始化、模式比對等) 都必須受到限制,以免使用者程式遭到 FIDL 程式庫的來源相容性變更破壞。
可轉換的變更是指「可以」編寫程式碼的變更,這些程式碼會在變更前後進行編譯。每個可轉換的來源相容性規則都必須明確指定 API 在轉換期間的「用途」。
初始來源相容性限制
以下列出必須與來源相容的變更:
- 新增頂層項目 (通訊協定、類型或常數)。
- 動機:使用者希望能夠宣告新的通訊協定、類型和常數,而不會影響現有 FIDL 程式庫的使用者。
- 例外狀況:使用「*」或從命名空間全面匯入的情況,可能會因為不同程式庫中同名項目之間的歧義而發生錯誤。
- 在非嚴格資料表中新增欄位。
- 動機:資料表的設計可輕鬆擴充,且應支援額外欄位,而不會造成損壞。如要選擇採用破損,可以使用
strict
修飾符。
- 動機:資料表的設計可輕鬆擴充,且應支援額外欄位,而不會造成損壞。如要選擇採用破損,可以使用
- 在非嚴格可延伸的聯結中新增變化版本。
- 動機:可擴充的聯集可輕鬆擴充,且應支援其他變化版本,而不會造成損壞。如要選擇啟用破損,可以使用
strict
修飾符。
- 動機:可擴充的聯集可輕鬆擴充,且應支援其他變化版本,而不會造成損壞。如要選擇啟用破損,可以使用
- 將成員新增至非嚴格枚舉
- 動機:非嚴格枚舉會隱含選擇擴充性,且應可在不破壞來源的情況下擴充。
- 將成員新增至非嚴格「位元」
- 動機:非嚴格位元會隱含選擇擴充性,且應可在不破壞來源的情況下擴充
- 將
[Layout = "Simple"]
新增至現有通訊協定- 動機:
[Layout = "Simple"]
的存在目的,是為了在簡單的 C 繫結中啟用使用。符合規範的現有通訊協定不應需要破壞來源變更,才能指定可在簡易 C 繫結中使用。
- 動機:
- 將
[MaxHandles]
新增至現有類型- 動機:
[MaxHandles]
的用途是提供類型的額外資訊,以便更彈性地使用。不應要求進行重大來源變更,才能指定類型已包含固定的句柄數量上限,並假設該類型會繼續包含最多該數量的句柄。
- 動機:
以下列出必須進行轉換的變更:
- 將
[Transitional]
新增至方法- 用途:在將
[Transitional]
屬性新增至該方法前後,必須能夠實作通訊協定,並提供使用相同來源的方法實作。 - 動機:只要所有現有實作項目都能逐步調整,就必須能夠逐步新增或移除通訊協定的方法。
- 用途:在將
- 新增
[Transitional]
方法- 用途:在新增
[Transitional]
方法之前和之後,都必須能夠使用相同的來源實作通訊協定 (不過,API 在轉換期間不必允許實作該方法)。 - 動機:只要所有現有實作項目都能逐步調整,就必須能夠逐步新增或移除通訊協定的方法。
- 用途:在新增
- 移除
[Transitional]
方法- 用途:在移除
[Transitional]
方法前後,必須能夠使用相同的來源實作通訊協定 (不過 API 在轉換期間不必允許實作該方法)。 - 動機:只要所有現有的實作項目都能逐步調整,就必須能夠逐步新增或移除通訊協定的方法。
- 用途:在移除
- 移除非嚴格表格的欄位
- 用途:在移除資料表欄位前後,必須能夠使用相同的來源建立資料表並存取其欄位 (除了要移除的欄位)。
- 動機:資料表的設計可輕鬆進行演進,且應支援移除作業,而不會造成損壞。如要選擇採用破損,您可以在資料表上使用
strict
修飾符。
- 移除非嚴格可延伸聯集的變化版本
- 用途:在移除 xunion 變數之前和之後,必須能夠使用相同的來源建立 xunion 並存取其變數 (除了要移除的變數)。
- 動機:xunion 的設計可輕鬆進行演進,且應支援移除而不會中斷。如要選擇採用破損,您可以在資料表上使用
strict
修飾符。
- 將類型標示為
strict
- 用途:在新增
strict
之前和之後,必須能夠使用相同來源存取資料表或「位元」的所有欄位,以及枚舉或 xunion 的所有變化版本。 - 動機:
strict
應在類型穩定後加入類型宣告,以便提供更完善的推理和開發人員工具。不過,這項要求僅適用於可轉換的變更,而非不中斷的變更,因為可擴充類型可能會允許存取未知的欄位或變化版本。這些功能對strict
類型來說並不合理,因為系統會拒絕未知的欄位或變化版本。
- 用途:在新增
- 將
[Transitional]
新增至枚舉或位元、資料表欄位或可擴充聯集的變數。- 用途:在
[Transitional]
推出前後,您必須能夠存取所有非轉換成員/位元/欄位/變體,並使用相同來源建構不含[Transitional]
值的列舉/位元/資料表/可擴充聯集。 - 動機:必須能夠逐步移除成員、欄位或變數。
- 用途:在
- 新增枚舉或位元的新成員、表格的欄位,或標示為
[Transitional]
的可擴充聯集的變化版本。- 用途:在引入新
[Transitional]
欄位之前和之後,必須能夠存取所有非轉換成員/位元/欄位/變體,並使用相同來源建構不含[Transitional]
值的 enum/位元/表格/可擴充聯集。 - 動機:必須能夠逐步新增成員、欄位或變化版本。
- 用途:在引入新
- 移除 enum 或位元、資料表欄位,或標示為
[Transitional].
的可擴充聯集變化版本的成員- 用途:您必須能夠在移除
[Transitional]
欄位前後,使用相同來源存取所有非過渡成員/位元/欄位/變體,並建構不含[Transitional]
值的列舉/位元/表格/可擴充聯合的值。 - 動機:必須能夠逐步移除成員、欄位或變數。
- 用途:您必須能夠在移除
以下是這份清單中未列出的潛在限制,以及未列入的原因:
- 新增或移除結構體中的欄位 (預設或非預設)
- 這是會破壞 ABI 的變更,因此需要其他重大努力,才能確保相容的轉換。如要讓這項變更不會造成中斷,就必須移除任何「針對所有欄位」類型的型別推理,包括自動方法衍生 (例如「此型別是否包含任何浮點值」)、位置初始化器,以及完整欄位比對和建構。
- 在嚴格表格和 xunion 中新增或移除欄位/變化版本 (預設或非預設)
strict
旨在啟用其他開發人員工具,這些工具會依據「for all fields」類型的推理方式來判斷型別,包括自動方法衍生 (例如「此型別是否包含任何浮點值」)、位置初始化器,以及完整的欄位比對和建構。強制這項變更為非破壞性變更,將會阻礙這項目的。
- 將含有句柄的欄位或子類加入未標示為
[MaxHandles]
的類型- 將欄位新增至嚴格類型或結構體,已是其他原因造成的來源變更,因此新增具備句柄的欄位也是類似的變更,可能會影響隨後產生的 API。
導入策略
這項 FTP 會建立初步的語言相容性標準。錯誤會提交並指派給每個語言繫結的作者,以確保其語言繫結符合規定。
人體工學
這項異動可為原始碼相容性設定明確的標準,讓您更輕鬆地使用 FIDL,並允許自動檢查,以及更輕鬆地手動檢查 FIDL 變更的原始碼相容性,同時為繫結作者提供更明確的原始碼相容性指引,讓他們能夠自由地建立符合語言慣例的繫結,同時遵守專案的標準規定。
說明文件和範例
在這個 FTP 獲得核准後,我們會將 FTP 建立的程序,以及來源相容性規則本身,連同其他 FIDL 參考文件一併發布。
回溯相容性
套用建議的指引可能需要變更繫結和使用這些繫結的方式,這取決於各繫結作者如何處理這些變更。
這個部分 (「向下相容性」部分) 將修訂為納入以下文字:
「如果您要推出新的資料類型或語言功能,請考量使用者會對 FIDL 定義做出哪些變更,且不會影響產生程式碼的使用者。如果您的功能對產生的語言繫結設有任何新的來源相容性限制,請在此列出這些限制。」
請注意,您應將「來源相容性」文字納入此 FTP 的實際連結,也就是:
[source compatibility](/docs/contribute/governance/rfcs/0024_mandatory_source_compatibility.md)
成效
這項 FTP 不會限制執行階段行為,但對來源 API 的限制可能會導致語言繫結作者設計效能較高或較低的 API。在引入新的來源相容性限制時,請考慮在支援的語言中建立高效繫結的可行性。
這項功能可能會影響編譯時間效能,因為它會推向需要大量內嵌和編譯器最佳化模式的模式,以便提升效能 (例如將複雜的建構工具 API 最佳化為簡單的結構體初始化)。繫結作者應盡力做出設計選擇,以免大幅影響編譯時間,但特定語言 API 的編譯時間後果不一定會導致引入新的來源相容性限制。
安全性
這項功能不會影響安全性。
測試
許多來源相容性規則的格式為「在變更前編譯的任何使用者程式碼,在變更後都不能存在。」很遺憾,這些限制很難或無法測試,因為必須在變更前列舉 API 的所有可能用途。
不過,我們可以 (也應該) 在 FIDL 變更測試套件中新增項目,以便顯示在變更前後,API 確實存在某些用途。這是符合來源相容性規定的必要條件,但並非充分條件。
缺點、替代方案和未知事項
- 請勿引入這類規格。允許繫結作者選擇他們希望變更內容是否會造成中斷。這與目前的實際狀態大致相同,但會讓繫結作者享有比目前系統實際授予的更大彈性,因為在目前的系統中,某些來源相容性不友善的變更已遭到反對。
- 請建立規格,說明哪些變更「可」造成來源中斷,而非哪些變更「不」可造成來源中斷。這項做法較難強制執行,且需要繫結作者預測變更,以便在變更發生時,確保繫結仍與來源相容。
- 稍微修改一下,即可指定「是」和「否」的變更,未指定的變更則會預設為某種方式。這基本上與此 FTP 或上述替代方案相同,但會設定更正式的預期,說明記錄不同 FIDL 變更的效果。
既有技術與參考資料
我們先前曾嘗試透過 [MaxHandles]
屬性引入可進化限制。本提案的前半段已討論過這項設計和預期的修改方式。