mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
librustc: Don't try to perform the magical
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]
This commit is contained in:
parent
7a93beef7f
commit
315f2a7054
@ -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| {
|
||||
|
31
src/test/compile-fail/vector-cast-weirdness.rs
Normal file
31
src/test/compile-fail/vector-cast-weirdness.rs
Normal file
@ -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];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user