Skip to content

ast,daslib: fix spurious 30151 on concept_assert under lint flags (#2830)#2835

Merged
borisbat merged 1 commit into
masterfrom
bbatkin/issue-2830-lint-static-assert-cond-fold
May 23, 2026
Merged

ast,daslib: fix spurious 30151 on concept_assert under lint flags (#2830)#2835
borisbat merged 1 commit into
masterfrom
bbatkin/issue-2830-lint-static-assert-cond-fold

Conversation

@borisbat
Copy link
Copy Markdown
Collaborator

Closes #2830.

Two independent bugs the lint pipeline surfaced together

The linter runs with cop.no_infer_time_folding = true + cop.no_optimizations = true. Bisect (toggle each lint policy off in turn) localizes no_infer_time_folding as the sole trigger. With infer-time folding off, two distinct paths in the codebase that quietly relied on it now break:

1. Compiler: concept_assert / static_assert cond never folds

typeinfo sizeof(T) <= typeinfo sizeof(U) rewrites itself to ExprConstInt <= ExprConstInt at infer time, but InferTypes::visit(ExprOp1/Op2/Op3) (ast_infer_type_op.cpp:106, :365, :405) only folds when enableInferTimeFolding is on. With lint disabling it and no_optimizations skipping ConstFolding, the binop stays unfolded; ContractFolding::visit(ExprStaticAssert*) (ast_const_folding.cpp:848-852) demands cond->constexpression || cond->rtti_isConstant() and raises the spurious 30151.

Fix mirrors the existing static_if save+force-enable+restore pattern (ast_infer_type.cpp:4418-4422, :4428-4430) inside preVisit(ExprStaticAssert*) / visit(ExprStaticAssert*), so the cond subtree always folds regardless of the surrounding policy.

2. Daslib: 6 sites if (typeinfo X) instead of static_if

These relied on the same infer-time fold of ExprIfThenElse (ast_infer_type.cpp:4444) to elide the dead branch — whose body references fields/operations only valid in the true branch's universe. Under lint flags, both branches survive and the dead one fails to resolve. The decs_boost case was what the bug exposed after the compiler fix landed:

daslib/decs_boost.das:245:36
        qloop.iteratorsAka[qli] := a._aka
                                    ^   field '_aka' not found in ast_core::FieldDeclaration const

Convert all 6 to static_if:

  • daslib/decs_boost.das:244if (typeinfo has_field<_aka>(a))
  • daslib/builtin.das:403if (typeinfo can_clone_from_const(varr))
  • daslib/builtin.das:892if (typeinfo can_clone_from_const(val))
  • daslib/builtin.das:914if (typeinfo can_clone_from_const(val))
  • daslib/builtin.das:1183if (typeinfo is_pod(a))
  • daslib/json_boost.das:477if (typeinfo can_copy(anything[0]))

"Linux x64 only"?

CI artifact: extended_checks gates the lint step to matrix.target == 'linux'. macOS / Windows builds never invoke utils/lint/main.das. Verified the bug platform-independent locally on Windows daslang.exe.

Also rolled in: 2 STYLE028 in decs_boost.das

Pre-existing master lint hits (self->implement(...)implement(...)), forced into scope by "every changed .das file lint-clean" once the PR touches that file.

Regression coverage

tests/_issue_2830_lint_repro.das — minimal [decs_template] + from_decs_template + _where fixture. CI lint runs on changed .das files, so the fixture exercises the failing path on this PR. Without the compiler fix, lint fails at the concept_assert 30151. Without the static_if sweep, lint fails at the _aka resolution 30928. Both fixes required for the fixture to pass.

Test plan

  • Bisect localizes cop.no_infer_time_folding = true as the single trigger
  • WSL Ubuntu2404-CI (clang 18.1.3, Release): lint clean on all 4 repro variants (bare [decs_template], +where one field, +where multi-field, +linq_fold count chain)
  • WSL regression: tests/decs 245/245, tests/json 266/266, tests/lint 8/8, utils/lint/tests 38/38
  • Windows MSVC Release: MCP lint clean on all 4 changed .das files
  • Pre-push hook: formatter --verify clean on 16695 files; lint clean on 3 ruleable changed files (builtin.das is in the lint utility's skip list)
  • CI 9-lane matrix
  • Copilot review

🤖 Generated with Claude Code

)

Two independent bugs surfaced together by the linter's
`no_infer_time_folding=true` + `no_optimizations=true` policy combo.

1. Compiler: `concept_assert` / `static_assert` cond never gets folded.
   `typeinfo sizeof(T) <= typeinfo sizeof(U)` rewrites to `ExprConstInt <=
   ExprConstInt` at infer time, but `InferTypes::visit(ExprOp1/Op2/Op3)`
   only folds when `enableInferTimeFolding` is on. With lint disabling
   it and `no_optimizations` skipping `ConstFolding`, the binop stayed
   unfolded and `ContractFolding::visit(ExprStaticAssert*)` raised the
   spurious 30151. Mirror the existing static_if save+force-enable+
   restore pattern in `preVisit`/`visit(ExprStaticAssert*)` so the cond
   subtree always folds.

2. Daslib: 6 sites used `if (typeinfo X)` instead of `static_if`. These
   relied on the same infer-time folding to elide the dead branch
   (whose body references fields/operations only valid in the true
   branch's universe). Under lint flags, both branches survive and
   the dead one fails to resolve. Convert all to `static_if`:
   `decs_boost.das:244` (`a._aka`), `builtin.das:403/892/914/1183`,
   `json_boost.das:477`.

`extended_checks` gates the lint step to `matrix.target == 'linux'`, so
this surfaced as "linux x64 only" in CI; verified platform-independent
locally on Windows daslang.exe.

Also: 2 pre-existing STYLE028 hits in `decs_boost.das` (`self->implement`
-> `implement`), required by the "every changed .das file lint-clean" PR
rule once the PR touches that file.

Regression fixture: `tests/_issue_2830_lint_repro.das`. CI lint runs
over changed .das files; the fixture exercises the failing path on
this PR and continues to exercise it on any future PR that touches it.

Verified locally on WSL Ubuntu2404-CI (clang 18.1.3, Release):
  - lint clean on all 4 repro variants
  - tests/decs   245/245 pass
  - tests/json   266/266 pass
  - tests/lint   8/8 + utils/lint/tests 38/38 pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 13:43
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a lint-mode regression where disabling infer-time folding (cop.no_infer_time_folding=true) + disabling optimizations (cop.no_optimizations=true) caused concept_assert/static_assert conditions and some typeinfo-guarded branches in daslib to stop folding, producing spurious typer/contract-folding errors (issue #2830).

Changes:

  • Compiler: force-enable infer-time folding for ExprStaticAssert condition subtrees via a save/restore pattern in InferTypes::preVisit/visit.
  • Daslib: replace 6 if (typeinfo ...) sites with static_if (typeinfo ...) so dead branches are eliminated even when infer-time folding is off.
  • Tests: add a minimal lint regression fixture to ensure the failing path is exercised by CI lint on changed .das files.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/_issue_2830_lint_repro.das Adds a lint regression fixture reproducing the issue #2830 failure mode.
src/ast/ast_infer_type.cpp Save/force-enable/restore infer-time folding around ExprStaticAssert traversal to ensure conditions fold under lint policies.
include/daScript/ast/ast_infer_type.h Adds savedFoldingForStaticAssert state to support the new save/restore logic.
daslib/json_boost.das Converts a typeinfo-guarded if to static_if to avoid typing the dead branch under lint flags.
daslib/decs_boost.das Converts if (typeinfo has_field<_aka>(a)) to static_if and fixes two STYLE028 call-site issues.
daslib/builtin.das Converts several if (typeinfo ...) sites to static_if to ensure compile-time elimination under lint flags.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@borisbat borisbat merged commit 2569c6a into master May 23, 2026
48 of 49 checks passed
borisbat added a commit that referenced this pull request May 23, 2026
#2835 fixed the typer-pass-order #2830 that originally tripped this test
on the extended_checks (linux, 64) lane. With master now containing the
fix, the test compiles cleanly on all lanes. Re-adding it covers a case
the current suite missed:

  from_decs_template(type<Row>)._where(_.a >= 0)._where(_.b >= 0).count()

Three chained single-field _where_s — all 3 fields read via field access,
no whole-var ref. The splice must keep all 3 get_ros (no slot pruning)
but elide the named-tuple bind (no decs_tup in the body, iter vars read
directly). Lesson saved to memory: not every CI lane runs every test, so
"platform-specific" failures often mean "we only check this on one
platform" — not that the bug itself is platform-specific.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@borisbat borisbat deleted the bbatkin/issue-2830-lint-static-assert-cond-fold branch May 30, 2026 15:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: multi-field _where predicate over from_decs_template triggers spurious error[30151] (concept_assert in decs.set()) — linux x64 only

2 participants