From 3d3cfbca858588b47b70463f594bb2f7da4170fa Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 29 Mar 2021 10:18:21 +0000 Subject: [PATCH] Explain the new valtree system for type level constants. --- src/const-eval.md | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/const-eval.md b/src/const-eval.md index 5a11f8cddf..e1966d7457 100644 --- a/src/const-eval.md +++ b/src/const-eval.md @@ -20,17 +20,49 @@ Additionally constant evaluation can be used to reduce the workload or binary size at runtime by precomputing complex operations at compiletime and only storing the result. +All uses of constant evaluation can either be categorized as "influencing the type system" +(array lengths, enum variant discriminants, const generic parameters), or as solely being +done to precompute expressions to be used at runtime. + Constant evaluation can be done by calling the `const_eval_*` functions of `TyCtxt`. They're the wrappers of the `const_eval` query. +`static` initializers must use the `eval_static_initializer` function. All other functions +do not represent statics correctly and have thus assertions preventing their use on statics. + The `const_eval_*` functions use a [`ParamEnv`](./param_env.html) of environment in which the constant is evaluated (e.g. the function within which the constant is used) and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant or static or of an `Instance` of a function and an index into the function's `Promoted` table. -Constant evaluation returns a [`EvalToConstValueResult`] with either the error, or a -representation of the constant. `static` initializers are always represented as -[`miri`](./miri.html) virtual memory allocations (via [`ConstValue::ByRef`]). +Constant evaluation returns a [`EvalToValTreeResult`] (for type system constants) or +[`EvalToConstValueResult`] with either the error, or a representation of the constant. + +Constants for the type system are encoded in "valtree representation". The `ValTree` datastructure +allows us to represent arrays, many structs, tuples, enums and most primitives. The basic rule for +being permitted in the type system is that every value must be uniquely represented. In other +words: a specific value must only be representable in one specific way. For example: there is only +one way to represent an array of two integers as a `ValTree`: +`ValTree::Branch(&[ValTree::Leaf(first_int), ValTree;:Leaf(second_int)])`. +Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a +`ValTree::Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` +(and is so complex to do, so it is unlikely anyone is tempted to do so). + +These rules also mean that some values are not representable. There can be no `union`s in type +level constants, as it is not clear how they should be represented, because their active variant +is unknown. Similarly there is no way to represent pointers, as addresses are unknown at +compile-time and thus we cannot make any assumptions about them. References on the other hand +can be represented, as equality for references is defined as equality on their value, so we +ignore their address and just look at the backing value. We must make sure that the pointer value +of the references are not observable. We thus encode `&42` exactly like `42`. Any conversion from +valtree back to codegen constants must reintroduce an actual indirection. At codegen time the +addresses may be deduplicated between multiple uses or not, entirely depending on arbitrary +optimization choices. + +As a consequence, all decoding of `ValTree` must happen by matching on the type first and making +decisions depending on that. The value itself gives no useful information without the type that +belongs to it. + Other constants get represented as [`ConstValue::Scalar`] or [`ConstValue::Slice`] if possible. This means that the `const_eval_*` functions cannot be used to create miri-pointers to the evaluated constant. @@ -42,4 +74,5 @@ If you need the value of a constant inside Miri, you need to directly work with [`ConstValue::Slice`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/value/enum.ConstValue.html#variant.Slice [`ConstValue::ByRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/value/enum.ConstValue.html#variant.ByRef [`EvalToConstValueResult`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/error/type.EvalToConstValueResult.html +[`EvalToValTreeResult`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/interpret/error/type.EvalToValTreeResult.html [`const_to_op`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/interpret/struct.InterpCx.html#method.const_to_op