Skip to content

Wire up dialyzer; address all warnings - #37

Merged
ausimian merged 1 commit into
mainfrom
dialyzer-cleanup
Apr 18, 2026
Merged

Wire up dialyzer; address all warnings#37
ausimian merged 1 commit into
mainfrom
dialyzer-cleanup

Conversation

@ausimian

Copy link
Copy Markdown
Owner

Summary

  • Adds :dialyxir as a dev-only dep with a lib-only PLT (priv/plts/, gitignored) — opt-in via mix dialyzer, not hooked into precommit.
  • Narrows @type dtype in Emily.Native / Emily to Nx.Type.t(), fixes one :ets.new/2 unmatched return in Emily.Telemetry, and suppresses 5 genuinely-raises / one-off NIF-boundary quirks with per-function @dialyzer {:nowarn_function, ...} directives.
  • Adds .dialyzer_ignore.exs for ~123 false-positive callback / contract warnings that stem from dialyzer's inability to track Nx.Tensor struct fields across the shared wrap/3 map update and through Nx.Defn.Expr.optional/3 (unspec'd upstream). Each ignore cluster is commented with its root cause so future readers know why it's there.

mix dialyzer now reports done (passed successfully) with 123 skipped, 0 unskipped.

Test plan

  • mix dialyzer exits clean (0 unskipped warnings).
  • mix precommit passes (compile, deps.unlock, format, credo --strict, test).
  • mix test --include conformance --include qwen3_full --include qwen3_quant_full --include vit_full --include whisper_full --include distilbert_full --include training_full --include fast_kernels_full — 449 tests, 76 properties, 1 doctest, 0 failures (151s on my machine).

Adds :dialyxir as a dev-only dep with a lib-only PLT (priv/plts/,
gitignored) and a curated .dialyzer_ignore.exs. Not hooked into
precommit — runnable on demand via `mix dialyzer`.

Real type fixes:

  - lib/emily/native.ex, lib/emily.ex: narrow `@type dtype` to
    Nx.Type.t() (was {atom(), non_neg_integer()}) so callsites stop
    widening through wrap/3.
  - lib/emily/telemetry.ex:118: capture the unmatched :ets.new/2
    return explicitly.
  - lib/emily/backend.ex: @dialyzer {:nowarn_function, ...} for the
    four genuinely-unimplementable callbacks (from_pointer, to_pointer,
    count_leading_zeros, population_count) and for select/4, which
    passes Nx's internal {:pred, 1} dtype through Native.astype.

Suppressed false positives (see .dialyzer_ignore.exs for rationale):

  - Emily.Backend callback_type_mismatch / callback_arg_type_mismatch
    and the related wrap/3 invalid_contract — driven by the map-update
    pattern `%T{out | data: ...}` narrowing the inferred return to an
    open map, losing :shape / :names / :vectorized_axes field tracking.
    The code is correct; dialyzer's inference can't follow the field
    types through a single shared wrap helper across ~115 callbacks.
  - Emily.Fast invalid_contract — Nx.Defn.Expr.optional/3 has no
    upstream @SPEC, so dialyzer infers a sloppy `tuple() | %{...}`
    union for our hook functions' return.
  - Emily.Quantization / Emily.QuantizedWeight invalid_contract — same
    class: explicit %T{} construction from NIF-returned refs gives
    dialyzer a wider inferred type than Nx.Tensor.t().
@ausimian
ausimian merged commit b152409 into main Apr 18, 2026
1 check passed
@ausimian
ausimian deleted the dialyzer-cleanup branch April 18, 2026 06:08
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.

1 participant