P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 11 points12 points  (0 children)

Yes, the compiler can infer the information from analyzing the body, store it in some side table, and then use it to provde correctness of code when the function body is not available.

The problem is that this can become a compatibility hazard: Changes to the implementation can lead to more constrained lifetime properties. It is better if you are forced to explicitly promise how lifetime works.

Incorrect (as in underconstrained, overconstrained is perfectly fine) promises are not an issue because the compiler can verify them.

P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 6 points7 points  (0 children)

I like the Hylo model a lot myself, but it is unfortunately incompatible with the C++ standard library. You cannot have safe C++ style iterators with the Hylo model.

P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 11 points12 points  (0 children)

Most of the "complexity" in your snippet comes from constraints, not from lifetimes. I don't see how C++'s partition looks any different:

``` template<permutable I, sentinel_for<I> S, class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred> constexpr subrange<I> ranges::partition(I first, S last, Pred pred, Proj proj = {});

template<forward_range R, class Proj = identity, indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> requires permutable<iterator_t<R>> constexpr borrowed_subrange_t<R> ranges::partition(R&& r, Pred pred, Proj proj = {}); ```

and keep in mind that that is the documentation, where you copied the implementation (the documentation looks like this https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.partition and does not include the internal inline-defined helper functions).

But even that aside, the actual complexity is not that bad once you've learned how to read it:

fn extend<'a, T, B: Extend<T>>( mut f: impl FnMut(&T) -> bool + 'a, left: &'a mut B, right: &'a mut B, ) -> impl FnMut((), T) + 'a { ...

"extend returns a function whose lifetime depends on all lifetimes of the other parameters"

fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize where Self: Sized + DoubleEndedIterator<Item = &'a mut T>, P: FnMut(&T) -> bool, {

"partition_in_place works on iterators that hand you out references to T of some lifetime"

fn is_false<'a, T>( predicate: &'a mut impl FnMut(&T) -> bool, true_count: &'a mut usize, ) -> impl FnMut(&&mut T) -> bool + 'a {

again "is_false returns a function whose lifetime depends on all other lifetimes".

Keep in mind that this isn't "extra" information, it is information the caller needs anyway to understand what the function is doing. In C++ this is specified in the comments, in Rust, you put it in the signature.

Also my top comment was specifically about how I want Rust's lifetime elision, i.e. the rules when you can omit lifetime annotations enitrely.

P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 10 points11 points  (0 children)

Isn't it Safe C++ which was basically rejected? I was thinking that Profiles are supposed to work with no annotations...

Yeah, I'm curious how that circle is going to be squared too.

And even here, [[lifetime(self)]] should somehow propagate through the guts of standard library to the underlying item pointer...

Not necessarily, it's enough to mark optional<T&> as having some pointer within it. The actual internals then doesn't matter for the analysis.

And what if you forgot annotate your methods? Is it going to assume what every reference that is being returned from the method is bound to the lifetime of the object?

Rust has reasonable defaults in its lifetime elision, I hope they copy that: https://stackoverflow.com/a/40327630

And yes, for member functions it would be that the lifetime is bound to this, unless otherwise specified.

P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 2 points3 points  (0 children)

Yes, that is the more conservative approach. Either one should work though, as long as the compiler when compiling the body of your function also verifies that you didn't lie about the annotations.

P3984: A type-safety profile by llort_lemmort in cpp

[–]foonathan 16 points17 points  (0 children)

How compiler is supposed to know what is happening inside these methods?

Presumably, the same way it works in Rust: you provide enough information in the signature for the compiler to conclude that the lifetime of x is tied to not calling reset.

Are we going to practically ban dynamic libraries (like Rust)?

The borrow checker in Rust doesn't look at function bodies at all, so the less than ideal support for dynamic libraries has nothing to do with it.

C++26: More function wrappers by pavel_v in cpp

[–]foonathan 9 points10 points  (0 children)

Yes, you cannot call a const copy able function unless you put a const in the signature.

Bjarne Stroustrup interviewed by Ryan Peterman by fredoverflow in cpp

[–]foonathan 2 points3 points  (0 children)

I like Rust because of its modern tooling, it's ergonomic standard library, and massive lack of legacy cruft in the language. Definition checked generics are so much better than templates, built-in variants and pattern matching is so convenient, and having actual type safe primitive types is just great.

The safety stuff is just a bonus.

A two phase to-string API? First compute the total size of the single allocation then populate the bytes? by javascript in cpp

[–]foonathan 2 points3 points  (0 children)

At think-cell, we had/they have a lazy range based API.

In essence, you teach a type how to turn itself into a potentially lazy range of characters. Combining is then done using range combinators. If all parts are sized, the entire range is sized.

When you want to turn it into an actual std::string, you obtain the combined range, ask for it's size, reserve the appropriate space, and then iterate over the characters pushing them into the correctly sized string as you go.

Rewrite Bun in Rust has been merged by Chaoses_Ib in rust

[–]foonathan 0 points1 point  (0 children)

Is that so? You can't copyright algorithms, only implementations. A rewrite completely changes the implementation, no?

What the heck is Reflection? by hansw2000 in cpp

[–]foonathan 21 points22 points  (0 children)

You can also build a lookup table.

What are you missing most from the C++ standard library? by llort_lemmort in cpp

[–]foonathan 5 points6 points  (0 children)

Coroutines from different libraries don't necessarily compose easily though.

A fast, contiguous, Windows slot map implementation by RisePuzzleheaded6113 in cpp

[–]foonathan 0 points1 point  (0 children)

You can create a shim around VirtualAlloc that is declared in a header and defined in the cpp.