Skip to content

feat(motoko): emit type alias names as PascalCase#729

Open
Kamirus wants to merge 5 commits intomasterfrom
motoko-upper-camel-case-types
Open

feat(motoko): emit type alias names as PascalCase#729
Kamirus wants to merge 5 commits intomasterfrom
motoko-upper-camel-case-types

Conversation

@Kamirus
Copy link
Copy Markdown
Contributor

@Kamirus Kamirus commented May 7, 2026

This is motivated by caffeinelabs/mops-ic#7, where I plan to set up automatic regeneration of the management canister Motoko package — idiomatic naming is essential when the output is consumed directly without manual editing.

What

Generated Motoko bindings now render type alias names in PascalCase (e.g. my_typeMyType, listList).

Why

Motoko types are conventionally PascalCase — the stdlib, the language spec, and the compiler's own style all follow this. Generated bindings that emit type my_type = ... are immediately out of place: developers either tolerate the inconsistency or write manual re-alias wrappers every time. This change makes generated code idiomatic and directly usable without wrapping.

Migration

Rename any references to generated type aliases from their old snake_case form to PascalCase:

// before
let x : MyService.my_response_type = ...;

// after
let x : MyService.MyResponseType = ...;

Edge cases handled

  • Collisions: when two Candid names map to the same PascalCase form (e.g. A and a both → A), the collider keeps its original escaped name. The first name alphabetically wins the PascalCase slot.
  • Self reservation: always reserved — pp_actor emits it verbatim for the actor declaration.
  • Empty transform: all-separator inputs (e.g. _) fall back to the escaped original.

These guarantees ensure generated bindings always compile.

Candid type aliases are now rendered in UpperCamelCase in generated
Motoko bindings (e.g. `my_type` → `MyType`). When two names would
map to the same UpperCamelCase form (e.g. `A` and `a`), the
collision is detected and the original escaped name is kept instead.
`Self` is always reserved as it is emitted verbatim by the actor
declaration. All-separator inputs (e.g. `_`) that produce an empty
transform also fall back to the original name.

Adds `to_upper_camel_case`, `escape_str` (string-returning escape
helper reused by both `escape` and the collision fallback), and
`type_display_name` which performs the collision check against the
full `TypeEnv`. All printer functions now accept `env: &TypeEnv`,
mirroring the TypeScript binding generator.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus Kamirus requested a review from a team as a code owner May 7, 2026 11:42
Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus Kamirus changed the title feat(motoko): emit type alias names as UpperCamelCase feat(motoko): emit type alias names as PascalCase May 7, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Name Max Mem (Kb) Encode Decode
blob 4_224 4_207_460 2_121_399
btreemap 75_456 529_878_081 10_161_391_774
double_option 128 1_322_312 ($\textcolor{green}{-0.07\%}$) 26_918_233
large_variant 320 1_039_286 ($\textcolor{green}{-0.07\%}$) 20_306_431
multi_arg 64 551_676 ($\textcolor{red}{0.02\%}$) 6_282_713
nns 192 2_006_575 5_524_217 ($\textcolor{green}{-0.01\%}$)
nns_list_neurons 1_152 6_597_767 ($\textcolor{green}{-0.01\%}$) 209_177_470 ($\textcolor{red}{0.00\%}$)
nns_list_proposal 1_216 6_964_970 ($\textcolor{green}{-0.05\%}$) 54_994_264 ($\textcolor{green}{-0.03\%}$)
option_list 128 727_583 16_198_353
result_variant 192 1_378_947 ($\textcolor{red}{0.04\%}$) 16_140_563
subtype_decode 512 2_664_961 ($\textcolor{green}{-0.03\%}$) 49_294_454 ($\textcolor{green}{-0.00\%}$)
text 6_336 4_204_312 7_877_260
variant_list 128 722_431 15_635_685
vec_int16 12_480 8_404_598 249_585_711
vec_nat 11_008 66_046_873 276_028_299
vec_nat32 24_768 16_793_206 243_294_536
vec_nat64 49_344 33_570_406 251_683_398
vec_service 64 689_583 94_753_660 ($\textcolor{green}{-0.08\%}$)
wide_record 1_152 3_265_428 ($\textcolor{green}{-0.11\%}$) 44_558_340 ($\textcolor{green}{-0.00\%}$)
  • Parser cost: 15_589_429 ($\textcolor{red}{0.01\%}$)
  • Extra args: 2_861_930
Click to see raw report
---------------------------------------------------

Benchmark: blob
  total:
    instructions: 6.33 M (no change)
    heap_increase: 66 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 4.21 M (no change)
    heap_increase: 66 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 2.12 M (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: btreemap
  total:
    instructions: 10.69 B (no change)
    heap_increase: 1179 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 529.88 M (no change)
    heap_increase: 159 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 10.16 B (no change)
    heap_increase: 1020 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: double_option
  total:
    instructions: 28.24 M (-0.00%) (change within noise threshold)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 1.32 M (-0.07%) (change within noise threshold)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 26.92 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: extra_args
  total:
    instructions: 2.86 M (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: large_variant
  total:
    instructions: 21.35 M (-0.00%) (change within noise threshold)
    heap_increase: 5 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 1.04 M (-0.07%) (change within noise threshold)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 20.31 M (no change)
    heap_increase: 3 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: multi_arg
  total:
    instructions: 6.84 M (0.00%) (change within noise threshold)
    heap_increase: 1 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 551.68 K (0.02%) (change within noise threshold)
    heap_increase: 1 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 6.28 M (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: nns
  total:
    instructions: 23.96 M (0.01%) (change within noise threshold)
    heap_increase: 3 pages (no change)
    stable_memory_increase: 0 pages (no change)

  0. Parsing (scope):
    calls: 1 (no change)
    instructions: 15.59 M (0.01%) (change within noise threshold)
    heap_increase: 3 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 2.01 M (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 5.52 M (-0.01%) (change within noise threshold)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: nns_list_neurons
  total:
    instructions: 215.78 M (-0.00%) (change within noise threshold)
    heap_increase: 18 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 6.60 M (-0.01%) (change within noise threshold)
    heap_increase: 18 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 209.18 M (0.00%) (change within noise threshold)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: nns_list_proposal
  total:
    instructions: 61.96 M (-0.03%) (change within noise threshold)
    heap_increase: 19 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 6.96 M (-0.05%) (change within noise threshold)
    heap_increase: 5 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 54.99 M (-0.03%) (change within noise threshold)
    heap_increase: 14 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: option_list
  total:
    instructions: 16.93 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 727.58 K (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 16.20 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: result_variant
  total:
    instructions: 17.52 M (0.00%) (change within noise threshold)
    heap_increase: 3 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 1.38 M (0.04%) (change within noise threshold)
    heap_increase: 1 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 16.14 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: subtype_decode
  total:
    instructions: 51.96 M (-0.00%) (change within noise threshold)
    heap_increase: 8 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 2.66 M (-0.03%) (change within noise threshold)
    heap_increase: 8 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 49.29 M (-0.00%) (change within noise threshold)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: text
  total:
    instructions: 12.08 M (no change)
    heap_increase: 99 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 4.20 M (no change)
    heap_increase: 66 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 7.88 M (no change)
    heap_increase: 33 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: variant_list
  total:
    instructions: 16.36 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 722.43 K (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 15.64 M (no change)
    heap_increase: 2 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: vec_int16
  total:
    instructions: 257.99 M (no change)
    heap_increase: 195 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 8.40 M (no change)
    heap_increase: 130 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 249.59 M (no change)
    heap_increase: 65 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: vec_nat
  total:
    instructions: 342.08 M (no change)
    heap_increase: 172 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 66.05 M (no change)
    heap_increase: 33 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 276.03 M (no change)
    heap_increase: 139 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: vec_nat32
  total:
    instructions: 260.09 M (no change)
    heap_increase: 387 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 16.79 M (no change)
    heap_increase: 258 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 243.29 M (no change)
    heap_increase: 129 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: vec_nat64
  total:
    instructions: 285.26 M (no change)
    heap_increase: 771 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 33.57 M (no change)
    heap_increase: 514 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 251.68 M (no change)
    heap_increase: 257 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: vec_service
  total:
    instructions: 95.45 M (-0.08%) (change within noise threshold)
    heap_increase: 1 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 689.58 K (no change)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 94.75 M (-0.08%) (change within noise threshold)
    heap_increase: 1 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Benchmark: wide_record
  total:
    instructions: 47.83 M (-0.01%) (change within noise threshold)
    heap_increase: 18 pages (no change)
    stable_memory_increase: 0 pages (no change)

  1. Encoding (scope):
    calls: 1 (no change)
    instructions: 3.27 M (-0.11%) (change within noise threshold)
    heap_increase: 18 pages (no change)
    stable_memory_increase: 0 pages (no change)

  2. Decoding (scope):
    calls: 1 (no change)
    instructions: 44.56 M (-0.00%) (change within noise threshold)
    heap_increase: 0 pages (no change)
    stable_memory_increase: 0 pages (no change)

---------------------------------------------------

Summary:
  instructions:
    status:   No significant changes 👍
    counts:   [total 20 | regressed 0 | improved 0 | new 0 | unchanged 20]
    change:   [max +1.41K | p75 0 | median 0 | p25 -857 | min -80.00K]
    change %: [max 0.01% | p75 0.00% | median 0.00% | p25 -0.00% | min -0.08%]

  heap_increase:
    status:   No significant changes 👍
    counts:   [total 20 | regressed 0 | improved 0 | new 0 | unchanged 20]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

  stable_memory_increase:
    status:   No significant changes 👍
    counts:   [total 20 | regressed 0 | improved 0 | new 0 | unchanged 20]
    change:   [max 0 | p75 0 | median 0 | p25 0 | min 0]
    change %: [max 0.00% | p75 0.00% | median 0.00% | p25 0.00% | min 0.00%]

---------------------------------------------------
Successfully persisted results to canbench_results.yml

Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus Kamirus marked this pull request as draft May 7, 2026 11:52
Kamirus and others added 2 commits May 7, 2026 15:07
- Introduce BindingCtx<'a> (Copy+Clone) bundling env + precomputed
  names map, threaded through all printer functions instead of bare
  &TypeEnv. compile_inner handles the lifetime split.
- build_names() runs one O(N) pass over env to count PascalCase
  collisions, then assigns display names in a second pass — replaces
  the previous O(N²) per-type env scan.
- to_pascal_case() simplified: just requires lowercase first char,
  no artificial domain restrictions needed since build_names sees all
  names at once and detects collisions via count map.
- Rename Ctx → BindingCtx; pp_defs uses ctx.names[id] (infallible).
- Add pascal_collision.did and self_type.did test fixtures covering
  two-name PascalCase collisions, verbatim env-key collisions, and
  the Self reservation guard.
- CHANGELOG: document breaking PascalCase rename.

Co-authored-by: Cursor <cursoragent@cursor.com>
- build_names: pass 1 assigns collision-free escape_str fallbacks,
  pass 2 upgrades to PascalCase where unclaimed — no implicit ordering
  dependency, fallback collisions provably impossible.
- to_pascal_case simplified: just requires lowercase first char.
- Rename Ctx → BindingCtx; pp_defs uses ctx.names[id] (infallible).
- Remove dead env.0.contains_key collision proxy.
- Trim comments throughout.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Kamirus Kamirus marked this pull request as ready for review May 7, 2026 14:17
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