Skip to content

Commit 944f71a

Browse files
committed
projection over std::ops::Try::Ok to infer try/?
1 parent 64b718b commit 944f71a

File tree

3 files changed

+77
-2
lines changed

3 files changed

+77
-2
lines changed

crates/ra_hir/src/name.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ pub(crate) const ITER: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"iter
115115
pub(crate) const INTO_ITERATOR: Name =
116116
Name::new(SmolStr::new_inline_from_ascii(12, b"IntoIterator"));
117117
pub(crate) const ITEM: Name = Name::new(SmolStr::new_inline_from_ascii(4, b"Item"));
118+
pub(crate) const OPS: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"ops"));
119+
pub(crate) const TRY: Name = Name::new(SmolStr::new_inline_from_ascii(3, b"Try"));
120+
pub(crate) const OK: Name = Name::new(SmolStr::new_inline_from_ascii(2, b"Ok"));
118121

119122
fn resolve_name(text: &SmolStr) -> SmolStr {
120123
let raw_start = "r#";

crates/ra_hir/src/ty/infer.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,8 +1140,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
11401140
self.insert_type_vars(ty)
11411141
}
11421142
Expr::Try { expr } => {
1143-
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
1144-
Ty::Unknown
1143+
let inner_ty = self.infer_expr(*expr, &Expectation::none());
1144+
let ty = match self.resolve_ops_try_ok() {
1145+
Some(ops_try_ok_alias) => {
1146+
let ty = self.new_type_var();
1147+
let projection = ProjectionPredicate {
1148+
ty: ty.clone(),
1149+
projection_ty: ProjectionTy {
1150+
associated_ty: ops_try_ok_alias,
1151+
parameters: vec![inner_ty].into(),
1152+
},
1153+
};
1154+
self.obligations.push(Obligation::Projection(projection));
1155+
self.resolve_ty_as_possible(&mut vec![], ty)
1156+
}
1157+
None => Ty::Unknown,
1158+
};
1159+
ty
11451160
}
11461161
Expr::Cast { expr, type_ref } => {
11471162
let _inner_ty = self.infer_expr(*expr, &Expectation::none());
@@ -1360,6 +1375,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
13601375
_ => None,
13611376
}
13621377
}
1378+
1379+
fn resolve_ops_try_ok(&self) -> Option<TypeAlias> {
1380+
use crate::name::{OK, OPS, TRY};
1381+
1382+
let ops_try_path = Path {
1383+
kind: PathKind::Abs,
1384+
segments: vec![
1385+
PathSegment { name: STD, args_and_bindings: None },
1386+
PathSegment { name: OPS, args_and_bindings: None },
1387+
PathSegment { name: TRY, args_and_bindings: None },
1388+
],
1389+
};
1390+
1391+
match self.resolver.resolve_path_segments(self.db, &ops_try_path).into_fully_resolved() {
1392+
PerNs { types: Some(Def(Trait(trait_))), .. } => {
1393+
Some(trait_.associated_type_by_name(self.db, OK)?)
1394+
}
1395+
_ => None,
1396+
}
1397+
}
13631398
}
13641399

13651400
/// The ID of a type variable.

crates/ra_hir/src/ty/tests.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,42 @@ use crate::{
2020
// against snapshots of the expected results using insta. Use cargo-insta to
2121
// update the snapshots.
2222

23+
#[test]
24+
fn infer_try() {
25+
let (mut db, pos) = MockDatabase::with_position(
26+
r#"
27+
//- /main.rs
28+
enum Result<O, E> {
29+
Ok(O),
30+
Err(E)
31+
}
32+
33+
impl<O, E> ::std::ops::Try for Result<O, E> {
34+
type Ok = O;
35+
type Error = E;
36+
}
37+
fn test() {
38+
let r: Result<i32, u64> = Result::Ok(1);
39+
let v = r?;
40+
v<|>;
41+
}
42+
43+
//- /lib.rs
44+
mod ops {
45+
trait Try {
46+
type Ok;
47+
type Error;
48+
}
49+
}
50+
"#,
51+
);
52+
db.set_crate_graph_from_fixture(crate_graph! {
53+
"main": ("/main.rs", ["std"]),
54+
"std": ("/lib.rs", []),
55+
});
56+
assert_eq!("i32", type_at_pos(&db, pos));
57+
}
58+
2359
#[test]
2460
fn infer_for_loop() {
2561
let (mut db, pos) = MockDatabase::with_position(
@@ -56,6 +92,7 @@ mod iter {
5692
});
5793
assert_eq!("&str", type_at_pos(&db, pos));
5894
}
95+
5996
#[test]
6097
fn infer_basics() {
6198
assert_snapshot_matches!(

0 commit comments

Comments
 (0)