From 972c3be6c35c0fa1121c4b497d98e51b4878c7c8 Mon Sep 17 00:00:00 2001
From: Matthew Jasper <mjjasper1@gmail.com>
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);