From 0aa92acda7cb14c0040dcd346e5f015b6f36c352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Thu, 3 May 2018 18:29:14 +0200 Subject: [PATCH] Add a query to convert from ConstValue to Allocation --- src/librustc/dep_graph/dep_node.rs | 11 +++--- src/librustc/ty/maps/config.rs | 6 +++ src/librustc/ty/maps/keys.rs | 9 +++++ src/librustc/ty/maps/mod.rs | 11 ++++++ src/librustc/ty/maps/plumbing.rs | 1 + src/librustc_mir/interpret/const_eval.rs | 28 ++++++++++++- src/librustc_mir/interpret/memory.rs | 50 ++++++++++++++++++++---- src/librustc_mir/interpret/mod.rs | 1 + src/librustc_mir/lib.rs | 1 + src/librustc_trans/base.rs | 13 +++--- 10 files changed, 110 insertions(+), 21 deletions(-) diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 54dda320e1fca..4847a7f4ddbec 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -621,13 +621,14 @@ define_dep_nodes!( <'tcx> [input] UsedCrateSource(CrateNum), [input] PostorderCnums, - // This query is not expected to have inputs -- as a result, it's - // not a good candidate for "replay" because it's essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just this - // query). Making it anonymous avoids hashing the result, which + // These queries are not expected to have inputs -- as a result, they + // are not good candidates for "replay" because they are essentially + // pure functions of their input (and hence the expectation is that + // no caller would be green **apart** from just these + // queries). Making them anonymous avoids hashing the result, which // may save a bit of time. [anon] EraseRegionsTy { ty: Ty<'tcx> }, + [anon] ConstValueToAllocation { val: ConstValue<'tcx>, ty: Ty<'tcx> }, [input] Freevars(DefId), [input] MaybeUnusedTraitImport(DefId), diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index ca594faf5cd3b..4e104692d859b 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -137,6 +137,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::super_predicates_of<'tcx> { } } +impl<'tcx> QueryDescription<'tcx> for queries::const_value_to_allocation<'tcx> { + fn describe(_tcx: TyCtxt, (val, ty): (ConstValue<'tcx>, Ty<'tcx>)) -> String { + format!("converting value `{:?}` ({}) to an allocation", val, ty) + } +} + impl<'tcx> QueryDescription<'tcx> for queries::erase_regions_ty<'tcx> { fn describe(_tcx: TyCtxt, ty: Ty<'tcx>) -> String { format!("erasing regions from `{:?}`", ty) diff --git a/src/librustc/ty/maps/keys.rs b/src/librustc/ty/maps/keys.rs index da29f23589e85..3510a1b7a028f 100644 --- a/src/librustc/ty/maps/keys.rs +++ b/src/librustc/ty/maps/keys.rs @@ -145,6 +145,15 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx>{ } } +impl<'tcx> Key for (mir::interpret::ConstValue<'tcx>, Ty<'tcx>) { + fn map_crate(&self) -> CrateNum { + LOCAL_CRATE + } + fn default_span(&self, _: TyCtxt) -> Span { + DUMMY_SP + } +} + impl<'tcx> Key for Ty<'tcx> { fn map_crate(&self) -> CrateNum { LOCAL_CRATE diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 80402f87650c6..6e419627dd8b5 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -228,6 +228,11 @@ define_maps! { <'tcx> [] fn const_eval: const_eval_dep_node(ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> EvalResult<'tcx>, + /// Converts a constant value to an constant allocation + [] fn const_value_to_allocation: const_value_to_allocation( + (ConstValue<'tcx>, Ty<'tcx>) + ) -> &'tcx Allocation, + [] fn check_match: CheckMatch(DefId) -> Result<(), ErrorReported>, @@ -478,6 +483,12 @@ fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> { DepConstructor::EraseRegionsTy { ty } } +fn const_value_to_allocation<'tcx>( + (val, ty): (ConstValue<'tcx>, Ty<'tcx>) +) -> DepConstructor<'tcx> { + DepConstructor::ConstValueToAllocation { val, ty } +} + fn type_param_predicates<'tcx>((item_id, param_id): (DefId, DefId)) -> DepConstructor<'tcx> { DepConstructor::TypeParamPredicates { item_id, diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 37950463f7444..65dcb7311d336 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -956,6 +956,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::FulfillObligation | DepKind::VtableMethods | DepKind::EraseRegionsTy | + DepKind::ConstValueToAllocation | DepKind::NormalizeProjectionTy | DepKind::NormalizeTyAfterErasingRegions | DepKind::DropckOutlives | diff --git a/src/librustc_mir/interpret/const_eval.rs b/src/librustc_mir/interpret/const_eval.rs index b8bb58b9ed24c..6bf965aaf4463 100644 --- a/src/librustc_mir/interpret/const_eval.rs +++ b/src/librustc_mir/interpret/const_eval.rs @@ -8,12 +8,13 @@ use rustc::ty::subst::Subst; use syntax::ast::Mutability; use syntax::codemap::Span; +use syntax::codemap::DUMMY_SP; use rustc::mir::interpret::{ EvalResult, EvalError, EvalErrorKind, GlobalId, Value, Pointer, PrimVal, AllocId, Allocation, ConstValue, }; -use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory}; +use super::{Place, EvalContext, StackPopCleanup, ValTy, PlaceExtra, Memory, MemoryKind}; use std::fmt; use std::error::Error; @@ -470,7 +471,6 @@ pub fn const_variant_index<'a, 'tcx>( let (ptr, align) = match value { Value::ByValPair(..) | Value::ByVal(_) => { let layout = ecx.layout_of(ty)?; - use super::MemoryKind; let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; let ptr: Pointer = ptr.into(); ecx.write_value_to_ptr(value, ptr, layout.align, ty)?; @@ -482,6 +482,30 @@ pub fn const_variant_index<'a, 'tcx>( ecx.read_discriminant_as_variant_index(place, ty) } +pub fn const_value_to_allocation_provider<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + (val, ty): (ConstValue<'tcx>, Ty<'tcx>), +) -> &'tcx Allocation { + match val { + ConstValue::ByRef(alloc) => return alloc, + _ => () + } + let result = || -> EvalResult<'tcx, &'tcx Allocation> { + let mut ecx = EvalContext::new( + tcx.at(DUMMY_SP), + ty::ParamEnv::reveal_all(), + CompileTimeEvaluator, + ()); + let value = ecx.const_value_to_value(val, ty)?; + let layout = ecx.layout_of(ty)?; + let ptr = ecx.memory.allocate(layout.size.bytes(), layout.align, Some(MemoryKind::Stack))?; + ecx.write_value_to_ptr(value, ptr.into(), layout.align, ty)?; + let alloc = ecx.memory.get(ptr.alloc_id)?; + Ok(tcx.intern_const_alloc(alloc.clone())) + }; + result().expect("unable to convert ConstVal to Allocation") +} + pub fn const_eval_provider<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 18cd75ae0bbf4..ba1c05deef1b4 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -1,14 +1,17 @@ use std::collections::{btree_map, VecDeque}; use std::ptr; +use rustc::hir::def_id::DefId; use rustc::ty::Instance; +use rustc::ty::ParamEnv; use rustc::ty::maps::TyCtxtAt; use rustc::ty::layout::{self, Align, TargetDataLayout}; use syntax::ast::Mutability; +use rustc::middle::const_val::{ConstVal, ErrKind}; use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use rustc::mir::interpret::{MemoryPointer, AllocId, Allocation, AccessKind, Value, Pointer, - EvalResult, PrimVal, EvalErrorKind}; + EvalResult, PrimVal, EvalErrorKind, GlobalId}; pub use rustc::mir::interpret::{write_target_uint, write_target_int, read_target_uint}; use super::{EvalContext, Machine}; @@ -274,6 +277,31 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { /// Allocation accessors impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { + fn const_eval_static(&self, def_id: DefId) -> EvalResult<'tcx, &'tcx Allocation> { + let instance = Instance::mono(self.tcx.tcx, def_id); + let gid = GlobalId { + instance, + promoted: None, + }; + self.tcx.const_eval(ParamEnv::reveal_all().and(gid)).map_err(|err| { + match *err.kind { + ErrKind::Miri(ref err, _) => match err.kind { + EvalErrorKind::TypeckError | + EvalErrorKind::Layout(_) => EvalErrorKind::TypeckError.into(), + _ => EvalErrorKind::ReferencedConstant.into(), + }, + ErrKind::TypeckError => EvalErrorKind::TypeckError.into(), + ref other => bug!("const eval returned {:?}", other), + } + }).map(|val| { + let const_val = match val.val { + ConstVal::Value(val) => val, + ConstVal::Unevaluated(..) => bug!("should be evaluated"), + }; + self.tcx.const_value_to_allocation((const_val, val.ty)) + }) + } + pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> { // normal alloc? match self.alloc_map.get(&id) { @@ -283,13 +311,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { Some(alloc) => Ok(alloc), None => { // static alloc? - self.tcx.interpret_interner.get_alloc(id) - // no alloc? produce an error - .ok_or_else(|| if self.tcx.interpret_interner.get_fn(id).is_some() { - EvalErrorKind::DerefFunctionPointer.into() - } else { - EvalErrorKind::DanglingPointerDeref.into() - }) + if let Some(a) = self.tcx.interpret_interner.get_alloc(id) { + return Ok(a); + } + // static variable? + if let Some(did) = self.tcx.interpret_interner.get_static(id) { + return self.const_eval_static(did); + } + // otherwise return an error + Err(if self.tcx.interpret_interner.get_fn(id).is_some() { + EvalErrorKind::DerefFunctionPointer.into() + } else { + EvalErrorKind::DanglingPointerDeref.into() + }) }, }, } diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 7f9e67a62ccc4..d39bae5e8dbba 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -23,6 +23,7 @@ pub use self::const_eval::{ mk_borrowck_eval_cx, eval_body, CompileTimeEvaluator, + const_value_to_allocation_provider, const_eval_provider, const_val_field, const_variant_index, diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 2545ba3a94af1..fbc0facbc4967 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -85,6 +85,7 @@ pub fn provide(providers: &mut Providers) { shim::provide(providers); transform::provide(providers); providers.const_eval = interpret::const_eval_provider; + providers.const_value_to_allocation = interpret::const_value_to_allocation_provider; providers.check_match = hair::pattern::check_match; } diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 54177c5d5a2cc..0dd1adbff86e0 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -1373,6 +1373,7 @@ mod temp_stable_hash_impls { fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { use rustc::mir::interpret::GlobalId; + use rustc::middle::const_val::ConstVal; info!("loading wasm section {:?}", id); @@ -1391,11 +1392,11 @@ fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { let param_env = ty::ParamEnv::reveal_all(); let val = tcx.const_eval(param_env.and(cid)).unwrap(); - let mem = val.to_ptr().expect("should be pointer"); - assert_eq!(mem.offset, 0); - let alloc = tcx - .interpret_interner - .get_alloc(mem.alloc_id) - .expect("miri allocation never successfully created"); + let const_val = match val.val { + ConstVal::Value(val) => val, + ConstVal::Unevaluated(..) => bug!("should be evaluated"), + }; + + let alloc = tcx.const_value_to_allocation((const_val, val.ty)); (section.to_string(), alloc.bytes.clone()) }