@@ -19,24 +19,24 @@ use hir_def::{
19
19
resolver:: resolver_for_expr,
20
20
ConstParamId , FieldId , ItemContainerId , Lookup ,
21
21
} ;
22
- use hir_expand:: name:: Name ;
22
+ use hir_expand:: { name, name :: Name } ;
23
23
use stdx:: always;
24
24
use syntax:: ast:: RangeOp ;
25
25
26
26
use crate :: {
27
27
autoderef:: { self , Autoderef } ,
28
28
consteval,
29
- infer:: { coerce:: CoerceMany , find_continuable, BreakableKind } ,
29
+ infer:: { coerce:: CoerceMany , find_continuable, path , BreakableKind } ,
30
30
lower:: {
31
31
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode ,
32
32
} ,
33
33
mapping:: { from_chalk, ToChalk } ,
34
34
method_resolution:: { self , lang_names_for_bin_op, VisibleFromModule } ,
35
35
primitive:: { self , UintTy } ,
36
- static_lifetime, to_chalk_trait_id,
36
+ static_lifetime, to_assoc_type_id , to_chalk_trait_id,
37
37
utils:: { generics, Generics } ,
38
- AdtId , Binders , CallableDefId , FnPointer , FnSig , FnSubst , Interner , Rawness , Scalar ,
39
- Substitution , TraitRef , Ty , TyBuilder , TyExt , TyKind ,
38
+ AdtId , AliasEq , AliasTy , Binders , CallableDefId , FnPointer , FnSig , FnSubst , Interner ,
39
+ ProjectionTy , Rawness , Scalar , Substitution , TraitRef , Ty , TyBuilder , TyExt , TyKind ,
40
40
} ;
41
41
42
42
use super :: {
@@ -564,9 +564,29 @@ impl<'a> InferenceContext<'a> {
564
564
let inner_ty = self . infer_expr_inner ( * expr, & Expectation :: none ( ) ) ;
565
565
self . resolve_associated_type ( inner_ty, self . resolve_future_future_output ( ) )
566
566
}
567
- Expr :: Try { expr } => {
568
- let inner_ty = self . infer_expr_inner ( * expr, & Expectation :: none ( ) ) ;
569
- self . resolve_associated_type ( inner_ty, self . resolve_ops_try_ok ( ) )
567
+ & Expr :: Try { expr } => {
568
+ let inner_ty = self . infer_expr_inner ( expr, & Expectation :: none ( ) ) ;
569
+ match self . resolve_try_impl_for ( inner_ty. clone ( ) ) {
570
+ Some ( ( _, Some ( ( output, residual) ) ) ) => {
571
+ if let Some ( ( _trait, false ) ) =
572
+ self . implements_from_residual ( self . return_ty . clone ( ) , residual)
573
+ {
574
+ self . push_diagnostic ( InferenceDiagnostic :: IncorrectTryTarget {
575
+ expr : tgt_expr,
576
+ } ) ;
577
+ }
578
+ output
579
+ }
580
+ Some ( ( trait_, None ) ) => {
581
+ self . push_diagnostic ( InferenceDiagnostic :: DoesNotImplement {
582
+ expr,
583
+ trait_,
584
+ ty : inner_ty,
585
+ } ) ;
586
+ self . err_ty ( )
587
+ }
588
+ None => self . err_ty ( ) ,
589
+ }
570
590
}
571
591
Expr :: Cast { expr, type_ref } => {
572
592
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
@@ -1530,4 +1550,67 @@ impl<'a> InferenceContext<'a> {
1530
1550
let ctx = self . breakables . pop ( ) . expect ( "breakable stack broken" ) ;
1531
1551
( ctx. may_break . then ( || ctx. coerce . complete ( ) ) , res)
1532
1552
}
1553
+
1554
+ /// Check whether `ty` implements `FromResidual<r>`
1555
+ fn implements_from_residual ( & mut self , ty : Ty , r : Ty ) -> Option < ( hir_def:: TraitId , bool ) > {
1556
+ let from_residual_trait = self
1557
+ . resolver
1558
+ . resolve_known_trait ( self . db . upcast ( ) , & ( super :: path![ core:: ops:: FromResidual ] ) ) ?;
1559
+ let r = GenericArgData :: Ty ( r) . intern ( Interner ) ;
1560
+ let b = TyBuilder :: trait_ref ( self . db , from_residual_trait) ;
1561
+ if b. remaining ( ) != 2 {
1562
+ return Some ( ( from_residual_trait, false ) ) ;
1563
+ }
1564
+ let trait_ref = b. push ( ty) . push ( r) . build ( ) ;
1565
+ Some ( ( from_residual_trait, self . table . try_obligation ( trait_ref. cast ( Interner ) ) . is_some ( ) ) )
1566
+ }
1567
+
1568
+ fn resolve_try_impl_for ( & mut self , ty : Ty ) -> Option < ( hir_def:: TraitId , Option < ( Ty , Ty ) > ) > {
1569
+ let path = path ! [ core:: ops:: Try ] ;
1570
+ let trait_ = self . resolver . resolve_known_trait ( self . db . upcast ( ) , & path) ?;
1571
+
1572
+ let trait_ref = TyBuilder :: trait_ref ( self . db , trait_) . push ( ty) . build ( ) ;
1573
+ let substitution = trait_ref. substitution . clone ( ) ;
1574
+ self . push_obligation ( trait_ref. clone ( ) . cast ( Interner ) ) ;
1575
+
1576
+ let trait_data = self . db . trait_data ( trait_) ;
1577
+ let output = trait_data. associated_type_by_name ( & name ! [ Output ] ) ;
1578
+ let residual = trait_data. associated_type_by_name ( & name ! [ Residual ] ) ;
1579
+
1580
+ let output_ty = match output {
1581
+ Some ( output) => {
1582
+ let output_ty = self . table . new_type_var ( ) ;
1583
+ let alias_eq = AliasEq {
1584
+ alias : AliasTy :: Projection ( ProjectionTy {
1585
+ associated_ty_id : to_assoc_type_id ( output) ,
1586
+ substitution : substitution. clone ( ) ,
1587
+ } ) ,
1588
+ ty : output_ty. clone ( ) ,
1589
+ } ;
1590
+ self . push_obligation ( alias_eq. cast ( Interner ) ) ;
1591
+ output_ty
1592
+ }
1593
+ None => self . err_ty ( ) ,
1594
+ } ;
1595
+ let residual_ty = match residual {
1596
+ Some ( residual) => {
1597
+ let residual_ty = self . table . new_type_var ( ) ;
1598
+ let alias_eq = AliasEq {
1599
+ alias : AliasTy :: Projection ( ProjectionTy {
1600
+ associated_ty_id : to_assoc_type_id ( residual) ,
1601
+ substitution,
1602
+ } ) ,
1603
+ ty : residual_ty. clone ( ) ,
1604
+ } ;
1605
+ self . push_obligation ( alias_eq. cast ( Interner ) ) ;
1606
+ residual_ty
1607
+ }
1608
+ None => self . err_ty ( ) ,
1609
+ } ;
1610
+ // FIXME: We are doing the work twice here I think?
1611
+ Some ( (
1612
+ trait_,
1613
+ self . table . try_obligation ( trait_ref. cast ( Interner ) ) . map ( |_| ( output_ty, residual_ty) ) ,
1614
+ ) )
1615
+ }
1533
1616
}
0 commit comments