| RFC-0024:強制原始碼相容性 | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 為 FIDL 語言繫結建立原始碼相容性標準,以及演進該標準的程序。 |
| 作者 | |
| 提交日期 (年-月-日) | 2019-04-02 |
| 審查日期 (年-月-日) | 2019-04-11 |
摘要
為 FIDL 語言繫結建立來源相容性標準,以及演進該標準的程序。
提振精神
目前,語言繫結產生的程式碼很少有書面規則。預期這些函式會符合特定線路 ABI,但除此之外,繫結作者在塑造 API 方面有很大的自由度。FIDL 定義的任何變更,都可能導致產生的繫結發生任意變更。
實際上,使用者會期望看到「常識」清單,列出應與來源相容的項目,例如定義新的頂層型別。不過,沒有明確的規則說明這類情況。 雖然這個案例有點荒謬,但說明瞭缺乏規格會如何破壞使用者的期望。實際發生的例子包括在表格中新增欄位、新增 xunion 變體,或在結構體中新增預設欄位。使用者有理由認為這些變更不會導致來源中斷,但目前沒有相關標準,而且所有這些變更都會導致一或多個語言繫結的來源層級中斷 (例如,由於 C++ 或 Go 中的位置初始值,或是 Rust 中的結構體模式)。
此外,過去有許多極為實用的 FIDL 語言繫結擴充功能,因與來源相容性互動而遭到拒絕。例如,將 copy 或 clone 函式新增至不含控制代碼的型別。含有任意控制代碼的型別無法複製,因此將控制代碼新增至型別會導致該型別無法提供 clone 函式 (或至少會導致該型別無法提供正常運作的複製函式)。由於會影響來源相容性,因此我們多次拒絕這項變更,也就是根據控點是否不存在,在產生的 Rust 繫結中導入 clone 函式的條件式納入。因此,Fuchsia 開發人員必須手動推出自己的 clone 函式,並為透過這些手動推出方法傳遞的 FIDL 產生型別新增包裝函式型別。clone本文建議採用一致的標準來評估這類功能,希望為開發人員提供更符合人體工學、更友善且免除樣板的體驗。
設計
流程
這份 FTP 會建立一組初始的來源相容性限制。這份清單會記錄在 Fuchsia 來源樹狀結構的文件中。必須使用 FTP 程序新增其他來源相容性限制。為方便新增與新功能相關的來源相容性規則,FTP 範本的「向後相容性」部分將會修訂,納入導入新來源相容性限制的建議 (如適用)。
定義:原始碼相容性和轉換性
下列變更必須與來源相容 (即不會破壞來源),或可轉換。
原始碼相容變更不得導致任何有效 (編譯) 使用所產生 FIDL 繫結的公開 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]欄位前後使用相同來源。 - 動機:必須能夠逐步新增成員、欄位或變體。
- 用途:必須能夠存取所有非過渡成員/位元/欄位/變體,並建構不含
- 移除列舉或位元成員、資料表欄位,或是標示為
[Transitional].的可擴充聯集變體- 用途:必須能夠存取所有非過渡成員/位元/欄位/變體,並建構不含
[Transitional]值的列舉/位元/表格/可擴充聯集值,且在移除[Transitional]欄位前後使用相同來源。 - 動機:必須能夠逐步移除成員、欄位或變體。
- 用途:必須能夠存取所有非過渡成員/位元/欄位/變體,並建構不含
以下是可能遭排除的限制,以及排除原因:
- 從結構體新增或移除欄位 (預設或非預設)
- 這是 ABI 破壞性變更,需要其他重大工作,才能確保相容的轉移作業。如要避免這項重大變更,必須排除所有「適用於所有欄位」的型別推理,包括自動方法衍生 (例如「這個型別是否包含任何浮點數」)、位置初始化程式,以及詳盡的欄位比對和建構。
- 從嚴格資料表和 xunion 新增或移除欄位/變體 (預設或非預設)
strict的用途是啟用額外的開發人員工具,這些工具依賴「適用於所有欄位」的型別推理,包括自動方法衍生 (例如「這個型別是否包含任何浮點數」)、位置初始化程式,以及詳盡的欄位比對和建構。如果強制將這項變更設為非破壞性,就會阻礙這個目的。
- 將含有控制代碼的欄位或變體新增至未標示
[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] 屬性導入可演化性限制。這項設計和預期修改內容已在本提案的先前部分討論過。