@@ -532,268 +532,17 @@ pub fn contains_typevar_where(ty: &Ty, pred: &dyn Fn(&Name) -> bool) -> bool {
532532 }
533533}
534534
535- /// Infer type variable bindings by walking formal and actual types in parallel.
536- ///
537- /// When `formal` is `Ty::TypeVar("T", TyAttr::default())` and `actual` is `Ty::Int { attr: TyAttr::default() }`,
538- /// records `T → int` in `bindings`. For structural types, recurses into
539- /// matching structures. Conflicting inferences are merged via `union_ty`.
540- fn infer_bindings_inner (
541- formal : & Ty ,
542- actual : & Ty ,
543- bindings : & mut FxHashMap < Name , Ty > ,
544- allow_typevar_actuals : bool ,
545- // A *rigid* type variable that must never be bound from an argument — the
546- // pinned `Self` of an interface method call (mirrors rustc's `ty::Param`,
547- // which unification never instantiates). `None` = no rigid variable (the
548- // historical behavior). This is the only thing that distinguishes a
549- // Self-pinned call from any other; ordinary calls pass `None`, so their
550- // inference is completely unchanged.
551- rigid : Option < & Name > ,
552- ) {
553- fn nullable_non_null_part ( ty : & Ty ) -> Option < Ty > {
554- let Ty :: Union ( members, attr) = ty else {
555- return None ;
556- } ;
557- if !members. iter ( ) . any ( Ty :: is_null) {
558- return None ;
559- }
560- let non_null: Vec < Ty > = members
561- . iter ( )
562- . filter ( |member| !member. is_null ( ) )
563- . cloned ( )
564- . collect ( ) ;
565- match non_null. as_slice ( ) {
566- [ ] => None ,
567- [ single] => Some ( single. clone ( ) ) ,
568- _ => Some ( Ty :: Union ( non_null, attr. clone ( ) ) ) ,
569- }
570- }
571-
572- match ( formal, actual) {
573- ( Ty :: TypeVar ( name, _) , actual_ty) => {
574- if rigid == Some ( name) {
575- return ;
576- }
577- // Skip TypeVar-to-TypeVar bindings by default — they usually provide
578- // no information for ordinary call inference. Some higher-order
579- // callable-summary paths opt into preserving them explicitly.
580- if !allow_typevar_actuals && matches ! ( actual_ty, Ty :: TypeVar ( _, _) ) {
581- return ;
582- }
583- // An `Unknown` actual carries NO information: binding it (or
584- // unioning it into an existing binding) only poisons the result —
585- // e.g. an expected return of `SpawnParams<unknown, unknown>`
586- // driving phase-0 must not turn a param-bound `T = int` into
587- // `int | unknown`.
588- if matches ! ( actual_ty, Ty :: Unknown { .. } ) {
589- return ;
590- }
591- bindings
592- . entry ( name. clone ( ) )
593- . and_modify ( |existing| * existing = union_ty ( existing, actual_ty) )
594- . or_insert_with ( || actual_ty. clone ( ) ) ;
595- }
596- ( Ty :: List ( f, _) , Ty :: List ( a, _) ) => {
597- infer_bindings_inner ( f, a, bindings, allow_typevar_actuals, rigid) ;
598- }
599- (
600- Ty :: Map {
601- key : fk, value : fv, ..
602- } ,
603- Ty :: Map {
604- key : ak, value : av, ..
605- } ,
606- ) => {
607- infer_bindings_inner ( fk, ak, bindings, allow_typevar_actuals, rigid) ;
608- infer_bindings_inner ( fv, av, bindings, allow_typevar_actuals, rigid) ;
609- }
610- ( Ty :: Union ( _, _) , _) if nullable_non_null_part ( formal) . is_some ( ) => {
611- let formal_inner = nullable_non_null_part ( formal) . expect ( "checked above" ) ;
612- let actual_inner = nullable_non_null_part ( actual) . unwrap_or_else ( || actual. clone ( ) ) ;
613- infer_bindings_inner (
614- & formal_inner,
615- & actual_inner,
616- bindings,
617- allow_typevar_actuals,
618- rigid,
619- ) ;
620- }
621- ( Ty :: Union ( f_members, _) , Ty :: Union ( a_members, _) )
622- if f_members. len ( ) == a_members. len ( ) =>
623- {
624- for ( formal_member, actual_member) in f_members. iter ( ) . zip ( a_members. iter ( ) ) {
625- infer_bindings_inner (
626- formal_member,
627- actual_member,
628- bindings,
629- allow_typevar_actuals,
630- rigid,
631- ) ;
632- }
633- }
634- (
635- Ty :: Function {
636- params : fp,
637- ret : fr,
638- throws : fth,
639- ..
640- } ,
641- Ty :: Function {
642- params : ap,
643- ret : ar,
644- throws : ath,
645- ..
646- } ,
647- ) => {
648- for ( fp, ap) in fp. iter ( ) . zip ( ap. iter ( ) ) {
649- infer_bindings_inner ( & fp. ty , & ap. ty , bindings, allow_typevar_actuals, rigid) ;
650- }
651- infer_bindings_inner ( fr, ar, bindings, allow_typevar_actuals, rigid) ;
652- infer_bindings_inner ( fth, ath, bindings, allow_typevar_actuals, rigid) ;
653- }
654- ( Ty :: Class ( fn_name, f_args, _) , Ty :: Class ( an_name, a_args, _) ) if fn_name == an_name => {
655- for ( ft, at) in f_args. iter ( ) . zip ( a_args. iter ( ) ) {
656- infer_bindings_inner ( ft, at, bindings, allow_typevar_actuals, rigid) ;
657- }
658- }
659- // `Future<T, E>` is its own variant — descend into both params so the
660- // future combinators can infer `<T, E>` from a `Future<T, E>[]` arg.
661- ( Ty :: Future ( f_value, f_error, _) , Ty :: Future ( a_value, a_error, _) ) => {
662- infer_bindings_inner ( f_value, a_value, bindings, allow_typevar_actuals, rigid) ;
663- infer_bindings_inner ( f_error, a_error, bindings, allow_typevar_actuals, rigid) ;
664- }
665- // A heterogeneous future array — e.g. `[spawn { 1 }, spawn { 2 }]` —
666- // types as `(Future<A, EA> | Future<B, EB>)[]` because `Future` is
667- // invariant. Match the `Future<T, E>` formal against each union member
668- // so `T`/`E` bind to the union of the member value/error types (the
669- // TypeVar arm merges the per-member bindings via `union_ty`).
670- ( Ty :: Future ( _, _, _) , Ty :: Union ( members, _) ) => {
671- for member in members {
672- infer_bindings_inner ( formal, member, bindings, allow_typevar_actuals, rigid) ;
673- }
674- }
675- (
676- Ty :: Interface ( fn_name, f_args, f_assoc, _) ,
677- Ty :: Interface ( an_name, a_args, a_assoc, _) ,
678- ) if fn_name == an_name => {
679- for ( ft, at) in f_args. iter ( ) . zip ( a_args. iter ( ) ) {
680- infer_bindings_inner ( ft, at, bindings, allow_typevar_actuals, rigid) ;
681- }
682- for ( formal_name, formal_ty) in f_assoc {
683- if let Some ( ( _, actual_ty) ) = a_assoc
684- . iter ( )
685- . find ( |( actual_name, _) | actual_name == formal_name)
686- {
687- infer_bindings_inner (
688- formal_ty,
689- actual_ty,
690- bindings,
691- allow_typevar_actuals,
692- rigid,
693- ) ;
694- }
695- }
696- }
697- // Builtin container bridging: Array<T> ↔ List(T), Map<K,V> ↔ Map(K,V)
698- // This enables UFCS calls like `Array.length(arr)` where the formal self
699- // type is Class(Array, [T]) and the actual is List(int).
700- ( Ty :: Class ( class_name, f_args, _) , Ty :: List ( actual_inner, _) )
701- if class_name. is_builtin_root_type ( "Array" ) && f_args. len ( ) == 1 =>
702- {
703- infer_bindings_inner (
704- & f_args[ 0 ] ,
705- actual_inner,
706- bindings,
707- allow_typevar_actuals,
708- rigid,
709- ) ;
710- }
711- (
712- Ty :: Class ( class_name, f_args, _) ,
713- Ty :: Map {
714- key : actual_key,
715- value : actual_val,
716- ..
717- } ,
718- ) if class_name. is_builtin_root_type ( "Map" ) && f_args. len ( ) == 2 => {
719- infer_bindings_inner (
720- & f_args[ 0 ] ,
721- actual_key,
722- bindings,
723- allow_typevar_actuals,
724- rigid,
725- ) ;
726- infer_bindings_inner (
727- & f_args[ 1 ] ,
728- actual_val,
729- bindings,
730- allow_typevar_actuals,
731- rigid,
732- ) ;
733- }
734- _ => { } // Concrete types: nothing to infer
735- }
736- }
737-
738- pub fn infer_bindings ( formal : & Ty , actual : & Ty , bindings : & mut FxHashMap < Name , Ty > ) {
739- infer_bindings_inner ( formal, actual, bindings, false , None ) ;
740- }
741-
742- pub fn infer_bindings_allow_typevars ( formal : & Ty , actual : & Ty , bindings : & mut FxHashMap < Name , Ty > ) {
743- infer_bindings_inner ( formal, actual, bindings, true , None ) ;
744- }
745-
746- /// Like [`infer_bindings`] but treats `rigid` (when `Some`) as a rigid type
747- /// variable that is never bound from an argument — the pinned `Self` of an
748- /// interface method call. Every other variable infers exactly as before.
749- pub fn infer_bindings_rigid_self (
750- formal : & Ty ,
751- actual : & Ty ,
752- bindings : & mut FxHashMap < Name , Ty > ,
753- rigid : Option < & Name > ,
754- ) {
755- infer_bindings_inner ( formal, actual, bindings, false , rigid) ;
756- }
757-
758- /// Combine two types into a union, deduplicating members.
759- ///
760- /// Used when the same type variable is inferred from multiple arguments
761- /// (e.g., `deep_equals(myInt, myString)` → `T` gets `int` then `string`).
762- pub fn union_ty ( a : & Ty , b : & Ty ) -> Ty {
763- normalize_union_members ( [ a. clone ( ) , b. clone ( ) ] , TyAttr :: default ( ) )
764- }
765-
766- fn normalize_union_members ( members : impl IntoIterator < Item = Ty > , attr : TyAttr ) -> Ty {
767- let mut normalized = Vec :: new ( ) ;
768- for member in members {
769- match member {
770- Ty :: Never { .. } => { }
771- Ty :: Union ( inner, _) => {
772- for inner_member in inner {
773- if !matches ! ( inner_member, Ty :: Never { .. } )
774- && !normalized. contains ( & inner_member)
775- {
776- normalized. push ( inner_member) ;
777- }
778- }
779- }
780- other if !normalized. contains ( & other) => normalized. push ( other) ,
781- _ => { }
782- }
783- }
784-
785- match normalized. len ( ) {
786- 0 => Ty :: Never { attr } ,
787- 1 => normalized. pop ( ) . expect ( "length checked" ) ,
788- _ => {
789- // TODO(TyAttr): This union is synthesized from multiple input types — there's no
790- // single "original attr" to preserve. If inputs carry different attrs, which one
791- // wins? May need a merge/lattice operation on TyAttr, or default may be correct if
792- // attrs describe declaration sites rather than computed types.
793- Ty :: Union ( normalized, attr)
794- }
795- }
796- }
535+ // ── Type variable inference & union normalization ──────────────────────────────
536+ //
537+ // The pure `Ty`-walking inference/union primitives now live in `baml_type` so the
538+ // runtime engine can share the single algorithm (it widens `RuntimeTy` → `Ty`,
539+ // runs the unifier, narrows back) without a runtime → compiler dependency. They
540+ // are re-exported here so every existing `crate::generics::…` caller is
541+ // unchanged. See `01c-inbound-inference-reuse.md`.
542+ pub use baml_type:: generics:: {
543+ infer_bindings, infer_bindings_allow_typevars, infer_bindings_rigid_self,
544+ normalize_union_members, union_ty,
545+ } ;
797546
798547/// Replace any remaining `Ty::TypeVar` with `Ty::Unknown` and emit diagnostics.
799548///
0 commit comments