Commit 6bc22ae
authored
## Motivation
An associated `static const` interface requirement (an *associated
constant*) is folded to a
different compile-time value depending on which path reaches it, so
otherwise-valid code that uses
the constant in an array size is rejected.
Concretely, given:
```slang
interface IAssocConst { static const uint COUNT; }
struct Data<let N : uint> : IAssocConst
{
static const uint COUNT = N;
float values[N];
}
struct Wrapper<T : IAssocConst>
{
static const uint COUNT = T::COUNT;
uint sum(float[T::COUNT] input) { /* ... */ } // signature-type position
}
static const let VALUE = Wrapper<Data<3>>();
float[VALUE::COUNT] makeInput() { /* ... */ } // signature-type position
```
When `T::COUNT` / `VALUE::COUNT` is reached from a **signature-type**
position (an array size in a
parameter or return type), the constant is left as a *symbolic*
`WitnessLookupIntVal`
(`int(Data<3>.COUNT)`). When the same constant is reached from the
**generic-substitution** path it
folds to the literal `3`. The two `IntVal`s intern to different syntax
classes
(`WitnessLookupIntVal` vs the literal), so they never compare equal, and
the two array sizes are
reported as mismatched — `E30019` (type mismatch, ×2) plus a cascading
`E30523` ("too many
initializers"). This is issue #6703.
## Proposed solution
Fold the associated constant to the same concrete value on both paths by
ensuring the conforming
type's conformances exist *before* the early signature-type fold, then
re-folding.
The producer is the interface-requirement branch of
`SemanticsVisitor::tryConstantFoldDeclRef`
(`source/slang/slang-check-expr.cpp`). The first
`WitnessLookupIntVal::tryFold` can run before the
conforming type's conformance witness tables have been built; in that
window the requirement lookup
reaches an `InheritanceDecl::witnessTable` that is still null, so
`tryFold` leaves the symbolic
`WitnessLookupIntVal`. The fix: when the first fold yields a symbolic
result and the witness
sub-type is a `DeclRefType`, call `ensureDecl(sub,
DeclCheckState::ReadyForConformances)` and
re-fold. After conformance state exists the requirement lookup resolves
to the concrete
`RequirementWitness::Flavor::val`, so the first value built for the
requirement is the literal
constant — identical to the substitution path.
This is the principled layer: the bug is a *phase-ordering* defect at
the producer, not a mismatch
to paper over at the consumer. It does **not** introduce a new
witness/`Val` representation or
alter lowering / witness-table construction; it makes this producer use
the already-existing
concrete constant `Val` that the substitution path already produces,
instead of leaving the
symbolic lookup (so it respects the one-canonical-representation rule
and stays lowering-neutral).
It only forces the conformance state the fold depends on to be present,
then re-folds. Patching
`IntVal`/`ArrayExpressionType` equality was rejected:
`getOrCreate` interns on syntax class, so a symbolic
`WitnessLookupIntVal` can never be made equal
to a literal — the fold must be fixed, not the comparison.
## Change summary
| File | Change |
| --- | --- |
| `source/slang/slang-check-expr.cpp` | In `tryConstantFoldDeclRef`'s
interface-requirement branch: if `tryFold` yields a symbolic
`WitnessLookupIntVal` and the witness sub is a `DeclRefType`,
`ensureDecl(sub, ReadyForConformances)` and re-fold. |
|
`tests/language-feature/constants/associated-const-in-signature-type.slang`
| New CPU `COMPARE_COMPUTE` regression test exercising the associated
constant from signature-type, value-access, and generic-argument
positions. |
## Concepts and vocabulary
- **Associated constant** — a `static const` requirement declared in an
`interface`, satisfied by
each conforming type (here `Data<N>.COUNT`).
- **`WitnessLookupIntVal`** — the symbolic `IntVal` for "the value of
requirement `R` via witness
`W`". `tryFold`/`tryFoldOrNull` collapse it to the concrete value
**only** when the requirement
lookup yields `RequirementWitness::Flavor::val`; otherwise the symbolic
form survives.
- **`InheritanceDecl::witnessTable`** — the per-conformance
requirement→witness dictionary,
populated during conformance checking. It can still be null before the
conforming type reaches
`DeclCheckState::ReadyForConformances`; the requirement lookup the fold
relies on reads it.
- **Signature-type position** — an array size appearing in a function
parameter/return type or a
global declaration type, which the checker folds early (before the
conforming type's conformance
tables are built) — the window in which the bug occurs.
## Process report
**The one change — re-fold after ensuring conformance state.** Local
instrumentation at the
producer (since removed) established that the witness reaching the
failing fold is already a
`DeclaredSubtypeWitness` (the
gate in `getUnspecializedLookupRec` only handles that class), that
`witness->resolve()` is a no-op
on it, and that re-folding the resolved witness still returns null. So
canonicalizing/resolving the
witness does **not** address the defect — ruling out the "normalize the
witness" direction. The
true discriminator is downstream: on the failing (signature-type) folds
the requirement lookup
reaches a null `InheritanceDecl::witnessTable`, while on the succeeding
generic-substitution folds
the table is populated. The producer simply runs too early for
signature-type positions. Forcing
`DeclCheckState::ReadyForConformances` on the conforming type's decl and
re-folding makes the
returned value the literal constant.
*Input-shape check (per methodology):* the input here — a symbolic
`WitnessLookupIntVal` emerging
from a fold that ran before conformance checking — is **not** a
correct/principled shape to consume;
it is an accident of checking order. The fix corrects the producer
(folds to the concrete constant
once state exists) rather than teaching a downstream consumer to
tolerate the symbolic form. The
retry path is limited to cases where the initial fold remains symbolic
(`as<WitnessLookupIntVal>`)
and the witness sub-type is a `DeclRefType`. Folds that already produce
a concrete value do not
enter the retry path.
**Verification** (run locally with a debug `slang-test`; CI re-runs the
full suite):
- New test `associated-const-in-signature-type.slang` passes on `-cpu`
(`1/1`).
- The original issue repro no longer emits `E30019`/`E30523`.
- RED check: stashing the fix and rebuilding makes the new test fail and
the repro emit
`E30019`/`E30523` again — confirming the test pins the bug.
- Regression sweeps clean: `constants` (28/28), `generics` (169/169);
and the conformance-order
sensitive areas `autodiff`, `dynamic-dispatch`, `inheritance`,
`interface` (no regressions).
Closes #6703.
---
<sub>🤖 Generated by an automated Slang coworker — may be inaccurate. A
human maintainer should verify.</sub>
---------
Co-authored-by: nv-slang-bot[bot] <274397474+nv-slang-bot[bot]@users.noreply.github.com>
1 parent efd7952 commit 6bc22ae
2 files changed
Lines changed: 81 additions & 5 deletions
File tree
- source/slang
- tests/language-feature/constants
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2759 | 2759 | | |
2760 | 2760 | | |
2761 | 2761 | | |
2762 | | - | |
2763 | | - | |
2764 | | - | |
2765 | | - | |
2766 | | - | |
| 2762 | + | |
| 2763 | + | |
| 2764 | + | |
| 2765 | + | |
| 2766 | + | |
| 2767 | + | |
| 2768 | + | |
| 2769 | + | |
| 2770 | + | |
| 2771 | + | |
| 2772 | + | |
| 2773 | + | |
| 2774 | + | |
| 2775 | + | |
| 2776 | + | |
| 2777 | + | |
| 2778 | + | |
| 2779 | + | |
2767 | 2780 | | |
2768 | 2781 | | |
2769 | 2782 | | |
| |||
Lines changed: 63 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
0 commit comments