Skip to content

Commit 40d1e69

Browse files
Utilize coercion information to guide never type fallback
This doesn't yet fully work -- running coercions seems to be prone to causing errors in the environment, which creates a little noise in some cases. It should be enough to let us re-run crater, though, I think.
1 parent 5a8bcf9 commit 40d1e69

File tree

8 files changed

+105
-66
lines changed

8 files changed

+105
-66
lines changed

compiler/rustc_typeck/src/check/cast.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
5454
#[derive(Debug)]
5555
pub struct CastCheck<'tcx> {
5656
expr: &'tcx hir::Expr<'tcx>,
57-
expr_ty: Ty<'tcx>,
58-
cast_ty: Ty<'tcx>,
57+
pub(crate) expr_ty: Ty<'tcx>,
58+
pub(crate) cast_ty: Ty<'tcx>,
5959
cast_span: Span,
6060
span: Span,
6161
}

compiler/rustc_typeck/src/check/coercion.rs

+35-18
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
6262
use smallvec::{smallvec, SmallVec};
6363
use std::ops::Deref;
6464

65-
struct Coerce<'a, 'tcx> {
65+
pub(crate) struct Coerce<'a, 'tcx> {
6666
fcx: &'a FnCtxt<'a, 'tcx>,
6767
cause: ObligationCause<'tcx>,
6868
use_lub: bool,
@@ -116,7 +116,7 @@ fn success<'tcx>(
116116
}
117117

118118
impl<'f, 'tcx> Coerce<'f, 'tcx> {
119-
fn new(
119+
pub(crate) fn new(
120120
fcx: &'f FnCtxt<'f, 'tcx>,
121121
cause: ObligationCause<'tcx>,
122122
allow_two_phase: AllowTwoPhase,
@@ -146,7 +146,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
146146
.and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations))
147147
}
148148

149+
pub(crate) fn coerce_silent(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
150+
self.coerce_(false, a, b)
151+
}
152+
149153
fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
154+
self.coerce_(true, a, b)
155+
}
156+
157+
fn coerce_(&self, report_error: bool, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
150158
// First, remove any resolved type variables (at the top level, at least):
151159
let a = self.shallow_resolve(a);
152160
let b = self.shallow_resolve(b);
@@ -174,7 +182,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
174182
// NOTE: this is wrapped in a `commit_if_ok` because it creates
175183
// a "spurious" type variable, and we don't want to have that
176184
// type variable in memory if the coercion fails.
177-
let unsize = self.commit_if_ok(|_| self.coerce_unsized(a, b));
185+
let unsize = self.commit_if_ok(|_| self.coerce_unsized(report_error, a, b));
178186
match unsize {
179187
Ok(_) => {
180188
debug!("coerce: unsize successful");
@@ -482,7 +490,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
482490
// &[T; n] or &mut [T; n] -> &[T]
483491
// or &mut [T; n] -> &mut [T]
484492
// or &Concrete -> &Trait, etc.
485-
fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> {
493+
fn coerce_unsized(
494+
&self,
495+
report_error: bool,
496+
mut source: Ty<'tcx>,
497+
mut target: Ty<'tcx>,
498+
) -> CoerceResult<'tcx> {
486499
debug!("coerce_unsized(source={:?}, target={:?})", source, target);
487500

488501
source = self.shallow_resolve(source);
@@ -690,13 +703,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
690703

691704
// Object safety violations or miscellaneous.
692705
Err(err) => {
693-
self.report_selection_error(
694-
obligation.clone(),
695-
&obligation,
696-
&err,
697-
false,
698-
false,
699-
);
706+
if report_error {
707+
self.report_selection_error(
708+
obligation.clone(),
709+
&obligation,
710+
&err,
711+
false,
712+
false,
713+
);
714+
}
700715
// Treat this like an obligation and follow through
701716
// with the unsizing - the lack of a coercion should
702717
// be silent, as it causes a type mismatch later.
@@ -707,13 +722,15 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
707722
}
708723

709724
if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
710-
feature_err(
711-
&self.tcx.sess.parse_sess,
712-
sym::unsized_tuple_coercion,
713-
self.cause.span,
714-
"unsized tuple coercion is not stable enough for use and is subject to change",
715-
)
716-
.emit();
725+
if report_error {
726+
feature_err(
727+
&self.tcx.sess.parse_sess,
728+
sym::unsized_tuple_coercion,
729+
self.cause.span,
730+
"unsized tuple coercion is not stable enough for use and is subject to change",
731+
)
732+
.emit();
733+
}
717734
}
718735

719736
if has_trait_upcasting_coercion && !self.tcx().features().trait_upcasting {

compiler/rustc_typeck/src/check/fallback.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use rustc_data_structures::{
55
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
66
stable_set::FxHashSet,
77
};
8-
use rustc_middle::ty::{self, Ty};
8+
use rustc_infer::traits::ObligationCauseCode;
9+
use rustc_middle::ty::{self, adjustment::AllowTwoPhase, Ty, TypeFoldable};
910

1011
impl<'tcx> FnCtxt<'_, 'tcx> {
1112
/// Performs type inference fallback, returning true if any fallback
@@ -16,6 +17,48 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
1617
self.fulfillment_cx.borrow_mut().pending_obligations()
1718
);
1819

20+
let mut cast_vars = Vec::new();
21+
22+
// FIXME: This seems to cause extra object safety errors. Not clear why; one would expect the probe and such to eat them.
23+
for cast in self.deferred_cast_checks.borrow().iter() {
24+
let source = cast.expr_ty;
25+
let target = cast.cast_ty;
26+
debug!("attempting coerce {:?} -> {:?}", source, target,);
27+
let source = self.resolve_vars_with_obligations(source);
28+
29+
let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable);
30+
// We don't ever need two-phase here since we throw out the result of the coercion
31+
let coerce = crate::check::coercion::Coerce::new(self, cause, AllowTwoPhase::No);
32+
if let Ok(infok) = self.probe(|_| coerce.coerce_silent(source, target)) {
33+
for obligation in infok.obligations {
34+
if let ty::PredicateKind::Projection(predicate) =
35+
obligation.predicate.kind().skip_binder()
36+
{
37+
if !predicate.projection_ty.has_escaping_bound_vars() {
38+
// FIXME: We really *should* do this even with escaping bound
39+
// vars, but there's not much we can do here. In the worst case
40+
// (if this ends up being important) we just don't register a relationship and then end up falling back to !,
41+
// which is not terrible.
42+
43+
if let Some(vid) = self
44+
.fulfillment_cx
45+
.borrow_mut()
46+
.normalize_projection_type(
47+
&self.infcx,
48+
obligation.param_env,
49+
predicate.projection_ty,
50+
obligation.cause.clone(),
51+
)
52+
.ty_vid()
53+
{
54+
cast_vars.push(vid);
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
1962
// All type checking constraints were added, try to fallback unsolved variables.
2063
self.select_obligations_where_possible(false, |_| {});
2164

@@ -36,7 +79,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
3679
}
3780

3881
let diverging_fallback =
39-
self.calculate_diverging_fallback(&unsolved_variables, &relationships);
82+
self.calculate_diverging_fallback(&unsolved_variables, &relationships, &cast_vars);
4083

4184
// We do fallback in two passes, to try to generate
4285
// better error messages.
@@ -263,6 +306,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
263306
&self,
264307
unsolved_variables: &[Ty<'tcx>],
265308
relationships: &FxHashMap<ty::TyVid, ty::FoundRelationships>,
309+
cast_vars: &[ty::TyVid],
266310
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
267311
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
268312

@@ -368,16 +412,26 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
368412
let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
369413

370414
for (vid, rel) in relationships.iter() {
371-
//if self.infcx.shallow_resolve(*ty).ty_vid().map(|t| self.infcx.root_var(t))
372415
if self.infcx.root_var(*vid) == root_vid {
373416
relationship.self_in_trait |= rel.self_in_trait;
374417
relationship.output |= rel.output;
375418
}
376419
}
377420

421+
let mut is_cast = false;
422+
for vid in cast_vars.iter() {
423+
if self.infcx.root_var(*vid) == root_vid {
424+
is_cast = true;
425+
break;
426+
}
427+
}
428+
378429
if relationship.self_in_trait && relationship.output {
379430
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
380431
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
432+
} else if is_cast {
433+
debug!("fallback to () - interacted with cast: {:?}", diverging_vid);
434+
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
381435
} else if can_reach_non_diverging {
382436
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
383437
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);

src/test/ui/issues/issue-22034.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//~ ERROR expected a `FnOnce<()>` closure, found `()`
12
#![feature(rustc_private)]
23

34
extern crate libc;

src/test/ui/issues/issue-22034.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
error[E0277]: expected a `FnOnce<()>` closure, found `()`
2+
|
3+
= help: the trait `FnOnce<()>` is not implemented for `()`
4+
= note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
5+
= note: required for the cast to the object type `dyn Fn()`
6+
17
error[E0277]: expected a `Fn<()>` closure, found `()`
2-
--> $DIR/issue-22034.rs:8:16
8+
--> $DIR/issue-22034.rs:9:16
39
|
410
LL | &mut *(ptr as *mut dyn Fn())
511
| ^^^ expected an `Fn<()>` closure, found `()`
@@ -8,6 +14,6 @@ LL | &mut *(ptr as *mut dyn Fn())
814
= note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
915
= note: required for the cast to the object type `dyn Fn()`
1016

11-
error: aborting due to previous error
17+
error: aborting due to 2 previous errors
1218

1319
For more information about this error, try `rustc --explain E0277`.

src/test/ui/never_type/fallback-closure-wrap.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
// check-pass
2+
13
use std::marker::PhantomData;
24

35
fn main() {
46
let error = Closure::wrap(Box::new(move || {
5-
//~^ ERROR type mismatch
67
panic!("Can't connect to server.");
78
}) as Box<dyn FnMut()>);
89
}

src/test/ui/never_type/fallback-closure-wrap.stderr

-17
This file was deleted.

src/test/ui/self/arbitrary-self-types-not-object-safe.stderr

-23
This file was deleted.

0 commit comments

Comments
 (0)