From 7d01aa8a4815207d83664353895ac168b7564e4b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 23 Feb 2019 14:25:03 +0000 Subject: [PATCH 1/2] Type check coercions to pointer types --- src/librustc/ich/impls_mir.rs | 1 + src/librustc/mir/mod.rs | 3 + src/librustc/ty/context.rs | 20 ++-- src/librustc_codegen_ssa/mir/rvalue.rs | 6 +- .../borrow_check/nll/type_check/mod.rs | 113 +++++++++++++++++- src/librustc_mir/build/expr/as_place.rs | 1 + src/librustc_mir/build/expr/as_rvalue.rs | 6 +- src/librustc_mir/build/expr/category.rs | 1 + src/librustc_mir/build/expr/into.rs | 1 + src/librustc_mir/hair/cx/expr.rs | 2 +- src/librustc_mir/hair/mod.rs | 3 + src/librustc_mir/interpret/cast.rs | 2 +- src/librustc_mir/transform/qualify_consts.rs | 1 + .../transform/qualify_min_const_fn.rs | 3 + .../ui/nll/type-check-pointer-coercions.rs | 39 ++++++ .../nll/type-check-pointer-coercions.stderr | 87 ++++++++++++++ 16 files changed, 274 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/nll/type-check-pointer-coercions.rs create mode 100644 src/test/ui/nll/type-check-pointer-coercions.stderr diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 51fc78ffc8669..69a1e2d8a1ef4 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -373,6 +373,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 3513d652b5346..5d596f81099f7 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -2220,6 +2220,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 6bb3222512565..02a7be121cca7 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 @@ -1819,12 +1818,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}; @@ -2067,7 +2065,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) } @@ -2085,8 +2083,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) } @@ -2993,6 +2991,12 @@ impl InternIteratorElement for Result { } } +// We are comparing types with different invariant lifetimes, so `ptr::eq` +// won't work for us. +fn ptr_eq(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 25a7754d118d7..c8b7216e29778 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 49f90eb90aaf0..6b948e40bfbb8 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -1984,7 +1984,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 + ) + } + } + } + } } } diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs index ed444191226a1..7f30d9f92514d 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 88dbd93939e54..6fa7393ee53e5 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -142,8 +142,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block.and(Rvalue::Use(Operand::Move(Place::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)) } @@ -163,6 +161,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 ca7d435e62229..c8c30ac3ce4d0 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 05231bc7b3f16..ed27fc38c2a06 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 10d04a80d7341..a08be74afbe83 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -88,7 +88,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 e615b009cf370..966549b42fa95 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 ce62d79e585a8..73c73cc23dcf0 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 285c674643f2e..fb4c64bd5f55f 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -1091,6 +1091,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 34f850fc4aad2..d0d6275499303 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 0000000000000..b6a25eddb866d --- /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 0000000000000..3b8d99421242e --- /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 + From 19a54e80934c802109ae7175cc824c22c672caa6 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 24 Feb 2019 12:54:45 +0000 Subject: [PATCH 2/2] Type check pointer comparisons --- .../borrow_check/nll/type_check/mod.rs | 40 +++++++++++- .../ui/nll/type-check-pointer-comparisons.rs | 33 ++++++++++ .../nll/type-check-pointer-comparisons.stderr | 62 +++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/nll/type-check-pointer-comparisons.rs create mode 100644 src/test/ui/nll/type-check-pointer-comparisons.stderr 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 6b948e40bfbb8..ef0f11ecfb9b4 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}; @@ -2103,7 +2104,44 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.add_reborrow_constraint(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/test/ui/nll/type-check-pointer-comparisons.rs b/src/test/ui/nll/type-check-pointer-comparisons.rs new file mode 100644 index 0000000000000..3c900356fab3b --- /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 0000000000000..c0a994cfb6381 --- /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 +