Skip to content

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

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

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

Conversation

@Kamirus

@KamirusKamirus commented May 7, 2026

Copy link
Copy Markdown
Contributor

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:

// beforelet x : MyService.my_response_type =...;
// afterlet 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 ownerMay 7, 2026 11:42
Co-authored-by: Cursor <cursoragent@cursor.com>
@KamirusKamirus changed the title feat(motoko): emit type alias names as UpperCamelCasefeat(motoko): emit type alias names as PascalCaseMay 7, 2026
@github-actions

github-actionsBot commented May 7, 2026

Copy link
Copy Markdown
NameMax Mem (Kb)EncodeDecode
blob4_2244_207_4602_121_399
btreemap75_456529_878_08110_161_391_774
double_option1281_322_312 ($\textcolor{green}{-0.07\%}$)26_918_233
large_variant3201_039_286 ($\textcolor{green}{-0.07\%}$)20_306_431
multi_arg64551_676 ($\textcolor{red}{0.02\%}$)6_282_713
nns1922_006_5755_524_217 ($\textcolor{green}{-0.01\%}$)
nns_list_neurons1_1526_597_767 ($\textcolor{green}{-0.01\%}$)209_177_470 ($\textcolor{red}{0.00\%}$)
nns_list_proposal1_2166_964_970 ($\textcolor{green}{-0.05\%}$)54_994_264 ($\textcolor{green}{-0.03\%}$)
option_list128727_58316_198_353
result_variant1921_378_947 ($\textcolor{red}{0.04\%}$)16_140_563
subtype_decode5122_664_961 ($\textcolor{green}{-0.03\%}$)49_294_454 ($\textcolor{green}{-0.00\%}$)
text6_3364_204_3127_877_260
variant_list128722_43115_635_685
vec_int1612_4808_404_598249_585_711
vec_nat11_00866_046_873276_028_299
vec_nat3224_76816_793_206243_294_536
vec_nat6449_34433_570_406251_683_398
vec_service64689_58394_753_660 ($\textcolor{green}{-0.08\%}$)
wide_record1_1523_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
Kamirusand 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
Kamirus added a commit to caffeinelabs/motoko that referenced this pull request Jul 31, 2026
Distilled from dfinity/candid#729 per review: acronym names like
HTTP_request are no longer mangled to HttpRequest, and _foo / ___ /
_1foo are left alone (the old path could replace a valid name with
its IDL-hash escape). Docs also gain the pattern-import example.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto 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

@Kamirus