feat(shader): add neutral IR and standalone diagnostics - #3054
feat(shader): add neutral IR and standalone diagnostics#3054zhuxudong wants to merge 177 commits into
Conversation
- move createPosition/createRange + their pools to ShaderCompilerUtils - move pass-text error context to ShaderCompilerUtils.processingPassText - add ICodeGenVisitor interface so AST no longer imports concrete CodeGenVisitor - parser/lexer/codegen now depend on ShaderCompilerUtils, not ShaderCompiler entry prep for extracting shared shader-parser package (c3); no behavior change, 197 tests green
- copy ClearableObjectPool/IPoolElement into local common/ObjectPool - add local no-op Logger (engine-core Logger is also disabled by default) - copy render-state enums into common/enums/RenderStateEnums (values mirror engine-core) - parser/lexer/lalr/sourceParser now engine-core-free; engine-math (Color) kept as foundation dep deviates from RFC: Color kept as engine-math dep, render-state enums copied; 197 tests green
…mpiler - move lexer/preprocessor/parser/lalr/AST/sourceParser + utils into @galacean/engine-shader-parser - shader-compiler depends on it; cross-package imports go through the package barrel - shader-parser ships one always-full build (jscc _VERBOSE=true), external to shader-compiler - shader-parser drops stripInternal so compiler/analyzer can use internal parser APIs pure relocation; 197 shader-compiler tests green
…gnostics - new @galacean/engine-shader-analyzer drives the parse + collects diagnostics, skips codegen - restores diagnostics the runtime compiler discards (parity verified vs verbose compiler) - harvest approach: checks stay in shader-parser (single source), no visitor duplication - Phase 1 returns GSError verbatim; structured API + new checks are Phase 2 harvest deviates from RFC's DiagnosticVisitor plan; 199 tests green
- analyzer now runs codegen too, capturing codegen-level diagnostics (struct/MRT/gl_FragData) - ungate codegen error collection so the single release build always collects them - remove ShaderCompiler._logErrors + calls: the compiler compiles, never reports - delete the verbose build variant (/verbose export, rollup push, stub dir) - shader-compiler drops stripInternal + exports GLES visitors so analyzer can drive codegen completes Phase 1: diagnostics live in the analyzer; 200 tests green
- drop unused ObjectPool.garbageCollection (pools reuse via clear(), never GC) - remove dead verboseMode branches from root rollup (no verbose build remains) - collapse duplicate glslValidate calls left by the verbose→release test switch - drop an obsolete warning-spy guard (the warning no longer exists; macro asserts cover it) - tighten comments: drop task-context and a claim of a non-existent sync test
- remove unused abstract ObjectPool base class (only ClearableObjectPool extends it; inline the two fields) - replace indirect ReturnType<typeof ShaderSourceParser.parse> with IShaderSource
- Diagnostic interface (severity, code, range, message, source, relatedSource) - DiagnosticCode registry: C0 (parser/codegen), A1 (ShaderLab), B1/B2 (RenderState) - gseErrorToDiagnostic converts GSError to structured Diagnostic - ShaderAnalyzer.analyze() returns AnalysisResult.diagnostics: Diagnostic[] - heuristic code mapping from GSErrorName + message content - tests verify structured output for all 3 diagnostic sources
- reportWarning routed to Logger.warn, a noop since Phase 1 decoupled Logger - the "declared before used" warning was silently dropped as a result - now push CompilationWarn to errors[] (gated by _VERBOSE, like reportError) - analyzer surfaces it as a C0-07 warning diagnostic; drop unused Logger import
- a failed function lookup is signature-keyed, conflating unknown names and wrong-arg calls - both surfaced as one opaque "No overload function type found" message - re-probe by name alone (+ builtin registry) to split the two cases - unknown names now report a distinct "Undefined function" (C0-09); wrong-args keeps C0-06
- shader-parser always builds _VERBOSE=true, so its 88 #if _VERBOSE blocks were dead scaffolding - the guarded code (diagnostics, line/column tracking) already shipped in every build - strip all markers + drop the 2 dead #else console.error fallbacks - dist and behavior identical to before; 202 shader tests stay green
- rollup _VERBOSE jscc is now a no-op (zero #if _VERBOSE left repo-wide) — remove it + the import - VisitorContext location: any -> BaseToken["location"]; IRenderState drops the pointless | any - BaseLexer throwError msgs: any[] -> unknown[] (only ever join()'d) - map the "referenced X not found" codegen error to a dedicated C0-22 instead of the C0-08 fallback
- SymbolTable.insert silently overwrote a same-scope duplicate via a now-noop Logger.warn - insert() now returns whether it replaced an equal symbol; decl sites surface it as a C0-10 warning - macro-branch siblings stay exempt (insert skips isInMacroBranch entries) — covered by a test - applies to local (SingleDeclaration/InitDeclaratorList) and global (VariableDeclaration) vars
- add PostfixExpression.semanticAnalyze: a `.field` on a known vector is validated as a swizzle - catches out-of-range components (.z on vec2), mixed sets (.xr), bad chars, length > 4 - only fires when the base type is a concrete vecN — struct members and unresolved bases skip - ParserUtils.swizzleError holds the rule; first C1 (GLSL type) layer check
- AssignmentExpression flags `a = b` when b's type cannot convert to a's - ParserUtils.isAssignable models GLSL ES3 implicit conversions (int->float, ivecN->vecN) - so valid coercions (float = int) are NOT flagged; only definite conflicts surface - fires only when both operand types are concrete; compound RHS / structs are skipped
- JumpStatement.semanticAnalyze checks `return expr` against the function's declared return type - reuses ParserUtils.isAssignable, so implicit conversions (return int from a float fn) pass - skips void returns (C0-04 covers those) and unresolved/compound expressions
- ShaderTargetParser singleton: a failed parse (syntax error) left _traceBackStack dirty - the next parse was then corrupted: a valid shader got a spurious diagnostic - this hits the runtime compiler too (ShaderCompiler/ShaderAnalyzer share the singleton) - clear _traceBackStack each parse; reset SymbolTableStack._macroLevel in clear() too - regression test: a broken analyze() must not corrupt the following valid one
- registerRule(rule) runs user rules after the built-in checks on every analyze() - rules get source + parsed structure + positionAt(); report() namespaces the code as <name>/<code> - a throwing rule surfaces a <name>/rule-error warning instead of crashing analysis - analyze() restructured so rules run even when structure parsing fails (built-in path unchanged)
- src/shader-playground.ts: a live editor + diagnostics panel driven by ShaderAnalyzer.analyze() - no engine init (analyzer is standalone); the sample shows C0-09/10 and C1-01/02/03 - also demos registerRule via a "demo/no-discard" custom rule - wire engine-shader-analyzer into examples deps + vite optimizeDeps exclude
- upgrade shader-parser's local Logger to a real controllable one (enable/disable, off by default) - keeps zero engine-core dependency (local copy mirroring engine-core's API) - route runtime console.* through it: error prints -> Logger.error, version banner -> Logger.info - the compiler version banner no longer prints on every import (silent unless logging enabled) - remove dead debug dumpers printStatePool / _printStack (uncalled) and their console - bundler CLI keeps console (build-time terminal output); shader-analyzer had no bare console
- local common/Logger.ts existed to avoid an engine-core dep, but core never imports shader pkgs - core injects shader-compiler (no import), so there was never a cycle to avoid - depend on engine-core and use its Logger; redirect 4 parser + 1 compiler imports, drop the copy - logging now unifies with the engine's Logger; 1428 tests pass, compiledShaders byte-identical
- the diagnostic package now logs each diagnostic via the engine Logger, off by default - severity-mapped: error->error, warning->warn, info->info, hint->debug - add @galacean/engine-core dep; analyze() logs after collecting all diagnostics - enable Logger to see every syntax/semantic problem in the console while analyzing
…cal copy - common/ObjectPool.ts was a local copy of core's pool (made to avoid the now-cycle-free core dep) - core's ClearableObjectPool/IPoolElement are behavior-identical (same get/clear logic) - redirect the 6 import sites to @galacean/engine-core; drop the local copy + its re-export - 213 shader tests pass, compiledShaders byte-identical to dev/2.0
…al copy - common/enums/RenderStateEnums.ts was a hand-synced local copy of core's 8 render-state enums - drop it; ShaderSourceParser imports them from @galacean/engine-core (merged into its core import) - no re-export consumer; design types render state by number, so no enum-identity issue at boundary - compiledShaders byte-identical (render-state serialization unchanged); 213 tests pass
- rollup.config.js: drop the dead jscc plugin (no #if _VERBOSE left) + its stale verbose comments - also drop a dangling src/enums/README.md reference in that file's header - convert.ts: drop the "Phase 2 ... DiagnosticVisitor" promise (no DiagnosticVisitor was built) - Preprocessor/Lexer: fix comments referencing the removed verbose build / wrong package
- drop ShaderInstructionEncoder's hand-synced local copy of the directive enum - core now exports ShaderPreprocessorDirective publicly (values unchanged Text=0..Undef=10) - compiledShaders stay byte-identical to dev/2.0; tsc clean across the 3 shader packages
…e registry - gSErrorNameToCode returns DiagnosticCode.* refs, dropping 34 duplicated raw "C0-xx" literals - return type is DiagnosticCodeValue so tsc rejects any code absent from the registry - remove the never-passed defaultSeverity param (all five callers use the default)
- add SemanticWalker that walks the built AST and derives diagnostics from node type clues - PostfixExpression still produces the type clue; swizzle check (C1-01) leaves its semanticAnalyze - establishes parser-produces-clues / analyzer-judges pattern; first step of diagnostics decoupling - compiledShaders byte-identical; full suite 1429 pass
- IntegerConstantExpressionOperator.compute is now optional; absence = unknown-operator clue - C0-02 judgment leaves parser semanticAnalyze for the walker - compiledShaders byte-identical; full suite 1429 pass
- remove SemanticWalker; swizzle (C1-01) and operator (C0-02) judgments go back to parser - analyzer-side instanceof was a hack; judgment belongs internalized in parser clue computation - correct model: error-as-clue in parser + analyzer generic collection - compiledShaders byte-identical; 1429 tests pass
- Reuse parsed shader passes across diagnostics and code generation. - Preserve the runtime parser hot path and object pool. - Report only proven macro conflicts. - Reject invalid 32-bit preprocessor literals consistently. - Add browser, package-consumer, isolation, and memory coverage.
- Retain parser-owned preprocessor trees through analysis and code generation. - Keep unsupported complex conditions silent instead of applying regex inference. - Fix browser test roots and allow clean packed-package dependency resolution.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(P1)。已基于 a71791b72a1d55001ab6ff245b8c97a66508105f...6dc9d17aefea480b3d70ec7e74b8cbe06cb9ce82 的 2 个增量 commit、64 个文件完成审查,并沿 #if/#elif source → shared expression tree → ParsedShaderPass.blockingErrors → ShaderCompiler admission → ShaderInstructionEncoder → ShaderMacroProcessor/runtime variant、ShaderAnalyzer → retained parsed pass → ShaderCompiler 以及 package manifests → pack/install → ESM/CJS/.bin consumer 各追一层。GitHub lint、三平台 build、四组 E2E、coverage 与 package-consumer 所在 build 均成功;当前 PR 另被 GitHub 标记为 CONFLICTING / DIRTY。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 6dc9d17aefea480b3d70ec7e74b8cbe06cb9ce82。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮的两套 expanded-range/source-map 转换已收敛:
ParsedShaderPass.sourceMap继续拥有唯一映射事实,mapExpandedShaderRange提供唯一纯转换,parser 规范化错误、analyzer 机械消费同一结果,compiler 不再另维护 formatter-side offset/position 算法(83c732b)。 - 发布契约已由真实消费者闭环:CI 在 build 后打包并安装 math/design/core/parser/analyzer/compiler tarball,覆盖 parser root reject、ESM/CJS internal entries、shared chunks、安装生成的
.bin、include/stdin/diagnostic/exit code;没有恢复 Rollup chmod、raw-dist wrapper 或 legacy export(83c732b、6dc9d17)。 - 前轮 expression 问题中的两项子问题已关闭:analyzer-local validator 与 core-local evaluator/tokenizer 已由 design 包的共享 parser/evaluator 取代,parser-owned condition tree 会传到 encoder;evaluator failure 也不再被翻译成 silent false。尚未进入统一 admission 的确定性/展开期错误见问题 1。
- include registry 的 prototype key、canonical include key/base/cache、source attribution、request-owned parser/semantic state、bounded GLES visitor pool,以及原有 branch/reachability/operator/declarator/source-structure gate 均继续保持关闭。
- 前轮列出的
AndCondition/OrCondition/NotCondition、GLESVisitor、VisitorContext、ShaderPosition、ShaderRange、SemanticAnalyzer与两个@ts-ignore已按多行 TSDoc或@ts-expect-error收口;本轮新增 surface 和残留项见问题 4。
问题
-
[P1]
packages/design/src/shader-compiler/PreprocessorExpression.ts:126-164/packages/shader-parser/src/lexer/Lexer.ts:240-243,1021-1052/packages/shader-parser/src/lexer/AnalyzerLexer.ts:422-436,496-527/packages/shader-parser/src/parser/ParsedShaderPass.ts:213-242/packages/shader-compiler/src/ShaderCompiler.ts:151-178/packages/shader-compiler/src/ShaderInstructionEncoder.ts:164-175/packages/core/src/shader/ShaderMacroProcessor.ts:336-360/tests/src/shader-compiler/PreprocessorConditionConformance.test.ts:407-433— 共享表达式 grammar 已落地,但确定性求值错误和 source-defined macro 展开错误仍不进入 compiler 的唯一 admission gate。evaluateContextFreePreprocessorCondition把“依赖宏”和“active divide/modulo by zero”都压成undefined;Lexer 只把 parse failure 放进expressionErrors,而ParsedShaderPass.blockingErrors只机械汇总这份数组。因此顶层#if 1 / 0会被 validated parser 接受、由 precompile 序列化成IfExpr,随后每一次 runtime variant selection 都抛Division by zero。同样,#define VALUE 4294967296配合#if VALUE + 0时,AnalyzerLexer 虽保存了 replacement 的valueError,但只在表达式能投影成 compactPreprocessorCondition时调用_findInvalidMacroValue;带算术的条件投影为undefined,analyzer/precompile 均放行,错误被推迟到 runtime expansion。现有测试只覆盖#if VALUE的 compact 形态,并把 divide-by-zero / oversized expanded literal 直接从 encoder 送到 runtime,因此没有守住公开 precompile 契约。同一根因还使 deferred runtime path 重新维护了一套 lexical owner:共享 tokenizer 把 block comment 当空白,但
_evalDeferredCondition用只接受\s*的defined正则和字符串级 macro scanner。合法的#define JOIN 1 &&、#define FLAG 1、#if JOIN defined/*comment*/(FLAG)会先被正确判为“待展开”,runtime 却把FLAG展开成数字后再让共享 parser 拒绝defined(1)。请保留 design 包 parser/evaluator 为表达式 grammar 与求值结果的唯一 owner、
ParsedShaderPass为 parser/preprocessor facts 的唯一 owner、ShaderCompiler为 backend admission 的唯一 owner:将 context-free 结果改为可区分value / dependency / evaluation error的 typed result,把确定性 active error 和已知 source-macro 展开错误映射进同一 source-mappedblockingErrors;deferred expansion 改为消费同一 token stream/规则,删除AnalyzerLexer._findInvalidMacroValue的 compact-only 校验和 core 的definedregex/string lexical path。仅依赖外部 runtime macro 配置的错误可继续在 variant 边界显式失败,但不能让必败 artifact 通过 precompile。相应测试应从_precompile与 installed/runtime consumer 边界覆盖上述反例,不能再只锁定 direct encoder fallback。 -
[P1]
packages/shader-parser/src/lexer/Lexer.ts:1021-1056/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:36-51/packages/shader-compiler/src/ShaderInstructionEncoder.ts:35-53,68-86,164-175/tests/src/shader-analyzer/PreprocessorExpressionDiagnostics.test.ts:87-93— analyzer 新接受的反斜杠续行条件在 codegen→encoder 边界被拆回物理行,合法 shader 无法编译或预编译。 Lexer 将#if A && \ defined(B)扫成一个逻辑表达式并缓存完整 parse tree,但会把反斜杠替换为空格而保留换行;默认 codegen 原样输出 token lexeme。Encoder 随后只读取到第一个物理行的#if A &&,既无法命中以完整逻辑表达式为 key 的 parser map,又会独立重解析残缺条件并抛错。新增测试只断言ShaderAnalyzer.analyze无诊断,恰好绕过了发生协议断裂的 compiler/encoder 下游。请让 parser-owned logical directive/tree 贯穿到 instruction encoding,删除 encoder 对同一条件的 physical-line acceptance 规则;若仍需从生成文本扫描,至少与 Lexer 共享同一个 logical-line continuation scanner 和稳定 key。补
ShaderCompiler._parseShaderPass、_precompile以及两个 macro 配置的 runtime evaluation 回归,证明 analyzer、compiler admission 和 variant selection 消费同一逻辑条件,而不是为 analyzer-only 测试保留第二条语法协议。 -
[P2]
packages/shader-analyzer/src/ShaderAnalyzer.ts:48-68,89-108,145-166/packages/shader-analyzer/src/index.ts:1-8/tests/src/shader-analyzer/MacroBranchMatrix.test.ts:23-38/tests/src/shader-compiler/StateIsolation.test.ts:89-103/tests/package-consumer/PublishedShaderPackages.test.ts:141-180— “first-party backend generation” handoff 仍没有生产消费者,只有测试在连接 analyzer 与 compiler 的两个 internal method。 仓内生产链仍是 Engine/ShaderCompiler 自己解析 runtime pass;ShaderAnalyzer.analyze也仍只返回 diagnostics。当前新增了AnalyzedShaderPass、ShaderAnalysisUnit、公开包导出的类型和_analyzeWithParsedPasses,再由 synthetic matrix/memory/cache tests 调_generateParsedShaderPass,但没有 first-party composition root 使用这条协议。相较改动前,编译器自己的 request-owned pool 已经解决热路径复用,这条跨包 handoff 只增加第三种 orchestration surface,并让测试成为它存在的唯一理由。请保留
ShaderAnalyzer.analyze为 standalone diagnostics owner、保留 ShaderCompiler 内部的 runtime/validated parser + pool 为 codegen owner;删除AnalyzedShaderPass、ShaderAnalysisUnit、_analyzeWithParsedPasses及其 package exports,将 parsed-pass generation 收回 compiler private implementation,并把 matrix/isolation/package-memory fixture 改为消费真实 analyzer 与 compiler 边界。真实 first-party composition root 落地时再在该层引入一次性 handoff,不要为未来消费者发布当前协议。 -
[P2]
packages/design/src/shader-compiler/PreprocessorExpression.ts:48-58/packages/design/src/shader-compiler/ICondition.ts:53-56/packages/shader-parser/src/ParserObjectPool.ts:30-75/packages/shader-parser/src/parser/SemanticDiagnostics.ts:4-12/packages/shader-parser/src/parser/ShaderTargetParser.ts:54-61,111-117/packages/shader-compiler/rollup.config.js:48-51/packages/shader-parser/src/parser/ShaderTargetParser.ts:170-171/tests/src/shader-compiler/Precompile.test.ts:388— 前轮注释/TSDoc 规范仍未一次性收口,本轮新增 surface 又重复了同类违规。 新的 exported interface/type 与ParserObjectPool五个 public method 仍使用单行 TSDoc,后者还缺参数/返回说明;ShaderTargetParser.create/setSource同样没有完整多行契约。对照本轮已修正的ShaderPosition.set、ShaderRange.set和RuntimePassParser同类 surface,这些写法不一致;BoolCondition的多行说明仍没有句末句号。另有三处新增//以句号结尾。请删除问题 3 所述 dormant surface;其余保留的 class/interface/type/public method 一次性展开多行 TSDoc并补齐@param name - description/@returns,去掉三处单行注释句号,不要为将删除的抽象补文档。
架构、熵增与测试治理
本轮有三处明确减熵:expression grammar/tree/evaluator 从 analyzer、parser、core 多套实现收敛到 design 包;expanded-range 映射从 analyzer + compiler-side formatter 收敛到 parser 的单一纯转换;发布验证从零变为 manifest → pack/install → ESM/CJS/.bin consumer 的真实链路。ParserObjectPool 只复用 compiler-owned 高水位载体,逐 pass reset,AST/diagnostic 不跨请求成为新权威状态;本轮逐类核对条件字段后未发现新的可观察状态泄漏。
但表达式链的 owner 数量尚未真正降到一份:parse tree 之外仍有 AnalyzerLexer 的 compact-only macro-value validator、Encoder 的 physical-line parser,以及 runtime 的 defined regex/string macro expander。于是语法 owner 虽合并,admission、logical-line 转换和 expansion 仍是平行协议,并已分别产生“必败 artifact 被预编译”“analyzer 接受而 compiler 拒绝”“共享 tokenizer 接受而 runtime regex 拒绝”的可观察分歧。按问题 1、2 保留 shared parser/tree + ParsedShaderPass gate、删除三条补偿路径后,owner、转换和校验数量才会净减少。
测试治理方向一正一负:packed consumer test 正确重写了已删除 raw-artifact fixture,没有恢复 chmod/wrapper/legacy fallback;malformed condition 也已从 silent-false 断言改为显式失败。反向上,divide-by-zero/invalid expanded literal 只测试 direct runtime,续行只测试 analyzer,synthetic parsed-pass reuse/memory tests又在维持一个无生产 owner 的协议。应按新的公开 precompile/runtime 契约重写这些 fixture,不能让测试继续驱动第二条 admission、第三条 orchestration 或 compatibility path。
- share neutral parser IR and macro facts across analyzer and compiler - align include resolution, source diagnostics and backend admission - restore pooled hot paths and validate CLI, packages and runtime consumers
- preserve alpha.42 resource identity fixes while retaining shader source metadata - block deterministic macro evaluation failures before runtime variants - align new shader package versions with the alpha.42 workspace
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(P1)。已基于 6dc9d17aefea480b3d70ec7e74b8cbe06cb9ce82...b0e0c9488e37b2baf5aa1a7cab44736c8cab4aea 的 11 个增量 commit、165 个文件完成审查;其中 9 个是同步进来的 dev/2.0 已合入提交,PR 自身的响应范围为 a5d71da 的 104-file shader rewrite 与 b0e0c948 的 merge/admission resolution。已沿 Particle ShaderLibrary #define/#ifdef → AnalyzerLexer/BranchAnalysis → AST global-reference retention → GLESVisitor DCE → ShaderPrecompiler/.shaderc → ShaderMacroProcessor/WebGL、ShaderAnalyzer.analyze → opaque ParsedShaderPass → ShaderCompiler.generate → packed consumer 以及 package build → built-in shader precompile → E2E runtime 各追一层。lint、三平台 build、CodeCov 与 E2E 1/2/4 成功,E2E 3/4 失败。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 b0e0c9488e37b2baf5aa1a7cab44736c8cab4aea。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮的确定性预处理表达式与 source-defined macro admission 问题已关闭:design 包的 typed parser/evaluator 能区分 dependency 与 evaluation error,validated parser 将确定性错误统一纳入 source-mapped blocking gate,runtime expansion 也改为消费共享 token/parser 规则,没有恢复
definedregex、silent-false 或 compact-only validator。 - 反斜杠逻辑续行协议已闭环:Lexer 与 instruction encoder 消费同一逻辑 directive,LF/CRLF、compiler/precompile 与 runtime macro variant 回归均覆盖,不再把 analyzer 接受的条件拆回物理行。
- 原 analyzer→compiler dormant handoff 已改为显式公开契约:
ShaderAnalyzer.analyze()返回 opaqueParsedShaderPass,ShaderCompiler.generate()机械消费同一 parser-owned payload;共享 package identity、README composition 和 pack/install consumer 均有验证,不再暴露可变 IR 或 internal method 对接。 - expanded-range/source-map、发布包 ESM/CJS/.bin consumer、include registry/canonical cache、request-owned parser state、bounded pool,以及上轮 TSDoc/单行注释问题继续保持关闭。
问题
-
[P1]
packages/shader-parser/src/parser/AST.ts:2033-2089/packages/shader-parser/src/parser/AnalyzerSemanticDiagnostics.ts:28-40/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:99-102/packages/shader-compiler/src/codeGen/GLESVisitor.ts:221-239,251-348/packages/shader/src/ShaderLibrary/Particle/Module/VelocityOverLifetime.glsl:4-18/packages/shader/src/ShaderLibrary/Particle/ParticleVert.glsl:115-125,178-191— branch coverage 的unknown被当成“未引用”,离线 DCE 删除了运行时分支仍会使用的全局声明,内置粒子 shader 已无法通过 WebGL 编译。 这里_VOL_LINEAR_MODULE_ENABLED由 runtimeRENDERER_VOL_*宏派生,_VOL_MODULE_ENABLED又由 linear/orbital 两类派生宏定义;因此声明位于_VOL_MODULE_ENABLED && _VOL_LINEAR_MODULE_ENABLED,使用位于_VOL_LINEAR_MODULE_ENABLED,逻辑上前者必然覆盖后者。本轮 symbolic branch proof 对这条嵌套蕴含保守返回unknown,branchAvailability正确地只把uncovered作为诊断;但_lookupAndMarkGlobalReference又用同一个 tri-state 驱动 backend liveness,在coverage !== "covered"时直接返回且不写入referenceGlobalSymbolNames。下游GLESVisitor只从这份引用表生成_referencedGlobalMacroASTs,于是把int renderer_VOLSpace删掉,却保留了 runtime 选中分支里的使用。这不是截图波动:目标 HEAD 的 E2E 3/4 在三次尝试中稳定失败 5 个 particle case;
particleDreamtrace 的生成顶点 shader 在第 118 行使用renderer_VOLSpace,声明列表中没有它,WebGL 明确报'renderer_VOLSpace' : undeclared identifier,视觉差异为 73.5517708333%。上一 HEAD 的四个 E2E shard 均成功。请保留
AnalyzerLexer/BranchAnalysis为 source-macro branch fact/coverage proof 的唯一 owner、GLESVisitor为机械 DCE consumer,将“是否可发诊断”和“是否必须为 codegen 保留”拆开:uncovered继续作为唯一可阻塞事实,unknown则应保守保留所有 overlap 的 global candidate/AST,并在需要时维持TypeAny,删除当前coverage !== "covered" → return false对引用保留的耦合。不要在 particle shader、core 或 runtime 增加renderer_VOLSpace特判、uniform fallback 或 compatibility branch。回归应使用这条真实派生宏链,从ShaderPrecompiler.precompile生成 instructions,再以RENDERER_VOL_*runtime macro 展开并验证 WebGL declaration/use parity;现有BranchAwareLookup.test.ts:374-391只断言 analyzer 无诊断,无法守住 artifact 契约,失败的 E2E snapshot 也不应被更新来掩盖编译错误。
架构、熵增与测试治理
本轮总体有明显减熵:表达式 grammar/evaluator、确定性 admission、logical directive 与 runtime expansion 已收敛到共享契约;runtime compiler 与 offline validated precompiler 分层,公开 parsed-pass handoff 也由 opaque parser payload 和真实 package consumer 约束,没有恢复旧 wrapper、fallback 或镜像 IR。
剩余阻塞点是同一个 branch-coverage tri-state 被 AST 同时解释为“诊断证明”和“后端引用事实”:unknown 在诊断侧表示不能证明错误,在 DCE 侧却被翻译成确定未引用,形成“admission 成功但 artifact 必败”的平行状态。按问题 1 保留 AnalyzerLexer/BranchAnalysis 的唯一证明 owner、删除 AST 的非 covered 即丢弃规则,让 codegen 对未知事实机械保守,owner、状态与转换数量都无需增加。
测试侧,derived-macro fixture 已按新诊断契约重写,pack/install consumer 也正确验证了公开路径;但 analyzer-only 的 “no diagnostic” 与 precompile smoke 都没有执行 runtime-selected built-in variant,因而未发现声明/使用不闭合。应新增真实 precompile→runtime→WebGL 回归并保留当前红色 E2E,不能通过更新基线、生产 fallback 或第二套 macro implication evaluator让旧测试重新变绿。
- Retain guarded globals before analyzer coverage gates type inference. - Add Particle and derived-macro runtime/precompile regressions.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(P1)。已基于 b0e0c9488e37b2baf5aa1a7cab44736c8cab4aea...b64bf174690f6183890691cfba0526302720df99 的 1 个增量 commit、3 个文件完成审查,并沿 Particle/source macro → AnalyzerLexer/BranchAnalysis → SymbolTableStack/AST global-reference retention → CodeGenVisitor/VisitorContext → GLESVisitor DCE → ShaderPrecompiler/ShaderMacroProcessor/WebGL 向上游和下游各追一层。上一轮失败的 E2E 3/4 已恢复,GitHub lint、三平台 build、四组 E2E、coverage 均成功;另在目标源码快照完成了全量 build,并对条件遮蔽反例执行了 analyzer→generate→runtime artifact 复现。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 b64bf174690f6183890691cfba0526302720df99。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮的 Particle 派生宏保活问题已关闭:
AST._lookupAndMarkGlobalReference现在会在 coverage/type gate 前记录候选全局,unknown不再把真实全局误当成确定未引用;新增 Particle constant/curve 两个 precompile→runtime→WebGL 回归,原先失败的 E2E shard 也已成功。 - 修复仍位于 parser-owned branch/liveness 链路,没有在 Particle shader、core 或 runtime 增加 uniform 特判、fallback、compatibility branch 或第二套 implication evaluator。
- 共享表达式 parser/evaluator、确定性 admission、逻辑续行、opaque parsed-pass handoff、expanded-range/source-map、pack/install consumer、include canonical cache、request-owned parser state 与有界 visitor pool等此前已关闭项继续保持关闭。
问题
-
[P1]
packages/shader-parser/src/common/SymbolTableStack.ts:85-93/packages/shader-parser/src/parser/AST.ts:1934-1974,2033-2090/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:99-102/packages/shader-compiler/src/codeGen/VisitorContext.ts:209-218/packages/shader-compiler/src/codeGen/GLESVisitor.ts:221-265— 条件局部变量会截断外层全局的保活候选,合法 runtime 宏变体仍会生成“使用存在、全局声明被 DCE”的 shader。 本提交把全局记录提前到了 coverage gate 之前,但只能记录lookupAll返回的最近作用域候选;SymbolTableStack.lookupAll一旦最近作用域找到同名 symbol 就无条件break,因此看不到“该条件局部声明在当前宏变体中不存在时”应回退到的外层全局。已在目标 HEAD 用公开链路复现:声明全局
float branchValue;,在#if defined(A) && VALUE * VALUE > 1内声明同名局部变量,再在#ifdef A中读取branchValue。Analyzer 对该 symbolic coverage 合理返回零诊断;但当A启用且VALUE=0时,预处理后的局部声明不存在,引用应解析到全局。当前ShaderCompiler.generate产物却完整删除了全局声明,同时保留gl_FragColor = vec4(branchValue),runtime instruction 也没有全局声明,最终 WebGL 得到 undeclared identifier。新回归只有一个全局候选,没有覆盖条件局部遮蔽,所以无法发现这条变体。请保留
AnalyzerLexer/BranchAnalysis为宏分支事实的唯一 owner、SymbolTableStack为词法解析的唯一 owner,让同一解析结果同时携带“最近作用域语义候选”和“最近候选未证明覆盖 callsite 时的外层保活 fallback”;删除保活路径中out.length > 0 → break的无条件截断,以及 AST 通过currentScopeSymbol/symbols.some对截断结果二次猜测 global ownership 的逻辑。下游CodeGenVisitor/VisitorContext/GLESVisitor继续只机械消费 parser 记录的全局 identity/name。不要在 AST 或 backend 再做第二次 symbol-table 扫描,也不要改成无条件保留全部全局、增加 compatibility branch 或另一套 branch solver。回归应从ShaderAnalyzer.analyze → ShaderCompiler.generate → ShaderMacroProcessor → WebGL覆盖A + VALUE=0/2两个变体,并让 live compiler 路径消费同一 fixture,验证 declaration/use parity。 -
[P2]
packages/shader-parser/src/parser/AST.ts:2077-2078— 本提交新增的两行//注释以句号结尾,不符合仓库单行注释不加句号的约定。 请移除末尾句号;无需改成额外文档抽象。
架构、熵增与测试治理
本轮对上一阻塞项是明确减熵:诊断 coverage 与 backend liveness 的语义已开始解耦,真实 Particle A/B 回归替代了 analyzer-only 断言,且没有把复杂度转移到 shader/runtime fallback。应继续保留 BranchAnalysis → SymbolTableStack → AST referenceGlobalSymbolNames → GLESVisitor 这条单向事实链。
剩余 P1 暴露的不是第三套宏规则,而是同一个 lookup 结果同时被解释为“最近词法声明”与“所有可能 runtime owner”:最近作用域的无条件 early-stop 对前者正确,对条件编译后的保活集合不完整,AST 再从这份截断集合推断 global 便形成漏失。按问题 1 让唯一的 SymbolTableStack resolution 显式产出保守 fallback、删除 AST 的 ownership 猜测后,不需要新增持久状态、镜像引用表或 backend 校验路径,owner 与转换数量均可保持不变。
测试侧,新增 Particle precompile/runtime/WebGL 用例正确关闭了真实故障,也不应更新 snapshot 来掩盖编译错误;但它只覆盖“全局候选自身受 guard 约束”,没有覆盖“内层条件声明遮蔽外层全局”的同根因矩阵。请按新的公开 artifact 契约补足该 fixture,不能为了既有测试在生产代码保留 nearest-scope fallback、全局全保留开关或第二套分支求值。
- Separate nearest-scope semantic candidates from runtime liveness fallbacks. - Cover analyzer, live compiler, offline precompiler, and WebGL macro variants.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(P1)。已基于 b64bf174690f6183890691cfba0526302720df99...10a523eba454bca492b841733a2e2a357b8f3ea1 的 1 个增量 commit、5 个文件完成审查,并沿 AnalyzerLexer/BranchIdentity → SymbolTable/SymbolTableStack → AST global liveness 与 resolvedValueSymbols → ShaderCoreInfo/VisitorContext → CodeGenVisitor member projection → ShaderMacroProcessor/WebGL 向上游和下游各追一层。GitHub lint、三平台 build、四组 E2E 与 coverage 均成功,目标源码快照的全量 build/type build 也成功;但 live runtime compiler 的结构体分支遮蔽反例仍能稳定生成缺失 varying 且引用 v.uv 的非法 artifact。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 10a523eba454bca492b841733a2e2a357b8f3ea1。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮以
defined(A) && VALUE * VALUE > 1条件局部变量遮蔽外层同名标量、导致 global declaration 被 DCE 的反例已关闭:新的lookupAllWithRuntimeFallbacks会保留外层 global name,新增 live/offline/analyzer-handoff 与VALUE=0/2回归覆盖了原始故障。 - 上轮指出的
AST.ts两处单行注释句尾句号已移除。 - Particle 派生宏下
unknown的保守全局保活,以及共享表达式 parser/evaluator、确定性 admission、逻辑续行、opaque parsed-pass handoff、expanded-range/source-map、pack/install consumer、include canonical cache、request-owned parser state 与有界 visitor pool等此前已关闭项继续保持关闭;本提交没有恢复 shader/runtime 特判、legacy fallback 或第二套 branch solver。
问题
-
[P1]
packages/shader-parser/src/common/SymbolTable.ts:123-140/packages/shader-parser/src/common/SymbolTableStack.ts:107-147/packages/shader-parser/src/parser/AST.ts:1935-1954/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:54-86/packages/shader-compiler/src/codeGen/VisitorContext.ts:149-163/tests/src/shader-compiler/MacroBranchRuntime.test.ts:546-603— 互斥分支中的局部声明仍留在 runtime lookup 的精确符号结果里,使同一引用的“全局保活名称”与“结构体 IO owner identity”分裂。 Runtime parser 没有 analyzer 的branchSemantics,而_getSymbols(..., includeMacro=true)会先把同一词法作用域中、位于另一个#if/#elsearm 的局部 symbol 放进结果。新 lookup 随后虽然通过isLexicalBranchVisibleFrom发现它不覆盖 callsite,并把外层 global 加入runtimeFallbacks,但仍将该互斥局部保留在主out中。VariableIdentifier因而按 fallback name 保活外层 global,却把_symbols固定为局部 identity;下游getStructVarRole和 member projection 只消费精确 identity,于是两个事实给出相反 owner。已在目标 HEAD 用实际 analyzer/live/offline 链路复现:声明全局
Varyings v;,在#ifdef LOCAL_SHADOWarm 声明局部float v,在同组#elsearm 读取v.uv。两个宏变体都合法,Analyzer 返回零诊断;analyzer handoff 与 offline precompiler 均正确生成varying vec2 uv并把成员访问投影为uv。但 live runtime_parseShaderPass在 GLSLES100 和 GLSLES300 都生成v.uv,同时没有全局v或对应 varying;LOCAL_SHADOW未定义时 artifact 因 undeclared identifier 必然无法通过 WebGL。现有新增回归的内外声明都是float branchValue,只验证 name-based declaration/use parity,无法观察 exact symbol identity 与 struct-role projection,因此 checks 全绿没有覆盖这条协议断裂。请保留
AnalyzerLexer/BranchAnalysis为 analyzer branch proof 的唯一 owner、BranchIdentity为 runtime 可证明词法关系的唯一 owner、SymbolTable/SymbolTableStack为词法解析的唯一 owner:在选择最近语义作用域前复用现有getLexicalDeclarationCoexistence(或收敛为同一个词法 overlap predicate)过滤已证明exclusive的候选,让主 lookup 直接返回当前 runtime 变体可能拥有引用的精确 symbol;再让 global liveness name 与resolvedValueSymbols()从同一结果机械派生。具体应删除“互斥局部仍留在out、外层 global 只作为 name fallback”的分裂路径,而不是在 AST 再扫一次 symbol table、在 backend 按名称猜 struct role、保留 compatibility branch,或引入第二套宏求解器。回归应使用外层 IO struct 与同一#if/#else组中的局部遮蔽,覆盖 analyzer handoff、live compiler、offline precompile、两个宏 arm 和 WebGL declaration/member parity;不要更新 snapshot 或增加生产 fallback 来迁就现有标量 fixture。
架构、熵增与测试治理
本轮对上一反例有明确减熵:AST 删除了 currentScopeSymbol/symbols.some 的 ownership 二次猜测,runtime fallback 被收回 parser-owned SymbolTableStack,scratch arrays 按 lookup 清空,没有新增持久状态、shader 特判或 compatibility path;标量端到端回归也替代了 analyzer-only 断言。
剩余 P1 表明这一收口尚未贯穿 identity consumer:同一次引用现在同时拥有 referenceGlobalSymbolNames 中的外层 global name 与 _symbols 中已被当前 arm 排除的局部 identity,下游 DCE/保活与 IO flattening 因而形成两份相互冲突的 owner 事实。按问题 1 在 SymbolTable/SymbolTableStack 删除 exclusive candidate、让 name 与 identity 机械派生自同一解析结果后,helper、状态和转换数量都无需再增加,也不会把复杂度转移到 AST、backend 或 runtime variant processor。
测试侧,新增 complex-condition 标量矩阵正确关闭了上一轮故障,既有 Particle/WebGL 和 package consumer 回归也无需恢复旧 fixture;但同型标量遮蔽天然掩盖 identity 漂移。应按当前公开 artifact 契约增加结构体 IO 遮蔽矩阵并保留 live/offline parity,不能为了已有测试在生产代码保留 name-only fallback、backend wrapper、镜像 role 状态或第二条转换/校验路径。
- Filter proven-exclusive candidates before nearest-scope selection. - Cover struct IO shadowing across all three pipelines and GLES targets. - Compile and link the generated programs in WebGL and WebGL2.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(P1)。已基于 10a523eba454bca492b841733a2e2a357b8f3ea1...8800df863abe22290a26801038f60c8c2a3cb7ef 的 1 个增量 commit、2 个文件完成审查,并沿 Lexer/AnalyzerLexer branch facts → BranchIdentity/BranchAnalysis → SymbolTable/SymbolTableStack → AST exact symbol 与 global liveness → VisitorContext/CodeGenVisitor member projection → ShaderMacroProcessor/WebGL 向上游和下游各追一层。目标源码快照的全量 build/type build 成功,GitHub lint、三平台 build、四组 E2E 与 coverage 均成功;新增的同组 #if/#else 结构体回归关闭了上一轮 P1,但本提交把跨独立条件组的同宏名相反 defined 也当成绝对互斥,遇到 #undef 后 live compiler 会静默读取错误的 owner。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 8800df863abe22290a26801038f60c8c2a3cb7ef。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮同一
#if/#else组中局部声明残留在精确 symbol 结果、导致 global liveness 与结构体 IO owner identity 分裂的问题已关闭:SymbolTable现在会在最近作用域选择前过滤同组互斥 arm;新增回归覆盖两个镜像 arm、analyzer handoff/live/offline、GLSLES100/300 与 WebGL/WebGL2 link。 - 更早的复杂条件局部遮蔽外层全局保活、Particle 派生宏下
unknown保守保活,以及两处单行注释句尾问题继续保持关闭;本提交没有恢复 AST/backend 的 name-based owner 猜测、shader 特判或 compatibility fallback。 - 共享表达式 grammar/admission、逻辑续行、opaque parsed-pass handoff、source-map、pack/install consumer、include canonical cache、request-owned parser state 与有界 visitor pool等此前已关闭项未被本增量改动触碰。
问题
-
[P1]
packages/shader-parser/src/common/SymbolTable.ts:133-143/packages/shader-parser/src/common/BranchIdentity.ts:54-87/packages/shader-parser/src/lexer/Lexer.ts:136-233/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:54-86/tests/src/shader-compiler/MacroBranchRuntime.test.ts:627-705— runtime candidate filter 把跨独立条件组的同名相反 guard 当成绝对互斥,#undef后会选择错误的精确符号并静默改写结构体成员访问。 本提交在没有 analyzerbranchSemantics的 live 路径改用getLexicalDeclarationCoexistence过滤候选;该 helper 不仅证明同一conditionalGroup的不同 arm 互斥,还会仅凭left.name === right.name && left.defined !== right.defined将不同条件组判为exclusive。然而 runtimeLexer的 branch signature 没有携带宏 mutation/version,两个 guard 之间的#define/#undef可以让它们顺序同时成立,因此这里没有可删除局部 symbol 的事实依据。已在目标 HEAD 用公开 analyzer/live/offline 链路复现:声明外层 IO
Varyings v;,在源码先#define LOCAL_SHADOW,于#ifdef LOCAL_SHADOW中声明并初始化同名LocalVaryings v,随后#undef LOCAL_SHADOW,再在独立的#ifndef LOCAL_SHADOW中读取v.uv。预处理后局部声明与读取确定共存,引用必须归属局部v。Analyzer 返回零诊断,analyzer handoff 与 offline precompiler 在 GLSLES100/300 都正确生成v.uv;上一 HEAD10a523e的 live compiler 也一致。目标 HEAD 的 live compiler 却把精确 identity 解析为外层 global,CodeGenVisitor随即将访问投影成 varyinguv:局部值0.25被静默替换为顶点 varying0.5。这个错误 artifact 在 WebGL/WebGL2 仍能 compile/link,因此当前新增用例只覆盖同组 arm 并断言链接成功,无法观察该语义漂移。请保留
AnalyzerLexer/BranchAnalysis为含#define/#undef状态的唯一 mutation-aware owner、Lexer/BranchIdentity为 runtime 词法 group/arm identity 的唯一 owner、SymbolTable/SymbolTableStack为解析 owner:删除“不同条件组仅凭同宏名且defined相反即可判定 exclusive”的无版本证明,让该关系在没有 mutation-aware fact 时保持unknown;同一conditionalGroup的不同 arm 仍可机械判定互斥。不要在 backend 按名称补猜 role、复制一套 runtime macro-state solver、增加 compatibility branch 或镜像 owner 状态。回归应在当前三条 pipeline/两个 GLES target 矩阵中加入上述define → local struct → undef → opposite guard use,至少断言 live 与 analyzer/offline 都保留v.uv(或验证像素为局部值),而不是仅检查可链接性。
架构、熵增与测试治理
本轮对上一 P1 的直接路径有明确减熵:精确 symbol 与 global liveness 再次从同一次 parser lookup 机械派生,结构体 role consumer 无需新增 fallback;三条 pipeline、两个 target 和真实 WebGL link 的回归也替代了只看 analyzer 结果的窄断言。
但将 getLexicalDeclarationCoexistence 的跨组同名 heuristic 接入 _getSymbols 后,runtime 词法 identity 实际承担了它并不拥有的宏 mutation 事实,与 AnalyzerLexer/BranchAnalysis 形成两份结论:同一源码在 analyzer/offline 选择局部 owner,live compiler 选择 global owner。相较上一 HEAD,状态字段没有增加,但一个原本保守的未知关系被新增转换成确定 exclusivity,并把复杂度/错误转移到下游 member projection。按问题 1 删除这条无版本 cross-group proof、只消费同组 arm 或 mutation-aware owner 给出的事实后,owner、状态机和转换数量都无需增加。
测试侧,新增 fixture 正确关闭了同组互斥 arm 的旧故障,且不应删除;不过 compile/link 只能守住结构合法性,无法发现仍合法但读取错误 varying 的 artifact。应按新公开语义重写/扩展 fixture,验证精确 member projection 或渲染值;不能为了现有 link-only 测试在生产代码保留 name heuristic、backend wrapper、第二套宏状态或 compatibility fallback。
- Move proven semantic validation into the parser-owned offline pass. Keep runtime bundles free of diagnostic branches. - Keep runtime branch ownership conservative across macro mutation. Align exact symbol identities with global liveness. - Add analyzer/live/offline, package-consumer, benchmark, and macro regressions.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(3 个 P1,另有 1 个 P2)。已基于 8800df863abe22290a26801038f60c8c2a3cb7ef...176633caaeceaf9bd1e19d1a1f2f3821a96b8d3f 的 1 个增量 commit、12 个文件完成审查,并沿 branch identity / macro mutation → SymbolTableStack → AST exact candidates → ShaderCoreInfo / VisitorContext → CodeGenVisitor member projection → runtime variant,以及 parser semantic facts → ParsedShaderPass.blockingErrors → Analyzer opaque handle / offline precompile → ShaderCompiler.generate 各向上游和下游追了一层。上一轮跨独立条件组误判互斥的问题已关闭;目标源码快照的全量 build/type build 成功,GitHub lint、三平台 build、四组 E2E 与 coverage 均成功,但公开 API 复现仍分别产生“Analyzer 报错而 handoff 生成”、++/-- 必败源码被离线接受,以及宏变体下引用未声明 v 的 artifact。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 176633caaeceaf9bd1e19d1a1f2f3821a96b8d3f。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮“跨独立条件组仅凭同名宏且
defined相反就判定 exclusive”的 P1 已关闭:getLexicalDeclarationCoexistence现在只把同一conditionalGroup的不同 arm 判为互斥;新增define → local struct → undef → opposite guard use回归覆盖 analyzer handoff、live compiler、offline precompiler、GLSLES100/300、生成文本与 WebGL。 - 更早的同组
#if/#else精确 symbol identity、复杂条件局部遮蔽下的外层全局保活、Particle 派生宏unknown的保守保活,以及相关单行注释问题继续保持关闭;本提交没有恢复 backend 按名称猜 owner 或 runtime 宏求解器。 - 共享表达式 grammar/admission、逻辑续行、opaque parsed-pass 公共契约、source-map、pack/install consumer、include canonical cache、request-owned parser state 与有界 visitor pool等此前已关闭项未被本增量恢复为 legacy/compat 路径。
问题
-
[P1]
packages/shader-parser/src/parser/ValidatedPassParser.ts:68-88/packages/shader-analyzer/src/ShaderAnalyzer.ts:132-151/packages/shader-compiler/src/ShaderCompilation.ts:44-57/packages/shader-compiler/src/ShaderCompiler.ts:88-90/tests/src/shader-compiler/ShaderNeutralIR.test.ts:306-320— 新增 parser semantic gate 只接入 offline wrapper,Analyzer 返回的 opaque pass 仍绕过同一 admission。parseValidatedShaderPass会把ParserSemanticValidation.collect()的结果追加到blockingErrors,但 Analyzer 走parseShaderPass,并在ShaderValidator.validate()之前就用原始 parsed data 创建 handle;后者产生的 diagnostics 不会回写 handle。ShaderCompiler.generate()又只机械检查 handle 中原有的blockingErrors。因此同一份void frag() { 1 = 2; ... }在目标 HEAD 的公开链路得到三种结论:ShaderAnalyzer.analyze()返回InvalidAssignmentTarget,new ShaderCompiler().generate(result.passes[0], ...)却成功生成含1 = 2的 fragment instruction,ShaderPrecompiler.precompile()则拒绝。当前矩阵只断言 analyzer diagnostic 与 offline throw,恰好没有消费刚公开的 handoff。请保留
ParsedShaderPassData.blockingErrors为唯一 backend-admission fact、ParserSemanticValidation为这些共享规则的唯一 owner:在创建 opaque handle 前的共同 parsed-pass 路径收集并附着 parser 可证明的 semantic issues,让 Analyzer 只把同一错误机械映射为 diagnostics、compiler 只消费同一 blocking fact;随后删除ShaderValidator对这些共享规则的平行转发,以及 offline wrapper 中的第二次附加路径。不要要求调用方先检查AnalysisResult.diagnostics再决定能否 generate,也不要增加 wrapper、fallback 或第二套 gate。回归应让每个新增 semantic case 同时经过Analyzer → pass → ShaderCompiler.generate与 offline precompile,并断言二者给出同一拒绝结论。 -
[P1]
packages/shader-parser/src/parser/ParserSemanticValidation.ts:27-57,60-140/packages/shader-analyzer/src/ShaderValidator.ts:358-383,548-564/packages/shader-parser/src/parser/ValidatedPassParser.ts:68-88/tests/src/shader-compiler/ShaderNeutralIR.test.ts:306-320— 共享 l-value helper 已支持 increment target,但唯一 offline collector 没有分派 prefix/postfix++/--,确定非法的 shader 仍被预编译。collect()只访问 assignment、condition、constructor 与 return 节点,没有为UnaryExpression/PostfixExpression产生 issue;Analyzer 的两个独立 visitor 分支却直接调用同一个nonAssignableReason()。目标 HEAD 实测const float locked = 1.0; ++locked;和locked++;都得到InvalidAssignmentTargetdiagnostic,但 offline precompile 成功,后者产物仍含locked ++。这说明规则 body 虽已共享,规则覆盖/dispatch 仍有两份 owner;现有四项 offline matrix 只覆盖1 = 2,未覆盖自增自减。请让
ParserSemanticValidation.collect成为共享规则的唯一 dispatch owner,在其中覆盖 prefix/postfix increment(可抽成同一个 increment issue),再删除 Analyzer_checkUnaryOperand/_checkPostfix中等价的 l-value 分支,仅保留 analyzer-only 的 operand、swizzle 与 index 检查。回归至少加入 prefix/postfix const,并从问题 1 所述 handoff/offline 两条公开边界验证;不能用 analyzer-only 校验或 runtime driver failure替代 admission。 -
[P1]
packages/shader-parser/src/parser/AST.ts:2028-2046/packages/shader-compiler/src/codeGen/VisitorContext.ts:149-163/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:54-86/tests/src/shader-compiler/MacroBranchRuntime.test.ts:759-811— 将条件局部与外层 fallback 合并成无配置关联的 symbol union 后,IO owner 与普通局部 owner 的分歧被翻译成普通成员访问,合法宏变体生成未声明的v.uv。 新代码把所有runtimeFallbacks直接追加进VariableIdentifier._symbols;但这只保存了候选集合,没有保存“哪个宏配置选择哪个 owner”。getStructVarRole遇到一个无 IO role 的局部候选便返回undefined,CodeGenVisitor又把undefined当成“普通 struct”,无条件输出v.uv。我把新增 fixture 的外层普通GlobalValue v换成真实 vertex-outputVaryings v后,在 analyzer handoff、live compiler、offline precompiler及 GLSLES100/300 六个组合中都得到同一结果:LOCAL_SHADOW=false时产物声明了扁平 varyinguv、删除了全局v,却仍读取v.uv;LOCAL_SHADOW=true时该文本才正确归属局部。前者是确定的 undeclared identifier。当前 fixture 的外层与局部都是普通 struct,因此v.uv在两个配置里恰好都合法,WebGL link 也无法观察 owner 漂移。请保留
SymbolTableStack.lookupAllWithRuntimeFallbacks为精确候选的唯一 owner、ShaderCoreInfo的 exact-symbol role map 为唯一 IO-role owner,删除下游“候选 role 不一致 →undefined→ 普通成员访问”的有损转换。候选若跨配置混合 IO/non-IO owner,应将配置关联贯穿到 instruction lowering;若本 PR 无法安全表达,最小正确边界是以明确的 branch-resolution ambiguity 阻断,而不是静默选中一种语法。不要在 backend 按名称猜 role、复制宏版本求解器、保留v/uv镜像声明或增加 compatibility branch。请把第二个回归改成真实 IO struct,覆盖初始宏 true/false、三条 pipeline 与两个 GLES target,并断言各配置生成正确的uv/v.uv,或一致地显式拒绝。 -
[P2]
packages/shader-parser/src/parser/ParserSemanticValidation.ts:9,19— 新增导出的 interface/class 使用了包含@internal的单行 TSDoc,与仓库 exported surface 的多行 TSDoc 规范不一致。 请按相邻 parser 公共/内部导出契约改为多行说明并将@internal独立成 tag;无需新增文档抽象。
架构、熵增与测试治理
本轮有两处明确减熵:删除跨独立条件组的无版本 exclusivity heuristic 后,runtime lexical identity 重新只声明自身能证明的事实;semantic rule body 从 Analyzer 大段实现收敛到 parser,构建产物也继续保持 runtime bundle 不含 analyzer-only validation。上一轮反例的新回归是有效替换,不应删除。
但 admission 的净变化是从同一 parsed-pass gate重新分裂为 offline collect → blockingErrors 与 Analyzer validator → diagnostics-only handle 两条协议;同一 AST 因而出现 error / generate / reject 三态。规则实现虽减少,walker/dispatch 仍有两份并已漏掉 ++/--。符号侧新增候选没有新增持久宏状态,却把配置相关 ownership 压平成集合,再由下游把“无 role”和“role 冲突”都编码为 undefined,把复杂度转移成错误 artifact。按问题 1、2 让 common parsed pass 产生唯一 semantic blocking facts、按问题 3 删除有损 role fallback 后,owner、转换和校验数量才会净减少。
测试侧,第一组 source-defined macro fixture正确关闭了上一轮 P1;第二组 ordinary-struct fixture却无法观察 IO projection,offline semantic matrix也没有消费 opaque handoff且漏掉 increment。应按新的公开契约重写这些 fixture,直接验证 admission parity与配置相关 artifact;不得为了现有绿测在生产代码保留 analyzer-only gate、legacy fallback、wrapper、镜像状态或第二条转换/校验路径。
- Make parser-owned semantic failures the shared admission fact for analyzer and codegen pipelines. - Preserve exact runtime owner candidates and reject unlowerable stage-IO ambiguity. - Cover increment validation and direct or expression-macro ownership across GLES targets.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(2 个 P1)。已基于 176633caaeceaf9bd1e19d1a1f2f3821a96b8d3f...8b8ea345e426aed2c9f1402280302b013b1299b4 的 1 个增量 commit、13 个文件完成审查,并沿 parser semantic facts → ParsedShaderPass.blockingErrors → Analyzer opaque pass / offline precompile → ShaderCompiler.generate,以及 macro branch/callsite identity → VariableIdentifier candidates/runtimeFallbackReferences → ShaderCoreInfo owner facts → ShaderIOValidator/ShaderCompilation → CodeGenVisitor → runtime variant 各向上游和下游追了一层。目标源码快照的全量 build 成功,新增两组聚焦测试共 55/55 通过,GitHub 12 项 checks 全部成功;但公开 API 反例仍分别生成 undeclared v.uv 和把局部 v.uv 静默改写成 varying uv,且新增的 unknown-owner gate 会让 Analyzer/CLI 报 clean、同一 opaque pass 却必然无法生成。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 8b8ea345e426aed2c9f1402280302b013b1299b4。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮“parser semantic blocking fact 只接入 offline wrapper、Analyzer opaque pass 可绕过”的 P1 已关闭:
parseShaderPassWith现在在创建 handle 前统一收集并 source-mapParserSemanticValidationissues,Analyzer handoff 与 offline precompile 机械消费同一blockingErrors;ValidatedPassParser与ShaderValidator中的重复附加/转发路径已删除。 - 上轮 prefix/postfix
++/--未进入共享 collector 的 P1 已关闭:ParserSemanticValidation.collect已统一分派两类 increment,Analyzer 的平行 l-value dispatch 已删除;新增 public matrix 同时覆盖 handoff/offline、GLSLES100/300、非法 const target 与合法可修改 target。 - 上轮
ParserSemanticIssue/ParserSemanticValidation单行 TSDoc 的 P2 已关闭,保留 surface 已改为多行契约并将@internal独立成 tag。 - 更早的跨独立条件组 exclusivity、同组 exact identity、复杂条件 global fallback、Particle 派生宏保活、共享预处理表达式/逻辑续行、opaque handoff、source-map 与 pack/install consumer 等已关闭项继续保持关闭;本提交没有恢复 backend name guessing、runtime proof solver、legacy wrapper 或 compatibility fallback。
问题
-
[P1]
packages/shader-parser/src/ir/ShaderCoreInfo.ts:148-155,161-236/packages/shader-parser/src/parser/AST.ts:1958-1969,2030-2065,2114-2120,2436-2467/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:54-86,199-215/tests/src/shader-compiler/MacroBranchRuntime.test.ts:926-994— 新 owner gate 只枚举 outer-scope fallback 且只识别调用点外层的成员语法,仍会漏掉同级候选与 replacement 内部成员,三条公开 pipeline 都能生成错误 GLES。collectStructMemberOwnerFacts的输入只有runtimeFallbackReferences;而_registerRuntimeFallbackReference在没有追加外层 fallback 时直接返回,所以同一词法 scope 的#ifdef USE_IO { Varyings v; } #else { Ordinary v; }两个 exact candidate 从不进入 gate。随后getStructVarRole面对 IO/ordinary 混合 owner 返回无 role,codegen 保留v.uv,却删除 IO arm 的全局v。目标 HEAD 实测 Analyzer 零诊断,analyzer handoff、live compiler、offline precompiler 在 GLSLES100/300 均成功;USE_IO=true的 fragment artifact 引用 undeclaredv.uv。第二个同根因反例是把现有 fixture 的
#define OWNER v/OWNER.uv改为合法的#define OWNER_UV v.uv/OWNER_UV:replacement 内的v虽被_collectIdentifierRefs关联到 callsite branch,但原始VariableIdentifier位于MacroDefine,不在isReachableStructMemberReference要求的 reachable function 下,因此也绕过 gate。六个 pipeline/target 组合都生成#define OWNER_UV uv;当 callsite 的 conditional localv存在时,正确值应为局部v.uv = 0.25,产物却静默读取 outer varyinguv = 0.5,即使 WebGL compile/link 成功也已改变语义。请保留
VariableIdentifier.resolvedValueSymbols()与 macro callsite branch association 为 exact candidate 的唯一 owner、ShaderCoreInfo.structVariableRoles为 IO role 的唯一 owner,删除“只有 outer fallback side table + caller-side parent shape 才参与 gate”的不完整事实边界:从每个 reachable member projection 机械派生 owner issue,覆盖同级 candidate union、outer fallback,以及 replacement AST 在各 callsite 下的投影。若某种配置关联仍无法安全降低,应进入同一个明确 blocking fact;不要在 backend 按名称猜 role、补镜像声明或增加 compatibility branch。回归应加入上述两个 fixture,覆盖 analyzer handoff/live/offline、两个 GLES target 与宏 true/false,并断言正确的uv/v.uv,或三条路径一致地显式拒绝。 -
[P1]
packages/shader-parser/src/ir/ShaderCoreInfo.ts:88-95,154-155,161-215/packages/shader-analyzer/src/ShaderIOValidator.ts:93-101/packages/shader-compiler/src/ShaderCompilation.ts:96-99/packages/shader-analyzer/src/cli.ts:58-73/tests/src/shader-compiler/MacroBranchRuntime.test.ts:762-800— 同一个 owner issue 被持久化成ambiguous与unlowerable两份真相,unknown分支因此让 Analyzer/CLI 报 clean、compiler 却确定拒绝。 Collector 对 IO/ordinary 混合 owner 总是写unlowerableLocations,仅在 coverage 为uncovered时另写ambiguousLocations;Analyzer 只消费后者,compiler 只消费前者。新增测试明确锁定analysis.diagnostics为空,同时要求 handoff/live/offline 全部失败。公开 CLI 只依据 diagnostics 决定 exit code,所以该必败 pass 会返回 0;而 README 展示的analyze → passes → generate组合又没有任何结构化原因可供调用方展示。这不是保守 proof 本身的问题,而是同一确定的 backend-admission 结果在上下游被投影成 clean/fail 两种协议。请保留一份带 location(若需要再带 proof classification)的 canonical owner issue 作为唯一 backend-admission fact,删除
ambiguousStructMemberOwnerLocations/unlowerableStructMemberOwnerLocations的平行 subset/superset 状态;Analyzer 应把每个会让generate必然失败的 issue 机械映射为明确的 backend-unlowerable error,compiler 消费同一列表。不要要求调用方先试generate、解析 Logger 文本或复制第二套 gate。相应用例应改为同时断言结构化 diagnostic 与三条生成路径的一致拒绝,而不是让测试要求生产代码保留“analysis silent / codegen blocking”的双协议。
架构、熵增与测试治理
语义 admission 侧是明确减熵:共享规则的 collection、dispatch 与 blocking fact 已收敛到 parser common path,Analyzer 只映射同一错误,compiler 只 admission;prefix/postfix 与其他 semantic cases 的公开矩阵正确替换了原来的 analyzer-only/offline-only断言,runtime lean parser 也没有被迫携带 authoring validator。
宏 owner 侧则从 per-reference exact candidates 新增了 runtimeFallbackReferences side table,再派生 ambiguous 与 unlowerable 两个持久列表;相较上一 HEAD,状态与转换净增三层,却仍漏掉同级 owner 和 replacement-body projection,并让 Analyzer 与 compiler 各消费一份不同结论。按问题 1 从全部 reachable projection 机械派生、按问题 2只保留一份 canonical issue 后,candidate、role、admission 各回到一个 owner,无需第三套宏求解器或 backend fallback。
测试侧,semantic matrix 应完整保留;新增 direct v.uv、OWNER.uv 和 GLSL/WebGL 矩阵有效覆盖了已接入路径,但没有覆盖 same-scope union 与 #define OWNER_UV v.uv,且 unknown 用例正把新的 clean/fail 分裂固化成契约。应按公开 artifact/admission 契约补写或重写这些 fixture;不得为了现有绿测保留 partial side table、silent gate、name-based wrapper、镜像状态或第二条转换/校验路径。
- Derive one canonical owner issue from direct and macro-replacement projections. - Keep unknown macro relations silent in Analyzer while blocking unsafe Codegen output. - Cover same-scope and replacement-member regressions across all compiler paths.
|
P1 问题:已修复 owner gate 只覆盖 runtime fallback 的缺口。Parser 现在同时收集直接 member projection 和宏替换 AST 在调用点的解析快照,Core 统一收敛为 预期:Analyzer 和 Codegen 共享同一事实,但保留不同策略:能证明冲突的 Analyzer 不持有 backend codegen 状态,也无法穷举任意宏表达式、运行时宏组合和 backend-specific lowering 失败,因此无法保证提前检测所有 Codegen failure。若你认为这个边界不可行,请给出一个同时满足“Analyzer 不依赖 backend、unknown 不误报、覆盖全部 Codegen failure”的具体方案或接口契约,我再按该方案分析可行性。 |
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(2 个 P1,另有 1 个 P2)。远端 HEAD 自上一份 review 后没有代码变化,仍为 2b1fb1a;本轮会话增量只有作者于 2026-08-27T05:06:09Z 对 unknown owner policy 的回复。已重新核对 8b8ea34...2b1fb1a 的 1 个 commit、6 个文件,并沿 macro definition / formal-actual binding / branch identity / exact symbols → reference snapshots / replacement projection → ShaderCoreInfo owner issue → Analyzer policy / compiler admission → CodeGenVisitor / runtime expansion 向上游和下游各追一层。作者对 unknown 分层的解释成立并已关闭;但函数式与嵌套 replacement 的漏检、以及跨独立宏定义的 path-level 合并仍在同一源码快照中。上一轮针对该不可变 HEAD 的全量 build 和公开 API 正反例探针证据继续有效,当前 GitHub 12 项 checks 也全部成功。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 2b1fb1a。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮“unknown 在 Analyzer 中 silent、在 Codegen 中 blocking 即两套 admission”的 P1 已按作者解释关闭。AnalysisResult 的公开契约只承诺结构化诊断与可复用 pass,ShaderAnalyzer 明确不生成 backend source,ShaderCompiler.generate 也显式允许返回 undefined;因此同一 structMemberOwnerIssues 事实由 Analyzer 只诊断 definite、由 emitter 对 unknown 保守拒绝,是职责不同的 policy projection,不要求 Analyzer 覆盖全部 backend failure。无需让 CLI 为 unknown 返回非零,也无需新增 backend diagnostic mirror。
- 同一词法 scope 的 IO/ordinary exact candidate union,以及直接对象式 replacement #define OWNER_UV v.uv,已由本 HEAD 的 directMemberOwnerReferences 与 referenceResolutionSnapshots 纳入 canonical owner issue;对应三条 pipeline、两个 GLES target 回归继续保留。
- ambiguousStructMemberOwnerLocations / unlowerableStructMemberOwnerLocations 两份持久 verdict 已收敛为一份带 certainty 的 structMemberOwnerIssues;本轮没有发现该 canonical fact 被重新分叉。
- 更早的 parser semantic common gate、prefix/postfix increment collector、跨独立条件组 exclusivity、同组 exact identity、复杂条件 global fallback、Particle 派生宏保活、共享预处理表达式与逻辑续行、opaque handoff、source-map 和 pack/install consumer 等已关闭项继续保持关闭;本轮没有恢复 backend name guessing、runtime proof solver、legacy wrapper 或 compatibility fallback。
问题
-
[P1] packages/shader-parser/src/parser/AST.ts:2469-2509,2529-2559 / packages/shader-parser/src/ir/ShaderCoreInfo.ts:194-256 / packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:148-185,199-215 / tests/src/shader-compiler/MacroBranchRuntime.test.ts:1056-1086 — replacement projection 仍没有建模函数式宏的 formal→actual 绑定,也不会递归嵌套 replacement,公开 pipeline 会生成引用 undeclared v 的 GLES。 _collectIdentifierRefs 在参数命中时跳过 identifier,并在遇到任意 VariableIdentifier 后直接 return;因此 GET_UV(x) 的 x.uv 不会投影到 GET_UV(v) 的实际 owner,child 为 MacroCallSymbol 的 OWNER.uv 也不会继续下钻。MacroCallFunction 只复制宏名节点已有 references,没有建立实参与形参的 exact-symbol 关系。作者回复所述 same-scope 与直接 #define OWNER_UV v.uv 覆盖不包含这两种形态。对同一目标 HEAD 已验证 #define GET_UV(x) x.uv + GET_UV(v),以及 #define OWNER v + #define OWNER_UV OWNER.uv:Analyzer 无诊断,analyzer handoff、live compiler、offline precompiler 在 GLSLES100/300 均成功;runtime instruction expansion 后 fragment 仍含 v.uv,而 IO 全局 v 已被 flatten/delete,只留下 uv,产物引用未声明变量。
请保留 parser 的 macro definition / formal-actual binding / branch identity 与 VariableIdentifier.resolvedValueSymbols() 为 owner projection 的唯一事实,ShaderCoreInfo.structVariableRoles 为 IO role 的唯一事实;从每个实际 emitted member occurrence 机械派生函数实参与 nested replacement 的 owner resolution,删除“只收集 replacement 内非形参 BaseToken”的不完整边界。无法安全降低的形态应进入现有 canonical issue;不要在 backend 按名称猜 role、复制宏求解器、补镜像声明或 compatibility branch。回归应加入上述两个 fixture,覆盖 analyzer handoff、live/offline、GLSLES100/300 和 runtime expansion,并断言正确 uv / v.uv,或三条路径一致地显式拒绝。
-
[P1] packages/shader-parser/src/parser/ShaderInfo.ts:23-29 / packages/shader-parser/src/ir/ShaderCoreInfo.ts:236-256,271-307 / packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:199-215 — replacementMemberOwnerPath 仍把互斥宏定义中的独立 replacement 合并成一份 role union,拒绝了 backend 已能逐定义正确降低的合法变体。 replacementGroups 的 key 只有同一个 call-site reference 与 replacement 内结构 path;两个 MacroDefine.valueExpression 只要形态相同就会被并组,即使它们来自同一 #if/#else 的不同定义。目标 HEAD 对 USE_IO 两个 arm 分别定义 OWNER_UV 为 io.uv 与 ordinary.uv 的 shader 报 AmbiguousMacroBranchResolution,并让三条生成路径、两个 target 全部拒绝;而绕过新增 gate 后,同一个 CodeGenVisitor 已分别把 true arm 降为 uv、false arm 保持 ordinary.uv,声明与引用均闭合。这里不存在同一 emitted occurrence 的 owner 冲突,冲突是 path-only union 人为制造的。
请保留每个 MacroDefine.valueExpression + branch identity 作为 emitted replacement 的 owner,删除跨定义仅凭相同字符串 path 做 role union 的转换;只有真正流入同一 emitted occurrence 的 call-site-dependent resolutions 才合并。无需增加 backend resolver,现有 CodeGenVisitor 已逐定义转换 replacement。回归应覆盖 analyzer handoff、live/offline、两个 target 和 USE_IO true/false,并分别断言 uv / ordinary.uv;不能用扩大 ambiguity gate、path-level 镜像状态或旧拒绝测试来维持当前 false positive。
-
[P2] packages/shader-parser/src/ir/ShaderCoreInfo.ts:55-61 — 新增 exported ShaderStructMemberOwnerIssue 仍把说明与 @internal 压在单行 TSDoc。 请只把这个本次新增的 interface 改成多行契约,并将 @internal 独立成 tag;无需整理未修改的相邻声明或新增文档抽象。
架构、熵增与测试治理
本轮接受作者说明后,Analyzer 的 definite-only diagnostic policy 与 compiler 的 conservative emission policy 不再视为平行真相:权威事实仍只有一份 structMemberOwnerIssues,certainty 允许两个职责不同的 consumer 机械选择策略,且公开 API 没有承诺 clean analysis 等于 backend 一定生成。
剩余熵增集中在 owner candidate projection。相较上一 HEAD 的 runtimeFallbackReferences 单表,本提交新增 directMemberOwnerReferences、referenceResolutionSnapshots 和 replacementMemberOwnerPath 三段表示,再经 path grouping 与 certainty 转换;它同时漏掉 formal→actual / nested replacement,又把独立定义合成冲突。应保留 macro definition / binding / branch + exact symbol resolution → per-emitted-occurrence owner → ShaderCoreInfo role / canonical issue → Analyzer policy与compiler admission 这一条事实链,具体删除跨定义 path union,并让 parser-owned substitution 投影补齐函数式与嵌套 expansion;不要再增加第三份 macro state、backend wrapper、镜像 role 或第二条校验路径。
测试侧,same-scope incompatible/compatible 与直接 expression-macro 三项回归正确关闭了上一轮反例,应保留;但仍缺 function-like、nested replacement 和“互斥定义各自可 lowering”的公开 artifact 矩阵。应补写这些用例;失效的 path-level 拒绝 fixture 应按新公开契约重写或删除,永远不得为了旧测试在生产代码保留 compatibility branch、legacy fallback、wrapper、镜像状态或第二条转换/校验路径。
- Bind function-like and nested macro owners to emitted reference identities. - Keep mutually exclusive replacements independent and block unresolved lowering. - Add GLES100/300 analyzer, live, offline, and runtime regressions.
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(1 个 P1,另有 1 个 P2)。已基于 2b1fb1a13ddafdb314ce9a47d282e4c6ab5f5c80...2a02367588ee1565e1d40a2496a77a2ebffe9069 的 1 个增量 commit、6 个文件完成审查,并沿 macro definition / formal→actual binding / macro result identity / call-site member occurrence → MacroReference / referenceResolutionSnapshots → ShaderCoreInfo canonical owner issue → ShaderIOValidator / ShaderCompilation policy → CodeGenVisitor / runtime expansion 向上游和下游各追一层。目标源码快照的全量 module + type build 成功;本提交新增的 6 个 owner 回归在 node runner 中验证 analyzer/compiler/artifact assertions 为 6/6 通过,GitHub 12 项 checks 也全部成功。默认 browser runner 仍因 macOS sandbox 权限无法启动,未产生可归因于代码的失败;公开 API 反向探针则确认 call-site member 形态在三条 pipeline、两个 GLES target 中都会生成引用未声明 v 的 artifact。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 2a02367588ee1565e1d40a2496a77a2ebffe9069。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮函数式 replacement 内的 formal→actual 漏投影,以及 nested expression/function macro 不递归的问题,已由
formalParameterIndex、appendSubstitutedOwnerReferences和嵌套referenceSymbols在2a023675中关闭。原反例GET_UV(v)、OWNER_UV与两层FORWARD_UV(v)现在都会进入同一 canonical issue,三条 pipeline、两个 target 一致拒绝。 - 上轮跨互斥宏定义按字符串
replacementMemberOwnerPath合并的 false positive 已关闭。字符串 path 已替换为 definition-ownedVariableIdentifierAST identity;USE_IO两个定义现在独立 lowering,false arm 保留ordinary.uv、true arm 生成uv。 - 上轮
ShaderStructMemberOwnerIssue单行 TSDoc 已改为多行说明并将@internal独立成 tag;本轮新增的两处单行注释问题见问题 2,不重复前一项。 unknown在 Analyzer 中 silent、在 emitter 中 conservative blocking 的 policy 分层继续按作者解释保持关闭;canonical fact 仍只有一份structMemberOwnerIssues,没有恢复两份 admission verdict。- 更早的 same-scope candidate union、直接
#define OWNER_UV v.uv、parser semantic common gate、prefix/postfix increment、跨独立条件组 exclusivity、复杂条件 global fallback、Particle 派生宏保活、共享预处理表达式与逻辑续行、opaque handoff、source-map 和 pack/install consumer 等关闭项均未被本增量恢复。
问题
-
[P1]
packages/shader-parser/src/parser/AST.ts:1852-1866,2588-2599,2718-2735/packages/shader-parser/src/ir/ShaderCoreInfo.ts:206-239/packages/shader-compiler/src/codeGen/CodeGenVisitor.ts:54-86,148-184/tests/src/shader-compiler/MacroBranchRuntime.test.ts:1086-1305— 新绑定只覆盖“成员发生在 replacement 内”,成员发生在调用点、宏只返回 owner 时仍没有 value-owner 投影,三条公开 pipeline 会生成 undeclaredv.MacroCallFunction.semanticAnalyze现在已经把OWNER(v)的实参v放进referenceSymbols,但VariableIdentifier.resolvedValueSymbols()对任意 AST-backed macro 一律返回空;同时 function-like define 在_analyzeForCodegen中必然令aliasesNonBuiltinIdent=false,所以collectStructMemberOwnerIssues的 direct-member 分支不会消费刚绑定出的 exact actual。对象式 bare identity 虽能进入该分支,也只按单一 IO role 判为无冲突,没有被标成 runtime-expanded owner。下游拿不到 macro result 的 role,只能保留调用文本。在目标 HEAD 对
#define OWNER(x) x+OWNER(v).uv、括号版、#define OWNER v+OWNER.uv以及两层 wrapper 做了公开 API 探针:Analyzer diagnostics 为空,analyzer handoff、live compiler、offline precompiler 在 GLSLES100/300 全部成功;runtime expansion 后完整 fragment 分别含v.uv或(v).uv,而全局Varyings v已被 flatten/delete,产物中没有v声明。这与本轮已关闭的#define GET_UV(x) x.uv不同:后者的 member occurrence 位于 definition replacement,前者的实际 emitted member occurrence 位于 call site。请保留 parser 的
MacroDefine.valueExpression + formal→actual binding + branch identity为 macro result identity 的唯一 owner、ShaderCoreInfo.structVariableRoles为 IO role 的唯一 owner:让resolvedValueSymbols()(或同级唯一 value-owner fact)对可证明的 bare/formal/nested identity replacement 机械返回 bound actual symbols,并让 direct member occurrence消费这份事实;具体删除 owner gate 对hasAstValue && aliasesNonBuiltinIdent这一形态启发式的依赖。可证明的单一 IO owner可直接 lowering 为uv,其余无法安全降低的形态进入现有 canonical issue;不要在 backend 按宏名猜 role、保留v镜像声明或新增 compatibility branch。回归应加入 function/object/nested identity 的... .uv、ordinary control,覆盖三条 pipeline、两个 target 与 runtime artifact。 -
[P2]
packages/shader-parser/src/parser/AST.ts:135/packages/shader-parser/src/ir/ShaderCoreInfo.ts:242— 本提交新增的两条单行//注释仍以句号结尾。 请只移除这两处末尾句号;无需改写为新文档抽象或整理未修改的相邻注释。
架构、熵增与测试治理
本轮总体是减熵:跨定义字符串 path 已删除,replacement owner 改由 definition-owned AST identity 表达;formal index 与 runtime-expansion provenance 使函数实参与 nested replacement 复用 parser 的 branch/exact-symbol resolution,canonical backend issue 仍只有一份,Analyzer 与 compiler 也没有新增 backend resolver、镜像 role、legacy fallback 或 compatibility wrapper。新增 kind 只是同一 issue 的机械原因投影,不构成第二份 verdict。
剩余 P1 表明 per-emitted-occurrence 链路仍分成两种 owner 协议:replacement 内成员走 replacementMemberOwner,调用点成员落在 macro result 上时则继续依赖 aliasesNonBuiltinIdent 与“AST macro 的 resolvedValueSymbols() 恒空”这条旧边界。于是同一 formal→actual exact fact 已存在,却没有流到相邻 direct-member consumer。应保留 macro definition/binding/branch → exact value owner → emitted member occurrence → struct role / canonical issue → analyzer/compiler policy 这一条事实链,删除 call-site 的形态启发式,而不是增加第三份 macro state或 backend 同步层。
测试侧,本提交的 function-body member、nested expression/function、unknown、ordinary 与互斥 definition 六项回归正确替换了上一轮缺口,均应保留;没有发现为了旧 fixture 新增生产 compatibility 路径。但矩阵只把 member 放在 replacement 内,没有覆盖 identity macro result 后再取 member,因而全绿仍漏掉上述 artifact。请按新公开 value-owner 契约补写 call-site fixture;不得为旧成功结果保留 wrapper、镜像声明、legacy fallback 或第二条转换/校验路径。
GuoLei1990
left a comment
There was a problem hiding this comment.
🫧 尘小沫
结论
阻塞级别:Request changes(1 个 P1)。已基于 2a02367588ee1565e1d40a2496a77a2ebffe9069...5ecc305ce6ecc9d9b4e8214cde7a223a27e06b85 的 1 个增量 commit、5 个文件完成审查,并沿 macro definition / formal→actual binding / branch identity → referenceSymbols / isValueIdentity / resolvedValueSymbols / referenceResolutionSnapshots → ShaderCoreInfo canonical owner issue → ShaderIOValidator / ShaderCompilation → CodeGenVisitor / analyzer handoff / live compiler / offline precompiler 向上游和下游各追一层。目标源码快照的全量 module + type build 成功,MacroBranchRuntime.test.ts 的 node runner 为 48/48 通过(WebGL context 分支未执行);公开 API 正反例探针覆盖三条 pipeline 与 GLSLES100/300,确认上一轮无条件 identity 反例已正确 lowering,但穷尽条件定义的合法 identity 变体只在 live compiler 被新增 gate 拒绝。远端当前仅有 labeler check 成功,PR 仍为 CONFLICTING。实际 review 动作为 REQUEST_CHANGES,目标 HEAD 为 5ecc305ce6ecc9d9b4e8214cde7a223a27e06b85。自动 CR 不替代人工 Reviewer 的合入门禁(APPROVE)。
已关闭问题清单
- 上轮 call-site member occurrence 缺少 value-owner 投影的具体 P1 已由
isValueIdentity、formal→actual substitution、_valueSymbols与 definition-owned resolution snapshot 在5ecc305c中关闭:OWNER(v).uv、括号版、对象式 identity 及两层 function/object wrapper 现在都在三条 pipeline、两个 target 中 lowering 为uv,runtime artifact 不再引用已删除的v。 - 上轮
AST.ts与ShaderCoreInfo.ts两处新增单行注释句号已移除,P2 已关闭;本增量新增/修改的注释与 TSDoc 未发现同类违规。 - replacement 内的 formal→actual / nested owner 投影,以及跨互斥定义按字符串 path 合并的 false positive 继续保持关闭:replacement owner 仍由 definition-owned AST identity 分组,没有恢复
replacementMemberOwnerPath、backend name guessing、镜像声明或 compatibility fallback。 unknownowner 在 Analyzer 中保持 silent、在 emitter 中保守阻断的职责分层继续按作者解释关闭;更早的 branch/liveness、parser common admission、preprocessor expression、opaque handoff、source-map、pack/install consumer 等关闭项未被本增量恢复。
问题
-
[P1]
packages/shader-parser/src/ir/ShaderCoreInfo.ts:325-359,482-489/tests/src/shader-compiler/MacroBranchRuntime.test.ts:1169-1400— 新 member-availability gate 把互斥 identity definitions 聚合后要求 runtime resolver 证明 sibling-arm union,合法穷尽变体因此只在 live compiler 被拒绝。 每个ReferenceResolutionSnapshot已分别携带当前MacroDefine.valueExpression + call-site branch,但classifyIOMemberAvailability又把所有 resolution 的ownerBranches/memberBranches合成列表,并相对未加 guard 的reference._branch做一次 coverage。Analyzer 的完整BranchAnalysis能证明同组#ifdef/#else覆盖,所以 handoff/offline 通过;默认getRuntimeBranchCoverage只在某一个 candidate 单独对 call site 词法可见时返回covered,无法用两个 sibling arms 的 union 得出覆盖,最终制造incompatible-io-member/unknown,而 compiler 对任意 canonical issue 都返回undefined。在目标 HEAD 验证了两个合法控制:对象式两 arm 分别为
#define OWNER a/b,其中a、b都是含uv的Varyings;函数式两 arm分别为#define OWNER(x) x/(x),调用OWNER(v).uv。两者在 GLSLES100/300 均为 Analyzer diagnostics 空、ShaderCompiler.generate与 offline precompile 成功并生成闭合的uvartifact,唯独 live_parseShaderPass返回undefined;直接以默认 runtime facts 重建ShaderCoreInfo可见唯一 issue 正是incompatible-io-member/unknown。现有成功矩阵只有无条件 identity,条件矩阵又全部是应拒绝用例,所以 48/48 绿测没有覆盖这个 false positive。请保留
MacroDefine.valueExpression + formal→actual binding + branch identity → ReferenceResolutionSnapshot为 value owner 的唯一事实、structVariableRoles为 IO role 的唯一事实:按每个 definition-owned resolution 相对其自己的callSiteBranch检查所选 IO symbol 的 struct/member 是否可用,再机械汇总结果;删除当前跨 resolution 的ownerBranches/memberBranches → reference._branch二次 union coverage。这样 runtime 只消费自己能证明的词法事实,不需要复制 Analyzer solver。回归应增加上述 object/function safe sibling-arm controls,覆盖 handoff/live/offline、两个 target 与 runtime artifact,同时保留现有 mixed/incompatible/opaque 拒绝用例;不要新增 runtime solver、compatibility branch、wrapper 或第二条 admission gate。
架构、熵增与测试治理
本增量对上一轮主链路总体是减熵:call-site 与 replacement 内成员现在都从 parser-owned macro definition、formal binding、branch identity 和 exact symbol resolution 单向派生;resolvedValueSymbols 只为可机械证明的 bare/formal/nested identity 暴露 owner,backend 继续只消费 structVariableRoles 与一份 structMemberOwnerIssues,没有新增 name-based resolver、镜像 role、legacy fallback 或第二份 verdict。isValueIdentity、_valueSymbols 与新 issue kind 分别是同一 provenance 在 binding、codegen 和 admission 边界的机械投影,不构成独立状态机。
剩余 P1 的熵增来自新 classifier 把已经按 definition 分开的 snapshot 再聚合成全局 coverage,并让同一 canonical fact依赖两种证明能力:Analyzer/handoff/offline 的完整 branch solver 与 live runtime 的单-candidate lexical resolver因而产生 pass/pass/reject 三态。应保留 per-definition snapshot owner、删除这次 aggregate union 转换,使数据流收敛为 definition/binding → per-resolution member fact → canonical issue → mechanical consumers,而不是给 runtime 增加第三套宏证明。测试新增的 unconditional identity、ordinary、mixed、incompatible 与 opaque fixtures 都应保留;没有发现为了旧 fixture 新增生产 compatibility 路径,但必须补一个合法 sibling-arm control,防止拒绝矩阵继续把 false positive 隐藏在全绿结果中。
设计依据:RFC RFC: Shader Static Analyzer #3017。核心参考 Naga 的
Frontend → IR → Validator/Info → Backend分层,让解析、生成和诊断独立消费中立事实;本 PR 只建立未来后端边界,不实现 WGSL。shader-parser负责 ShaderLab/source-pass 解析并拥有ShaderClueIR、ShaderCoreInfo等中立结构;shader-compiler只消费这些事实并通过ShaderBackend生成 GLES;shader-analyzer作为同级消费者产出结构化 diagnostics,并提供浏览器 API 与 headless CLI。parser 仅暴露
@galacean/engine-shader-parser/internal运行时入口和/internal/analyzer分析入口,根包拒绝解析;已删除_VERBOSE、jscc 双构建和 verbose 产物。冷构建通过显式 workspace runtime source 解析完成,不向 npm 包发布源码条件。source parser 统一拥有 source error、entry binding、原始 range/source mapping 与 include canonical path;compiler 消费 typed parse envelope,结构性错误会在 precompile 序列化前失败,不再发布被静默丢弃 RenderState 的部分产物。
VariableDeclaratorInfo是 const、initializer 与 array shape 的唯一事实;ShaderValidator统一产生NonConstInitializer等诊断,局部、全局、数组和逗号 declarator 使用同一规则。宏分支分析采用三态契约:可证明安全时不报,确定错误时报 error 并保留 witness,无法证明时只报 warning;analyzer 不枚举全部宏组合,也不改变或阻断 runtime codegen。复杂或未知关系不被伪装成确定错误。
include 支持 canonical URL、嵌套相对路径、循环检测、双向 root 顺序回归与精确文件定位。默认运行时产物不包含 analyzer proof solver、authoring diagnostic 文案、分析 Lexer 或报告方法。
基线固定为
dev/2.0@bd34daa45612af8b402cd3be916ff181f21ae742。本地完整 Chromium Vitest 为 141/141 个测试文件、445/445 个 suite、2149/2149 个用例;14 个包类型构建通过,22/22 个内置 Shader 在 bootstrap 与最终模块构建中均成功预编译。本次 head 的 GitHub CI 10/10 通过,覆盖三平台 build、四组 e2e、lint 与 codecov。当前 shipping Shader source 相对上一 PR head 无新增改动。对
dev/2.0执行 22 个内置 Shader × 25 组宏配置、1300 个 stage 对比:49 个差异全部来自已知的非法 Fog/SSAO/Particle 重叠宏 fallback,unexpected mismatch 为 0;所有受支持配置与基线一致。真实 WebGL 预编译 A/B 为 57/57。相同 consumer entry 下,当前运行时 bundle 为 345601 B / 63149 B gzip;
dev/2.0基线为 383670 B / 65870 B gzip,分别减少 9.92% / 4.13%。产物与 sourcemap 门禁确认 runtime 未包含BranchAnalysis、AnalyzerLexer、AnalyzerSemanticDiagnostics、PassParser或 analyzer-only diagnostic 文案。同机交替 A/B 基准每轮包含 200 次 warmup、15 个交替 batch、每 batch 200 次。完整 pipeline 三轮结果为 +1.266% / +0.0368 ms、+2.527% / +0.0727 ms、-0.502% / -0.0146 ms;没有任何一轮同时超过 +3% 和 +0.05 ms 的回归门槛。
parser、compiler、analyzer 的 npm dry-run、resolver 与 export-target 门禁通过;parser 包为 80 个条目且不发布
src。Standalone CLI 已验证 clean=0、warning=0、error=1;8 份 parser/compiler/analyzer sourcemap 均包含完整sourcesContent且不泄漏绝对路径。已知边界:branch signature 不是 proof-complete 的 ESSL 宏求解器;不能证明的复杂宏关系保持 warning,最终 GLES driver 仍是运行时可接受性的事实来源。