ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Slang 编译器诊断系统深度解析:从 DiagnosticSink 到 Lua 驱动的诊断生成管线

Slang 编译器诊断系统深度解析:从 DiagnosticSink 到 Lua 驱动的诊断生成管线 Slang 编译器诊断系统深度解析从 DiagnosticSink 到 Lua 驱动的诊断生成管线【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slangSlang 编译器GitHub_Trending/sl/slang拥有一套贯穿所有编译阶段的统一诊断系统错误、警告与提示note都经由同一个DiagnosticSink汇入、过滤、格式化并输出。本文以 Slang 设计文档 docs/generated/design/cross-cutting/diagnostics.md 为主体脉络并结合仓库内 gap-intake 源码审计报告16 项差距中 15 项已以源码证据修复、1 项判定越界拒绝逐条验证的结论系统讲解诊断系统的架构、Lua 定义生成管线、严重级别与警告分组机制、渲染格式、错误码命名空间约束以及为编译器新增一个诊断的完整操作步骤。读完本文你将掌握如何在 Slang 中定义、触发、抑制、美化与机器化消费诊断信息并理解每条规则背后的源码依据。核心架构DiagnosticSink 是所有诊断的中枢诊断系统的中心抽象是DiagnosticSink声明于 source/compiler-core/slang-diagnostic-sink.h。前端front-end各阶段从其持有的FrontEndCompileRequest/CompileRequestBase中获取 sink后端back-end阶段则通过CodeGenContext::getSink从共享的代码生成状态中取出同一个 sink参见 docs/generated/design/architecture/overview.md。无论哪种途径一个编译阶段都只是向它拿到手的 sink 指针发射诊断自身不直接接触输出。一个 sink 拥有哪些状态从 slang-diagnostic-sink.h 的类定义DiagnosticSink类m_前缀成员可以看出每个 sink 持有按 id 的严重级别覆盖表m_severityOverridesDictionaryint, Severity可以把任意诊断升级也可以抑制或降级 note 与 warning但不能把已经处于Error及以上的诊断再调低。已启用警告分组的位掩码m_enabledWarningLevels门控下面警告分组一节介绍的-Wall/-Wextra/-Wpedantic按需启用型警告。SourceManager引用用于把SourceLoc解码为file:line:column。输出缓冲当没有设置writer时格式化文本累积在outputBufferStringBuilder中设置了ISlangWriter* writer则直接流向流式输出。按源码的警告状态跟踪器SourceWarningStateTrackerBase让#pragma与按文件覆盖可以逐 token 调整严重级别的执行。用户可见的入口是#pragma warning(push)/(pop)括起一个区域(specifier : id-list)specifier 含disable、suppress等改变其后 token 的状态。该指令本身报告的是 slang-diagnostics.lua 中pragma-warning-*段代码 15611–15616它在slang-preprocessor.cpp中解析不在本文档的监控路径内。嵌套 sink继承设置 vs 向上转发DiagnosticSink(SourceManager*, SourceLocationLexer, DiagnosticSink* parentSink)这个构造函数会从parentSink复制 flags、颜色模式、unicode 设置、已启用警告分组和严重级别覆盖表——这属于继承设置。它与setParentSink完全不同setParentSink是向上路由旧式legacy诊断以已格式化文本的形式转发而富诊断richGenericDiagnostic以结构化形式转发由父 sink 用自身的设置重新渲染。源码层面可在 slang-diagnostic-sink.h 的DiagnosticSink(SourceManager*, SourceLocationLexer, DiagnosticSink*)构造函数复制getFlags()、getDiagnosticColorMode()、getEnableUnicode()、getEnabledWarningLevels()、getSeverityOverrides()与setParentSink/getParentSink方法中直接印证。两类数据记录Diagnostic 与 DiagnosticInfoDiagnostic是一个小的运行时记录class Diagnostic { public: String Message; SourceLoc loc; int ErrorID; Severity severity; };更高层的静态元数据放在DiagnosticInfo中struct DiagnosticInfo { int id; Severity severity; char const* name; // unique identifier char const* messageFormat; // legacy $0-style argument format WarningLevel level WarningLevel::Default; // warning group };DiagnosticInfo实例由下文所述的 Lua 表在构建期生成。level字段带有默认值因此旧式的四元素聚合初始化宏DIAGNOSTIC(code, severity, name, messageFormat)例如 source/compiler-core/slang-core-diagnostics.h 消费的目录无需改动即可继续编译。诊断定义Lua 驱动的构建期生成管线诊断不是在 C 里手写的而是在 Lua 源文件中声明再由slang-fiddle在构建期转成 C 结构体与消息表。整个仓库只有一个诊断目录catalog且其中每一条都是富诊断rich diagnostic。整条生成链分为四步source/slang/slang-diagnostics.lua 通过调用err、warning、standalone_note、internal、fatal这几个 helper 声明编译器的全部诊断。source/slang/slang-diagnostics-helpers.lua 收集这些调用、做校验并把每条消息解析为带类型的参数与 span。文件末尾调用helpers.process_diagnostics并返回处理后的列表若校验失败会直接抛 Lua 错误。source/slang/slang-rich-diagnostics.h.lua 加载处理后的列表提供模板所需的映射 helpertoPascalCase、getCppType、getSeverityEnum、getWarningLevelEnum。内嵌在 source/slang/slang-rich-diagnostics.h 与 source/slang/slang-rich-diagnostics.cpp 中的 FIDDLE 模板为每条目生成namespace Slang::Diagnostics下一个带每参数一个成员、每位置一个成员的结构体、一个负责插值消息并构建 spans 的toGenericDiagnostic()方法以及一个携带代码、严重级别、名称与警告分组的DiagnosticInfo常量。Diagnostics::getRichDiagnosticsInfo()/getRichDiagnosticsInfoCount()把生成的DiagnosticInfo数组交给DiagnosticsLookup随后 source/slang/slang-diagnostics.cpp 再补充来自getCoreDiagnosticsLookup()的无冲突条目以及唯一的一个别名overlappingBindings→parameterBindingsOverlap源码位置见slang-diagnostics.cpp:38的lookup-addAlias(overlappingBindings, parameterBindingsOverlap)。别名只在用户按名称点名某个诊断时可被观察到——-warnings-disable、-warnings-as-errors、-Wid、-Wno-id的操作数都经由findDiagnosticByName解析——因此-warnings-disable overlappingBindings与使用规范名parameterBindingsOverlap一样始终有效。消费这些生成表的头文件是 source/slang/slang-diagnostics.h其头部注释明确写道All diagnostics are now defined in slang-diagnostics.lua and generated via slang-rich-diagnostics.h. The old slang-diagnostic-defs.h has been removed.需要留意的是第 2、3 步依赖slang-diagnostics-helpers.lua与slang-rich-diagnostics.h.lua而这两者不在本文档的 watched paths 中尽管清单已监控slang-diagnostics.lua和两个slang-rich-diagnosticsC 文件。若要保证 schema 或生成器 helper 的改动能让本文档标记为过期应把这两个文件加入 manifest。诊断条目的解剖一个条目具有 kebab-case 的name、整数code、简短标题以及一个可选的主 span其后可跟附加 spans、notes 与警告分组哨兵err( function-redeclaration-with-different-return-type, 30202, function return type mismatch, span { loc decl:Decl, message function ~decl declared to return ~newReturnType:Type was previously declared to return ~prevReturnType:Type } )主 span 是可选的无位置的诊断例如命令行错误cannot-deduce-source-language会整体省略它。err、warning、internal、fatal、standalone_note、span、note、variadic_span、variadic_note这些 helper 都定义在 slang-diagnostics-helpers.lua它们的签名如err(name, code, message, primary_span, ...)固定了上面展示的参数顺序。消息中的~name:Typetoken 是调用点必须提供的类型化插值参数helper 的parse_message还理解成员访问如~decl.name。validate_diagnostic会拒绝不是合法 kebab-case 的name以及不在error、warning、note、internal、fatal之内的severity。声明 helper 同时固定了条目的严重级别也就固定了它描述的条件类别err、warning、standalone_note条目报告输入源码本身的问题fatal条目也报告输入问题但针对的是编译器无法继续越过的条件如cyclic-referenceinternal条目报告编译器自身的失败internal-compiler-error、unimplemented、unexpected三者代码均为99999正是下文内部编译器错误一节中宏所触发的诊断。因此能让输入走到internal条目意味着编译器缺陷而不是一个可复现的合法输入。variadic_span或variadic_note会在生成的结构体上展开为一个嵌套结构体加一个List成员toGenericDiagnostic()遍历该列表、每个元素追加一条DiagnosticSpan或DiagnosticNote——所以一个变长 note 渲染成每个提供的条目一条 note 记录而不是拼接成一条。ambiguous-overload-for-name-with-args就用它列出候选variadic_note { cpp_name Candidate, message candidate: ~candidateSignature, span { loc candidate:Decl } }要把警告放入某个需显式开启的分组就在尾部位置参数传入哨兵值helpers.all、helpers.extra或helpers.pedantic。slang-diagnostics.lua 在文件顶部附近把extra和pedantic绑定为局部变量all目前还没有使用者将来使用也只需同样的单行绑定。add_diagnostic通过哨兵的is_warning_level标记识别它记为条目的level而不会误当作 spanwarning( vertex-shader-missing-sv-position, 38052, vertex shader ~entryPoint:Name has no output with the SV_Position system value semantic, span { loc location, message ... }, pedantic )slang-rich-diagnostics.h.lua中的getWarningLevelEnum把字符串default/all/extra/pedantic映射为生成DiagnosticInfo所携带的WarningLevel::枚举值。原型 schemasource/slang/diagnostics/下的试验田source/slang/diagnostics/type-errors.lua 使用一种不同的、声明式的 schema——diagnostic name { code ..., severity ..., flag ..., params ..., primary_label ..., secondary_labels ..., notes ..., helps ... }——并把自身描述为多 span 诊断系统的guinea pig试验对象。但在当前source_commit上仓库中没有任何构建规则、FIDDLE 模板或 Luadofile加载该文件该目录下也没有其他文件因此发布版编译器没有任何代码由它生成。请把 slang-diagnostics.lua 视为唯一的活目录特别是其中的flag键在当前管线中没有任何消费者。严重级别六个枚举值与它们的执行语义Severity声明于 source/compiler-core/slang-diagnostic-sink.henum class Severity { Disable, Note, Warning, Error, Fatal, Internal };同一头文件中的一组static_assert保证枚举值与会话公开 API include/slang.h 暴露的SLANG_SEVERITY_*常量数值一致因此使用公共 API 的调用方看到的数值与内部一致。getSeverityName渲染给用户看的名称依次是ignored、note、warning、error、fatal error、internal error。六个名称中只有五个会被真正打印有效严重级别为Severity::Disable的诊断在diagnoseImpl/diagnoseRichImpl进入渲染之前就返回了见 slang-diagnostic-sink.cpp 中Severity::Disable在渲染前被丢弃的分支所以ignored命名的是严重级别机制的一种状态而不是一种输出形式。fatal error与internal error打印后都会通过SLANG_ABORT_COMPILATION中止编译。fatal error的两条产生路径fatal error是唯一一个没有单一明显触发点的渲染名称值得专门说明。它有两条路径。路径一用fatalhelper 声明。目前 slang-diagnostics.lua 中有九个fatal条目CodeNameMessageE39901cannot-process-includeinternal compiler error: cannot process__includein the current semantic checking contextE39997maximum-type-nesting-level-exceededmaximum type nesting level exceededE40002cyclic-referencecyclic reference~declE40003compilation-ceasedcompilation ceasedE40030function-never-returns-fatalfunction never returnsE51701cooperative-matrix-unsupported-captureCoopMat.MapElementper-element function cannot capture buffers, resources or any opaque type valuesE55206generic-specialization-recursion-cyclerecursive generic specialization detectedE55207generic-specialization-budget-exceededgeneric specialization exceeded maximum depthE56003use-of-uninitialized-opaque-handleuse of uninitialized opaque handle这张表应被读作候选清单而非保证清单fatalhelper 只固定声明的严重级别某个具体编译是否真正走到触发点、上游是否先恢复了取决于触发它们的 checking 与 IR pass这些文件不在本文档监控路径内因此本文档无法断言这九条中哪些会被某个特定输入实际产出。审计报告同样指出fatal条目slang-diagnostics.lua 的 3432–3469 行与internal条目5896–5946 行全部99999是两种不同类别的条件internal条目意味着编译器缺陷而非可复现输入。路径二不需要任何声明的条目。source/compiler-core/slang-diagnostic-sink.cpp 中的outputExceptionDiagnostic第 978 行调用diagnoseRaw(Severity::Fatal, An unknown exception occurred)因此任何逃逸到该边界的 C 异常都会渲染出一条无代码的fatal error: An unknown exception occurred。随后它故意捕获由此产生的AbortCompilationException以免中止操作经由loadModule泄漏出去。另外注意getEffectiveMessageSeverity见下不会让-Wno-风格的覆盖把已经达到Error、Fatal或Internal的严重级别调低——fatal 诊断无法从命令行被抑制。有效严重级别的裁决顺序DiagnosticSink::getEffectiveMessageSeverityslang-diagnostic-sink.cpp把静态的DiagnosticInfo::severity转化为实际使用的严重级别顺序如下按源码的警告状态跟踪器可能先调整 note/warning若m_severityOverrides中存在该 id 的条目则它获胜——例外它不能降低已达Error、Fatal、Internal的严重级别此时只有至少同样严重的覆盖才生效否则未启用的警告分组把警告降级为Severity::Disable最后TreatWarningsAsErrors标志把任何幸存的警告提升为Severity::Error。因此按 id 的覆盖优先于分组门控——这正是-Wid能对某个关闭分组中的单条警告强制启用的原因。选项表把它们拼写为-Wid/-Wno-id但操作数要经过overrideDiagnostic它既接受整数 id 也接受诊断名称审计报告 d4aeafd64cde 修正了文档中-Wname的笔误并确认-Wid操作数可为 id 或名称对应源码 slang-options.cpp 593–594 行的注册与 slang-compiler-options.cpp 635–644 行、slang-diagnostics.cpp 71–97 行的解析。警告分组独立而非嵌套警告可以打上分组标签只在用户显式开启时才发出。分组在 slang-diagnostic-sink.h 中声明为WarningLevel建模自 clang/gcc 的-Wall/-Wextra/-Wpedantic分组enum class WarningLevel { Default 0, All 1, Extra 2, Pedantic 3, };一组static_assert让这些值保持与 slang.h 中公共SlangWarningLevel枚举的SLANG_WARNING_LEVEL_*常量同步正如旁边为Severity所做的一样。几个关键语义分组相互独立、不嵌套一条警告只由它携带的那一个分组门控。Default是每个未打标签诊断的隐式分组总是发出。sink 的m_enabledWarningLevels位掩码初始只置了Extra位源码见 slang-diagnostic-sink.h 中m_enabledWarningLevels (uint32_t(1) uint32_t(WarningLevel::Extra))所以Extra警告开箱即发而All与Pedantic警告保持静默直到被启用。DiagnosticSink::enableWarningLevel置位移位前做了边界检查因此经公共 API 传入的非法整数不会造成越界移位——源码注释明确说明这种值会被忽略applySettingsToDiagnosticSink还会在 API 边界再过滤一次isWarningLevelEnabled是getEffectiveMessageSeverity咨询的谓词Default恒为真。getEnabledWarningLevels/setEnabledWarningLevels暴露原始掩码便于在 sink 之间复制设置与getFlags/setFlags互为镜像。因为启用是按分组而非累计的overrideDiagnosticSeverity不能把覆盖等于名义严重级别当作无操作只有当info-level WarningLevel::Default时它才会丢弃这样的覆盖对于分组警告把它覆盖回Warning正是强制启用这一有意义的动作因此条目必须保留。把用户请求变成enableWarningLevel调用的管道-Wall/-Wextra/-Wpedantic命令行拼写、SlangWarningLevel枚举、以intValue0携带分组的CompilerOptionName::WarningLevel选项位于 include/slang.h、source/slang/slang-options.cpp 与 source/slang/slang-compiler-options.cpp。源码位置与消息渲染当 sink 格式化一条诊断时它用SourceManager把SourceLoc解码成file:line:column并取回原始源码行用于 caret插入符渲染。这里有两条不同的格式化路径旧式路径diagnoseImpl仅在未设置AlwaysGenerateRichDiagnostics标志时走调用 slang-diagnostic-sink.cpp 中的formatDiagnostichelper。当位置落在合成的 token-paste 视图PathInfo::Type::TokenPaste中时它通过SourceView::getInitiatingSourceLoc()循环回溯每跳发出一条MiscDiagnostics::seeTokenPasteLocationnote。审计报告 37d42be463f4 用源码确认了这个循环只存在于旧式diagnoseImpl路径slang-diagnostic-sink.cpp 455–495 行仅由 786 行的diagnoseImpl到达。富诊断路径diagnoseRichImpl目录中每条条目都经由它报告通过renderDiagnostic渲染没有这种循环所以##拼接内部的富诊断只指向拼接后的文本本身。一条渲染出的诊断以形如severity[E5-digit id]: message的头部开头。id 零填充且无论什么严重级别都恒带E前缀——所以 id 为 41016 的警告打印为warning[E41016]而不是W41016代码为负数的条目seeTokenPasteLocation是-1不打印方括号。位置、源码摘录与 notes 跟在后面warning[E41016]: use of uninitialized variable -- uninit.slang:6:13头部格式的精确拼写已在审计中由 source/compiler-core/slang-rich-diagnostics-render.cpp 796–805 行确认先是严重级别名称然后[E加code 0时零填充到五位再: 和消息。颜色、Unicode 字形与-diagnostic-color-diagnostic-color always|never|auto选择的不只是颜色除非宿主通过setEnableUnicode显式设置了 unicode 标志否则渲染器用同一个谓词选择框架字形。颜色关闭时框架是 ASCII--、|、^、-颜色开启时是包裹在 ANSI SGR 转义里的 Unicode 方框绘制╭╼、│、━、┬。auto会询问 writer 是否为控制台因此管道输出得到 ASCII 形式。源码依据slang-rich-diagnostics-render.cpp 142 行根据enableUnicode选择s_unicodeGlyphs还是s_asciiGlyphs197–211 行slang-diagnostic-sink.h 362–394 行的shouldEnableTerminalColors/shouldEnableUnicode说明 unicode 在未显式设置时由颜色谓词推导-diagnostic-color选项注册于 slang-options.cpp 1264–1265 行附近OptionKind::DiagnosticColor。命令行的合成源码位置解析命令行期间产生的诊断被挂到一个合成源码上而不是翻译单元中的某个文件选项解析器把自己的 sink 配给CommandLineContext的 source manager它的唯一一个SourceView携带PathInfo::Type::CommandLine。渲染器对该类型做了特判slang-rich-diagnostics-render.cpp 765–777 行打印路径command line且不带line:column——因此基于位置或 caret 锚定的匹配器无处可锚定请改用错误代码来固定这类诊断。审计报告 3de1c2fff064 给出了完整链路slang-options.cpp:4962给解析 sink 提供CommandLineContextsource manager配合 slang-diagnostic-sink.cpp 687–690 行。输出目标与机器可读格式格式化文本在有writer时写入 writer否则累积进outputBuffergetBlobIfNeeded可把它作为ISlangBlob交还。需要机器可读形式的工具可以设置 slang-diagnostic-sink.h 中声明的DiagnosticSink::Flag::MachineReadableDiagnostics标志——命令行拼写-enable-machine-readable-diagnostics同时设置它和AlwaysGenerateRichDiagnostics见 slang-options.cpp 2857–2873 行前者隐含启用实验性富诊断。该标志把渲染切换为制表符分隔的记录Ecode\tseverity\tfilename\tbeginline\tbegincol\tendline\tendcol\tmessage注意这不是 JSON schema。错误码命名空间集中管理的单一整数域诊断 id 位于一个集中管理的单一共享整数命名空间上面的30202即为一例与唯一name并列存在。slang-diagnostics-helpers.lua 中的process_diagnostics在生成期强制执行三条规则名称与代码都必须唯一allow_duplicate_diagnostic_codes为false。共享同一代码的诊断必须共享严重级别allow_severity_conflicts为false因为-warnings-disable id会把 id 解析到单一条目然后检查其严重级别。一个代码不能在 Lua 目录中绑定一个名称、又在某个 CDIAGNOSTIC(...)目录中绑定不同名称后者即cpp_diagnostic_defs_files列出的 source/compiler-core 下的slang-misc-、slang-lexer-、slang-json-diagnostic-defs.h文件。一个简短的intentional_shared_code_list把故意多绑定的代码从唯一性与跨目录检查中豁免负数哨兵、10000非法字符变体、39999重载/查找伞形代码、99999内部错误兜底代码以及 JSON 目录的20001–20012区间。由于 slang-diagnostic-sink.h 中getDiagnosticById注明多个诊断可能拥有相同 id且只返回第一个加入的实现见 slang-diagnostic-sink.cpp 920 行m_idMap.addIfNotExists先到先得需要精确定位某条诊断的工具应优先使用name而非整数 id。读取输出时也有同样的注意事项39999由 27 个条目共享no-applicable-overload-for-name-with-args与ambiguous-overload-for-name-with-args都在其中而渲染头部只带 id 和消息、从不带名称slang-rich-diagnostics-render.cpp 796–805 行渲染严重级别、[Ecode]与消息从不渲染名称所以对多绑定代码而言消息文本是唯一的区分手段。目录条目 ≠ 可达性承诺目录中的一条目只是编译器可以发出的诊断它不承诺某个输入能到达它其摘要行也不标识触发它的构造。两条审计验证的案例最能说明问题expected-array-expression30020只由 source/slang/slang-check-expr.cpp 的SemanticsExprVisitor::visitGetArrayLengthExpr抛出而它检查的GetArrayLengthExpr节点没有 parser 产出——唯一的生产者是ASTSynthesizer::emitGetArrayLengthExprslang-ast-synthesis.cpp 112–118 行唯一调用方是 slang-check-decl.cpp 8905 行仅用于数组类型字段的逐成员赋值合成所以没有任何源码文本能触发它。用户可见的拼写Array::getCountcore.meta.slang 2265–2267 行是一个由__intrinsic_op($(kIROp_GetArrayLength))支撑的普通方法作用于非数组时报告的是no-member-of-name-in-type30027slang-diagnostics.lua 1309–1313 行。相反cannot-convert-array-of-smaller-to-larger-size30024读起来像赋值错误实际却来自GLSL varying 合法化针对的是声明得比目标所需更大的系统值数组——例如[domain(quad)]着色器中的float inside[4] : SV_InsideTessFactor用slangc ds.slang -target glsl -stage domain -entry main可复现出error[E30024]: array size mismatch prevents conversion而目标类型是float[2]而普通的float b[4] a;a为float[2]报告的是type-mismatch30019。审计报告 53a781a689ef 用原生 arm64 的build-arm64/Debug/bin/slangc实测确认x[0]作用于float得到error[E30013] ... no subscript declarations found for type floatfloat b[4] a;得到error[E30019] ... expected an expression of type float[4], got float[2]——两条遮蔽示例其实都不是遮蔽30024只在 source/slang/slang-ir-glsl-legalize.cpp 1976–1980 行发出对照 838–846 行要求的float[2]类型。因此凭条目名称猜测输入是不可靠的回归测试才是哪些条目有可演示复现的记录。工具如何抑制或提升诊断工具通过overrideDiagnostic/overrideDiagnostics声明于 source/slang/slang-diagnostics.h实现在 source/slang/slang-diagnostics.cpp抑制或提升诊断。两者都接受单个标识符或逗号分隔的列表标识符可以是整数 id 或名称。几个关键语义未识别的名称报告为UnknownDiagnosticName未识别的数字 id 被静默忽略——这样构建脚本可以在不知道哪个编译器版本引入某警告的情况下安全地禁用之。若originalSeverity不是Severity::Disable则被查到的诊断的严重级别必须与之匹配因此-warnings-disable不能用来压制 error。这种不匹配在这里选项层被拒绝而不是由getEffectiveMessageSeverity拒绝报告为unknown-diagnostic-name31111slang-diagnostics.lua 2623–2628 行声明并指名所请求的标识符——与名称不存在用的是同一条诊断。由于该条目是err请求会使编译失败而不是留下未抑制的诊断。名称查找对大小写约定不敏感findDiagnosticByName既接受 Lua 条目中的 kebab-case 拼写也接受生成器据其产出的 lowerCamelDiagnosticInfo::name。审计报告 f5bfc8605d11 确认该拒绝31111 请求的标识符是在选项层强制执行的而非在getEffectiveMessageSeverity中。面向用户的诊断风格指南是 docs/diagnostic-guidelines.md本文档不重复它——如何挑选错误码、如何写出好消息由该约定文档负责。内部编译器错误走同一个 sink 的特殊诊断宏SLANG_INTERNAL_ERROR、SLANG_UNIMPLEMENTED与SLANG_DIAGNOSE_UNEXPECTED定义于 source/slang/slang-diagnostics.h以Severity::Internal把内部编译器错误送入与普通诊断相同的 sink。在调试构建中SLANG_INTERNAL_ERROR与SLANG_UNIMPLEMENTED——但不是定义在调试条件之外的SLANG_DIAGNOSE_UNEXPECTED——会额外发出一条记录宏触发处 C 源码位置的伴随 note(sink)-diagnoseRaw( Slang::Severity::Note, note: internal error triggered at __FILE__ : SLANG_DIAG_STRINGIFY(__LINE__) \n);审计报告 a9afaeff7d54 逐一核对internal条目slang-diagnostics.lua 5896–5946 行全部99999与fatal条目3432–3469 行分别描述两类条件slang-diagnostics.h 43–68 行的 ICE 宏恰好抛出那三个internal条目slang-diagnostic-sink.cpp 696–700 行在 Fatal时中止。运行时行为方面SLANG_ASSERT/SLANG_RELEASE_ASSERT的行为由SLANG_ASSERT环境变量决定支持值system、debugbreak、release-assert-only或未设置见 CLAUDE.md。在 Windows 上构建选项SLANG_IGNORE_ABORT_MSG进一步在无人值守运行时抑制模态中止对话框。这些机制独立于诊断 sinkSLANG_RELEASE_ASSERT总是直接调用::Slang::handleAssertSLANG_ASSERT在调试构建中同样如此但在发布构建中展开为SLANG_ASSUME(VALUE)成为优化器提示而非检查SLANG_ASSERT_FAILURE是另一个独立宏无条件调用::Slang::handleAssert不是某个 assert 的展开SLANG_UNREACHABLE经由::Slang::handleSignalSignalType::Unreachable路由handleAssert与handleSignal都通过 source/core/slang-common.h 中的slang-signal.h包含声明完全绕过 sink。基于 sink 的内部错误路径就是上面的SLANG_INTERNAL_ERROR、SLANG_UNIMPLEMENTED与SLANG_DIAGNOSE_UNEXPECTED三个宏。新增一个诊断八步操作流程要在 Slang 编译器中加入一条新诊断按以下步骤操作在 source/slang/slang-diagnostics.lua 中紧挨着同一数值区间的其他诊断添加一个err、warning、standalone_note、internal或fatal调用。在所属子系统parser、checker、lowering、IR pass、emit的约定区间内分配一个唯一整数 id——约定见 docs/diagnostic-guidelines.md。若 id 与其他 Lua 条目或某个 CDIAGNOSTIC(...)目录冲突process_diagnostics会让构建失败。选择唯一的 kebab-casename生成器会据此派生 C 的PascalCase结构体名和 lowerCamel 的DiagnosticInfo::name。编写消息文本使用~param/~param:Type插值并添加一个主span及任何次级 spans、notes 或变长 spans/notes。若这是一条不应默认发出的警告把all或pedantic哨兵作为最后一个位置参数追加——该警告此后需要-Wall或-Wpedantic才会发出。不要为此使用extrasink 初始化时m_enabledWarningLevels已置Extra位extra警告无需任何-W标志就会触发。重新构建——slang-fiddle会重新生成消费方头文件使该诊断出现在Slang::Diagnostics::Name中。在检测到该条件的代码位置调用sink-diagnose(Slang::Diagnostics::Name{...})并在该翻译单元包含slang-rich-diagnostics.h。在 tests/ 下添加DIAGNOSTIC_TEST回归测试测试指令约定见 CLAUDE.md。注意DIAGNOSTIC_TEST指令与注解的语法、解析器tools/slang-test/diagnostic-annotation-util.cpp不在 diagnostics 设计文档的监控路径内审计报告 78544eaa1263 将该项判定为越界out-of-scope相关约定在 docs/diagnostics.md 105–165 行维护。用测试验证诊断行为诊断系统的行为有大量回归测试支撑。与本文最直接相关的测试位于 docs/generated/tests/design/cross-cutting/diagnostics/由设计文档生成的测试快照与 tests/diagnostics/。例如pragma-warning-disable-suppresses.slang验证#pragma warning(disable: 41024)会抑制其后 token 的警告——同一个逗号运算符表达式在无 pragma 时告警、有 pragma 时不告警CHECK-NOT断言代码与消息都不出现CHECK断言入口点仍被发出。pragma-warning-unknown-specifier-warns.slang验证未知 specifier 的告警行为。tests/diagnostics/pragma-warning-single-file.slang、tests/diagnostics/pragma-warning-multifile-main.slang 等覆盖单文件与多文件场景下的 pragma 状态跟踪。这些测试正是前面强调的回归测试才是条目可达性的记录的实证。本文档不涉及的内容为保持聚焦以下内容不在本文范围诊断写作风格指南见 docs/diagnostic-guidelines.md。全部诊断 id 的完整枚举权威来源是 source/slang/slang-diagnostics.lua加上它交叉校验的 source/compiler-core 下 CDIAGNOSTIC(...)目录。在此罗列会重复构建产物并在每次变更时漂移。驱动 sink 的命令行与 API 表面-warnings-disable、-warnings-as-errors、-Wno-name、-Wall/-Wextra/-Wpedantic以及对应的CompilerOptionName条目它们位于 source/slang/slang-options.cpp、source/slang/slang-compiler-options.cpp 与 include/slang.h选项表面本身由用户指南文档说明。小结Slang 的诊断系统是一条从 Lua 声明出发、经slang-fiddle生成 C、再由DiagnosticSink统一执行的完整链路。把握住几个核心心法即可游刃有余目录只有一份、条目全是富诊断严重级别由声明 helper 固定但实际生效值要经过警告状态跟踪器、按 id 覆盖、分组门控、TreatWarningsAsErrors的四级裁决警告分组独立不嵌套Extra默认开启、All/Pedantic需显式启用错误码命名空间由生成期三条规则守护但目录条目不等于可达性承诺精确定位诊断应优先用name、区分多绑定代码时只能依赖消息文本internal/fatal会中止编译fatal error还可能在无任何声明条目的情况下经outputExceptionDiagnostic出现。无论你是新增诊断、改进错误格式还是把 Slang 接入自己的工具链消费诊断本文给出的源码路径与测试用例都可以作为你继续深入的起点。【免费下载链接】slangMaking it easier to work with shaders项目地址: https://gitcode.com/GitHub_Trending/sl/slang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表