]> git.lizzy.rs Git - rust.git/commitdiff
rustc_typeck: don't expect rvalues to have unsized types.
authorEduard Burtescu <edy.burt@gmail.com>
Tue, 23 Dec 2014 11:30:36 +0000 (13:30 +0200)
committerEduard Burtescu <edy.burt@gmail.com>
Tue, 23 Dec 2014 11:30:36 +0000 (13:30 +0200)
src/librustc/middle/ty.rs
src/librustc_typeck/check/closure.rs
src/librustc_typeck/check/mod.rs
src/test/run-pass/coerce-expect-unsized.rs [new file with mode: 0644]

index 22fdea8afb59bbcace7efc92f52bb19c1e9f1d4f..b2cc52ab0a87bd80d6d187c45f2c351495e9fbdd 100644 (file)
@@ -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
index 092260523670745db03f5c5442c27371a7ab82f8..e25543ea99241c651141e656d4503811169c6a79 100644 (file)
@@ -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>)
index 3139a17f9989eacf022405208c52e303a1742e55..fbd40ef6fed0ebbd223a4e6a914c919f46e04fca 100644 (file)
@@ -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 @@ fn adjust_for_branches<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> {
                     NoExpectation
                 }
             }
+            ExpectRvalueLikeUnsized(ety) => {
+                ExpectRvalueLikeUnsized(ety)
+            }
             _ => NoExpectation
         }
     }
@@ -3678,7 +3685,7 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
             match unop {
                 ast::UnUniq => match ty.sty {
                     ty::ty_uniq(ty) => {
-                        ExpectHasType(ty)
+                        Expectation::rvalue_hint(ty)
                     }
                     _ => {
                         NoExpectation
@@ -3767,7 +3774,16 @@ fn check_struct_fields_on_error(fcx: &FnCtxt,
         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_struct_fields_on_error(fcx: &FnCtxt,
         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_struct_fields_on_error(fcx: &FnCtxt,
         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 @@ fn resolve<'a>(self, fcx: &FnCtxt<'a, '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 @@ fn map<'a, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Expectation<'tcx> wher
     {
         match self.resolve(fcx) {
             NoExpectation => NoExpectation,
-            ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
+            ExpectCastableToType(ty) |
+            ExpectHasType(ty) |
+            ExpectRvalueLikeUnsized(ty) => unpack(ty),
         }
     }
 
@@ -4338,7 +4385,9 @@ fn map_to_option<'a, O, F>(self, fcx: &FnCtxt<'a, 'tcx>, unpack: F) -> Option<O>
     {
         match self.resolve(fcx) {
             NoExpectation => None,
-            ExpectCastableToType(ty) | ExpectHasType(ty) => unpack(ty),
+            ExpectCastableToType(ty) |
+            ExpectHasType(ty) |
+            ExpectRvalueLikeUnsized(ty) => unpack(ty),
         }
     }
 }
@@ -4351,6 +4400,8 @@ fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
                                         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 (file)
index 0000000..09f2307
--- /dev/null
@@ -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' };
+}