Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
884ba4f1
R
Rust
项目概览
int
/
Rust
12 个月 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
Rust
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
884ba4f1
编写于
4月 12, 2020
作者:
D
Dylan MacKenzie
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Use internal mutability for predecessor cache
上级
00f677d8
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
133 addition
and
287 deletion
+133
-287
src/librustc_middle/mir/cache.rs
src/librustc_middle/mir/cache.rs
+0
-276
src/librustc_middle/mir/mod.rs
src/librustc_middle/mir/mod.rs
+66
-11
src/librustc_middle/mir/predecessors.rs
src/librustc_middle/mir/predecessors.rs
+67
-0
未找到文件。
src/librustc_middle/mir/cache.rs
已删除
100644 → 0
浏览文件 @
00f677d8
use
crate
::
ich
::
StableHashingContext
;
use
crate
::
mir
::{
BasicBlock
,
BasicBlockData
,
Body
,
LocalDecls
,
Location
,
Successors
};
use
rustc_data_structures
::
graph
::
dominators
::{
dominators
,
Dominators
};
use
rustc_data_structures
::
graph
::{
self
,
GraphPredecessors
,
GraphSuccessors
};
use
rustc_data_structures
::
stable_hasher
::{
HashStable
,
StableHasher
};
use
rustc_index
::
vec
::
IndexVec
;
use
rustc_serialize
::{
Decodable
,
Decoder
,
Encodable
,
Encoder
};
use
smallvec
::
SmallVec
;
use
std
::
iter
;
use
std
::
ops
::{
Deref
,
DerefMut
,
Index
,
IndexMut
};
use
std
::
vec
::
IntoIter
;
#[derive(Clone,
Debug)]
pub
struct
Cache
{
// Typically 95%+ of the inner vectors have 4 or fewer elements.
predecessors
:
Option
<
IndexVec
<
BasicBlock
,
SmallVec
<
[
BasicBlock
;
4
]
>>>
,
}
impl
rustc_serialize
::
Encodable
for
Cache
{
fn
encode
<
S
:
Encoder
>
(
&
self
,
s
:
&
mut
S
)
->
Result
<
(),
S
::
Error
>
{
Encodable
::
encode
(
&
(),
s
)
}
}
impl
rustc_serialize
::
Decodable
for
Cache
{
fn
decode
<
D
:
Decoder
>
(
d
:
&
mut
D
)
->
Result
<
Self
,
D
::
Error
>
{
Decodable
::
decode
(
d
)
.map
(|
_
v
:
()|
Self
::
new
())
}
}
impl
<
'a
>
HashStable
<
StableHashingContext
<
'a
>>
for
Cache
{
fn
hash_stable
(
&
self
,
_
:
&
mut
StableHashingContext
<
'a
>
,
_
:
&
mut
StableHasher
)
{
// Do nothing.
}
}
impl
Cache
{
pub
fn
new
()
->
Self
{
Self
{
predecessors
:
None
}
}
pub
fn
invalidate_predecessors
(
&
mut
self
)
{
// FIXME: consider being more fine-grained
self
.predecessors
=
None
;
}
pub
fn
ensure_predecessors
(
&
mut
self
,
body
:
&
Body
<
'_
>
)
{
if
self
.predecessors
.is_none
()
{
let
mut
result
=
IndexVec
::
from_elem
(
smallvec!
[],
body
.basic_blocks
());
for
(
bb
,
data
)
in
body
.basic_blocks
()
.iter_enumerated
()
{
if
let
Some
(
ref
term
)
=
data
.terminator
{
for
&
tgt
in
term
.successors
()
{
result
[
tgt
]
.push
(
bb
);
}
}
}
self
.predecessors
=
Some
(
result
)
}
}
/// This will recompute the predecessors cache if it is not available
fn
predecessors
(
&
mut
self
,
body
:
&
Body
<
'_
>
,
)
->
&
IndexVec
<
BasicBlock
,
SmallVec
<
[
BasicBlock
;
4
]
>>
{
self
.ensure_predecessors
(
body
);
self
.predecessors
.as_ref
()
.unwrap
()
}
fn
unwrap_predecessors_for
(
&
self
,
bb
:
BasicBlock
)
->
&
[
BasicBlock
]
{
&
self
.predecessors
.as_ref
()
.unwrap
()[
bb
]
}
fn
unwrap_predecessor_locations
<
'a
>
(
&
'a
self
,
loc
:
Location
,
body
:
&
'a
Body
<
'a
>
,
)
->
impl
Iterator
<
Item
=
Location
>
+
'a
{
let
if_zero_locations
=
if
loc
.statement_index
==
0
{
let
predecessor_blocks
=
self
.unwrap_predecessors_for
(
loc
.block
);
let
num_predecessor_blocks
=
predecessor_blocks
.len
();
Some
(
(
0
..
num_predecessor_blocks
)
.map
(
move
|
i
|
predecessor_blocks
[
i
])
.map
(
move
|
bb
|
body
.terminator_loc
(
bb
)),
)
}
else
{
None
};
let
if_not_zero_locations
=
if
loc
.statement_index
==
0
{
None
}
else
{
Some
(
Location
{
block
:
loc
.block
,
statement_index
:
loc
.statement_index
-
1
})
};
if_zero_locations
.into_iter
()
.flatten
()
.chain
(
if_not_zero_locations
)
}
pub
fn
basic_blocks_mut
<
'a
,
'tcx
>
(
&
mut
self
,
body
:
&
'a
mut
Body
<
'tcx
>
,
)
->
&
'a
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
{
debug!
(
"bbm: Clearing predecessors cache for body at: {:?}"
,
body
.span
.data
());
self
.invalidate_predecessors
();
&
mut
body
.basic_blocks
}
pub
fn
basic_blocks_and_local_decls_mut
<
'a
,
'tcx
>
(
&
mut
self
,
body
:
&
'a
mut
Body
<
'tcx
>
,
)
->
(
&
'a
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
,
&
'a
mut
LocalDecls
<
'tcx
>
)
{
debug!
(
"bbaldm: Clearing predecessors cache for body at: {:?}"
,
body
.span
.data
());
self
.invalidate_predecessors
();
(
&
mut
body
.basic_blocks
,
&
mut
body
.local_decls
)
}
}
#[derive(Clone,
Debug,
HashStable,
RustcEncodable,
RustcDecodable,
TypeFoldable)]
pub
struct
BodyAndCache
<
'tcx
>
{
body
:
Body
<
'tcx
>
,
cache
:
Cache
,
}
impl
BodyAndCache
<
'tcx
>
{
pub
fn
new
(
body
:
Body
<
'tcx
>
)
->
Self
{
Self
{
body
,
cache
:
Cache
::
new
()
}
}
}
#[macro_export]
macro_rules!
read_only
{
(
$body:expr
)
=>
{{
$body
.ensure_predecessors
();
$body
.unwrap_read_only
()
}};
}
impl
BodyAndCache
<
'tcx
>
{
pub
fn
ensure_predecessors
(
&
mut
self
)
{
self
.cache
.ensure_predecessors
(
&
self
.body
);
}
pub
fn
predecessors
(
&
mut
self
)
->
&
IndexVec
<
BasicBlock
,
SmallVec
<
[
BasicBlock
;
4
]
>>
{
self
.cache
.predecessors
(
&
self
.body
)
}
pub
fn
unwrap_read_only
(
&
self
)
->
ReadOnlyBodyAndCache
<
'_
,
'tcx
>
{
ReadOnlyBodyAndCache
::
new
(
&
self
.body
,
&
self
.cache
)
}
pub
fn
basic_blocks_mut
(
&
mut
self
)
->
&
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
{
self
.cache
.basic_blocks_mut
(
&
mut
self
.body
)
}
pub
fn
basic_blocks_and_local_decls_mut
(
&
mut
self
,
)
->
(
&
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
,
&
mut
LocalDecls
<
'tcx
>
)
{
self
.cache
.basic_blocks_and_local_decls_mut
(
&
mut
self
.body
)
}
}
impl
<
'tcx
>
Index
<
BasicBlock
>
for
BodyAndCache
<
'tcx
>
{
type
Output
=
BasicBlockData
<
'tcx
>
;
fn
index
(
&
self
,
index
:
BasicBlock
)
->
&
BasicBlockData
<
'tcx
>
{
&
self
.body
[
index
]
}
}
impl
<
'tcx
>
IndexMut
<
BasicBlock
>
for
BodyAndCache
<
'tcx
>
{
fn
index_mut
(
&
mut
self
,
index
:
BasicBlock
)
->
&
mut
Self
::
Output
{
&
mut
self
.basic_blocks_mut
()[
index
]
}
}
impl
<
'tcx
>
Deref
for
BodyAndCache
<
'tcx
>
{
type
Target
=
Body
<
'tcx
>
;
fn
deref
(
&
self
)
->
&
Self
::
Target
{
&
self
.body
}
}
impl
<
'tcx
>
DerefMut
for
BodyAndCache
<
'tcx
>
{
fn
deref_mut
(
&
mut
self
)
->
&
mut
Self
::
Target
{
&
mut
self
.body
}
}
#[derive(Copy,
Clone,
Debug)]
pub
struct
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
body
:
&
'a
Body
<
'tcx
>
,
cache
:
&
'a
Cache
,
}
impl
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
fn
new
(
body
:
&
'a
Body
<
'tcx
>
,
cache
:
&
'a
Cache
)
->
Self
{
assert
!
(
cache
.predecessors
.is_some
(),
"Cannot construct ReadOnlyBodyAndCache without computed predecessors"
);
Self
{
body
,
cache
}
}
pub
fn
predecessors
(
&
self
)
->
&
IndexVec
<
BasicBlock
,
SmallVec
<
[
BasicBlock
;
4
]
>>
{
self
.cache.predecessors
.as_ref
()
.unwrap
()
}
pub
fn
predecessors_for
(
&
self
,
bb
:
BasicBlock
)
->
&
[
BasicBlock
]
{
self
.cache
.unwrap_predecessors_for
(
bb
)
}
pub
fn
predecessor_locations
(
&
self
,
loc
:
Location
)
->
impl
Iterator
<
Item
=
Location
>
+
'_
{
self
.cache
.unwrap_predecessor_locations
(
loc
,
self
.body
)
}
pub
fn
basic_blocks
(
&
self
)
->
&
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
{
&
self
.body.basic_blocks
}
pub
fn
dominators
(
&
self
)
->
Dominators
<
BasicBlock
>
{
dominators
(
self
)
}
}
impl
graph
::
DirectedGraph
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
type
Node
=
BasicBlock
;
}
impl
graph
::
GraphPredecessors
<
'graph
>
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
type
Item
=
BasicBlock
;
type
Iter
=
IntoIter
<
BasicBlock
>
;
}
impl
graph
::
WithPredecessors
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
fn
predecessors
(
&
self
,
node
:
Self
::
Node
)
->
<
Self
as
GraphPredecessors
<
'_
>>
::
Iter
{
self
.cache
.unwrap_predecessors_for
(
node
)
.to_vec
()
.into_iter
()
}
}
impl
graph
::
WithNumNodes
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
fn
num_nodes
(
&
self
)
->
usize
{
self
.body
.num_nodes
()
}
}
impl
graph
::
WithStartNode
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
fn
start_node
(
&
self
)
->
Self
::
Node
{
self
.body
.start_node
()
}
}
impl
graph
::
WithSuccessors
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
fn
successors
(
&
self
,
node
:
Self
::
Node
)
->
<
Self
as
GraphSuccessors
<
'_
>>
::
Iter
{
self
.body
.successors
(
node
)
}
}
impl
<
'a
,
'b
,
'tcx
>
graph
::
GraphSuccessors
<
'b
>
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
type
Item
=
BasicBlock
;
type
Iter
=
iter
::
Cloned
<
Successors
<
'b
>>
;
}
impl
Deref
for
ReadOnlyBodyAndCache
<
'a
,
'tcx
>
{
type
Target
=
&
'a
Body
<
'tcx
>
;
fn
deref
(
&
self
)
->
&
Self
::
Target
{
&
self
.body
}
}
CloneTypeFoldableAndLiftImpls!
{
Cache
,
}
src/librustc_middle/mir/mod.rs
浏览文件 @
884ba4f1
...
...
@@ -21,27 +21,28 @@
pub
use
rustc_ast
::
ast
::
Mutability
;
use
rustc_ast
::
ast
::
Name
;
use
rustc_data_structures
::
fx
::
FxHashSet
;
use
rustc_data_structures
::
graph
::
dominators
::
Dominators
;
use
rustc_data_structures
::
graph
::
dominators
::
{
dominators
,
Dominators
}
;
use
rustc_data_structures
::
graph
::{
self
,
GraphSuccessors
};
use
rustc_data_structures
::
sync
::
MappedLockGuard
;
use
rustc_index
::
bit_set
::
BitMatrix
;
use
rustc_index
::
vec
::{
Idx
,
IndexVec
};
use
rustc_macros
::
HashStable
;
use
rustc_serialize
::{
Decodable
,
Encodable
};
use
rustc_span
::
symbol
::
Symbol
;
use
rustc_span
::{
Span
,
DUMMY_SP
};
use
smallvec
::
SmallVec
;
use
std
::
borrow
::
Cow
;
use
std
::
fmt
::{
self
,
Debug
,
Display
,
Formatter
,
Write
};
use
std
::
ops
::
Index
;
use
std
::
ops
::
{
Index
,
IndexMut
}
;
use
std
::
slice
;
use
std
::{
iter
,
mem
,
option
};
pub
use
self
::
cache
::{
BodyAndCache
,
ReadOnlyBodyAndCache
};
use
self
::
predecessors
::{
PredecessorCache
,
Predecessors
};
pub
use
self
::
query
::
*
;
pub
use
crate
::
read_only
;
mod
cache
;
pub
mod
interpret
;
pub
mod
mono
;
mod
predecessors
;
mod
query
;
pub
mod
tcx
;
pub
mod
traversal
;
...
...
@@ -108,7 +109,7 @@ pub struct Body<'tcx> {
pub
yield_ty
:
Option
<
Ty
<
'tcx
>>
,
/// Generator drop glue.
pub
generator_drop
:
Option
<
Box
<
Body
AndCache
<
'tcx
>>>
,
pub
generator_drop
:
Option
<
Box
<
Body
<
'tcx
>>>
,
/// The layout of a generator. Produced by the state transformation.
pub
generator_layout
:
Option
<
GeneratorLayout
<
'tcx
>>
,
...
...
@@ -164,6 +165,8 @@ pub struct Body<'tcx> {
/// implementation without the flag hid this situation silently.
/// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components.
pub
ignore_interior_mut_in_const_validation
:
bool
,
pub
predecessor_cache
:
PredecessorCache
,
}
impl
<
'tcx
>
Body
<
'tcx
>
{
...
...
@@ -202,6 +205,7 @@ pub fn new(
span
,
ignore_interior_mut_in_const_validation
:
false
,
control_flow_destroyed
,
predecessor_cache
:
PredecessorCache
::
new
(),
}
}
...
...
@@ -227,6 +231,7 @@ pub fn new_cfg_only(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) ->
generator_kind
:
None
,
var_debug_info
:
Vec
::
new
(),
ignore_interior_mut_in_const_validation
:
false
,
predecessor_cache
:
PredecessorCache
::
new
(),
}
}
...
...
@@ -235,6 +240,25 @@ pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
&
self
.basic_blocks
}
#[inline]
pub
fn
basic_blocks_mut
(
&
mut
self
)
->
&
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
{
// Because the user could mutate basic block terminators via this reference, we need to
// invalidate the predecessor cache.
//
// FIXME: Use a finer-grained API for this, so only transformations that alter terminators
// invalidate the predecessor cache.
self
.predecessor_cache
.invalidate
();
&
mut
self
.basic_blocks
}
#[inline]
pub
fn
basic_blocks_and_local_decls_mut
(
&
mut
self
,
)
->
(
&
mut
IndexVec
<
BasicBlock
,
BasicBlockData
<
'tcx
>>
,
&
mut
LocalDecls
<
'tcx
>
)
{
self
.predecessor_cache
.invalidate
();
(
&
mut
self
.basic_blocks
,
&
mut
self
.local_decls
)
}
/// Returns `true` if a cycle exists in the control-flow graph that is reachable from the
/// `START_BLOCK`.
pub
fn
is_cfg_cyclic
(
&
self
)
->
bool
{
...
...
@@ -365,6 +389,23 @@ pub fn return_ty(&self) -> Ty<'tcx> {
pub
fn
terminator_loc
(
&
self
,
bb
:
BasicBlock
)
->
Location
{
Location
{
block
:
bb
,
statement_index
:
self
[
bb
]
.statements
.len
()
}
}
pub
fn
predecessors_for
(
&
self
,
bb
:
BasicBlock
,
)
->
impl
std
::
ops
::
Deref
<
Target
=
SmallVec
<
[
BasicBlock
;
4
]
>>
+
'_
{
let
predecessors
=
self
.predecessor_cache
.compute
(
&
self
.basic_blocks
);
MappedLockGuard
::
map
(
predecessors
,
|
preds
|
&
mut
preds
[
bb
])
}
pub
fn
predecessors
(
&
self
)
->
impl
std
::
ops
::
Deref
<
Target
=
Predecessors
>
+
'_
{
self
.predecessor_cache
.compute
(
&
self
.basic_blocks
)
}
#[inline]
pub
fn
dominators
(
&
self
)
->
Dominators
<
BasicBlock
>
{
dominators
(
self
)
}
}
#[derive(Copy,
Clone,
Debug,
RustcEncodable,
RustcDecodable,
HashStable)]
...
...
@@ -387,6 +428,13 @@ fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
}
}
impl
<
'tcx
>
IndexMut
<
BasicBlock
>
for
Body
<
'tcx
>
{
#[inline]
fn
index_mut
(
&
mut
self
,
index
:
BasicBlock
)
->
&
mut
BasicBlockData
<
'tcx
>
{
&
mut
self
.basic_blocks_mut
()[
index
]
}
}
#[derive(Copy,
Clone,
Debug,
HashStable,
TypeFoldable)]
pub
enum
ClearCrossCrate
<
T
>
{
Clear
,
...
...
@@ -2613,6 +2661,17 @@ impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> {
type
Iter
=
iter
::
Cloned
<
Successors
<
'b
>>
;
}
impl
graph
::
GraphPredecessors
<
'graph
>
for
Body
<
'tcx
>
{
type
Item
=
BasicBlock
;
type
Iter
=
smallvec
::
IntoIter
<
[
BasicBlock
;
4
]
>
;
}
impl
graph
::
WithPredecessors
for
Body
<
'tcx
>
{
fn
predecessors
(
&
self
,
node
:
Self
::
Node
)
->
<
Self
as
graph
::
GraphPredecessors
<
'_
>>
::
Iter
{
self
.predecessors_for
(
node
)
.clone
()
.into_iter
()
}
}
/// `Location` represents the position of the start of the statement; or, if
/// `statement_index` equals the number of statements, then the start of the
/// terminator.
...
...
@@ -2642,11 +2701,7 @@ pub fn successor_within_block(&self) -> Location {
}
/// Returns `true` if `other` is earlier in the control flow graph than `self`.
pub
fn
is_predecessor_of
<
'tcx
>
(
&
self
,
other
:
Location
,
body
:
ReadOnlyBodyAndCache
<
'_
,
'tcx
>
,
)
->
bool
{
pub
fn
is_predecessor_of
<
'tcx
>
(
&
self
,
other
:
Location
,
body
:
&
Body
<
'tcx
>
)
->
bool
{
// If we are in the same block as the other location and are an earlier statement
// then we are a predecessor of `other`.
if
self
.block
==
other
.block
&&
self
.statement_index
<
other
.statement_index
{
...
...
src/librustc_middle/mir/predecessors.rs
0 → 100644
浏览文件 @
884ba4f1
use
rustc_data_structures
::
stable_hasher
::{
HashStable
,
StableHasher
};
use
rustc_data_structures
::
sync
::{
Lock
,
LockGuard
,
MappedLockGuard
};
use
rustc_index
::
vec
::
IndexVec
;
use
rustc_serialize
as
serialize
;
use
smallvec
::
SmallVec
;
use
crate
::
mir
::{
BasicBlock
,
BasicBlockData
};
// Typically 95%+ of basic blocks have 4 or fewer predecessors.
pub
type
Predecessors
=
IndexVec
<
BasicBlock
,
SmallVec
<
[
BasicBlock
;
4
]
>>
;
#[derive(Clone,
Debug)]
pub
struct
PredecessorCache
{
cache
:
Lock
<
Option
<
Predecessors
>>
,
}
impl
PredecessorCache
{
pub
fn
new
()
->
Self
{
PredecessorCache
{
cache
:
Lock
::
new
(
None
)
}
}
pub
fn
invalidate
(
&
mut
self
)
{
*
self
.cache
.get_mut
()
=
None
;
}
pub
fn
compute
(
&
self
,
basic_blocks
:
&
IndexVec
<
BasicBlock
,
BasicBlockData
<
'_
>>
,
)
->
MappedLockGuard
<
'_
,
Predecessors
>
{
LockGuard
::
map
(
self
.cache
.lock
(),
|
cache
|
{
cache
.get_or_insert_with
(||
{
let
mut
preds
=
IndexVec
::
from_elem
(
SmallVec
::
new
(),
basic_blocks
);
for
(
bb
,
data
)
in
basic_blocks
.iter_enumerated
()
{
if
let
Some
(
term
)
=
&
data
.terminator
{
for
&
succ
in
term
.successors
()
{
preds
[
succ
]
.push
(
bb
);
}
}
}
preds
})
})
}
}
impl
serialize
::
Encodable
for
PredecessorCache
{
fn
encode
<
S
:
serialize
::
Encoder
>
(
&
self
,
s
:
&
mut
S
)
->
Result
<
(),
S
::
Error
>
{
serialize
::
Encodable
::
encode
(
&
(),
s
)
}
}
impl
serialize
::
Decodable
for
PredecessorCache
{
fn
decode
<
D
:
serialize
::
Decoder
>
(
d
:
&
mut
D
)
->
Result
<
Self
,
D
::
Error
>
{
serialize
::
Decodable
::
decode
(
d
)
.map
(|
_
v
:
()|
Self
::
new
())
}
}
impl
<
CTX
>
HashStable
<
CTX
>
for
PredecessorCache
{
fn
hash_stable
(
&
self
,
_
:
&
mut
CTX
,
_
:
&
mut
StableHasher
)
{
// do nothing
}
}
CloneTypeFoldableAndLiftImpls!
{
PredecessorCache
,
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录