Skip to content

Commit

Permalink
rustc_typeck: don't expect rvalues to have unsized types.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Dec 22, 2014
1 parent 34d6800 commit 87f39e9
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 61 deletions.
12 changes: 8 additions & 4 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4177,10 +4177,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
58 changes: 18 additions & 40 deletions src/librustc_typeck/check/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@

//! 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;
use middle::subst;
use middle::ty::{mod, Ty};
use middle::ty;
use rscope::RegionScope;
use syntax::abi;
use syntax::ast;
Expand All @@ -34,13 +32,27 @@ 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| {
match ty.sty {
ty::ty_trait(ref object_type) => {
deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal)
}
ty::ty_infer(ty::TyVar(vid)) => {
deduce_unboxed_closure_expectations_from_obligations(fcx, vid)
}
_ => {
None
}
}
});

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 +78,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,37 +156,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>)
-> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)>
{
match expected_ty.sty {
ty::ty_trait(ref object_type) => {
deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal)
}
ty::ty_infer(ty::TyVar(vid)) => {
deduce_unboxed_closure_expectations_from_obligations(fcx, vid)
}
_ => {
None
}
}
}

fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
trait_ref: &ty::PolyTraitRef<'tcx>)
Expand Down
73 changes: 56 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 @@ -3658,7 +3665,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 @@ -3747,7 +3754,13 @@ 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) {
ExpectHasType(mt.ty)
} else {
Expectation::rvalue_hint(mt.ty)
}
}
_ => NoExpectation
}
});
Expand Down Expand Up @@ -3965,15 +3978,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 @@ -4000,8 +4010,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 @@ -4278,10 +4288,29 @@ 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 usecase is unpacking `&T` and `Box<T>`, where `T` is
/// unsized and propagating `ExpectHasType(T)` would try to assign `T` to
/// an rvalue expression, which is invalid. Instead, a weaker hint is used.
/// This is required, when e.g `T` is `[u8]` and the rvalue is `[1, 2, 3]`,
/// without the hint the integer literals would remain uninferred.
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 @@ -4301,6 +4330,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 @@ -4309,7 +4342,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 @@ -4318,7 +4353,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 @@ -4331,6 +4368,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.

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

0 comments on commit 87f39e9

Please sign in to comment.