about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-06-25 08:06:21 +0000
committerbors <bors@rust-lang.org>2014-06-25 08:06:21 +0000
commitbab614f5fa725d248afc5f0530c835f37998ce8f (patch)
treeea688a212917e421b2ebf1105c5d3e62cf7dc407
parent9e103acaba98ce654c073c89f74a84b861f16d6e (diff)
parent315f2a70542078c45c9f7ebf4face32a54fde774 (diff)
downloadrust-bab614f5fa725d248afc5f0530c835f37998ce8f.tar.gz
rust-bab614f5fa725d248afc5f0530c835f37998ce8f.zip
auto merge of #15164 : pcwalton/rust/vector-cast-weirdness, r=alexcrichton
vector-reference-to-unsafe-pointer-to-element cast if the type to be
casted to is not fully specified.

This is a conservative change to fix the user-visible symptoms of the
issue. A more flexible treatment would delay cast checks to after
function typechecking.

This can break code that did:

    let x: *u8 = &([0, 0]) as *_;

Change this code to:

    let x: *u8 = &([0, 0]) as *u8;

Closes #14893.

[breaking-change]

r? @alexcrichton
-rw-r--r--src/librustc/middle/typeck/check/mod.rs289
-rw-r--r--src/test/compile-fail/vector-cast-weirdness.rs31
2 files changed, 191 insertions, 129 deletions
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 647b099a10a..0b35eab5679 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1060,6 +1060,164 @@ fn compare_impl_method(tcx: &ty::ctxt,
     }
 }
 
+fn check_cast(fcx: &FnCtxt,
+              e: &ast::Expr,
+              t: &ast::Ty,
+              id: ast::NodeId,
+              span: Span) {
+    // Find the type of `e`. Supply hints based on the type we are casting to,
+    // if appropriate.
+    let t_1 = fcx.to_ty(t);
+    let t_1 = structurally_resolved_type(fcx, span, t_1);
+
+    if ty::type_is_scalar(t_1) {
+        // Supply the type as a hint so as to influence integer
+        // literals and other things that might care.
+        check_expr_with_hint(fcx, e, t_1)
+    } else {
+        check_expr(fcx, e)
+    }
+
+    let t_e = fcx.expr_ty(e);
+
+    debug!("t_1={}", fcx.infcx().ty_to_str(t_1));
+    debug!("t_e={}", fcx.infcx().ty_to_str(t_e));
+
+    if ty::type_is_error(t_e) {
+        fcx.write_error(id);
+        return
+    }
+    if ty::type_is_bot(t_e) {
+        fcx.write_bot(id);
+        return
+    }
+
+    if ty::type_is_trait(t_1) {
+        // This will be looked up later on.
+        fcx.write_ty(id, t_1);
+        return
+    }
+
+    let t_1 = structurally_resolved_type(fcx, span, t_1);
+    let t_e = structurally_resolved_type(fcx, span, t_e);
+
+    if ty::type_is_nil(t_e) {
+        fcx.type_error_message(span, |actual| {
+            format!("cast from nil: `{}` as `{}`",
+                    actual,
+                    fcx.infcx().ty_to_str(t_1))
+        }, t_e, None);
+    } else if ty::type_is_nil(t_1) {
+        fcx.type_error_message(span, |actual| {
+            format!("cast to nil: `{}` as `{}`",
+                    actual,
+                    fcx.infcx().ty_to_str(t_1))
+        }, t_e, None);
+    }
+
+    let t_1_is_scalar = ty::type_is_scalar(t_1);
+    let t_1_is_char = ty::type_is_char(t_1);
+    let t_1_is_bare_fn = ty::type_is_bare_fn(t_1);
+    let t_1_is_float = ty::type_is_floating_point(t_1);
+
+    // casts to scalars other than `char` and `bare fn` are trivial
+    let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn;
+    if ty::type_is_c_like_enum(fcx.tcx(), t_e) && t_1_is_trivial {
+        if t_1_is_float {
+            fcx.type_error_message(span, |actual| {
+                format!("illegal cast; cast through an \
+                         integer first: `{}` as `{}`",
+                        actual,
+                        fcx.infcx().ty_to_str(t_1))
+            }, t_e, None);
+        }
+        // casts from C-like enums are allowed
+    } else if t_1_is_char {
+        let t_e = fcx.infcx().resolve_type_vars_if_possible(t_e);
+        if ty::get(t_e).sty != ty::ty_uint(ast::TyU8) {
+            fcx.type_error_message(span, |actual| {
+                format!("only `u8` can be cast as \
+                         `char`, not `{}`", actual)
+            }, t_e, None);
+        }
+    } else if ty::get(t_1).sty == ty::ty_bool {
+        fcx.tcx()
+           .sess
+           .span_err(span,
+                     "cannot cast as `bool`, compare with zero instead");
+    } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
+        fn is_vec(t: ty::t) -> bool {
+            match ty::get(t).sty {
+                ty::ty_vec(..) => true,
+                ty::ty_ptr(ty::mt{ty: t, ..}) |
+                ty::ty_rptr(_, ty::mt{ty: t, ..}) |
+                ty::ty_box(t) |
+                ty::ty_uniq(t) => {
+                    match ty::get(t).sty {
+                        ty::ty_vec(_, None) => true,
+                        _ => false,
+                    }
+                }
+                _ => false
+            }
+        }
+        fn types_compatible(fcx: &FnCtxt, sp: Span,
+                            t1: ty::t, t2: ty::t) -> bool {
+            if !is_vec(t1) {
+                // If the type being casted from is not a vector, this special
+                // case does not apply.
+                return false
+            }
+            if ty::type_needs_infer(t2) {
+                // This prevents this special case from going off when casting
+                // to a type that isn't fully specified; e.g. `as *_`. (Issue
+                // #14893.)
+                return false
+            }
+
+            let el = ty::sequence_element_type(fcx.tcx(), t1);
+            infer::mk_eqty(fcx.infcx(),
+                           false,
+                           infer::Misc(sp),
+                           el,
+                           t2).is_ok()
+        }
+
+        // Due to the limitations of LLVM global constants,
+        // region pointers end up pointing at copies of
+        // vector elements instead of the original values.
+        // To allow unsafe pointers to work correctly, we
+        // need to special-case obtaining an unsafe pointer
+        // from a region pointer to a vector.
+
+        /* this cast is only allowed from &[T] to *T or
+        &T to *T. */
+        match (&ty::get(t_e).sty, &ty::get(t_1).sty) {
+            (&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
+             &ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
+            if types_compatible(fcx, e.span, mt1, mt2) => {
+                /* this case is allowed */
+            }
+            _ => {
+                demand::coerce(fcx, e.span, t_1, &*e);
+            }
+        }
+    } else if !(ty::type_is_scalar(t_e) && t_1_is_trivial) {
+        /*
+        If more type combinations should be supported than are
+        supported here, then file an enhancement issue and
+        record the issue number in this comment.
+        */
+        fcx.type_error_message(span, |actual| {
+            format!("non-scalar cast: `{}` as `{}`",
+                    actual,
+                    fcx.infcx().ty_to_str(t_1))
+        }, t_e, None);
+    }
+
+    fcx.write_ty(id, t_1);
+}
+
 impl<'a> AstConv for FnCtxt<'a> {
     fn tcx<'a>(&'a self) -> &'a ty::ctxt { self.ccx.tcx }
 
@@ -3049,11 +3207,8 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
             fcx.write_bot(id);
         }
       }
-      ast::ExprCast(expr_from, t) => {
-        let ty_to = fcx.to_ty(t);
-        debug!("ExprCast ty_to={}", fcx.infcx().ty_to_str(ty_to));
-        check_cast(fcx, expr_from, ty_to);
-        fcx.write_ty(id, ty_to);
+      ast::ExprCast(ref e, ref t) => {
+        check_cast(fcx, &**e, &**t, id, expr.span);
       }
       ast::ExprVec(ref args) => {
         let t: ty::t = fcx.infcx().next_ty_var();
@@ -3248,130 +3403,6 @@ impl Repr for Expectation {
     }
 }
 
-fn check_cast(fcx: &FnCtxt, expr_from: Gc<ast::Expr>, ty_to: ty::t) {
-    // Find the type of `expr_from`. Supply hints based on the type
-    // we are casting to, if appropriate.
-    let ty_to = structurally_resolved_type(fcx, expr_from.span, ty_to);
-    if ty::type_is_scalar(ty_to) {
-        // Supply the type as a hint so as to influence integer
-        // literals and other things that might care.
-        check_expr_with_hint(fcx, expr_from, ty_to)
-    } else {
-        check_expr(fcx, expr_from)
-    }
-    let ty_from = fcx.expr_ty(expr_from);
-
-    // Object creation is checked during the vtable phase.
-    if ty::type_is_trait(ty_to) {
-        check_expr(fcx, expr_from);
-        return;
-    }
-
-    let ty_from = fcx.infcx().resolve_type_vars_if_possible(ty_from);
-
-    if ty::type_is_nil(ty_from) {
-        fcx.type_error_message(expr_from.span, |actual| {
-            format!("cast from nil: `{}` as `{}`", actual,
-                    fcx.infcx().ty_to_str(ty_to))
-        }, ty_from, None);
-        return;
-    }
-
-    if ty::type_is_nil(ty_to) {
-        fcx.type_error_message(expr_from.span, |actual| {
-            format!("cast to nil: `{}` as `{}`", actual,
-                    fcx.infcx().ty_to_str(ty_to))
-        }, ty_from, None);
-        return;
-    }
-
-    let t_e = structurally_resolved_type(fcx, expr_from.span, ty_from);
-    let t_1 = structurally_resolved_type(fcx, expr_from.span, ty_to);
-
-    let to_is_scalar = ty::type_is_scalar(t_1);
-    let to_is_float = ty::type_is_floating_point(t_1);
-    let to_is_char = ty::type_is_char(t_1);
-    let to_is_bare_fn = ty::type_is_bare_fn(t_1);
-
-    // casts to scalars other than `char` and `bare fn` are trivial
-    let to_is_trivial = to_is_scalar &&
-        !to_is_char && !to_is_bare_fn;
-
-    if ty::type_is_c_like_enum(fcx.tcx(), t_e) && to_is_trivial {
-        if to_is_float {
-            fcx.type_error_message(expr_from.span, |actual| {
-                format!("illegal cast; cast through an integer first: `{}` \
-                         as `{}`",
-                        actual,
-                        fcx.infcx().ty_to_str(t_1))
-            }, ty_from, None);
-        }
-        // casts from C-like enums are allowed
-    } else if to_is_char {
-        if ty::get(ty_from).sty != ty::ty_uint(ast::TyU8) {
-            fcx.type_error_message(expr_from.span, |actual| {
-                format!("only `u8` can be cast as `char`, not `{}`", actual)
-            }, ty_from, None);
-        }
-    } else if ty::type_is_bool(t_1) {
-        fcx.tcx().sess.span_err(expr_from.span,
-                                "cannot cast as `bool`, compare with zero instead");
-    } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) {
-        fn is_vec(t: ty::t) -> bool {
-            match ty::get(t).sty {
-                ty::ty_vec(..) => true,
-                ty::ty_ptr(ty::mt{ty: t, ..}) |
-                ty::ty_rptr(_, ty::mt{ty: t, ..}) |
-                ty::ty_box(t) |
-                ty::ty_uniq(t) => match ty::get(t).sty {
-                    ty::ty_vec(_, None) => true,
-                    _ => false,
-                },
-                _ => false
-            }
-        }
-        fn types_compatible(fcx: &FnCtxt, sp: Span,
-                            t1: ty::t, t2: ty::t) -> bool {
-            if !is_vec(t1) {
-                false
-            } else {
-                let el = ty::sequence_element_type(fcx.tcx(),
-                                                   t1);
-                infer::mk_eqty(fcx.infcx(), false,
-                               infer::Misc(sp), el, t2).is_ok()
-            }
-        }
-
-        // Due to the limitations of LLVM global constants,
-        // region pointers end up pointing at copies of
-        // vector elements instead of the original values.
-        // To allow unsafe pointers to work correctly, we
-        // need to special-case obtaining an unsafe pointer
-        // from a region pointer to a vector.
-
-        /* this cast is only allowed from &[T] to *T or
-        &T to *T. */
-        match (&ty::get(t_e).sty, &ty::get(t_1).sty) {
-            (&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
-             &ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable }))
-                if types_compatible(fcx, expr_from.span, mt1, mt2) => {
-                    /* this case is allowed */
-                }
-            _ => {
-                demand::coerce(fcx, expr_from.span, ty_to, expr_from);
-            }
-        }
-    } else if !(ty::type_is_scalar(t_e) && to_is_trivial) {
-        // If more type combinations should be supported than are
-        // supported here, then file an enhancement issue and
-        // record the issue number in this comment.
-        fcx.type_error_message(expr_from.span, |actual| {
-            format!("non-scalar cast: `{}` as `{}`", actual,
-                    fcx.infcx().ty_to_str(ty_to))
-        }, ty_from, None);
-    }
-}
-
 pub fn require_uint(fcx: &FnCtxt, sp: Span, t: ty::t) {
     if !type_is_uint(fcx, sp, t) {
         fcx.type_error_message(sp, |actual| {
diff --git a/src/test/compile-fail/vector-cast-weirdness.rs b/src/test/compile-fail/vector-cast-weirdness.rs
new file mode 100644
index 00000000000..da0f0f5f7c9
--- /dev/null
+++ b/src/test/compile-fail/vector-cast-weirdness.rs
@@ -0,0 +1,31 @@
+// Copyright 2012 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.
+
+// Issue #14893. Tests that casts from vectors don't behave strangely in the
+// presence of the `_` type shorthand notation.
+
+struct X {
+    y: [u8, ..2],
+}
+
+fn main() {
+    let x1 = X { y: [0, 0] };
+
+    let p1: *u8 = &x1.y as *_;  //~ ERROR mismatched types
+    let t1: *[u8, ..2] = &x1.y as *_;
+    let h1: *[u8, ..2] = &x1.y as *[u8, ..2];
+
+    let mut x1 = X { y: [0, 0] };
+
+    let p1: *mut u8 = &mut x1.y as *mut _;  //~ ERROR mismatched types
+    let t1: *mut [u8, ..2] = &mut x1.y as *mut _;
+    let h1: *mut [u8, ..2] = &mut x1.y as *mut [u8, ..2];
+}
+