11use super :: { HMInferenceContext , MonoType } ;
2- use crate :: hir_ty:: hm:: inference:: annotated_to_mono;
32use crate :: hir_ty:: hm:: PolyType ;
3+ use crate :: hir_ty:: InferredType ;
4+ use crate :: value_signature:: infer_value_signature;
45use alloy_hir_def as hir;
56use alloy_hir_resolved as res;
6- use alloy_hir_resolved:: { resolve_annotated_type, Fql } ;
7+ use alloy_hir_resolved:: Fql ;
8+ use rustc_hash:: FxHashMap ;
79
810mod binary;
911mod function_call;
@@ -23,35 +25,35 @@ pub(crate) fn infer_expr_hm(
2325 return existing_ty;
2426 }
2527
26- // Lazy constraint generation: if this expression is in a later group,
27- // don't infer it now - just return a fresh type variable.
28- // Only applies to expressions in the current module — cross-module expressions
29- // have their own independent ordering and must always be inferred immediately.
30- // DON'T assign to type_env to avoid polluting env_type_vars for generalization
31- if source_fql. module_id == ctx. module_id && ctx. is_in_later_group ( source_fql. local_id ) {
32- return ctx. fresh_type_var ( ) ;
33- }
28+ // Use `infer_value_signature` for cross-module value definitions and
29+ // same-module definitions with polymorphic annotations (when referenced
30+ // from other definitions, not when being directly inferred).
31+ //
32+ // Skip the shortcut for:
33+ // - The expression currently being inferred (inferring_expr) to prevent cycles
34+ // and ensure sub-expression types are collected
35+ // - Same-module unannotated definitions, so call-site constraints flow back
36+ let is_self = ctx
37+ . inferring_expr
38+ . as_ref ( )
39+ . is_some_and ( |e| * e == source_fql) ;
40+ if !is_self {
41+ if let Some ( value_def) =
42+ hir:: module_value_def ( ctx. db , source_fql. module_id , source_fql. local_id )
43+ {
44+ let is_cross_module = source_fql. module_id != ctx. module_id ;
45+ let has_poly_annotation = value_def
46+ . type_annotation ( ctx. db )
47+ . map ( |ta| {
48+ alloy_hir_resolved:: resolve_annotated_type ( ctx. db , source_fql. module_id , ta)
49+ . is_polymorphic ( )
50+ } )
51+ . unwrap_or ( false ) ;
3452
35- // For cross-module expressions with polymorphic type annotations, use the annotation
36- // directly rather than re-inferring from the body. This ensures each use site gets
37- // fresh type variables via poly_env instantiation, avoiding stale specialization
38- // when a polymorphic cross-module function (e.g., <|) is used multiple times.
39- if source_fql. module_id != ctx. module_id {
40- let ( other_hir_module, _) = hir:: lower_file ( ctx. db , source_fql. module_id ) ;
41- if let Some ( value) = other_hir_module. get_value_by_id ( & source_fql. local_id ) {
42- if let Some ( type_annotation) = value. type_annotation {
43- let annotated =
44- resolve_annotated_type ( ctx. db , source_fql. module_id , type_annotation) ;
45- if annotated. is_polymorphic ( ) {
46- if let Some ( mono_ty) = annotated_to_mono ( & annotated, ctx) {
47- let poly_ty = PolyType :: generalize_all ( mono_ty) ;
48- ctx. poly_env . insert ( source_fql. clone ( ) . into ( ) , poly_ty) ;
49- // Return a fresh instantiation
50- if let Some ( instantiated) = ctx. maybe_find_type ( & source_fql) {
51- return instantiated;
52- }
53- }
54- }
53+ if is_cross_module || has_poly_annotation {
54+ let sig = infer_value_signature ( ctx. db , value_def) ;
55+ let mono_ty = inferred_to_mono ( & sig, ctx) ;
56+ return ctx. generalize_to_poly ( mono_ty, & source_fql) ;
5557 }
5658 }
5759 }
@@ -108,6 +110,80 @@ pub(crate) fn infer_expr_hm(
108110 }
109111}
110112
113+ /// Convert an InferredType (from `infer_value_signature`) to a MonoType for use
114+ /// in constraint generation. Generic IDs are mapped to fresh type variables,
115+ /// with consistent mapping so the same Generic(id) produces the same TypeVarId.
116+ fn inferred_to_mono ( inferred : & InferredType , ctx : & mut HMInferenceContext ) -> MonoType {
117+ let mut generic_map: FxHashMap < usize , super :: super :: TypeVarId > = FxHashMap :: default ( ) ;
118+ inferred_to_mono_inner ( inferred, ctx, & mut generic_map)
119+ }
120+
121+ fn inferred_to_mono_inner (
122+ inferred : & InferredType ,
123+ ctx : & mut HMInferenceContext ,
124+ generic_map : & mut FxHashMap < usize , super :: super :: TypeVarId > ,
125+ ) -> MonoType {
126+ match inferred {
127+ InferredType :: Unconstrained => MonoType :: Unconstrained ,
128+ InferredType :: Missing => ctx. fresh_type_var ( ) ,
129+ InferredType :: Unit => MonoType :: Unit ,
130+ InferredType :: BuiltIn ( b) => MonoType :: Concrete ( * b) ,
131+ InferredType :: TypeDef ( fql, name) => MonoType :: TypeDef {
132+ fql : fql. clone ( ) ,
133+ type_args : vec ! [ ] ,
134+ type_def_name : name. clone ( ) ,
135+ } ,
136+ InferredType :: Lambda {
137+ arg_type,
138+ return_type,
139+ } => MonoType :: Function (
140+ Box :: new ( inferred_to_mono_inner ( arg_type, ctx, generic_map) ) ,
141+ Box :: new ( inferred_to_mono_inner ( return_type, ctx, generic_map) ) ,
142+ ) ,
143+ InferredType :: Tuple ( elements) => MonoType :: Tuple (
144+ elements
145+ . iter ( )
146+ . map ( |e| inferred_to_mono_inner ( e, ctx, generic_map) )
147+ . collect ( ) ,
148+ ) ,
149+ InferredType :: Bounded { base, args } => {
150+ // When the base is a TypeDef, populate its type_args with fresh TypeVarIds
151+ // to preserve arity information (used in error messages and display)
152+ let constructor = match base. as_ref ( ) {
153+ InferredType :: TypeDef ( fql, name) => {
154+ let type_args = args. iter ( ) . map ( |_| ctx. type_var_gen . fresh ( ) ) . collect ( ) ;
155+ MonoType :: TypeDef {
156+ fql : fql. clone ( ) ,
157+ type_args,
158+ type_def_name : name. clone ( ) ,
159+ }
160+ }
161+ other => inferred_to_mono_inner ( other, ctx, generic_map) ,
162+ } ;
163+ MonoType :: App {
164+ constructor : Box :: new ( constructor) ,
165+ args : args
166+ . iter ( )
167+ . map ( |a| inferred_to_mono_inner ( a, ctx, generic_map) )
168+ . collect ( ) ,
169+ }
170+ }
171+ InferredType :: Generic ( id) => {
172+ let var_id = * generic_map
173+ . entry ( * id)
174+ . or_insert_with ( || ctx. type_var_gen . fresh ( ) ) ;
175+ MonoType :: Var ( var_id)
176+ }
177+ InferredType :: ConstrainedGeneric { id, .. } => {
178+ // TODO: Track constraints during solving
179+ let var_id = * generic_map
180+ . entry ( * id)
181+ . or_insert_with ( || ctx. type_var_gen . fresh ( ) ) ;
182+ MonoType :: Var ( var_id)
183+ }
184+ }
185+ }
186+
111187fn infer_missing_expr ( ctx : & mut HMInferenceContext , source_fql : Fql < hir:: Expression > ) -> MonoType {
112188 let ty = ctx. fresh_type_var ( ) ;
113189 ctx. assign_type ( source_fql, ty)
0 commit comments