From 972c3be6c35c0fa1121c4b497d98e51b4878c7c8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 25 Oct 2019 22:19:08 +0100 Subject: [PATCH] Don't cast directly from `&[T; N]` to `*const T` Instead coerce to `*const [T; N]` and then cast. --- src/librustc/ty/adjustment.rs | 3 + src/librustc_codegen_ssa/mir/rvalue.rs | 1 + .../borrow_check/nll/type_check/mod.rs | 162 ++++++++++++------ src/librustc_mir/hair/cx/expr.rs | 5 + src/librustc_mir/interpret/cast.rs | 4 +- .../transform/qualify_min_const_fn.rs | 3 +- src/librustc_typeck/check/cast.rs | 9 + 7 files changed, 131 insertions(+), 56 deletions(-) diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 9ba99768215a6..1d5ed4273abf6 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -20,6 +20,9 @@ pub enum PointerCast { /// Go from a mut raw pointer to a const raw pointer. MutToConstPointer, + /// Go from `*const [T; N]` to `*const T` + ArrayToPointer, + /// Unsize a pointer/reference value, e.g., `&[T; n]` to /// `&[T]`. Note that the source could be a thin or fat pointer. /// This will do things like convert thin pointers to fat diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 1608f222bc614..981fdf2298419 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -269,6 +269,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::CastKind::Pointer(PointerCast::MutToConstPointer) + | mir::CastKind::Pointer(PointerCast::ArrayToPointer) | mir::CastKind::Misc => { assert!(bx.cx().is_backend_immediate(cast)); let ll_t_out = bx.cx().immediate_backend_type(cast); diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index b5560fe6751bd..2a6ee880e0d95 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -35,6 +35,7 @@ use rustc::traits::query::type_op::custom::CustomTypeOp; use rustc::traits::query::{Fallible, NoSolution}; use rustc::traits::{self, ObligationCause, PredicateObligations}; use rustc::ty::adjustment::{PointerCast}; +use rustc::ty::cast::CastTy; use rustc::ty::fold::TypeFoldable; use rustc::ty::subst::{Subst, SubstsRef, GenericArgKind, UserSubsts}; use rustc::ty::{ @@ -2169,72 +2170,125 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ty_from, ty_to, terr - ) + ); } } - CastKind::Misc => { - if let ty::Ref(_, mut ty_from, _) = op.ty(body, tcx).kind { - let (mut ty_to, mutability) = if let ty::RawPtr(ty::TypeAndMut { - ty: ty_to, - mutbl, - }) = ty.kind { - (ty_to, mutbl) - } else { + CastKind::Pointer(PointerCast::ArrayToPointer) => { + let ty_from = op.ty(body, tcx); + + let opt_ty_elem = match ty_from.kind { + ty::RawPtr( + ty::TypeAndMut { mutbl: hir::MutImmutable, ty: array_ty } + ) => { + match array_ty.kind { + ty::Array(ty_elem, _) => Some(ty_elem), + _ => None, + } + } + _ => None, + }; + + let ty_elem = match opt_ty_elem { + Some(ty_elem) => ty_elem, + None => { span_mirbug!( self, rvalue, - "invalid cast types {:?} -> {:?}", - op.ty(body, tcx), + "ArrayToPointer cast from unexpected type {:?}", + ty_from, + ); + return; + } + }; + + let ty_to = match ty.kind { + ty::RawPtr( + ty::TypeAndMut { mutbl: hir::MutImmutable, ty: ty_to } + ) => { + ty_to + } + _ => { + span_mirbug!( + self, + rvalue, + "ArrayToPointer cast to unexpected type {:?}", ty, ); return; - }; - - // Handle the direct cast from `&[T; N]` to `*const T` by unwrapping - // any array we find. - while let ty::Array(ty_elem_from, _) = ty_from.kind { - ty_from = ty_elem_from; - if let ty::Array(ty_elem_to, _) = ty_to.kind { - ty_to = ty_elem_to; - } else { - break; - } } + }; - if let hir::MutMutable = mutability { - if let Err(terr) = self.eq_types( - ty_from, - ty_to, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "equating {:?} with {:?} yields {:?}", - ty_from, - ty_to, - terr - ) - } - } else { - if let Err(terr) = self.sub_types( - ty_from, - ty_to, - location.to_locations(), - ConstraintCategory::Cast, - ) { - span_mirbug!( - self, - rvalue, - "relating {:?} with {:?} yields {:?}", - ty_from, - ty_to, - terr - ) + if let Err(terr) = self.sub_types( + ty_elem, + ty_to, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "relating {:?} with {:?} yields {:?}", + ty_elem, + ty_to, + terr + ) + } + } + + CastKind::Misc => { + let ty_from = op.ty(body, tcx); + let cast_ty_from = CastTy::from_ty(ty_from); + let cast_ty_to = CastTy::from_ty(ty); + match (cast_ty_from, cast_ty_to) { + (Some(CastTy::RPtr(ref_tm)), Some(CastTy::Ptr(ptr_tm))) => { + if let hir::MutMutable = ptr_tm.mutbl { + if let Err(terr) = self.eq_types( + ref_tm.ty, + ptr_tm.ty, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "equating {:?} with {:?} yields {:?}", + ref_tm.ty, + ptr_tm.ty, + terr + ) + } + } else { + if let Err(terr) = self.sub_types( + ref_tm.ty, + ptr_tm.ty, + location.to_locations(), + ConstraintCategory::Cast, + ) { + span_mirbug!( + self, + rvalue, + "relating {:?} with {:?} yields {:?}", + ref_tm.ty, + ptr_tm.ty, + terr + ) + } } - } + }, + (None, _) + | (_, None) + | (_, Some(CastTy::FnPtr)) + | (Some(CastTy::Float), Some(CastTy::Ptr(_))) + | (Some(CastTy::Ptr(_)), Some(CastTy::Float)) + | (Some(CastTy::FnPtr), Some(CastTy::Float)) => span_mirbug!( + self, + rvalue, + "Invalid cast {:?} -> {:?}", + ty_from, + ty, + ), + _ => (), } } } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 7bb96661bb746..fbdc4ceeeede8 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -628,6 +628,11 @@ fn make_mirror_unadjusted<'a, 'tcx>( let cast = if cx.tables().is_coercion_cast(source.hir_id) { // Convert the lexpr to a vexpr. ExprKind::Use { source: source.to_ref() } + } else if cx.tables().expr_ty(source).is_region_ptr() { + // Special cased so that we can type check that the element + // type of the source matches the pointed to type of the + // destination. + ExprKind::Pointer { source: source.to_ref(), cast: PointerCast::ArrayToPointer } } else { // check whether this is casting an enum variant discriminant // to prevent cycles, we refer to the discriminant initializer diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 9ab347957f97a..086f092941208 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -26,7 +26,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.unsize_into(src, dest)?; } - Misc | Pointer(PointerCast::MutToConstPointer) => { + Misc + | Pointer(PointerCast::MutToConstPointer) + | Pointer(PointerCast::ArrayToPointer) => { let src = self.read_immediate(src)?; let res = self.cast_immediate(src, dest.layout)?; self.write_immediate(res, dest)?; diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs index c4e44091bc90d..2c968d2a38e9e 100644 --- a/src/librustc_mir/transform/qualify_min_const_fn.rs +++ b/src/librustc_mir/transform/qualify_min_const_fn.rs @@ -150,7 +150,8 @@ fn check_rvalue( _ => check_operand(tcx, operand, span, def_id, body), } } - Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) => { + Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _) + | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => { check_operand(tcx, operand, span, def_id, body) } Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _) | diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index 9cbde276ae97c..dc088586198a4 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -639,6 +639,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { // need to special-case obtaining a raw pointer // from a region pointer to a vector. + // Coerce to a raw pointer so that we generate AddressOf in MIR. + let array_ptr_type = fcx.tcx.mk_ptr(m_expr); + fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) + .unwrap_or_else(|_| bug!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type, + )); + // this will report a type mismatch if needed fcx.demand_eqtype(self.span, ety, m_cast.ty); return Ok(CastKind::ArrayPtrCast);