Skip to content

Commit c332b60

Browse files
fix(typecheck): instantiate constructor schemes per use-site in patterns (#128) (#187)
check_pattern's PatCon read the monomorphic constructor_env entry and wrapped it as a 0-tyvar scheme, so every occurrence of a constructor shared one set of type variables. Two `Ok(_)` patterns in a single match (e.g. result.affine `apply`'s tuple match) thus forced both payloads to the same type, yielding `Unify ((_ -> _), T)`. Now prefer the polymorphic scheme bound in name_types (set via bind_scheme for builtin Ok/Err/Some/None/RuntimeError and user variants) and instantiate it fresh per occurrence — exactly as lookup_var does for ordinary identifiers — falling back to the constructor_env entry only when no scheme is registered. Correctness/soundness fix. Full suite 233/233, zero regression. (result.affine remains blocked on a separate import-path issue: imported parametric ctor schemes are built with fresh_tyvar 0 then generalize, a no-op at level 0 — tracked for the next slice.) Refs #128. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e3d4806 commit c332b60

1 file changed

Lines changed: 23 additions & 5 deletions

File tree

lib/typecheck.ml

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,11 +488,29 @@ let rec check_pattern (ctx : context) (pat : pattern) (expected : ty)
488488
let* bindings_list = check_patterns ctx pats elem_tys in
489489
Ok (List.concat bindings_list)
490490
| PatCon ({ name; _ }, sub_pats) ->
491-
(* Look up the constructor type *)
492-
begin match Hashtbl.find_opt ctx.constructor_env name with
493-
| Some ctor_ty ->
494-
let ctor_ty' = instantiate ctx.level
495-
{ sc_tyvars = []; sc_effvars = []; sc_rowvars = []; sc_body = ctor_ty } in
491+
(* Resolve the constructor's type, freshly instantiated *per use-site*.
492+
Prefer the polymorphic scheme bound in [name_types] (set via
493+
[bind_scheme] for both builtin Ok/Err/Some/None/RuntimeError and
494+
user-declared variants) so every occurrence gets independent type
495+
variables — exactly as [lookup_var] does for ordinary identifiers.
496+
Fall back to the monomorphic [constructor_env] entry only when no
497+
scheme is registered. Previously this read [constructor_env] alone
498+
and wrapped it as a 0-tyvar scheme, so all occurrences *shared* one
499+
set of variables: two `Ok(_)` patterns in one match then forced both
500+
payloads to the same type (`Unify ((_ -> _), T)` in result.affine). *)
501+
let ctor_ty_opt =
502+
match Hashtbl.find_opt ctx.name_types name with
503+
| Some sc -> Some (instantiate ctx.level sc)
504+
| None ->
505+
(match Hashtbl.find_opt ctx.constructor_env name with
506+
| Some ctor_ty ->
507+
Some (instantiate ctx.level
508+
{ sc_tyvars = []; sc_effvars = []; sc_rowvars = [];
509+
sc_body = ctor_ty })
510+
| None -> None)
511+
in
512+
begin match ctor_ty_opt with
513+
| Some ctor_ty' ->
496514
(* Constructor type should be T1 -> T2 -> ... -> ResultType *)
497515
let rec peel_arrows ty pats acc =
498516
match pats with

0 commit comments

Comments
 (0)