Skip to content

Fix default value lowering for fields of associated type - #12435

Closed
fknfilewalker wants to merge 8 commits into
shader-slang:masterfrom
fknfilewalker:fix-assoc-default-init-and-matrix-layout
Closed

Fix default value lowering for fields of associated type#12435
fknfilewalker wants to merge 8 commits into
shader-slang:masterfrom
fknfilewalker:fix-assoc-default-init-and-matrix-layout

Conversation

@fknfilewalker

@fknfilewalker fknfilewalker commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

getDefaultVal treated an associated type like a struct and emitted a member-wise makeStruct. An associated type has no fields, so the makeStruct got zero operands, and once specialization resolved the type to a scalar (e.g. Real.Differential -> float) the emitter produced an OpConstantComposite %float with no constituents, which spirv-val rejects.

Since the resolved type may not be a struct at all, emit DefaultConstruct instead; the existing peephole folds it to a concrete zero once the type is known. The same applies to the other decls whose concrete type is unknown before specialization (InterfaceDecl, ThisTypeDecl, GlobalGenericParamDecl).

@fknfilewalker
fknfilewalker requested a review from a team as a code owner August 9, 2026 10:00
@fknfilewalker
fknfilewalker requested review from bmillsNV and a lite review from Copilot and removed request for a team August 9, 2026 10:00
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • ✅ Review completed - (🔄 Check again to review again)

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 183c37bc-100e-4e73-aeb9-0902c3019fba

📥 Commits

Reviewing files that changed from the base of the PR and between 1e74b40 and a0b7aa2.

📒 Files selected for processing (5)
  • source/slang/slang-emit.cpp
  • source/slang/slang-lower-to-ir.cpp
  • tests/spirv/assoc-type-default-init.slang
  • tests/spirv/matrix-layout-late-specialize-module.slang
  • tests/spirv/matrix-layout-late-specialize.slang

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Changes

The compiler now processes matrix layouts after specialization and constructs specialization-dependent types explicitly. New SPIR-V tests cover associated-type default initialization and late matrix-layout specialization.

Specialization and Default Construction

Layer / File(s) Summary
Post-specialization matrix layout processing
source/slang/slang-emit.cpp
Runs specializeMatrixLayout after finalizeSpecialization.
Associated-type default construction
source/slang/slang-lower-to-ir.cpp
Uses emitDefaultConstruct for specialization-dependent declaration reference types in getDefaultVal.
Associated-type SPIR-V regression coverage
tests/spirv/assoc-type-default-init.slang
Adds float and float3 associated-type specializations and validates their default initialization in SPIR-V.
Late matrix specialization regression coverage
tests/spirv/matrix-layout-late-specialize-module.slang, tests/spirv/matrix-layout-late-specialize.slang
Adds differentiable matrix abstractions and checks for duplicate natural-layout matrix storage types.

Suggested reviewers: bmillsnv

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (4 skipped: 3 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly explains the associated-type default-value fix and its SPIR-V impact. It directly relates to the changeset.
Title check ✅ Passed The title accurately describes the associated-type default-value lowering fix, which is a primary part of the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. (4 skipped: 3 unsupported, 1 too large.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a5cc3ff8-f95c-4648-a22e-f4d8d5492ce8

📥 Commits

Reviewing files that changed from the base of the PR and between 716ec59 and c20db1b.

📒 Files selected for processing (3)
  • source/slang/slang-emit.cpp
  • source/slang/slang-lower-to-ir.cpp
  • tests/spirv/assoc-type-default-init.slang

Comment thread tests/spirv/assoc-type-default-init.slang Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes two sources of invalid SPIR-V produced when types are only fully resolved during/after specialization: (1) associated-type default values incorrectly lowering to a zero-operand makeStruct that later becomes an invalid scalar OpConstantComposite, and (2) matrix types with unknown/default layout being instantiated after the matrix-layout specialization pass, causing duplicate matrix storage structs and invalid pointer access chains.

Changes:

  • Adjust default-value lowering so AssocTypeDecl defaults lower to DefaultConstruct (instead of member-wise makeStruct), allowing later specialization/peepholes to materialize a valid concrete default.
  • Move specializeMatrixLayout to run after finalizeSpecialization so it also covers matrix types created from lazily imported generic bodies during specialization.
  • Add a SPIR-V regression test covering the associated-type default initialization case (ensuring no scalar-typed OpConstantComposite is emitted).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
tests/spirv/assoc-type-default-init.slang New SPIR-V test guarding against invalid scalar OpConstantComposite from associated-type default initialization after specialization.
source/slang/slang-lower-to-ir.cpp Treat AssocTypeDecl default values like interfaces: lower to emitDefaultConstruct to avoid zero-operand makeStruct on unresolved associated types.
source/slang/slang-emit.cpp Reorder passes so specializeMatrixLayout runs after finalizeSpecialization, covering lazily imported generics that instantiate matrices later in the pipeline.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread source/slang/slang-emit.cpp Outdated
Comment thread source/slang/slang-emit.cpp Outdated
github-actions[bot]

This comment was marked as outdated.

@fknfilewalker fknfilewalker added the pr: non-breaking PRs without breaking changes label Aug 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/spirv/assoc-type-default-init.slang (1)

1-1: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Set SPIR-V validation configuration explicitly.

CI runs SLANG_RUN_SPIRV_VALIDATION=1, but the test file itself does not set it while FileCheck checks only OpEntryPoint. Add a wrapper/test invocation with this variable or use the harness to make this regression test self-contained.

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3dd661e5-a22d-42ba-a54a-c3e1cc7f5a7a

📥 Commits

Reviewing files that changed from the base of the PR and between c20db1b and 617b9ef.

📒 Files selected for processing (1)
  • tests/spirv/assoc-type-default-init.slang

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c8c2b98b-caca-4066-ae5c-2923091a42e1

📥 Commits

Reviewing files that changed from the base of the PR and between 617b9ef and c4428b8.

📒 Files selected for processing (2)
  • tests/spirv/matrix-layout-late-specialize-module.slang
  • tests/spirv/matrix-layout-late-specialize.slang

Comment thread tests/spirv/matrix-layout-late-specialize.slang Outdated
@fknfilewalker
fknfilewalker force-pushed the fix-assoc-default-init-and-matrix-layout branch from c4428b8 to bc21376 Compare August 9, 2026 11:33
github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

Comment thread source/slang/slang-emit.cpp Outdated
github-actions[bot]

This comment was marked as outdated.

github-actions[bot]

This comment was marked as outdated.

@fknfilewalker
fknfilewalker force-pushed the fix-assoc-default-init-and-matrix-layout branch from 6260c69 to ddbbc85 Compare August 30, 2026 06:18
@fknfilewalker fknfilewalker changed the title Fix two sources of invalid SPIR-V from types resolved during specialization Fix default value lowering for fields of associated type Aug 30, 2026
github-actions[bot]

This comment was marked as outdated.

@jkwak-work jkwak-work removed Office-Tess To be discussed during Tess' office hours Office-Sai labels Aug 31, 2026
The default value for a field of associated type used to lower as a
zero-operand makeStruct; once specialization resolved the type to a
scalar, that emitted an OpConstantComposite with a scalar result type,
which spirv-val rejects.

The lowering fix landed upstream in shader-slang#12712 (getDefaultVal now only takes
the member-wise makeStruct path for a concrete field-owning aggregate,
and otherwise falls through to emitDefaultConstruct), but that PR added
no test. This keeps the regression test for it.
Generic bodies pulled in during specialization can mention matrix with the
defaulted (unknown) layout, creating matrix types after the pass ran. The
layout-only duplicates lower to distinct but identically named _MatrixStorage
structs, and an OpPtrAccessChain mixing them fails spirv-val because its
result type must be the base's exact type id. Run the pass once specialization
is done so those types are covered as well.
CHECK-NOT is bounded by the next positive match, and OpEntryPoint precedes
the constants section, so the negative check scanned a region where
OpConstantComposite cannot appear. Move it after the OpEntryPoint match;
verified the reordered directives fail against the unfixed compiler's
output.
The scenario requires a -conformance naming a specialized generic from an
imported module: only then is the generic body - carrying matrix<T, R, C>
with the defaulted (unknown) layout - cloned during specializeModule,
after the early specializeMatrixLayout run. Verified the test fails
against a compiler without the pass move (duplicate
_MatrixStorage_float4x4natural_0 in the output, all occurrences after the
OpEntryPoint anchor) and passes with it.
- Note why deferring specializeMatrixLayout past specialization is safe.
- Strengthen tests: cover vector resolution of the associated type and add
  positive CHECK anchors so the CHECK-NOTs cannot pass vacuously.

The default-value branch fold is dropped: upstream shader-slang#12712 now gates the
member-wise makeStruct path on isConcreteFieldOwningAggregate, so
AssocTypeDecl / ThisTypeDecl / GlobalGenericParamDecl already fall through
to emitDefaultConstruct and an explicit branch for them would be redundant.
Leaves this branch with the associated-type default-value fix only. The
specializeMatrixLayout change and its tests move to matrix-layout-mode-enum,
where the layout is given its own type so an unresolved layout stays
recognizable after it is passed as a generic argument.
@fknfilewalker
fknfilewalker force-pushed the fix-assoc-default-init-and-matrix-layout branch from f2c00cb to db68698 Compare September 2, 2026 11:47
@fknfilewalker

Copy link
Copy Markdown
Contributor Author

closed because fixed by #12708

auto-merge was automatically disabled September 2, 2026 11:50

Pull request was closed

@fknfilewalker
fknfilewalker deleted the fix-assoc-default-init-and-matrix-layout branch September 2, 2026 11:51

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: 🟡 Minor — 0 bugs, 1 (low) gap, plus 2 clarity notes on the test file.

This PR's net diff against master adds exactly one regression test, tests/spirv/assoc-type-default-init.slang; the underlying getDefaultVal fix (the isConcreteFieldOwningAggregate guard + emitDefaultConstruct fallthrough at source/slang/slang-lower-to-ir.cpp:6736,6764) already landed in master, so this review covers the test only. The test correctly reproduces the bug — a zero-constituent OpConstantComposite from a zero-operand makeStruct on an associated-type field default — and its CHECK-NOT ...{{$}} assertions would catch a naive reintroduction. Findings are about coverage breadth and maintenance robustness, not correctness of the guard.

Changes Overview

Regression test for associated-type field default init (tests/spirv/assoc-type-default-init.slang)

  • Adds a SIMPLE/-target spirv FileCheck test: a generic struct P<Real:IReal> : IDifferentiable with an associated-type field, instantiated at float and float3. CHECK-NOT lines assert no scalar/vector-result zero-constituent OpConstantComposite is emitted; a positive OpFunctionCall line bounds the range. In CI (SLANG_RUN_SPIRV_VALIDATION=1) the pre-fix compiler additionally fails spirv-val, so the positive CHECK: OpEntryPoint would also fail.
Findings (3 total)
Severity Location Finding
🟡 Gap tests/spirv/assoc-type-default-init.slang:41 Sibling ThisType/global-generic default-init paths sharing the same emitDefaultConstruct fallthrough are untested
🔵 Clarity tests/spirv/assoc-type-default-init.slang:49 Positive CHECK couples the test to a synthesized __init name and one lowering strategy; range/type-coupling of the CHECK-NOTs is implicit
🔵 Clarity tests/spirv/assoc-type-default-init.slang:25 [noinline] is load-bearing for the repro but its purpose is unstated

reviewed: db68698 · diff sha256 6804e51ad131

Real area;
}

[noinline]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Clarity: [noinline] looks load-bearing but its purpose is unstated

[noinline] appears essential to reproducing the bug: if use were inlined, the P<Real>.Differential(v) construction and its associated-type-field default could be constant-folded away before the malformed OpConstantComposite is emitted, so the test would pass even against the pre-fix (buggy) compiler. Because the whole file is a regression guard, a future maintainer cleaning up an "unnecessary" attribute could silently defeat it.

Suggestion: Add a one-line comment stating that inlining must be prevented so the associated-type default survives to codegen. (If [noinline] is not actually required to reproduce, dropping it would simplify the repro instead.)

[numthreads(1, 1, 1)]
void main(uint3 tid: SV_DispatchThreadID)
{
outBuf[0] = get<float>(float3(tid.x, 1, 2)) + get<float3>(float3(tid.x, 3, 4));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Gap: sibling associated-type-like default-init paths (ThisType, global generic param) are not covered

The fix this test guards routes AssocTypeDecl to emitDefaultConstruct via the fallthrough at source/slang/slang-lower-to-ir.cpp:6764. ThisTypeDecl and GlobalGenericParamDecl also derive from AggTypeDecl (source/slang/slang-ast-decl.h), are not concrete-field-owning, and rely on the same fallthrough — yet nothing exercises a default value for a field whose type is a This type or a global generic parameter. The InterfaceDecl sibling is separately covered by tools/slang-unit-test/unit-test-spirv-interface-default-init-validation.cpp; these two are not.

Criticality is low (they share the fallback with the tested AssocType case), but a future refactor of that branch could regress them without any test failing.

Suggestion: Optionally add a this-typed and/or global-generic-typed field default-init case (here, or in the existing interface-default-init validation test) so the whole AggTypeDecl-fallthrough family is guarded, not just the AssocType instance.

// CHECK: OpEntryPoint
// CHECK-NOT: OpConstantComposite %float{{$}}
// CHECK-NOT: OpConstantComposite %v3float{{$}}
// CHECK: OpFunctionCall %P_Differential %P_Differential__init {{.*}} %float_0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Clarity: the positive CHECK couples the test to a synthesized name and one lowering strategy

The real regression guard is the two CHECK-NOT: OpConstantComposite ...{{$}} lines above — the {{$}} end-anchor precisely catches a zero-constituent composite (a well-formed default has constituents after the result type). This final positive CHECK additionally pins the output to a specific synthesized name (%P_Differential__init) and to the current lowering (default → non-inlined init call passing %float_0), and only anchors the float instantiation. A later still-correct change that folds the default into a valid non-scalar constant, renames the synthesized __init, or inlines the constructor would break this line even though the guarded bug has not regressed.

Two smaller clarity points on the same block:

  • The comment ("the positive check anchors the range") is doing real work: the CHECK-NOTs only guard the region between the OpEntryPoint and OpFunctionCall matches. That correctly covers the SPIR-V types/constants section, but a reader has to know constants sit after OpEntryPoint and before function bodies to see the anchors bracket the right region — worth a one-line note.
  • The %float/%v3float CHECK-NOTs are tied to the two instantiations in main; if those types change, the guards silently stop matching. A brief note tying them to the get<float>/get<float3> calls would keep that coupling visible.

Suggestion: Document that this positive line exists only as the closing range anchor for the CHECK-NOTs, or use a looser anchor (e.g. OpFunction) that bounds the same range with less coupling to naming/inlining.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: non-breaking PRs without breaking changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants