Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
d558353f
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,发现更多精彩内容 >>
提交
d558353f
编写于
8月 10, 2023
作者:
L
lcnr
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
make the provisional cache slightly less broken
上级
abc910be
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
59 addition
and
55 deletion
+59
-55
compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
...ler/rustc_trait_selection/src/solve/search_graph/cache.rs
+18
-14
compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+39
-26
tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs
tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs
+1
-1
tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr
...ui/traits/new-solver/cycles/inductive-not-on-stack.stderr
+1
-14
未找到文件。
compiler/rustc_trait_selection/src/solve/search_graph/cache.rs
浏览文件 @
d558353f
...
...
@@ -19,21 +19,25 @@ pub struct EntryIndex {}
#[derive(Debug,
Clone)]
pub
(
super
)
struct
ProvisionalEntry
<
'tcx
>
{
// In case we have a coinductive cycle, this is the
// the currently least restrictive result of this goal.
pub
(
super
)
response
:
QueryResult
<
'tcx
>
,
// In case of a cycle, the position of deepest stack entry involved
// in that cycle. This is monotonically decreasing in the stack as all
// elements between the current stack element in the deepest stack entry
// involved have to also be involved in that cycle.
//
// We can only move entries to the global cache once we're complete done
// with the cycle. If this entry has not been involved in a cycle,
// this is just its own depth.
/// In case we have a coinductive cycle, this is the
/// the current provisional result of this goal.
///
/// This starts out as `None` for all goals and gets to some
/// when the goal gets popped from the stack or we rerun evaluation
/// for this goal to reach a fixpoint.
pub
(
super
)
response
:
Option
<
QueryResult
<
'tcx
>>
,
/// In case of a cycle, the position of deepest stack entry involved
/// in that cycle. This is monotonically decreasing in the stack as all
/// elements between the current stack element in the deepest stack entry
/// involved have to also be involved in that cycle.
///
/// We can only move entries to the global cache once we're complete done
/// with the cycle. If this entry has not been involved in a cycle,
/// this is just its own depth.
pub
(
super
)
depth
:
StackDepth
,
// The goal for this entry. Should always be equal to the corresponding goal
// in the lookup table.
//
/
The goal for this entry. Should always be equal to the corresponding goal
//
/
in the lookup table.
pub
(
super
)
input
:
CanonicalInput
<
'tcx
>
,
}
...
...
@@ -92,7 +96,7 @@ pub(super) fn depth(&self, entry_index: EntryIndex) -> StackDepth {
self
.entries
[
entry_index
]
.depth
}
pub
(
super
)
fn
provisional_result
(
&
self
,
entry_index
:
EntryIndex
)
->
QueryResult
<
'tcx
>
{
pub
(
super
)
fn
provisional_result
(
&
self
,
entry_index
:
EntryIndex
)
->
Option
<
QueryResult
<
'tcx
>
>
{
self
.entries
[
entry_index
]
.response
}
}
compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
浏览文件 @
d558353f
...
...
@@ -13,7 +13,7 @@
use
rustc_middle
::
traits
::
solve
::{
CanonicalInput
,
Certainty
,
EvaluationCache
,
QueryResult
};
use
rustc_middle
::
ty
::
TyCtxt
;
use
rustc_session
::
Limit
;
use
std
::
{
collections
::
hash_map
::
Entry
,
mem
}
;
use
std
::
collections
::
hash_map
::
Entry
;
rustc_index
::
newtype_index!
{
pub
struct
StackDepth
{}
...
...
@@ -216,8 +216,8 @@ pub(super) fn with_new_goal(
cycle_participants
:
Default
::
default
(),
};
assert_eq!
(
self
.stack
.push
(
entry
),
depth
);
let
response
=
Self
::
response_no_constraints
(
tcx
,
input
,
Certainty
::
Yes
);
let
entry_index
=
cache
.entries
.push
(
ProvisionalEntry
{
respons
e
,
depth
,
input
});
let
entry_index
=
cache
.entries
.push
(
ProvisionalEntry
{
response
:
Non
e
,
depth
,
input
});
v
.insert
(
entry_index
);
}
// We have a nested goal which relies on a goal `root` deeper in the stack.
...
...
@@ -243,23 +243,31 @@ pub(super) fn with_new_goal(
root
.cycle_participants
.insert
(
e
.input
);
}
// NOTE: The goals on the stack aren't the only goals involved in this cycle.
// We can also depend on goals which aren't part of the stack but coinductively
// depend on the stack themselves. We already checked whether all the goals
// between these goals and their root on the stack. This means that as long as
// each goal in a cycle is checked for coinductivity by itself, simply checking
// the stack is enough.
if
self
.stack.raw
[
stack_depth
.index
()
..
]
.iter
()
.all
(|
g
|
g
.input.value.goal.predicate
.is_coinductive
(
tcx
))
{
// If we're in a coinductive cycle, we have to retry proving the current goal
// until we reach a fixpoint.
self
.stack
[
stack_depth
]
.has_been_used
=
true
;
return
cache
.provisional_result
(
entry_index
);
// If we're in a cycle, we have to retry proving the current goal
// until we reach a fixpoint.
self
.stack
[
stack_depth
]
.has_been_used
=
true
;
return
if
let
Some
(
result
)
=
cache
.provisional_result
(
entry_index
)
{
result
}
else
{
return
Self
::
response_no_constraints
(
tcx
,
input
,
Certainty
::
OVERFLOW
);
}
// If we don't have a provisional result yet, the goal has to
// still be on the stack.
let
mut
goal_on_stack
=
false
;
let
mut
is_coinductive
=
true
;
for
entry
in
self
.stack.raw
[
stack_depth
.index
()
..
]
.iter
()
.skip_while
(|
entry
|
entry
.input
!=
input
)
{
goal_on_stack
=
true
;
is_coinductive
&=
entry
.input.value.goal.predicate
.is_coinductive
(
tcx
);
}
debug_assert!
(
goal_on_stack
);
if
is_coinductive
{
Self
::
response_no_constraints
(
tcx
,
input
,
Certainty
::
Yes
)
}
else
{
Self
::
response_no_constraints
(
tcx
,
input
,
Certainty
::
OVERFLOW
)
}
};
}
}
...
...
@@ -288,15 +296,18 @@ pub(super) fn with_new_goal(
let
provisional_entry_index
=
*
cache
.lookup_table
.get
(
&
stack_entry
.input
)
.unwrap
();
let
provisional_entry
=
&
mut
cache
.entries
[
provisional_entry_index
];
let
prev_response
=
mem
::
replace
(
&
mut
provisional_entry
.response
,
response
);
if
stack_entry
.has_been_used
&&
prev_response
!=
response
{
// If so, remove all entries whose result depends on this goal
// from the provisional cache...
if
stack_entry
.has_been_used
&&
provisional_entry
.response
.map_or
(
true
,
|
r
|
r
!=
response
)
{
// If so, update the provisional result for this goal and remove
// all entries whose result depends on this goal from the provisional
// cache...
//
// That's not completely correct, as a nested goal can also
// That's not completely correct, as a nested goal can also
only
// depend on a goal which is lower in the stack so it doesn't
// actually depend on the current goal. This should be fairly
// rare and is hopefully not relevant for performance.
provisional_entry
.response
=
Some
(
response
);
#[allow(rustc::potential_query_instability)]
cache
.lookup_table
.retain
(|
_
key
,
index
|
*
index
<=
provisional_entry_index
);
cache
.entries
.truncate
(
provisional_entry_index
.index
()
+
1
);
...
...
@@ -315,8 +326,8 @@ pub(super) fn with_new_goal(
});
// We're now done with this goal. In case this goal is involved in a larger cycle
// do not remove it from the provisional cache and
do not add it to the global
// cache.
// do not remove it from the provisional cache and
update its provisional result.
//
We only add the root of cycles to the global
cache.
//
// It is not possible for any nested goal to depend on something deeper on the
// stack, as this would have also updated the depth of the current goal.
...
...
@@ -348,6 +359,8 @@ pub(super) fn with_new_goal(
dep_node
,
result
,
)
}
else
{
provisional_entry
.response
=
Some
(
result
);
}
result
...
...
tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs
浏览文件 @
d558353f
...
...
@@ -39,7 +39,7 @@ fn impls_ar<T: AR>() {}
fn
main
()
{
impls_a
::
<
()
>
();
//
~^ ERROR overflow evaluating the requirement `(): A`
//
FIXME(-Ztrait-solver=next): This is broken and should error.
impls_ar
::
<
()
>
();
//~^ ERROR overflow evaluating the requirement `(): AR`
...
...
tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr
浏览文件 @
d558353f
error[E0275]: overflow evaluating the requirement `(): A`
--> $DIR/inductive-not-on-stack.rs:41:5
|
LL | impls_a::<()>();
| ^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`)
note: required by a bound in `impls_a`
--> $DIR/inductive-not-on-stack.rs:25:15
|
LL | fn impls_a<T: A>() {}
| ^ required by this bound in `impls_a`
error[E0275]: overflow evaluating the requirement `(): AR`
--> $DIR/inductive-not-on-stack.rs:44:5
|
...
...
@@ -24,6 +11,6 @@ note: required by a bound in `impls_ar`
LL | fn impls_ar<T: AR>() {}
| ^^ required by this bound in `impls_ar`
error: aborting due to
2 previous errors
error: aborting due to
previous error
For more information about this error, try `rustc --explain E0275`.
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录