Rust: 提前报告 const 索引越界条件

创建于 2012-08-10  ·  24评论  ·  资料来源: rust-lang/rust

目前,如果您尝试对 const 向量进行索引,在 const expr 中,我们会报告编译后期的边界溢出——在翻译期间。 我们应该更早地注意到它,在 const 评估阶段。

A-diagnostics C-cleanup P-low T-compiler

最有用的评论

是的,我认为我们在 6 年后解决了这个问题 :laughing:

所有24条评论

显然,这产生的“错误”不会导致编译失败——将生成代码并且 rustc 将返回 0。LLVM 会抱怨索引是否物理上越界,目前这总是等价于越界index (我认为),但对于 const 切片来说情况并非如此。

这在测试期间尤其出乎意料,它(显然除了快速检查?)在成功时不显示输出/错误。

对 0.6 不重要; 去里程碑

提名里程碑 5,生产就绪

接受生产就绪里程碑

作为我认为这是在谈论的一个例子:

static a: &'static [int] = &[];
static b: int = a[1];

fn main() {}

产量

$ rustc foo.rs
foo.rs:2:16: 2:19 error: const index-expr is out of bounds
foo.rs:2 static b: int = a[1];
                         ^~~
Assertion failed: (ReqTy && "extractvalue indices invalid!"), function getExtractValue, file ../../../../src/llvm/lib/IR/Constants.cpp, line 1982.
zsh: abort      rustc foo.rs

我们根本就遇到了 LLVM 断言,这似乎很糟糕。

接受 P 低。

分类: @alexcrichton的(9 个月大)示例在语法上仍然有效(是的!)并且仍然因该断言而失败(嘘!)。

我相信这是固定的

如果您更新@alexcrichton的示例:

#![allow(dead_code)]
const A: &'static [usize] = &[];
const B: usize = A[1];

fn main() {}

它现在编译成功,没有任何抱怨

只有当您尝试使用无效值B时, rustc才会遇到问题。

#![allow(dead_code)]
const A: &'static [usize] = &[];
const B: usize = A[1];

fn main() {
    println!("B={}", B);
}
<anon>:3:18: 3:22 error: const index-expr is out of bounds
<anon>:3 const B: usize = A[1];
                          ^~~~
rustc: /home/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/lib/IR/Constants.cpp:2174: static llvm::Constant* llvm::ConstantExpr::getExtractValue(llvm::Constant*, llvm::ArrayRef<unsigned int>, llvm::Type*): Assertion `ReqTy && "extractvalue indices invalid!"' failed.
Aborted (core dumped)
playpen: application terminated with error code 134

错误:无法按值引用其他静态,请改用地址运算符或常量

是的,看起来不错。

等等, @JustAPerson的例子对我来说似乎不太好。 重新开放。

我认为现在已经解决了这个问题:

$ cat foo.rs
#![allow(dead_code)]
const A: &'static [usize] = &[];
const B: usize = A[1];

fn main() {
    println!("B={}", B);
}
$ rustc foo.rs
foo.rs:3:18: 3:22 error: const index-expr is out of bounds
foo.rs:3 const B: usize = A[1];
                          ^~~~
error: aborting due to previous error
$ rustc --version
rustc 1.0.0-dev (a691f1eef 2015-04-15) (built 2015-04-15)

因此,在过去 11 天的某个时间:

hello.rs:3:18: 3:22 error: const index-expr is out of bounds
hello.rs:3 const B: usize = A[1];
                            ^~~~
rustc: /home/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/include/llvm/Support/Casting.h:237: typename llvm::cast_retty<X, Y*>::ret_type llvm::cast(Y*) [with X = llvm::SequentialType; Y = llvm::Type; typename llvm::cast_retty<X, Y*>::ret_type = llvm::SequentialType*]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.

soooo 好像已经退步了?

呃,这是我的错误。 我使用在禁用 LLVM 断言的情况下编译的工具链制作了上述报告(我不知道这是默认设置)。

(你好,我正在努力帮助新人更容易理解 E-easy 问题:smile_cat:)

我使用在禁用 LLVM 断言的情况下编译的工具链制作了上述报告(我不知道这是默认设置)。

听起来为了重现此问题,您必须使用使用非默认设置编译的工具链? 如果是这样,如何去做呢?

在这里应该发生的事情有点令人困惑,所有关闭重新开放的事情都在进行。 有人可以澄清预期的行为是什么以及它与当前行为有何不同?

./configure --enable-llvm-assertions

但是,您可以每晚使用 Rust,因为在那里启用了 LLVM 断言。 http://is.gd/X2RztV仍然无法在夜间断言:

<anon>:2:17: 2:21 error: const index-expr is out of bounds
<anon>:2 static b: i32 = a[1];
                         ^~~~
rustc: /home/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/llvm/include/llvm/Support/Casting.h:237: typename llvm::cast_retty<X, Y*>::ret_type llvm::cast(Y*) [with X = llvm::SequentialType; Y = llvm::Type; typename llvm::cast_retty<X, Y*>::ret_type = llvm::SequentialType*]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.
Aborted (core dumped)
playpen: application terminated with error code 134

看起来我在 2013 年苦恼的事情已经解决了:如果 rustc 给出const index-expr is out of bounds错误,即使 LLVM 断言被禁用,它也会以失败状态退出(并且不写入输出文件)。 我认为翻译过去无法访问检查所做的会话/错误内容,因为它不应该能够找到检查遗漏的错误? 但现在看来不是这样了。

我过去的自己提到的另一件事……听起来好像我们过去只是将索引传递给 LLVM,即使我们知道它超出范围,但现在我们构造了一个undef 。 但无论如何,这部分都得到了处理,因为现在错误得到了正确处理。

至于我们今天看到的 LLVM 断言,我有一个猜测。 报告错误后,我们这样做:

C_undef(type_of::type_of(cx, bt).element_type())

我认为我们想要这样的东西(未经测试):

C_undef(val_ty(arr).element_type())

因为如果被索引的值是切片或指针,那么bt (它的类型)将不会被数组类型表示,所以element_type将失败。

但是@graydon打开这个问题的目的,以及引用这个问题的代码中的注释建议做什么,是将该检查移动到编译的早期阶段。 看起来#25370/#25570 可能会或多或少地实现这一目标?

我认为现在已经完全解决了。 @oli-obk ?

不行,需要在check_const解决,需要把当前报告位置改成bug位置

@oli-obk 这是一个简单的解决方法,如果是这样,您是否想指导它并留下一些提示,以便新人可以尝试破解它?

虽然这是一个简单的修复(你基本上可以窃取 https://github.com/rust-lang/rust/blob/master/src/librustc/middle/check_const.rs#L470-L490 并以ExprIndex然后将 https://github.com/rust-lang/rust/blob/master/src/librustc_trans/trans/consts.rs#L708-L712 变成一个错误/ unimplemented!() )。

但这也将是一个重大变化,因为未使用的const ARR: [u32; 0] = []; const X: u32 = ARR[5];目前不会导致编译错误。 而check_const也会检查未使用的常量。

此外,它最终会进行两次所有 const 评估。 一旦它成为一个问题,就可以通过缓存常量来补救。

当然,可以通过简单地发出const_err lint 来补救重大变化。

这似乎已经“解决”了(可能是由于 MIRI),因为它现在在使用时抛出E0080 ,而不是 LLVM 断言。 如果访问权限未使用,它仍然会通过。

未使用 const 时丢失的 lint 将由https://github.com/rust-lang/rust/pull/50110修复

是的,我认为我们在 6 年后解决了这个问题 :laughing:

此页面是否有帮助?
0 / 5 - 0 等级