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
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
@@ -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
28 changes: 7 additions & 21 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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(),
@@ -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);
}
}
@@ -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>)
85 changes: 68 additions & 17 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
@@ -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> {
@@ -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) {
@@ -205,6 +209,9 @@ impl<'tcx> Expectation<'tcx> {
NoExpectation
}
}
ExpectRvalueLikeUnsized(ety) => {
ExpectRvalueLikeUnsized(ety)
}
_ => NoExpectation
}
}
@@ -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
@@ -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
}
});
@@ -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) => {
@@ -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
@@ -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
}
}

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

@@ -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),
}
}

@@ -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),
}
}
}
@@ -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)),
}
}
}
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' };
}