I think I see what you mean now. In your version we have
void function() {
consteval std::string const_string("Test 1");
std::string normal_string("Test 2");
constexpr auto c1 = const_string.size(); // valid
auto c2 = const_string.size(); // invalid, const_string can't be used outside a constant expression
constexpr auto c3 = normal_string.size(); // invalid, normal_string can't be used in a constant expression
auto c4 = normal_string.size(); // valid
}
so the situations where "const_string" and "normal_string" can be used are in fact completely orthogonal.
What I had in mind was:
consteval void function() {
consteval std::string const_string("Test 1");
std::string normal_string("Test 2");
constexpr auto c1 = const_string.size(); // valid
auto c2 = const_string.size(); // valid
constexpr auto c3 = normal_string.size(); // invalid, normal_string can't be used in a constant expression
auto c4 = normal_string.size(); // valid
}
Where the second case can also be allowed since we are in an immediate function and we can use "const_string"
without having to leak it to runtime (because the function can't be called at runtime).
I think you can and maybe should have both behaviours at the same time:
A consteval function is a function that has to be executed at compile time. Which means that in regular functions it must
produce a constant expression. But inside of a consteval function you are allowed to call other consteval functions, without
producing a constant expression since you are already guaranteed to be "at compile time".
Similarly a "consteval" variable would have to be used at compile time, so it has to produce a constant expression inside
regular functions but doesn't have to in consteval functions.
In fact this would mean that the example of "auto c2 = const_string.size();" would be valid even inside a regular function
since "const_string.size()" still produces a constant expression. This essentially analogous to
consteval int get_value() { return 6; }
int main() {
int i = get_value();
}
which is perfectly valid. But things like
void use_string(const std::string&); // not constexpr!
int main() {
consteval std::string str("Test");
use_string(str);
}
aren't allowed since this really would require leaking the string to runtime code.
pt., 4 mar 2022 o 16:03 Torben Thaysen <thaysentorben@gmail.com> napisał(a):
>>
>> This means we can't use `std::string` as it would "leak" memory from
>> compile time to run time and mixup life times of compilation and
>> runtime.
>
> That is why I made the restriction to consteval functions. There shouldn't be any way to leak the address of a local variable inside a consteval function to
> runtime code. The same is true for any intermediate object created during the evaluation of a constant expression. Otherwise we couldn't even use
> vector/string as regular variables in constant expressions. For example
>
> consteval auto foo() {
> std::string s("Test");
> return s.size();
> }
>
> is clearly valid and there better be no way to leak the memory allocated by "s" to runtime. And that doesn't change when we make "s" a "consteval" variable.
>
You miss my point, we have effectively two types of `std::string` now,
one allocated by compiler and another by runtime.
We can't mix them and even refer to them in the same context at once.
I hope that my approach simply remedy problem closer to root cause and
allow easier to transfer data between this two "scopes" aka compile
time and runtime.
> Marcin Jaczewski <marcinjaczewski86@gmail.com>:
>>
>> pt., 4 mar 2022 o 14:53 Torben Thaysen <thaysentorben@gmail.com> napisał(a):
>> >>
>> >> I think this should be fixed a bit diffrent way. Current `constexpr`
>> >> variables can materialize in result code and this is problem if we use
>> >> memory allocation.
>> >
>> > I am not sure what you mean. The way I understand it, whether you find some trace of constexpr variables in the final binary/assembly
>> > (or whether they are "materialized") is decided by the compiler the same way as for any other (inline) variable. Since what I am proposing is
>> > a local variable inside of immediate functions, the only place they could "materialize" is into the body of that function, which by definition doesn't
>> > exist at runtime.
>> > When you try something like
>> >
>> > consteval const int* leak_ptr() {
>> > consteval int i = 1;
>> > return &i;
>> > }
>> > int main() {
>> > constexpr auto ptr = leak_ptr();
>> > }
>> >
>> > the local variable won't "materialize", instead you would get a compiler error since a pointer to an object of automatic storage duration is not
>> > allowed in a constant expression.
>> >
>> I mean case like:
>> ```
>> int main()
>> {
>> constexpr auto i = 5;
>>
>> return (unsigned long int)&i;
>> }
>> ```
>> https://godbolt.org/z/Wx1v5KWjo
>> We have a real variable on stack with address.
>> This means we can't use `std::string` as it would "leak" memory from
>> compile time to run time and mixup life times of compilation and
>> runtime.
>> Most of the limitations of `constexpr` is because of this case.
>>
>>
>> >> One way I could see to fix it make option for "pure" `constexpr
>> >> variables that only live during compilation and are not accessible in
>> >> anyway by run time.
>> >
>> > This sounds quite similar to what I proposed but with the requirement to be in an immediate function removed. This might have the advantage
>> > that it could save you from having to define an extra function. However consider that with your version you are restricted to the initializer of result
>> > variable, which means that when the constructor you mentioned doesn't exist or you want to do something more complicated the just copy some data,
>> > you would have to write a helper function anyways.
>> >
>>
>> Yes, but the difference is I make a clear distinction between what
>> lives at compile time and what lives at runtime. as now `constexpr`
>> variables can migrate between.
>>
>> > Marcin Jaczewski <marcinjaczewski86@gmail.com>:
>> >>
>> >> pt., 4 mar 2022 o 12:54 Torben Thaysen via Std-Proposals
>> >> <std-proposals@lists.isocpp.org> napisał(a):
>> >> >
>> >> > Currently when computing things at compile time one sometimes faces a problem when trying to output the result to a constexpr variable.
>> >> > This happens when the result is data structure without a fixed size that utilizes dynamic memory allocation, which in itself is not a
>> >> > constant expression (for example a vector). Today the result can be converted to a more appropriate fixed sized structure like this:
>> >> >
>> >> > constexpr std::vector<int> compute_vector();
>> >> > constexpr std::size_t extract_size() { return compute_vector().size(); }
>> >> > constexpr auto as_array() {
>> >> > auto vec = compute_vector();
>> >> > std::array<int, extract_size()> arr;
>> >> > std::copy(vec.begin(), vec.end(), arr.begin());
>> >> > return arr;
>> >> > }
>> >> >
>> >> > But this somewhat awkwardly involves doing the computation twice. The only way around this is to copy the data to a large intermediate array
>> >> > then copy it again into an array of the right size. Neither of these solutions is really ideal and that is what this proposal aims to solve.
>> >> >
>> >> > For this I want to introduce a new kind of variable that can be declared inside an immediate function. With this the above example could look like:
>> >> >
>> >> > constexpr std::vector<int> compute_vector();
>> >> > consteval auto as_array() {
>> >> > consteval auto vec = compute_vector(); // example syntax
>> >> > std::array<int, vec.size()> arr;
>> >> > std::copy(vec.begin(), vec.end(), arr.begin());
>> >> > return arr;
>> >> > }
>> >> >
>> >> > The requirements for this type of variable should conceptually be similar to those for constexpr variables (see 9.2.6.10 and 7.7.11) except that:
>> >> >
>> >> > Targets of pointers and references don't have to have static storage duration. In particular pointers to dynamically allocated memory are allowed.
>> >> > Memory allocations performed in the initialization can (and must) be deallocated in the destructor.
>> >> > And (i think) pointers to immediate functions can also be allowed, since this type of variable only exists inside immediate functions. Although this
>> >> > isn't required for the aim of this proposal.
>> >> >
>> >> > To my reading this lifts all the requirements of 7.7.11 so that any core constant expression with the modified allocation requirement would be allowed.
>> >> > Like with constexpr variables these new variables should also be implied to be const. And I propose for consistency they should be allowed to be
>> >> > declared in any immediate function context as by 7.7.13 (i.e. also inside a if consteval statement).
>> >> >
>> >> > Looking forward to your feedback
>> >> > Torben
>> >> >
>> >> > --
>> >> > Std-Proposals mailing list
>> >> > Std-Proposals@lists.isocpp.org
>> >> > https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>> >>
>> >> I think this should be fixed a bit diffrent way. Current `constexpr`
>> >> variables can materialize in result code and this is problem if we use
>> >> memory allocation.
>> >> One way I could see to fix it make option for "pure" `constexpr
>> >> variables that only live during compilation and are not accessible in
>> >> anyway by run time.
>> >> This way we could make:
>> >> ```
>> >> int main() //can be used in any function!
>> >> {
>> >> consteval auto s1 = std::string("Test"); //variable and memory
>> >> allocation live only during "compilation" of `main`, we could consider
>> >> this a new `static` in some way.
>> >> static constexpr std::array<int, s1.size()> a1 = { s1.begin(),
>> >> s1.end() }; //assume that array have constructor that can copy from
>> >> range
>> >>
>> >> auto s2 = std::string(s1); //Error! `s1` is not accessible here!
>> >> auto s3 = std::string(a1.begin(), a1.end()); //normal string that
>> >> copy data from static memory
>> >> }
>> >> ```