diff --git a/library/std/src/sys/thread_local/fast_local/eager.rs b/library/std/src/sys/thread_local/fast_local/eager.rs index c2bc580530ba4..b97bd9cc88cef 100644 --- a/library/std/src/sys/thread_local/fast_local/eager.rs +++ b/library/std/src/sys/thread_local/fast_local/eager.rs @@ -21,43 +21,35 @@ impl Storage { Storage { state: Cell::new(State::Initial), val: UnsafeCell::new(val) } } - /// Get a reference to the TLS value. If the TLS variable has been destroyed, - /// `None` is returned. + /// Get a pointer to the TLS value. If the TLS variable has been destroyed, + /// a null pointer is returned. /// - /// # Safety - /// * The `self` reference must remain valid until the TLS destructor has been - /// run. - /// * The returned reference may only be used until thread destruction occurs - /// and may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after thread destruction has + /// occurred. /// - // FIXME(#110897): return NonNull instead of lying about the lifetime. + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[inline] - pub unsafe fn get(&self) -> Option<&'static T> { + pub unsafe fn get(&self) -> *const T { match self.state.get() { - // SAFETY: as the state is not `Destroyed`, the value cannot have - // been destroyed yet. The reference fulfills the terms outlined - // above. - State::Alive => unsafe { Some(&*self.val.get()) }, - State::Destroyed => None, + State::Alive => self.val.get(), + State::Destroyed => ptr::null(), State::Initial => unsafe { self.initialize() }, } } #[cold] - unsafe fn initialize(&self) -> Option<&'static T> { + unsafe fn initialize(&self) -> *const T { // Register the destructor // SAFETY: - // * the destructor will be called at thread destruction. - // * the caller guarantees that `self` will be valid until that time. + // The caller guarantees that `self` will be valid until thread destruction. unsafe { register_dtor(ptr::from_ref(self).cast_mut().cast(), destroy::); } + self.state.set(State::Alive); - // SAFETY: as the state is not `Destroyed`, the value cannot have - // been destroyed yet. The reference fulfills the terms outlined - // above. - unsafe { Some(&*self.val.get()) } + self.val.get() } } diff --git a/library/std/src/sys/thread_local/fast_local/lazy.rs b/library/std/src/sys/thread_local/fast_local/lazy.rs index c2e9a17145468..c1ada35d484d3 100644 --- a/library/std/src/sys/thread_local/fast_local/lazy.rs +++ b/library/std/src/sys/thread_local/fast_local/lazy.rs @@ -39,49 +39,31 @@ where Storage { state: UnsafeCell::new(State::Initial) } } - /// Get a reference to the TLS value, potentially initializing it with the - /// provided parameters. If the TLS variable has been destroyed, `None` is - /// returned. + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. /// - /// # Safety - /// * The `self` reference must remain valid until the TLS destructor is run, - /// at which point the returned reference is invalidated. - /// * The returned reference may only be used until thread destruction occurs - /// and may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. /// - // FIXME(#110897): return NonNull instead of lying about the lifetime. + /// # Safety + /// The `self` reference must remain valid until the TLS destructor is run. #[inline] - pub unsafe fn get_or_init( - &self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { - // SAFETY: - // No mutable reference to the inner value exists outside the calls to - // `replace`. The lifetime of the returned reference fulfills the terms - // outlined above. + pub unsafe fn get_or_init(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let state = unsafe { &*self.state.get() }; match state { - State::Alive(v) => Some(v), - State::Destroyed(_) => None, + State::Alive(v) => v, + State::Destroyed(_) => ptr::null(), State::Initial => unsafe { self.initialize(i, f) }, } } #[cold] - unsafe fn initialize( - &self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { + unsafe fn initialize(&self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { // Perform initialization let v = i.and_then(Option::take).unwrap_or_else(f); - // SAFETY: - // If references to the inner value exist, they were created in `f` - // and are invalidated here. The caller promises to never use them - // after this. let old = unsafe { self.state.get().replace(State::Alive(v)) }; match old { // If the variable is not being recursively initialized, register @@ -92,12 +74,10 @@ where val => drop(val), } - // SAFETY: - // Initialization was completed and the state was set to `Alive`, so the - // reference fulfills the terms outlined above. + // SAFETY: the state was just set to `Alive` unsafe { let State::Alive(v) = &*self.state.get() else { unreachable_unchecked() }; - Some(v) + v } } } diff --git a/library/std/src/sys/thread_local/fast_local/mod.rs b/library/std/src/sys/thread_local/fast_local/mod.rs index 152137d92709b..575d60de4ee95 100644 --- a/library/std/src/sys/thread_local/fast_local/mod.rs +++ b/library/std/src/sys/thread_local/fast_local/mod.rs @@ -52,32 +52,26 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::EagerStorage; + unsafe { use $crate::mem::needs_drop; - use $crate::ptr::addr_of; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::EagerStorage; - if needs_drop::<$t>() { - #[thread_local] - static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); - unsafe { - VAL.get() + LocalKey::new(const { + if needs_drop::<$t>() { + |_| { + #[thread_local] + static VAL: EagerStorage<$t> = EagerStorage::new(__INIT); + VAL.get() + } + } else { + |_| { + #[thread_local] + static VAL: $t = __INIT; + &VAL + } } - } else { - #[thread_local] - static VAL: $t = __INIT; - unsafe { - $crate::option::Option::Some(&*addr_of!(VAL)) - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + }) } }}, @@ -88,31 +82,26 @@ pub macro thread_local_inner { $init } - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::LazyStorage; + unsafe { use $crate::mem::needs_drop; + use $crate::thread::LocalKey; + use $crate::thread::local_impl::LazyStorage; - if needs_drop::<$t>() { - #[thread_local] - static VAL: LazyStorage<$t, ()> = LazyStorage::new(); - unsafe { - VAL.get_or_init(init, __init) + LocalKey::new(const { + if needs_drop::<$t>() { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, ()> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } + } else { + |init| { + #[thread_local] + static VAL: LazyStorage<$t, !> = LazyStorage::new(); + VAL.get_or_init(init, __init) + } } - } else { - #[thread_local] - static VAL: LazyStorage<$t, !> = LazyStorage::new(); - unsafe { - VAL.get_or_init(init, __init) - } - } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + }) } }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { diff --git a/library/std/src/sys/thread_local/os_local.rs b/library/std/src/sys/thread_local/os_local.rs index d6ddbb78a9c86..ee5adef66eacb 100644 --- a/library/std/src/sys/thread_local/os_local.rs +++ b/library/std/src/sys/thread_local/os_local.rs @@ -16,30 +16,22 @@ pub macro thread_local_inner { }, // used to generate the `LocalKey` value for `thread_local!` - (@key $t:ty, $init:expr) => { - { - #[inline] - fn __init() -> $t { $init } + (@key $t:ty, $init:expr) => {{ + #[inline] + fn __init() -> $t { $init } - // `#[inline] does not work on windows-gnu due to linking errors around dllimports. - // See https://github.com/rust-lang/rust/issues/109797. - #[cfg_attr(not(windows), inline)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { - use $crate::thread::local_impl::Key; - - static __KEY: Key<$t> = Key::new(); - unsafe { - __KEY.get(init, __init) - } - } + unsafe { + use $crate::thread::LocalKey; + use $crate::thread::local_impl::Key; - unsafe { - $crate::thread::LocalKey::new(__getit) - } + // Inlining does not work on windows-gnu due to linking errors around + // dllimports. See https://github.com/rust-lang/rust/issues/109797. + LocalKey::new(#[cfg_attr(windows, inline(never))] |init| { + static VAL: Key<$t> = Key::new(); + VAL.get(init, __init) + }) } - }, + }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { $(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> = $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); @@ -67,38 +59,33 @@ impl Key { Key { os: OsKey::new(Some(destroy_value::)), marker: PhantomData } } - /// Get the value associated with this key, initializating it if necessary. + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. If the TLS variable has been destroyed, a null + /// pointer is returned. /// - /// # Safety - /// * the returned reference must not be used after recursive initialization - /// or thread destruction occurs. - pub unsafe fn get( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> Option<&'static T> { + /// The resulting pointer may not be used after reentrant inialialization + /// or thread destruction has occurred. + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { // SAFETY: (FIXME: get should actually be safe) let ptr = unsafe { self.os.get() as *mut Value }; if ptr.addr() > 1 { // SAFETY: the check ensured the pointer is safe (its destructor // is not running) + it is coming from a trusted source (self). - unsafe { Some(&(*ptr).value) } + unsafe { &(*ptr).value } } else { - // SAFETY: At this point we are sure we have no value and so - // initializing (or trying to) is safe. - unsafe { self.try_initialize(ptr, i, f) } + self.try_initialize(ptr, i, f) } } - unsafe fn try_initialize( + fn try_initialize( &'static self, ptr: *mut Value, i: Option<&mut Option>, f: impl FnOnce() -> T, - ) -> Option<&'static T> { + ) -> *const T { if ptr.addr() == 1 { // destructor is running - return None; + return ptr::null(); } let value = i.and_then(Option::take).unwrap_or_else(f); @@ -119,7 +106,7 @@ impl Key { } // SAFETY: We just created this value above. - unsafe { Some(&(*ptr).value) } + unsafe { &(*ptr).value } } } diff --git a/library/std/src/sys/thread_local/static_local.rs b/library/std/src/sys/thread_local/static_local.rs index 6beda2e718802..0f08cab1ae4ff 100644 --- a/library/std/src/sys/thread_local/static_local.rs +++ b/library/std/src/sys/thread_local/static_local.rs @@ -13,19 +13,14 @@ pub macro thread_local_inner { (@key $t:ty, const $init:expr) => {{ const __INIT: $t = $init; - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - _init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { + unsafe { + use $crate::thread::LocalKey; use $crate::thread::local_impl::EagerStorage; - static VAL: EagerStorage<$t> = EagerStorage { value: __INIT }; - $crate::option::Option::Some(&VAL.value) - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + LocalKey::new(|_| { + static VAL: EagerStorage<$t> = EagerStorage { value: __INIT }; + &VAL.value + }) } }}, @@ -34,19 +29,14 @@ pub macro thread_local_inner { #[inline] fn __init() -> $t { $init } - #[inline] - #[deny(unsafe_op_in_unsafe_fn)] - unsafe fn __getit( - init: $crate::option::Option<&mut $crate::option::Option<$t>>, - ) -> $crate::option::Option<&'static $t> { + unsafe { + use $crate::thread::LocalKey; use $crate::thread::local_impl::LazyStorage; - static VAL: LazyStorage<$t> = LazyStorage::new(); - unsafe { $crate::option::Option::Some(VAL.get(init, __init)) } - } - - unsafe { - $crate::thread::LocalKey::new(__getit) + LocalKey::new(|init| { + static VAL: LazyStorage<$t> = LazyStorage::new(); + VAL.get(init, __init) + }) } }}, ($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => { @@ -73,16 +63,13 @@ impl LazyStorage { LazyStorage { value: UnsafeCell::new(None) } } - /// Gets a reference to the contained value, initializing it if necessary. + /// Get a pointer to the TLS value, potentially initializing it with the + /// provided parameters. /// - /// # Safety - /// The returned reference may not be used after reentrant initialization has occurred. + /// The resulting pointer may not be used after reentrant inialialization + /// has occurred. #[inline] - pub unsafe fn get( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> &'static T { + pub fn get(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let value = unsafe { &*self.value.get() }; match value { Some(v) => v, @@ -91,11 +78,7 @@ impl LazyStorage { } #[cold] - unsafe fn initialize( - &'static self, - i: Option<&mut Option>, - f: impl FnOnce() -> T, - ) -> &'static T { + fn initialize(&'static self, i: Option<&mut Option>, f: impl FnOnce() -> T) -> *const T { let value = i.and_then(Option::take).unwrap_or_else(f); // Destroy the old value, after updating the TLS variable as the // destructor might reference it. diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index c1b4440e56088..aed185637fd1f 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -123,7 +123,7 @@ pub struct LocalKey { // trivially devirtualizable by LLVM because the value of `inner` never // changes and the constant should be readonly within a crate. This mainly // only runs into problems when TLS statics are exported across crates. - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, + inner: fn(Option<&mut Option>) -> *const T, } #[stable(feature = "std_debug", since = "1.16.0")] @@ -238,9 +238,7 @@ impl LocalKey { issue = "none" )] #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] - pub const unsafe fn new( - inner: unsafe fn(Option<&mut Option>) -> Option<&'static T>, - ) -> LocalKey { + pub const unsafe fn new(inner: fn(Option<&mut Option>) -> *const T) -> LocalKey { LocalKey { inner } } @@ -281,8 +279,7 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - // SAFETY: `inner` is safe to call within the lifetime of the thread - let thread_local = unsafe { (self.inner)(None).ok_or(AccessError)? }; + let thread_local = unsafe { (self.inner)(None).as_ref().ok_or(AccessError)? }; Ok(f(thread_local)) } @@ -304,9 +301,8 @@ impl LocalKey { { let mut init = Some(init); - // SAFETY: `inner` is safe to call within the lifetime of the thread let reference = unsafe { - (self.inner)(Some(&mut init)).expect( + (self.inner)(Some(&mut init)).as_ref().expect( "cannot access a Thread Local Storage value \ during or after destruction", ) diff --git a/tests/ui/suggestions/missing-lifetime-specifier.rs b/tests/ui/suggestions/missing-lifetime-specifier.rs index cd7fa0c1d8562..dd437fc0c0b8b 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.rs +++ b/tests/ui/suggestions/missing-lifetime-specifier.rs @@ -1,6 +1,11 @@ -// different number of duplicated diagnostics on different targets -//@ only-x86_64 -//@ only-linux +// The specific errors produced depend the thread-local implementation. +// Run only on platforms with "fast" TLS. +//@ ignore-windows FIXME(#84933) +//@ ignore-wasm globals are used instead of thread locals +//@ ignore-emscripten globals are used instead of thread locals +//@ ignore-android does not use #[thread_local] +//@ ignore-nto does not use #[thread_local] +// Different number of duplicated diagnostics on different targets //@ compile-flags: -Zdeduplicate-diagnostics=yes #![allow(bare_trait_objects)] @@ -21,23 +26,19 @@ trait Tar<'t, 'k, I> {} thread_local! { static a: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers + //~^ ERROR missing lifetime specifiers } thread_local! { static b: RefCell>>> = RefCell::new(HashMap::new()); - //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers + //~^ ERROR missing lifetime specifiers } thread_local! { static c: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers } thread_local! { static d: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR missing lifetime specifiers - //~| ERROR missing lifetime specifiers } thread_local! { @@ -47,8 +48,7 @@ thread_local! { thread_local! { static f: RefCell>>>> = RefCell::new(HashMap::new()); //~^ ERROR trait takes 2 lifetime arguments but 1 lifetime argument was supplied - //~| ERROR missing lifetime - //~| ERROR missing lifetime + //~| ERROR missing lifetime specifier } fn main() {} diff --git a/tests/ui/suggestions/missing-lifetime-specifier.stderr b/tests/ui/suggestions/missing-lifetime-specifier.stderr index 2b85cfde7b64b..e0b6bb872b55d 100644 --- a/tests/ui/suggestions/missing-lifetime-specifier.stderr +++ b/tests/ui/suggestions/missing-lifetime-specifier.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 + --> $DIR/missing-lifetime-specifier.rs:28:44 | LL | static a: RefCell>>> = RefCell::new(HashMap::new()); | ^^^ expected 2 lifetime parameters @@ -11,20 +11,7 @@ LL | static a: RefCell>>>> = RefC | ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:23:44 - | -LL | / thread_local! { -LL | | static a: RefCell>>> = RefCell::new(HashMap::new()); - | | ^^^ expected 2 lifetime parameters -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:44 + --> $DIR/missing-lifetime-specifier.rs:32:44 | LL | static b: RefCell>>> = RefCell::new(HashMap::new()); | ^^^^ expected 2 lifetime parameters @@ -38,22 +25,7 @@ LL | static b: RefCell>> | +++++++ ++++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:28:44 - | -LL | / thread_local! { -LL | | static b: RefCell>>> = RefCell::new(HashMap::new()); - | | ^^^^ expected 2 lifetime parameters - | | | - | | expected named lifetime parameter -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:47 + --> $DIR/missing-lifetime-specifier.rs:36:47 | LL | static c: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected 2 lifetime parameters @@ -65,20 +37,7 @@ LL | static c: RefCell>>>> = | +++++++++++++++++ error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:33:47 - | -LL | / thread_local! { -LL | | static c: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ expected 2 lifetime parameters -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:38:44 + --> $DIR/missing-lifetime-specifier.rs:40:44 | LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | ^ ^ expected 2 lifetime parameters @@ -91,23 +50,8 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | static d: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ +++++++++++++++++ -error[E0106]: missing lifetime specifiers - --> $DIR/missing-lifetime-specifier.rs:38:44 - | -LL | / thread_local! { -LL | | static d: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ ^ expected 2 lifetime parameters - | | | - | | expected named lifetime parameter -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 4 lifetimes it is borrowed from - error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:48:44 + --> $DIR/missing-lifetime-specifier.rs:49:44 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^ expected named lifetime parameter @@ -118,22 +62,8 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++ -error[E0106]: missing lifetime specifier - --> $DIR/missing-lifetime-specifier.rs:48:44 - | -LL | / thread_local! { -LL | | static f: RefCell>>>> = RefCell::new(HashMap::new()); - | | ^ expected named lifetime parameter -LL | | -LL | | -LL | | -LL | | } - | |_- - | - = help: this function's return type contains a borrowed value, but the signature does not say which one of `init`'s 3 lifetimes it is borrowed from - error[E0107]: union takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:44:44 + --> $DIR/missing-lifetime-specifier.rs:45:44 | LL | static e: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -141,7 +71,7 @@ LL | static e: RefCell>>>> = RefCell: | expected 2 lifetime arguments | note: union defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:16:11 + --> $DIR/missing-lifetime-specifier.rs:21:11 | LL | pub union Qux<'t, 'k, I> { | ^^^ -- -- @@ -151,7 +81,7 @@ LL | static e: RefCell>>>> = | +++++++++ error[E0107]: trait takes 2 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/missing-lifetime-specifier.rs:48:45 + --> $DIR/missing-lifetime-specifier.rs:49:45 | LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | ^^^ ------- supplied 1 lifetime argument @@ -159,7 +89,7 @@ LL | static f: RefCell>>>> = RefCell | expected 2 lifetime arguments | note: trait defined here, with 2 lifetime parameters: `'t`, `'k` - --> $DIR/missing-lifetime-specifier.rs:20:7 + --> $DIR/missing-lifetime-specifier.rs:25:7 | LL | trait Tar<'t, 'k, I> {} | ^^^ -- -- @@ -168,7 +98,7 @@ help: add missing lifetime argument LL | static f: RefCell>>>> = RefCell::new(HashMap::new()); | +++++++++ -error: aborting due to 12 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`.