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
This commit is contained in:
commit
2cfd6444a7
18 changed files with 408 additions and 16 deletions
|
|
@ -378,6 +378,7 @@ impl_stable_hash_for!(enum mir::CastKind {
|
|||
ReifyFnPointer,
|
||||
ClosureFnPointer,
|
||||
UnsafeFnPointer,
|
||||
MutToConstPointer,
|
||||
Unsize
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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(..)
|
||||
|
|
|
|||
|
|
@ -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 { .. }
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ impl Category {
|
|||
| ExprKind::ReifyFnPointer { .. }
|
||||
| ExprKind::ClosureFnPointer { .. }
|
||||
| ExprKind::UnsafeFnPointer { .. }
|
||||
| ExprKind::MutToConstPointer { .. }
|
||||
| ExprKind::Unsize { .. }
|
||||
| ExprKind::Repeat { .. }
|
||||
| ExprKind::Borrow { .. }
|
||||
|
|
|
|||
|
|
@ -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 { .. }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ pub enum ExprKind<'tcx> {
|
|||
UnsafeFnPointer {
|
||||
source: ExprRef<'tcx>,
|
||||
},
|
||||
MutToConstPointer {
|
||||
source: ExprRef<'tcx>,
|
||||
},
|
||||
Unsize {
|
||||
source: ExprRef<'tcx>,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(..) |
|
||||
|
|
|
|||
|
|
@ -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((
|
||||
|
|
|
|||
39
src/test/ui/nll/type-check-pointer-coercions.rs
Normal file
39
src/test/ui/nll/type-check-pointer-coercions.rs
Normal file
|
|
@ -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() {}
|
||||
87
src/test/ui/nll/type-check-pointer-coercions.stderr
Normal file
87
src/test/ui/nll/type-check-pointer-coercions.stderr
Normal file
|
|
@ -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
|
||||
|
||||
33
src/test/ui/nll/type-check-pointer-comparisons.rs
Normal file
33
src/test/ui/nll/type-check-pointer-comparisons.rs
Normal file
|
|
@ -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() {}
|
||||
62
src/test/ui/nll/type-check-pointer-comparisons.stderr
Normal file
62
src/test/ui/nll/type-check-pointer-comparisons.stderr
Normal file
|
|
@ -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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue