Skip to content

SC2011: any-typed local rejected by build --dynamic even though coverage --dynamic reports the site as an eligible island site #290

Description

@mmamedel

Environment

  • scriptc: 0.0.36
  • OS: macOS 26.5 (arm64), Darwin 25.5.0
  • Node: v24.20.0
  • Install: npm install scriptc@0.0.36 (bin: node_modules/scriptc/dist/bootstrap.js)

Summary

An any-typed local variable used with an operator inside a function body is rejected by scriptc build --dynamic with SC2011, even though scriptc coverage --dynamic on the same file classifies that exact statement as a "runs with --dynamic" (dynamically-eligible) site rather than a rejected blocker. The identical operation on an any-typed function parameter builds and runs correctly under --dynamic. coverage and build --dynamic disagree about whether the local-variable site is coverable.

Repro — failing shape (local variable)

local-any.ts:

function f(): number {
  const x: any = JSON.parse("41");
  return x + 1;
}

console.log(f());
$ scriptc build local-any.ts --dynamic
local-any.ts:3:10 - error SC2011: the '+' operator on 'any'-typed values runs in the embedded dynamic engine, which this build does not include

  2 |   const x: any = JSON.parse("41");
  3 |   return x + 1;
    |          ^~~~~
  4 | }

  hint: build with --dynamic to run 'any'-typed code in the embedded engine (adds ~620KB to the binary), or stay static with 'unknown' and a checked cast ('x as T')

1 error.

scriptc run local-any.ts --dynamic fails identically.

$ scriptc coverage local-any.ts --dynamic
scriptc coverage local-any.ts

  statements analyzed   3
  compile statically    2  (66%)
  compile dynamically   0  (0%) (island sites — the embedded engine runs them)

  runs with --dynamic   1 site (embeds a JS engine, ~620KB — static stays the default)
      ×1  the '+' operator on 'any'-typed values runs in the embedded dynamic engine, which this build does not include  SC2011

coverage puts this SC2011 site under "runs with --dynamic" (the dynamically-capable bucket, not the "blockers:" section) — i.e. it reports the site as coverable by --dynamic. build --dynamic rejects the same site with the same SC2011 instead of lowering it to the embedded engine.

Working shape (function parameter) — for comparison

param-any.ts:

function dynamicProof(v: any): number {
  return v + 1;
}

console.log(dynamicProof(JSON.parse("41")));
$ scriptc build param-any.ts --dynamic -o param-any.exe
param-any.exe

$ ./param-any.exe
42
$ scriptc coverage param-any.ts --dynamic
scriptc coverage param-any.ts

  statements analyzed   2
  compile statically    1  (50%)
  compile dynamically   1  (50%) (island sites — the embedded engine runs them)

  builds with --dynamic — no remaining blockers (the island sites above run in the embedded engine).

Here the any value crosses a function-parameter boundary instead of staying in a local, coverage reports "no remaining blockers", and build --dynamic builds and runs correctly (prints 42).

Expected

For the local-variable shape, either:

  • build --dynamic lowers the x + 1 site to the embedded dynamic engine and builds/runs successfully (matching what coverage --dynamic reports), or
  • coverage --dynamic lists the site under blockers: (not under "runs with --dynamic") so the two commands agree that the site is not actually dynamically coverable.

Actual

coverage --dynamic classifies the site as dynamically-eligible ("runs with --dynamic", SC2011 in the dynamic-capable bucket), but build --dynamic (and run --dynamic) reject the exact same statement with SC2011, saying the operator "runs in the embedded dynamic engine, which this build does not include" — despite --dynamic being passed and the engine being embedded (as proven by the parameter-boundary shape building and running with the same flag).

Workaround

Move the any value across a function-parameter boundary (pass it into a function that takes an any parameter and operate on the parameter there) instead of binding it to a local const/let before operating on it. This builds and runs correctly under --dynamic.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions