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

Resolve long compile times when evaluating always valid constants #67667

Merged
Merged
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
15 changes: 12 additions & 3 deletions src/librustc_mir/interpret/validity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,19 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
}
ty::Array(tys, ..) | ty::Slice(tys)
if {
// This optimization applies only for integer and floating point types
// (i.e., types that can hold arbitrary bytes).
// This optimization applies for types that can hold arbitrary bytes (such as
// integer and floating point types) or for structs or tuples with no fields.
// FIXME(wesleywiser) This logic could be extended further to arbitrary structs
// or tuples made up of integer/floating point types or inhabited ZSTs with no
// padding.
match tys.kind {
ty::Int(..) | ty::Uint(..) | ty::Float(..) => true,
ty::Tuple(tys) if tys.len() == 0 => true,
ty::Adt(adt_def, _)
if adt_def.is_struct() && adt_def.all_fields().next().is_none() =>
{
true
}
_ => false,
}
} =>
Expand All @@ -609,7 +618,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// Size is not 0, get a pointer.
let ptr = self.ecx.force_ptr(mplace.ptr)?;

// This is the optimization: we just check the entire range at once.
// Optimization: we just check the entire range at once.
// NOTE: Keep this in sync with the handling of integer and float
// types above, in `visit_primitive`.
// In run-time mode, we accept pointers in here. This is actually more
Expand Down
24 changes: 24 additions & 0 deletions src/test/ui/consts/const-eval/validate_uninhabited_zsts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![feature(const_fn)]
#![feature(const_transmute)]

const fn foo() -> ! {
unsafe { std::mem::transmute(()) }
//~^ WARN any use of this value will cause an error [const_err]
//~| WARN the type `!` does not permit zero-initialization [invalid_value]
}

#[derive(Clone, Copy)]
enum Empty { }

#[warn(const_err)]
const FOO: [Empty; 3] = [foo(); 3];

#[warn(const_err)]
const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
Copy link
Contributor

Choose a reason for hiding this comment

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

side note: if you remove this constant and its use, you do get a hard error on the use of FOO, but BAR is what we are actually interested in testing here, because it causes validation to error instead of const eval.

//~^ ERROR it is undefined behavior to use this value
//~| WARN the type `Empty` does not permit zero-initialization

fn main() {
FOO;
BAR;
}
52 changes: 52 additions & 0 deletions src/test/ui/consts/const-eval/validate_uninhabited_zsts.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
warning: any use of this value will cause an error
--> $DIR/validate_uninhabited_zsts.rs:5:14
|
LL | unsafe { std::mem::transmute(()) }
| ^^^^^^^^^^^^^^^^^^^^^^^
| |
| entering unreachable code
| inside call to `foo` at $DIR/validate_uninhabited_zsts.rs:14:26
...
LL | const FOO: [Empty; 3] = [foo(); 3];
| -----------------------------------
|
note: lint level defined here
--> $DIR/validate_uninhabited_zsts.rs:13:8
|
LL | #[warn(const_err)]
| ^^^^^^^^^

error[E0080]: it is undefined behavior to use this value
--> $DIR/validate_uninhabited_zsts.rs:17:1
|
LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
|
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.

warning: the type `!` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:5:14
|
LL | unsafe { std::mem::transmute(()) }
| ^^^^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: `#[warn(invalid_value)]` on by default
= note: The never type (`!`) has no valid value

warning: the type `Empty` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:17:35
|
LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
| ^^^^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: 0-variant enums have no valid value

error: aborting due to previous error

For more information about this error, try `rustc --explain E0080`.
11 changes: 11 additions & 0 deletions src/test/ui/consts/huge-values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// build-pass
// ignore-32bit

#[derive(Clone, Copy)]
struct Foo;

fn main() {
let _ = [(); 4_000_000_000];
let _ = [0u8; 4_000_000_000];
let _ = [Foo; 4_000_000_000];
}