Skip to content

Commit 1f7db21

Browse files
committed
Do not consider match/let/ref of place that evaluates to ! to diverge, disallow coercions from them too
1 parent 0fb804a commit 1f7db21

File tree

8 files changed

+534
-92
lines changed

8 files changed

+534
-92
lines changed

crates/hir-ty/src/infer.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,7 @@ impl<'a> InferenceContext<'a> {
11541154
_ = self.infer_expr_coerce(
11551155
self.body.body_expr,
11561156
&Expectation::has_type(self.return_ty.clone()),
1157+
true,
11571158
)
11581159
}
11591160
}

crates/hir-ty/src/infer/cast.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ impl CastCheck {
127127
return Ok(());
128128
}
129129

130-
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) {
130+
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, true) {
131131
apply_adjustments(self.source_expr, adj);
132132
set_coercion_cast(self.source_expr);
133133
return Ok(());
@@ -153,7 +153,7 @@ impl CastCheck {
153153
let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
154154
let sig = table.normalize_associated_types_in(sig);
155155
let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
156-
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) {
156+
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, true) {
157157
apply_adjustments(self.source_expr, adj);
158158
} else {
159159
return Err(CastError::IllegalCast);
@@ -240,7 +240,7 @@ impl CastCheck {
240240
if let TyKind::Array(ety, _) = t_expr.kind(Interner) {
241241
// Coerce to a raw pointer so that we generate RawPtr in MIR.
242242
let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner);
243-
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) {
243+
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, true) {
244244
apply_adjustments(self.source_expr, adj);
245245
} else {
246246
never!(
@@ -252,7 +252,7 @@ impl CastCheck {
252252

253253
// This is a less strict condition than rustc's `demand_eqtype`,
254254
// but false negative is better than false positive
255-
if table.coerce(ety, t_cast).is_ok() {
255+
if table.coerce(ety, t_cast, true).is_ok() {
256256
return Ok(());
257257
}
258258
}

crates/hir-ty/src/infer/closure.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl InferenceContext<'_> {
6565
}
6666

6767
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
68-
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
68+
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, true);
6969

7070
// Coroutines are not Fn* so return early.
7171
if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) {

crates/hir-ty/src/infer/coerce.rs

+27-16
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ impl CoerceMany {
139139
};
140140
if let Some(sig) = sig {
141141
let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
142-
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
143-
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
142+
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, true);
143+
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, true);
144144
if let (Ok(result1), Ok(result2)) = (result1, result2) {
145145
ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
146146
for &e in &self.expressions {
@@ -159,9 +159,9 @@ impl CoerceMany {
159159
// type is a type variable and the new one is `!`, trying it the other
160160
// way around first would mean we make the type variable `!`, instead of
161161
// just marking it as possibly diverging.
162-
if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty()) {
162+
if let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), true) {
163163
self.final_ty = Some(res);
164-
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
164+
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty, true) {
165165
self.final_ty = Some(res);
166166
} else {
167167
match cause {
@@ -197,7 +197,7 @@ pub(crate) fn coerce(
197197
let vars = table.fresh_subst(tys.binders.as_slice(Interner));
198198
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
199199
let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner);
200-
let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars)?;
200+
let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, true)?;
201201
// default any type vars that weren't unified back to their original bound vars
202202
// (kind of hacky)
203203
let find_var = |iv| {
@@ -227,10 +227,16 @@ impl InferenceContext<'_> {
227227
expr: Option<ExprId>,
228228
from_ty: &Ty,
229229
to_ty: &Ty,
230+
// [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89)
231+
// Whether we allow `NeverToAny` coercions. This is unsound if we're
232+
// coercing a place expression without it counting as a read in the MIR.
233+
// This is a side-effect of HIR not really having a great distinction
234+
// between places and values.
235+
coerce_never: bool,
230236
) -> Result<Ty, TypeError> {
231237
let from_ty = self.resolve_ty_shallow(from_ty);
232238
let to_ty = self.resolve_ty_shallow(to_ty);
233-
let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty)?;
239+
let (adjustments, ty) = self.table.coerce(&from_ty, &to_ty, coerce_never)?;
234240
if let Some(expr) = expr {
235241
self.write_expr_adj(expr, adjustments);
236242
}
@@ -245,10 +251,11 @@ impl InferenceTable<'_> {
245251
&mut self,
246252
from_ty: &Ty,
247253
to_ty: &Ty,
254+
coerce_never: bool,
248255
) -> Result<(Vec<Adjustment>, Ty), TypeError> {
249256
let from_ty = self.resolve_ty_shallow(from_ty);
250257
let to_ty = self.resolve_ty_shallow(to_ty);
251-
match self.coerce_inner(from_ty, &to_ty) {
258+
match self.coerce_inner(from_ty, &to_ty, coerce_never) {
252259
Ok(InferOk { value: (adjustments, ty), goals }) => {
253260
self.register_infer_ok(InferOk { value: (), goals });
254261
Ok((adjustments, ty))
@@ -260,19 +267,23 @@ impl InferenceTable<'_> {
260267
}
261268
}
262269

263-
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult {
270+
fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: bool) -> CoerceResult {
264271
if from_ty.is_never() {
265-
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
266-
// type variable, we want `?T` to fallback to `!` if not
267-
// otherwise constrained. An example where this arises:
268-
//
269-
// let _: Option<?T> = Some({ return; });
270-
//
271-
// here, we would coerce from `!` to `?T`.
272272
if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) {
273273
self.set_diverging(*tv, true);
274274
}
275-
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
275+
if coerce_never {
276+
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
277+
// type variable, we want `?T` to fallback to `!` if not
278+
// otherwise constrained. An example where this arises:
279+
//
280+
// let _: Option<?T> = Some({ return; });
281+
//
282+
// here, we would coerce from `!` to `?T`.
283+
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
284+
} else {
285+
return self.unify_and(&from_ty, to_ty, identity);
286+
}
276287
}
277288

278289
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.

0 commit comments

Comments
 (0)