about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-03-03 16:46:12 +0000
committerbors <bors@rust-lang.org>2019-03-03 16:46:12 +0000
commit2cfd6444a7fb4bd6acef2b97ba53059adc0500a6 (patch)
treed04772a8cd5ed241e7a8c13ddcaf7eaebaf0b0f3 /src
parentc0086b9e8972fef9fd4af24bae20d45021ed06c6 (diff)
parent19a54e80934c802109ae7175cc824c22c672caa6 (diff)
downloadrust-2cfd6444a7fb4bd6acef2b97ba53059adc0500a6.tar.gz
rust-2cfd6444a7fb4bd6acef2b97ba53059adc0500a6.zip
Auto merge of #58673 - matthewjasper:typeck-ptr-coercions, r=pnkfelix
[NLL] Type check operations with pointer types

It seems these were forgotten about. Moving to `Rvalue::AddressOf` simplifies the coercions from references, but I want this to be fixed as soon as possible.

r? @pnkfelix
Diffstat (limited to 'src')
-rw-r--r--src/librustc/ich/impls_mir.rs1
-rw-r--r--src/librustc/mir/mod.rs3
-rw-r--r--src/librustc/ty/context.rs20
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs153
-rw-r--r--src/librustc_mir/build/expr/as_place.rs1
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs6
-rw-r--r--src/librustc_mir/build/expr/category.rs1
-rw-r--r--src/librustc_mir/build/expr/into.rs1
-rw-r--r--src/librustc_mir/hair/cx/expr.rs2
-rw-r--r--src/librustc_mir/hair/mod.rs3
-rw-r--r--src/librustc_mir/interpret/cast.rs2
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs1
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs3
-rw-r--r--src/test/ui/nll/type-check-pointer-coercions.rs39
-rw-r--r--src/test/ui/nll/type-check-pointer-coercions.stderr87
-rw-r--r--src/test/ui/nll/type-check-pointer-comparisons.rs33
-rw-r--r--src/test/ui/nll/type-check-pointer-comparisons.stderr62
18 files changed, 408 insertions, 16 deletions
diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index 41b78ee5096..ba47116434f 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -378,6 +378,7 @@ impl_stable_hash_for!(enum mir::CastKind {
     ReifyFnPointer,
     ClosureFnPointer,
     UnsafeFnPointer,
+    MutToConstPointer,
     Unsize
 });
 
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index b6f5ff25c8f..0462bb80231 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -2237,6 +2237,9 @@ pub enum CastKind {
     /// Converts safe fn() to unsafe fn()
     UnsafeFnPointer,
 
+    /// Coerces *mut T to *const T, preserving T.
+    MutToConstPointer,
+
     /// "Unsize" -- convert a thin-or-fat pointer to a fat pointer.
     /// codegen must figure out the details once full monomorphization
     /// is known. For example, this could be used to cast from a
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 1983dfbd4b7..3c63dcb9ef3 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -59,7 +59,6 @@ use std::hash::{Hash, Hasher};
 use std::fmt;
 use std::mem;
 use std::ops::{Deref, Bound};
-use std::ptr;
 use std::iter;
 use std::sync::mpsc;
 use std::sync::Arc;
@@ -171,7 +170,7 @@ impl<'gcx: 'tcx, 'tcx> CtxtInterners<'tcx> {
 
                 // Make sure we don't end up with inference
                 // types/regions in the global interner
-                if ptr::eq(local, global) {
+                if ptr_eq(local, global) {
                     bug!("Attempted to intern `{:?}` which contains \
                         inference types/regions in the global type context",
                         &ty_struct);
@@ -1163,7 +1162,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
     /// Returns `true` if self is the same as self.global_tcx().
     fn is_global(self) -> bool {
-        ptr::eq(self.interners, &self.global_interners)
+        ptr_eq(self.interners, &self.global_interners)
     }
 
     /// Creates a type context and call the closure with a `TyCtxt` reference
@@ -1817,12 +1816,11 @@ impl<'a, 'tcx> Lift<'tcx> for &'a mir::interpret::Allocation {
 }
 
 pub mod tls {
-    use super::{GlobalCtxt, TyCtxt};
+    use super::{GlobalCtxt, TyCtxt, ptr_eq};
 
     use std::fmt;
     use std::mem;
     use std::marker::PhantomData;
-    use std::ptr;
     use syntax_pos;
     use crate::ty::query;
     use errors::{Diagnostic, TRACK_DIAGNOSTICS};
@@ -2065,7 +2063,7 @@ pub mod tls {
     {
         with_context(|context| {
             unsafe {
-                assert!(ptr::eq(context.tcx.gcx, tcx.gcx));
+                assert!(ptr_eq(context.tcx.gcx, tcx.gcx));
                 let context: &ImplicitCtxt<'_, '_, '_> = mem::transmute(context);
                 f(context)
             }
@@ -2083,8 +2081,8 @@ pub mod tls {
     {
         with_context(|context| {
             unsafe {
-                assert!(ptr::eq(context.tcx.gcx, tcx.gcx));
-                assert!(ptr::eq(context.tcx.interners, tcx.interners));
+                assert!(ptr_eq(context.tcx.gcx, tcx.gcx));
+                assert!(ptr_eq(context.tcx.interners, tcx.interners));
                 let context: &ImplicitCtxt<'_, '_, '_> = mem::transmute(context);
                 f(context)
             }
@@ -2970,6 +2968,12 @@ impl<T, R, E> InternIteratorElement<T, R> for Result<T, E> {
     }
 }
 
+// We are comparing types with different invariant lifetimes, so `ptr::eq`
+// won't work for us.
+fn ptr_eq<T, U>(t: *const T, u: *const U) -> bool {
+    t as *const () == u as *const ()
+}
+
 pub fn provide(providers: &mut ty::query::Providers<'_>) {
     providers.in_scope_traits_map = |tcx, id| tcx.gcx.trait_map.get(&id).cloned();
     providers.module_exports = |tcx, id| tcx.gcx.export_map.get(&id).cloned();
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index b0c667a965d..b8131671320 100644
--- a/src/librustc_codegen_ssa/mir/rvalue.rs
+++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -257,7 +257,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             }
                         }
                     }
-                    mir::CastKind::Misc if bx.cx().is_backend_scalar_pair(operand.layout) => {
+                    mir::CastKind::MutToConstPointer
+                    | mir::CastKind::Misc if bx.cx().is_backend_scalar_pair(operand.layout) => {
                         if let OperandValue::Pair(data_ptr, meta) = operand.val {
                             if bx.cx().is_backend_scalar_pair(cast) {
                                 let data_cast = bx.pointercast(data_ptr,
@@ -274,7 +275,8 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             bug!("Unexpected non-Pair operand")
                         }
                     }
-                    mir::CastKind::Misc => {
+                    mir::CastKind::MutToConstPointer
+                    | mir::CastKind::Misc => {
                         assert!(bx.cx().is_backend_immediate(cast));
                         let ll_t_out = bx.cx().immediate_backend_type(cast);
                         if operand.layout.abi.is_uninhabited() {
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 9fa3bf55247..cc03d4a0c96 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -27,6 +27,7 @@ use rustc::hir::def_id::DefId;
 use rustc::infer::canonical::QueryRegionConstraint;
 use rustc::infer::outlives::env::RegionBoundPairs;
 use rustc::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime, NLLRegionVariableOrigin};
+use rustc::infer::type_variable::TypeVariableOrigin;
 use rustc::mir::interpret::EvalErrorKind::BoundsCheck;
 use rustc::mir::tcx::PlaceTy;
 use rustc::mir::visit::{PlaceContext, Visitor, MutatingUseContext, NonMutatingUseContext};
@@ -2074,7 +2075,118 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         );
                     }
 
-                    CastKind::Misc => {}
+                    CastKind::MutToConstPointer => {
+                        let ty_from = match op.ty(mir, tcx).sty {
+                            ty::RawPtr(ty::TypeAndMut {
+                                ty: ty_from,
+                                mutbl: hir::MutMutable,
+                            }) => ty_from,
+                            _ => {
+                                span_mirbug!(
+                                    self,
+                                    rvalue,
+                                    "unexpected base type for cast {:?}",
+                                    ty,
+                                );
+                                return;
+                            }
+                        };
+                        let ty_to = match ty.sty {
+                            ty::RawPtr(ty::TypeAndMut {
+                                ty: ty_to,
+                                mutbl: hir::MutImmutable,
+                            }) => ty_to,
+                            _ => {
+                                span_mirbug!(
+                                    self,
+                                    rvalue,
+                                    "unexpected target type for cast {:?}",
+                                    ty,
+                                );
+                                return;
+                            }
+                        };
+                        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
+                            )
+                        }
+                    }
+
+                    CastKind::Misc => {
+                        if let ty::Ref(_, mut ty_from, _) = op.ty(mir, tcx).sty {
+                            let (mut ty_to, mutability) = if let ty::RawPtr(ty::TypeAndMut {
+                                ty: ty_to,
+                                mutbl,
+                            }) = ty.sty {
+                                (ty_to, mutbl)
+                            } else {
+                                span_mirbug!(
+                                    self,
+                                    rvalue,
+                                    "invalid cast types {:?} -> {:?}",
+                                    op.ty(mir, tcx),
+                                    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.sty {
+                                ty_from = ty_elem_from;
+                                if let ty::Array(ty_elem_to, _) = ty_to.sty {
+                                    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
+                                    )
+                                }
+                            }
+                        }
+                    }
                 }
             }
 
@@ -2082,7 +2194,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 self.add_reborrow_constraint(mir, location, region, borrowed_place);
             }
 
-            // FIXME: These other cases have to be implemented in future PRs
+            Rvalue::BinaryOp(BinOp::Eq, left, right)
+            | Rvalue::BinaryOp(BinOp::Ne, left, right)
+            | Rvalue::BinaryOp(BinOp::Lt, left, right)
+            | Rvalue::BinaryOp(BinOp::Le, left, right)
+            | Rvalue::BinaryOp(BinOp::Gt, left, right)
+            | Rvalue::BinaryOp(BinOp::Ge, left, right) => {
+                let ty_left = left.ty(mir, tcx);
+                if let ty::RawPtr(_) | ty::FnPtr(_) = ty_left.sty {
+                    let ty_right = right.ty(mir, tcx);
+                    let common_ty = self.infcx.next_ty_var(
+                        TypeVariableOrigin::MiscVariable(mir.source_info(location).span),
+                    );
+                    self.sub_types(
+                        common_ty,
+                        ty_left,
+                        location.to_locations(),
+                        ConstraintCategory::Boring
+                    ).unwrap_or_else(|err| {
+                        bug!("Could not equate type variable with {:?}: {:?}", ty_left, err)
+                    });
+                    if let Err(terr) = self.sub_types(
+                        common_ty,
+                        ty_right,
+                        location.to_locations(),
+                        ConstraintCategory::Boring
+                    ) {
+                        span_mirbug!(
+                            self,
+                            rvalue,
+                            "unexpected comparison types {:?} and {:?} yields {:?}",
+                            ty_left,
+                            ty_right,
+                            terr
+                        )
+                    }
+                }
+            }
+
             Rvalue::Use(..)
             | Rvalue::Len(..)
             | Rvalue::BinaryOp(..)
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 3bea88024b3..20b95c363f5 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -196,6 +196,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             | ExprKind::ReifyFnPointer { .. }
             | ExprKind::ClosureFnPointer { .. }
             | ExprKind::UnsafeFnPointer { .. }
+            | ExprKind::MutToConstPointer { .. }
             | ExprKind::Unsize { .. }
             | ExprKind::Repeat { .. }
             | ExprKind::Borrow { .. }
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index b00d1c612ed..f1a82ecef1b 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -147,8 +147,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 block.and(Rvalue::Use(Operand::Move(Place::Base(PlaceBase::Local(result)))))
             }
             ExprKind::Cast { source } => {
-                let source = this.hir.mirror(source);
-
                 let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
             }
@@ -168,6 +166,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty))
             }
+            ExprKind::MutToConstPointer { source } => {
+                let source = unpack!(block = this.as_operand(block, scope, source));
+                block.and(Rvalue::Cast(CastKind::MutToConstPointer, source, expr.ty))
+            }
             ExprKind::Unsize { source } => {
                 let source = unpack!(block = this.as_operand(block, scope, source));
                 block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
diff --git a/src/librustc_mir/build/expr/category.rs b/src/librustc_mir/build/expr/category.rs
index ca7d435e622..c8c30ac3ce4 100644
--- a/src/librustc_mir/build/expr/category.rs
+++ b/src/librustc_mir/build/expr/category.rs
@@ -62,6 +62,7 @@ impl Category {
             | ExprKind::ReifyFnPointer { .. }
             | ExprKind::ClosureFnPointer { .. }
             | ExprKind::UnsafeFnPointer { .. }
+            | ExprKind::MutToConstPointer { .. }
             | ExprKind::Unsize { .. }
             | ExprKind::Repeat { .. }
             | ExprKind::Borrow { .. }
diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs
index d9839e0c6ec..4d0418beea4 100644
--- a/src/librustc_mir/build/expr/into.rs
+++ b/src/librustc_mir/build/expr/into.rs
@@ -383,6 +383,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             | ExprKind::ReifyFnPointer { .. }
             | ExprKind::ClosureFnPointer { .. }
             | ExprKind::UnsafeFnPointer { .. }
+            | ExprKind::MutToConstPointer { .. }
             | ExprKind::Unsize { .. }
             | ExprKind::Repeat { .. }
             | ExprKind::Borrow { .. }
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 0751af9b12a..65cd9f7103d 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -89,7 +89,7 @@ fn apply_adjustment<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
             ExprKind::NeverToAny { source: expr.to_ref() }
         }
         Adjust::MutToConstPointer => {
-            ExprKind::Cast { source: expr.to_ref() }
+            ExprKind::MutToConstPointer { source: expr.to_ref() }
         }
         Adjust::Deref(None) => {
             // Adjust the span from the block, to the last expression of the
diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs
index 2efdacd7622..6707b01ccc1 100644
--- a/src/librustc_mir/hair/mod.rs
+++ b/src/librustc_mir/hair/mod.rs
@@ -190,6 +190,9 @@ pub enum ExprKind<'tcx> {
     UnsafeFnPointer {
         source: ExprRef<'tcx>,
     },
+    MutToConstPointer {
+        source: ExprRef<'tcx>,
+    },
     Unsize {
         source: ExprRef<'tcx>,
     },
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index ce62d79e585..73c73cc23dc 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -33,7 +33,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 self.unsize_into(src, dest)?;
             }
 
-            Misc => {
+            Misc | MutToConstPointer => {
                 let src = self.read_immediate(src)?;
 
                 if self.type_is_fat_ptr(src.layout.ty) {
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index d41098e2881..20e5c0a23bc 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1095,6 +1095,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Checker<'a, 'tcx> {
             Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
             Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
             Rvalue::Cast(CastKind::Unsize, ..) |
+            Rvalue::Cast(CastKind::MutToConstPointer, ..) |
             Rvalue::Discriminant(..) |
             Rvalue::Len(_) |
             Rvalue::Ref(..) |
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 6c44fac10f5..f82e536ab25 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -152,6 +152,9 @@ fn check_rvalue(
                 _ => check_operand(tcx, mir, operand, span),
             }
         }
+        Rvalue::Cast(CastKind::MutToConstPointer, operand, _) => {
+            check_operand(tcx, mir, operand, span)
+        }
         Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
         Rvalue::Cast(CastKind::ClosureFnPointer, _, _) |
         Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
diff --git a/src/test/ui/nll/type-check-pointer-coercions.rs b/src/test/ui/nll/type-check-pointer-coercions.rs
new file mode 100644
index 00000000000..b6a25eddb86
--- /dev/null
+++ b/src/test/ui/nll/type-check-pointer-coercions.rs
@@ -0,0 +1,39 @@
+#![feature(nll)]
+
+fn shared_to_const<'a, 'b>(x: &&'a i32) -> *const &'b i32 {
+    x   //~ ERROR
+}
+
+fn unique_to_const<'a, 'b>(x: &mut &'a i32) -> *const &'b i32 {
+    x   //~ ERROR
+}
+
+fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 {
+    // Two errors because *mut is invariant
+    x   //~ ERROR
+        //~| ERROR
+}
+
+fn mut_to_const<'a, 'b>(x: *mut &'a i32) -> *const &'b i32 {
+    x   //~ ERROR
+}
+
+fn array_elem<'a, 'b>(x: &'a i32) -> *const &'b i32 {
+    let z = &[x; 3];
+    let y = z as *const &i32;
+    y   //~ ERROR
+}
+
+fn array_coerce<'a, 'b>(x: &'a i32) -> *const [&'b i32; 3] {
+    let z = &[x; 3];
+    let y = z as *const [&i32; 3];
+    y   //~ ERROR
+}
+
+fn nested_array<'a, 'b>(x: &'a i32) -> *const [&'b i32; 2] {
+    let z = &[[x; 2]; 3];
+    let y = z as *const [&i32; 2];
+    y   //~ ERROR
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/type-check-pointer-coercions.stderr b/src/test/ui/nll/type-check-pointer-coercions.stderr
new file mode 100644
index 00000000000..3b8d9942124
--- /dev/null
+++ b/src/test/ui/nll/type-check-pointer-coercions.stderr
@@ -0,0 +1,87 @@
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:4:5
+   |
+LL | fn shared_to_const<'a, 'b>(x: &&'a i32) -> *const &'b i32 {
+   |                    --  -- lifetime `'b` defined here
+   |                    |
+   |                    lifetime `'a` defined here
+LL |     x   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:8:5
+   |
+LL | fn unique_to_const<'a, 'b>(x: &mut &'a i32) -> *const &'b i32 {
+   |                    --  -- lifetime `'b` defined here
+   |                    |
+   |                    lifetime `'a` defined here
+LL |     x   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:13:5
+   |
+LL | fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     // Two errors because *mut is invariant
+LL |     x   //~ ERROR
+   |     ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:13:5
+   |
+LL | fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     // Two errors because *mut is invariant
+LL |     x   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:18:5
+   |
+LL | fn mut_to_const<'a, 'b>(x: *mut &'a i32) -> *const &'b i32 {
+   |                 --  -- lifetime `'b` defined here
+   |                 |
+   |                 lifetime `'a` defined here
+LL |     x   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:24:5
+   |
+LL | fn array_elem<'a, 'b>(x: &'a i32) -> *const &'b i32 {
+   |               --  -- lifetime `'b` defined here
+   |               |
+   |               lifetime `'a` defined here
+...
+LL |     y   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:30:5
+   |
+LL | fn array_coerce<'a, 'b>(x: &'a i32) -> *const [&'b i32; 3] {
+   |                 --  -- lifetime `'b` defined here
+   |                 |
+   |                 lifetime `'a` defined here
+...
+LL |     y   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-coercions.rs:36:5
+   |
+LL | fn nested_array<'a, 'b>(x: &'a i32) -> *const [&'b i32; 2] {
+   |                 --  -- lifetime `'b` defined here
+   |                 |
+   |                 lifetime `'a` defined here
+...
+LL |     y   //~ ERROR
+   |     ^ returning this value requires that `'a` must outlive `'b`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/test/ui/nll/type-check-pointer-comparisons.rs b/src/test/ui/nll/type-check-pointer-comparisons.rs
new file mode 100644
index 00000000000..3c900356fab
--- /dev/null
+++ b/src/test/ui/nll/type-check-pointer-comparisons.rs
@@ -0,0 +1,33 @@
+#![feature(nll)]
+
+// Check that we assert that pointers have a common subtype for comparisons
+
+fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) {
+    x == y;
+    //~^ ERROR lifetime may not live long enough
+    //~| ERROR lifetime may not live long enough
+}
+
+fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) {
+    x == y;
+    //~^ ERROR lifetime may not live long enough
+    //~| ERROR lifetime may not live long enough
+}
+
+fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) {
+    f == g;
+    //~^ ERROR lifetime may not live long enough
+    //~| ERROR lifetime may not live long enough
+}
+
+fn compare_hr_fn_ptr<'a>(f: fn(&'a i32), g: fn(&i32)) {
+    // Ideally this should compile with the operands swapped as well, but HIR
+    // type checking prevents it (and stops compilation) for now.
+    f == g; // OK
+}
+
+fn compare_const_fn_ptr<'a>(f: *const fn(&'a i32), g: *const fn(&i32)) {
+    f == g; // OK
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/type-check-pointer-comparisons.stderr b/src/test/ui/nll/type-check-pointer-comparisons.stderr
new file mode 100644
index 00000000000..c0a994cfb63
--- /dev/null
+++ b/src/test/ui/nll/type-check-pointer-comparisons.stderr
@@ -0,0 +1,62 @@
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:6:5
+   |
+LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     x == y;
+   |     ^ requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:6:10
+   |
+LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) {
+   |                  --  -- lifetime `'b` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+LL |     x == y;
+   |          ^ requires that `'b` must outlive `'a`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:12:5
+   |
+LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) {
+   |                --  -- lifetime `'b` defined here
+   |                |
+   |                lifetime `'a` defined here
+LL |     x == y;
+   |     ^ requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:12:10
+   |
+LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) {
+   |                --  -- lifetime `'b` defined here
+   |                |
+   |                lifetime `'a` defined here
+LL |     x == y;
+   |          ^ requires that `'b` must outlive `'a`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:18:5
+   |
+LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) {
+   |                   --  -- lifetime `'b` defined here
+   |                   |
+   |                   lifetime `'a` defined here
+LL |     f == g;
+   |     ^ requires that `'a` must outlive `'b`
+
+error: lifetime may not live long enough
+  --> $DIR/type-check-pointer-comparisons.rs:18:10
+   |
+LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) {
+   |                   --  -- lifetime `'b` defined here
+   |                   |
+   |                   lifetime `'a` defined here
+LL |     f == g;
+   |          ^ requires that `'b` must outlive `'a`
+
+error: aborting due to 6 previous errors
+