fix(typecheck): instantiate generic fn type params (#135 slice 7)#163
Merged
Conversation
… slice 7) #135 slice 7 — root cause was NOT empty-array literals (those compile). It was a fundamental let-polymorphism bug: `check_fn_decl` never registered a function's explicit `<T>` type parameters, so an uppercase param `x: T` lowered via `lower_type_expr`'s `TyCon` fallthrough to a *rigid* `TCon "T"`. `generalize` ignores `TCon`, so every generic top-level function got `sc_tyvars = []` — effectively monomorphic. The first call pinned `T`; any second instantiation (or the very first against a concrete arg) failed `Unify.TypeMismatch (T, Int)`, and `use prelude::{…}` import-checks failed transitively (result/option/…). Fix (mirrors the existing let-generalization discipline): in `check_fn_decl`, before lowering, `enter_level` and register each `fd_type_params` name in `ctx.type_env` as a fresh tyvar at the deeper level; `exit_level` before the existing `generalize`, so those vars (level > ctx.level) become the scheme's quantified `sc_tyvars`. Scoped save/restore of `type_env` so params don't leak. Applied to both the extern and normal branches. Also fixes genuine stdlib bugs surfaced once typecheck passed: prelude.affine `map`/`filter`/`range`/`repeat`/`fold` reassigned immutable `let` bindings (`result`/`i`/`acc`) → `let mut`. With both, **prelude.affine now compiles end-to-end** (resolve→typecheck→codegen). Verification: `id<T>` instantiated at Int+Bool, generic `fold` from a monomorphic Int `sum`, monomorphic fns — all compile; the fundamental change regressed **none** of the suite. 2 regression tests; full suite green (230). Unblocks the typecheck wall for prelude (and, once visibility/slice 8 lands, result/option/collections via `use prelude`). Advances #135. Refs #128, #135. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
🔍 Hypatia Security ScanFindings: 44 issues detected
View findings[
{
"reason": "Stray AI.a2ml in root -- use 0-AI-MANIFEST.a2ml only",
"type": "banned",
"file": "AI.a2ml",
"action": "delete",
"rule_module": "root_hygiene",
"severity": "high"
},
{
"reason": "Superseded by 0-AI-MANIFEST.a2ml",
"type": "banned",
"file": "AI.djot",
"action": "delete",
"rule_module": "root_hygiene",
"severity": "high"
},
{
"reason": "Issue in quality.yml",
"type": "missing_workflow",
"file": "quality.yml",
"action": "create",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in security-policy.yml",
"type": "missing_workflow",
"file": "security-policy.yml",
"action": "create",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
"type": "unpinned_action",
"file": "governance.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/example/smoke_driver.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/cli.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/mod.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/compile.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
},
{
"reason": "TypeScript file detected -- banned language",
"type": "banned_language_file",
"file": "/home/runner/work/affinescript/affinescript/affinescript-deno-test/lib/runner.ts",
"action": "flag",
"rule_module": "cicd_rules",
"severity": "critical"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
#135 slice 7 — the root cause was not empty-array literals (those compile). It was a fundamental let-polymorphism bug.
check_fn_declnever registered a function's explicit<T>type parameters, so an uppercase paramx: Tlowered (vialower_type_exprTyConfallthrough) to a rigidTCon "T".generalizeignoresTCon, so every generic top-level function gotsc_tyvars = []— effectively monomorphic. The first call pinnedT; any further instantiation failedUnify.TypeMismatch (T, Int), anduse prelude::{…}import-checks failed transitively across result/option/collections.How
Mirrors the existing let-generalization discipline: in
check_fn_decl, before lowering,enter_leveland register eachfd_type_paramsname inctx.type_envas a fresh tyvar at the deeper level;exit_levelbefore the existinggeneralizeso those vars become the scheme's quantifiedsc_tyvars. Scoped save/restore oftype_env. Both extern and normal branches.Plus genuine stdlib bugs surfaced once typecheck passed:
prelude.affinemap/filter/range/repeat/foldreassigned immutableletbindings →let mut.Result
prelude.affinenow compiles end-to-end (resolve→typecheck→codegen).Verification
id<T>at Int+Bool, genericfoldfrom monomorphicsum, monomorphic fns — all compile.High-leverage: unblocks the typecheck wall; result/option/collections follow once slice 8 (module visibility) lands. Advances #135 (does not close). Refs #128, #135.
🤖 Generated with Claude Code