
Slang 语言 struct 类型规范一致性测试指南从语言参考文档到 44 项可验证声明【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang本文档深入剖析 SlangShader Language编译器中struct类型特性的一致性测试套件。该套件位于 docs/generated/tests/conformance/types-struct/以官方语言参考文档 docs/language-reference/types-struct.md 为唯一规范来源将其中每一条结构体相关的规范性描述claim拆解为 44 项可执行验证并配套 31 个.slang测试文件在 HLSL、GLSL、SPIR-V、Metal、WGSL、CUDA、CPP 与 CPU 等多后端上逐一验证。读完本文你将理解 Slang 编译器如何用测试钉住语言规范包括结构体的四种声明形式、六类成员、构造与聚合初始化、成员函数可改性[mutating]、属性property、下标__subscript、函数调用运算符operator ()、内存布局规则以及规范中尚未被覆盖或与编译器实现存在偏差的部分。测试包定位规范与实现之间的契约types-struct一致性测试包是一个自动生成的规范跟踪清单。其 README 的 front-matter 明确记录了生成元数据source_doc: docs/language-reference/types-struct.md—— 唯一的规范来源generated_at与source_commit—— 记录生成时间与对应源码提交防止测试漂移warning: Auto-generated. May drift from source. Do not edit by hand.—— 提示该清单由工具链自动生成人工修改会被覆盖。测试包的组织方式体现了每条规范声明至少对应一个功能测试的策略见 README.md 的 Intent 一节Coverage strategy: one functional test (INTERPRET or emit) per claim, expanded along documented dimensions (member kinds, visibility, constructor overloads, accessor forms, subscript arities, call-operator overloads, memory layout, generic structs, interface conformance, nested types, andThistype keyword).即每个声明一条功能测试解释执行或代码发射并沿文档定义的维度展开——成员种类、可见性、构造函数重载、访问器形式、下标参数数量、调用运算符重载、内存布局、泛型结构体、接口一致性、嵌套类型与This类型关键字。负面测试negative test覆盖向只读成员写入与构造函数中初始化 const 成员两类编译错误。静态数据成员因为 slangi VM 字节码发射器不支持模块级全局指令只能通过 HLSL 发射测试验证。规范声明全解44 项可验证声明规范声明按文档章节组织为 13 个主题以下逐项展开声明编号 C1–C44 与 README.md 完全对应。语法与声明形式C1–C5语法部分对应语言参考的 Syntax 章节定义了四种声明产生式C1 无体声明no-body formstruct Identifier;仅声明结构类型的存在使其可以在完整定义之前用于类型表达式C2 带成员声明with-members formstruct Identifier { ... }定义带布局与非布局成员列表的结构类型C3 具名/匿名结构名作为标识符使用省略标识符时结构为匿名编译器分配一个未指定的唯一名称C4 匿名结构的内联用法struct { int a; } obj;—— 匿名结构只在 with-members 形式中有意义主要用于内联类型定义表达式C5 链接期声明extern struct与export struct用于跨模块的链接期类型定义。语法产生式细节摘自 types-struct.mdstruct-decl (no-body) [modifier-list] struct [identifier] [generic-params-decl] [: bases-clause] [ simple-type-spec] ; struct-decl (with-members) [modifier-list] struct [identifier] [generic-params-decl] [: bases-clause] (where where-clause)* { member-list } struct-decl (extern) [modifier-list] extern struct [identifier] [generic-params-decl] [: bases-clause] [ simple-type-spec] ; struct-decl (export) [modifier-list] export struct [identifier] [generic-params-decl] [: bases-clause] simple-type-spec ;member-list允许七类成员变量声明var-decl、嵌套类型type-decl、成员函数function-decl、构造函数constructor-decl、属性property-decl、下标运算符subscript-op-decl与函数调用运算符function-call-op-decl。对应的功能测试包括struct-no-body-decl.slang验证 C1并在 hlsl/glsl/spirv-asm/metal/wgsl 五个目标上钉住诊断输出struct-basic-fields-functional.slang验证 C3 与 C6struct-anonymous.slang验证 C4其发射检查显示匿名结构在所有文本发射后端统一命名为SLANG_anonymous。成员种类C6–C12对应语言参考的 Description 章节C6 非静态数据成员以不带static的变量声明存储随每个实例分配在结构体内C7 静态数据成员以static声明存储分配在全局存储中。测试 struct-static-member-functional.slang 验证其在 HLSL 中发射为模块级static int b int(5)在 WGSL 中为varprivate在-O0 -g2的 SPIR-V 中为Private存储类的OpVariableC8static const数据成员必须带默认初始化器。测试 struct-static-const-initializer.slang 验证可通过类型名访问并折叠为常量值C9 嵌套类型通过外层结构名访问TestStruct.NestedStructC10 非静态数据成员顺序分配按声明顺序在结构体内顺序分配C11 接口一致性结构体可实现一个或多个接口测试 struct-interface-conformance.slang 演示了struct MyStruct : IHasValue满足泛型约束readValueT : IHasValueC12 访问控制成员可用public、internal、private修饰默认为internal。从语言参考还可补充两点访问控制细节types-struct.md自语言版本 2026 起无显式说明符的成员继承其外层struct的可见性public结构体的未修饰成员为public嵌套struct的成员可访问外层struct的private成员。此外参考文档明确警告struct 继承 struct 在 Slang 2025 及更早版本中不稳定已在 2026 中移除应改用组合将结构体作为成员代替继承。对象C13–C14对应语言参考的 Objects 章节C13 实例组成对象实例由结构体内所有非静态数据成员组成C14 初始化方式数据成员可通过初始化列表或构造函数初始化。构造函数C15–C21对应语言参考的 Constructors 章节。构造函数语法为__init(参数列表) { 函数体 }无返回类型可有任意数量参数C15 用户构造函数执行定义__init后对象实例化时执行之C16 构造函数重载可定义多个__init按重载决议选择最合适者。测试 struct-constructor-overload.slang 定义了无参、单参__init(int)、双参__init(int,int)三个重载验证TestClass obj2 { 42 };正确命中__init(int)C17 无参构造未提供初始化器时调用无参构造函数C18 未初始化成员状态未被构造函数初始化的非静态数据成员处于未定义状态无法断言见未测试声明一节C19 const 成员禁止在构造函数中初始化测试 struct-constructor-no-const-member.slang 为负面测试断言在__init内对const成员赋值触发编译错误 E30011C20 聚合初始化无用户构造函数时执行聚合初始化初始化列表按声明顺序填充C21 部分初始化列表值不足时剩余成员默认初始化。测试 struct-aggregate-init.slang 验证Plain p2 { 5, 6 };后p2.z被默认初始化为 0。语言参考还给出一个值得注意的 Remarktypes-struct.md无用户构造函数的类在无初始化列表时实例化其初始状态未定义——即使其成员类型自带__init如内嵌TestField f且TestField有__init()外层对象的该字段仍处于未定义状态访问未定义状态的数据成员属于未定义行为。默认初始化器C22–C23C22 初始化列表场景初始化列表未提供某成员值时该成员的默认初始化器提供其值C23 构造函数场景构造函数可覆盖非const成员的默认初始化器。测试 struct-default-field-initializer.slang 与 struct-default-initializer-constructor-override.slang 分别验证这两种场景。静态成员函数C24–C25对应语言参考的 Static Member Functions 章节C24 无对象调用静态成员函数调用不要求对象C25 静态成员访问限制静态成员函数只能访问静态数据成员。测试 struct-static-member-function.slang 验证静态成员函数在 HLSL 中作为自由函数发射。注意修改静态数据成员不需要[mutating]属性见语言参考中incrementAndReturnB的示例注释。非静态成员函数与thisC26–C29对应语言参考的 Non-static Member Functions 章节C26 隐藏的this参数非静态成员函数带隐藏参数this指向对象this.member与裸member访问同一字段。测试 struct-this-implicit-optional.slang 验证二者等价C27 默认只读默认情况下成员函数对非静态数据成员只读。测试 struct-member-function-readonly.slang 验证无[mutating]时读取字段并正确返回值C28[mutating]授权写[mutating]属性授予对非静态数据成员的写权限。测试 struct-mutating-member-function.slang 展示了后端发射细节HLSL/GLSL 中以inout TestStruct this_形参发射Metal 中为thread*指针WGSL 中为ptrfunction, TestStructC29 无属性写入报错非[mutating]函数内写入非静态成员触发编译错误 E30011。测试 struct-mutating-rejected-without-attribute.slang 为负面测试通过//DIAGNOSTIC_TEST:SIMPLE指令断言error、E30011、l-value同时出现。用 C 术语类比语言参考原话成员函数默认为const[mutating]使其成为非const成员函数。属性C30–C33对应语言参考的 Properties 章节C30 读写属性带get与set的property将读取路由到get、写入路由到set。测试 struct-property-get-set.slang 复刻了语言参考中的someProp示例——set同时更新m_val与自动维护的m_valIsPositiveC31 只读属性只有get的属性为只读。测试 struct-property-readonly.slang 验证 getter 在 HLSL 中正确发射C32 隐式newValue省略set参数时set体内自动提供与属性同类型的newValue参数C33 显式参数名可在set访问器声明中显式给出参数名如set(float newVal)。测试 struct-property-explicit-param.slang 验证显式参数在 set 体中使用。属性语法同时支持现代形式property someProp : float { get {...} set {...} }与传统形式property float someProp { ... }无访问器的属性声明形式如property someProp : float;仅用于接口声明。语言参考还提示ref访问器是 Slang 内部特性可能变更、不保证可用因此测试包明确不覆盖ref。成员与嵌套类型访问C34–C35对应语言参考的 Accessing Members and Nested Types 章节C34 点运算符静态/非静态成员与嵌套类型统一通过.访问。测试 struct-dot-member-access.slang 与 struct-members-full-example-emission.slang 验证C35::弃用C 风格作用域解析运算符::已弃用静态成员访问推荐使用.。语言参考示例中TestStruct::incrementAndReturnB()与TestStruct.incrementAndReturnB()等价但后者被推荐。下标运算符C36–C38对应语言参考的 Subscript operator 章节C36__subscript读写路由__subscript与property概念类似但作用于结构体实例本身而非成员且接受参数[]读取调用get、写入调用set。测试 struct-subscript-op-functional.slang 验证x[i] v写入与float v x[i]读取C37 多重重载允许定义多个签名不同的__subscript重载决议与函数调用一致C38 零参数下标__subscript () - float合法。语言参考示例展示了x[]、x[id.y]、x[id.x, id.y]三种用法0/1/2 参数。测试 struct-subscript-multi-param.slang 验证按参数数量分派。函数调用运算符C39–C40对应语言参考的 Function call operator 章节C39 对象即函数operator ()允许对结构体实例应用参数使其像函数一样被调用C40 多重重载签名不同的多个operator ()按重载决议选择。测试 struct-call-op-functional.slang 验证obj()、obj(id.y)、obj(id.z, id.z*2)三种调用形式。内存布局C41–C43对应语言参考的 Memory Layout 章节定义了三种布局C41 自然布局顺序非静态数据成员按声明顺序发射。测试 struct-natural-layout-emission.slang 用混合字段int x; float y; int z;验证HLSL 中三个字段按序出现SPIR-V 中OpMemberName索引 0/1/2 及OpTypeStruct %int %float %int均保持声明顺序测试以-O0固定避免优化消除结构体C42 无尾填充自然布局下结构体大小不进行尾填充C 风格布局才将大小向上取整到对齐值C43 D3D 常量缓冲布局最小对齐为 16且遵循improper straddle规则——数据成员跨越 16 字节边界且偏移未按 16 对齐时偏移向上取整到下一个 16 的倍数。自然布局的完整算法摘自 types-struct.md初始化size 0alignment 1对结构体的每个字段falignment更新为alignment与f对齐值的最大值将size取为不小于size的alignment的最小倍数字段f的偏移设为size将f的大小加到size。完成后size与alignment即结构体的大小与对齐。语言参考特别注明大多数目标平台并不直接使用自然布局但它为其他布局提供了基线——任何布局都必须保证对齐不小于自然布局。This类型关键字C44C44This指代外层结构体在结构体体内This指代外层结构体类型自身。测试 struct-This-type-keyword.slang 中成员函数This withValue(int v)同时把This用作返回类型与局部变量声明类型。在接口体内This指代符合该接口的具体类型即this的类型。测试方法论指令、目标与断言机制所有测试文件均为带//TEST指令注释的.slang源文件由 Slang 测试框架驱动。从测试文件可以总结出三类核心指令示例取自 struct-basic-fields-functional.slang//TEST:COMPARE_COMPUTE(filecheck-bufferCHECK):-cpu -output-using-type //TEST:SIMPLE(filecheckHLSL):-target hlsl -entry computeMain -stage compute //TEST:SIMPLE(filecheckGLSL):-target glsl -entry computeMain -stage compute //TEST:SIMPLE(filecheckSPV):-target spirv-asm -entry main -stage compute -O0 -g2 //DIAGNOSTIC_TEST:SIMPLE(filecheckCHECK):COMPARE_COMPUTE功能测试在-cpu目标上解释执行着色器通过//TEST_INPUT指定的RWStructuredBuffer输出缓冲区与// CHECK: 3形式的逐值断言比对运行结果SIMPLE发射测试对hlsl、glsl、spirv-asm、metal、wgsl、cuda、cpp等目标执行代码发射用// HLSL: struct Point_{{[0-9_]*}}形式的 filecheck 正则断言发射产物结构。测试包对不同后端采用不同策略SPIR-V 发射测试常附加-O0甚至-O0 -g2防止优化消除待断言的结构体DIAGNOSTIC_TEST负面测试断言编译错误输出例如struct-mutating-rejected-without-attribute.slang中的// CHECK-DAG: E30011。每个测试文件头部的//META:注释记录了生成信息doc_ref对应的规范锚点如docs/language-reference/types-struct.md#description、doc_section_digest规范章节摘要用于漂移检测、intentfunctional / negative / emit、pipeline_stageruntime / check / emit。这套元数据使规范与测试的双向追踪成为可能——规范改动会触发摘要变化进而暴露测试漂移。功能覆盖矩阵声明到测试的完整映射以下完整覆盖矩阵摘自 README.md展示了每条声明对应的测试文件与规范锚点声明意图规范锚点测试文件C1 — 无体声明可在完整定义前用于类型表达式functionalDescriptionstruct-no-body-decl.slangC3 — 具名结构体struct Foo { ... }以Foo为类型名字段经点访问functionalDescriptionstruct-basic-fields-functional.slangC4 — 匿名结构体struct { int a; } obj;允许点访问字段functionalDescriptionstruct-anonymous.slangC6 — 非静态字段经实例点运算符读写functionalDescriptionstruct-basic-fields-functional.slang、struct-basic-fields-emission.slangC7 — 静态数据成员分配于全局存储HLSL 中发射为模块级静态变量functionalDescriptionstruct-static-member-functional.slangC8 — 带默认初始化器的static const成员经类型名访问并折叠为常量functionalDescriptionstruct-static-const-initializer.slangC9 — 嵌套类型经外层结构名点访问functionalDescriptionstruct-nested-type.slangC10 — 非静态数据成员顺序分配HLSL 按声明顺序发射functionalNatural Layoutstruct-natural-layout-emission.slangC11 — 实现接口的结构体可传入以该接口为约束的泛型functionalDescriptionstruct-interface-conformance.slangC12 —public成员可访问结构体声明为public以允许public成员functionalDescriptionstruct-access-visibility-default-internal.slangC13/C14 — 实例字段按对象分配初始化列表在构造时设置字段值functionalObjectsstruct-aggregate-init.slangC15 — 用户__init()在实例化时执行字段为构造函数赋值结果functionalConstructorsstruct-constructor-no-param.slangC16/C17 — 多个__init构造函数按参数个数重载决议functionalConstructorsstruct-constructor-overload.slangC19 —__init内对const成员赋值是编译错误E30011negativeConstructorsstruct-constructor-no-const-member.slangC20/C21 — 聚合初始化按序填充字段未指定的尾随字段默认初始化functionalConstructorsstruct-aggregate-init.slangC22 — 初始化列表未提供字段时默认初始化器提供其值functionalDescriptionstruct-default-field-initializer.slangC23 — 构造函数可在体内显式赋值覆盖非const字段的默认初始化器functionalDescriptionstruct-default-initializer-constructor-override.slangC24/C25 — 静态成员函数无对象调用HLSL 中发射为自由函数functionalStatic Member Functionsstruct-static-member-function.slangC26 —this.member与裸member在非静态成员函数内访问同一字段functionalNon-static Member Functionsstruct-this-implicit-optional.slangC27 — 无[mutating]的成员函数读取字段返回值正确functionalNon-static Member Functionsstruct-member-function-readonly.slangC28 —[mutating]成员函数写入非静态字段变异在实例上可观察functionalNon-static Member Functionsstruct-mutating-member-function.slangC29 — 非[mutating]函数内写非静态成员报错 E30011非左值negativeNon-static Member Functionsstruct-mutating-rejected-without-attribute.slangC30 — 带get/set的property读取走get、写入走set隐式newValuefunctionalPropertiesstruct-property-get-set.slangC31 — 只有get的property为只读getter 在 HLSL 中正确发射functionalPropertiesstruct-property-readonly.slangC33 —set访问器显式参数名在 set 体内使用而非newValuefunctionalPropertiesstruct-property-explicit-param.slangC34 — 静态/非静态成员、嵌套类型与成员函数统一经.访问functionalAccessing Members and Nested Typesstruct-dot-member-access.slangC36 — 带get/set的__subscript使[]读写可用functionalSubscript Operatorstruct-subscript-op-functional.slangC37/C38 — 多个__subscript重载含零参数形式按元数分派functionalSubscript Operatorstruct-subscript-multi-param.slangC39/C40 —operator ()使对象可被当作函数调用多重重载按参数个数决议functionalFunction Call Operatorstruct-call-op-functional.slangC41 — 自然布局非静态字段按声明顺序发射到 HLSL 结构体functionalNatural Layoutstruct-natural-layout-emission.slangC44 — 结构体体内This指代外层结构体类型可用作成员函数返回类型functionalD3D Constant Buffer Layoutstruct-This-type-keyword.slang泛型结构体T在 int 与 float 上实例化字段以 T 为类型functionalSyntaxstruct-generic.slang文档完整示例静态/非静态成员、函数、嵌套结构体发射出合法 HLSLfunctionalAccessing Members and Nested Typesstruct-members-full-example-emission.slang其中struct-members-full-example-emission.slang值得特别关注它原样复刻了语言参考 Accessing Members and Nested Types 一节中的完整TestStruct示例含普通字段、静态字段、静态常量字段、嵌套结构体、只读成员函数、[mutating]成员函数与两个静态成员函数并验证其在全部后端可发射。这保证了文档示例本身始终与实现保持同步。未被覆盖的声明边界与原因测试包如实记录了 8 项无法或不宜通过当前测试框架验证的声明README.md 的 Untested claims 一节声明原因分类说明C2 —extern struct链接期声明needs-multi-file-test需要两个.slang文件模块链接与-module输出单文件//TEST指令无法测试C5 —export struct导出类型别名needs-multi-file-test需要多文件模块编译与链接C18 — 构造函数未初始化的成员处于未定义状态(unclassified)未定义行为无法断言编译器允许任意值无可测试声明C7 — CUDA 目标上静态数据成员作为具名模块级变量implementation-detailCUDA 将静态成员直接折叠为内核体内的int(5)无名变量SPIR-V 在-O0 -g2下会发射%b OpVariable %_ptr_Private_int Private测试已钉住该行为C32 —set参数可选、省略时提供隐式newValueout-of-bundle已被使用隐式newValue的 struct-property-get-set.slang 覆盖C35 —::已弃用编译器应警告或接受deprecated文档仅警告弃用未规定警告/接受的具体行为无可测试的错误码C42 — 自然布局无尾填充、C 风格布局向上取整needs-unit-test从 slangc CLI 不可观察需反射 API 查询结构体大小与对齐C43 — D3D 常量缓冲布局最小对齐 16 与 improper-straddle 规则needs-unit-test无反射的 CLI 下不可观察测试着色器中缺少 cbuffer 布局查询或sizeof运算符这组记录的价值在于它显式区分规范写了但无法测与规范写了但编译器行为与之不同并为后续测试基建多文件模块测试、反射 API 单测指明了演进方向。文档缺口观察规范与实现的分歧测试包最具工程价值的产出之一是Doc gaps observed——测试过程中发现的规范文档问题drift-from-sourceC1 无体声明的二义性文档将无体声明描述为指定结构类型的存在……使其可在没有成员声明的情况下用于类型表达式这读起来像前向声明后续 with-members 声明补全。但编译器不会合并二者struct ForwardDeclared;后跟struct ForwardDeclared { int x; }会在 hlsl、glsl、spirv-asm、metal、wgsl 五个目标上一致地报告error[E30200] conflicting declaration与error[E39999] ambiguous reference。而单独使用无体声明struct Fwd;后直接Fwd f;则可以编译。该分歧被记录为_meta/findings/types-struct-no-body-forward-decl-conflicting-declaration.yaml并建议文档明确无体声明是否可由同模块内同名的 with-members 声明补全若不可应在 no-body 条目中说明并给出可接受的用法声明后在类型表达式中使用定义由另一模块经extern struct提供missing-surfaceThis关键字缺少可引用锚点文档末尾的 This Type 小节没有显式标题锚点其前一个主标题是# Memory Layout导致无法用稳定的#this-type锚点引用lint 工具也无法校验doc_ref锚点片段。建议为 This Type 小节添加显式{#this-type}锚点missing-example未定义状态缺少最小示例文档 Remark 1 称无用户构造函数的类在无初始化列表时实例化为未定义状态但未给出外层结构体无构造函数、内层字段类型自带__init的独立最小示例。建议补一段隔离该场景的代码片段并加注释说明。这些观察直接回馈给语言参考文档维护者形成了测试 → 发现问题 → 修正规范的闭环正是该测试包作为规范质量基础设施的核心价值。结语把语言规范钉在可执行测试上types-struct一致性测试包展示了 Slang 项目管理语言特性的工程范式以 语言参考文档 为唯一规范源把每一句应当/必须/会拆解为带编号的声明C1–C44逐条配套功能测试、发射测试或负面测试横跨 CPU 解释执行与 HLSL/GLSL/SPIR-V/Metal/WGSL/CUDA/CPP 全部文本发射后端对无法覆盖的声明如实标注原因对测试中发现的规范缺口按漂移/缺失表面/缺失示例分类回写文档。如果你正在为 Slang 的某个语言特性编写一致性测试或希望理解struct的完整语义边界这个目录docs/generated/tests/conformance/types-struct/连同其规范锚点 types-struct.md 是最值得精读的第一手资料。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考