---
generated: true
model: claude-opus-4-7
generated_at: 2026-05-20T16:30:00+00:00
source_commit: 74db89b9f77cdced9c4d0c47f377b38fffb9180b
watched_paths_digest: 47917e9e7bbd0d4c7cec93d02043b25787343696017d2490659c825a87e68dbe
source_doc: docs/generated/design/pipeline/03-semantic-check.md
source_doc_digest: 7cb7a8661d1aff4316b63053539fe2827ef6a707dcb4338674753b06c8afbfa9
warning: "Auto-generated. May drift from source. Do not edit by hand."
---

# Tests for pipeline/03-semantic-check

## Intent
Tests verify the semantic-checking behaviors described in
[`docs/generated/design/pipeline/03-semantic-check.md`](../../../design/pipeline/03-semantic-check.md):
turning a raw AST into a fully resolved, type-checked AST. The doc
hands off algorithmic detail for name resolution and overload
resolution to the
[`name-resolution/`](../../../design/name-resolution/)
subtree, so a small number of tests anchor to those secondary docs
where the primary doc explicitly references them.

Semantic checking is dominantly about rejection. The bundle is
weighted toward `DIAGNOSTIC_TEST` (negative) coverage of the doc's
"these things are checked" claims: type mismatches, missing
interface members, control-flow rule violations, lvalue checks,
modifier-based mutability, generic-without-arguments, redeclaration,
ambiguous-reference, function-redefinition, divide-by-zero,
case-outside-switch, return-needs-expression, missing-return,
entry-point stage requirement. Functional `//TEST:INTERPRET` tests
cover the doc's positive claims: type-attached expressions, function
bodies parsed/checked on demand, container-level overload
accumulation, conversion-cost-based overload selection, generic
argument inference, interface conformance, implicit conversion,
typedef resolution, operator overloading, base-member lookup, and
synthesized initializer-list construction.

Multi-backend rule: every claim in this doc is target-independent
(the doc never asserts target-conditional semantic behavior), so
each test uses a single directive. Positive observations use
`//TEST:INTERPRET` (the lightest-weight runner that exercises
checked code at runtime); diagnostic claims use
`//DIAGNOSTIC_TEST:SIMPLE`.


## Functional coverage
| Claim | Intent | Anchor | Tests |
| --- | --- | --- | --- |
| Block-local shadowing hides a local from lookup until its DeclStmt is reached; using `x` before its declaration produces an undefined-identifier diagnostic. | negative | [#block-local-shadowing](../../../design/pipeline/03-semantic-check.md#block-local-shadowing) | [`forward-reference-in-block-rejected.slang`](forward-reference-in-block-rejected.slang) |
| Same-named functions in one container accumulate into an overload set; overload resolution picks each by argument type at the call site. | functional | [#container-level-overload-accumulation](../../../design/pipeline/03-semantic-check.md#container-level-overload-accumulation) | [`overload-accumulation-by-arg-type.slang`](overload-accumulation-by-arg-type.slang) |
| Overload checking accepts a function taking 8 positional arguments and resolves at the call site; each argument flows through to the body. | boundary | [#conversion-costs](../../../design/pipeline/03-semantic-check.md#conversion-costs) | [`many-args-positional.slang`](many-args-positional.slang) |
| Overload checking accepts a zero-argument call to a function with a default argument; the default-value expression is filled in at check time. | boundary | [#conversion-costs](../../../design/pipeline/03-semantic-check.md#conversion-costs) | [`default-argument-with-zero-args.slang`](default-argument-with-zero-args.slang) |
| Overload resolution prefers the candidate that requires no implicit conversion (lowest conversion cost) over one that requires a numeric widening. | functional | [#conversion-costs](../../../design/pipeline/03-semantic-check.md#conversion-costs) | [`overload-prefer-exact-type.slang`](overload-prefer-exact-type.slang) |
| Overload resolution selects the exact-type `int` candidate over the `uint` candidate for an `int`-typed argument because no signedness conversion is needed. | boundary | [#conversion-costs](../../../design/pipeline/03-semantic-check.md#conversion-costs) | [`overload-prefer-int-over-uint.slang`](overload-prefer-int-over-uint.slang) |
| A call with zero applicable candidates among multiple overloads is rejected with "no overload applicable" naming the call name. | negative | [#edge-cases-and-failure-modes](../../../design/pipeline/03-semantic-check.md#edge-cases-and-failure-modes) | [`no-applicable-overload-zero-candidate-rejected.slang`](no-applicable-overload-zero-candidate-rejected.slang) |
| Overload resolution rejects a call passing too many arguments with the "too many arguments to call" diagnostic, counterpart of "not enough arguments". | negative | [#edge-cases-and-failure-modes](../../../design/pipeline/03-semantic-check.md#edge-cases-and-failure-modes) | [`too-many-arguments-rejected.slang`](too-many-arguments-rejected.slang) |
| Two equally-applicable overloads to the same call site are rejected with the "ambiguous call" diagnostic naming the function and arg types. | negative | [#edge-cases-and-failure-modes](../../../design/pipeline/03-semantic-check.md#edge-cases-and-failure-modes) | [`ambiguous-overload-rejected.slang`](ambiguous-overload-rejected.slang) |
| When a non-overloadable lookup resolves to multiple decls (two types of the same name reachable via different namespace imports), the checker reports ambiguous-reference. | negative | [#edge-cases-and-failure-modes](../../../design/pipeline/03-semantic-check.md#edge-cases-and-failure-modes) | [`ambiguous-reference-diagnostic.slang`](ambiguous-reference-diagnostic.slang) |
| When no candidate in the overload set matches the call's argument count and types, the call is rejected with a diagnostic. | negative | [#edge-cases-and-failure-modes](../../../design/pipeline/03-semantic-check.md#edge-cases-and-failure-modes) | [`no-applicable-overload-diagnostic.slang`](no-applicable-overload-diagnostic.slang) |
| A type mismatch at a coercion site is rejected with the type-mismatch (30019) diagnostic citing the expected and actual types. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`type-mismatch-rejected.slang`](type-mismatch-rejected.slang) |
| An unresolved name produces a diagnostic; the checker recovers by treating the reference as ErrorType so the failure does not cascade. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`undefined-identifier-becomes-error.slang`](undefined-identifier-becomes-error.slang) |
| Check-level recovery is "continue with a placeholder type" so one error does not cascade; an undefined identifier in one statement does not erase the diagnostic for an unrelated error in the next. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`recovery-after-error-does-not-cascade.slang`](recovery-after-error-does-not-cascade.slang) |
| Decl checking rejects a variable whose initializer depends on the variable itself ("used in its own definition"). | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`variable-used-in-own-definition-rejected.slang`](variable-used-in-own-definition-rejected.slang) |
| Declaring the same function name twice with different return types is rejected with the function-return-type-mismatch diagnostic (E30202). | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`function-return-type-mismatch-rejected.slang`](function-return-type-mismatch-rejected.slang) |
| Defining the same function twice is rejected (function-redefinition, 30201) with a note pointing at the previous definition. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`function-redefinition-diagnostic.slang`](function-redefinition-diagnostic.slang) |
| Inheritance checking rejects a struct that names itself as its base ("cyclic reference in inheritance graph"). | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`cyclic-inheritance-rejected.slang`](cyclic-inheritance-rejected.slang) |
| Member lookup on a struct without the requested name produces E30027 with the requested member and host type cited verbatim. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`member-not-found-on-struct-rejected.slang`](member-not-found-on-struct-rejected.slang) |
| Member lookup that finds no match is rejected with diagnostic 30027 naming the missing member and the host type. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`member-lookup-no-match.slang`](member-lookup-no-match.slang) |
| Slang's inheritance checker rejects a struct attempting to inherit from a built-in non-interface type, naming the offending base type. | negative | [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | [`inheritance-from-non-interface-rejected.slang`](inheritance-from-non-interface-rejected.slang) |
| Decl checking rejects a bit-field whose declared width exceeds the type's width (E31300). | boundary | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`bit-field-too-wide-rejected.slang`](bit-field-too-wide-rejected.slang) |
| Decl checking rejects a bit-field whose underlying type is not integral (E31301). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`bit-field-non-integral-rejected.slang`](bit-field-non-integral-rejected.slang) |
| Decl checking rejects an unsized member that is not the last member of a struct (E30070). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`unsized-member-must-be-last-rejected.slang`](unsized-member-must-be-last-rejected.slang) |
| Decl checking rejects two struct types declared with the same name as a conflicting declaration (E30200) at the type-decl shape. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`conflicting-struct-redeclaration-rejected.slang`](conflicting-struct-redeclaration-rejected.slang) |
| Declaration checking rejects two locals with the same name in the same block as a conflicting declaration. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`local-redeclaration-rejected.slang`](local-redeclaration-rejected.slang) |
| Expression checking detects a literal divide-by-zero in a constant expression and emits diagnostic 30002. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`divide-by-zero-rejected.slang`](divide-by-zero-rejected.slang) |
| Expression checking enforces lvalue-ness; assigning to a non-lvalue expression is rejected with diagnostic 30011. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`assign-to-non-lvalue-rejected.slang`](assign-to-non-lvalue-rejected.slang) |
| Expression checking rejects `new T` when T is a struct (E30065): `new` is only valid on classes. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`new-on-non-class-rejected.slang`](new-on-non-class-rejected.slang) |
| Expression checking rejects a `try` clause applied to a non-call expression (E30090). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`try-on-non-call-rejected.slang`](try-on-non-call-rejected.slang) |
| Expression checking rejects a bare assignment used as the predicate of an `if` statement (E30057). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`assignment-in-predicate-rejected.slang`](assignment-in-predicate-rejected.slang) |
| Expression checking rejects a call expression whose callee resolves to a non-callable type with diagnostic 30016. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`call-on-non-callable-rejected.slang`](call-on-non-callable-rejected.slang) |
| Expression checking rejects constructor-style initialization of a class without `new` (E30066): classes must be initialized with `new`. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`class-without-new-rejected.slang`](class-without-new-rejected.slang) |
| Expression checking rejects passing a non-lvalue argument to an `out` parameter (E30047). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`argument-must-be-lvalue-rejected.slang`](argument-must-be-lvalue-rejected.slang) |
| Expression checking rejects subscripting a non-array, non-subscriptable type (E30013). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`subscript-on-non-array-rejected.slang`](subscript-on-non-array-rejected.slang) |
| Implicit-conversion ranking inserts an int-to-float coercion at a typed initialization site so the value preserves the source value as float. | functional | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`implicit-int-to-float-conversion.slang`](implicit-int-to-float-conversion.slang) |
| Statement checking detects duplicate `case` values inside a switch and rejects with E30605. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`switch-duplicate-cases-rejected.slang`](switch-duplicate-cases-rejected.slang) |
| Statement checking detects functions that fall off the end without returning and emits the missing-return warning (41010). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`missing-return-warning.slang`](missing-return-warning.slang) |
| Statement checking rejects `break` inside a `defer` block (E30108). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`break-inside-defer-rejected.slang`](break-inside-defer-rejected.slang) |
| Statement checking rejects `case` outside a `switch` statement. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`case-outside-switch-rejected.slang`](case-outside-switch-rejected.slang) |
| Statement checking rejects `continue` inside a `defer` block (E30109). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`continue-inside-defer-rejected.slang`](continue-inside-defer-rejected.slang) |
| Statement checking rejects `continue` outside a loop with diagnostic 30004. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`continue-outside-loop-rejected.slang`](continue-outside-loop-rejected.slang) |
| Statement checking rejects `return` inside a `defer` block (E30110). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`return-inside-defer-rejected.slang`](return-inside-defer-rejected.slang) |
| Statement checking rejects an uncaught `try` inside a function not declared `throws` (E30093). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`uncaught-try-in-non-throw-rejected.slang`](uncaught-try-in-non-throw-rejected.slang) |
| Statement checking rejects more than one `default:` inside a single switch (E30602). | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`switch-multiple-default-rejected.slang`](switch-multiple-default-rejected.slang) |
| Statement checking validates `return` against the function's return type; a bare `return` in a non-void function is rejected. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`return-needs-expression-rejected.slang`](return-needs-expression-rejected.slang) |
| Statement checking validates control-flow scope rules; `break` outside a loop or switch is rejected with diagnostic 30003. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`break-outside-loop-rejected.slang`](break-outside-loop-rejected.slang) |
| Type checking rejects `void` as a variable type with diagnostic 30009. | negative | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`invalid-type-void-rejected.slang`](invalid-type-void-rejected.slang) |
| Type checking rejects an array declared with a negative size literal (E30025). | boundary | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`array-negative-size-rejected.slang`](array-negative-size-rejected.slang) |
| Type checking resolves a typedef'd name to its base type so an operation on the alias is the same as on the underlying type. | functional | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`typedef-resolves-to-base-type.slang`](typedef-resolves-to-base-type.slang) |
| Type resolution follows a chain of `typealias` declarations and resolves all the way to the underlying primitive type. | boundary | [#files-and-responsibilities](../../../design/pipeline/03-semantic-check.md#files-and-responsibilities) | [`typedef-chain-resolves.slang`](typedef-chain-resolves.slang) |
| A generic type used without specifying its arguments is rejected with a diagnostic explaining a generic argument is required. | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`generic-without-args-rejected.slang`](generic-without-args-rejected.slang) |
| A struct declared as conforming to an interface but missing a required member is rejected with a diagnostic naming the missing member. | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`missing-conformance-rejected.slang`](missing-conformance-rejected.slang) |
| Conformance checking rejects a member that overrides an interface default without the `override` keyword (E30853). | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`missing-override-rejected.slang`](missing-override-rejected.slang) |
| Constraint checking rejects a generic value parameter whose type is not an integer or enum (E30624). | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`generic-value-parameter-type-rejected.slang`](generic-value-parameter-type-rejected.slang) |
| Constraint solving rejects a non-interface type used as a generic constraint (E30401). | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`invalid-type-for-constraint-rejected.slang`](invalid-type-for-constraint-rejected.slang) |
| Decl checking rejects a generic parameter declared after a variadic pack parameter (E30522). | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`pack-param-must-be-last-rejected.slang`](pack-param-must-be-last-rejected.slang) |
| Generic functions are specialized at the call site; the constraint solver infers the type argument from the actual argument. | functional | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`generic-argument-inferred-from-call.slang`](generic-argument-inferred-from-call.slang) |
| Inheritance checking rejects an `extension` directly on an interface type (E30852). | negative | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`invalid-extension-on-interface-rejected.slang`](invalid-extension-on-interface-rejected.slang) |
| The checker finds a witness that a type satisfies an interface required by a constraint, so a generic constrained on the interface accepts conforming types. | functional | [#generic-specialization-and-constraints](../../../design/pipeline/03-semantic-check.md#generic-specialization-and-constraints) | [`interface-conformance-checked.slang`](interface-conformance-checked.slang) |
| After checking, a DeclRef-bearing node points at the canonical decl; a name used after its declaration resolves to that decl and the call returns the expected value. | functional | [#inputs-and-outputs](../../../design/pipeline/03-semantic-check.md#inputs-and-outputs) | [`declref-points-at-canonical-decl.slang`](declref-points-at-canonical-decl.slang) |
| The checker attaches a Type to every Expr; arithmetic between int and float coerces to float and the result type matches that. | functional | [#inputs-and-outputs](../../../design/pipeline/03-semantic-check.md#inputs-and-outputs) | [`expr-gets-type-attached.slang`](expr-gets-type-attached.slang) |
| Member lookup walks the inheritance chain so a method declared on an interface is callable through a generic constrained on that interface. | functional | [#member-lookup](../../../design/pipeline/03-semantic-check.md#member-lookup) | [`inheritance-walk-finds-base-member.slang`](inheritance-walk-finds-base-member.slang) |
| A [mutating] method cannot be invoked on an immutable receiver; the checker rejects the call with diagnostic 30050. | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`mutating-method-on-immutable-rejected.slang`](mutating-method-on-immutable-rejected.slang) |
| Decl checking rejects a `static` global variable with an opaque (resource) type (E30076). | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`global-var-opaque-type-rejected.slang`](global-var-opaque-type-rejected.slang) |
| Modifier validation enforces the immutability of `let`-bound locals; assigning to one is rejected as a non-lvalue. | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`assign-to-let-is-rejected.slang`](assign-to-let-is-rejected.slang) |
| Modifier validation rejects a `[SpecializationConstant]` on a non-scalar/non-enum value (E31218). | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`spec-constant-must-be-scalar-rejected.slang`](spec-constant-must-be-scalar-rejected.slang) |
| Modifier validation rejects a redundant repetition of the same modifier (E31202) on one declaration. | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`duplicate-modifier-rejected.slang`](duplicate-modifier-rejected.slang) |
| Modifier validation rejects an attribute placed on a target where it is not allowed (E31002). | negative | [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | [`attribute-not-applicable-rejected.slang`](attribute-not-applicable-rejected.slang) |
| Operator overloading: a user-defined `operator+` on a struct is found by overload resolution at a `+` expression with that struct's argument types. | functional | [#operator-overloading](../../../design/pipeline/03-semantic-check.md#operator-overloading) | [`operator-overload-on-struct.slang`](operator-overload-on-struct.slang) |
| Attribute argument checking rejects a non-string-literal argument to `[shader(...)]` (E33071). | negative | [#shader-specific-checks](../../../design/pipeline/03-semantic-check.md#shader-specific-checks) | [`expected-string-literal-attr-rejected.slang`](expected-string-literal-attr-rejected.slang) |
| Attribute-value checking rejects a non-positive integer in `[numthreads]` (E31102). | boundary | [#shader-specific-checks](../../../design/pipeline/03-semantic-check.md#shader-specific-checks) | [`numthreads-non-positive-rejected.slang`](numthreads-non-positive-rejected.slang) |
| Entry-point checks require a stage attribute on the function or a -stage command-line option; without either the checker emits diagnostic 38007. | negative | [#shader-specific-checks](../../../design/pipeline/03-semantic-check.md#shader-specific-checks) | [`entry-point-needs-stage-rejected.slang`](entry-point-needs-stage-rejected.slang) |
| Initializer-list checking rejects more initializers than the synthesized constructor accepts (E30523). | boundary | [#synthesizing-implicit-code](../../../design/pipeline/03-semantic-check.md#synthesizing-implicit-code) | [`too-many-initializers-rejected.slang`](too-many-initializers-rejected.slang) |
| The checker synthesizes implicit construction code (e.g. an initializer-list constructor) so a struct can be built from a brace-enclosed value list without an explicit `__init`. | functional | [#synthesizing-implicit-code](../../../design/pipeline/03-semantic-check.md#synthesizing-implicit-code) | [`synthesized-positional-constructor.slang`](synthesized-positional-constructor.slang) |
| The checker synthesizes nested initializer-list construction so a struct containing another struct can be initialized by a brace-enclosed list of brace-enclosed lists. | boundary | [#synthesizing-implicit-code](../../../design/pipeline/03-semantic-check.md#synthesizing-implicit-code) | [`synthesized-positional-constructor-nested.slang`](synthesized-positional-constructor-nested.slang) |
| Function bodies are left as UnparsedStmt by the parser and checked on demand; a well-formed body parses and runs. | functional | [#two-pass-interaction-with-the-parser](../../../design/pipeline/03-semantic-check.md#two-pass-interaction-with-the-parser) | [`function-body-checked-after-parser-handoff.slang`](function-body-checked-after-parser-handoff.slang) |


## Untested claims
NA


## Doc gaps observed
| Anchor | Kind | Gap | Suggested addition |
| --- | --- | --- | --- |
| [#synthesizing-implicit-code](../../../design/pipeline/03-semantic-check.md#synthesizing-implicit-code) | undocumented-behavior | The `## Synthesizing implicit code` section names "generated comparison / construction methods" as a category but does not enumerate which comparison operators are auto-synthesized (`==`, `!=`, `<`, `>`, `<=`, `>=`) nor which structural rules trigger them. The bundle tests the construction half (initializer- list constructor) but not comparison-method synthesis; a doc enumeration would let the bundle grow exhaustively in that area. |  |
| [#shader-specific-checks](../../../design/pipeline/03-semantic-check.md#shader-specific-checks) | undocumented-behavior | The `## Shader-specific checks` section lists "parameter modifiers (`in`, `out`, `inout` and stage-specific intrinsics)" and "return type compatibility with the stage" as checked, but the doc does not promise specific diagnostic codes or message text. The `entry-point-needs-stage` test anchors to the one concrete claim (a stage attribute or `-stage` is required); the broader parameter-modifier / return-type-per-stage claims need a doc enumeration before a test can anchor to them. |  |
| [#modifier-validation](../../../design/pipeline/03-semantic-check.md#modifier-validation) | undocumented-behavior | The `## Modifier validation` section says slang-check-modifier.cpp validates "which modifiers are allowed on which decls, mutually exclusive combinations, attribute argument types, and HLSL-vs-Slang dialect differences". No specific mutually-exclusive pair is named in the doc. Tests for individual combos (e.g. `static [mutating]`) would be anchoring to undocumented behavior; deferred until the doc lists at least one explicit exclusion. |  |
| [#failure-modes](../../../design/pipeline/03-semantic-check.md#failure-modes) | undocumented-behavior | The `## Failure modes` section says overload resolution "returns a synthetic `errorExpr` rather than aborting". The user-visible consequence is "compilation continues past the offending site" — the `recovery-after-error-does-not-cascade` test exercises the general claim, but the doc has no observable surface that specifically distinguishes "synthetic errorExpr" from any other error-recovery placeholder. |  |
| [#semanticsvisitor](../../../design/pipeline/03-semantic-check.md#semanticsvisitor) | undocumented-behavior | The `## SemanticsVisitor` and `## Files and responsibilities` sections describe the architectural split (which `.cpp` file handles which concern). Most of those claims are not directly observable from outside the compiler — the bundle anchors negative tests to the file table by treating each row's "concern" as a testable surface claim (e.g. "statement checking validates control-flow rules"). A stronger anchoring would be to add a per-concern subsection enumerating exactly which user-visible behaviors live in each file. |  |
| [#name-resolution](../../../design/pipeline/03-semantic-check.md#name-resolution) | undocumented-behavior | The doc points readers to [`../name-resolution/`](../../../design/name-resolution/) for the lookup algorithm but does not restate any specific consequences in this file. The bundle therefore anchors a handful of tests directly to `lookup.md` and `overload-resolution.md` (which the per-section prompt explicitly permits). If the source doc were extended with one-line summaries of each user-visible lookup behavior, those tests could re-anchor here. |  |
