拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Carbon 语言命名空间中的名称绑定澄清:P003407 提案解析与实现验证

Carbon 语言命名空间中的名称绑定澄清P003407 提案解析与实现验证【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang导读本篇文章以 Carbon Language 仓库中的提案 p003407-clarify-name-bindings-in-namespaces.md 为核心系统梳理 Carbon 语言中名称绑定binding pattern与命名空间namespace相互作用的语法与语义规则包括命名空间成员只能在声明命名空间的同一名称作用域内声明、绑定模式中多个名称必须同属一个命名空间、以及命名空间只能声明在文件作用域等关键约束。读完本文你将理解这些规则的设计动机、完整语法示例并能通过仓库中的设计文档与 toolchain 测试用例验证其真实行为。背景Carbon 的命名空间与绑定模式命名空间Namespaces在 Carbon 中命名空间为实体提供结构化命名路径其设计集中在 代码与名称组织设计文档 中。namespace关键字的语法可以粗略表示为如下正则namespace NAME_PATH;命名空间通过名称前缀作用于其他实体例如package Time; namespace Timezones.Internal; struct Timezones.Internal.RawData { ... } fn ParseData(data: Timezones.Internal.RawData);一个namespace声明会把名称路径中的第一个标识符加入文件library的命名空间中。在上例中声明namespace Timezones.Internal;之后Timezones成为一个可用标识符而Internal需要通过Timezones访问。值得注意的是命名空间可以跨库合并并且可以从其他包导入但即使某命名空间已存在于当前包导入的库中若要向其添加符号仍必须在本地再次声明它参见 Redeclaring imported namespaces。绑定模式Binding Patterns绑定模式是模式匹配体系的一部分详细规范见 pattern_matching.md 的 Binding patterns 小节。一个名称绑定模式声明一个由标识符指定的绑定binding-pattern :: ref? (identifier : expression | self (: expression)?) binding-pattern :: (generic | template)? identifier : expression pattern :: binding-pattern绑定模式具有阶段phase属性运行时绑定模式runtime binding pattern绑定到运行时的动态值是显式函数参数和局部绑定的默认方式检查型泛型绑定模式checked generic binding pattern绑定到符号常量symbolic constant即类型检查时未知的编译期值是推导型函数参数和编译期实体参数的默认方式模板泛型绑定模式template generic binding pattern绑定到模板常量在类型检查时已知需要使用template关键字显式声明。检查型与模板绑定模式统称为编译期绑定模式它们不能出现在var模式内部。当var或let声明使用绑定模式来声明名称时就与命名空间产生了交集——这正是 P003407 提案要澄清的核心问题。问题NS.a的歧义与缺失细节虽然class NS.C这种平凡情形看起来已被提案 #107: Code and name organization 顺带支持但其细节仍然缺失。例如在绑定多个名称时存在多种语法选择同时下列代码没有明确结论namespace NS; class ClassT { // 这是通过 NS 访问的类成员还是 NS 内的文件作用域成员 // 它的生命周期是什么 var NS.a: i32 0; }提案指出这里var NS.a到底表示通过 NS 访问的类成员还是位于 NS 命名空间内部的文件作用域成员完全不清晰其生命周期也无法确定。此外关于命名空间能否在文件作用域之外声明也存在不确定性。本提案的主要目标就是消除这些歧义。提案三条核心规则P003407 提案确立了三条核心规则要求命名空间成员在与命名空间声明相同的名称作用域内声明。由于在文件作用域之外声明命名空间已被禁止这实际上意味着命名空间成员只能在文件作用域内声明。允许绑定模式直接在命名空间中声明名称如NS.a。禁止在同一模式中向不同命名空间引入绑定。这三条规则已落入正式设计文档 代码与名称组织 中其具体语义如下。规则一命名空间成员必须在同一作用域内声明命名空间成员只能在与声明该命名空间相同的名称作用域中声明namespace NS; // ✅ 允许声明位于文件作用域与 NS 的声明相同。 class NS.ClassT { // ❌ 错误类体有它自己的名称作用域。 var NS.a: i32 0; } fn Function() { // ❌ 错误函数体有它自己的名称作用域。 var NS.b: i32 1; } // ✅ 允许声明位于文件作用域与 NS 的声明相同。 namespace NS.MemberNS; // ✅ 允许声明位于文件作用域与 NS.MemberNS 的声明相同。 class NS.MemberNS.MemberClassT {}这条规则直接回答了引言中的歧义问题class ClassT { var NS.a: i32 0; }是非法的因为ClassT的类体是独立名称作用域而NS是在文件作用域声明的。它同时让成员的生命周期变得清晰命名空间成员与其命名空间具有相同的声明上下文。规则二绑定模式可声明命名空间成员当一个模式中的绑定模式用于声明名称如var或let时允许使用命名空间限定名。但由于规则一限制了作用域命名空间限定的模式绑定只能用于var或let声明的模式中函数参数、模式匹配等其他场景不受影响。规则三同一模式内所有名称必须同属一个命名空间当一个模式通过绑定模式声明多个名称时所有名称必须在同一个命名空间中namespace NS; // ✅ 允许a 和 b 使用默认命名空间。 var (a: i32, b: i32) (1, 2); // ✅ 允许c 和 d 位于同一命名空间。 var (NS.c: i32, NS.d: i32) (3, 4); // ❌ 错误e 和 f 不在同一命名空间。 var (e: i32, NS.f: i32) (5, 6);需要强调的是此限制仅适用于绑定模式中声明名称的情形不适用于模式中名称的其他用法例如表达式中引用已有实体。规则三的实践示例库级命名空间应用把上述规则放入真实的库组织场景中可以更直观地看到它的作用。以 代码与名称组织设计文档 中的导出与调用示例为参照一个使用命名空间的库可以这样组织package Geometry library Shapes; namespace TwoDimensional; struct TwoDimensional.Circle { ... } fn TwoDimensional.Area(c: TwoDimensional.Circle) - f64;调用方通过包实体和命名空间逐级访问package Caller; import Geometry library Shapes; fn Run() { var c: Geometry.TwoDimensional.Circle ...; Print(Geometry.TwoDimensional.Area(c)); }而如果要在一个声明中同时初始化多个命名空间成员规则三要求它们全部属于TwoDimensionalnamespace TwoDimensional; // ✅ 允许两个成员都在 TwoDimensional 内。 var (TwoDimensional.x: f64, TwoDimensional.y: f64) (0.0, 0.0); // ❌ 错误z 在默认命名空间TwoDimensional.w 在命名空间内。 var (z: f64, TwoDimensional.w: f64) (0.0, 0.0);这种约束让名称声明位置可预测也使得重构例如把实体从一个命名空间移动到另一个对调用方的影响范围更容易分析参见设计文档中的 Other refactorings 小节。源码验证toolchain 中的规则实现上述规则不仅是纸面设计也已在 Carbon 的语义检查器toolchain/check中实现并通过测试固定。以下测试文件是理解这些规则真实行为的直接证据。命名空间只能在文件作用域声明测试文件 fail_not_top_level.carbon 验证了命名空间不得在非顶层作用域声明fn F() { // 错误namespace declaration not at top level [NamespaceDeclNotAtTopLevel] namespace N; fn N.F() {} } class C { // 错误namespace declaration not at top level [NamespaceDeclNotAtTopLevel] namespace N; fn N.F() {} } interface I { // 错误namespace declaration not at top level [NamespaceDeclNotAtTopLevel] namespace N; fn N.I() {} }在函数体、类体、接口体中声明namespace都会触发诊断NamespaceDeclNotAtTopLevel直接对应提案中禁止在文件作用域外声明命名空间的决定。该文件属于toolchain/testing:file_test驱动的文件测试体系可以通过如下命令单独运行验证bazel test //toolchain/testing:file_test --test_arg--file_teststoolchain/check/testdata/namespace/fail_not_top_level.carbon命名空间与局部变量的遮蔽测试文件 shadowing.carbon 展示了命名空间名可以在局部作用域中被变量遮蔽namespace NS; fn Main() { var NS: () (); NS (); ... }这从侧面印证了名称作用域的边界namespace NS位于文件作用域而局部var NS位于函数体内两者通过作用域规则自然隔离。这也正是提案反复强调同一名称作用域的原因——名称绑定与命名空间声明的归属都必须以作用域为边界来判断。此外toolchain/check/testdata/namespace 目录下的其他测试如fail_duplicate.carbon、fail_conflict_imported_namespace_first.carbon、merging.carbon、nested.carbon等分别覆盖了命名空间重复声明、导入冲突、跨库合并、嵌套等场景可作为继续研究命名空间语义的入口。设计理由提案的取舍基于 Carbon 的项目目标代码易于阅读、理解和编写要求多个名称的声明统一使用NS.a语法与单变量情形如var NS.a保持一致降低认知负担要求命名空间成员必须在与命名空间相同的名称作用域内声明使生命周期更清晰——成员与命名空间拥有相同的声明上下文读者无需在多个作用域之间跳转推理禁止混合命名空间可以避免var (NS.a: i32, b: i32)这类写法中b被误认为也属于NS的困惑。备选方案与被否决的设计提案共评估了四种备选方案其否决理由有助于深入理解最终规则的边界。备选一允许用命名空间前缀整个元组绑定模式var NS.(a: i32, b: i32) (3, 4);被否决的原因单个语句声明多个名称的场景本就少见这种命名空间限定符与被声明标识符分离的语法可能成为孤例相比之下NS.a与class NS.Class等其他限定场景保持一致因此最终选用NS.a形式。备选二允许绑定模式向多个命名空间声明名称namespace NS; var (NS.a: i32, b: i32) InitData();被否决的原因混合命名空间会造成混淆——例如b可能被误读为声明在NS中。既然没有数据表明这种能力能带来足够收益为保持简单性单一声明内禁止混合命名空间。备选三允许在非当前作用域所有的命名空间中声明名称namespace NS; class ClassT { var NS.val: i32; class NS.ChildT {} }被否决的原因最为复杂这里package.NS.val更像全局变量而ClassT.NS.val看起来像实例成员由于NS不在ClassT的名称作用域内ClassT.NS.val或instance.NS.val能否用于引用产生的变量也不明确。这种命名问题同样扩展到非绑定声明如NS.ChildT。禁止用命名空间跨越名称作用域与通常禁止在其他名称作用域中声明名称的既有规则一致例如class A { class B { // C 必须直接声明在 A 内部。 class A.C; } } // D 必须在 A 内声明即使它是单独定义的。 class A.D {}namespace声明与其中的名称必须书写在同一名称作用域内这避免了名称查找歧义并让名称作用域边界在不同声明之间保持一致。备选四允许在文件作用域之外的任意作用域声明命名空间提案 #107 的示例都聚焦于文件作用域其他作用域未被仔细考虑因此路径语义模糊。虽然成员命名空间在某些场景可能有价值例如复杂类class Complex { namespace OptionSet1; class OptionSet1.MemberClassA; class OptionSet1.MemberClassB; namespace OptionSet2; class OptionSet2.MemberClassC; class OptionSet2.MemberClassD; namespace Vars; var Vars.a; }但本提案明确反对在文件作用域之外声明命名空间理由有二提案 #107 只提及文件作用域命名空间隐含排除了其他作用域禁止在其他作用域声明命名空间与 C 保持一致。如果允许则必须进一步决定Complex.Vars.a是实例生命周期还是全局生命周期。目前命名空间只能在文件作用域声明以保持与 C 的一致性这一决定可能在未来提案中被重新评估。总结P003407 提案为 Carbon 语言中名称绑定 × 命名空间的交互确立了三条清晰规则命名空间成员须在与命名空间相同的名称作用域实际即文件作用域内声明、绑定模式可直接声明命名空间成员、同一模式内的多个名称必须同属一个命名空间。这些规则不仅消除了class ClassT { var NS.a: i32 0; }之类的歧义还与 C 及 Carbon 既有的名称作用域边界保持一致。从 fail_not_top_level.carbon 等测试文件可以看出规则已经落地为NamespaceDeclNotAtTopLevel等具体诊断并被语义检查器强制实施读者可以通过文件测试体系自行验证。【免费下载链接】carbon-langCarbon Languages main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)项目地址: https://gitcode.com/GitHub_Trending/ca/carbon-lang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门