1
Fork 0

port return expressions to use CoerceMany

This slightly affects the error messages in one particular compile-fail
test.
This commit is contained in:
Niko Matsakis 2017-03-17 09:54:26 -04:00
parent b725272b26
commit a6e6be5f88
4 changed files with 66 additions and 33 deletions

View file

@ -362,7 +362,7 @@ fn compare_predicate_entailment<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
&infcx.parameter_environment.caller_bounds);
infcx.resolve_regions_and_report_errors(&free_regions, impl_m_body_id);
} else {
let fcx = FnCtxt::new(&inh, Some(tcx.types.err), impl_m_body_id);
let fcx = FnCtxt::new(&inh, impl_m_body_id);
fcx.regionck_item(impl_m_body_id, impl_m_span, &[]);
}

View file

@ -448,7 +448,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
// expects the types within the function to be consistent.
err_count_on_creation: usize,
ret_ty: Option<Ty<'tcx>>,
ret_coercion: Option<RefCell<CoerceMany<'gcx, 'tcx>>>,
ps: RefCell<UnsafetyState>,
@ -679,7 +679,7 @@ fn typeck_tables<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
check_fn(&inh, fn_sig, decl, id, body)
} else {
let fcx = FnCtxt::new(&inh, None, body.value.id);
let fcx = FnCtxt::new(&inh, body.value.id);
let expected_type = tcx.item_type(def_id);
let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type);
fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
@ -800,15 +800,16 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
// Create the function context. This is either derived from scratch or,
// in the case of function expressions, based on the outer context.
let mut fcx = FnCtxt::new(inherited, None, body.value.id);
let ret_ty = fn_sig.output();
let mut fcx = FnCtxt::new(inherited, body.value.id);
*fcx.ps.borrow_mut() = UnsafetyState::function(fn_sig.unsafety, fn_id);
let ret_ty = fn_sig.output();
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
fcx.ret_ty = fcx.instantiate_anon_types(&Some(ret_ty));
let ret_ty = fcx.instantiate_anon_types(&ret_ty);
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
fn_sig = fcx.tcx.mk_fn_sig(
fn_sig.inputs().iter().cloned(),
fcx.ret_ty.unwrap(),
ret_ty,
fn_sig.variadic,
fn_sig.unsafety,
fn_sig.abi
@ -833,7 +834,38 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
fcx.check_expr_coercable_to_type(&body.value, fcx.ret_ty.unwrap());
fcx.check_return_expr(&body.value);
// Finalize the return check by taking the LUB of the return types
// we saw and assigning it to the expected return type. This isn't
// really expected to fail, since the coercions would have failed
// earlier when trying to find a LUB.
//
// However, the behavior around `!` is sort of complex. In the
// event that the `actual_return_ty` comes back as `!`, that
// indicates that the fn either does not return or "returns" only
// values of type `!`. In this case, if there is an expected
// return type that is *not* `!`, that should be ok. But if the
// return type is being inferred, we want to "fallback" to `!`:
//
// let x = move || panic!();
//
// To allow for that, I am creating a type variable with diverging
// fallback. This was deemed ever so slightly better than unifying
// the return value with `!` because it allows for the caller to
// make more assumptions about the return type (e.g., they could do
//
// let y: Option<u32> = Some(x());
//
// which would then cause this return type to become `u32`, not
// `!`).
let coercion = fcx.ret_coercion.take().unwrap().into_inner();
let mut actual_return_ty = coercion.complete(&fcx);
if actual_return_ty.is_never() {
actual_return_ty = fcx.next_diverging_ty_var(
TypeVariableOrigin::DivergingFn(body.value.span));
}
fcx.demand_suptype(body.value.span, ret_ty, actual_return_ty);
fcx
}
@ -1429,14 +1461,13 @@ enum TupleArgumentsFlag {
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn new(inh: &'a Inherited<'a, 'gcx, 'tcx>,
rty: Option<Ty<'tcx>>,
body_id: ast::NodeId)
-> FnCtxt<'a, 'gcx, 'tcx> {
FnCtxt {
ast_ty_to_ty_cache: RefCell::new(NodeMap()),
body_id: body_id,
err_count_on_creation: inh.tcx.sess.err_count(),
ret_ty: rty,
ret_coercion: None,
ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal,
ast::CRATE_NODE_ID)),
diverges: Cell::new(Diverges::Maybe),
@ -2738,6 +2769,23 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ret_ty
}
fn check_return_expr(&self, return_expr: &'gcx hir::Expr) {
let ret_coercion =
self.ret_coercion
.as_ref()
.unwrap_or_else(|| span_bug!(return_expr.span,
"check_return_expr called outside fn body"));
let ret_ty = ret_coercion.borrow().expected_ty();
let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty);
ret_coercion.borrow_mut()
.coerce(self,
&self.misc(return_expr.span),
return_expr,
return_expr_ty);
}
// A generic function for checking the then and else in an if
// or if-else.
fn check_then_else(&self,
@ -3522,24 +3570,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
hir::ExprAgain(_) => { tcx.types.never }
hir::ExprRet(ref expr_opt) => {
if self.ret_ty.is_none() {
if self.ret_coercion.is_none() {
struct_span_err!(self.tcx.sess, expr.span, E0572,
"return statement outside of function body").emit();
} else if let Some(ref e) = *expr_opt {
self.check_expr_coercable_to_type(&e, self.ret_ty.unwrap());
self.check_return_expr(e);
} else {
match self.eq_types(false,
&self.misc(expr.span),
self.ret_ty.unwrap(),
tcx.mk_nil()) {
Ok(ok) => self.register_infer_ok_obligations(ok),
Err(_) => {
struct_span_err!(tcx.sess, expr.span, E0069,
"`return;` in a function whose return type is not `()`")
.span_label(expr.span, &format!("return type is not ()"))
.emit();
}
}
let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut();
let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression);
coercion.coerce_forced_unit(self, &cause);
}
tcx.types.never
}

View file

@ -50,7 +50,7 @@ impl<'a, 'gcx, 'tcx> CheckWfFcxBuilder<'a, 'gcx, 'tcx> {
let id = self.id;
let span = self.span;
self.inherited.enter(|inh| {
let fcx = FnCtxt::new(&inh, None, id);
let fcx = FnCtxt::new(&inh, id);
let wf_tys = f(&fcx, &mut CheckTypeWellFormedVisitor {
tcx: fcx.tcx.global_tcx(),
code: code

View file

@ -16,17 +16,11 @@ struct an_enum<'a>(&'a isize);
struct a_class<'a> { x:&'a isize }
fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> {
return e; //~ ERROR mismatched types
//~| expected type `an_enum<'b>`
//~| found type `an_enum<'a>`
//~| lifetime mismatch
return e; //~^ ERROR mismatched types
}
fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> {
return e; //~ ERROR mismatched types
//~| expected type `a_class<'b>`
//~| found type `a_class<'a>`
//~| lifetime mismatch
return e; //~^ ERROR mismatched types
}
fn main() { }