@@ -63,24 +63,106 @@ let create_registry () : trait_registry = {
6363 impls = Hashtbl. create 64 ;
6464}
6565
66- (* * Register a trait definition *)
66+ (* * Register a trait definition.
67+
68+ The [tm_ret_ty] field is populated from the AST return-type annotation
69+ (if present) and later used by the type checker when verifying impl bodies. *)
6770let register_trait (registry : trait_registry ) (trait_decl : trait_decl ) : unit =
6871 let methods = List. filter_map (fun item ->
6972 match item with
7073 | TraitFn fs ->
74+ (* fs_ret_ty is the declared return-type annotation from the signature *)
75+ let ret_ty = match fs.fs_ret_ty with
76+ | None -> None
77+ | Some te ->
78+ (* Convert the AST type expression to an internal ty using a simple
79+ structural walk. We do not have the full context here, so only
80+ built-in primitive names are resolved; everything else becomes
81+ TCon which the type checker resolves later during unification. *)
82+ let rec lower_simple (te : type_expr ) : ty =
83+ match te with
84+ | TyCon { name = "Int" ; _ } -> TCon " Int"
85+ | TyCon { name = "Float" ; _ } -> TCon " Float"
86+ | TyCon { name = "Bool" ; _ } -> TCon " Bool"
87+ | TyCon { name = "String" ; _ } -> TCon " String"
88+ | TyCon { name = "Char" ; _ } -> TCon " Char"
89+ | TyCon { name = "Unit" ; _ } | TyTuple [] -> TCon " Unit"
90+ | TyCon { name = "Never" ; _ } -> TCon " Never"
91+ | TyCon { name; _ } -> TCon name
92+ | TyVar { name; _ } -> TCon name
93+ | TyApp ({ name; _ } , args ) ->
94+ let arg_tys = List. filter_map (fun a ->
95+ match a with TyArg te' -> Some (lower_simple te')
96+ ) args in
97+ TApp (TCon name, arg_tys)
98+ | TyTuple tes -> TTuple (List. map lower_simple tes)
99+ | TyArrow (a , _q , b , _eff ) ->
100+ TArrow (lower_simple a, QOmega , lower_simple b, EPure )
101+ | TyOwn te' -> TOwn (lower_simple te')
102+ | TyRef te' -> TRef (lower_simple te')
103+ | TyMut te' -> TMut (lower_simple te')
104+ | TyRecord (fields , _ ) ->
105+ let row = List. fold_right (fun (rf : row_field ) acc ->
106+ RExtend (rf.rf_name.name, lower_simple rf.rf_ty, acc)
107+ ) fields REmpty in
108+ TRecord row
109+ | TyHole ->
110+ let id = ! Types. next_tyvar in
111+ Types. next_tyvar := id + 1 ;
112+ TVar (ref (Unbound (id, 0 )))
113+ in
114+ Some (lower_simple te)
115+ in
71116 Some {
72117 tm_name = fs.fs_name.name;
73118 tm_type_params = fs.fs_type_params;
74119 tm_params = fs.fs_params;
75- tm_ret_ty = None ; (* Will be filled by type checker *)
120+ tm_ret_ty = ret_ty;
76121 tm_has_default = false ;
77122 }
78123 | TraitFnDefault fd ->
124+ let ret_ty = match fd.fd_ret_ty with
125+ | None -> None
126+ | Some te ->
127+ let rec lower_simple (te : type_expr ) : ty =
128+ match te with
129+ | TyCon { name = "Int" ; _ } -> TCon " Int"
130+ | TyCon { name = "Float" ; _ } -> TCon " Float"
131+ | TyCon { name = "Bool" ; _ } -> TCon " Bool"
132+ | TyCon { name = "String" ; _ } -> TCon " String"
133+ | TyCon { name = "Char" ; _ } -> TCon " Char"
134+ | TyCon { name = "Unit" ; _ } | TyTuple [] -> TCon " Unit"
135+ | TyCon { name = "Never" ; _ } -> TCon " Never"
136+ | TyCon { name; _ } -> TCon name
137+ | TyVar { name; _ } -> TCon name
138+ | TyApp ({ name; _ } , args ) ->
139+ let arg_tys = List. filter_map (fun a ->
140+ match a with TyArg te' -> Some (lower_simple te')
141+ ) args in
142+ TApp (TCon name, arg_tys)
143+ | TyTuple tes -> TTuple (List. map lower_simple tes)
144+ | TyArrow (a , _q , b , _eff ) ->
145+ TArrow (lower_simple a, QOmega , lower_simple b, EPure )
146+ | TyOwn te' -> TOwn (lower_simple te')
147+ | TyRef te' -> TRef (lower_simple te')
148+ | TyMut te' -> TMut (lower_simple te')
149+ | TyRecord (fields , _ ) ->
150+ let row = List. fold_right (fun (rf : row_field ) acc ->
151+ RExtend (rf.rf_name.name, lower_simple rf.rf_ty, acc)
152+ ) fields REmpty in
153+ TRecord row
154+ | TyHole ->
155+ let id = ! Types. next_tyvar in
156+ Types. next_tyvar := id + 1 ;
157+ TVar (ref (Unbound (id, 0 )))
158+ in
159+ Some (lower_simple te)
160+ in
79161 Some {
80162 tm_name = fd.fd_name.name;
81163 tm_type_params = fd.fd_type_params;
82164 tm_params = fd.fd_params;
83- tm_ret_ty = None ; (* Will be filled by type checker *)
165+ tm_ret_ty = ret_ty;
84166 tm_has_default = true ;
85167 }
86168 | TraitType _ -> None
@@ -204,42 +286,118 @@ let check_impl_satisfies_trait (registry : trait_registry) (impl : trait_impl) :
204286 | Some _ -> Ok ()
205287 ) (Ok () ) trait_def.td_assoc_types
206288
207- (* * Find implementation of a trait for a given type *)
208- let find_impl (registry : trait_registry ) (trait_name : string ) (self_ty : ty ) : trait_impl option =
289+ (* * Substitute type-param names with concrete types in a ty.
290+
291+ [subst] maps type-parameter names to fresh unification variables.
292+ We walk the type tree and replace [TCon name] with [Hashtbl.find subst name]
293+ wherever a type parameter of that name exists. *)
294+ let rec subst_ty (subst : (string, ty) Hashtbl.t ) (ty : ty ) : ty =
295+ match Types. repr ty with
296+ | TVar _ -> ty
297+ | TCon name ->
298+ begin match Hashtbl. find_opt subst name with
299+ | Some replacement -> replacement
300+ | None -> ty
301+ end
302+ | TApp (head , args ) ->
303+ TApp (subst_ty subst head, List. map (subst_ty subst) args)
304+ | TArrow (a , q , b , eff ) ->
305+ TArrow (subst_ty subst a, q, subst_ty subst b, eff)
306+ | TTuple tys ->
307+ TTuple (List. map (subst_ty subst) tys)
308+ | TRecord row ->
309+ TRecord (subst_row subst row)
310+ | TVariant row ->
311+ TVariant (subst_row subst row)
312+ | TForall (v , k , body ) ->
313+ TForall (v, k, subst_ty subst body)
314+ | TExists (v , k , body ) ->
315+ TExists (v, k, subst_ty subst body)
316+ | TRef t -> TRef (subst_ty subst t)
317+ | TMut t -> TMut (subst_ty subst t)
318+ | TOwn t -> TOwn (subst_ty subst t)
319+
320+ and subst_row (subst : (string, ty) Hashtbl.t ) (row : row ) : row =
321+ match Types. repr_row row with
322+ | REmpty -> REmpty
323+ | RExtend (l , ty , rest ) ->
324+ RExtend (l, subst_ty subst ty, subst_row subst rest)
325+ | RVar _ -> row
326+
327+ (* * Create a fresh instantiation of an impl's self type.
328+
329+ For each type parameter declared on the impl, we create a fresh
330+ unification variable and substitute it for the parameter name in
331+ the impl's self type. This allows unification-based matching
332+ without permanently committing to any particular substitution.
333+
334+ [fresh_var] should create a fresh [TVar (ref (Unbound (...)))] at
335+ the caller's current unification level. *)
336+ let fresh_impl_self_ty (impl : trait_impl ) (fresh_var : unit -> ty ) : ty =
337+ let subst = Hashtbl. create 4 in
338+ List. iter (fun (tp : type_param ) ->
339+ Hashtbl. replace subst tp.tp_name.name (fresh_var () )
340+ ) impl.ti_type_params;
341+ if Hashtbl. length subst = 0 then
342+ impl.ti_self_ty
343+ else
344+ subst_ty subst impl.ti_self_ty
345+
346+ (* * Find implementation of a trait for a given type using unification.
347+
348+ For each candidate impl we:
349+ 1. Instantiate its type parameters as fresh unification variables.
350+ 2. Attempt [Unify.unify self_ty instantiated_self_ty].
351+ 3. If unification succeeds the substitution is captured in the mutable
352+ type variables — we return that impl.
353+ 4. If unification fails we move on to the next candidate.
354+
355+ The [fresh_var] callback creates a new [TVar (Unbound _)] at the
356+ appropriate level; callers typically pass a closure over [ctx.level]. *)
357+ let find_impl_with_unify (registry : trait_registry ) (trait_name : string )
358+ (self_ty : ty ) (fresh_var : unit -> ty ) : trait_impl option =
209359 match Hashtbl. find_opt registry.impls trait_name with
210360 | None -> None
211361 | Some impls ->
212- (* Find impl where self_ty matches ti_self_ty *)
213- (* For now, simple name matching - TODO: proper unification *)
214- let rec type_name = function
215- | TVar _ -> None (* Type variables don't have concrete names *)
216- | TCon name -> Some name
217- | TApp (TCon name , _ ) -> Some name
218- | TApp (ty , _ ) -> type_name ty
219- | _ -> None
220- in
221- let self_name = type_name self_ty in
222362 List. find_opt (fun impl ->
223- match (self_name, type_name impl.ti_self_ty) with
224- | (Some n1 , Some n2 ) -> n1 = n2
225- | _ -> false
363+ let candidate_self = fresh_impl_self_ty impl fresh_var in
364+ match Unify. unify self_ty candidate_self with
365+ | Ok () -> true
366+ | Error _ -> false
226367 ) impls
227368
228- (* * Find all implementations for a given type (search all traits) *)
369+ (* * Find implementation of a trait for a given type.
370+
371+ Uses unification-based matching when fresh type variables are available
372+ (via [~fresh_var]). Falls back to structural constructor-name matching
373+ when no [fresh_var] callback is supplied (e.g. from legacy call sites). *)
374+ let find_impl (registry : trait_registry ) (trait_name : string ) (self_ty : ty ) : trait_impl option =
375+ (* Use a simple level-0 fresh var for the fallback path *)
376+ let fresh_var () =
377+ let id = ! Types. next_tyvar in
378+ Types. next_tyvar := id + 1 ;
379+ TVar (ref (Unbound (id, 0 )))
380+ in
381+ find_impl_with_unify registry trait_name self_ty fresh_var
382+
383+ (* * Find all implementations for a given type across all traits.
384+
385+ Uses the same unification-based matching as [find_impl]. Each candidate
386+ self type is instantiated with fresh type variables so that impls with
387+ generic parameters (e.g. [impl Display for Option[T]]) are handled
388+ correctly by structural unification. *)
229389let find_impls_for_type (registry : trait_registry ) (self_ty : ty ) : trait_impl list =
390+ let fresh_var () =
391+ let id = ! Types. next_tyvar in
392+ Types. next_tyvar := id + 1 ;
393+ TVar (ref (Unbound (id, 0 )))
394+ in
230395 Hashtbl. fold (fun _trait_name impls acc ->
231396 let matching = List. filter (fun impl ->
232- (* Simple type matching - TODO: proper unification *)
233- let rec type_name = function
234- | TVar _ -> None (* Type variables don't have concrete names *)
235- | TCon name -> Some name
236- | TApp (TCon name , _ ) -> Some name
237- | TApp (ty , _ ) -> type_name ty
238- | _ -> None
239- in
240- match (type_name self_ty, type_name impl.ti_self_ty) with
241- | (Some n1 , Some n2 ) -> n1 = n2
242- | _ -> false
397+ let candidate_self = fresh_impl_self_ty impl fresh_var in
398+ match Unify. unify self_ty candidate_self with
399+ | Ok () -> true
400+ | Error _ -> false
243401 ) impls in
244402 matching @ acc
245403 ) registry.impls []
0 commit comments