1
Fork 0

Propagate coercion cause into try_coerce

Currently, `coerce_inner` discards its `ObligationCause`
when calling `try_coerce`. This interfers with other
diagnostc improvements I'm working on, since we will lose
the original span by the time the actual coercion occurs.

Additionally, we now use the span of the trailing expression
(rather than the span of the entire function) when performing
a coercion in `check_return_expr`. This currently has no visible
effect on any of the unit tests, but will unblock future
diagnostic improvements.
This commit is contained in:
Aaron Hill 2021-09-16 16:53:40 -05:00
parent 237bb5e008
commit 31cdd8cdd2
No known key found for this signature in database
GPG key ID: B4087E510E98B164
16 changed files with 52 additions and 23 deletions

View file

@ -362,6 +362,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
), ),
self.cast_ty, self.cast_ty,
AllowTwoPhase::No, AllowTwoPhase::No,
None,
) )
.is_ok() .is_ok()
{ {
@ -379,6 +380,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
), ),
self.cast_ty, self.cast_ty,
AllowTwoPhase::No, AllowTwoPhase::No,
None,
) )
.is_ok() .is_ok()
{ {
@ -394,6 +396,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
self.cast_ty, self.cast_ty,
AllowTwoPhase::No, AllowTwoPhase::No,
None,
) )
.is_ok() .is_ok()
{ {
@ -409,6 +412,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
), ),
self.cast_ty, self.cast_ty,
AllowTwoPhase::No, AllowTwoPhase::No,
None,
) )
.is_ok() .is_ok()
{ {
@ -666,6 +670,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
self.expr_ty, self.expr_ty,
fcx.tcx.mk_fn_ptr(f), fcx.tcx.mk_fn_ptr(f),
AllowTwoPhase::No, AllowTwoPhase::No,
None,
); );
if let Err(TypeError::IntrinsicCast) = res { if let Err(TypeError::IntrinsicCast) = res {
return Err(CastError::IllegalCast); return Err(CastError::IllegalCast);
@ -829,7 +834,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
// Coerce to a raw pointer so that we generate AddressOf in MIR. // Coerce to a raw pointer so that we generate AddressOf in MIR.
let array_ptr_type = fcx.tcx.mk_ptr(m_expr); let array_ptr_type = fcx.tcx.mk_ptr(m_expr);
fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
bug!( bug!(
"could not cast from reference to array to pointer to array ({:?} to {:?})", "could not cast from reference to array to pointer to array ({:?} to {:?})",
@ -861,7 +866,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
} }
fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'_>> { fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'_>> {
match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No) { match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(err), Err(err) => Err(err),
} }

View file

@ -214,7 +214,7 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
} else { } else {
fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
fcx.check_return_expr(&body.value); fcx.check_return_expr(&body.value, false);
} }
fcx.in_tail_expr = false; fcx.in_tail_expr = false;

View file

@ -941,11 +941,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr_ty: Ty<'tcx>, expr_ty: Ty<'tcx>,
target: Ty<'tcx>, target: Ty<'tcx>,
allow_two_phase: AllowTwoPhase, allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>,
) -> RelateResult<'tcx, Ty<'tcx>> { ) -> RelateResult<'tcx, Ty<'tcx>> {
let source = self.resolve_vars_with_obligations(expr_ty); let source = self.resolve_vars_with_obligations(expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let cause = self.cause(expr.span, ObligationCauseCode::ExprAssignable); let cause =
cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable));
let coerce = Coerce::new(self, cause, allow_two_phase); let coerce = Coerce::new(self, cause, allow_two_phase);
let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?;
@ -1369,7 +1371,13 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// Special-case the first expression we are coercing. // Special-case the first expression we are coercing.
// To be honest, I'm not entirely sure why we do this. // To be honest, I'm not entirely sure why we do this.
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
fcx.try_coerce(expression, expression_ty, self.expected_ty, AllowTwoPhase::No) fcx.try_coerce(
expression,
expression_ty,
self.expected_ty,
AllowTwoPhase::No,
Some(cause.clone()),
)
} else { } else {
match self.expressions { match self.expressions {
Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub( Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub(

View file

@ -134,7 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) { ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
let expected = self.resolve_vars_with_obligations(expected); let expected = self.resolve_vars_with_obligations(expected);
let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) { let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
Ok(ty) => return (ty, None), Ok(ty) => return (ty, None),
Err(e) => e, Err(e) => e,
}; };

View file

@ -747,7 +747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.ret_coercion_span.get().is_none() { if self.ret_coercion_span.get().is_none() {
self.ret_coercion_span.set(Some(e.span)); self.ret_coercion_span.set(Some(e.span));
} }
self.check_return_expr(e); self.check_return_expr(e, true);
} else { } else {
let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
if self.ret_coercion_span.get().is_none() { if self.ret_coercion_span.get().is_none() {
@ -776,16 +776,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.types.never self.tcx.types.never
} }
pub(super) fn check_return_expr(&self, return_expr: &'tcx hir::Expr<'tcx>) { /// `explicit_return` is `true` if we're checkng an explicit `return expr`,
/// and `false` if we're checking a trailing expression.
pub(super) fn check_return_expr(
&self,
return_expr: &'tcx hir::Expr<'tcx>,
explicit_return: bool,
) {
let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
span_bug!(return_expr.span, "check_return_expr called outside fn body") span_bug!(return_expr.span, "check_return_expr called outside fn body")
}); });
let ret_ty = ret_coercion.borrow().expected_ty(); let ret_ty = ret_coercion.borrow().expected_ty();
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
let mut span = return_expr.span;
// Use the span of the trailing expression for our cause,
// not the span of the entire function
if !explicit_return {
if let ExprKind::Block(body, _) = return_expr.kind {
if let Some(last_expr) = body.expr {
span = last_expr.span;
}
}
}
ret_coercion.borrow_mut().coerce( ret_coercion.borrow_mut().coerce(
self, self,
&self.cause(return_expr.span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)),
return_expr, return_expr,
return_expr_ty, return_expr_ty,
); );

View file

@ -15,7 +15,7 @@ note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/iss
LL | Box::new(self.out_edges(u).map(|e| e.target())) LL | Box::new(self.out_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime... = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/issue-55796.rs:18:9 --> $DIR/issue-55796.rs:18:9
| |
LL | Box::new(self.out_edges(u).map(|e| e.target())) LL | Box::new(self.out_edges(u).map(|e| e.target()))
@ -40,7 +40,7 @@ note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/iss
LL | Box::new(self.in_edges(u).map(|e| e.target())) LL | Box::new(self.in_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime... = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/issue-55796.rs:23:9 --> $DIR/issue-55796.rs:23:9
| |
LL | Box::new(self.in_edges(u).map(|e| e.target())) LL | Box::new(self.in_edges(u).map(|e| e.target()))

View file

@ -17,7 +17,7 @@ LL | Box::new(move |_| fut)
= note: expected `(Pin<Box<dyn Future<Output = A> + Send>>,)` = note: expected `(Pin<Box<dyn Future<Output = A> + Send>>,)`
found `(Pin<Box<(dyn Future<Output = A> + Send + 'a)>>,)` found `(Pin<Box<(dyn Future<Output = A> + Send + 'a)>>,)`
= note: but, the lifetime must be valid for the static lifetime... = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/issue-75777.rs:13:5 --> $DIR/issue-75777.rs:13:5
| |
LL | Box::new(move |_| fut) LL | Box::new(move |_| fut)

View file

@ -19,7 +19,7 @@ note: but, the lifetime must be valid for the lifetime `'_` as defined on the im
| |
LL | impl Foo<'_> { LL | impl Foo<'_> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/issue-55394.rs:9:9 --> $DIR/issue-55394.rs:9:9
| |
LL | Foo { bar } LL | Foo { bar }

View file

@ -21,7 +21,7 @@ note: but, the lifetime must be valid for the lifetime `'a` as defined on the im
| |
LL | impl<'a> FromBox<'a> for C<'a> { LL | impl<'a> FromBox<'a> for C<'a> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/type-alias-free-regions.rs:17:9 --> $DIR/type-alias-free-regions.rs:17:9
| |
LL | C { f: b } LL | C { f: b }
@ -52,7 +52,7 @@ note: but, the lifetime must be valid for the lifetime `'a` as defined on the im
| |
LL | impl<'a> FromTuple<'a> for C<'a> { LL | impl<'a> FromTuple<'a> for C<'a> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/type-alias-free-regions.rs:27:9 --> $DIR/type-alias-free-regions.rs:27:9
| |
LL | C { f: Box::new(b.0) } LL | C { f: Box::new(b.0) }

View file

@ -19,7 +19,7 @@ note: but, the lifetime must be valid for the lifetime `'b` as defined on the fu
| |
LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait { LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/object-lifetime-default-elision.rs:71:5 --> $DIR/object-lifetime-default-elision.rs:71:5
| |
LL | ss LL | ss
@ -48,7 +48,7 @@ note: but, the lifetime must be valid for the lifetime `'b` as defined on the fu
| |
LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait { LL | fn load3<'a,'b>(ss: &'a dyn SomeTrait) -> &'b dyn SomeTrait {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/object-lifetime-default-elision.rs:71:5 --> $DIR/object-lifetime-default-elision.rs:71:5
| |
LL | ss LL | ss

View file

@ -69,7 +69,7 @@ note: but, the lifetime must be valid for the lifetime `'b` as defined on the fu
| |
LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> { LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/region-object-lifetime-in-coercion.rs:23:5 --> $DIR/region-object-lifetime-in-coercion.rs:23:5
| |
LL | Box::new(v) LL | Box::new(v)

View file

@ -19,7 +19,7 @@ note: but, the lifetime must be valid for the lifetime `'c` as defined on the fu
| |
LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> { LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/regions-close-over-type-parameter-multiple.rs:20:5 --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
| |
LL | box v as Box<dyn SomeTrait + 'a> LL | box v as Box<dyn SomeTrait + 'a>

View file

@ -21,7 +21,7 @@ note: but, the lifetime must be valid for the lifetime `'b` as defined on the fu
| |
LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> { LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/regions-creating-enums4.rs:7:5 --> $DIR/regions-creating-enums4.rs:7:5
| |
LL | Ast::Add(x, y) LL | Ast::Add(x, y)

View file

@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on th
| |
LL | with(|o| o) LL | with(|o| o)
| ^^^^^ | ^^^^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/regions-ret-borrowed-1.rs:10:14 --> $DIR/regions-ret-borrowed-1.rs:10:14
| |
LL | with(|o| o) LL | with(|o| o)

View file

@ -9,7 +9,7 @@ note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on th
| |
LL | with(|o| o) LL | with(|o| o)
| ^^^^^ | ^^^^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/regions-ret-borrowed.rs:13:14 --> $DIR/regions-ret-borrowed.rs:13:14
| |
LL | with(|o| o) LL | with(|o| o)

View file

@ -36,7 +36,7 @@ note: but, the lifetime must be valid for the lifetime `'b` as defined on the fu
| |
LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy { LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
| ^^ | ^^
note: ...so that the expression is assignable note: ...so that the types are compatible
--> $DIR/regions-trait-object-subtyping.rs:15:5 --> $DIR/regions-trait-object-subtyping.rs:15:5
| |
LL | x LL | x