Skip to content

Commit 64a1b26

Browse files
committed
Implement CoerceMany
1 parent 9779526 commit 64a1b26

File tree

3 files changed

+120
-89
lines changed

3 files changed

+120
-89
lines changed

crates/hir_ty/src/infer/closure.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ impl InferenceContext<'_> {
2525
};
2626

2727
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
28-
if let Ok(res) = self.coerce(closure_ty, &expected_ty) {
29-
self.write_expr_adj(closure_expr, res.value.0);
30-
}
28+
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
29+
3130
// Deduction based on the expected `dyn Fn` is done separately.
3231
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(&Interner) {
3332
if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) {

crates/hir_ty/src/infer/coerce.rs

Lines changed: 85 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use hir_def::{expr::ExprId, lang_item::LangItemTarget};
1010

1111
use crate::{
1212
autoderef,
13-
infer::{Adjust, Adjustment, AutoBorrow, PointerCast, TypeMismatch},
13+
infer::{Adjust, Adjustment, AutoBorrow, InferResult, PointerCast, TypeMismatch},
1414
static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Interner, Solution, Substitution, Ty,
1515
TyBuilder, TyExt, TyKind,
1616
};
@@ -36,23 +36,25 @@ fn success(
3636
) -> CoerceResult {
3737
Ok(InferOk { goals, value: (adj, target) })
3838
}
39+
#[derive(Clone, Debug)]
40+
pub(super) struct CoerceMany {
41+
expected_ty: Ty,
42+
}
3943

40-
impl<'a> InferenceContext<'a> {
41-
/// Unify two types, but may coerce the first one to the second one
42-
/// using "implicit coercion rules" if needed.
43-
pub(super) fn coerce(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult {
44-
let from_ty = self.resolve_ty_shallow(from_ty);
45-
let to_ty = self.resolve_ty_shallow(to_ty);
46-
match self.coerce_inner(from_ty, &to_ty) {
47-
Ok(InferOk { value, goals }) => {
48-
self.table.register_infer_ok(InferOk { value: (), goals });
49-
Ok(InferOk { value, goals: Vec::new() })
50-
}
51-
Err(e) => {
52-
// FIXME deal with error
53-
Err(e)
54-
}
55-
}
44+
impl CoerceMany {
45+
pub(super) fn new(expected: Ty) -> Self {
46+
CoerceMany { expected_ty: expected }
47+
}
48+
49+
pub(super) fn once(
50+
ctx: &mut InferenceContext<'_>,
51+
expected: Ty,
52+
expr: Option<ExprId>,
53+
expr_ty: &Ty,
54+
) -> Ty {
55+
let mut this = CoerceMany::new(expected);
56+
this.coerce(ctx, expr, expr_ty);
57+
this.complete()
5658
}
5759

5860
/// Merge two types from different branches, with possible coercion.
@@ -62,51 +64,88 @@ impl<'a> InferenceContext<'a> {
6264
/// coerce both to function pointers;
6365
/// - if we were concerned with lifetime subtyping, we'd need to look for a
6466
/// least upper bound.
65-
pub(super) fn coerce_merge_branch(&mut self, id: Option<ExprId>, ty1: &Ty, ty2: &Ty) -> Ty {
66-
// TODO
67-
let ty1 = self.resolve_ty_shallow(ty1);
68-
let ty2 = self.resolve_ty_shallow(ty2);
67+
pub(super) fn coerce(
68+
&mut self,
69+
ctx: &mut InferenceContext<'_>,
70+
expr: Option<ExprId>,
71+
expr_ty: &Ty,
72+
) {
73+
let expr_ty = ctx.resolve_ty_shallow(expr_ty);
74+
self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty);
75+
6976
// Special case: two function types. Try to coerce both to
7077
// pointers to have a chance at getting a match. See
7178
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
72-
let sig = match (ty1.kind(&Interner), ty2.kind(&Interner)) {
79+
let sig = match (self.expected_ty.kind(&Interner), expr_ty.kind(&Interner)) {
7380
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
7481
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
7582
// we should be coercing the closure to a fn pointer of the safety of the FnDef
7683
cov_mark::hit!(coerce_fn_reification);
77-
let sig = ty1.callable_sig(self.db).expect("FnDef without callable sig");
84+
let sig =
85+
self.expected_ty.callable_sig(ctx.db).expect("FnDef without callable sig");
7886
Some(sig)
7987
}
8088
_ => None,
8189
};
8290
if let Some(sig) = sig {
8391
let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(&Interner);
84-
let result1 = self.coerce_inner(ty1.clone(), &target_ty);
85-
let result2 = self.coerce_inner(ty2.clone(), &target_ty);
92+
let result1 = ctx.coerce_inner(self.expected_ty.clone(), &target_ty);
93+
let result2 = ctx.coerce_inner(expr_ty.clone(), &target_ty);
8694
if let (Ok(result1), Ok(result2)) = (result1, result2) {
87-
self.table.register_infer_ok(result1);
88-
self.table.register_infer_ok(result2);
89-
return target_ty;
95+
ctx.table.register_infer_ok(result1);
96+
ctx.table.register_infer_ok(result2);
97+
return self.expected_ty = target_ty;
9098
}
9199
}
92100

93-
// It might not seem like it, but order is important here: ty1 is our
94-
// "previous" type, ty2 is the "new" one being added. If the previous
101+
// It might not seem like it, but order is important here: If the expected
95102
// type is a type variable and the new one is `!`, trying it the other
96103
// way around first would mean we make the type variable `!`, instead of
97104
// just marking it as possibly diverging.
98-
if self.coerce(&ty2, &ty1).is_ok() {
99-
ty1
100-
} else if self.coerce(&ty1, &ty2).is_ok() {
101-
ty2
105+
if ctx.coerce(expr, &expr_ty, &self.expected_ty).is_ok() {
106+
/* self.expected_ty is already correct */
107+
} else if ctx.coerce(expr, &self.expected_ty, &expr_ty).is_ok() {
108+
self.expected_ty = expr_ty;
102109
} else {
103-
if let Some(id) = id {
104-
self.result
105-
.type_mismatches
106-
.insert(id.into(), TypeMismatch { expected: ty1.clone(), actual: ty2 });
110+
if let Some(id) = expr {
111+
ctx.result.type_mismatches.insert(
112+
id.into(),
113+
TypeMismatch { expected: self.expected_ty.clone(), actual: expr_ty },
114+
);
107115
}
108116
cov_mark::hit!(coerce_merge_fail_fallback);
109-
ty1
117+
/* self.expected_ty is already correct */
118+
}
119+
}
120+
121+
pub(super) fn complete(self) -> Ty {
122+
self.expected_ty
123+
}
124+
}
125+
126+
impl<'a> InferenceContext<'a> {
127+
/// Unify two types, but may coerce the first one to the second one
128+
/// using "implicit coercion rules" if needed.
129+
pub(super) fn coerce(
130+
&mut self,
131+
expr: Option<ExprId>,
132+
from_ty: &Ty,
133+
to_ty: &Ty,
134+
) -> InferResult<Ty> {
135+
let from_ty = self.resolve_ty_shallow(from_ty);
136+
let to_ty = self.resolve_ty_shallow(to_ty);
137+
match self.coerce_inner(from_ty, &to_ty) {
138+
Ok(InferOk { value: (adjustments, ty), goals }) => {
139+
if let Some(expr) = expr {
140+
self.write_expr_adj(expr, adjustments);
141+
}
142+
self.table.register_infer_ok(InferOk { value: (), goals });
143+
Ok(InferOk { value: ty, goals: Vec::new() })
144+
}
145+
Err(e) => {
146+
// FIXME deal with error
147+
Err(e)
148+
}
110149
}
111150
}
112151

@@ -189,7 +228,6 @@ impl<'a> InferenceContext<'a> {
189228

190229
// Check that the types which they point at are compatible.
191230
let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(&Interner);
192-
// self.table.try_unify(&from_raw, to_ty);
193231

194232
// Although references and unsafe ptrs have the same
195233
// representation, we still register an Adjust::DerefRef so that
@@ -518,15 +556,13 @@ impl<'a> InferenceContext<'a> {
518556
// FIXME: should we accept ambiguous results here?
519557
_ => return Err(TypeError),
520558
};
521-
// TODO: this is probably wrong?
522-
let coerce_target = self.table.new_type_var();
523-
self.unify_and(&coerce_target, to_ty, |target| {
524-
let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target };
525-
match reborrow {
526-
None => vec![unsize],
527-
Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize],
528-
}
529-
})
559+
let unsize =
560+
Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() };
561+
let adjustments = match reborrow {
562+
None => vec![unsize],
563+
Some((deref, autoref)) => vec![deref, autoref, unsize],
564+
};
565+
success(adjustments, to_ty.clone(), vec![])
530566
}
531567
}
532568

0 commit comments

Comments
 (0)