From b84f7e20293cd1cebee011dbb485583c79f7ede3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Oct 2018 11:21:38 +0200 Subject: [PATCH] add Borrow tag to pointers; remove old locking code --- src/fn_call.rs | 28 ++++++------- src/helpers.rs | 4 +- src/intrinsic.rs | 10 ++--- src/lib.rs | 61 ++++++++++++++------------- src/locks.rs | 94 ------------------------------------------ src/operator.rs | 47 ++++++++++++--------- src/stacked_borrows.rs | 36 ++++++++++++++++ src/tls.rs | 10 ++--- 8 files changed, 119 insertions(+), 171 deletions(-) delete mode 100644 src/locks.rs create mode 100644 src/stacked_borrows.rs diff --git a/src/fn_call.rs b/src/fn_call.rs index 812df49b0b8f..280cc2bdea58 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx, 'mir> { fn emulate_foreign_item( &mut self, def_id: DefId, - args: &[OpTy<'tcx>], - dest: PlaceTy<'tcx>, + args: &[OpTy<'tcx, Borrow>], + dest: PlaceTy<'tcx, Borrow>, ret: mir::BasicBlock, ) -> EvalResult<'tcx>; @@ -28,28 +28,28 @@ pub trait EvalContextExt<'tcx, 'mir> { fn emulate_missing_fn( &mut self, path: String, - args: &[OpTy<'tcx>], - dest: Option>, + args: &[OpTy<'tcx, Borrow>], + dest: Option>, ret: Option, ) -> EvalResult<'tcx>; fn find_fn( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: Option>, + args: &[OpTy<'tcx, Borrow>], + dest: Option>, ret: Option, ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>>; - fn write_null(&mut self, dest: PlaceTy<'tcx>) -> EvalResult<'tcx>; + fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>; } impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> { fn find_fn( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: Option>, + args: &[OpTy<'tcx, Borrow>], + dest: Option>, ret: Option, ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> { trace!("eval_fn_call: {:#?}, {:?}", instance, dest.map(|place| *place)); @@ -108,8 +108,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, ' fn emulate_foreign_item( &mut self, def_id: DefId, - args: &[OpTy<'tcx>], - dest: PlaceTy<'tcx>, + args: &[OpTy<'tcx, Borrow>], + dest: PlaceTy<'tcx, Borrow>, ret: mir::BasicBlock, ) -> EvalResult<'tcx> { let attrs = self.tcx.get_attrs(def_id); @@ -675,8 +675,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, ' fn emulate_missing_fn( &mut self, path: String, - _args: &[OpTy<'tcx>], - dest: Option>, + _args: &[OpTy<'tcx, Borrow>], + dest: Option>, ret: Option, ) -> EvalResult<'tcx> { // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early. @@ -724,7 +724,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for EvalContext<'a, ' Ok(()) } - fn write_null(&mut self, dest: PlaceTy<'tcx>) -> EvalResult<'tcx> { + fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> { self.write_scalar(Scalar::from_int(0, dest.layout.size), dest) } } diff --git a/src/helpers.rs b/src/helpers.rs index 27b2109d18a1..de787145e22c 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -7,7 +7,7 @@ pub trait FalibleScalarExt { fn to_bytes(self) -> EvalResult<'static, u128>; } -impl FalibleScalarExt for Scalar { +impl FalibleScalarExt for Scalar { fn to_bytes(self) -> EvalResult<'static, u128> { match self { Scalar::Bits { bits, size } => { @@ -19,7 +19,7 @@ impl FalibleScalarExt for Scalar { } } -impl FalibleScalarExt for ScalarMaybeUndef { +impl FalibleScalarExt for ScalarMaybeUndef { fn to_bytes(self) -> EvalResult<'static, u128> { self.not_undef()?.to_bytes() } diff --git a/src/intrinsic.rs b/src/intrinsic.rs index 19c4f04f4826..11c0bd7907c1 100644 --- a/src/intrinsic.rs +++ b/src/intrinsic.rs @@ -6,7 +6,7 @@ use rustc::mir::interpret::{EvalResult, PointerArithmetic}; use rustc_mir::interpret::{EvalContext, PlaceTy, OpTy}; use super::{ - Value, Scalar, ScalarMaybeUndef, + Value, Scalar, ScalarMaybeUndef, Borrow, FalibleScalarExt, OperatorEvalContextExt }; @@ -14,8 +14,8 @@ pub trait EvalContextExt<'tcx> { fn call_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: PlaceTy<'tcx>, + args: &[OpTy<'tcx, Borrow>], + dest: PlaceTy<'tcx, Borrow>, ) -> EvalResult<'tcx>; } @@ -23,8 +23,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super: fn call_intrinsic( &mut self, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: PlaceTy<'tcx>, + args: &[OpTy<'tcx, Borrow>], + dest: PlaceTy<'tcx, Borrow>, ) -> EvalResult<'tcx> { if self.emulate_intrinsic(instance, args, dest)? { return Ok(()); diff --git a/src/lib.rs b/src/lib.rs index 0bebe40d529a..9ac703e2675b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,11 +21,9 @@ use rustc::ty::layout::{TyLayout, LayoutOf, Size}; use rustc::hir::def_id::DefId; use rustc::mir; -use syntax::ast::Mutability; use syntax::attr; -pub use rustc::mir::interpret::*; pub use rustc_mir::interpret::*; pub use rustc_mir::interpret::{self, AllocMap}; // resolve ambiguity @@ -34,9 +32,9 @@ mod operator; mod intrinsic; mod helpers; mod tls; -mod locks; mod range_map; mod mono_hash_map; +mod stacked_borrows; use fn_call::EvalContextExt as MissingFnsEvalContextExt; use operator::EvalContextExt as OperatorEvalContextExt; @@ -45,6 +43,7 @@ use tls::{EvalContextExt as TlsEvalContextExt, TlsData}; use range_map::RangeMap; use helpers::FalibleScalarExt; use mono_hash_map::MonoHashMap; +use stacked_borrows::Borrow; pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -56,7 +55,6 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( tcx.at(syntax::source_map::DUMMY_SP), ty::ParamEnv::reveal_all(), Evaluator::new(validate), - Default::default(), ); let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id); @@ -118,9 +116,9 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>( let foo = ecx.memory.allocate_static_bytes(b"foo\0"); let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8); let foo_layout = ecx.layout_of(foo_ty)?; - let foo_place = ecx.allocate(foo_layout, MemoryKind::Stack)?; // will be interned in just a second + let foo_place = ecx.allocate(foo_layout, MiriMemoryKind::Env.into())?; ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?; - ecx.memory.intern_static(foo_place.to_ptr()?.alloc_id, Mutability::Immutable)?; + ecx.memory.mark_immutable(foo_place.to_ptr()?.alloc_id)?; ecx.write_scalar(foo_place.ptr, dest)?; assert!(args.next().is_none(), "start lang item has more arguments than expected"); @@ -227,7 +225,7 @@ impl Into> for MiriMemoryKind { pub struct Evaluator<'tcx> { /// Environment variables set by `setenv` /// Miri does not expose env vars from the host to the emulated program - pub(crate) env_vars: HashMap, Pointer>, + pub(crate) env_vars: HashMap, Pointer>, /// TLS state pub(crate) tls: TlsData<'tcx>, @@ -247,11 +245,11 @@ impl<'tcx> Evaluator<'tcx> { } impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { - type MemoryData = (); type MemoryKinds = MiriMemoryKind; - type PointerTag = (); // still WIP + type AllocExtra = (); + type PointerTag = Borrow; - type MemoryMap = MonoHashMap, Allocation<()>)>; + type MemoryMap = MonoHashMap, Allocation)>; const STATIC_KIND: Option = Some(MiriMemoryKind::MutStatic); @@ -282,8 +280,8 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { fn find_fn( ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: Option>, + args: &[OpTy<'tcx, Borrow>], + dest: Option>, ret: Option, ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> { ecx.find_fn(instance, args, dest, ret) @@ -292,8 +290,8 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { fn call_intrinsic( ecx: &mut rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - args: &[OpTy<'tcx>], - dest: PlaceTy<'tcx>, + args: &[OpTy<'tcx, Borrow>], + dest: PlaceTy<'tcx, Borrow>, ) -> EvalResult<'tcx> { ecx.call_intrinsic(instance, args, dest) } @@ -301,17 +299,17 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { fn ptr_op( ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>, bin_op: mir::BinOp, - left: Scalar, + left: Scalar, left_layout: TyLayout<'tcx>, - right: Scalar, + right: Scalar, right_layout: TyLayout<'tcx>, - ) -> EvalResult<'tcx, (Scalar, bool)> { + ) -> EvalResult<'tcx, (Scalar, bool)> { ecx.ptr_op(bin_op, left, left_layout, right, right_layout) } fn box_alloc( ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, - dest: PlaceTy<'tcx>, + dest: PlaceTy<'tcx, Borrow>, ) -> EvalResult<'tcx> { trace!("box_alloc for {:?}", dest.layout.ty); // Call the `exchange_malloc` lang item @@ -351,7 +349,7 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { fn find_foreign_static( tcx: TyCtxtAt<'a, 'tcx, 'tcx>, def_id: DefId, - ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> { + ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> { let attrs = tcx.get_attrs(def_id); let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") { Some(name) => name.as_str(), @@ -371,16 +369,6 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { Ok(Cow::Owned(alloc)) } - fn validation_op( - _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>, - _op: ::rustc::mir::ValidationOp, - _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>, - ) -> EvalResult<'tcx> { - // FIXME: prevent this from ICEing - //ecx.validation_op(op, operand) - Ok(()) - } - fn before_terminator(_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> { // We are not interested in detecting loops @@ -389,8 +377,19 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> { fn static_with_default_tag( alloc: &'_ Allocation - ) -> Cow<'_, Allocation> { - let alloc = alloc.clone(); + ) -> Cow<'_, Allocation> { + let alloc: Allocation = Allocation { + bytes: alloc.bytes.clone(), + relocations: Relocations::from_presorted( + alloc.relocations.iter() + .map(|&(offset, ((), alloc))| (offset, (Borrow::default(), alloc))) + .collect() + ), + undef_mask: alloc.undef_mask.clone(), + align: alloc.align, + mutability: alloc.mutability, + extra: Self::AllocExtra::default(), + }; Cow::Owned(alloc) } } diff --git a/src/locks.rs b/src/locks.rs deleted file mode 100644 index a87ff6367e3a..000000000000 --- a/src/locks.rs +++ /dev/null @@ -1,94 +0,0 @@ -#![allow(unused)] - -use super::*; -use rustc::middle::region; -use rustc::ty::layout::Size; - -//////////////////////////////////////////////////////////////////////////////// -// Locks -//////////////////////////////////////////////////////////////////////////////// - -// Just some dummy to keep this compiling; I think some of this will be useful later -type AbsPlace<'tcx> = ::rustc::ty::Ty<'tcx>; - -/// Information about a lock that is currently held. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct LockInfo<'tcx> { - /// Stores for which lifetimes (of the original write lock) we got - /// which suspensions. - suspended: HashMap, Vec>, - /// The current state of the lock that's actually effective. - pub active: Lock, -} - -/// Write locks are identified by a stack frame and an "abstract" (untyped) place. -/// It may be tempting to use the lifetime as identifier, but that does not work -/// for two reasons: -/// * First of all, due to subtyping, the same lock may be referred to with different -/// lifetimes. -/// * Secondly, different write locks may actually have the same lifetime. See `test2` -/// in `run-pass/many_shr_bor.rs`. -/// The Id is "captured" when the lock is first suspended; at that point, the borrow checker -/// considers the path frozen and hence the Id remains stable. -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct WriteLockId<'tcx> { - frame: usize, - path: AbsPlace<'tcx>, -} - - -use rustc::mir::interpret::Lock::*; -use rustc::mir::interpret::Lock; - -impl<'tcx> Default for LockInfo<'tcx> { - fn default() -> Self { - LockInfo::new(NoLock) - } -} - -impl<'tcx> LockInfo<'tcx> { - fn new(lock: Lock) -> LockInfo<'tcx> { - LockInfo { - suspended: HashMap::new(), - active: lock, - } - } - - fn access_permitted(&self, frame: Option, access: AccessKind) -> bool { - use super::AccessKind::*; - match (&self.active, access) { - (&NoLock, _) => true, - (&ReadLock(ref lfts), Read) => { - assert!(!lfts.is_empty(), "Someone left an empty read lock behind."); - // Read access to read-locked region is okay, no matter who's holding the read lock. - true - } - (&WriteLock(ref lft), _) => { - // All access is okay if we are the ones holding it - Some(lft.frame) == frame - } - _ => false, // Nothing else is okay. - } - } -} - -impl<'tcx> RangeMap> { - pub fn check( - &self, - frame: Option, - offset: u64, - len: u64, - access: AccessKind, - ) -> Result<(), LockInfo<'tcx>> { - if len == 0 { - return Ok(()); - } - for lock in self.iter(offset, len) { - // Check if the lock is in conflict with the access. - if !lock.access_permitted(frame, access) { - return Err(lock.clone()); - } - } - Ok(()) - } -} diff --git a/src/operator.rs b/src/operator.rs index 4662a162679e..dd79f293134f 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -7,44 +7,44 @@ pub trait EvalContextExt<'tcx> { fn ptr_op( &self, bin_op: mir::BinOp, - left: Scalar, + left: Scalar, left_layout: TyLayout<'tcx>, - right: Scalar, + right: Scalar, right_layout: TyLayout<'tcx>, - ) -> EvalResult<'tcx, (Scalar, bool)>; + ) -> EvalResult<'tcx, (Scalar, bool)>; fn ptr_int_arithmetic( &self, bin_op: mir::BinOp, - left: Pointer, + left: Pointer, right: u128, signed: bool, - ) -> EvalResult<'tcx, (Scalar, bool)>; + ) -> EvalResult<'tcx, (Scalar, bool)>; fn ptr_eq( &self, - left: Scalar, - right: Scalar, + left: Scalar, + right: Scalar, size: Size, ) -> EvalResult<'tcx, bool>; fn pointer_offset_inbounds( &self, - ptr: Scalar, + ptr: Scalar, pointee_ty: Ty<'tcx>, offset: i64, - ) -> EvalResult<'tcx, Scalar>; + ) -> EvalResult<'tcx, Scalar>; } impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super::Evaluator<'tcx>> { fn ptr_op( &self, bin_op: mir::BinOp, - left: Scalar, + left: Scalar, left_layout: TyLayout<'tcx>, - right: Scalar, + right: Scalar, right_layout: TyLayout<'tcx>, - ) -> EvalResult<'tcx, (Scalar, bool)> { + ) -> EvalResult<'tcx, (Scalar, bool)> { use rustc::mir::BinOp::*; trace!("ptr_op: {:?} {:?} {:?}", left, bin_op, right); @@ -124,8 +124,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super: fn ptr_eq( &self, - left: Scalar, - right: Scalar, + left: Scalar, + right: Scalar, size: Size, ) -> EvalResult<'tcx, bool> { Ok(match (left, right) { @@ -203,13 +203,13 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super: fn ptr_int_arithmetic( &self, bin_op: mir::BinOp, - left: Pointer, + left: Pointer, right: u128, signed: bool, - ) -> EvalResult<'tcx, (Scalar, bool)> { + ) -> EvalResult<'tcx, (Scalar, bool)> { use rustc::mir::BinOp::*; - fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) { + fn map_to_primval((res, over): (Pointer, bool)) -> (Scalar, bool) { (Scalar::Ptr(res), over) } @@ -237,7 +237,14 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super: if right & base_mask == base_mask { // Case 1: The base address bits are all preserved, i.e., right is all-1 there let offset = (left.offset.bytes() as u128 & right) as u64; - (Scalar::Ptr(Pointer::new(left.alloc_id, Size::from_bytes(offset))), false) + ( + Scalar::Ptr(Pointer::new_with_tag( + left.alloc_id, + Size::from_bytes(offset), + left.tag, + )), + false, + ) } else if right & base_mask == 0 { // Case 2: The base address bits are all taken away, i.e., right is all-0 there (Scalar::Bits { bits: (left.offset.bytes() as u128) & right, size: ptr_size }, false) @@ -277,10 +284,10 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'mir, 'tcx, super: /// allocation. fn pointer_offset_inbounds( &self, - ptr: Scalar, + ptr: Scalar, pointee_ty: Ty<'tcx>, offset: i64, - ) -> EvalResult<'tcx, Scalar> { + ) -> EvalResult<'tcx, Scalar> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.layout_of(pointee_ty)?.size.bytes() as i64; let offset = offset.checked_mul(pointee_size).ok_or_else(|| EvalErrorKind::Overflow(mir::BinOp::Mul))?; diff --git a/src/stacked_borrows.rs b/src/stacked_borrows.rs new file mode 100644 index 000000000000..dd9f1b370890 --- /dev/null +++ b/src/stacked_borrows.rs @@ -0,0 +1,36 @@ +use super::RangeMap; + +pub type Timestamp = u64; + +/// Information about a potentially mutable borrow +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum Mut { + /// A unique, mutable reference + Uniq(Timestamp), + /// Any raw pointer, or a shared borrow with interior mutability + Raw, +} + +/// Information about any kind of borrow +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum Borrow { + /// A mutable borrow, a raw pointer, or a shared borrow with interior mutability + Mut(Mut), + /// A shared borrow without interior mutability + Frz(Timestamp) +} + +/// An item in the borrow stack +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum BorStackItem { + /// Defines which references are permitted to mutate *if* the location is not frozen + Mut(Mut), + /// A barrier, tracking the function it belongs to by its index on the call stack + FnBarrier(usize) +} + +impl Default for Borrow { + fn default() -> Self { + Borrow::Mut(Mut::Raw) + } +} diff --git a/src/tls.rs b/src/tls.rs index c04f7a9c3502..c5d119ec7f5b 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -5,14 +5,14 @@ use rustc::{ty, ty::layout::HasDataLayout, mir}; use super::{ EvalResult, EvalErrorKind, StackPopCleanup, EvalContext, Evaluator, - MPlaceTy, Scalar, + MPlaceTy, Scalar, Borrow, }; pub type TlsKey = u128; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct TlsEntry<'tcx> { - pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread. + pub(crate) data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread. pub(crate) dtor: Option>, } @@ -67,7 +67,7 @@ impl<'tcx> TlsData<'tcx> { } } - pub fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> { + pub fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar> { match self.keys.get(&key) { Some(&TlsEntry { data, .. }) => { trace!("TLS key {} loaded: {:?}", key, data); @@ -77,7 +77,7 @@ impl<'tcx> TlsData<'tcx> { } } - pub fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> { + pub fn store_tls(&mut self, key: TlsKey, new_data: Scalar) -> EvalResult<'tcx> { match self.keys.get_mut(&key) { Some(&mut TlsEntry { ref mut data, .. }) => { trace!("TLS key {} stored: {:?}", key, new_data); @@ -110,7 +110,7 @@ impl<'tcx> TlsData<'tcx> { &mut self, key: Option, cx: impl HasDataLayout, - ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { + ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> { use std::collections::Bound::*; let thread_local = &mut self.keys;