auto merge of #20083 : eddyb/rust/fix-expectation, r=nikomatsakis

This fixes a few corner cases with expected type propagation, e.g.:
```rust
fn take_int_slice(_: &[int]) {}
take_int_slice(&if 1 < 0 { [ 0, 1 ] } else { [ 0, 1 ] });
```
```rust
<anon>:2:28: 2:36 error: mismatched types: expected `[int]`, found `[int, ..2]`
<anon>:2 take_int_slice(&if 1 < 0 { [ 0, 1 ] } else { [ 0, 1 ] });
                                    ^~~~~~~~
<anon>:2:46: 2:54 error: mismatched types: expected `[int]`, found `[int, ..2]`
<anon>:2 take_int_slice(&if 1 < 0 { [ 0, 1 ] } else { [ 0, 1 ] });
                                                      ^~~~~~~~
```
Right now we unpack the expected `&[int]` and pass down `[int]`, forcing
rvalue expressions to take unsized types, which causes mismatch errors.
Instead, I replaced that expectation with a weaker hint, for the unsized
cases - a hint is still required to infer the integer literals' types, above.

Fixes #20169.
This commit is contained in:
bors 2014-12-23 18:01:22 +00:00
commit d10642ef0f
4 changed files with 113 additions and 42 deletions

View File

@ -4212,10 +4212,14 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
} }
def::DefStruct(_) => { def::DefStruct(_) => {
match expr_ty(tcx, expr).sty { match tcx.node_types.borrow().get(&expr.id) {
ty_bare_fn(..) => RvalueDatumExpr, Some(ty) => match ty.sty {
_ => RvalueDpsExpr ty_bare_fn(..) => RvalueDatumExpr,
} _ => RvalueDpsExpr
},
// See ExprCast below for why types might be missing.
None => RvalueDatumExpr
}
} }
// Special case: A unit like struct's constructor must be called without () at the // Special case: A unit like struct's constructor must be called without () at the

View File

@ -10,9 +10,7 @@
//! Code for type-checking closure expressions. //! Code for type-checking closure expressions.
use super::check_fn; use super::{check_fn, Expectation, FnCtxt};
use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation};
use super::FnCtxt;
use astconv; use astconv;
use middle::infer; use middle::infer;
@ -34,13 +32,17 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr.repr(fcx.tcx()), expr.repr(fcx.tcx()),
expected.repr(fcx.tcx())); expected.repr(fcx.tcx()));
let expected_sig_and_kind = expected.map_to_option(fcx, |ty| {
deduce_unboxed_closure_expectations_from_expected_type(fcx, ty)
});
match opt_kind { match opt_kind {
None => { None => {
// If users didn't specify what sort of closure they want, // If users didn't specify what sort of closure they want,
// examine the expected type. For now, if we see explicit // examine the expected type. For now, if we see explicit
// evidence than an unboxed closure is desired, we'll use // evidence than an unboxed closure is desired, we'll use
// that, otherwise we'll fall back to boxed closures. // that, otherwise we'll fall back to boxed closures.
match deduce_unboxed_closure_expectations_from_expectation(fcx, expected) { match expected_sig_and_kind {
None => { // doesn't look like an unboxed closure None => { // doesn't look like an unboxed closure
let region = astconv::opt_ast_region_to_region(fcx, let region = astconv::opt_ast_region_to_region(fcx,
fcx.infcx(), fcx.infcx(),
@ -66,10 +68,7 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind,
}; };
let expected_sig = let expected_sig = expected_sig_and_kind.map(|t| t.0);
deduce_unboxed_closure_expectations_from_expectation(fcx, expected)
.map(|t| t.0);
check_unboxed_closure(fcx, expr, kind, decl, body, expected_sig); check_unboxed_closure(fcx, expr, kind, decl, body, expected_sig);
} }
} }
@ -147,19 +146,6 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
.insert(expr_def_id, unboxed_closure); .insert(expr_def_id, unboxed_closure);
} }
fn deduce_unboxed_closure_expectations_from_expectation<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
expected: Expectation<'tcx>)
-> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)>
{
match expected.resolve(fcx) {
NoExpectation => None,
ExpectCastableToType(t) | ExpectHasType(t) => {
deduce_unboxed_closure_expectations_from_expected_type(fcx, t)
}
}
}
fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>( fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>, fcx: &FnCtxt<'a,'tcx>,
expected_ty: Ty<'tcx>) expected_ty: Ty<'tcx>)

View File

@ -176,6 +176,10 @@ enum Expectation<'tcx> {
/// This expression will be cast to the `Ty` /// This expression will be cast to the `Ty`
ExpectCastableToType(Ty<'tcx>), ExpectCastableToType(Ty<'tcx>),
/// This rvalue expression will be wrapped in `&` or `Box` and coerced
/// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`.
ExpectRvalueLikeUnsized(Ty<'tcx>),
} }
impl<'tcx> Expectation<'tcx> { impl<'tcx> Expectation<'tcx> {
@ -196,7 +200,7 @@ impl<'tcx> Expectation<'tcx> {
// when checking the 'then' block which are incompatible with the // when checking the 'then' block which are incompatible with the
// 'else' branch. // 'else' branch.
fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
match self.only_has_type() { match *self {
ExpectHasType(ety) => { ExpectHasType(ety) => {
let ety = fcx.infcx().shallow_resolve(ety); let ety = fcx.infcx().shallow_resolve(ety);
if !ty::type_is_ty_var(ety) { if !ty::type_is_ty_var(ety) {
@ -205,6 +209,9 @@ impl<'tcx> Expectation<'tcx> {
NoExpectation NoExpectation
} }
} }
ExpectRvalueLikeUnsized(ety) => {
ExpectRvalueLikeUnsized(ety)
}
_ => NoExpectation _ => NoExpectation
} }
} }
@ -3678,7 +3685,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
match unop { match unop {
ast::UnUniq => match ty.sty { ast::UnUniq => match ty.sty {
ty::ty_uniq(ty) => { ty::ty_uniq(ty) => {
ExpectHasType(ty) Expectation::rvalue_hint(ty)
} }
_ => { _ => {
NoExpectation NoExpectation
@ -3767,7 +3774,16 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
let expected = expected.only_has_type(); let expected = expected.only_has_type();
let hint = expected.map(fcx, |ty| { let hint = expected.map(fcx, |ty| {
match ty.sty { match ty.sty {
ty::ty_rptr(_, ref mt) | ty::ty_ptr(ref mt) => ExpectHasType(mt.ty), ty::ty_rptr(_, ref mt) | ty::ty_ptr(ref mt) => {
if ty::expr_is_lval(fcx.tcx(), &**oprnd) {
// Lvalues may legitimately have unsized types.
// For example, dereferences of a fat pointer and
// the last field of a struct can be unsized.
ExpectHasType(mt.ty)
} else {
Expectation::rvalue_hint(mt.ty)
}
}
_ => NoExpectation _ => NoExpectation
} }
}); });
@ -3985,15 +4001,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
check_cast(fcx, expr, &**e, &**t); check_cast(fcx, expr, &**e, &**t);
} }
ast::ExprVec(ref args) => { ast::ExprVec(ref args) => {
let uty = match expected { let uty = expected.map_to_option(fcx, |uty| {
ExpectHasType(uty) => { match uty.sty {
match uty.sty { ty::ty_vec(ty, _) => Some(ty),
ty::ty_vec(ty, _) => Some(ty), _ => None
_ => None
}
} }
_ => None });
};
let typ = match uty { let typ = match uty {
Some(uty) => { Some(uty) => {
@ -4020,8 +4033,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
let uty = match expected { let uty = match expected {
ExpectHasType(uty) => { ExpectHasType(uty) => {
match uty.sty { match uty.sty {
ty::ty_vec(ty, _) => Some(ty), ty::ty_vec(ty, _) => Some(ty),
_ => None _ => None
} }
} }
_ => None _ => None
@ -4298,10 +4311,38 @@ fn constrain_path_type_parameters(fcx: &FnCtxt,
} }
impl<'tcx> Expectation<'tcx> { impl<'tcx> Expectation<'tcx> {
/// Provide an expectation for an rvalue expression given an *optional*
/// hint, which is not required for type safety (the resulting type might
/// be checked higher up, as is the case with `&expr` and `box expr`), but
/// is useful in determining the concrete type.
///
/// The primary use case is where the expected type is a fat pointer,
/// like `&[int]`. For example, consider the following statement:
///
/// let x: &[int] = &[1, 2, 3];
///
/// In this case, the expected type for the `&[1, 2, 3]` expression is
/// `&[int]`. If however we were to say that `[1, 2, 3]` has the
/// expectation `ExpectHasType([int])`, that would be too strong --
/// `[1, 2, 3]` does not have the type `[int]` but rather `[int, ..3]`.
/// It is only the `&[1, 2, 3]` expression as a whole that can be coerced
/// to the type `&[int]`. Therefore, we propagate this more limited hint,
/// which still is useful, because it informs integer literals and the like.
/// See the test case `test/run-pass/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
fn rvalue_hint(ty: Ty<'tcx>) -> Expectation<'tcx> {
match ty.sty {
ty::ty_vec(_, None) | ty::ty_trait(..) => {
ExpectRvalueLikeUnsized(ty)
}
_ => ExpectHasType(ty)
}
}
fn only_has_type(self) -> Expectation<'tcx> { fn only_has_type(self) -> Expectation<'tcx> {
match self { match self {
NoExpectation | ExpectCastableToType(..) => NoExpectation, ExpectHasType(t) => ExpectHasType(t),
ExpectHasType(t) => ExpectHasType(t) _ => NoExpectation
} }
} }
@ -4321,6 +4362,10 @@ impl<'tcx> Expectation<'tcx> {
ExpectHasType( ExpectHasType(
fcx.infcx().resolve_type_vars_if_possible(&t)) fcx.infcx().resolve_type_vars_if_possible(&t))
} }
ExpectRvalueLikeUnsized(t) => {
ExpectRvalueLikeUnsized(
fcx.infcx().resolve_type_vars_if_possible(&t))
}
} }
} }
@ -4329,7 +4374,9 @@ impl<'tcx> Expectation<'tcx> {
{ {
match self.resolve(fcx) { match self.resolve(fcx) {
NoExpectation => NoExpectation, NoExpectation => NoExpectation,
ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty), ExpectCastableToType(ty) |
ExpectHasType(ty) |
ExpectRvalueLikeUnsized(ty) => unpack(ty),
} }
} }
@ -4338,7 +4385,9 @@ impl<'tcx> Expectation<'tcx> {
{ {
match self.resolve(fcx) { match self.resolve(fcx) {
NoExpectation => None, NoExpectation => None,
ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty), ExpectCastableToType(ty) |
ExpectHasType(ty) |
ExpectRvalueLikeUnsized(ty) => unpack(ty),
} }
} }
} }
@ -4351,6 +4400,8 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
t.repr(tcx)), t.repr(tcx)),
ExpectCastableToType(t) => format!("ExpectCastableToType({})", ExpectCastableToType(t) => format!("ExpectCastableToType({})",
t.repr(tcx)), t.repr(tcx)),
ExpectRvalueLikeUnsized(t) => format!("ExpectRvalueLikeUnsized({})",
t.repr(tcx)),
} }
} }
} }

View File

@ -0,0 +1,30 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::fmt::Show;
// Check that coercions apply at the pointer level and don't cause
// rvalue expressions to be unsized. See #20169 for more information.
pub fn main() {
let _: Box<[int]> = box { [1, 2, 3] };
let _: Box<[int]> = box if true { [1, 2, 3] } else { [1, 3, 4] };
let _: Box<[int]> = box match true { true => [1, 2, 3], false => [1, 3, 4] };
let _: Box<Fn(int) -> _> = box { |x| (x as u8) };
let _: Box<Show> = box if true { false } else { true };
let _: Box<Show> = box match true { true => 'a', false => 'b' };
let _: &[int] = &{ [1, 2, 3] };
let _: &[int] = &if true { [1, 2, 3] } else { [1, 3, 4] };
let _: &[int] = &match true { true => [1, 2, 3], false => [1, 3, 4] };
let _: &Fn(int) -> _ = &{ |x| (x as u8) };
let _: &Show = &if true { false } else { true };
let _: &Show = &match true { true => 'a', false => 'b' };
}