11use either:: Either ;
2+ use smallvec1:: SmallVec ;
23
34use crate :: core:: hir_def:: {
45 ArithBinOp , BinOp , CallableDef , Expr , ExprId , FieldIndex , IdentId , Partial , Pat , PatId , PathId ,
@@ -43,6 +44,7 @@ use crate::analysis::{
4344 } ,
4445} ;
4546
47+ #[ derive( Debug , Clone , Copy ) ]
4648enum EffectRequirement < ' db > {
4749 Type ( TyId < ' db > ) ,
4850 Trait ( TraitInstId < ' db > ) ,
@@ -203,9 +205,6 @@ impl<'db> TyChecker<'db> {
203205
204206 for binding in bindings {
205207 let value_prop = self . check_expr_unknown ( binding. value ) ;
206- let Some ( key_path) = binding. key_path . to_opt ( ) else {
207- continue ;
208- } ;
209208
210209 let is_mut = value_prop
211210 . binding
@@ -220,7 +219,16 @@ impl<'db> TyChecker<'db> {
220219 is_mut,
221220 } ;
222221
223- self . env . insert_effect_binding ( key_path, provided) ;
222+ match binding. key_path {
223+ Some ( key_path) => {
224+ if let Some ( key_path) = key_path. to_opt ( ) {
225+ self . env . insert_effect_binding ( key_path, provided) ;
226+ }
227+ }
228+ None => {
229+ self . env . insert_unkeyed_effect_binding ( provided) ;
230+ }
231+ }
224232 }
225233
226234 let result = self . check_expr ( body_expr, expected) ;
@@ -294,7 +302,8 @@ impl<'db> TyChecker<'db> {
294302 return ;
295303 }
296304
297- let call_span = expr. span ( self . body ( ) ) ;
305+ let body = self . body ( ) ;
306+ let call_span = expr. span ( body) ;
298307 let callee_assumptions = collect_func_def_constraints ( self . db , func. into ( ) , true )
299308 . instantiate_identity ( )
300309 . extend_all_bounds ( self . db ) ;
@@ -321,8 +330,135 @@ impl<'db> TyChecker<'db> {
321330 let cands =
322331 self . env
323332 . effect_candidates_in_scope ( key_path, func. scope ( ) , callee_assumptions) ;
324- let provided = match cands. as_slice ( ) {
333+ if cands. is_empty ( ) {
334+ let diag = BodyDiag :: MissingEffect {
335+ primary : call_span. clone ( ) . into ( ) ,
336+ func,
337+ key : key_path,
338+ } ;
339+ self . push_diag ( diag) ;
340+ continue ;
341+ }
342+
343+ let required_mut = effect. is_mut ( self . db ) ;
344+ let mut_compatible: SmallVec < [ ProvidedEffect < ' db > ; 2 ] > = cands
345+ . iter ( )
346+ . copied ( )
347+ . filter ( |p| !required_mut || p. is_mut )
348+ . collect ( ) ;
349+
350+ let provided_span = |provided : ProvidedEffect < ' db > | match provided. origin {
351+ EffectOrigin :: With { value_expr } => Some ( value_expr. span ( body) . into ( ) ) ,
352+ EffectOrigin :: Param { .. } => None ,
353+ } ;
354+
355+ if mut_compatible. is_empty ( ) {
356+ // Effects are present but don't satisfy mutability.
357+ let diag = BodyDiag :: EffectMutabilityMismatch {
358+ primary : call_span. clone ( ) . into ( ) ,
359+ func,
360+ key : key_path,
361+ provided_span : cands. first ( ) . copied ( ) . and_then ( provided_span) ,
362+ } ;
363+ self . push_diag ( diag) ;
364+ continue ;
365+ }
366+
367+ let ingot = self . env . body ( ) . top_mod ( self . db ) . ingot ( self . db ) ;
368+ let mut viable: SmallVec < [ ( ProvidedEffect < ' db > , EffectRequirement < ' db > ) ; 2 ] > =
369+ SmallVec :: new ( ) ;
370+ for provided in mut_compatible. iter ( ) . copied ( ) {
371+ let Some ( requirement) = self . resolve_effect_requirement (
372+ key_path,
373+ callable,
374+ func. scope ( ) ,
375+ callee_assumptions,
376+ provided. ty ,
377+ ) else {
378+ continue ;
379+ } ;
380+
381+ match requirement {
382+ EffectRequirement :: Type ( expected) => {
383+ let snapshot = self . table . snapshot ( ) ;
384+ let ok = self . table . unify ( expected, provided. ty ) . is_ok ( ) ;
385+ self . table . rollback_to ( snapshot) ;
386+ if ok {
387+ viable. push ( ( provided, EffectRequirement :: Type ( expected) ) ) ;
388+ }
389+ }
390+ EffectRequirement :: Trait ( trait_req) => {
391+ let canonical = Canonicalized :: new ( self . db , trait_req) ;
392+ match is_goal_satisfiable (
393+ self . db ,
394+ ingot,
395+ canonical. value ,
396+ self . env . assumptions ( ) ,
397+ ) {
398+ GoalSatisfiability :: UnSat ( _) => { }
399+ GoalSatisfiability :: ContainsInvalid => { }
400+ _ => viable. push ( ( provided, EffectRequirement :: Trait ( trait_req) ) ) ,
401+ }
402+ }
403+ }
404+ }
405+
406+ let ( provided, requirement) = match viable. as_slice ( ) {
325407 [ ] => {
408+ // Preserve detailed mismatch diagnostics when there's a single candidate.
409+ if mut_compatible. len ( ) == 1 {
410+ let provided = mut_compatible[ 0 ] ;
411+ let Some ( requirement) = self . resolve_effect_requirement (
412+ key_path,
413+ callable,
414+ func. scope ( ) ,
415+ callee_assumptions,
416+ provided. ty ,
417+ ) else {
418+ continue ;
419+ } ;
420+ match requirement {
421+ EffectRequirement :: Type ( expected) => {
422+ if self . table . unify ( expected, provided. ty ) . is_err ( ) {
423+ let diag = BodyDiag :: EffectTypeMismatch {
424+ primary : call_span. clone ( ) . into ( ) ,
425+ func,
426+ key : key_path,
427+ expected,
428+ given : provided. ty ,
429+ provided_span : provided_span ( provided) ,
430+ } ;
431+ self . push_diag ( diag) ;
432+ }
433+ }
434+ EffectRequirement :: Trait ( trait_req) => {
435+ let canonical = Canonicalized :: new ( self . db , trait_req) ;
436+ match is_goal_satisfiable (
437+ self . db ,
438+ ingot,
439+ canonical. value ,
440+ self . env . assumptions ( ) ,
441+ ) {
442+ GoalSatisfiability :: UnSat ( _) => {
443+ let diag = BodyDiag :: EffectTraitUnsatisfied {
444+ primary : call_span. clone ( ) . into ( ) ,
445+ func,
446+ key : key_path,
447+ trait_req,
448+ given : provided. ty ,
449+ provided_span : provided_span ( provided) ,
450+ } ;
451+ self . push_diag ( diag) ;
452+ }
453+ GoalSatisfiability :: ContainsInvalid => { }
454+ _ => { }
455+ }
456+ }
457+ }
458+ continue ;
459+ }
460+
461+ // Multiple candidates exist, but none can satisfy this effect.
326462 let diag = BodyDiag :: MissingEffect {
327463 primary : call_span. clone ( ) . into ( ) ,
328464 func,
@@ -331,7 +467,7 @@ impl<'db> TyChecker<'db> {
331467 self . push_diag ( diag) ;
332468 continue ;
333469 }
334- [ one ] => * one ,
470+ [ ( provided , requirement ) ] => ( * provided , * requirement ) ,
335471 _ => {
336472 let diag = BodyDiag :: AmbiguousEffect {
337473 primary : call_span. clone ( ) . into ( ) ,
@@ -343,74 +479,37 @@ impl<'db> TyChecker<'db> {
343479 }
344480 } ;
345481
346- if effect. is_mut ( self . db ) && !provided. is_mut {
347- let provided_span = match provided. origin {
348- EffectOrigin :: With { value_expr } => Some ( value_expr. span ( self . body ( ) ) . into ( ) ) ,
349- EffectOrigin :: Param { .. } => None ,
350- } ;
351- let diag = BodyDiag :: EffectMutabilityMismatch {
352- primary : call_span. clone ( ) . into ( ) ,
353- func,
354- key : key_path,
355- provided_span,
356- } ;
357- self . push_diag ( diag) ;
358- continue ;
359- }
360-
361- let Some ( requirement) = self . resolve_effect_requirement (
362- key_path,
363- callable,
364- func. scope ( ) ,
365- callee_assumptions,
366- provided. ty ,
367- ) else {
368- continue ;
369- } ;
370-
371482 match requirement {
372483 EffectRequirement :: Type ( expected) => {
484+ // Commit unification for the selected candidate.
373485 if self . table . unify ( expected, provided. ty ) . is_err ( ) {
374- let provided_span = match provided. origin {
375- EffectOrigin :: With { value_expr } => {
376- Some ( value_expr. span ( self . body ( ) ) . into ( ) )
377- }
378- EffectOrigin :: Param { .. } => None ,
379- } ;
380486 let diag = BodyDiag :: EffectTypeMismatch {
381487 primary : call_span. clone ( ) . into ( ) ,
382488 func,
383489 key : key_path,
384490 expected,
385491 given : provided. ty ,
386- provided_span,
492+ provided_span : provided_span ( provided ) ,
387493 } ;
388494 self . push_diag ( diag) ;
389495 }
390496 }
391497 EffectRequirement :: Trait ( trait_req) => {
392498 let canonical = Canonicalized :: new ( self . db , trait_req) ;
393- let ingot = self . env . body ( ) . top_mod ( self . db ) . ingot ( self . db ) ;
394499 match is_goal_satisfiable (
395500 self . db ,
396501 ingot,
397502 canonical. value ,
398503 self . env . assumptions ( ) ,
399504 ) {
400505 GoalSatisfiability :: UnSat ( _) => {
401- let provided_span = match provided. origin {
402- EffectOrigin :: With { value_expr } => {
403- Some ( value_expr. span ( self . body ( ) ) . into ( ) )
404- }
405- EffectOrigin :: Param { .. } => None ,
406- } ;
407506 let diag = BodyDiag :: EffectTraitUnsatisfied {
408507 primary : call_span. clone ( ) . into ( ) ,
409508 func,
410509 key : key_path,
411510 trait_req,
412511 given : provided. ty ,
413- provided_span,
512+ provided_span : provided_span ( provided ) ,
414513 } ;
415514 self . push_diag ( diag) ;
416515 }
0 commit comments