Forbid casting to/from a pointer of unknown kind

This commit is contained in:
Wonwoo Choi 2017-11-03 16:58:01 +09:00
parent 74be072068
commit 99ada043b6
4 changed files with 111 additions and 23 deletions

View File

@ -83,28 +83,30 @@ enum PointerKind<'tcx> {
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
/// Returns the kind of unsize information of t, or None
/// if t is sized or it is unknown.
fn pointer_kind(&self, t: Ty<'tcx>, span: Span) -> PointerKind<'tcx> {
/// if t is unknown.
fn pointer_kind(&self, t: Ty<'tcx>, span: Span) -> Option<PointerKind<'tcx>> {
if self.type_is_known_to_be_sized(t, span) {
return PointerKind::Thin;
return Some(PointerKind::Thin);
}
match t.sty {
ty::TySlice(_) | ty::TyStr => PointerKind::Length,
ty::TySlice(_) | ty::TyStr => Some(PointerKind::Length),
ty::TyDynamic(ref tty, ..) =>
PointerKind::Vtable(tty.principal().map(|p| p.def_id())),
Some(PointerKind::Vtable(tty.principal().map(|p| p.def_id()))),
ty::TyAdt(def, substs) if def.is_struct() => {
// FIXME(arielb1): do some kind of normalization
match def.struct_variant().fields.last() {
None => PointerKind::Thin,
None => Some(PointerKind::Thin),
Some(f) => self.pointer_kind(f.ty(self.tcx, substs), span),
}
}
// Pointers to foreign types are thin, despite being unsized
ty::TyForeign(..) => PointerKind::Thin,
ty::TyForeign(..) => Some(PointerKind::Thin),
// We should really try to normalize here.
ty::TyProjection(ref pi) => PointerKind::OfProjection(pi),
ty::TyParam(ref p) => PointerKind::OfParam(p),
ty::TyProjection(ref pi) => Some(PointerKind::OfProjection(pi)),
ty::TyParam(ref p) => Some(PointerKind::OfParam(p)),
// Insufficient type information.
ty::TyInfer(_) => None,
_ => panic!(),
}
}
@ -123,6 +125,8 @@ enum CastError {
NeedViaThinPtr,
NeedViaInt,
NonScalar,
UnknownExprPtrKind,
UnknownCastPtrKind,
}
fn make_invalid_casting_error<'a, 'gcx, 'tcx>(sess: &'a Session,
@ -241,6 +245,25 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
self.expr_ty,
fcx.ty_to_string(self.cast_ty)).emit();
}
CastError::UnknownCastPtrKind |
CastError::UnknownExprPtrKind => {
let unknown_cast_to = match e {
CastError::UnknownCastPtrKind => true,
CastError::UnknownExprPtrKind => false,
_ => bug!(),
};
let mut err = struct_span_err!(fcx.tcx.sess, self.span, E0641,
"cannot cast {} a pointer of an unknown kind",
if unknown_cast_to { "to" } else { "from" });
err.note("The type information given here is insufficient to check whether \
the pointer cast is valid");
if unknown_cast_to {
err.span_suggestion_short(self.cast_span,
"consider giving more type information",
String::new());
}
err.emit();
}
}
}
@ -457,14 +480,27 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast);
// ptr-ptr cast. vtables must match.
// Cast to thin pointer is OK
let expr_kind = fcx.pointer_kind(m_expr.ty, self.span);
let cast_kind = fcx.pointer_kind(m_cast.ty, self.span);
let cast_kind = match cast_kind {
// We can't cast if target pointer kind is unknown
None => return Err(CastError::UnknownCastPtrKind),
Some(cast_kind) => cast_kind,
};
// Cast to thin pointer is OK
if cast_kind == PointerKind::Thin {
return Ok(CastKind::PtrPtrCast);
}
let expr_kind = match expr_kind {
// We can't cast to fat pointer if source pointer kind is unknown
None => return Err(CastError::UnknownExprPtrKind),
Some(expr_kind) => expr_kind,
};
// thin -> fat? report invalid cast (don't complain about vtable kinds)
let expr_kind = fcx.pointer_kind(m_expr.ty, self.span);
if expr_kind == PointerKind::Thin {
return Err(CastError::SizedUnsizedCast);
}
@ -483,10 +519,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
-> Result<CastKind, CastError> {
// fptr-ptr cast. must be to thin ptr
if fcx.pointer_kind(m_cast.ty, self.span) == PointerKind::Thin {
Ok(CastKind::FnPtrPtrCast)
} else {
Err(CastError::IllegalCast)
match fcx.pointer_kind(m_cast.ty, self.span) {
None => Err(CastError::UnknownCastPtrKind),
Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast),
_ => Err(CastError::IllegalCast),
}
}
@ -496,10 +532,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
-> Result<CastKind, CastError> {
// ptr-addr cast. must be from thin ptr
if fcx.pointer_kind(m_expr.ty, self.span) == PointerKind::Thin {
Ok(CastKind::PtrAddrCast)
} else {
Err(CastError::NeedViaThinPtr)
match fcx.pointer_kind(m_expr.ty, self.span) {
None => Err(CastError::UnknownExprPtrKind),
Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast),
_ => Err(CastError::NeedViaThinPtr),
}
}
@ -533,10 +569,10 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> {
m_cast: &'tcx ty::TypeAndMut<'tcx>)
-> Result<CastKind, CastError> {
// ptr-addr cast. pointer must be thin.
if fcx.pointer_kind(m_cast.ty, self.span) == PointerKind::Thin {
Ok(CastKind::AddrPtrCast)
} else {
Err(CastError::IllegalCast)
match fcx.pointer_kind(m_cast.ty, self.span) {
None => Err(CastError::UnknownCastPtrKind),
Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
_ => Err(CastError::IllegalCast),
}
}

View File

@ -4743,4 +4743,5 @@ register_diagnostics! {
E0627, // yield statement outside of generator literal
E0632, // cannot provide explicit type parameters when `impl Trait` is used in
// argument position.
E0641, // cannot cast to/from a pointer with an unknown kind
}

View File

@ -0,0 +1,19 @@
// Copyright 2017 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;
fn main() {
let x: *const _ = 0 as _;
let x: *const _ = 0 as *const _;
let y: Option<*const fmt::Debug> = Some(x) as _;
let x = 0 as *const i32 as *const _ as *mut _;
}

View File

@ -0,0 +1,32 @@
error[E0641]: cannot cast to a pointer of an unknown kind
--> $DIR/issue-45730.rs:13:23
|
13 | let x: *const _ = 0 as _;
| ^^^^^-
| |
| help: consider giving more type information
|
= note: The type information given here is insufficient to check whether the pointer cast is valid
error[E0641]: cannot cast to a pointer of an unknown kind
--> $DIR/issue-45730.rs:15:23
|
15 | let x: *const _ = 0 as *const _;
| ^^^^^--------
| |
| help: consider giving more type information
|
= note: The type information given here is insufficient to check whether the pointer cast is valid
error[E0641]: cannot cast to a pointer of an unknown kind
--> $DIR/issue-45730.rs:18:13
|
18 | let x = 0 as *const i32 as *const _ as *mut _;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------
| |
| help: consider giving more type information
|
= note: The type information given here is insufficient to check whether the pointer cast is valid
error: aborting due to 3 previous errors