什么是Rust中的“序列点”/“排序前”规则?

时间:2014-07-01 11:02:20

标签: c++ rust

Rust中有哪些规则,类似于规则,这里描述了{+ 3}}对于C ++?

现在我从经验上发现了那个 1)函数的参数按直接顺序进行评估 2)所有带副作用的内置操作(=,+ =, - =等)返回单位,因此很难(但可能)编写表达式,这将在C ++中显示UB。
一个例子:

let mut a = 1i;
let b = 2i;
let c = 3i;
let d = (a = b) == (a = c); // What is a? (a is actually 3)

3)似乎函数调用按照C ++中的顺序排序 4)似乎内置操作被排序,好像它们是函数(方法)调用,即评估顺序与运算符优先级相关

我的结论是否正确?什么是确切的评估模型?

1 个答案:

答案 0 :(得分:5)

我不相信它已被明确定义,我对the manual的嘲讽没有发现任何事情。但是,我可以保证这些东西不会是未定义的行为(Rust明确地避免在unsafe代码之外的UB),如果它不是“从左到右”,那么我会感到惊讶,也就是说,顺序你推断出来了。虽然,#6268的分辨率可能导致最后评估接收器的方法(或者可能不是,这只是一种可能性)。

我打开了#15300


顺便说一下,如果你正在研究这个问题,你可以让编译器用精确评估顺序拆分出漂亮的控制流图(注意。这是所有内部API,因此不能依赖于,使编译器崩溃的可能性比正常情况高,并且不会隐藏编译器实现细节:它主要是为rustc)工作的人设计的。

正如@pnkfelix指出的那样,编译器没有使用CFG进行代码生成(截至2014-07-02),这意味着CFG不能保证准确无误。 < / p>

E.g。以@ ChrisMorgan的一个例子中的一个被剥离的版本为例:

fn foo(_: (), x: int) -> int {
    x
}

fn main() {
    let mut a = 1;
    { // A
        a * foo(a = 3, a)
    };
}

我们希望编译器在一些块(即{ ... })中拆分语句/表达式的控制流图,这可以通过编译器的--pretty flowgraph=<nodeid>选项来完成,但是对于那个我们需要拥有我们感兴趣的块的ID。在这种情况下,我们想要的块是A。要使用rustc --pretty expanded,identified编译id(请注意,仅使用identified是一个毫无意义的遗物:现在只在宏扩展后才分配ID):

#![feature(phase)]
#![no_std]
#![feature(globs)]
#[phase(plugin, link)]
extern crate std = "std#0.11.0-pre";
extern crate native = "native#0.11.0-pre";
use std::prelude::*;
fn foo(_ /* pat 7 */: (), x /* pat 11 */: int) -> int { (x /* 15 */) } /*
block 14 */ /* 4 */

fn main() {
    let mut a /* pat 22 */ = (1 /* 23 */);
    ({
         ((a /* 28 */) *
             ((foo /* 30
                  */)(((a /* 32 */) = (3 /* 33 */) /* 31 */), (a /* 34 */)) /*
                 29 */) /* 27 */)
     } /* block 26 */ /* 25 */);
} /* block 18 */ /* 16 */

很多肮脏的内部垃圾,但我们需要的是评论/* block 26 */rustc --pretty flowgraph=26给出一个点文件,呈现如下。您可以参考上面标识符的注释源来准确地处理每个表达式(id = ..位):

CFG

(对不起,显然这段代码没有分支,因此只是一长串操作。)

FWIW,该表达式的计算结果为9,而我期待3(并且控制流程图确认在RHS之后正在评估LHS上的孤立a,包括a = 3那里。我在#15300( e:和现在isolated it to a strange difference in evaluation order)上提出了这个问题。