@@ -139,8 +139,8 @@ impl CoerceMany {
139
139
} ;
140
140
if let Some ( sig) = sig {
141
141
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, CoerceNever :: Yes ) ;
143
+ let result2 = ctx. table . coerce_inner ( expr_ty. clone ( ) , & target_ty, CoerceNever :: Yes ) ;
144
144
if let ( Ok ( result1) , Ok ( result2) ) = ( result1, result2) {
145
145
ctx. table . register_infer_ok ( InferOk { value : ( ) , goals : result1. goals } ) ;
146
146
for & e in & self . expressions {
@@ -159,9 +159,9 @@ impl CoerceMany {
159
159
// type is a type variable and the new one is `!`, trying it the other
160
160
// way around first would mean we make the type variable `!`, instead of
161
161
// 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 ( ) , CoerceNever :: Yes ) {
163
163
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, CoerceNever :: Yes ) {
165
165
self . final_ty = Some ( res) ;
166
166
} else {
167
167
match cause {
@@ -197,7 +197,7 @@ pub(crate) fn coerce(
197
197
let vars = table. fresh_subst ( tys. binders . as_slice ( Interner ) ) ;
198
198
let ty1_with_vars = vars. apply ( tys. value . 0 . clone ( ) , Interner ) ;
199
199
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, CoerceNever :: Yes ) ?;
201
201
// default any type vars that weren't unified back to their original bound vars
202
202
// (kind of hacky)
203
203
let find_var = |iv| {
@@ -219,6 +219,12 @@ pub(crate) fn coerce(
219
219
Ok ( ( adjustments, table. resolve_with_fallback ( ty, & fallback) ) )
220
220
}
221
221
222
+ #[ derive( Clone , Copy , PartialEq , Eq ) ]
223
+ pub ( crate ) enum CoerceNever {
224
+ Yes ,
225
+ No ,
226
+ }
227
+
222
228
impl InferenceContext < ' _ > {
223
229
/// Unify two types, but may coerce the first one to the second one
224
230
/// using "implicit coercion rules" if needed.
@@ -227,10 +233,16 @@ impl InferenceContext<'_> {
227
233
expr : Option < ExprId > ,
228
234
from_ty : & Ty ,
229
235
to_ty : & Ty ,
236
+ // [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89)
237
+ // Whether we allow `NeverToAny` coercions. This is unsound if we're
238
+ // coercing a place expression without it counting as a read in the MIR.
239
+ // This is a side-effect of HIR not really having a great distinction
240
+ // between places and values.
241
+ coerce_never : CoerceNever ,
230
242
) -> Result < Ty , TypeError > {
231
243
let from_ty = self . resolve_ty_shallow ( from_ty) ;
232
244
let to_ty = self . resolve_ty_shallow ( to_ty) ;
233
- let ( adjustments, ty) = self . table . coerce ( & from_ty, & to_ty) ?;
245
+ let ( adjustments, ty) = self . table . coerce ( & from_ty, & to_ty, coerce_never ) ?;
234
246
if let Some ( expr) = expr {
235
247
self . write_expr_adj ( expr, adjustments) ;
236
248
}
@@ -245,10 +257,11 @@ impl InferenceTable<'_> {
245
257
& mut self ,
246
258
from_ty : & Ty ,
247
259
to_ty : & Ty ,
260
+ coerce_never : CoerceNever ,
248
261
) -> Result < ( Vec < Adjustment > , Ty ) , TypeError > {
249
262
let from_ty = self . resolve_ty_shallow ( from_ty) ;
250
263
let to_ty = self . resolve_ty_shallow ( to_ty) ;
251
- match self . coerce_inner ( from_ty, & to_ty) {
264
+ match self . coerce_inner ( from_ty, & to_ty, coerce_never ) {
252
265
Ok ( InferOk { value : ( adjustments, ty) , goals } ) => {
253
266
self . register_infer_ok ( InferOk { value : ( ) , goals } ) ;
254
267
Ok ( ( adjustments, ty) )
@@ -260,19 +273,23 @@ impl InferenceTable<'_> {
260
273
}
261
274
}
262
275
263
- fn coerce_inner ( & mut self , from_ty : Ty , to_ty : & Ty ) -> CoerceResult {
276
+ fn coerce_inner ( & mut self , from_ty : Ty , to_ty : & Ty , coerce_never : CoerceNever ) -> CoerceResult {
264
277
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`.
272
278
if let TyKind :: InferenceVar ( tv, TyVariableKind :: General ) = to_ty. kind ( Interner ) {
273
279
self . set_diverging ( * tv, true ) ;
274
280
}
275
- return success ( simple ( Adjust :: NeverToAny ) ( to_ty. clone ( ) ) , to_ty. clone ( ) , vec ! [ ] ) ;
281
+ if coerce_never == CoerceNever :: Yes {
282
+ // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
283
+ // type variable, we want `?T` to fallback to `!` if not
284
+ // otherwise constrained. An example where this arises:
285
+ //
286
+ // let _: Option<?T> = Some({ return; });
287
+ //
288
+ // here, we would coerce from `!` to `?T`.
289
+ return success ( simple ( Adjust :: NeverToAny ) ( to_ty. clone ( ) ) , to_ty. clone ( ) , vec ! [ ] ) ;
290
+ } else {
291
+ return self . unify_and ( & from_ty, to_ty, identity) ;
292
+ }
276
293
}
277
294
278
295
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
0 commit comments