Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/expr.rs')
-rw-r--r--crates/hir-ty/src/infer/expr.rs99
1 files changed, 91 insertions, 8 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index f56108b26c..59ab50d071 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -19,24 +19,24 @@ use hir_def::{
resolver::resolver_for_expr,
ConstParamId, FieldId, ItemContainerId, Lookup,
};
-use hir_expand::name::Name;
+use hir_expand::{name, name::Name};
use stdx::always;
use syntax::ast::RangeOp;
use crate::{
autoderef::{self, Autoderef},
consteval,
- infer::{coerce::CoerceMany, find_continuable, BreakableKind},
+ infer::{coerce::CoerceMany, find_continuable, path, BreakableKind},
lower::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
},
mapping::{from_chalk, ToChalk},
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
primitive::{self, UintTy},
- static_lifetime, to_chalk_trait_id,
+ static_lifetime, to_assoc_type_id, to_chalk_trait_id,
utils::{generics, Generics},
- AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar,
- Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+ AdtId, AliasEq, AliasTy, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner,
+ ProjectionTy, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
@@ -564,9 +564,29 @@ impl<'a> InferenceContext<'a> {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
}
- Expr::Try { expr } => {
- let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
- self.resolve_associated_type(inner_ty, self.resolve_ops_try_ok())
+ &Expr::Try { expr } => {
+ let inner_ty = self.infer_expr_inner(expr, &Expectation::none());
+ match self.resolve_try_impl_for(inner_ty.clone()) {
+ Some((_, Some((output, residual)))) => {
+ if let Some((_trait, false)) =
+ self.implements_from_residual(self.return_ty.clone(), residual)
+ {
+ self.push_diagnostic(InferenceDiagnostic::IncorrectTryTarget {
+ expr: tgt_expr,
+ });
+ }
+ output
+ }
+ Some((trait_, None)) => {
+ self.push_diagnostic(InferenceDiagnostic::DoesNotImplement {
+ expr,
+ trait_,
+ ty: inner_ty,
+ });
+ self.err_ty()
+ }
+ None => self.err_ty(),
+ }
}
Expr::Cast { expr, type_ref } => {
// FIXME: propagate the "castable to" expectation (and find a test case that shows this is necessary)
@@ -1530,4 +1550,67 @@ impl<'a> InferenceContext<'a> {
let ctx = self.breakables.pop().expect("breakable stack broken");
(ctx.may_break.then(|| ctx.coerce.complete()), res)
}
+
+ /// Check whether `ty` implements `FromResidual<r>`
+ fn implements_from_residual(&mut self, ty: Ty, r: Ty) -> Option<(hir_def::TraitId, bool)> {
+ let from_residual_trait = self
+ .resolver
+ .resolve_known_trait(self.db.upcast(), &(super::path![core::ops::FromResidual]))?;
+ let r = GenericArgData::Ty(r).intern(Interner);
+ let b = TyBuilder::trait_ref(self.db, from_residual_trait);
+ if b.remaining() != 2 {
+ return Some((from_residual_trait, false));
+ }
+ let trait_ref = b.push(ty).push(r).build();
+ Some((from_residual_trait, self.table.try_obligation(trait_ref.cast(Interner)).is_some()))
+ }
+
+ fn resolve_try_impl_for(&mut self, ty: Ty) -> Option<(hir_def::TraitId, Option<(Ty, Ty)>)> {
+ let path = path![core::ops::Try];
+ let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
+
+ let trait_ref = TyBuilder::trait_ref(self.db, trait_).push(ty).build();
+ let substitution = trait_ref.substitution.clone();
+ self.push_obligation(trait_ref.clone().cast(Interner));
+
+ let trait_data = self.db.trait_data(trait_);
+ let output = trait_data.associated_type_by_name(&name![Output]);
+ let residual = trait_data.associated_type_by_name(&name![Residual]);
+
+ let output_ty = match output {
+ Some(output) => {
+ let output_ty = self.table.new_type_var();
+ let alias_eq = AliasEq {
+ alias: AliasTy::Projection(ProjectionTy {
+ associated_ty_id: to_assoc_type_id(output),
+ substitution: substitution.clone(),
+ }),
+ ty: output_ty.clone(),
+ };
+ self.push_obligation(alias_eq.cast(Interner));
+ output_ty
+ }
+ None => self.err_ty(),
+ };
+ let residual_ty = match residual {
+ Some(residual) => {
+ let residual_ty = self.table.new_type_var();
+ let alias_eq = AliasEq {
+ alias: AliasTy::Projection(ProjectionTy {
+ associated_ty_id: to_assoc_type_id(residual),
+ substitution,
+ }),
+ ty: residual_ty.clone(),
+ };
+ self.push_obligation(alias_eq.cast(Interner));
+ residual_ty
+ }
+ None => self.err_ty(),
+ };
+ // FIXME: We are doing the work twice here I think?
+ Some((
+ trait_,
+ self.table.try_obligation(trait_ref.cast(Interner)).map(|_| (output_ty, residual_ty)),
+ ))
+ }
}