diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 7b35b466830..d68c139894b 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -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> { 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 { // 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 { // 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 { // 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), } } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 075367cbbb7..a23c7ded526 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -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 } diff --git a/src/test/ui/issue-45730.rs b/src/test/ui/issue-45730.rs new file mode 100644 index 00000000000..f725d69ca65 --- /dev/null +++ b/src/test/ui/issue-45730.rs @@ -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 or the MIT license +// , 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 _; +} diff --git a/src/test/ui/issue-45730.stderr b/src/test/ui/issue-45730.stderr new file mode 100644 index 00000000000..c4f2e856b7b --- /dev/null +++ b/src/test/ui/issue-45730.stderr @@ -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 +