Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc_typeck: don't expect rvalues to have unsized types. #20083

Merged
merged 1 commit into from
Dec 23, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4212,10 +4212,14 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
}

def::DefStruct(_) => {
match expr_ty(tcx, expr).sty {
ty_bare_fn(..) => RvalueDatumExpr,
_ => RvalueDpsExpr
}
match tcx.node_types.borrow().get(&expr.id) {
Some(ty) => match ty.sty {
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
Expand Down
28 changes: 7 additions & 21 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@

//! 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;
Expand All @@ -34,13 +32,17 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
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(),
Expand All @@ -66,10 +68,7 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
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);
}
}
Expand Down Expand Up @@ -147,19 +146,6 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
.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>)
Expand Down
85 changes: 68 additions & 17 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ enum Expectation<'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> {
Expand All @@ -196,7 +200,7 @@ 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) {
Expand All @@ -205,6 +209,9 @@ impl<'tcx> Expectation<'tcx> {
NoExpectation
}
}
ExpectRvalueLikeUnsized(ety) => {
ExpectRvalueLikeUnsized(ety)
}
_ => NoExpectation
}
}
Expand Down Expand Up @@ -3678,7 +3685,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
match unop {
ast::UnUniq => match ty.sty {
ty::ty_uniq(ty) => {
ExpectHasType(ty)
Expectation::rvalue_hint(ty)
}
_ => {
NoExpectation
Expand Down Expand Up @@ -3767,7 +3774,16 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
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
}
});
Expand Down Expand Up @@ -3985,15 +4001,12 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
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) => {
Expand All @@ -4020,8 +4033,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
let uty = match expected {
ExpectHasType(uty) => {
match uty.sty {
ty::ty_vec(ty, _) => Some(ty),
_ => None
ty::ty_vec(ty, _) => Some(ty),
_ => None
}
}
_ => None
Expand Down Expand Up @@ -4298,10 +4311,38 @@ fn constrain_path_type_parameters(fcx: &FnCtxt,
}

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
}
}

Expand All @@ -4321,6 +4362,10 @@ impl<'tcx> Expectation<'tcx> {
ExpectHasType(
fcx.infcx().resolve_type_vars_if_possible(&t))
}
ExpectRvalueLikeUnsized(t) => {
ExpectRvalueLikeUnsized(
fcx.infcx().resolve_type_vars_if_possible(&t))
}
}
}

Expand All @@ -4329,7 +4374,9 @@ impl<'tcx> Expectation<'tcx> {
{
match self.resolve(fcx) {
NoExpectation => NoExpectation,
ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
ExpectCastableToType(ty) |
ExpectHasType(ty) |
ExpectRvalueLikeUnsized(ty) => unpack(ty),
}
}

Expand All @@ -4338,7 +4385,9 @@ impl<'tcx> Expectation<'tcx> {
{
match self.resolve(fcx) {
NoExpectation => None,
ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
ExpectCastableToType(ty) |
ExpectHasType(ty) |
ExpectRvalueLikeUnsized(ty) => unpack(ty),
}
}
}
Expand All @@ -4351,6 +4400,8 @@ impl<'tcx> Repr<'tcx> for Expectation<'tcx> {
t.repr(tcx)),
ExpectCastableToType(t) => format!("ExpectCastableToType({})",
t.repr(tcx)),
ExpectRvalueLikeUnsized(t) => format!("ExpectRvalueLikeUnsized({})",
t.repr(tcx)),
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/test/run-pass/coerce-expect-unsized.rs
Original file line number Diff line number Diff line change
@@ -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' };
}