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

Why access the heap... #68

Closed
gdennie opened this issue Jul 9, 2021 · 9 comments
Closed

Why access the heap... #68

gdennie opened this issue Jul 9, 2021 · 9 comments

Comments

@gdennie
Copy link

gdennie commented Jul 9, 2021

Still a newbie so bear with me...

Not sure why heap access is necessary or even desirable for const and static's values. That they are binary embedded values seems like a very tight abstraction where compile time evaluation is merely another way to building such values (byte patterns) as part of the binary construction process. In particular, alignment and other runtime constraints aren't truly relevant but if they are they can be easily accommodated by dedicated code. For instance, converting a byte array to an i64 can have code to align on construction or conversion since both the value and the shape of the result are known. Ultimately, the heap, stack, and registers are strictly runtime resources and making them otherwise complicates the load, align, and execute at entry point abstraction. It also doesn't make sense in terms of libraries for it to somehow populate the heap at load time. However, I may be missing something...

Additionally, when const and static's only affect the binary then what they can do in generating results becomes conceptually unlimited only by time and security considerations, though all such constructed values must now be access by reference. Interestingly, static mut becomes a possible way to have self modifying binaries.

@gdennie gdennie closed this as completed Jul 9, 2021
@gdennie gdennie reopened this Jul 9, 2021
@oli-obk
Copy link
Contributor

oli-obk commented Jul 9, 2021

It also doesn't make sense in terms of libraries for it to somehow populate the heap at load time. However, I may be missing something...

Heh, you aren't missing anything. This is exactly what we don't allow with "const heap". The "const heap" only exists at compile-time. All such "heap" values will get turned into static allocations. There will be no automatic conversion between compile time heap values and runtime heap values. Instead you have to use some form of cloning or "to_owned" to get a runtime copy that you have ownership of.

The reason we want a heap at compile-time is that we don't want to limit people to ArrayVec, but want you to be able to "just" use a Vec, as long as you don't end up with a const FOO: Vec<i32> = vec![42, 43];, as that would indeed be unsound and otherwise problematic.

@gdennie
Copy link
Author

gdennie commented Jul 9, 2021

Thanks.

Per your example, I would think that const modifies the meaning of that expression into one stating that at compile time FOO becomes &[i32; 2], namely &[42_i32, 43_i32]. I suppose the ambiguity is that FOO has the type Vec<i32>.

I suppose a more correct treatment might be...

const FOO: &[i32; 2] = &vec![42, 43]

I wonder how feasible it is to simply convert dynamic types to their constant equivalent when they are assigned to const identifiers? I guess that's the big question?

@oli-obk
Copy link
Contributor

oli-obk commented Jul 9, 2021

I wonder how feasible it is to simply convert dynamic types to their constant equivalent when they are assigned to const identifiers? I guess that's the big question?

this would work out of the box, the big question is how we differentiate between the "bad" and the "good" constants. how do we prevent the user from having a Vec<i32> in their constant while allowing the user to use a Vec<i32> to end up with a &[i32] in the constant's value.

@gdennie
Copy link
Author

gdennie commented Jul 9, 2021

I think you might have solved it. Namely, simply restrict the types that may be const and provide an adapter applied by the compiler to freeze and copy heap allocated structures create by const eval into the fixed sized immutable type permitted in the binary.

I suppose the trait system could be used to classify const capable types as well as types convertible to a const capable type. The later trait would provide a freeze operator that converts their value to an equivalent const capable type, perhaps utilizing generics and the fish operator as needed.

trait ConstCapable {}
trait ConstConvertible {
   fn freeze<T: ConstCapable>(&self) -> T {}
}

@oli-obk
Copy link
Contributor

oli-obk commented Jul 9, 2021

well, we discussed this to death in #20 and https://rust-lang.zulipchat.com/#narrow/stream/146212-t-compiler.2Fconst-eval/topic/Questions.20regarding.20Heap.20allocation.20in.20const.20eval/near/216632678 and there are problems with a lot of designs that we came up with. I forget what the current status is. Someone was working on experimenting with it, but life happened. We do need impl const Trait first, so we can even experiment with allocators at all.

@gdennie
Copy link
Author

gdennie commented Jul 10, 2021

My skim of the discussion discovered ConstSafe and ConstRefSafe tagging traits. There was also discussion about the in-lining behaviour of const needing to be change (perhaps to accommodate arbitrarily large const values) and whether Copy and Clone traits pertain here and how. There was also discussion about referencing heap from const objects in some way. There is also a presumption that const functions produce the same value each time (be idempotent). Unfortunately, I only skimmed the material.

In my view, const are data structures created and frozen by user supplied code at compile time. As such, const may only refer to each and functions (which are themselves implicitly const). I would think the Copy trait implies that a const value may be in-lined though such presumptions may be overridden by an attribute. With respect to copying and cloning the topic of deep cloning/copying versus shallow cloning/copying surfaces. Yet, if const can only refer to each other then that issue may be resolved by the existing type system constraints but at the very least we require deep cloning/copying if a finer grain isn't feasible.

Since the permissible types of const must be fixed sized objects, const a : Vec<i32> or const a: String are not permissible. However, the compiler would be able to "freeze" a constructed Vec<i32> of size n into a [i32; n] whose size is fixed.

The evaluation of the code constructing the const values may present a cyclic dependency issue, though I believe that area of compiler technology is a trimmed garden. However, the need for const value generating functions be idempotent is an overkill. In the general case these functions may need to capture aspects of the compile time and production environment. In fact, to the extent they may invoke a command that generates a file that is then incorporated into their values implies the loss of idempotentness. Still, a flagging compiler attribute may resolve the dispute.

@oli-obk
Copy link
Contributor

oli-obk commented Jul 10, 2021

Since the permissible types of const must be fixed sized objects,

String is fixed size, it's a pointer and two integers, the problem is that it is !Copy and will thus be moved without cloning.

const a : Vec<i32> or const a: String are not permissible. However, the compiler would be able to "freeze" a constructed Vec<i32> of size n into a [i32; n] whose size is fixed.

we could try to come up with an unsafe "constify trait" that offers a method that is called before giving you access, but I don't see how we can do that backwards compatilby.

users can already define their own types and put them into constants

@gdennie
Copy link
Author

gdennie commented Jul 10, 2021

The mutability of String, unlike str, is the thing that makes String inadequate as a const capable type. With immutability in mind, const references should only refer to other const values.

On a separate note, the library available to const value creation functions should include devDependency libraries. It may well be that the majority code in an application could be that which defines its const values.

Constify ? ... How about Constable or Constantable. Perhaps, Constifiable :)

@oli-obk
Copy link
Contributor

oli-obk commented Jul 17, 2021

I'm going to close this issue. Thanks for keeping the discussion out of the main issue, that issue is clogged enough already.

@oli-obk oli-obk closed this as completed Jul 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants