Skip to content

Commit 8c6156e

Browse files
committed
have coercion supply back the target type
The `try_coerce` method coerces from a source to a target type, possibly inserting adjustments. It should guarantee that the post-adjustment type is a subtype of the target type (or else that some side-constraint has been registered which will lead to an error). However, it used to return the (possibly adjusted) source as the type of the expression rather than the target. This led to less good downstream errors. To work around this, the code around blocks -- and particular tail expressions in blocks -- had some special case manipulation. However, since that code is now using the more general `CoerceMany` construct (to account for breaks), it can no longer take advantage of that. This lead to some regressions in compile-fail tests were errors were reported at "less good" locations than before. This change modifies coercions to return the target type when successful rather the source type. This extends the behavior from blocks to all coercions. Typically this has limited effect but on a few tests yielded better errors results (and avoided regressions, of course). This change also restores the hint about removing semicolons which went missing (by giving 'force-unit' coercions a chance to add notes etc).
1 parent d08a6da commit 8c6156e

File tree

7 files changed

+91
-61
lines changed

7 files changed

+91
-61
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
498498
if is_if_let_fallback {
499499
let cause = self.cause(expr.span, ObligationCauseCode::IfExpressionWithNoElse);
500500
assert!(arm_ty.is_nil());
501-
coercion.coerce_forced_unit(self, &cause);
501+
coercion.coerce_forced_unit(self, &cause, &mut |_| ());
502502
} else {
503503
let cause = self.cause(expr.span, ObligationCauseCode::MatchExpressionArm {
504504
arm_span: arm.body.span,

src/librustc_typeck/check/coercion.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ use rustc::ty::fold::TypeFoldable;
7474
use rustc::ty::error::TypeError;
7575
use rustc::ty::relate::RelateResult;
7676
use rustc::ty::subst::Subst;
77+
use errors::DiagnosticBuilder;
7778
use syntax::abi;
7879
use syntax::feature_gate;
7980
use syntax::ptr::P;
@@ -718,7 +719,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
718719
}
719720
self.write_adjustment(expr.id, adjustment);
720721
}
721-
Ok(adjustment.target)
722+
723+
// We should now have added sufficient adjustments etc to
724+
// ensure that the type of expression, post-adjustment, is
725+
// a subtype of target.
726+
Ok(target)
722727
})
723728
}
724729

@@ -1000,7 +1005,7 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
10001005
expression_ty: Ty<'tcx>,
10011006
expression_diverges: Diverges)
10021007
{
1003-
self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges)
1008+
self.coerce_inner(fcx, cause, Some(expression), expression_ty, expression_diverges, None)
10041009
}
10051010

10061011
/// Indicates that one of the inputs is a "forced unit". This
@@ -1011,15 +1016,21 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
10111016
/// purposes. Note that these tend to correspond to cases where
10121017
/// the `()` expression is implicit in the source, and hence we do
10131018
/// not take an expression argument.
1019+
///
1020+
/// The `augment_error` gives you a chance to extend the error
1021+
/// message, in case any results (e.g., we use this to suggest
1022+
/// removing a `;`).
10141023
pub fn coerce_forced_unit<'a>(&mut self,
10151024
fcx: &FnCtxt<'a, 'gcx, 'tcx>,
1016-
cause: &ObligationCause<'tcx>)
1025+
cause: &ObligationCause<'tcx>,
1026+
augment_error: &mut FnMut(&mut DiagnosticBuilder))
10171027
{
10181028
self.coerce_inner(fcx,
10191029
cause,
10201030
None,
10211031
fcx.tcx.mk_nil(),
1022-
Diverges::Maybe)
1032+
Diverges::Maybe,
1033+
Some(augment_error))
10231034
}
10241035

10251036
/// The inner coercion "engine". If `expression` is `None`, this
@@ -1030,7 +1041,8 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
10301041
cause: &ObligationCause<'tcx>,
10311042
expression: Option<&'gcx hir::Expr>,
10321043
mut expression_ty: Ty<'tcx>,
1033-
expression_diverges: Diverges)
1044+
expression_diverges: Diverges,
1045+
augment_error: Option<&mut FnMut(&mut DiagnosticBuilder)>)
10341046
{
10351047
// Incorporate whatever type inference information we have
10361048
// until now; in principle we might also want to process
@@ -1126,19 +1138,25 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
11261138
(self.final_ty.unwrap_or(self.expected_ty), expression_ty)
11271139
};
11281140

1141+
let mut db;
11291142
match cause.code {
11301143
ObligationCauseCode::ReturnNoExpression => {
1131-
struct_span_err!(fcx.tcx.sess, cause.span, E0069,
1132-
"`return;` in a function whose return type is not `()`")
1133-
.span_label(cause.span, &format!("return type is not ()"))
1134-
.emit();
1144+
db = struct_span_err!(
1145+
fcx.tcx.sess, cause.span, E0069,
1146+
"`return;` in a function whose return type is not `()`");
1147+
db.span_label(cause.span, &format!("return type is not ()"));
11351148
}
11361149
_ => {
1137-
fcx.report_mismatched_types(cause, expected, found, err)
1138-
.emit();
1150+
db = fcx.report_mismatched_types(cause, expected, found, err);
11391151
}
11401152
}
11411153

1154+
if let Some(mut augment_error) = augment_error {
1155+
augment_error(&mut db);
1156+
}
1157+
1158+
db.emit();
1159+
11421160
self.final_ty = Some(fcx.tcx.types.err);
11431161
}
11441162
}

src/librustc_typeck/check/mod.rs

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ use fmt_macros::{Parser, Piece, Position};
8787
use hir::def::{Def, CtorKind};
8888
use hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
8989
use rustc_back::slice::ref_slice;
90-
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin, TypeTrace};
90+
use rustc::infer::{self, InferCtxt, InferOk, RegionVariableOrigin};
9191
use rustc::infer::type_variable::{self, TypeVariableOrigin};
9292
use rustc::ty::subst::{Kind, Subst, Substs};
9393
use rustc::traits::{self, ObligationCause, ObligationCauseCode, Reveal};
@@ -99,6 +99,7 @@ use rustc::ty::adjustment;
9999
use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
100100
use rustc::ty::maps::Providers;
101101
use rustc::ty::util::{Representability, IntTypeExt};
102+
use errors::DiagnosticBuilder;
102103
use require_c_abi_if_variadic;
103104
use session::{Session, CompileResult};
104105
use TypeAndSubsts;
@@ -2875,7 +2876,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
28752876
self.diverges.set(cond_diverges | then_diverges & else_diverges);
28762877
} else {
28772878
let else_cause = self.cause(sp, ObligationCauseCode::IfExpressionWithNoElse);
2878-
coerce.coerce_forced_unit(self, &else_cause);
2879+
coerce.coerce_forced_unit(self, &else_cause, &mut |_| ());
28792880

28802881
// If the condition is false we can't diverge.
28812882
self.diverges.set(cond_diverges);
@@ -3591,7 +3592,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
35913592
coerce.coerce(self, &cause, e, e_ty, e_diverges);
35923593
} else {
35933594
assert!(e_ty.is_nil());
3594-
coerce.coerce_forced_unit(self, &cause);
3595+
coerce.coerce_forced_unit(self, &cause, &mut |_| ());
35953596
}
35963597
} else {
35973598
// If `ctxt.coerce` is `None`, we can just ignore
@@ -3626,7 +3627,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
36263627
} else {
36273628
let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
36283629
let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
3629-
coercion.coerce_forced_unit(self, &cause);
3630+
coercion.coerce_forced_unit(self, &cause, &mut |_| ());
36303631
}
36313632
tcx.types.never
36323633
}
@@ -4158,8 +4159,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41584159
tail_expr,
41594160
tail_expr_ty,
41604161
self.diverges.get()); // TODO
4161-
} else if !self.diverges.get().always() {
4162-
coerce.coerce_forced_unit(self, &self.misc(blk.span));
4162+
} else {
4163+
// Subtle: if there is no explicit tail expression,
4164+
// that is typically equivalent to a tail expression
4165+
// of `()` -- except if the block diverges. In that
4166+
// case, there is no value supplied from the tail
4167+
// expression (assuming there are no other breaks,
4168+
// this implies that the type of the block will be
4169+
// `!`).
4170+
if !self.diverges.get().always() {
4171+
coerce.coerce_forced_unit(self, &self.misc(blk.span), &mut |err| {
4172+
if let Some(expected_ty) = expected.only_has_type(self) {
4173+
self.consider_hint_about_removing_semicolon(blk,
4174+
expected_ty,
4175+
err);
4176+
}
4177+
});
4178+
}
41634179
}
41644180
});
41654181

@@ -4175,43 +4191,42 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
41754191
ty
41764192
}
41774193

4178-
pub fn check_block_no_expr(&self, blk: &'gcx hir::Block, ty: Ty<'tcx>, ety: Ty<'tcx>) {
4179-
// We're not diverging and there's an expected type, which,
4180-
// in case it's not `()`, could result in an error higher-up.
4181-
// We have a chance to error here early and be more helpful.
4182-
let cause = self.misc(blk.span);
4183-
let trace = TypeTrace::types(&cause, false, ty, ety);
4184-
match self.sub_types(false, &cause, ty, ety) {
4185-
Ok(InferOk { obligations, .. }) => {
4186-
// FIXME(#32730) propagate obligations
4187-
assert!(obligations.is_empty());
4188-
},
4189-
Err(err) => {
4190-
let mut err = self.report_and_explain_type_error(trace, &err);
4191-
4192-
// Be helpful when the user wrote `{... expr;}` and
4193-
// taking the `;` off is enough to fix the error.
4194-
let mut extra_semi = None;
4195-
if let Some(stmt) = blk.stmts.last() {
4196-
if let hir::StmtSemi(ref e, _) = stmt.node {
4197-
if self.can_sub_types(self.node_ty(e.id), ety).is_ok() {
4198-
extra_semi = Some(stmt);
4199-
}
4200-
}
4201-
}
4202-
if let Some(last_stmt) = extra_semi {
4203-
let original_span = original_sp(last_stmt.span, blk.span);
4204-
let span_semi = Span {
4205-
lo: original_span.hi - BytePos(1),
4206-
hi: original_span.hi,
4207-
ctxt: original_span.ctxt,
4208-
};
4209-
err.span_help(span_semi, "consider removing this semicolon:");
4210-
}
4211-
4212-
err.emit();
4213-
}
4194+
/// A common error is to add an extra semicolon:
4195+
///
4196+
/// ```
4197+
/// fn foo() -> usize {
4198+
/// 22;
4199+
/// }
4200+
/// ```
4201+
///
4202+
/// This routine checks if the final statement in a block is an
4203+
/// expression with an explicit semicolon whose type is compatible
4204+
/// with `expected_ty`. If so, it suggests removing the semicolon.
4205+
fn consider_hint_about_removing_semicolon(&self,
4206+
blk: &'gcx hir::Block,
4207+
expected_ty: Ty<'tcx>,
4208+
err: &mut DiagnosticBuilder) {
4209+
// Be helpful when the user wrote `{... expr;}` and
4210+
// taking the `;` off is enough to fix the error.
4211+
let last_stmt = match blk.stmts.last() {
4212+
Some(s) => s,
4213+
None => return,
4214+
};
4215+
let last_expr = match last_stmt.node {
4216+
hir::StmtSemi(ref e, _) => e,
4217+
_ => return,
4218+
};
4219+
let last_expr_ty = self.expr_ty(last_expr);
4220+
if self.can_sub_types(last_expr_ty, expected_ty).is_err() {
4221+
return;
42144222
}
4223+
let original_span = original_sp(last_stmt.span, blk.span);
4224+
let span_semi = Span {
4225+
lo: original_span.hi - BytePos(1),
4226+
hi: original_span.hi,
4227+
ctxt: original_span.ctxt,
4228+
};
4229+
err.span_help(span_semi, "consider removing this semicolon:");
42154230
}
42164231

42174232
// Instantiates the given path, which must refer to an item with the given

src/test/compile-fail/issue-15965.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
fn main() {
1212
return
1313
{ return () }
14-
//~^ ERROR expected function, found `!`
14+
//~^ ERROR the type of this value must be known in this context
1515
()
1616
;
1717
}

src/test/compile-fail/issue-2149.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ impl<A> vec_monad<A> for Vec<A> {
2121
}
2222
fn main() {
2323
["hi"].bind(|x| [x] );
24-
//~^ ERROR no method named `bind` found for type `[&'static str; 1]` in the current scope
24+
//~^ ERROR no method named `bind` found for type `[&str; 1]` in the current scope
2525
}

src/test/compile-fail/region-invariant-static-error-reporting.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@
1313
// over time, but this test used to exhibit some pretty bogus messages
1414
// that were not remotely helpful.
1515

16-
// error-pattern:cannot infer
17-
// error-pattern:cannot outlive the lifetime 'a
18-
// error-pattern:must be valid for the static lifetime
1916
// error-pattern:cannot infer
2017
// error-pattern:cannot outlive the lifetime 'a
2118
// error-pattern:must be valid for the static lifetime

src/test/compile-fail/regions-bounds.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ struct an_enum<'a>(&'a isize);
1616
struct a_class<'a> { x:&'a isize }
1717

1818
fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
19-
return e; //~^ ERROR mismatched types
19+
return e; //~ ERROR mismatched types
2020
}
2121

2222
fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
23-
return e; //~^ ERROR mismatched types
23+
return e; //~ ERROR mismatched types
2424
}
2525

2626
fn main() { }

0 commit comments

Comments
 (0)