From 6bd7e16c99f6068f0dcc469aa809e0000a23f2d9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 16:08:01 +0200 Subject: [PATCH 01/17] cast: no need to catch errors any more, force_bits should succeed --- src/librustc_mir/interpret/cast.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 980697360eb75..0f8f649cfed73 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -250,12 +250,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { RawPtr(_) => Ok(ptr.into()), Int(_) | Uint(_) => { let size = self.memory.pointer_size(); - - match self.force_bits(Scalar::Ptr(ptr), size) { - Ok(bits) => self.cast_from_int(bits, src_layout, dest_layout), - Err(_) if dest_layout.size == size => Ok(ptr.into()), - Err(e) => Err(e), - } + let bits = self.force_bits(Scalar::Ptr(ptr), size)?; + self.cast_from_int(bits, src_layout, dest_layout) } _ => bug!("invalid MIR: ptr to {:?} cast", dest_layout.ty) } From 305eadb9ed6879b9cbccaa6bf6ebc953c6f55e1a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 16:08:50 +0200 Subject: [PATCH 02/17] operator: implement binary_op strictly by first checking the type, then dispatching further; call ptr_op machine hook only for pointer types --- src/librustc_mir/const_eval.rs | 2 +- src/librustc_mir/interpret/machine.rs | 5 ++-- src/librustc_mir/interpret/operator.rs | 39 +++++++++++++------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 37d4c5b2f09ce..2eec10a2a4f38 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -397,7 +397,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ) } - fn ptr_op( + fn binary_ptr_op( _ecx: &InterpCx<'mir, 'tcx, Self>, _bin_op: mir::BinOp, _left: ImmTy<'tcx>, diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index f37c474fa4fff..08e53d1ffa51b 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -155,11 +155,10 @@ pub trait Machine<'mir, 'tcx>: Sized { def_id: DefId, ) -> InterpResult<'tcx, Cow<'tcx, Allocation>>; - /// Called for all binary operations on integer(-like) types when one operand is a pointer - /// value, and for the `Offset` operation that is inherently about pointers. + /// Called for all binary operations where the LHS has pointer type. /// /// Returns a (value, overflowed) pair if the operation succeeded - fn ptr_op( + fn binary_ptr_op( ecx: &InterpCx<'mir, 'tcx, Self>, bin_op: mir::BinOp, left: ImmTy<'tcx, Self::PointerTag>, diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index b4edee72a4d19..d56674f8bdbc3 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -290,30 +290,29 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?), }) } - _ => { - // Must be integer(-like) types. Don't forget about == on fn pointers. - assert!( - left.layout.ty.is_integral() || - left.layout.ty.is_unsafe_ptr() || left.layout.ty.is_fn_ptr(), - "Unexpected LHS type {:?} for BinOp {:?}", left.layout.ty, bin_op); + _ if left.layout.ty.is_integral() => { + // the RHS type can be different, e.g. for shifts -- but it has to be integral, too assert!( - right.layout.ty.is_integral() || - right.layout.ty.is_unsafe_ptr() || right.layout.ty.is_fn_ptr(), - "Unexpected RHS type {:?} for BinOp {:?}", right.layout.ty, bin_op); - - // Handle operations that support pointer values - if left.to_scalar_ptr()?.is_ptr() || - right.to_scalar_ptr()?.is_ptr() || - bin_op == mir::BinOp::Offset - { - return M::ptr_op(self, bin_op, left, right); - } + right.layout.ty.is_integral(), + "Unexpected types for BinOp: {:?} {:?} {:?}", + left.layout.ty, bin_op, right.layout.ty + ); - // Everything else only works with "proper" bits - let l = left.to_bits().expect("we checked is_ptr"); - let r = right.to_bits().expect("we checked is_ptr"); + let l = self.force_bits(left.to_scalar()?, left.layout.size)?; + let r = self.force_bits(right.to_scalar()?, right.layout.size)?; self.binary_int_op(bin_op, l, left.layout, r, right.layout) } + _ if left.layout.ty.is_unsafe_ptr() || left.layout.ty.is_fn_ptr() => { + // The RHS type must be the same *or an integer type* (for `Offset`) + assert!( + right.layout.ty == left.layout.ty || right.layout.ty.is_integral(), + "Unexpected types for BinOp: {:?} {:?} {:?}", + left.layout.ty, bin_op, right.layout.ty + ); + + M::binary_ptr_op(self, bin_op, left, right) + } + _ => bug!("Invalid MIR: bad LHS type for binop: {:?}", left.layout.ty), } } From 6946b1468954e6b9414a8ea8cac2549c3ad94aa2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 19:01:12 +0200 Subject: [PATCH 03/17] turn cast_immediate into its own function --- src/librustc_mir/interpret/cast.rs | 75 ++++++++++++++------------- src/librustc_mir/interpret/operand.rs | 14 +++++ 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 0f8f649cfed73..928cc0f08fd45 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -11,7 +11,7 @@ use rustc::mir::interpret::{ }; use rustc::mir::CastKind; -use super::{InterpCx, Machine, PlaceTy, OpTy, Immediate, FnVal}; +use super::{InterpCx, Machine, PlaceTy, OpTy, ImmTy, Immediate, FnVal}; impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { @@ -37,40 +37,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Misc | Pointer(PointerCast::MutToConstPointer) => { let src = self.read_immediate(src)?; - - if self.type_is_fat_ptr(src.layout.ty) { - match (*src, self.type_is_fat_ptr(dest.layout.ty)) { - // pointers to extern types - (Immediate::Scalar(_),_) | - // slices and trait objects to other slices/trait objects - (Immediate::ScalarPair(..), true) => { - // No change to immediate - self.write_immediate(*src, dest)?; - } - // slices and trait objects to thin pointers (dropping the metadata) - (Immediate::ScalarPair(data, _), false) => { - self.write_scalar(data, dest)?; - } - } - } else { - match src.layout.variants { - layout::Variants::Single { index } => { - if let Some(discr) = - src.layout.ty.discriminant_for_variant(*self.tcx, index) - { - // Cast from a univariant enum - assert!(src.layout.is_zst()); - return self.write_scalar( - Scalar::from_uint(discr.val, dest.layout.size), - dest); - } - } - layout::Variants::Multiple { .. } => {}, - } - - let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?; - self.write_scalar(dest_val, dest)?; - } + let res = self.cast_immediate(src, dest.layout)?; + self.write_immediate(res, dest)?; } Pointer(PointerCast::ReifyFnPointer) => { @@ -126,6 +94,43 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } + fn cast_immediate( + &self, + src: ImmTy<'tcx, M::PointerTag>, + dest_layout: TyLayout<'tcx>, + ) -> InterpResult<'tcx, Immediate> { + if self.type_is_fat_ptr(src.layout.ty) { + return match (*src, self.type_is_fat_ptr(dest_layout.ty)) { + // pointers to extern types + (Immediate::Scalar(_),_) | + // slices and trait objects to other slices/trait objects + (Immediate::ScalarPair(..), true) => { + // No change to immediate + Ok(*src) + } + // slices and trait objects to thin pointers (dropping the metadata) + (Immediate::ScalarPair(data, _), false) => { + Ok(data.into()) + } + }; + } else { + match src.layout.variants { + layout::Variants::Single { index } => { + if let Some(discr) = + src.layout.ty.discriminant_for_variant(*self.tcx, index) + { + // Cast from a univariant enum + assert!(src.layout.is_zst()); + return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); + } + } + layout::Variants::Multiple { .. } => {}, + } + + return Ok(self.cast_scalar(src.to_scalar()?, src.layout, dest_layout)?.into()); + } + } + fn cast_scalar( &self, val: Scalar, diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 1816171d7b127..c556fa74f128a 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -33,6 +33,20 @@ pub enum Immediate { ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef), } +impl From> for Immediate { + #[inline(always)] + fn from(val: ScalarMaybeUndef) -> Self { + Immediate::Scalar(val) + } +} + +impl From> for Immediate { + #[inline(always)] + fn from(val: Scalar) -> Self { + Immediate::Scalar(val.into()) + } +} + impl<'tcx, Tag> Immediate { #[inline] pub fn from_scalar(val: Scalar) -> Self { From f92c3a35ffb7c98b32dc1d5b893e3f8e9dd07648 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 20:05:53 +0200 Subject: [PATCH 04/17] refactor cast_immediate to dispatch on the type first, and on the value second --- src/librustc_mir/interpret/cast.rs | 110 ++++++++++++----------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 928cc0f08fd45..29b4cbb71e7f2 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -14,15 +14,6 @@ use rustc::mir::CastKind; use super::{InterpCx, Machine, PlaceTy, OpTy, ImmTy, Immediate, FnVal}; impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { - fn type_is_fat_ptr(&self, ty: Ty<'tcx>) -> bool { - match ty.sty { - ty::RawPtr(ty::TypeAndMut { ty, .. }) | - ty::Ref(_, ty, _) => !self.type_is_sized(ty), - ty::Adt(def, _) if def.is_box() => !self.type_is_sized(ty.boxed_ty()), - _ => false, - } - } - pub fn cast( &mut self, src: OpTy<'tcx, M::PointerTag>, @@ -99,68 +90,59 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { src: ImmTy<'tcx, M::PointerTag>, dest_layout: TyLayout<'tcx>, ) -> InterpResult<'tcx, Immediate> { - if self.type_is_fat_ptr(src.layout.ty) { - return match (*src, self.type_is_fat_ptr(dest_layout.ty)) { - // pointers to extern types - (Immediate::Scalar(_),_) | - // slices and trait objects to other slices/trait objects - (Immediate::ScalarPair(..), true) => { - // No change to immediate - Ok(*src) - } - // slices and trait objects to thin pointers (dropping the metadata) - (Immediate::ScalarPair(data, _), false) => { - Ok(data.into()) - } - }; - } else { - match src.layout.variants { - layout::Variants::Single { index } => { - if let Some(discr) = - src.layout.ty.discriminant_for_variant(*self.tcx, index) - { - // Cast from a univariant enum - assert!(src.layout.is_zst()); - return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); - } - } - layout::Variants::Multiple { .. } => {}, - } - - return Ok(self.cast_scalar(src.to_scalar()?, src.layout, dest_layout)?.into()); - } - } - - fn cast_scalar( - &self, - val: Scalar, - src_layout: TyLayout<'tcx>, - dest_layout: TyLayout<'tcx>, - ) -> InterpResult<'tcx, Scalar> { use rustc::ty::TyKind::*; - trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty); + trace!("Casting {:?}: {:?} to {:?}", *src, src.layout.ty, dest_layout.ty); - match src_layout.ty.sty { + match src.layout.ty.sty { // Floating point - Float(FloatTy::F32) => self.cast_from_float(val.to_f32()?, dest_layout.ty), - Float(FloatTy::F64) => self.cast_from_float(val.to_f64()?, dest_layout.ty), - // Integer(-like), including fn ptr casts and casts from enums that - // are represented as integers (this excludes univariant enums, which - // are handled in `cast` directly). - _ => { + Float(FloatTy::F32) => + return Ok(self.cast_from_float(src.to_scalar()?.to_f32()?, dest_layout.ty)?.into()), + Float(FloatTy::F64) => + return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, dest_layout.ty)?.into()), + // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that + // are represented as integers. + _ => assert!( - src_layout.ty.is_bool() || src_layout.ty.is_char() || - src_layout.ty.is_enum() || src_layout.ty.is_integral() || - src_layout.ty.is_unsafe_ptr() || src_layout.ty.is_fn_ptr() || - src_layout.ty.is_region_ptr(), - "Unexpected cast from type {:?}", src_layout.ty - ); - match val.to_bits_or_ptr(src_layout.size, self) { - Err(ptr) => self.cast_from_ptr(ptr, src_layout, dest_layout), - Ok(data) => self.cast_from_int(data, src_layout, dest_layout), + src.layout.ty.is_bool() || src.layout.ty.is_char() || + src.layout.ty.is_enum() || src.layout.ty.is_integral() || + src.layout.ty.is_unsafe_ptr() || src.layout.ty.is_fn_ptr() || + src.layout.ty.is_region_ptr(), + "Unexpected cast from type {:?}", src.layout.ty + ) + } + + // Handle cast the metadata away from a fat pointer. + if dest_layout.size != src.layout.size { + assert_eq!(dest_layout.size, self.memory.pointer_size()); + return match *src { + Immediate::ScalarPair(data, _) => Ok(data.into()), + Immediate::Scalar(..) => + bug!( + "{:?} input to a fat-to-thin cast ({:?} -> {:?})", + *src, src.layout.ty, dest_layout.ty + ), + }; + } + + // Handle cast from a univariant (ZST) enum + match src.layout.variants { + layout::Variants::Single { index } => { + if let Some(discr) = + src.layout.ty.discriminant_for_variant(*self.tcx, index) + { + assert!(src.layout.is_zst()); + return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); } } + layout::Variants::Multiple { .. } => {}, } + + // Handle all the rest. + let val = src.to_scalar()?; + Ok(match val.to_bits_or_ptr(src.layout.size, self) { + Err(ptr) => self.cast_from_ptr(ptr, src.layout, dest_layout)?, + Ok(data) => self.cast_from_int(data, src.layout, dest_layout)?, + }.into()) } fn cast_from_int( From 8debe645ea0534197aa89e7cbc1a128e8f924fdc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 20:20:55 +0200 Subject: [PATCH 05/17] use From to convert scalars to immediates --- src/librustc_mir/interpret/operand.rs | 21 ++++++++------------- src/librustc_mir/interpret/terminator.rs | 4 ++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index c556fa74f128a..b5289187ef08f 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -48,11 +48,6 @@ impl From> for Immediate { } impl<'tcx, Tag> Immediate { - #[inline] - pub fn from_scalar(val: Scalar) -> Self { - Immediate::Scalar(ScalarMaybeUndef::Scalar(val)) - } - pub fn new_slice( val: Scalar, len: u64, @@ -197,7 +192,7 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { #[inline] pub fn from_scalar(val: Scalar, layout: TyLayout<'tcx>) -> Self { - ImmTy { imm: Immediate::from_scalar(val), layout } + ImmTy { imm: val.into(), layout } } #[inline] @@ -255,7 +250,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let ptr = match self.check_mplace_access(mplace, None)? { Some(ptr) => ptr, None => return Ok(Some(ImmTy { // zero-sized type - imm: Immediate::Scalar(Scalar::zst().into()), + imm: Scalar::zst().into(), layout: mplace.layout, })), }; @@ -266,7 +261,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { .get(ptr.alloc_id)? .read_scalar(self, ptr, mplace.layout.size)?; Ok(Some(ImmTy { - imm: Immediate::Scalar(scalar), + imm: scalar.into(), layout: mplace.layout, })) } @@ -368,7 +363,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let field = field.try_into().unwrap(); let field_layout = op.layout.field(self, field)?; if field_layout.is_zst() { - let immediate = Immediate::Scalar(Scalar::zst().into()); + let immediate = Scalar::zst().into(); return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); } let offset = op.layout.fields.offset(field); @@ -378,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // extract fields from types with `ScalarPair` ABI Immediate::ScalarPair(a, b) => { let val = if offset.bytes() == 0 { a } else { b }; - Immediate::Scalar(val) + Immediate::from(val) }, Immediate::Scalar(val) => bug!("field access on non aggregate {:#?}, {:#?}", val, op.layout), @@ -415,7 +410,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Deref => self.deref_operand(base)?.into(), Subslice { .. } | ConstantIndex { .. } | Index(_) => if base.layout.is_zst() { OpTy { - op: Operand::Immediate(Immediate::Scalar(Scalar::zst().into())), + op: Operand::Immediate(Scalar::zst().into()), // the actual index doesn't matter, so we just pick a convenient one like 0 layout: base.layout.field(self, 0)?, } @@ -439,7 +434,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let layout = self.layout_of_local(frame, local, layout)?; let op = if layout.is_zst() { // Do not read from ZST, they might not be initialized - Operand::Immediate(Immediate::Scalar(Scalar::zst().into())) + Operand::Immediate(Scalar::zst().into()) } else { frame.locals[local].access()? }; @@ -567,7 +562,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Operand::Indirect(MemPlace::from_ptr(ptr, align)) }, ConstValue::Scalar(x) => - Operand::Immediate(Immediate::Scalar(tag_scalar(x).into())), + Operand::Immediate(tag_scalar(x).into()), ConstValue::Slice { data, start, end } => { // We rely on mutability being set correctly in `data` to prevent writes // where none should happen. diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 27bd0f8889634..8258192db4fdb 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -8,7 +8,7 @@ use rustc_target::spec::abi::Abi; use super::{ InterpResult, PointerArithmetic, InterpError, Scalar, - InterpCx, Machine, Immediate, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal, + InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal, }; impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -462,7 +462,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Adjust receiver argument. args[0] = OpTy::from(ImmTy { layout: this_receiver_ptr, - imm: Immediate::Scalar(receiver_place.ptr.into()) + imm: receiver_place.ptr.into() }); trace!("Patched self operand to {:#?}", args[0]); // recurse with concrete function From 8713a7bf297d4089b4ceb73e81272b3c5c479b3c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Jul 2019 20:09:18 +0200 Subject: [PATCH 06/17] fix casts from fat pointers --- src/librustc_mir/interpret/cast.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 29b4cbb71e7f2..04ef7b5f7e5ac 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -111,9 +111,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } - // Handle cast the metadata away from a fat pointer. - if dest_layout.size != src.layout.size { + // Handle casting the metadata away from a fat pointer. + if src.layout.ty.is_unsafe_ptr() && dest_layout.ty.is_unsafe_ptr() && + dest_layout.size != src.layout.size + { + assert_eq!(src.layout.size, 2*self.memory.pointer_size()); assert_eq!(dest_layout.size, self.memory.pointer_size()); + assert!(dest_layout.ty.is_unsafe_ptr()); return match *src { Immediate::ScalarPair(data, _) => Ok(data.into()), Immediate::Scalar(..) => @@ -124,6 +128,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; } + // Handle casting reference to raw ptr or raw to other raw (might be a fat ptr). + if (src.layout.ty.is_region_ptr() || src.layout.ty.is_unsafe_ptr()) && + dest_layout.ty.is_unsafe_ptr() + { + // The only possible size-unequal case was handled above. + assert_eq!(src.layout.size, dest_layout.size); + return Ok(*src); + } + // Handle cast from a univariant (ZST) enum match src.layout.variants { layout::Variants::Single { index } => { From 8890624f558322f33fc76ab17cddf37063037c46 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 00:06:47 +0200 Subject: [PATCH 07/17] improve error when CTFE does ptr-int-cast; update tests --- src/librustc_mir/const_eval.rs | 13 +++++++++++-- src/librustc_mir/interpret/machine.rs | 6 +----- src/test/ui/consts/const-eval/const_raw_ptr_ops.rs | 4 ++-- .../ui/consts/const-eval/const_raw_ptr_ops.stderr | 14 +++++++++++--- src/test/ui/consts/const-eval/issue-52442.rs | 2 +- src/test/ui/consts/const-eval/issue-52442.stderr | 8 +++----- .../ui/consts/const-eval/match-test-ptr-null.rs | 5 ++--- .../consts/const-eval/match-test-ptr-null.stderr | 12 ++++++------ 8 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 2eec10a2a4f38..b54fe5e09bef6 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -20,10 +20,10 @@ use rustc_data_structures::fx::FxHashMap; use syntax::source_map::{Span, DUMMY_SP}; use crate::interpret::{self, - PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, + PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer, RawConst, ConstValue, InterpResult, InterpErrorInfo, InterpError, GlobalId, InterpCx, StackPopCleanup, - Allocation, AllocId, MemoryKind, + Allocation, AllocId, MemoryKind, Memory, snapshot, RefTracking, intern_const_alloc_recursive, }; @@ -397,6 +397,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, ) } + fn ptr_to_int( + _mem: &Memory<'mir, 'tcx, Self>, + _ptr: Pointer, + ) -> InterpResult<'tcx, u64> { + Err( + ConstEvalError::NeedsRfc("pointer-to-integer cast".to_string()).into(), + ) + } + fn binary_ptr_op( _ecx: &InterpCx<'mir, 'tcx, Self>, _bin_op: mir::BinOp, diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 08e53d1ffa51b..5d16da79709b4 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -223,7 +223,6 @@ pub trait Machine<'mir, 'tcx>: Sized { extra: Self::FrameExtra, ) -> InterpResult<'tcx>; - #[inline(always)] fn int_to_ptr( _mem: &Memory<'mir, 'tcx, Self>, int: u64, @@ -235,11 +234,8 @@ pub trait Machine<'mir, 'tcx>: Sized { }).into()) } - #[inline(always)] fn ptr_to_int( _mem: &Memory<'mir, 'tcx, Self>, _ptr: Pointer, - ) -> InterpResult<'tcx, u64> { - err!(ReadPointerAsBytes) - } + ) -> InterpResult<'tcx, u64>; } diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs b/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs index 44266682a5c6c..9be1374f85d99 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops.rs @@ -4,8 +4,8 @@ fn main() {} // unconst and bad, will thus error in miri const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; //~ ERROR any use of this -// unconst and fine -const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; +// unconst and bad, will thus error in miri +const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; //~ ERROR any use of this // unconst and fine const Y: usize = unsafe { 42usize as *const i32 as usize + 1 }; // unconst and bad, will thus error in miri diff --git a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr index a12575b3975b6..2cba833a74896 100644 --- a/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr +++ b/src/test/ui/consts/const-eval/const_raw_ptr_ops.stderr @@ -8,13 +8,21 @@ LL | const X: bool = unsafe { &1 as *const i32 == &2 as *const i32 }; | = note: `#[deny(const_err)]` on by default +error: any use of this value will cause an error + --> $DIR/const_raw_ptr_ops.rs:8:27 + | +LL | const X2: bool = unsafe { 42 as *const i32 == 43 as *const i32 }; + | --------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | | + | "pointer arithmetic or comparison" needs an rfc before being allowed inside constants + error: any use of this value will cause an error --> $DIR/const_raw_ptr_ops.rs:12:28 | LL | const Y2: usize = unsafe { &1 as *const i32 as usize + 1 }; - | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--- + | ---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^------- | | - | "pointer arithmetic or comparison" needs an rfc before being allowed inside constants + | "pointer-to-integer cast" needs an rfc before being allowed inside constants error: any use of this value will cause an error --> $DIR/const_raw_ptr_ops.rs:16:26 @@ -32,5 +40,5 @@ LL | const Z3: i32 = unsafe { *(44 as *const i32) }; | | | a memory access tried to interpret some bytes as a pointer -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/src/test/ui/consts/const-eval/issue-52442.rs b/src/test/ui/consts/const-eval/issue-52442.rs index 2989b200b2fc4..ea24578c7dd0c 100644 --- a/src/test/ui/consts/const-eval/issue-52442.rs +++ b/src/test/ui/consts/const-eval/issue-52442.rs @@ -1,5 +1,5 @@ fn main() { [(); { &loop { break } as *const _ as usize } ]; //~^ ERROR casting pointers to integers in constants is unstable - //~| ERROR it is undefined behavior to use this value + //~| ERROR evaluation of constant value failed } diff --git a/src/test/ui/consts/const-eval/issue-52442.stderr b/src/test/ui/consts/const-eval/issue-52442.stderr index 88c94d917fe0e..5bd4979bdb33c 100644 --- a/src/test/ui/consts/const-eval/issue-52442.stderr +++ b/src/test/ui/consts/const-eval/issue-52442.stderr @@ -7,13 +7,11 @@ LL | [(); { &loop { break } as *const _ as usize } ]; = note: for more information, see https://github.com/rust-lang/rust/issues/51910 = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable -error[E0080]: it is undefined behavior to use this value - --> $DIR/issue-52442.rs:2:11 +error[E0080]: evaluation of constant value failed + --> $DIR/issue-52442.rs:2:13 | LL | [(); { &loop { break } as *const _ as usize } ]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants error: aborting due to 2 previous errors diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.rs b/src/test/ui/consts/const-eval/match-test-ptr-null.rs index 50757afaf5651..9c930221e73e3 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.rs +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.rs @@ -1,3 +1,4 @@ + fn main() { // Make sure match uses the usual pointer comparison code path -- i.e., it should complain // that pointer comparison is disallowed, not that parts of a pointer are accessed as raw @@ -5,11 +6,9 @@ fn main() { let _: [u8; 0] = [4; { match &1 as *const i32 as usize { //~^ ERROR casting pointers to integers in constants - //~| NOTE for more information, see //~| ERROR constant contains unimplemented expression type - 0 => 42, //~ ERROR constant contains unimplemented expression type - //~^ NOTE "pointer arithmetic or comparison" needs an rfc before being allowed //~| ERROR evaluation of constant value failed + 0 => 42, //~ ERROR constant contains unimplemented expression type n => n, } }]; diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr index d8a3bac5ce689..9bb561f31ebb0 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr @@ -1,5 +1,5 @@ error[E0658]: casting pointers to integers in constants is unstable - --> $DIR/match-test-ptr-null.rs:6:15 + --> $DIR/match-test-ptr-null.rs:7:15 | LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,22 +8,22 @@ LL | match &1 as *const i32 as usize { = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable error[E0019]: constant contains unimplemented expression type - --> $DIR/match-test-ptr-null.rs:6:15 + --> $DIR/match-test-ptr-null.rs:7:15 | LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0019]: constant contains unimplemented expression type - --> $DIR/match-test-ptr-null.rs:10:13 + --> $DIR/match-test-ptr-null.rs:11:13 | LL | 0 => 42, | ^ error[E0080]: evaluation of constant value failed - --> $DIR/match-test-ptr-null.rs:10:13 + --> $DIR/match-test-ptr-null.rs:7:15 | -LL | 0 => 42, - | ^ "pointer arithmetic or comparison" needs an rfc before being allowed inside constants +LL | match &1 as *const i32 as usize { + | ^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants error: aborting due to 4 previous errors From 3cd4079a9a4a8aa4c8bf566e8c86c528da2810bc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 00:22:05 +0200 Subject: [PATCH 08/17] get rid of some remaining type-based dispatching in cast code --- src/librustc_mir/interpret/cast.rs | 70 +++++++++++------------------- 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 04ef7b5f7e5ac..1309006026e21 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -7,7 +7,7 @@ use syntax::symbol::sym; use rustc_apfloat::ieee::{Single, Double}; use rustc_apfloat::{Float, FloatConvert}; use rustc::mir::interpret::{ - Scalar, InterpResult, Pointer, PointerArithmetic, InterpError, + Scalar, InterpResult, PointerArithmetic, InterpError, }; use rustc::mir::CastKind; @@ -111,6 +111,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } + // Handle cast from a univariant (ZST) enum + match src.layout.variants { + layout::Variants::Single { index } => { + if let Some(discr) = + src.layout.ty.discriminant_for_variant(*self.tcx, index) + { + assert!(src.layout.is_zst()); + return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); + } + } + layout::Variants::Multiple { .. } => {}, + } + // Handle casting the metadata away from a fat pointer. if src.layout.ty.is_unsafe_ptr() && dest_layout.ty.is_unsafe_ptr() && dest_layout.size != src.layout.size @@ -118,8 +131,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert_eq!(src.layout.size, 2*self.memory.pointer_size()); assert_eq!(dest_layout.size, self.memory.pointer_size()); assert!(dest_layout.ty.is_unsafe_ptr()); - return match *src { - Immediate::ScalarPair(data, _) => Ok(data.into()), + match *src { + Immediate::ScalarPair(data, _) => + return Ok(data.into()), Immediate::Scalar(..) => bug!( "{:?} input to a fat-to-thin cast ({:?} -> {:?})", @@ -128,8 +142,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { }; } - // Handle casting reference to raw ptr or raw to other raw (might be a fat ptr). - if (src.layout.ty.is_region_ptr() || src.layout.ty.is_unsafe_ptr()) && + // Handle casting any ptr to raw ptr (might be a fat ptr). + if (src.layout.ty.is_region_ptr() || src.layout.ty.is_unsafe_ptr() || src.layout.ty.is_fn_ptr()) && dest_layout.ty.is_unsafe_ptr() { // The only possible size-unequal case was handled above. @@ -137,25 +151,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(*src); } - // Handle cast from a univariant (ZST) enum - match src.layout.variants { - layout::Variants::Single { index } => { - if let Some(discr) = - src.layout.ty.discriminant_for_variant(*self.tcx, index) - { - assert!(src.layout.is_zst()); - return Ok(Scalar::from_uint(discr.val, dest_layout.size).into()); - } - } - layout::Variants::Multiple { .. } => {}, - } - - // Handle all the rest. - let val = src.to_scalar()?; - Ok(match val.to_bits_or_ptr(src.layout.size, self) { - Err(ptr) => self.cast_from_ptr(ptr, src.layout, dest_layout)?, - Ok(data) => self.cast_from_int(data, src.layout, dest_layout)?, - }.into()) + // For all remaining casts, we either + // (a) cast a raw ptr to usize, or + // (b) cast from an integer-like (including bool, char, enums). + // In both cases we want the bits. + let bits = self.force_bits(src.to_scalar()?, src.layout.size)?; + Ok(self.cast_from_int(bits, src.layout, dest_layout)?.into()) } fn cast_from_int( @@ -236,27 +237,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - fn cast_from_ptr( - &self, - ptr: Pointer, - src_layout: TyLayout<'tcx>, - dest_layout: TyLayout<'tcx>, - ) -> InterpResult<'tcx, Scalar> { - use rustc::ty::TyKind::*; - - match dest_layout.ty.sty { - // Casting to a reference or fn pointer is not permitted by rustc, - // no need to support it here. - RawPtr(_) => Ok(ptr.into()), - Int(_) | Uint(_) => { - let size = self.memory.pointer_size(); - let bits = self.force_bits(Scalar::Ptr(ptr), size)?; - self.cast_from_int(bits, src_layout, dest_layout) - } - _ => bug!("invalid MIR: ptr to {:?} cast", dest_layout.ty) - } - } - fn unsize_into_ptr( &mut self, src: OpTy<'tcx, M::PointerTag>, From 2bebfd3416257e1785b48619d2d37bfad800afbd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 01:02:41 +0200 Subject: [PATCH 09/17] add is_any_ptr type test; this also helps pacify tidy --- src/librustc/ty/sty.rs | 6 ++++++ src/librustc_mir/interpret/cast.rs | 6 ++---- src/librustc_mir/interpret/operator.rs | 2 +- src/test/ui/consts/const-eval/match-test-ptr-null.rs | 1 - 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 5d17080a9b2bc..47b666bd02c31 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -1864,6 +1864,12 @@ impl<'tcx> TyS<'tcx> { } } + /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). + #[inline] + pub fn is_any_ptr(&self) -> bool { + self.is_region_ptr() || self.is_unsafe_ptr() || self.is_fn_ptr() + } + /// Returns `true` if this type is an `Arc`. #[inline] pub fn is_arc(&self) -> bool { diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 1309006026e21..16fb083b7fc2f 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -105,8 +105,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { assert!( src.layout.ty.is_bool() || src.layout.ty.is_char() || src.layout.ty.is_enum() || src.layout.ty.is_integral() || - src.layout.ty.is_unsafe_ptr() || src.layout.ty.is_fn_ptr() || - src.layout.ty.is_region_ptr(), + src.layout.ty.is_any_ptr(), "Unexpected cast from type {:?}", src.layout.ty ) } @@ -143,8 +142,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } // Handle casting any ptr to raw ptr (might be a fat ptr). - if (src.layout.ty.is_region_ptr() || src.layout.ty.is_unsafe_ptr() || src.layout.ty.is_fn_ptr()) && - dest_layout.ty.is_unsafe_ptr() + if src.layout.ty.is_any_ptr() && dest_layout.ty.is_unsafe_ptr() { // The only possible size-unequal case was handled above. assert_eq!(src.layout.size, dest_layout.size); diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index d56674f8bdbc3..f67ec17238729 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -302,7 +302,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let r = self.force_bits(right.to_scalar()?, right.layout.size)?; self.binary_int_op(bin_op, l, left.layout, r, right.layout) } - _ if left.layout.ty.is_unsafe_ptr() || left.layout.ty.is_fn_ptr() => { + _ if left.layout.ty.is_any_ptr() => { // The RHS type must be the same *or an integer type* (for `Offset`) assert!( right.layout.ty == left.layout.ty || right.layout.ty.is_integral(), diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.rs b/src/test/ui/consts/const-eval/match-test-ptr-null.rs index 9c930221e73e3..5b89b0262aca5 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.rs +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.rs @@ -1,4 +1,3 @@ - fn main() { // Make sure match uses the usual pointer comparison code path -- i.e., it should complain // that pointer comparison is disallowed, not that parts of a pointer are accessed as raw From 1f57eb0958defc740be6dd6ade16d566dc8ffa18 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 09:08:00 +0200 Subject: [PATCH 10/17] trailing full stops Co-Authored-By: Mazdak Farrokhzad --- src/librustc_mir/interpret/cast.rs | 2 +- src/librustc_mir/interpret/operator.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 16fb083b7fc2f..ac146eaaf25a6 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -110,7 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } - // Handle cast from a univariant (ZST) enum + // Handle cast from a univariant (ZST) enum. match src.layout.variants { layout::Variants::Single { index } => { if let Some(discr) = diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index f67ec17238729..a893f8012db99 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -303,7 +303,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.binary_int_op(bin_op, l, left.layout, r, right.layout) } _ if left.layout.ty.is_any_ptr() => { - // The RHS type must be the same *or an integer type* (for `Offset`) + // The RHS type must be the same *or an integer type* (for `Offset`). assert!( right.layout.ty == left.layout.ty || right.layout.ty.is_integral(), "Unexpected types for BinOp: {:?} {:?} {:?}", From 0ba2e80e765c9b2a9b2e54adde97408bdb445cf8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 09:11:53 +0200 Subject: [PATCH 11/17] bless all the things --- src/test/ui/consts/const-eval/match-test-ptr-null.stderr | 8 ++++---- src/test/ui/issues/issue-52023-array-size-pointer-cast.rs | 2 +- .../ui/issues/issue-52023-array-size-pointer-cast.stderr | 6 ++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr index 9bb561f31ebb0..3d34ac4266270 100644 --- a/src/test/ui/consts/const-eval/match-test-ptr-null.stderr +++ b/src/test/ui/consts/const-eval/match-test-ptr-null.stderr @@ -1,5 +1,5 @@ error[E0658]: casting pointers to integers in constants is unstable - --> $DIR/match-test-ptr-null.rs:7:15 + --> $DIR/match-test-ptr-null.rs:6:15 | LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,19 +8,19 @@ LL | match &1 as *const i32 as usize { = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable error[E0019]: constant contains unimplemented expression type - --> $DIR/match-test-ptr-null.rs:7:15 + --> $DIR/match-test-ptr-null.rs:6:15 | LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0019]: constant contains unimplemented expression type - --> $DIR/match-test-ptr-null.rs:11:13 + --> $DIR/match-test-ptr-null.rs:10:13 | LL | 0 => 42, | ^ error[E0080]: evaluation of constant value failed - --> $DIR/match-test-ptr-null.rs:7:15 + --> $DIR/match-test-ptr-null.rs:6:15 | LL | match &1 as *const i32 as usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants diff --git a/src/test/ui/issues/issue-52023-array-size-pointer-cast.rs b/src/test/ui/issues/issue-52023-array-size-pointer-cast.rs index 63f1128f10642..d12b483ba4473 100644 --- a/src/test/ui/issues/issue-52023-array-size-pointer-cast.rs +++ b/src/test/ui/issues/issue-52023-array-size-pointer-cast.rs @@ -1,4 +1,4 @@ fn main() { let _ = [0; (&0 as *const i32) as usize]; //~ ERROR casting pointers to integers in constants - //~^ ERROR it is undefined behavior to use this value + //~^ ERROR evaluation of constant value failed } diff --git a/src/test/ui/issues/issue-52023-array-size-pointer-cast.stderr b/src/test/ui/issues/issue-52023-array-size-pointer-cast.stderr index 2db6f42405c17..68ee53754161c 100644 --- a/src/test/ui/issues/issue-52023-array-size-pointer-cast.stderr +++ b/src/test/ui/issues/issue-52023-array-size-pointer-cast.stderr @@ -7,13 +7,11 @@ LL | let _ = [0; (&0 as *const i32) as usize]; = note: for more information, see https://github.com/rust-lang/rust/issues/51910 = help: add `#![feature(const_raw_ptr_to_usize_cast)]` to the crate attributes to enable -error[E0080]: it is undefined behavior to use this value +error[E0080]: evaluation of constant value failed --> $DIR/issue-52023-array-size-pointer-cast.rs:2:17 | LL | let _ = [0; (&0 as *const i32) as usize]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes - | - = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ "pointer-to-integer cast" needs an rfc before being allowed inside constants error: aborting due to 2 previous errors From d7b9bbf8005943e3806aa74937b4d916073b2ad8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Jul 2019 10:47:11 +0200 Subject: [PATCH 12/17] const_prop no longer does ptr-to-int casts --- src/test/mir-opt/const_prop/reify_fn_ptr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/mir-opt/const_prop/reify_fn_ptr.rs b/src/test/mir-opt/const_prop/reify_fn_ptr.rs index 809eb19ade899..7e36b2a6b1b39 100644 --- a/src/test/mir-opt/const_prop/reify_fn_ptr.rs +++ b/src/test/mir-opt/const_prop/reify_fn_ptr.rs @@ -19,7 +19,7 @@ fn main() { // _3 = const Scalar(AllocId(1).0x0) : fn(); // _2 = move _3 as usize (Misc); // ... -// _1 = const Scalar(AllocId(1).0x0) : *const fn(); +// _1 = move _2 as *const fn() (Misc); // ... // } // END rustc.main.ConstProp.after.mir From 08a8de8181e606f009007475c743d4572758e160 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 25 Jul 2019 14:29:05 +0200 Subject: [PATCH 13/17] Add keywords item into the sidebar --- src/librustdoc/html/render.rs | 3 ++- src/test/rustdoc/keyword.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 3cd520fd4b50b..d7b8a7f1b4a0a 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -5007,7 +5007,8 @@ fn sidebar_module(fmt: &mut fmt::Formatter<'_>, _it: &clean::Item, ItemType::Enum, ItemType::Constant, ItemType::Static, ItemType::Trait, ItemType::Function, ItemType::Typedef, ItemType::Union, ItemType::Impl, ItemType::TyMethod, ItemType::Method, ItemType::StructField, ItemType::Variant, - ItemType::AssocType, ItemType::AssocConst, ItemType::ForeignType] { + ItemType::AssocType, ItemType::AssocConst, ItemType::ForeignType, + ItemType::Keyword] { if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) { let (short, name) = item_ty_to_strs(&myty); sidebar.push_str(&format!("
  • {name}
  • ", diff --git a/src/test/rustdoc/keyword.rs b/src/test/rustdoc/keyword.rs index c721c024468dd..db5d115c6da74 100644 --- a/src/test/rustdoc/keyword.rs +++ b/src/test/rustdoc/keyword.rs @@ -4,6 +4,7 @@ // @has foo/index.html '//h2[@id="keywords"]' 'Keywords' // @has foo/index.html '//a[@href="keyword.match.html"]' 'match' +// @has foo/index.html '//div[@class="block items"]//a/@href' '#keywords' // @has foo/keyword.match.html '//a[@class="keyword"]' 'match' // @has foo/keyword.match.html '//span[@class="in-band"]' 'Keyword match' // @has foo/keyword.match.html '//section[@id="main"]//div[@class="docblock"]//p' 'this is a test!' From f3a3290ba36b66ee091f6442fe7f0a22dc57941b Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 30 Jul 2019 04:22:09 +0200 Subject: [PATCH 14/17] Account for maybe_whole_expr in range patterns. --- src/libsyntax/parse/parser.rs | 1 + src/libsyntax/parse/token.rs | 13 +++ .../issue-63115-range-pat-interpolated.rs | 16 ++++ src/test/ui/parser/recover-range-pats.rs | 28 +++++++ src/test/ui/parser/recover-range-pats.stderr | 83 ++++++++++++++++++- 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/parser/issue-63115-range-pat-interpolated.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6cb965bf817d1..2fa6d20430bf1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3741,6 +3741,7 @@ impl<'a> Parser<'a> { self.token.is_path_start() // e.g. `MY_CONST`; || self.token == token::Dot // e.g. `.5` for recovery; || self.token.can_begin_literal_or_bool() // e.g. `42`. + || self.token.is_whole_expr() } // Helper function to decide whether to parse as ident binding diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 472e4b474d627..d6d13c19f7183 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -476,6 +476,19 @@ impl Token { false } + /// Would `maybe_whole_expr` in `parser.rs` return `Ok(..)`? + /// That is, is this a pre-parsed expression dropped into the token stream + /// (which happens while parsing the result ofmacro expansion)? + crate fn is_whole_expr(&self) -> bool { + if let Interpolated(ref nt) = self.kind { + if let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt { + return true; + } + } + + false + } + /// Returns `true` if the token is either the `mut` or `const` keyword. crate fn is_mutability(&self) -> bool { self.is_keyword(kw::Mut) || diff --git a/src/test/ui/parser/issue-63115-range-pat-interpolated.rs b/src/test/ui/parser/issue-63115-range-pat-interpolated.rs new file mode 100644 index 0000000000000..a7d10ca9320a6 --- /dev/null +++ b/src/test/ui/parser/issue-63115-range-pat-interpolated.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(exclusive_range_pattern)] + +#![allow(ellipsis_inclusive_range_patterns)] + +fn main() { + macro_rules! mac_expr { + ($e:expr) => { + if let 2...$e = 3 {} + if let 2..=$e = 3 {} + if let 2..$e = 3 {} + } + } + mac_expr!(4); +} diff --git a/src/test/ui/parser/recover-range-pats.rs b/src/test/ui/parser/recover-range-pats.rs index c66652ff4fa01..260e108315973 100644 --- a/src/test/ui/parser/recover-range-pats.rs +++ b/src/test/ui/parser/recover-range-pats.rs @@ -121,3 +121,31 @@ fn inclusive2_to() { //~| ERROR `...` range patterns are deprecated //~| ERROR mismatched types } + +fn with_macro_expr_var() { + macro_rules! mac2 { + ($e1:expr, $e2:expr) => { + let $e1..$e2; + let $e1...$e2; + //~^ ERROR `...` range patterns are deprecated + let $e1..=$e2; + } + } + + mac2!(0, 1); + + macro_rules! mac { + ($e:expr) => { + let ..$e; //~ ERROR `..X` range patterns are not supported + let ...$e; //~ ERROR `...X` range patterns are not supported + //~^ ERROR `...` range patterns are deprecated + let ..=$e; //~ ERROR `..=X` range patterns are not supported + let $e..; //~ ERROR `X..` range patterns are not supported + let $e...; //~ ERROR `X...` range patterns are not supported + //~^ ERROR `...` range patterns are deprecated + let $e..=; //~ ERROR `X..=` range patterns are not supported + } + } + + mac!(0); +} diff --git a/src/test/ui/parser/recover-range-pats.stderr b/src/test/ui/parser/recover-range-pats.stderr index c50d5e6eb6153..89ec059cb8234 100644 --- a/src/test/ui/parser/recover-range-pats.stderr +++ b/src/test/ui/parser/recover-range-pats.stderr @@ -214,6 +214,60 @@ error: `...X` range patterns are not supported LL | if let ....3 = 0 {} | ^^^^^ help: try using the minimum value for the type: `MIN...0.3` +error: `..X` range patterns are not supported + --> $DIR/recover-range-pats.rs:139:17 + | +LL | let ..$e; + | ^^ help: try using the minimum value for the type: `MIN..0` +... +LL | mac!(0); + | -------- in this macro invocation + +error: `...X` range patterns are not supported + --> $DIR/recover-range-pats.rs:140:17 + | +LL | let ...$e; + | ^^^ help: try using the minimum value for the type: `MIN...0` +... +LL | mac!(0); + | -------- in this macro invocation + +error: `..=X` range patterns are not supported + --> $DIR/recover-range-pats.rs:142:17 + | +LL | let ..=$e; + | ^^^ help: try using the minimum value for the type: `MIN..=0` +... +LL | mac!(0); + | -------- in this macro invocation + +error: `X..` range patterns are not supported + --> $DIR/recover-range-pats.rs:143:19 + | +LL | let $e..; + | ^^ help: try using the maximum value for the type: `0..MAX` +... +LL | mac!(0); + | -------- in this macro invocation + +error: `X...` range patterns are not supported + --> $DIR/recover-range-pats.rs:144:19 + | +LL | let $e...; + | ^^^ help: try using the maximum value for the type: `0...MAX` +... +LL | mac!(0); + | -------- in this macro invocation + +error: `X..=` range patterns are not supported + --> $DIR/recover-range-pats.rs:146:19 + | +LL | let $e..=; + | ^^^ help: try using the maximum value for the type: `0..=MAX` +... +LL | mac!(0); + | -------- in this macro invocation + error: `...` range patterns are deprecated --> $DIR/recover-range-pats.rs:41:13 | @@ -316,6 +370,33 @@ error: `...` range patterns are deprecated LL | if let ....3 = 0 {} | ^^^ help: use `..=` for an inclusive range +error: `...` range patterns are deprecated + --> $DIR/recover-range-pats.rs:129:20 + | +LL | let $e1...$e2; + | ^^^ help: use `..=` for an inclusive range +... +LL | mac2!(0, 1); + | ------------ in this macro invocation + +error: `...` range patterns are deprecated + --> $DIR/recover-range-pats.rs:140:17 + | +LL | let ...$e; + | ^^^ help: use `..=` for an inclusive range +... +LL | mac!(0); + | -------- in this macro invocation + +error: `...` range patterns are deprecated + --> $DIR/recover-range-pats.rs:144:19 + | +LL | let $e...; + | ^^^ help: use `..=` for an inclusive range +... +LL | mac!(0); + | -------- in this macro invocation + error[E0029]: only char and numeric types are allowed in range patterns --> $DIR/recover-range-pats.rs:19:12 | @@ -532,7 +613,7 @@ LL | if let ....3 = 0 {} = note: expected type `{integer}` found type `{float}` -error: aborting due to 76 previous errors +error: aborting due to 85 previous errors Some errors have detailed explanations: E0029, E0308. For more information about an error, try `rustc --explain E0029`. From 87e73c1f8288823e689ca254cdee9ab6c1723154 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 31 Jul 2019 01:51:20 +0100 Subject: [PATCH 15/17] Remove redundant method with const variable resolution --- src/librustc/infer/canonical/canonicalizer.rs | 2 +- src/librustc/infer/mod.rs | 27 +++---------------- src/librustc/ty/relate.rs | 12 ++++----- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/librustc/infer/canonical/canonicalizer.rs b/src/librustc/infer/canonical/canonicalizer.rs index 3d57a89493e1e..db724875b8aa3 100644 --- a/src/librustc/infer/canonical/canonicalizer.rs +++ b/src/librustc/infer/canonical/canonicalizer.rs @@ -693,7 +693,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { const_var: &'tcx ty::Const<'tcx> ) -> &'tcx ty::Const<'tcx> { let infcx = self.infcx.expect("encountered const-var without infcx"); - let bound_to = infcx.resolve_const_var(const_var); + let bound_to = infcx.shallow_resolve(const_var); if bound_to != const_var { self.fold_const(bound_to) } else { diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 663acd67dcd83..e1d77a97c1160 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1351,23 +1351,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - pub fn resolve_const_var( - &self, - ct: &'tcx ty::Const<'tcx> - ) -> &'tcx ty::Const<'tcx> { - if let ty::Const { val: ConstValue::Infer(InferConst::Var(v)), .. } = ct { - self.const_unification_table - .borrow_mut() - .probe_value(*v) - .val - .known() - .map(|c| self.resolve_const_var(c)) - .unwrap_or(ct) - } else { - ct - } - } - pub fn fully_resolve>(&self, value: &T) -> FixupResult<'tcx, T> { /*! * Attempts to resolve all type/region/const variables in @@ -1586,7 +1569,7 @@ impl<'a, 'tcx> ShallowResolver<'a, 'tcx> { // it can be resolved to an int/float variable, which // can then be recursively resolved, hence the // recursion. Note though that we prevent type - // variables from unifyxing to other type variables + // variables from unifying to other type variables // directly (though they may be embedded // structurally), and we prevent cycles in any case, // so this recursion should always be of very limited @@ -1626,17 +1609,15 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { } fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { - match ct { - ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } => { + if let ty::Const { val: ConstValue::Infer(InferConst::Var(vid)), .. } = ct { self.infcx.const_unification_table .borrow_mut() .probe_value(*vid) .val .known() - .map(|c| self.fold_const(c)) .unwrap_or(ct) - } - _ => ct, + } else { + ct } } } diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index a6bfc2dee613b..ca54f63b83afe 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -594,13 +594,11 @@ pub fn super_relate_consts>( ty: a.ty, })) } - (ConstValue::ByRef { .. }, _) => { - bug!( - "non-Scalar ConstValue encountered in super_relate_consts {:?} {:?}", - a, - b, - ); - } + + // FIXME(const_generics): we should either handle `Scalar::Ptr` or add a comment + // saying that we're not handling it intentionally. + + // FIXME(const_generics): handle `ConstValue::ByRef` and `ConstValue::Slice`. // FIXME(const_generics): this is wrong, as it is a projection (ConstValue::Unevaluated(a_def_id, a_substs), From 3f461f5ec674ba42c12cb000f307c98464ed5ee7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Jul 2019 19:55:01 +0300 Subject: [PATCH 16/17] cleanup StringReader fields --- src/libsyntax/parse/lexer/mod.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 3cd5464f35710..263eb1ac7a480 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -29,16 +29,15 @@ pub struct UnmatchedBrace { } pub struct StringReader<'a> { - crate sess: &'a ParseSess, - /// The absolute offset within the source_map of the current character - crate pos: BytePos, - /// The current character (which has been read from self.pos) - crate source_file: Lrc, + sess: &'a ParseSess, + /// Initial position, read-only. + start_pos: BytePos, + /// The absolute offset within the source_map of the current character. + pos: BytePos, /// Stop reading src at this index. - crate end_src_index: usize, + end_src_index: usize, fatal_errs: Vec>, - // cache a direct reference to the source text, so that we don't have to - // retrieve it via `self.source_file.src.as_ref().unwrap()` all the time. + /// Source text to tokenize. src: Lrc, override_span: Option, } @@ -56,8 +55,8 @@ impl<'a> StringReader<'a> { StringReader { sess, + start_pos: source_file.start_pos, pos: source_file.start_pos, - source_file, end_src_index: src.len(), src, fatal_errs: Vec::new(), @@ -108,12 +107,12 @@ impl<'a> StringReader<'a> { let text: &str = &self.src[start_src_index..self.end_src_index]; if text.is_empty() { - let span = self.mk_sp(self.source_file.end_pos, self.source_file.end_pos); + let span = self.mk_sp(self.pos, self.pos); return Ok(Token::new(token::Eof, span)); } { - let is_beginning_of_file = self.pos == self.source_file.start_pos; + let is_beginning_of_file = self.pos == self.start_pos; if is_beginning_of_file { if let Some(shebang_len) = rustc_lexer::strip_shebang(text) { let start = self.pos; @@ -533,7 +532,7 @@ impl<'a> StringReader<'a> { #[inline] fn src_index(&self, pos: BytePos) -> usize { - (pos - self.source_file.start_pos).to_usize() + (pos - self.start_pos).to_usize() } /// Slice of the source text from `start` up to but excluding `self.pos`, From 6551285ccaf1562eb73ca1013730165b4d415d8e Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 31 Jul 2019 21:25:11 +0200 Subject: [PATCH 17/17] Address review comments. --- src/libsyntax/parse/parser.rs | 8 ++------ src/libsyntax/parse/token.rs | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2fa6d20430bf1..ae4925ec34083 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -143,6 +143,7 @@ macro_rules! maybe_whole_expr { $p.token.span, ExprKind::Block(block, None), ThinVec::new() )); } + // N.B: `NtIdent(ident)` is normalized to `Ident` in `fn bump`. _ => {}, }; } @@ -2781,12 +2782,7 @@ impl<'a> Parser<'a> { // can't continue an expression after an ident token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), token::Literal(..) | token::Pound => true, - token::Interpolated(ref nt) => match **nt { - token::NtIdent(..) | token::NtExpr(..) | - token::NtBlock(..) | token::NtPath(..) => true, - _ => false, - }, - _ => false + _ => t.is_whole_expr(), }; let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr); if cannot_continue_expr { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index d6d13c19f7183..73adb5c947c0b 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -478,10 +478,10 @@ impl Token { /// Would `maybe_whole_expr` in `parser.rs` return `Ok(..)`? /// That is, is this a pre-parsed expression dropped into the token stream - /// (which happens while parsing the result ofmacro expansion)? + /// (which happens while parsing the result of macro expansion)? crate fn is_whole_expr(&self) -> bool { if let Interpolated(ref nt) = self.kind { - if let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtBlock(_) = **nt { + if let NtExpr(_) | NtLiteral(_) | NtPath(_) | NtIdent(..) | NtBlock(_) = **nt { return true; } }