Skip to content
26 changes: 23 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLAUDE.md
# AGENTS.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Shared guidance for code agents (Claude, Gemini, Codex, etc.) working in this repository. The files `CLAUDE.md` and `GEMINI.md` are symlinks to this document. For Codex‑specific workflow details also see `CODEX.md`.

## Project Overview

Expand Down Expand Up @@ -136,4 +136,24 @@ The compiler is designed for fast feedback loops and scales to large codebases.
- Avoid introducing meaningless symbols
- Maintain readable JavaScript output
- Consider compilation speed impact
- Use appropriate optimization passes in the Lambda and JS IRs
- Use appropriate optimization passes in the Lambda and JS IRs

## Agent Notes and Gotchas

- **Node version features:** Runtime docstring tests conditionally enable features by Node major version:
- 20+: array `toReversed`/`toSorted`
- 22+: new `Set` APIs and `Promise.withResolvers`
- 24+: `RegExp.escape`
Tests auto‑skip unsupported features based on `process.version`.
- **CPU count in sandboxes:** Some CI/sandboxed environments report `os.cpus().length === 0`.
- Clamp concurrency/batch size to at least 1 when using `os.cpus()`.
- This pattern is used in `tests/docstring_tests/DocTest.res` and `cli/rescript-legacy/format.js`.
- **Formatting in tests:** `make test` checks formatting (OCaml, ReScript, JS, Rust). If it fails locally, run `make format` and re‑run tests.
- **Executables location:** Build copies platform binaries into `packages/@rescript/<platform>/bin/` and convenience folders like `darwinarm64/`.
- **Direct dune usage:** You can use `dune build`/`dune build -w`, but prefer `make` targets which also copy executables.

## References

- `CODEX.md`: detailed setup, build, testing, and workflows for agented development
- `README.md`: high‑level repo overview and usage
- `Makefile`: authoritative list of build/test targets
30 changes: 30 additions & 0 deletions compiler/ml/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3018,6 +3018,36 @@ and type_expect_ ~context ?in_function ?(recarg = Rejected) env sexp ty_expected
raise (Error (loc, env, Not_subtype (tr1, tr2, ctx))));
(arg, ty', cty')
in
(* After a successful coercion, if we coerced between concrete variant
types, mark the intersection of constructors as positively used for the
target type to avoid spurious "constructor ... is never used" warnings
when values are introduced via coercions. *)
(match
( (try Some (Ctype.extract_concrete_typedecl env arg.exp_type)
with Not_found -> None),
try Some (Ctype.extract_concrete_typedecl env ty')
with Not_found -> None )
with
| Some (_p_src, _p_src_conc, src_decl), Some (_p_tgt, p_tgt_conc, tgt_decl)
-> (
match (src_decl.type_kind, tgt_decl.type_kind) with
Copy link
Member

Choose a reason for hiding this comment

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

These matches could be done in one match, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@codex can this be done in one match?

Choose a reason for hiding this comment

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

| Type_variant src_cons, Type_variant tgt_cons ->
let src_names =
List.map
(fun (c : Types.constructor_declaration) -> Ident.name c.cd_id)
src_cons
in
let has_src name = List.exists (fun n -> n = name) src_names in
let tgt_ty_name = Path.last p_tgt_conc in
Copy link
Member

Choose a reason for hiding this comment

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

Use StringSet instead for perf for large amounts of constructors?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@codex Use StringSet instead for perf for large amounts of constructors?

Choose a reason for hiding this comment

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

List.iter
(fun (c : Types.constructor_declaration) ->
let cname = Ident.name c.cd_id in
if has_src cname then
Env.mark_constructor_used Env.Positive env tgt_ty_name tgt_decl
cname)
tgt_cons
| _ -> ())
| _ -> ());
rue
{
exp_desc = arg.exp_desc;
Expand Down
6 changes: 5 additions & 1 deletion tests/docstring_tests/DocTest.res
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ let extractDocFromFile = async file => {
}
}

let batchSize = OS.cpus()->Array.length
// Some environments may report 0 CPUs; clamp to at least 1 to avoid zero-sized batches
let batchSize = switch OS.cpus()->Array.length {
| n if n > 0 => n
| _ => 1
}

let runtimePath = Path.join(["packages", "@rescript", "runtime"])

Expand Down
4 changes: 3 additions & 1 deletion tests/docstring_tests/DocTest.res.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions tests/tests/src/VariantCoercionConstructUsed.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Generated by ReScript, PLEASE EDIT WITH CARE


function upcast(x) {
return x;
}

export {
upcast,
}
/* No side effect */
12 changes: 12 additions & 0 deletions tests/tests/src/VariantCoercionConstructUsed.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Repro for incorrect "constructor ... is never used" warning with coercions
// This should compile cleanly without warnings when coercing from a -> b.

type a = A | B

type b =
| ...a
| C

let upcast = (x: a): b => (x :> b)

let _ = upcast(A)
Loading