From 18521bb23b12f21e80be83a26c34eb543ca94174 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 29 Mar 2021 10:18:21 +0000 Subject: [PATCH 1/3] Explain the new valtree system for type level constants. --- src/const-eval.md | 52 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/src/const-eval.md b/src/const-eval.md index 5a11f8cdd..d1a56e533 100644 --- a/src/const-eval.md +++ b/src/const-eval.md @@ -20,17 +20,62 @@ 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. +* `const_eval_global_id_for_typeck` evaluates a constant to a valtree, + so the result value can be further inspected by the compiler. +* `const_eval_global_id` evaluate a constant to an "opaque blob" containing its final value; + this is only useful for codegen backends and the CTFE evaluator engine itself. +* `eval_static_initializer` specifically computes the initial values of a static. + Statics are special; 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 an [`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 very 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 +87,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 From 0d22497fd78675091af472fbd2e35df7518320d9 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 13 Sep 2022 14:31:12 +0200 Subject: [PATCH 2/3] Update src/const-eval.md Co-authored-by: Noah Lev --- src/const-eval.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/const-eval.md b/src/const-eval.md index d1a56e533..f27b5a382 100644 --- a/src/const-eval.md +++ b/src/const-eval.md @@ -63,7 +63,7 @@ Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just 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 +is unknown. Similarly there is no way to represent raw 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 From b9847324637b6815121c14c3d03e40f3fcd1e90e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 13 Sep 2022 14:31:52 +0200 Subject: [PATCH 3/3] Update src/const-eval.md --- src/const-eval.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/const-eval.md b/src/const-eval.md index f27b5a382..35f5c82ea 100644 --- a/src/const-eval.md +++ b/src/const-eval.md @@ -66,8 +66,9 @@ level constants, as it is not clear how they should be represented, because thei is unknown. Similarly there is no way to represent raw 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 +ignore their address and just look at the backing value. We must make sure that the pointer values +of the references are not observable at compile time. 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.