@@ -19,24 +19,24 @@ use hir_def::{
1919 resolver:: resolver_for_expr,
2020 ConstParamId , FieldId , ItemContainerId , Lookup ,
2121} ;
22- use hir_expand:: name:: Name ;
22+ use hir_expand:: { name, name :: Name } ;
2323use stdx:: always;
2424use syntax:: ast:: RangeOp ;
2525
2626use crate :: {
2727 autoderef:: { self , Autoderef } ,
2828 consteval,
29- infer:: { coerce:: CoerceMany , find_continuable, BreakableKind } ,
29+ infer:: { coerce:: CoerceMany , find_continuable, path , BreakableKind } ,
3030 lower:: {
3131 const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode ,
3232 } ,
3333 mapping:: { from_chalk, ToChalk } ,
3434 method_resolution:: { self , lang_names_for_bin_op, VisibleFromModule } ,
3535 primitive:: { self , UintTy } ,
36- static_lifetime, to_chalk_trait_id,
36+ static_lifetime, to_assoc_type_id , to_chalk_trait_id,
3737 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 ,
4040} ;
4141
4242use super :: {
@@ -564,9 +564,29 @@ impl<'a> InferenceContext<'a> {
564564 let inner_ty = self . infer_expr_inner ( * expr, & Expectation :: none ( ) ) ;
565565 self . resolve_associated_type ( inner_ty, self . resolve_future_future_output ( ) )
566566 }
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+ }
570590 }
571591 Expr :: Cast { expr, type_ref } => {
572592 // FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
@@ -1530,4 +1550,67 @@ impl<'a> InferenceContext<'a> {
15301550 let ctx = self . breakables . pop ( ) . expect ( "breakable stack broken" ) ;
15311551 ( ctx. may_break . then ( || ctx. coerce . complete ( ) ) , res)
15321552 }
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+ }
15331616}
0 commit comments