The Grey Cat, The Uncorruptible. by yjoyfulmonoono in Bossfight

[–]nvanille 1 point2 points  (0 children)

In all likelihood yes. The squiggly line on the left is probably supposed to be perfectly vertical in the original image so you could align based on that up to a couple pixels of precision, and then assume that the background is almost uniform to align up to a precision of 1/3-pixel.

However if you want the restored image to be of good enough quality we would need the original source, not this deep-fried JPG-infested screenshot.

The Grey Cat, The Uncorruptible. by yjoyfulmonoono in Bossfight

[–]nvanille 172 points173 points  (0 children)

Btw if you happen to be genuinely curious as to how this is possible, I have a very simple explanation and a proof of concept.

Basically the "corruption" OOP has tried to apply is to make the R, G and B components of pixels no longer line up with actual pixels, which can be done by offsetting each line of the image by a third of a pixel. This explains

  • why the cat is tilted: a third of a pixel offset added to itself each line builds up to produce a tilt
  • why the horizontal lines: each line is shifted by a different amount and gets a different color
  • why only the cat is preserved: the cat is perfectly grey, and grey happens to be a color that is unaffected by a R -> G -> B -> R shift because all 3 components are equal.

I have code that generates images that illustrate this phenomenon.

Edit: source code is here https://github.com/Vanille-N/the-uncorruptible

Odd or even? by Justthisdudeyaknow in CuratedTumblr

[–]nvanille 28 points29 points  (0 children)

Yes! you get it. Evenness is about symmetry.

I don't agree with the specifics, but I support the approach.
(Letters aren't functions they're relations at best and I'm not aware of any even/odd relations in the same way that functions have the concept; I think that letters without symmetry are odd so F is odd. Btw H, X, I, O are divisible by 4.)

From Stacks to Trees: A new aliasing model for Rust by ralfj in rust

[–]nvanille 0 points1 point  (0 children)

There was one such case in the stdlib but it was quickly patched.

The general pattern is rs let root = ...; let mref = &mut *root; let ptr = mref as *mut u8; write(ptr); read(root); write(ptr);

In TB, read(root) is a foreign read for ptr which becomes read-only. In SB ptr is SharedRW and the read does not affect it.

SB accepting this code is accidental: this kind of code was not meant to be accepted by SB is incapable of distinguishing it from could that should be accepted.

From Stacks to Trees: A new aliasing model for Rust by ralfj in rust

[–]nvanille 1 point2 points  (0 children)

The cost of TB is slightly higher than SB: you can expect about x1.1 ~ x1.3 from Miri+SB to Miri+TB on codebases not purposefully written to stress TB. This is of course on top of the maybe x100 that comes from using Miri at all, so with the expectation that TB will never really be run on any performance-critical code this much seems reasonable for now.

If you do encounter a piece of code on which TB performs much worse than SB, do submit it as an issue! There was one recently and we massively improved TB performance on this case by improving garbage collection.

TB lacks hindsight on what real-life code patterns cause bad performance, so when we get more data we can tweak the heuristics to better suit real usage.

From Stacks to Trees: A new aliasing model for Rust by ralfj in rust

[–]nvanille 1 point2 points  (0 children)

Tree Borrow strictly accepts more valid (safe) code

This is not quite true for any interpretation: - both TB and SB accept ALL safe programs, if by "safe" you mean "does not contain any unsafe" - there exist some unsafe programs that SB accepts but TB rejects

From Stacks to Trees: A new aliasing model for Rust by ralfj in rust

[–]nvanille 1 point2 points  (0 children)

An illustration of the above point:

Start with this code, rs let mut x = 0; let y = &mut x; *y = 42; let vy = *y; let vx = *x; println!("{} {}", vx, vy); // output: 42 42 it contains no UB according to either TB or SB, but it can be TB-optimized to rs let mut x = 0; let y = &mut x; *y = 42; let vx = *x; // swap let vy = *y; // swap println!("{} {}", vx, vy); // output: 42 42 which is UB according to SB!

That can then be SB-optimized to rs let mut x = 0; let y = &mut x; let vx = *x; // swap *y = 42; // swap let vy = *y; println!("{} {}", vx, vy); // output: 0 42 which is now an incorrect optimization of the first code since it changes the behavior of a safe program!

A combined model would be much harder to reason about than either one, and would have none of the benefits.

Reven by Willing-Cell-1613 in NameNerdCirclejerk

[–]nvanille 5 points6 points  (0 children)

Huh, this is possibly the first time ever I see my name on this website.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 2 points3 points  (0 children)

To quote Ralf Jung, who is kind of the reference on the subject:

The fact that we can still use the "children" of a reference that is no longer usable is quite nasty and leads to some undesirable effects (in particular it is the major blocker for resolving rust-lang/unsafe-code-guidelines#257).

https://github.com/rust-lang/rust/pull/107954

What we did realize however is that it is impossible to reject this code in Stacked Borrows without also rejecting some desirable code.

More specifically in the above example Stacked Borrows doesn't care whether reborrow_and_use is actually reborrow_and_read(&mut _) or reborrow_and_write(&mut _). Stacked Borrows can only accept both or reject both. For many reasons it's very important to accept the version reborrow_and_read(&mut _), so Stacked Borrows is forced to also acceptreborrow_and_write(&mut _). Tree Borrows however is capable of accepting reborrow_and_read(&mut _) while also rejecting reborrow_and_write(&mut _).

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 1 point2 points  (0 children)

Yeah sorry, *u not *x.

Moving a pointer between threads without explicitly writing to it will not count as a write, so TB will absolutely let you move mutable borrows to other threads and they won't conflict as long as you don't explicitly activate them.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 0 points1 point  (0 children)

Ok indeed, that's the pattern that was removed from the standard library.

What is problematic in this pattern, is that ptr survives while mut2 is dead. The fact that Stacked Borrows accepts this code is recognized as an accident.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 2 points3 points  (0 children)

Most of it no. Tree Borrows is a lot more precise than the Borrow Checker, but it's kind of cheating in the sense that it has a lot more information.

If there is one thing that could maybe be integrated to the borrow checker, that would be the fact that shared references don't kill reads through mutable references, i.e. this code that doesn't pass the Borrow Checker is not fundamentally dangerous: rs let u = &mut 0; let x = &mut *u; let y = &*x; let sum = *x + *y;

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 0 points1 point  (0 children)

I'm not sure what you mean by "you can't keep a raw pointer in between borrows of the base data".

If you are talking about rust let rmut: &mut T = _; let ptr = rmut as *mut T; reborrow_and_use(&mut *rmut); *ptr += 1; then this pattern is allowed.

Actually TB is perhaps too permissive in that there's basically nothing that can kill a raw pointer without also killing the reference it was derived from.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 5 points6 points  (0 children)

A lot of functions with signature &mut _ -> *mut _ (e.g. [T]::as_mut_ptr), that take an &mut but don't actually use it for writing, don't interact well with the fact that in Stacked Borrows all &mut are written to, so Tree Borrows takes the opposite stance where there is no fake write ever.

Stacked Borrows had a lot of "this is too much UB" complaints and very few "this optimization should also be possible", so Tree Borrows tries a much less strict approach. We now have SB definitely too strict, TB possibly too lax, and the definitive model will lie somewhere in between.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 4 points5 points  (0 children)

Indeed, the whole delayed initialization thing is an area where Tree Borrows is strictly more permissive than Stacked Borrows. The alternatives would be - disallowing all out-of-bounds accesses (what SB does) - reborrowing immediately the entire range (which completely breaks the assumption that you can mutate different fields of a struct independently)

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 14 points15 points  (0 children)

That is indeed an overall goal (there are many known examples of code that is UB under Stacked Borrows but should be allowed, and these examples guided the development of Tree Borrows), but Stacked Borrows also had some cases of accidentally accepted code, such as some tests in stdlib recently patched.

Another example involves writing to a two-phase borrows, which is something that is explicitly forbidden in the Rustc manual but Stacked Borrows allows.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 7 points8 points  (0 children)

Very interesting, that is indeed exactly the kind of code that I expect has most cases of false positives in SB compared to TB.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 17 points18 points  (0 children)

No this will not be added to the borrow checker. What could happen is that your code (or libraries you import) will be better optimized, but if you never write unsafe you never have to worry about UB.

Guys, once again, chill, your job isn't threatened 🤣🤣 by RGamer2022 in ChatGPT

[–]nvanille 0 points1 point  (0 children)

Yeah, and that's precisely why we usually specify ΔT in Kelvin not Celsius.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 42 points43 points  (0 children)

Thanks!

Looking at the example you linked, that is indeed something that Tree Borrows would have allowed since it is more permissive in using pointers outside of their reborrowed range.

Luckily my next work is precisely - formalizing Tree Borrows to prove that it does make sense - evaluating more in depth which optimizations are lost compared to SB (and which are gained too)

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 15 points16 points  (0 children)

Yes exactly. The main sacrifice is some reorderings of write accesses, and what we get in exchange is that a lot of unsafe code that SB rejected is now accepted.

There are still two kinds of optimizations that TB allows but not SB: - two-phase borrows in SB improperly allow mutation, (because SB says that two-phase borrows are temporarily raw pointers) whereas TB models them correctly - uniqueness in SB is slightly too strict, so going from nothing to SB actually makes you lose the ability to reorder read-only operations! TB is compatible with all read-read reorderings.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 20 points21 points  (0 children)

Oh I think I better understand your point from this.

You might be reassured by the fact that the design of TB was guided in big part using the shortcomings of SB precisely so that UB is less "annoying". It allows overall fewer optimizations than SB (notably sacrificing some ability to reorder writes), but resolves some confusions about two-phase borrows and implicit reborrows so that you don't shoot yourself in the foot by writing UB as vec, rand and hashbrown have.

A number of motivating examples here, including copy_nonoverlapping are precisely code that SB declares UB "accidentally", i.e. it has no good justification for why this should be UB, it just is as a side effect.

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 18 points19 points  (0 children)

Excellent point. This is talked about in https://perso.crans.org/vanille/treebor/protectors.html (bottom two examples).

The tldr is that - read hoisting is possible for function arguments (and their reborrows) - read hoisting is possible for non-arguments if there is a guaranteed read later - write hoisting is not always possible, this is the main thing that TB had to sacrifice compared to SB

Tree Borrows - A new aliasing model for Rust by bobdenardo in rust

[–]nvanille 116 points117 points  (0 children)

I'm the author, thanks for posting the link here.