From adabf4e63d6b1d33b045aa5078b12dec3693c574 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Tue, 23 Dec 2014 13:30:36 +0200 Subject: [PATCH] rustc_typeck: don't expect rvalues to have unsized types. --- src/librustc/middle/ty.rs | 12 ++- src/librustc_typeck/check/closure.rs | 28 ++----- src/librustc_typeck/check/mod.rs | 85 +++++++++++++++++----- src/test/run-pass/coerce-expect-unsized.rs | 30 ++++++++ 4 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 src/test/run-pass/coerce-expect-unsized.rs diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 22fdea8afb5..b2cc52ab0a8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 09226052367..e25543ea992 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -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>) diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3139a17f998..fbd40ef6fed 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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`, 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)), } } } diff --git a/src/test/run-pass/coerce-expect-unsized.rs b/src/test/run-pass/coerce-expect-unsized.rs new file mode 100644 index 00000000000..09f230792a9 --- /dev/null +++ b/src/test/run-pass/coerce-expect-unsized.rs @@ -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 or the MIT license +// , 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 _> = box { |x| (x as u8) }; + let _: Box = box if true { false } else { true }; + let _: Box = 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' }; +}