//! Code for type-checking closure expressions.
-use super::check_fn;
-use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation};
-use super::FnCtxt;
+use super::{check_fn, Expectation, FnCtxt};
use astconv;
use middle::infer;
expr.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 {
None => {
// If users didn't specify what sort of closure they want,
// examine the expected type. For now, if we see explicit
// evidence than an unboxed closure is desired, we'll use
// 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
let region = astconv::opt_ast_region_to_region(fcx,
fcx.infcx(),
ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind,
};
- let expected_sig =
- deduce_unboxed_closure_expectations_from_expectation(fcx, expected)
- .map(|t| t.0);
-
+ let expected_sig = expected_sig_and_kind.map(|t| t.0);
check_unboxed_closure(fcx, expr, kind, decl, body, expected_sig);
}
}
.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>(
fcx: &FnCtxt<'a,'tcx>,
expected_ty: Ty<'tcx>)
/// This expression will be cast to the `Ty`
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> {
// when checking the 'then' block which are incompatible with the
// 'else' branch.
fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
- match self.only_has_type() {
+ match *self {
ExpectHasType(ety) => {
let ety = fcx.infcx().shallow_resolve(ety);
if !ty::type_is_ty_var(ety) {
NoExpectation
}
}
+ ExpectRvalueLikeUnsized(ety) => {
+ ExpectRvalueLikeUnsized(ety)
+ }
_ => NoExpectation
}
}
match unop {
ast::UnUniq => match ty.sty {
ty::ty_uniq(ty) => {
- ExpectHasType(ty)
+ Expectation::rvalue_hint(ty)
}
_ => {
NoExpectation
let expected = expected.only_has_type();
let hint = expected.map(fcx, |ty| {
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
}
});
check_cast(fcx, expr, &**e, &**t);
}
ast::ExprVec(ref args) => {
- let uty = match expected {
- ExpectHasType(uty) => {
- match uty.sty {
- ty::ty_vec(ty, _) => Some(ty),
- _ => None
- }
+ let uty = expected.map_to_option(fcx, |uty| {
+ match uty.sty {
+ ty::ty_vec(ty, _) => Some(ty),
+ _ => None
}
- _ => None
- };
+ });
let typ = match uty {
Some(uty) => {
let uty = match expected {
ExpectHasType(uty) => {
match uty.sty {
- ty::ty_vec(ty, _) => Some(ty),
- _ => None
+ ty::ty_vec(ty, _) => Some(ty),
+ _ => None
}
}
_ => None
}
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> {
match self {
- NoExpectation | ExpectCastableToType(..) => NoExpectation,
- ExpectHasType(t) => ExpectHasType(t)
+ ExpectHasType(t) => ExpectHasType(t),
+ _ => NoExpectation
}
}
ExpectHasType(
fcx.infcx().resolve_type_vars_if_possible(&t))
}
+ ExpectRvalueLikeUnsized(t) => {
+ ExpectRvalueLikeUnsized(
+ fcx.infcx().resolve_type_vars_if_possible(&t))
+ }
}
}
{
match self.resolve(fcx) {
NoExpectation => NoExpectation,
- ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
+ ExpectCastableToType(ty) |
+ ExpectHasType(ty) |
+ ExpectRvalueLikeUnsized(ty) => unpack(ty),
}
}
{
match self.resolve(fcx) {
NoExpectation => None,
- ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
+ ExpectCastableToType(ty) |
+ ExpectHasType(ty) |
+ ExpectRvalueLikeUnsized(ty) => unpack(ty),
}
}
}
t.repr(tcx)),
ExpectCastableToType(t) => format!("ExpectCastableToType({})",
t.repr(tcx)),
+ ExpectRvalueLikeUnsized(t) => format!("ExpectRvalueLikeUnsized({})",
+ t.repr(tcx)),
}
}
}
--- /dev/null
+// 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' };
+}