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:
commit
d10642ef0f
|
@ -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
|
||||||
|
|
|
@ -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>)
|
||||||
|
|
|
@ -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)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' };
|
||||||
|
}
|
Loading…
Reference in New Issue