Skip to content

Instantly share code, notes, and snippets.

@SeanTAllen
Last active October 2, 2026 20:07
Show Gist options
  • Select an option

  • Save SeanTAllen/e001d8fb869e3b768539090e7d3a2fa5 to your computer and use it in GitHub Desktop.

Select an option

Save SeanTAllen/e001d8fb869e3b768539090e7d3a2fa5 to your computer and use it in GitHub Desktop.
PR #6222 Overview: Add iftype specialization for method overloading on type parameters
<title>PR #6222 Overview</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=Inter:wght@400;500;600;700&display=swap">
<style>
:root {
--bg: #fafaf9;
--fg: #1c1917;
--muted: #78716c;
--accent: #2563eb;
--accent-soft: #dbeafe;
--border: #e7e5e4;
--code-bg: #f5f5f4;
--added-bg: #dcfce7;
--added-fg: #166534;
--removed-bg: #fee2e2;
--removed-fg: #991b1b;
--card-bg: #ffffff;
--nav-bg: #1c1917;
--nav-fg: #d6d3d1;
--nav-active: #60a5fa;
--diagram-node: #e0e7ff;
--diagram-edge: #6366f1;
--diagram-highlight: #f59e0b;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #0c0a09;
--fg: #e7e5e4;
--muted: #a8a29e;
--accent: #60a5fa;
--accent-soft: #1e3a5f;
--border: #292524;
--code-bg: #1c1917;
--added-bg: #052e16;
--added-fg: #86efac;
--removed-bg: #450a0a;
--removed-fg: #fca5a5;
--card-bg: #1c1917;
--nav-bg: #0c0a09;
--nav-fg: #a8a29e;
--nav-active: #60a5fa;
--diagram-node: #312e81;
--diagram-edge: #818cf8;
--diagram-highlight: #fbbf24;
color-scheme: dark;
}
}
:root[data-theme="dark"] {
--bg: #0c0a09;
--fg: #e7e5e4;
--muted: #a8a29e;
--accent: #60a5fa;
--accent-soft: #1e3a5f;
--border: #292524;
--code-bg: #1c1917;
--added-bg: #052e16;
--added-fg: #86efac;
--removed-bg: #450a0a;
--removed-fg: #fca5a5;
--card-bg: #1c1917;
--nav-bg: #0c0a09;
--nav-fg: #a8a29e;
--nav-active: #60a5fa;
--diagram-node: #312e81;
--diagram-edge: #818cf8;
--diagram-highlight: #fbbf24;
color-scheme: dark;
}
body {
background: var(--bg);
color: var(--fg);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
line-height: 1.6;
padding: 0;
}
nav {
position: fixed;
top: env(safe-area-inset-top, 0px);
left: 0;
width: 220px;
height: 100vh;
background: var(--nav-bg);
color: var(--nav-fg);
padding: 24px 0;
overflow-y: auto;
z-index: 100;
display: flex;
flex-direction: column;
}
nav .pr-label {
padding: 0 16px 8px;
font-family: 'IBM Plex Mono', monospace;
font-size: 11px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
}
nav .pr-title {
padding: 0 16px 20px;
font-size: 13px;
font-weight: 600;
border-bottom: 1px solid var(--border);
line-height: 1.4;
}
nav ul { list-style: none; padding: 12px 0; flex: 1; }
nav a {
display: block;
padding: 7px 16px 7px 20px;
color: var(--nav-fg);
text-decoration: none;
font-size: 13px;
transition: background 0.15s, color 0.15s;
position: relative;
}
nav a:hover { background: rgba(255,255,255,0.05); }
nav a.active {
color: var(--nav-active);
background: rgba(96,165,250,0.08);
}
nav a.active::before {
content: '';
position: absolute;
left: 0; top: 4px; bottom: 4px;
width: 3px;
background: var(--nav-active);
border-radius: 0 2px 2px 0;
}
nav .kbd-help {
padding: 16px;
border-top: 1px solid var(--border);
font-size: 11px;
color: var(--muted);
line-height: 1.9;
}
kbd {
background: var(--code-bg);
border: 1px solid var(--border);
border-radius: 3px;
padding: 1px 5px;
font-size: 11px;
font-family: 'IBM Plex Mono', monospace;
}
main {
margin-left: 220px;
padding: 32px 48px;
max-width: 860px;
}
section { margin-bottom: 48px; scroll-margin-top: 24px; }
h1 { font-size: 24px; font-weight: 700; margin-bottom: 8px; text-wrap: balance; }
h2 { font-size: 17px; font-weight: 600; margin-bottom: 16px; padding-bottom: 6px; border-bottom: 2px solid var(--accent); display: inline-block; }
h3 { font-size: 15px; font-weight: 600; margin: 20px 0 10px; }
p { margin-bottom: 12px; }
.summary-box {
background: var(--accent-soft);
border-left: 4px solid var(--accent);
padding: 14px 18px;
margin-bottom: 20px;
border-radius: 0 6px 6px 0;
font-size: 14px;
}
.panel {
background: var(--card-bg);
border: 1px solid var(--border);
border-radius: 6px;
margin-bottom: 12px;
overflow: hidden;
}
.panel-header {
padding: 10px 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
user-select: none;
font-size: 14px;
}
.panel-header:hover { background: rgba(0,0,0,0.02); }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .panel-header:hover { background: rgba(255,255,255,0.03); }
}
:root[data-theme="dark"] .panel-header:hover { background: rgba(255,255,255,0.03); }
.panel-header .arrow { transition: transform 0.2s; font-size: 10px; color: var(--muted); flex-shrink: 0; }
.panel.open .arrow { transform: rotate(90deg); }
.panel-body { display: none; padding: 0 14px 14px; border-top: 1px solid var(--border); }
.panel.open .panel-body { display: block; padding-top: 14px; }
.badge {
display: inline-block;
padding: 1px 7px;
border-radius: 3px;
font-family: 'IBM Plex Mono', monospace;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
flex-shrink: 0;
}
.badge-removed { background: var(--removed-bg); color: var(--removed-fg); }
.badge-added { background: var(--added-bg); color: var(--added-fg); }
.badge-changed { background: var(--accent-soft); color: var(--accent); }
.sig { font-family: 'IBM Plex Mono', monospace; font-size: 13px; min-width: 0; }
.before-after { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 12px 0; }
.before-after > div { border-radius: 5px; overflow: hidden; min-width: 0; }
.before-after label { display: block; padding: 3px 10px; font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
.before-after .before label { background: var(--removed-bg); color: var(--removed-fg); }
.before-after .after label { background: var(--added-bg); color: var(--added-fg); }
pre { background: var(--code-bg); padding: 10px 14px; border-radius: 5px; overflow-x: auto; font-family: 'IBM Plex Mono', monospace; font-size: 12px; line-height: 1.5; }
.before-after pre { border-radius: 0 0 5px 5px; margin: 0; }
code { font-family: 'IBM Plex Mono', monospace; font-size: 12px; background: var(--code-bg); padding: 1px 4px; border-radius: 3px; }
.file-group-label { font-weight: 600; font-size: 13px; }
.file-count { font-size: 12px; color: var(--muted); }
.file-list { list-style: none; padding-left: 18px; }
.file-list li { padding: 3px 0; font-family: 'IBM Plex Mono', monospace; font-size: 12px; }
.file-list a { color: var(--muted); text-decoration: none; transition: color 0.15s; }
.file-list a:hover { color: var(--accent); text-decoration: underline; }
.file-list .stat { font-size: 11px; margin-left: 6px; }
.file-list .stat-add { color: var(--added-fg); }
.file-list .stat-del { color: var(--removed-fg); }
.stats { display: flex; gap: 16px; font-size: 13px; color: var(--muted); margin-bottom: 4px; flex-wrap: wrap; }
.diagram-container { margin: 16px 0; padding: 20px; background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; overflow-x: auto; }
.diagram-container svg { display: block; margin: 0 auto; }
svg text { font-family: 'Inter', sans-serif; fill: var(--fg); }
.type-block { background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; margin-bottom: 16px; overflow: hidden; }
.type-block-header { padding: 10px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.type-kind { font-family: 'IBM Plex Mono', monospace; font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--accent); flex-shrink: 0; }
.type-name { font-family: 'IBM Plex Mono', monospace; font-size: 14px; font-weight: 600; min-width: 0; }
.type-body { padding: 12px 14px; }
.type-doc { font-size: 13px; color: var(--muted); margin-bottom: 12px; line-height: 1.5; }
.method-list { list-style: none; }
.method-list li { padding: 6px 0; border-top: 1px solid var(--border); font-family: 'IBM Plex Mono', monospace; font-size: 12px; line-height: 1.5; }
.method-list li:first-child { border-top: none; }
.method-kind { color: var(--accent); font-weight: 600; font-size: 11px; }
a.type-link { color: var(--accent); text-decoration: none; border-bottom: 1px dotted var(--accent); }
a.type-link:hover { border-bottom-style: solid; }
.release-notes-content { background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px; padding: 16px 18px; font-size: 14px; line-height: 1.6; }
.release-notes-content h3 { font-size: 15px; margin: 0 0 8px; }
.release-notes-content p { margin-bottom: 8px; }
.release-notes-content p:last-child { margin-bottom: 0; }
.release-notes-content ul { margin: 8px 0 8px 20px; }
.release-notes-content li { margin-bottom: 4px; }
.release-notes-content pre { margin: 8px 0; }
@media (max-width: 768px) {
nav { position: static; width: 100%; height: auto; padding: 12px 16px; }
nav ul { display: flex; flex-wrap: wrap; gap: 2px; padding: 8px 0; }
nav li { flex: none; }
nav a { padding: 5px 10px; font-size: 12px; border-radius: 4px; }
nav a.active::before { display: none; }
nav .kbd-help { display: none; }
main { margin-left: 0; padding: 16px; }
.before-after { grid-template-columns: 1fr; }
}
</style>
<nav>
<div class="pr-label">ponylang/ponyc</div>
<div class="pr-title">#6222 &mdash; Add iftype specialization for method overloading on type parameters</div>
<ul>
<li><a href="#summary" class="active">Summary</a></li>
<li><a href="#motivation">Motivation</a></li>
<li><a href="#files">File Map</a></li>
<li><a href="#behavior">Behavioral Changes</a></li>
<li><a href="#api">API Changes</a></li>
<li><a href="#architecture">Architecture</a></li>
<li><a href="#release-notes">Release Notes</a></li>
</ul>
<div class="kbd-help">
<kbd>j</kbd> / <kbd>k</kbd> next / prev section<br>
<kbd>Enter</kbd> expand focused item<br>
<kbd>Esc</kbd> collapse all
</div>
</nav>
<main>
<section id="summary">
<h1>Add iftype specialization for method overloading on type parameters</h1>
<div class="summary-box">
Introduces a new language feature: methods on generic types can have multiple definitions distinguished by <code>iftype</code> guards on type parameters, with the matching body selected at reification time for zero runtime cost.
</div>
<div class="stats">
<span>99 files changed</span>
<span style="color:var(--added-fg)">+2750</span>
<span style="color:var(--removed-fg)">&minus;194</span>
</div>
</section>
<section id="motivation">
<h2>Motivation</h2>
<p>Generic types in Pony currently have no way to provide a more specific implementation when a type parameter satisfies an additional constraint. The existing <code>iftype</code> expression works inside method bodies but forces the author to put all logic into a single method with conditional branches.</p>
<p>This PR introduces method-level <code>iftype</code> guards that let a generic type declare multiple definitions of the same method, each gated on a constraint over the type parameters. The compiler selects the matching body at reification time, so there is no runtime dispatch overhead. This is the moral equivalent of C++ template specialization or Rust's specialization RFC, but resolved entirely at compile time through Pony's existing subtype machinery.</p>
</section>
<section id="files">
<h2>File Map</h2>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Grammar &amp; Lexer</span>
<span class="file-count">2 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/pony.g" target="_blank" rel="noopener">pony.g</a> <span class="stat"><span class="stat-add">+17</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/lexer.c" target="_blank" rel="noopener">src/libponyc/ast/lexer.c</a> <span class="stat"><span class="stat-add">+5</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">AST &amp; Parser</span>
<span class="file-count">5 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/parser.c" target="_blank" rel="noopener">src/libponyc/ast/parser.c</a> <span class="stat"><span class="stat-add">+38</span> <span class="stat-del">&minus;3</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/token.h" target="_blank" rel="noopener">src/libponyc/ast/token.h</a> <span class="stat"><span class="stat-add">+5</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/treecheckdef.h" target="_blank" rel="noopener">src/libponyc/ast/treecheckdef.h</a> <span class="stat"><span class="stat-add">+18</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/symtab.c" target="_blank" rel="noopener">src/libponyc/ast/symtab.c</a> <span class="stat"><span class="stat-add">+13</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/symtab.h" target="_blank" rel="noopener">src/libponyc/ast/symtab.h</a> <span class="stat"><span class="stat-add">+2</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Compiler Passes (scope, sugar, syntax, traits, verify, refer, flatten, expr, finalisers)</span>
<span class="file-count">13 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/scope.c" target="_blank" rel="noopener">src/libponyc/pass/scope.c</a> <span class="stat"><span class="stat-add">+370</span> <span class="stat-del">&minus;3</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/traits.c" target="_blank" rel="noopener">src/libponyc/pass/traits.c</a> <span class="stat"><span class="stat-add">+187</span> <span class="stat-del">&minus;36</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/verify/fun.c" target="_blank" rel="noopener">src/libponyc/verify/fun.c</a> <span class="stat"><span class="stat-add">+374</span> <span class="stat-del">&minus;8</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/sugar.c" target="_blank" rel="noopener">src/libponyc/pass/sugar.c</a> <span class="stat"><span class="stat-add">+18</span> <span class="stat-del">&minus;10</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/syntax.c" target="_blank" rel="noopener">src/libponyc/pass/syntax.c</a> <span class="stat"><span class="stat-add">+3</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/refer.c" target="_blank" rel="noopener">src/libponyc/pass/refer.c</a> <span class="stat"><span class="stat-add">+9</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/flatten.c" target="_blank" rel="noopener">src/libponyc/pass/flatten.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;4</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/expr.c" target="_blank" rel="noopener">src/libponyc/pass/expr.c</a> <span class="stat"><span class="stat-add">+1</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/finalisers.c" target="_blank" rel="noopener">src/libponyc/pass/finalisers.c</a> <span class="stat"><span class="stat-add">+29</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/pass/verify.c" target="_blank" rel="noopener">src/libponyc/pass/verify.c</a> <span class="stat"><span class="stat-add">+5</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/verify/fun.h" target="_blank" rel="noopener">src/libponyc/verify/fun.h</a> <span class="stat"><span class="stat-add">+1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/verify/type.c" target="_blank" rel="noopener">src/libponyc/verify/type.c</a> <span class="stat"><span class="stat-add">+15</span> <span class="stat-del">&minus;4</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/ast/frame.c" target="_blank" rel="noopener">src/libponyc/ast/frame.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;1</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Type System (subtype, lookup, matchtype, reify, assemble)</span>
<span class="file-count">5 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/type/subtype.c" target="_blank" rel="noopener">src/libponyc/type/subtype.c</a> <span class="stat"><span class="stat-add">+134</span> <span class="stat-del">&minus;12</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/type/lookup.c" target="_blank" rel="noopener">src/libponyc/type/lookup.c</a> <span class="stat"><span class="stat-add">+9</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/type/matchtype.c" target="_blank" rel="noopener">src/libponyc/type/matchtype.c</a> <span class="stat"><span class="stat-add">+24</span> <span class="stat-del">&minus;8</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/type/reify.c" target="_blank" rel="noopener">src/libponyc/type/reify.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;4</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/type/assemble.c" target="_blank" rel="noopener">src/libponyc/type/assemble.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;2</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Codegen &amp; Reach</span>
<span class="file-count">6 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/reach/reach.c" target="_blank" rel="noopener">src/libponyc/reach/reach.c</a> <span class="stat"><span class="stat-add">+84</span> <span class="stat-del">&minus;5</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/codegen/genexport.c" target="_blank" rel="noopener">src/libponyc/codegen/genexport.c</a> <span class="stat"><span class="stat-add">+79</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/codegen/genfun.c" target="_blank" rel="noopener">src/libponyc/codegen/genfun.c</a> <span class="stat"><span class="stat-add">+8</span> <span class="stat-del">&minus;8</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/codegen/genexe.cc" target="_blank" rel="noopener">src/libponyc/codegen/genexe.cc</a> <span class="stat"><span class="stat-add">+8</span> <span class="stat-del">&minus;3</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/codegen/genmatch.c" target="_blank" rel="noopener">src/libponyc/codegen/genmatch.c</a> <span class="stat"><span class="stat-add">+2</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/verify/call.c" target="_blank" rel="noopener">src/libponyc/verify/call.c</a> <span class="stat"><span class="stat-add">+1</span> <span class="stat-del">&minus;1</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Expression Handling (array, call, lambda, match, reference)</span>
<span class="file-count">5 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/expr/lambda.c" target="_blank" rel="noopener">src/libponyc/expr/lambda.c</a> <span class="stat"><span class="stat-add">+27</span> <span class="stat-del">&minus;3</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/expr/array.c" target="_blank" rel="noopener">src/libponyc/expr/array.c</a> <span class="stat"><span class="stat-add">+14</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/expr/match.c" target="_blank" rel="noopener">src/libponyc/expr/match.c</a> <span class="stat"><span class="stat-add">+6</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/expr/call.c" target="_blank" rel="noopener">src/libponyc/expr/call.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/src/libponyc/expr/reference.c" target="_blank" rel="noopener">src/libponyc/expr/reference.c</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;2</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Full-Program Tests</span>
<span class="file-count">38 files (19 test cases)</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-selects-match/main.pony" target="_blank" rel="noopener">iftype-specialization-selects-match/</a> <span class="stat"><span class="stat-add">+19</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-selects-default/main.pony" target="_blank" rel="noopener">iftype-specialization-selects-default/</a> <span class="stat"><span class="stat-add">+21</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-first-match-wins/main.pony" target="_blank" rel="noopener">iftype-specialization-first-match-wins/</a> <span class="stat"><span class="stat-add">+30</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-conjunction-guard/main.pony" target="_blank" rel="noopener">iftype-specialization-conjunction-guard/</a> <span class="stat"><span class="stat-add">+21</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-conjunction-fallback/main.pony" target="_blank" rel="noopener">iftype-specialization-conjunction-fallback/</a> <span class="stat"><span class="stat-add">+24</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-disjunction-guard/main.pony" target="_blank" rel="noopener">iftype-specialization-disjunction-guard/</a> <span class="stat"><span class="stat-add">+26</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-and-same-param/main.pony" target="_blank" rel="noopener">iftype-specialization-and-same-param/</a> <span class="stat"><span class="stat-add">+22</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-constructor/main.pony" target="_blank" rel="noopener">iftype-specialization-constructor/</a> <span class="stat"><span class="stat-add">+21</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-behavior/main.pony" target="_blank" rel="noopener">iftype-specialization-behavior/</a> <span class="stat"><span class="stat-add">+24</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-partial/main.pony" target="_blank" rel="noopener">iftype-specialization-partial/</a> <span class="stat"><span class="stat-add">+26</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-type-narrowing/main.pony" target="_blank" rel="noopener">iftype-specialization-type-narrowing/</a> <span class="stat"><span class="stat-add">+19</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-multiple-typeparams/main.pony" target="_blank" rel="noopener">iftype-specialization-multiple-typeparams/</a> <span class="stat"><span class="stat-add">+27</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-method-typeparam/main.pony" target="_blank" rel="noopener">iftype-specialization-method-typeparam/</a> <span class="stat"><span class="stat-add">+16</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-trait-provides/main.pony" target="_blank" rel="noopener">iftype-specialization-trait-provides/</a> <span class="stat"><span class="stat-add">+20</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-trait-override/main.pony" target="_blank" rel="noopener">iftype-specialization-trait-override/</a> <span class="stat"><span class="stat-add">+26</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-trait-chain/main.pony" target="_blank" rel="noopener">iftype-specialization-trait-chain/</a> <span class="stat"><span class="stat-add">+17</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-interface-structural/main.pony" target="_blank" rel="noopener">iftype-specialization-interface-structural/</a> <span class="stat"><span class="stat-add">+24</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/iftype-specialization-tuple-guard/main.pony" target="_blank" rel="noopener">iftype-specialization-tuple-guard/</a> <span class="stat"><span class="stat-add">+21</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/export-methodgroup/main.pony" target="_blank" rel="noopener">export-methodgroup/</a> <span class="stat"><span class="stat-add">+31</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/full-program-tests/export-methodgroup-default/main.pony" target="_blank" rel="noopener">export-methodgroup-default/</a> <span class="stat"><span class="stat-add">+34</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Unit Tests (scope, verify, traits, sugar, annotations)</span>
<span class="file-count">5 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/libponyc/verify.cc" target="_blank" rel="noopener">test/libponyc/verify.cc</a> <span class="stat"><span class="stat-add">+351</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/libponyc/scope.cc" target="_blank" rel="noopener">test/libponyc/scope.cc</a> <span class="stat"><span class="stat-add">+172</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/libponyc/traits.cc" target="_blank" rel="noopener">test/libponyc/traits.cc</a> <span class="stat"><span class="stat-add">+19</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/libponyc/sugar.cc" target="_blank" rel="noopener">test/libponyc/sugar.cc</a> <span class="stat"><span class="stat-add">+2</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/test/libponyc/annotations.cc" target="_blank" rel="noopener">test/libponyc/annotations.cc</a> <span class="stat"><span class="stat-add">+2</span> <span class="stat-del">&minus;2</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Tooling (pony-doc, pony-lint, pony-lsp, pony_compiler lib)</span>
<span class="file-count">10 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-doc/extractor.pony" target="_blank" rel="noopener">tools/pony-doc/extractor.pony</a> <span class="stat"><span class="stat-add">+37</span> <span class="stat-del">&minus;3</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/lib/ponylang/pony_compiler/pony_compiler/token.pony" target="_blank" rel="noopener">tools/lib/ponylang/pony_compiler/pony_compiler/token.pony</a> <span class="stat"><span class="stat-add">+25</span> <span class="stat-del">&minus;16</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/lib/ponylang/pony_compiler/pony_compiler/ast.pony" target="_blank" rel="noopener">tools/lib/ponylang/pony_compiler/pony_compiler/ast.pony</a> <span class="stat"><span class="stat-add">+1</span> <span class="stat-del">&minus;1</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/public_docstring.pony" target="_blank" rel="noopener">tools/pony-lint/public_docstring.pony</a> <span class="stat"><span class="stat-add">+11</span> <span class="stat-del">&minus;8</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/docstring_format.pony" target="_blank" rel="noopener">tools/pony-lint/docstring_format.pony</a> <span class="stat"><span class="stat-add">+10</span> <span class="stat-del">&minus;7</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/docstring_leading_blank.pony" target="_blank" rel="noopener">tools/pony-lint/docstring_leading_blank.pony</a> <span class="stat"><span class="stat-add">+9</span> <span class="stat-del">&minus;6</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/line_length.pony" target="_blank" rel="noopener">tools/pony-lint/line_length.pony</a> <span class="stat"><span class="stat-add">+4</span> <span class="stat-del">&minus;4</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/blank_lines.pony" target="_blank" rel="noopener">tools/pony-lint/blank_lines.pony</a> <span class="stat"><span class="stat-add">+3</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/method_declaration_format.pony" target="_blank" rel="noopener">tools/pony-lint/method_declaration_format.pony</a> <span class="stat"><span class="stat-add">+2</span> <span class="stat-del">&minus;2</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/tools/pony-lint/partial_spacing.pony" target="_blank" rel="noopener">tools/pony-lint/partial_spacing.pony</a> <span class="stat"><span class="stat-add">+2</span> <span class="stat-del">&minus;2</span></span></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="file-group-label">Examples &amp; Release Notes</span>
<span class="file-count">4 files</span>
</div>
<div class="panel-body">
<ul class="file-list">
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/.release-notes/add-iftype-specialization.md" target="_blank" rel="noopener">.release-notes/add-iftype-specialization.md</a> <span class="stat"><span class="stat-add">+52</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/examples/README.md" target="_blank" rel="noopener">examples/README.md</a> <span class="stat"><span class="stat-add">+4</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/examples/language-features/iftype-specialization/main.pony" target="_blank" rel="noopener">examples/language-features/iftype-specialization/main.pony</a> <span class="stat"><span class="stat-add">+34</span></span></li>
<li><a href="https://github.com/ponylang/ponyc/blob/13c8d2daef16bcef19010527e59bb8ff5e996808/examples/language-features/iftype-specialization/iftype_specialization.pony" target="_blank" rel="noopener">examples/language-features/iftype-specialization/iftype_specialization.pony</a> <span class="stat"><span class="stat-add">+8</span></span></li>
</ul>
</div>
</div>
</section>
<section id="behavior">
<h2>Behavioral Changes</h2>
<h3>New syntax: <code>iftype</code> guard on method declarations</h3>
<p>Methods on generic types can now be declared multiple times with the same name. Each duplicate carries an <code>iftype</code> guard that constrains one or more type parameters. Exactly one method must be unguarded (the default). The first guarded method whose constraint matches the concrete type arguments is selected at reification time; if none matches, the default is used. No runtime dispatch occurs.</p>
<h3>Guard syntax</h3>
<p>A guard appears between the return type and the <code>?</code> partial marker:</p>
<pre>fun method_name(): ReturnType iftype A &lt;: Constraint =&gt; ...</pre>
<p>Guards support conjunction (<code>and</code>) and disjunction (<code>or</code>), but not both in a single guard. An <code>or</code> guard requires all branches to constrain the same type parameter. An <code>and</code> guard can constrain different type parameters.</p>
<h3>Type narrowing inside guarded methods</h3>
<p>Inside a guarded method body, the type parameter is narrowed to the intersection of its original constraint and the guard's supertype. This allows calling methods from the guard's constraint without additional <code>iftype</code> expressions.</p>
<h3>Trait and interface inheritance</h3>
<p>Traits can declare method groups (a default plus specializations). Implementing classes inherit the entire group. A class can override the trait's default or specialization with its own definition. Interfaces with specializations enforce structural subtyping: the implementing type must provide matching specializations with equivalent guards.</p>
<h3>Compile-time validation</h3>
<ul style="margin: 0 0 12px 20px;">
<li>Specializations must have the same parameter types, receiver capability, and method kind as the default.</li>
<li>The return type of a specialization must be a subtype of the default's return type.</li>
<li>A specialization can only be partial (<code>?</code>) if the default is partial.</li>
<li>Unreachable specializations (where an earlier guard subsumes a later one) are a compile error.</li>
<li>Method groups without a default (all methods have guards) are a compile error.</li>
<li>Mixing method kinds (<code>fun</code>/<code>be</code>/<code>new</code>) in a method group is a compile error.</li>
</ul>
<h3>C API export support</h3>
<p>When a generic type with method groups is exported via <code>\c_api\</code>, the correct specialization is selected based on the concrete type arguments of the export alias.</p>
</section>
<section id="api">
<h2>API Changes</h2>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-changed">Changed</span>
<span class="sig">Method AST node gains a guard child at index 5</span>
</div>
<div class="panel-body">
<div class="before-after">
<div class="before"><label>Before</label><pre>// Method AST children:
// 0: cap
// 1: id
// 2: typeparams
// 3: params
// 4: return_type
// 5: can_error
// 6: body
// 7: docstring</pre></div>
<div class="after"><label>After</label><pre>// Method AST children:
// 0: cap
// 1: id
// 2: typeparams
// 3: params
// 4: return_type
// 5: guard (TK_IFTYPEGUARD | TK_IFTYPEGUARD_AND | TK_IFTYPEGUARD_OR | TK_NONE)
// 6: can_error
// 7: body
// 8: docstring</pre></div>
</div>
<p>Every <code>ast_childidx</code> call for <code>can_error</code>, <code>body</code>, and <code>docstring</code> throughout the compiler is updated from indices 5/6/7 to 6/7/8.</p>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-added">New</span>
<span class="sig">TK_METHODGROUP AST node</span>
</div>
<div class="panel-body">
<p>When the scope pass encounters multiple methods with the same name (one unguarded default plus one or more guarded specializations), it groups them under a new <code>TK_METHODGROUP</code> node in the <code>TK_MEMBERS</code> list. The default is always the first child; specializations follow in declaration order.</p>
<p>All existing code that walks members lists is updated to handle <code>TK_METHODGROUP</code> by unwrapping to the first child (the default) for operations that don't need to see specializations.</p>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-added">New</span>
<span class="sig">Guard AST nodes: TK_IFTYPEGUARD, TK_IFTYPEGUARD_AND, TK_IFTYPEGUARD_OR</span>
</div>
<div class="panel-body">
<p><code>TK_IFTYPEGUARD</code> has three children: subtype, supertype, and a typeparam store (populated during the scope pass with narrowed type parameters). <code>TK_IFTYPEGUARD_AND</code> and <code>TK_IFTYPEGUARD_OR</code> hold two or more <code>TK_IFTYPEGUARD</code> children.</p>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-added">New</span>
<span class="sig">symtab_replace() in symtab.h</span>
</div>
<div class="panel-body">
<p>New function to replace the definition pointer for an existing symbol table entry without changing its status. Used by the scope pass when converting a standalone method into a method group.</p>
<pre>bool symtab_replace(symtab_t* symtab, const char* name, ast_t* def);</pre>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-added">New</span>
<span class="sig">verify_methodgroup() in verify/fun.h</span>
</div>
<div class="panel-body">
<p>Validates all specializations in a method group against the default: same parameter types, receiver cap, method kind, method-level type parameters, covariant return types, partial consistency, and unreachable-guard detection.</p>
<pre>bool verify_methodgroup(pass_opt_t* opt, ast_t* ast);</pre>
</div>
</div>
<div class="panel">
<div class="panel-header" onclick="this.parentElement.classList.toggle('open')">
<span class="arrow">&#9654;</span>
<span class="badge badge-changed">Changed</span>
<span class="sig">Grammar: method production gains optional iftype guard</span>
</div>
<div class="panel-body">
<div class="before-after">
<div class="before"><label>Before</label><pre>method
: ('fun' | 'be' | 'new')
annotations? (cap | '@')?
ID typeparams?
('(' | LPAREN_NEW) params? ')'
(':' type)? '?'?
STRING? ('=>' rawseq)?
;</pre></div>
<div class="after"><label>After</label><pre>method
: ('fun' | 'be' | 'new')
annotations? (cap | '@')?
ID typeparams?
('(' | LPAREN_NEW) params? ')'
(':' type)?
('iftype' iftypeguard)?
'?'? STRING? ('=>' rawseq)?
;
iftypeguard
: iftypeguardsingle
(iftypeguardandext |
iftypeguardorext)?
;
iftypeguardsingle
: type '&lt;:' type
;
iftypeguardandext
: 'and' iftypeguardsingle
('and' iftypeguardsingle)*
;
iftypeguardorext
: 'or' iftypeguardsingle
('or' iftypeguardsingle)*
;</pre></div>
</div>
</div>
</div>
</section>
<section id="architecture">
<h2>Architecture</h2>
<h3>Compilation Pipeline</h3>
<p>The feature threads through the entire compiler pipeline. Each pass has a specific responsibility:</p>
<div class="diagram-container">
<svg width="100%" viewBox="0 0 700 520" style="max-width:700px">
<defs>
<marker id="ah" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="var(--diagram-edge)"/>
</marker>
</defs>
<text x="350" y="24" text-anchor="middle" font-size="14" font-weight="600">Iftype Specialization Through the Compiler</text>
<!-- Row 1: Parse -->
<rect x="230" y="40" width="240" height="48" rx="6" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="60" text-anchor="middle" font-size="12" font-weight="600">Parser</text>
<text x="350" y="76" text-anchor="middle" font-size="11" fill="var(--muted)">Parse iftype guard syntax</text>
<line x1="350" y1="88" x2="350" y2="108" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah)"/>
<!-- Row 2: Scope -->
<rect x="230" y="112" width="240" height="60" rx="6" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="132" text-anchor="middle" font-size="12" font-weight="600">Scope Pass</text>
<text x="350" y="148" text-anchor="middle" font-size="11" fill="var(--muted)">Group methods into TK_METHODGROUP</text>
<text x="350" y="163" text-anchor="middle" font-size="11" fill="var(--muted)">Narrow type params in guard scope</text>
<line x1="350" y1="172" x2="350" y2="192" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah)"/>
<!-- Row 3: Traits -->
<rect x="230" y="196" width="240" height="48" rx="6" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="216" text-anchor="middle" font-size="12" font-weight="600">Traits Pass</text>
<text x="350" y="232" text-anchor="middle" font-size="11" fill="var(--muted)">Inherit method groups from traits</text>
<line x1="350" y1="244" x2="350" y2="264" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah)"/>
<!-- Row 4: Verify -->
<rect x="200" y="268" width="300" height="60" rx="6" fill="var(--diagram-highlight)" stroke="var(--diagram-edge)" stroke-width="1.5" fill-opacity="0.3"/>
<text x="350" y="288" text-anchor="middle" font-size="12" font-weight="600">Verify Pass</text>
<text x="350" y="304" text-anchor="middle" font-size="11" fill="var(--muted)">Check signatures, return types, partialness</text>
<text x="350" y="319" text-anchor="middle" font-size="11" fill="var(--muted)">Detect unreachable guards</text>
<line x1="350" y1="328" x2="350" y2="348" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah)"/>
<!-- Row 5: Subtype -->
<rect x="230" y="352" width="240" height="48" rx="6" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="372" text-anchor="middle" font-size="12" font-weight="600">Subtype Checking</text>
<text x="350" y="388" text-anchor="middle" font-size="11" fill="var(--muted)">Match specializations structurally</text>
<line x1="350" y1="400" x2="350" y2="420" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah)"/>
<!-- Row 6: Reach + Codegen -->
<rect x="230" y="424" width="240" height="60" rx="6" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="444" text-anchor="middle" font-size="12" font-weight="600">Reach &amp; Codegen</text>
<text x="350" y="460" text-anchor="middle" font-size="11" fill="var(--muted)">Evaluate guards at reification</text>
<text x="350" y="475" text-anchor="middle" font-size="11" fill="var(--muted)">Select matching body for codegen</text>
</svg>
</div>
<h3>AST Structure</h3>
<p>The scope pass transforms individual method nodes into a grouped representation when it encounters duplicate names with guards:</p>
<div class="diagram-container">
<svg width="100%" viewBox="0 0 700 400" style="max-width:700px">
<defs>
<marker id="ah2" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto">
<polygon points="0 0, 8 3, 0 6" fill="var(--diagram-edge)"/>
</marker>
</defs>
<!-- Before section -->
<text x="240" y="24" text-anchor="middle" font-size="13" font-weight="600">Before Scope Pass</text>
<rect x="170" y="40" width="140" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="240" y="63" text-anchor="middle" font-size="12">TK_MEMBERS</text>
<line x1="205" y1="76" x2="90" y2="100" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<line x1="240" y1="76" x2="240" y2="100" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<line x1="275" y1="76" x2="400" y2="100" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<rect x="15" y="104" width="150" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="90" y="127" text-anchor="middle" font-size="11">TK_FUN describe</text>
<rect x="210" y="104" width="60" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="240" y="127" text-anchor="middle" font-size="11">...</text>
<rect x="310" y="104" width="180" height="36" rx="5" fill="var(--diagram-highlight)" stroke="var(--diagram-edge)" stroke-width="1.5" fill-opacity="0.3"/>
<text x="400" y="121" text-anchor="middle" font-size="11">TK_FUN describe</text>
<text x="400" y="133" text-anchor="middle" font-size="10" fill="var(--muted)">guard: A &lt;: Stringable</text>
<!-- Divider -->
<line x1="0" y1="170" x2="700" y2="170" stroke="var(--border)" stroke-width="1" stroke-dasharray="6,4"/>
<!-- After section -->
<text x="350" y="200" text-anchor="middle" font-size="13" font-weight="600">After Scope Pass</text>
<rect x="280" y="216" width="140" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="350" y="239" text-anchor="middle" font-size="12">TK_MEMBERS</text>
<line x1="350" y1="252" x2="350" y2="272" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<rect x="255" y="276" width="190" height="36" rx="5" fill="var(--diagram-highlight)" stroke="var(--diagram-edge)" stroke-width="1.5" fill-opacity="0.3"/>
<text x="350" y="299" text-anchor="middle" font-size="12">TK_METHODGROUP</text>
<line x1="310" y1="312" x2="260" y2="336" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<line x1="390" y1="312" x2="440" y2="336" stroke="var(--diagram-edge)" stroke-width="1.5" marker-end="url(#ah2)"/>
<rect x="170" y="340" width="180" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="260" y="357" text-anchor="middle" font-size="11">TK_FUN describe</text>
<text x="260" y="369" text-anchor="middle" font-size="10" fill="var(--muted)">default (no guard)</text>
<rect x="360" y="340" width="180" height="36" rx="5" fill="var(--diagram-node)" stroke="var(--diagram-edge)" stroke-width="1.5"/>
<text x="450" y="357" text-anchor="middle" font-size="11">TK_FUN describe</text>
<text x="450" y="369" text-anchor="middle" font-size="10" fill="var(--muted)">guard: A &lt;: Stringable</text>
</svg>
</div>
<h3>Specialization Selection at Reification</h3>
<p>When the reach pass encounters a method call on a reified generic type, it checks whether the method's parent is a <code>TK_METHODGROUP</code>. If so, it evaluates each specialization's guard against the concrete type arguments using <code>is_subtype_constraint</code>. The first matching specialization replaces the default in the deferred reification, so codegen emits only the selected body.</p>
</section>
<section id="release-notes">
<h2>Release Notes</h2>
<div class="release-notes-content">
<h3>Add iftype specialization for method overloading on type parameters</h3>
<p>Methods on generic types can now have multiple definitions distinguished by <code>iftype</code> guards on type parameters. The matching body is selected at reification time, so there is no runtime dispatch cost.</p>
<pre>class Container[A: Any val]
var _data: A
new create(data: A) =>
_data = data
fun describe(): String =>
"opaque value"
fun describe(): String iftype A &lt;: Stringable val =>
_data.string()</pre>
<p>When <code>A</code> satisfies the guard constraint, the specialization is used. When it does not, the unguarded default is used. A method can have multiple specializations; the first in declaration order whose guard matches is selected.</p>
<p>Guards can use <code>and</code> or <code>or</code> to combine multiple constraints:</p>
<pre>class Pair[A: Any val, B: Any val]
fun process(): String =>
"generic"
fun process(): String iftype A &lt;: Stringable val and B &lt;: Stringable val =>
"both stringable"
fun process(): String iftype A &lt;: Stringable val or A &lt;: Hashable val =>
"stringable or hashable"</pre>
<p>Traits and interfaces can declare specializations, and concrete types inherit the entire method group:</p>
<pre>trait Describable[A: Any val]
fun describe(): String =>
"unknown"
fun describe(): String iftype A &lt;: Stringable val =>
"stringable"
class MyType[A: Any val] is Describable[A]</pre>
<p>When a concrete type provides its own definition, it overrides the trait's specialization. Interfaces with specializations enforce structural subtyping &mdash; concrete types must provide matching specializations.</p>
<p>An <code>or</code> guard requires all branches to constrain the same type parameter. An <code>and</code> guard can constrain different type parameters. Mixing <code>and</code> and <code>or</code> in a single guard is not allowed.</p>
<p>Specializations must have the same parameter types, receiver capability, and method kind as the default. The return type of a specialization must be a subtype of the default's return type. A specialization can only be partial (<code>?</code>) if the default is partial.</p>
</div>
</section>
</main>
<script>
const sections = document.querySelectorAll('section');
const navLinks = document.querySelectorAll('nav a[href^="#"]');
let currentSection = 0;
function updateActiveNav() {
navLinks.forEach(a => a.classList.remove('active'));
const id = sections[currentSection]?.id;
if (id) {
const link = document.querySelector('nav a[href="#' + id + '"]');
if (link) link.classList.add('active');
}
}
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const idx = Array.from(sections).indexOf(entry.target);
if (idx >= 0) { currentSection = idx; updateActiveNav(); }
}
});
}, { rootMargin: '-20% 0px -60% 0px' });
sections.forEach(s => observer.observe(s));
document.addEventListener('keydown', e => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.key === 'j' || e.key === 'J') {
e.preventDefault();
currentSection = Math.min(currentSection + 1, sections.length - 1);
sections[currentSection].scrollIntoView({ behavior: 'smooth' });
updateActiveNav();
} else if (e.key === 'k' || e.key === 'K') {
e.preventDefault();
currentSection = Math.max(currentSection - 1, 0);
sections[currentSection].scrollIntoView({ behavior: 'smooth' });
updateActiveNav();
} else if (e.key === 'Enter') {
e.preventDefault();
sections[currentSection].querySelectorAll('.panel:not(.open)').forEach(el => el.classList.add('open'));
} else if (e.key === 'Escape') {
document.querySelectorAll('.panel.open').forEach(el => el.classList.remove('open'));
}
});
navLinks.forEach(a => {
a.addEventListener('click', e => {
e.preventDefault();
const target = document.querySelector(a.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth' });
const idx = Array.from(sections).indexOf(target);
if (idx >= 0) { currentSection = idx; updateActiveNav(); }
}
});
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment