diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index d68c139894b..7c199587c98 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -48,6 +48,7 @@ use rustc::session::Session; use rustc::traits; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::ty::cast::{CastKind, CastTy}; +use rustc::ty::subst::Substs; use rustc::middle::lang_items; use syntax::ast; use syntax_pos::Span; @@ -77,6 +78,8 @@ enum PointerKind<'tcx> { Length, /// The unsize info of this projection OfProjection(&'tcx ty::ProjectionTy<'tcx>), + /// The unsize info of this anon ty + OfAnon(DefId, &'tcx Substs<'tcx>), /// The unsize info of this parameter OfParam(&'tcx ty::ParamTy), } @@ -84,36 +87,65 @@ enum PointerKind<'tcx> { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { /// Returns the kind of unsize information of t, or None /// 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 Some(PointerKind::Thin); + fn pointer_kind(&self, t: Ty<'tcx>, span: Span) -> + Result>, ErrorReported> + { + debug!("pointer_kind({:?}, {:?})", t, span); + + let t = self.resolve_type_vars_if_possible(&t); + + if t.references_error() { + return Err(ErrorReported); } - match t.sty { + if self.type_is_known_to_be_sized(t, span) { + return Ok(Some(PointerKind::Thin)); + } + + Ok(match t.sty { ty::TySlice(_) | ty::TyStr => Some(PointerKind::Length), ty::TyDynamic(ref tty, ..) => 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 => Some(PointerKind::Thin), - Some(f) => self.pointer_kind(f.ty(self.tcx, substs), span), + Some(f) => { + let field_ty = self.field_ty(span, f, substs); + self.pointer_kind(field_ty, span)? + } } } + ty::TyTuple(fields, _) => match fields.last() { + None => Some(PointerKind::Thin), + Some(f) => self.pointer_kind(f, span)? + }, + // Pointers to foreign types are thin, despite being unsized ty::TyForeign(..) => Some(PointerKind::Thin), // We should really try to normalize here. ty::TyProjection(ref pi) => Some(PointerKind::OfProjection(pi)), + ty::TyAnon(def_id, substs) => Some(PointerKind::OfAnon(def_id, substs)), ty::TyParam(ref p) => Some(PointerKind::OfParam(p)), // Insufficient type information. ty::TyInfer(_) => None, - _ => panic!(), - } + + ty::TyBool | ty::TyChar | ty::TyInt(..) | ty::TyUint(..) | + ty::TyFloat(_) | ty::TyArray(..) | + ty::TyRawPtr(_) | ty::TyRef(..) | ty::TyFnDef(..) | + ty::TyFnPtr(..) | ty::TyClosure(..) | ty::TyGenerator(..) | + ty::TyAdt(..) | ty::TyNever | ty::TyError => { + self.tcx.sess.delay_span_bug( + span, &format!("`{:?}` should be sized but is not?", t)); + return Err(ErrorReported); + } + }) } } #[derive(Copy, Clone)] enum CastError { + ErrorReported, + CastToBool, CastToChar, DifferingKinds, @@ -129,6 +161,12 @@ enum CastError { UnknownCastPtrKind, } +impl From for CastError { + fn from(ErrorReported: ErrorReported) -> Self { + CastError::ErrorReported + } +} + fn make_invalid_casting_error<'a, 'gcx, 'tcx>(sess: &'a Session, span: Span, expr_ty: Ty<'tcx>, @@ -173,6 +211,9 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { fn report_cast_error(&self, fcx: &FnCtxt<'a, 'gcx, 'tcx>, e: CastError) { match e { + CastError::ErrorReported => { + // an error has already been reported + } CastError::NeedDeref => { let error_span = self.span; let mut err = make_invalid_casting_error(fcx.tcx.sess, self.span, self.expr_ty, @@ -480,8 +521,8 @@ 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. - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span); - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span); + 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 @@ -519,7 +560,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { -> Result { // fptr-ptr cast. must be to thin ptr - match fcx.pointer_kind(m_cast.ty, self.span) { + match fcx.pointer_kind(m_cast.ty, self.span)? { None => Err(CastError::UnknownCastPtrKind), Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), _ => Err(CastError::IllegalCast), @@ -532,7 +573,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { -> Result { // ptr-addr cast. must be from thin ptr - match fcx.pointer_kind(m_expr.ty, self.span) { + match fcx.pointer_kind(m_expr.ty, self.span)? { None => Err(CastError::UnknownExprPtrKind), Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), _ => Err(CastError::NeedViaThinPtr), @@ -569,7 +610,7 @@ impl<'a, 'gcx, 'tcx> CastCheck<'tcx> { m_cast: &'tcx ty::TypeAndMut<'tcx>) -> Result { // ptr-addr cast. pointer must be thin. - match fcx.pointer_kind(m_cast.ty, self.span) { + 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/test/run-pass/cast-rfc0401-vtable-kinds.rs b/src/test/run-pass/cast-rfc0401-vtable-kinds.rs index 3a9f24ad4cc..32a155c13e6 100644 --- a/src/test/run-pass/cast-rfc0401-vtable-kinds.rs +++ b/src/test/run-pass/cast-rfc0401-vtable-kinds.rs @@ -11,6 +11,8 @@ // Check that you can cast between different pointers to trait objects // whose vtable have the same kind (both lengths, or both trait pointers). +#![feature(unsized_tuple_coercion)] + trait Foo { fn foo(&self, _: T) -> u32 { 42 } } @@ -39,6 +41,11 @@ fn foo_to_bar(u: *const FooS) -> *const BarS { u as *const BarS } +fn tuple_i32_to_u32(u: *const (i32, T)) -> *const (u32, T) { + u as *const (u32, T) +} + + fn main() { let x = 4u32; let y : &Foo = &x; @@ -51,4 +58,14 @@ fn main() { let bar_ref : *const BarS<[u32]> = foo_to_bar(u); let z : &BarS<[u32]> = unsafe{&*bar_ref}; assert_eq!(&z.0, &[0,1,2]); + + // this assumes that tuple reprs for (i32, _) and (u32, _) are + // the same. + let s = (0i32, [0, 1, 2]); + let u: &(i32, [u8]) = &s; + let u: *const (i32, [u8]) = u; + let u_u32 : *const (u32, [u8]) = tuple_i32_to_u32(u); + unsafe { + assert_eq!(&(*u_u32).1, &[0, 1, 2]); + } } diff --git a/src/test/ui/casts-differing-anon.rs b/src/test/ui/casts-differing-anon.rs new file mode 100644 index 00000000000..74c8ff370f9 --- /dev/null +++ b/src/test/ui/casts-differing-anon.rs @@ -0,0 +1,34 @@ +// 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. + +#![feature(conservative_impl_trait)] + +use std::fmt; + +fn foo() -> Box { + let x : Box<[u8]> = Box::new([0]); + x +} +fn bar() -> Box { + let y: Box = Box::new([0]); + y +} + +fn main() { + let f = foo(); + let b = bar(); + + // this is an `*mut [u8]` in practice + let f_raw : *mut _ = Box::into_raw(f); + // this is an `*mut fmt::Debug` in practice + let mut b_raw = Box::into_raw(b); + // ... and they should not be mixable + b_raw = f_raw as *mut _; //~ ERROR is invalid +} diff --git a/src/test/ui/casts-differing-anon.stderr b/src/test/ui/casts-differing-anon.stderr new file mode 100644 index 00000000000..8db6854dba9 --- /dev/null +++ b/src/test/ui/casts-differing-anon.stderr @@ -0,0 +1,10 @@ +error[E0606]: casting `*mut impl std::fmt::Debug+?Sized` as `*mut impl std::fmt::Debug+?Sized` is invalid + --> $DIR/casts-differing-anon.rs:33:13 + | +33 | b_raw = f_raw as *mut _; //~ ERROR is invalid + | ^^^^^^^^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to previous error + diff --git a/src/test/ui/casts-issue-46365.rs b/src/test/ui/casts-issue-46365.rs new file mode 100644 index 00000000000..79f636e413f --- /dev/null +++ b/src/test/ui/casts-issue-46365.rs @@ -0,0 +1,17 @@ +// 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. + +struct Lorem { + ipsum: Ipsum //~ ERROR cannot find type `Ipsum` +} + +fn main() { + let _foo: *mut Lorem = 0 as *mut _; // no error here +} diff --git a/src/test/ui/casts-issue-46365.stderr b/src/test/ui/casts-issue-46365.stderr new file mode 100644 index 00000000000..ce3c8593a97 --- /dev/null +++ b/src/test/ui/casts-issue-46365.stderr @@ -0,0 +1,8 @@ +error[E0412]: cannot find type `Ipsum` in this scope + --> $DIR/casts-issue-46365.rs:12:12 + | +12 | ipsum: Ipsum //~ ERROR cannot find type `Ipsum` + | ^^^^^ not found in this scope + +error: aborting due to previous error +