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

Add is_const_eval intrinsic #64683

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/libcore/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,26 @@ extern "rust-intrinsic" {
/// Executes a breakpoint trap, for inspection by a debugger.
pub fn breakpoint();

/// Returns `true` during constant evaluation and `false` otherwise.
///
/// # Safety
///
/// This intrinsic allows breaking [referential transparency] in `const fn`
/// and is therefore `unsafe`.
///
/// Code that uses this intrinsic must be extremely careful to ensure that
/// `const fn`s remain referentially-transparent independently of when they
/// are evaluated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the reference of referential transparency here mostly unhelpful. At least, I would have no idea what exactly const code must (not) do, based on this description.

But also, this does not affect const-qualif. It doesn't actually introduce any referential intransparency. So personally I'd trim this down to "the code must do the same thing for both cases". Which leaves you wonder why this is even added.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that this is used in C++ to speed up the run-time code with SIMD and whatnot but that both code paths should observably behave same (for some notion of observational equivalence). https://en.cppreference.com/w/cpp/types/is_constant_evaluated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this SIMD code used run-time feature detection, couldn't be "just" make that report "no SIMD support" for const-eval to avoid needing any other magic intrinsic?

Either way though, this should be stated in the PR description.

Copy link
Contributor Author

@gnzlbg gnzlbg Sep 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this SIMD code used run-time feature detection, couldn't be "just" make that report "no SIMD support" for const-eval to avoid needing any other magic intrinsic?

How would that run-time feature detection thing be implemented ? 😉

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought that already existed for SIMD anyway?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_x86_feature_detected! has an if cfg!(miri) { false } branch in it, but it is not usable inside const fns. To be able to use the macro in const fns we'd need something like this, the macro uses inline assembly internally..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would that run-time feature detection thing be implemented ? wink

Not sure, but I don't think we need this intrinsic for it? (And if we do, the name should be is_miri.)

Is the problem just that you cannot do if in const fn? We are accumulating more and more bad hacks to work around that and I'd rather put the line down before we add intrinsics for this reason. You can for example already do

#[cfg(miri)]
const fn is_miri() { true }
#[cfg(not(miri))]
const fn is_miri() { false }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you have understood what the problem is here.

is_x86_feature_detected! needs to use inline assembly at run-time. If you want to call that from const fns, either const fns need to support inline assembly, or you need to provide a way to do something else at compile time.

If you want to "properly" emulate is_x86_feature_detected on miri then miri would need to support inline assembly. Right now, we just have a #[cfg(miri)] return false; that does nothing on miri instead. That could be improved with some "hook" that miri provides for doing run-time feature detection, that calls some function from the "miri run-time" that is implemented using inline assembly or similar. This has nothing to do with the problem being discussed here though, which is how to call is_x86_feature_detected! from a const fn, or more generally, how to be able to call efficient run-time code that uses "stuff that constant evaluation probably won't ever support" like inline assembly by providing an alternative slower implementation for constant evaluation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I completely misunderstood const fns scope and the plan is indeed to actually support inline assembly in const fns at some point, that is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I completely misunderstood const fns scope and the plan is indeed to actually support inline assembly in const fns at some point, that is.

😱: https://github.com/CraneStation/cranelift/issues/444#issuecomment-531574394

///
/// The Rust compiler assumes that it is sound to replace a call to a `const
/// fn` with the result produced by evaluating it at compile-time. If
/// evaluating the function at run-time were to produce a different result,
/// or have any other observable side-effects, the behavior is undefined.
///
/// [referential transparency]: https://en.wikipedia.org/wiki/Referential_transparency
#[cfg(not(boostrap_stdarch_ignore_this))]
pub fn is_const_eval() -> bool;
oli-obk marked this conversation as resolved.
Show resolved Hide resolved

/// The size of a type in bytes.
///
/// More specifically, this is the offset in bytes between successive
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_codegen_llvm/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
}

},
"is_const_eval" => {
self.const_bool(false)
},
"fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => {
match float_type_width(arg_tys[0]) {
Some(_width) =>
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,11 @@ crate fn eval_nullary_intrinsic<'tcx>(
def_id: DefId,
substs: SubstsRef<'tcx>,
) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> {
let tp_ty = substs.type_at(0);
let name = &*tcx.item_name(def_id).as_str();
if name == "is_const_eval" {
return Ok(ty::Const::from_bool(tcx, true));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this mean that it returns true in Miri, which seems odd (for now, anyway)?

This should query a machine flag to determine the intrinsic's return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't this mean that it returns true in Miri, which seems odd (for now, anyway)?

This returns true in miri. Were it to return false, miri would fail trying to do something it doesn't support.

Copy link
Contributor Author

@gnzlbg gnzlbg Sep 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is odd at all given that miri already provides cfg!(miri) which can be used to the same effect.

This should query a machine flag to determine the intrinsic's return value.

Does cfg!(miri) do this?

EDIT: users that want to do something else for miri can just use cfg(miri) on top of this, instead of making the result of this intrinsic be target dependent.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is that miri is more like codegen and not like const eval in its semantics. Cfgs flags are their own world independent of the runtime

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Miri is implementing Rust's run-time semantics as much as it can. So is_const_eval must return false.

If you actually want to check "is this a restricted interpreted environment that does not support SIMD", then you should pick an appropriate name for the intrinsic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Miri is implementing Rust's run-time semantics as much as it can. So is_const_eval must return false.

I could cfg(miri) is_const_eval to always return false, but then miri won't be able to execute any code for which is_const_eval makes sense using.

}
let tp_ty = substs.type_at(0);
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
Ok(match name {
"type_name" => {
let alloc = type_name::alloc_type_name(tcx, tp_ty);
Expand Down Expand Up @@ -94,6 +97,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

let intrinsic_name = &self.tcx.item_name(instance.def_id()).as_str()[..];
match intrinsic_name {
"is_const_eval" |
"min_align_of" |
"pref_align_of" |
"needs_drop" |
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,8 @@ impl Qualif for IsNotPromotable {
| "saturating_add"
| "saturating_sub"
| "transmute"
| "is_const_eval"
=> return true,

_ => {}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem) {
"rustc_peek" => (1, vec![param(0)], param(0)),
"panic_if_uninhabited" => (1, Vec::new(), tcx.mk_unit()),
"init" => (1, Vec::new(), param(0)),
"is_const_eval" => (0, Vec::new(), tcx.types.bool),
"uninit" => (1, Vec::new(), param(0)),
"forget" => (1, vec![param(0)], tcx.mk_unit()),
"transmute" => (2, vec![ param(0) ], param(1)),
Expand Down
16 changes: 16 additions & 0 deletions src/test/codegen/intrinsics/is_const_eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]
#![feature(core_intrinsics)]

use std::intrinsics::is_const_eval;

// CHECK-LABEL: @is_const_eval_test
#[no_mangle]
pub unsafe fn is_const_eval_test() -> bool {
// CHECK: %0 = alloca i8, align 1
// CHECK: store i8 0, i8* %0, align 1
// CHECK: %2 = trunc i8 %1 to i1
// CHECK: ret i1 %2
is_const_eval()
}
11 changes: 11 additions & 0 deletions src/test/ui/intrinsics/is_const_eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// run-pass

#![feature(core_intrinsics)]
use std::intrinsics::is_const_eval;

fn main() {
const X: bool = unsafe { is_const_eval() };
Copy link
Contributor Author

@gnzlbg gnzlbg Sep 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So AFAICT the only remaining issue is that this currently works due to #61495 allowing all intrinsics to run in const-contexts (notice that feature(const_fn) is not enabled!).

Even if we were to fix #61495, this would still "just work" behind the const_fn feature. That would allow it to "silently" be used in APIs exposed by libstd. I'm not sure how to fix that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is fixed as I imagine it, the const fn feature gate won't have an effect on it. It would only permit a whitelist of intrinsics and forbid all others outside unleash mode

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @oli-obk. Why would just "just fork" with const_fn? That gate won't make non-const-fn callable, and all intrinsics (except for a whitelist) should be considered non-const-fn.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the "why" issue is already resolved in the updated OP description and the new test that shows an use case that works with this, but wouldn't work without it.

let y = unsafe { is_const_eval() };
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(X, true);
assert_eq!(y, false);
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
}
34 changes: 34 additions & 0 deletions src/test/ui/intrinsics/is_const_eval_unleash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// run-pass
// only-x86_64
// compile-flags: -Zunleash-the-miri-inside-of-you

#![feature(core_intrinsics)]
use std::intrinsics::is_const_eval;
use std::arch::x86_64::*;
use std::mem::transmute;

const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
if unsafe { is_const_eval() } {
x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3]
} else {
unsafe {
let x = _mm_loadu_si128(&x as *const _ as *const _);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is essentially what I worried about. Such code will never ever be accepted by rustc AFAIK. If you want it for experimentation, that seems fine but ultimately pointless. Writing a const qualification that could accept this would be the motherload of unsoundness dangers. My suggestion with an intrinsic, that calls one of two functions given to it depending on the context, would be much more manageable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long version:

In contrast to C++, we statically prove that your const fn is sane. Of course we also have escape hatches via unsafe, but that's a different story. What your example has is an if condition that works differently whether you are const evaluating or codegenning. Now, this is not a problem by itself, but the problem arises with the else arm's content. That content contains statically provable nonconst code and since it's part of a const fn that will be errored about. I'm presuming the intent is to decide whether to run const checks depending on the arm that one is in. This may seem easy in the given example, but once you involve loops, short circuiting, matching, break and continue, this can get very complex to decide. Additionally even the simple version will complicate the const qualif check a lot, making one of the most fragile things keeping Rust's safety guarantees even more fragile. If there is a less complex version (and I believe there is), we should go for that one.

Copy link
Contributor Author

@gnzlbg gnzlbg Sep 23, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Yes, the "boolean intrinsic" approach implemented here would require the constant evaluator to only try to prove things about the branches that constant evaluation actually can reach. I can see how that could get nasty.

IIUC, what you are proposing is something like:

mod intrinsics {
    pub fn const_eval_select<T, U, V>(called_in_const: T, called_at_rt: U) -> V
        where T: const Fn() -> V, U: Fn() -> V;
}

that can be used to rewrite the example above as follows:

const fn eq_ct(x: [i32; 4], y: [i32; 4]) -> bool {
    x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3]
}

fn eq_rt(x: [i32; 4], y: [i32; 4]) -> bool {
    let x = _mm_loadu_si128(&x as *const _ as *const _);
    let y = _mm_loadu_si128(&y as *const _ as *const _);
    let r = _mm_cmpeq_epi32(x, y);
    let r = _mm_movemask_ps(transmute(r) );
    r == 0b1111
}

const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
    const_eval_select(|| eq_ct(x, y), || eq_rt(x, y)) 
    // unclear to me how to make these closures work well here
}

?

Is that correct? Maybe you could post about it in the tracking issue (rust-lang/const-eval#7) ? Your last comment there appeared to be in agreement with using a "boolean intrinsic", but it does have quite a bit of issues, so maybe we can move the discussion there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While closures would be neat, they seem an unnecessary complication of such a low level function

mod intrinsics {
    pub fn const_eval_select<ARG, F, G, RET>(arg: ARG called_in_const: F, called_at_rt: G) -> RET;
}

you could then use it as

const fn eq_ct((x, y): ([i32; 4], [i32; 4])) -> bool {
    x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3]
}

fn eq_rt((x, y): ([i32; 4], [i32; 4])) -> bool {
    let x = _mm_loadu_si128(&x as *const _ as *const _);
    let y = _mm_loadu_si128(&y as *const _ as *const _);
    let r = _mm_cmpeq_epi32(x, y);
    let r = _mm_movemask_ps(transmute(r) );
    r == 0b1111
}

const fn eq(x: [i32; 4], y: [i32; 4]) -> bool {
    const_eval_select((x, y), eq_ct, eq_rt)
    // unclear to me how to make these closures work well here
}

The const_eval_select function then compiles down to a call to eq_rt(arg) in llvm and is interpreted as a call to eq_ct(arg) in const eval

Copy link
Contributor Author

@gnzlbg gnzlbg Oct 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds great.

So how do I implement this? IIUC, I just add an intrinsic to typeck like I did here, and implement it with the "const semantics" in const-eval, and then add a different implementation for it in the llvm backend, and that's it ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the intrinsic checks would report errors on any illegal uses, but I don't know how easy that is to do

I can't imagine this is feasible, we'd need to analyze the "runtime code" and figure out if it does something non-const friendly, but we can't really tell much about that because the point of this feature is to be able to call SIMD intrinsics or inline assembly, which is essentially stuf we cannot reason about.

I'd be fine with saying that this intrinsic is unsafe to use, and making the user responsible for using it correctly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's not what I meant. I just mean that

  • the signature of the two functions needs to match,
  • the first function needs to be const fn,
  • the return type of both functions is the same as the generic RET parameter
  • the argument of both functions is the same type as the ARG parameter

The content of the functions is indeed left to the user as per the docs you already provided

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, gotcha. Hm, what about just saying:

mod intrinsics {
    pub fn const_eval_select<ARG, F, G, RET>(
        arg: ARG called_in_const: F, called_at_rt: G
    ) -> RET 
    where F: FnOnce(ARG) -> RET, G: FnOnce(ARG) -> RET
}

?

That way, typeck enforces that the arguments and return types match, and well, that they are function types. I don't know how to check that the first function is const in typeck though, but maybe it is possible to do during const qualif.

Copy link
Member

@RalfJung RalfJung Oct 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we wouldn't go for closures, to keep things simple? Then it would just be

pub fn const_eval_select<ARG, RET>(
    arg: ARG,
    called_in_const: fn(ARG) -> RET,
    called_at_rt: fn(ARG) -> RET,
) -> RET

Copy link
Contributor

@oli-obk oli-obk Oct 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

function pointers are not the same thing as the zst arguments and won't do us any favours here. The intrinsic would not be able to figure out which function to call and would have to insert function pointer calls and we can't have const function pointers. While all solvable problems, I'd rather not go down that road

I mean you can add Fn bounds as you suggestd, that should suffice indeed.

that they are function types

That's not checked, it could be a closure. Thus it needs an extra check to make sure only functions are used as generic arguments

let y = _mm_loadu_si128(&y as *const _ as *const _);
let r = _mm_cmpeq_epi32(x, y);
let r = _mm_movemask_ps(transmute(r) );
r == 0b1111
}
}
}

fn main() {
const X: bool = eq([0, 1, 2, 3], [0, 1, 2, 3]);
assert_eq!(X, true);
let x = eq([0, 1, 2, 3], [0, 1, 2, 3]);
assert_eq!(x, true);

const Y: bool = eq([0, 1, 2, 3], [0, 1, 3, 2]);
assert_eq!(Y, false);
let y = eq([0, 1, 2, 3], [0, 1, 3, 2]);
assert_eq!(y, false);
}
114 changes: 114 additions & 0 deletions src/test/ui/intrinsics/is_const_eval_unleash.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:11:17
|
LL | if unsafe { is_const_eval() } {
| ^^^^^^^^^^^^^^^

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:11:5
|
LL | / if unsafe { is_const_eval() } {
LL | | x[0] == y[0] && x[1] == y[1] && x[2] == y[2] && x[3] == y[3]
LL | | } else {
LL | | unsafe {
... |
LL | | }
LL | | }
| |_____^

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:26:5
|
LL | assert_eq!(X, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:26:5
|
LL | assert_eq!(X, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:26:5
|
LL | assert_eq!(X, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:28:5
|
LL | assert_eq!(x, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:28:5
|
LL | assert_eq!(x, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:28:5
|
LL | assert_eq!(x, true);
| ^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:31:5
|
LL | assert_eq!(Y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:31:5
|
LL | assert_eq!(Y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:31:5
|
LL | assert_eq!(Y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:33:5
|
LL | assert_eq!(y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:33:5
|
LL | assert_eq!(y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/is_const_eval_unleash.rs:33:5
|
LL | assert_eq!(y, false);
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)