Skip to content

Forbid casting to/from a pointer of unknown kind #45735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 59 additions & 23 deletions src/librustc_typeck/check/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(),
}
}
Expand All @@ -123,6 +125,8 @@ enum CastError {
NeedViaThinPtr,
NeedViaInt,
NonScalar,
UnknownExprPtrKind,
UnknownCastPtrKind,
}

fn make_invalid_casting_error<'a, 'gcx, 'tcx>(sess: &'a Session,
Expand Down Expand Up @@ -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();
}
}
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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),
}
}

Expand All @@ -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),
}
}

Expand Down Expand Up @@ -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),
}
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
19 changes: 19 additions & 0 deletions src/test/ui/issue-45730.rs
Original file line number Diff line number Diff line change
@@ -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 _;
}
32 changes: 32 additions & 0 deletions src/test/ui/issue-45730.stderr
Original file line number Diff line number Diff line change
@@ -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