From 017877cbbe7903beb483f13678aedae1e40b6ab0 Mon Sep 17 00:00:00 2001 From: Adam Perry Date: Thu, 24 Oct 2019 08:03:57 -0700 Subject: [PATCH] Implementation of const caller_location. --- src/librustc_mir/const_eval.rs | 3 +- src/librustc_mir/interpret/intrinsics.rs | 7 +++ .../interpret/intrinsics/caller_location.rs | 54 +++++++++++++++++++ src/librustc_mir/interpret/machine.rs | 2 + src/librustc_mir/interpret/terminator.rs | 2 +- src/librustc_mir/transform/const_prop.rs | 1 + .../const-eval/const_caller_location.rs | 23 ++++++++ 7 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 src/librustc_mir/interpret/intrinsics/caller_location.rs create mode 100644 src/test/ui/consts/const-eval/const_caller_location.rs diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index eeaa3c6792d..eed26c32b7a 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -375,11 +375,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx>], dest: PlaceTy<'tcx>, ) -> InterpResult<'tcx> { - if ecx.emulate_intrinsic(instance, args, dest)? { + if ecx.emulate_intrinsic(span, instance, args, dest)? { return Ok(()); } // An intrinsic that we do not support diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 20cb2926d66..12e080869c7 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -3,6 +3,7 @@ //! and miri. use syntax::symbol::Symbol; +use syntax_pos::Span; use rustc::ty; use rustc::ty::layout::{LayoutOf, Primitive, Size}; use rustc::ty::subst::SubstsRef; @@ -15,6 +16,7 @@ use super::{ Machine, PlaceTy, OpTy, InterpCx, }; +mod caller_location; mod type_name; fn numeric_intrinsic<'tcx, Tag>( @@ -86,6 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Returns `true` if emulation happened. pub fn emulate_intrinsic( &mut self, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, M::PointerTag>], dest: PlaceTy<'tcx, M::PointerTag>, @@ -94,6 +97,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..]; match intrinsic_name { + "caller_location" => { + self.write_caller_location(span, dest)?; + } + "min_align_of" | "pref_align_of" | "needs_drop" | diff --git a/src/librustc_mir/interpret/intrinsics/caller_location.rs b/src/librustc_mir/interpret/intrinsics/caller_location.rs new file mode 100644 index 00000000000..5addb13b61c --- /dev/null +++ b/src/librustc_mir/interpret/intrinsics/caller_location.rs @@ -0,0 +1,54 @@ +use rustc::middle::lang_items::PanicLocationLangItem; +use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar}; +use rustc::ty::subst::Subst; +use rustc_target::abi::{LayoutOf, Size}; +use syntax_pos::Span; + +use crate::interpret::{ + MemoryKind, + intrinsics::{InterpCx, InterpResult, Machine, PlaceTy}, +}; + +impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { + pub fn write_caller_location( + &mut self, + span: Span, + dest: PlaceTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx> { + let caller = self.tcx.sess.source_map().lookup_char_pos(span.lo()); + let filename = caller.file.name.to_string(); + let line = Scalar::from_u32(caller.line as u32); + let col = Scalar::from_u32(caller.col_display as u32 + 1); + + let ptr_size = self.pointer_size(); + let u32_size = Size::from_bits(32); + + let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None)) + .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter())); + let loc_layout = self.layout_of(loc_ty)?; + + let file_alloc = self.tcx.allocate_bytes(filename.as_bytes()); + let file_ptr = Pointer::new(file_alloc, Size::ZERO); + let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr)); + let file_len = Scalar::from_uint(filename.len() as u128, ptr_size); + + let location = self.allocate(loc_layout, MemoryKind::Stack); + + let file_out = self.mplace_field(location, 0)?; + let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?; + let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?; + let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?; + let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?; + + let layout = &self.tcx.data_layout; + let alloc = self.memory.get_mut(file_ptr_out.alloc_id)?; + + alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?; + alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?; + alloc.write_scalar(layout, line_out, line.into(), u32_size)?; + alloc.write_scalar(layout, col_out, col.into(), u32_size)?; + + self.write_scalar(location.ptr, dest)?; + Ok(()) + } +} diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index c30c59bbf10..870e50a3cbb 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -8,6 +8,7 @@ use std::hash::Hash; use rustc::hir::def_id::DefId; use rustc::mir; use rustc::ty::{self, Ty, TyCtxt}; +use syntax_pos::Span; use super::{ Allocation, AllocId, InterpResult, Scalar, AllocationExtra, @@ -152,6 +153,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// If this returns successfully, the engine will take care of jumping to the next block. fn call_intrinsic( ecx: &mut InterpCx<'mir, 'tcx, Self>, + span: Span, instance: ty::Instance<'tcx>, args: &[OpTy<'tcx, Self::PointerTag>], dest: PlaceTy<'tcx, Self::PointerTag>, diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index 7f6baf0bb49..d90f2058aa7 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -255,7 +255,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Some(dest) => dest, None => throw_ub!(Unreachable) }; - M::call_intrinsic(self, instance, args, dest)?; + M::call_intrinsic(self, span, instance, args, dest)?; // No stack frame gets pushed, the main loop will just act as if the // call completed. self.goto_block(ret)?; diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 13097a21561..e7095101f46 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -158,6 +158,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { fn call_intrinsic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, + _span: Span, _instance: ty::Instance<'tcx>, _args: &[OpTy<'tcx>], _dest: PlaceTy<'tcx>, diff --git a/src/test/ui/consts/const-eval/const_caller_location.rs b/src/test/ui/consts/const-eval/const_caller_location.rs new file mode 100644 index 00000000000..c63822f052b --- /dev/null +++ b/src/test/ui/consts/const-eval/const_caller_location.rs @@ -0,0 +1,23 @@ +// run-pass + +#![feature(const_fn, core_intrinsics)] + +use std::{intrinsics::caller_location, panic::Location}; + +const LOCATION: &Location = caller_location(); +const NESTED: &Location = { + const fn nested_location() -> &'static Location<'static> { + caller_location() + }; + nested_location() +}; + +fn main() { + assert_eq!(LOCATION.file(), file!()); + assert_eq!(LOCATION.line(), 7); + assert_eq!(LOCATION.column(), 29); + + assert_eq!(NESTED.file(), file!()); + assert_eq!(NESTED.line(), 10); + assert_eq!(NESTED.column(), 9); +}