Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong result after short circuit in const_let #53515

Closed
dtolnay opened this issue Aug 20, 2018 · 10 comments
Closed

Wrong result after short circuit in const_let #53515

dtolnay opened this issue Aug 20, 2018 · 10 comments
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) C-bug Category: This is a bug.

Comments

@dtolnay
Copy link
Member

dtolnay commented Aug 20, 2018

I would expect this program to print true. As of rustc 1.30.0-nightly (33b923f 2018-08-18) it prints false. If you change const fn to fn then it prints true as expected.

#![feature(const_fn, const_let)]

const fn id(condition: bool) -> bool {
    let mut ret = true;

    // If condition is true, we short circuit and ret remains true.
    // If condition is false, we assign ret = false.
    let _ = condition || { ret = false; true };

    ret
}

fn main() {
    println!("{}", id(true));
}

Mentioning @oli-obk.
Mentioning const_let tracking issue #48821.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 20, 2018

Uh.... Apparently short circuiting operators don't exist in consts:

// FIXME(eddyb) use logical ops in constants when
// they can handle that kind of control-flow.
(hir::BinOpKind::And, hir::Constness::Const) => {
ExprKind::Binary {
op: BinOp::BitAnd,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
(hir::BinOpKind::Or, hir::Constness::Const) => {
ExprKind::Binary {
op: BinOp::BitOr,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
(hir::BinOpKind::And, hir::Constness::NotConst) => {
ExprKind::LogicalOp {
op: LogicalOp::And,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}
(hir::BinOpKind::Or, hir::Constness::NotConst) => {
ExprKind::LogicalOp {
op: LogicalOp::Or,
lhs: lhs.to_ref(),
rhs: rhs.to_ref(),
}
}

cc @eddyb

Since we did not have a way to observe short circuiting before the const_let feature, this transformation was correct.

Note that const fn is unnecessary:

#![feature(const_let)]

const COND: bool = true;

const X: bool = {
    let mut ret = true;

    // If COND is true, we short circuit and ret remains true.
    // If COND is false, we assign ret = false.
    let _ = COND || { ret = false; true };

    ret
};

fn main() {
    println!("{}", X);
}

@eddyb
Copy link
Member

eddyb commented Aug 20, 2018

Oh wow, whoops, at least we haven't stabilized anything that could observe this, whew.

@oli-obk oli-obk added C-bug Category: This is a bug. A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) labels Aug 20, 2018
@RalfJung
Copy link
Member

RalfJung commented Aug 23, 2018

So where is the tracking issue to allow if in const so that we can get rid of this hack?

if seems like a fairly innocent feature, even more so than loop.

@oli-obk
Copy link
Contributor

oli-obk commented Aug 23, 2018

if is very innocent if you ignore associated constant bodies and using associated constants in generic functions (where the associated constant is taken from a generic parameter).

@eddyb can we just mark any MIR containing SwitchInt to be NOT_PROMOTABLE, NEEDS_DROP and MUTABLE_INTERIOR (https://github.com/rust-lang/rust/blob/master/src/librustc_mir/transform/qualify_consts.rs#L51) even if they aren't? And then later loosen the constraints?

@RalfJung
Copy link
Member

if is very innocent if you ignore associated constant bodies and using associated constants in generic functions (where the associated constant is taken from a generic parameter).

What is the problem there?

@oli-obk
Copy link
Contributor

oli-obk commented Aug 23, 2018

We need to check whether

if T::FOO {
    Some(Cell::new(42))
} else {
    None
}

results in a Freeze value or not. While this example is fairly easy, once you get local variables and mutable state it gets very messy and needs a proper dataflow analysis.

Since (Some(Cell::new(42)), None).1 currently counts as "contains interior mutability", imo we can just say that if true { None } else { Some(Cell::new(42)) } also does

@RalfJung
Copy link
Member

Oh, we are already more precise than the type is?

@RalfJung
Copy link
Member

I think the place that has the best information about this kind of stuff is the sanity check. So if we make that part of the query for constants, it could probably do this.

@Centril
Copy link
Contributor

Centril commented Dec 30, 2018

(Noting that) this no longer compiles on nightly:

error[E0019]: constant function contains unimplemented expression type
 --> src/main.rs:8:13
  |
8 |     let _ = condition || { ret = false; true };
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

@RalfJung
Copy link
Member

So I think this can be closed because there's no longer an incorrect result. See #49146 for the tracking issue for control flow in const fn (that includes short-circuiting).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-const-eval Area: Constant evaluation, covers all const contexts (static, const fn, ...) C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

5 participants