| 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 函数,并为通过这些手动推出的方法 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]值的枚举/位/表/可扩展联合的值。 - 动机:必须能够逐步添加成员、字段或变体。
- 使用:在引入新的
- 移除枚举或位数的成员、表的字段或标记为
[Transitional].的可扩展联合的变体- 使用:在移除
[Transitional]字段之前和之后,必须能够使用相同的来源访问所有非过渡成员/位/字段/变体,并构建不包含[Transitional]值的枚举/位/表/可扩展联合的值。 - 动机:必须能够逐步移除成员、字段或变体。
- 使用:在移除
以下是已从该列表中省略的潜在限制条件,包括省略原因:
- 在结构体中添加或移除字段(默认或非默认)
- 这是一项 ABI 破坏性变更,需要付出其他重大努力才能确保兼容的过渡。 若要使此更改不属于破坏性更改,需要消除任何对类型进行“针对所有字段”式推理的内容,包括自动方法派生(例如“此类型是否包含任何浮点数”)、位置初始化程序以及详尽的字段匹配和构造。
- 在严格表和 xunion 中添加或移除字段/变体(默认或非默认)
strict旨在启用依赖于对类型进行“针对所有字段”式推理的其他开发者工具,包括自动方法派生(例如“此类型是否包含任何浮点数”)、位置初始化程序以及详尽的字段匹配和构造。如果强制将其设为非重大变更,则会阻碍此目的的实现。
- 向未标记为
[MaxHandles]的类型添加包含句柄的字段或变体- 出于其他原因,向严格类型或结构体添加字段已经是源代码破坏性变更,因此添加具有句柄的字段同样是破坏性变更,可能会影响生成的 API。
实施策略
此 FTP 确立了最初提议的语言兼容性标准。 系统会提交 bug 并将其分配给每种语言绑定的一个作者,以确保其语言绑定符合要求。
工效学设计
此变更通过为源代码兼容性设置明确的标准,使 FIDL 更易于使用,从而可以自动检查 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] 属性引入可演化性限制。此设计及其预期修改已在本提案的先前部分中讨论过。