That's not sufficient - consider the following pseudocode
x = malloc();
if (opaque_cond()) free(x);
if (other_opaque_cond()) use(x);
Conditions can be opaque and non-analyzable due to rices theorem - in any turing complete language. This code is correct (or at least not memory unsound) if opaque_cond and other_opaque_cond are never both true. Otherwise it isn't.
And functionally compiler analyses of whether conditions hold have to be trivial because using some form of theorem prover to decide of code is correct or not leads to code that is brittle against compiler version changes, and slow compile times. Thus opaque_cond could be as simple as `len == 0` and `other_opaque_cond` could be `len > 0` and it's unlikely you'd want the compiler to realize those are mutually exclusive (at the stage where it accepts programs, obviously during optimization it is very likely to take advantage of this).
Rust solves this by simply rejecting the pattern. Very roughly forcing you to write if opaque_cond() { free(x) } else if other_opaque_cond() { use_x } (or something else where the program structure and not just the logic in the conditions guarantees correctness). Zig simply allows it and leaves it up to the programmer not to make a mistake.
And as onlyrealcuzzo suggests aliases are where this type of analysis (accepting enough programs to be useful but still imposing enough structure you can prove correctness) is really tricky.
It's possible to detect and ban the version of the problematic pattern that I made as short and simple as possible to illustrate the point, sure.
I'm general though, I don't believe it is practical to do so. Not without every library being designed with the checker in mind and annotated to more precisely describe their APIs. Which is why I'm not surprised to see the limitations.md that seems to exclude all the hard cases (aliasing, pointers used as first class values, cross function analysis): https://github.com/ityonemo/clr/blob/main/LIMITATIONS.md#mem...
Obviously if you rewrite the zig world to obey rust like rules and include rust like annotations you can implement a rust like borrow checker, but I don't think that it would still be meaningfully zig. It might be an interesting language worth exploring.
i have in mind a strategy to address all of them. this is a side project, a proof of concept, i have other things going on in my life. i dont chip away at it every week.
you make, without any evidence ("Obviously"), an assertion that "it would look like another language". so far if anything applying zig clr would push a user to write more idiomatically ziggy code, away from idiomatically c-ish code. i dont see why continuing with clr wouldn't go further along that trend. so consider what is "obvious" to you might just be flat out wrong.
> It might be an interesting language worth exploring.
worth how much? youre welcome to sponsor my exploration and put your money where your mouth is:
I wish them the best of luck, but I don't expect them to succeed, and unspecified plans don't constitute a demonstration of feasibility. Plenty of people have made plans to solve unsolvable problems in the past, me included. I strongly suspect that that is the case here (or that they're willing to iterate away from zig).
The evidence is the amount that rust had to iterate on the underlying language to make the borrow checker work well. Something it had the freedom to do since it was co-designing the language and linter.
Edit: Didn't realize this was your project (responded before you added the donation link) - I would have worded my response slightly differently but my opinion is unchanged. Seriously mean it with the best of luck getting this to work.
do you want a direct description of the plan for async/alias work? hash the globally accessed gid list and if any operation changes retrigger analysis of all functions in the same execution block with the new layout.
i have not had to change the language to accomodate uaf/df/leak analysis (which is not easy). i see no reason why i should have to to get async/alias to work.
unlike rust, as of zig 0.16 async conceptually is abstracted to a userland interface in the stdlib (versus a keyword), which means that its easier to detect, and easier to work into the existing clr system. that means it's either "not doable at all" (unlikely) or is unlikely to need any back and forths to be done with the language
i dont understand the downvotes here. the point of any safety checker is to flag and ban potentially unsafe code, and force the author to rewrite with existing language patterns that guarantee the desired safety parameters.
in this case, zig has a first class nullable syntax that the checker can use ti guarantee correctness for, so a checker can deterministically sidestep this turing completeness issue, by squeezing indeterminate code into the knowably safer language idiom.
1. rust didnt invent affine ownership. 2. It's possible to bolt on to other languages (see ada). zig in particular is easy (disclaimer: i think, i haven't implemented it yet)
> zig in particular is easy (disclaimer: i think, i haven't implemented it yet)
I guess it's "easy" compared to other languages - but if you think it's "easy", we have different definitions of "easy".
You could implement it, but it would look like efforts in Rust to get SPARK-like safety, and SPARK itself. It will essentially be a different language.
You will not be able to work seamlessly with any regular Zig code. That may or may not be a problem if you're willing to assume you can just use it all unsafely and it works enough that things are fine.
That's somewhat analogous to unsafe Rust. The difference with unsafe Rust is... That's a very small fraction of what you're using, not the vast majority of what you use.
When you use a Rust crate - you generally do not expect that it could have infinite race conditions. It may have some unsafe code, but that should be the exception, not the norm.
By all means, please build it. I'd consider using it [=
> You will not be able to work seamlessly with any regular Zig code. That may or may not be a problem if you're willing to assume you can just use it all unsafely and it works enough that things are fine.
that may be true, but it seems "not". to date, all of the patterns in zig-clr nudge you towards idiomatic zig and not away from it. i run tests on not-my-code (an unaltered version of an existing zig project -- you can see it's vendored in the "vendored/validate"), and it passes. still working through forestmq.
and I'm planning a mechanism to let you reach into a function and "oracle" its safety parameters, probably most useful when someone else has written code that you know is ok but you cant tell them "hey make this work to pass my linter"
Also remember that zig compiles as a single compilation unit so even if you draw in zig dependencies, unless they are hidden behind a .so, zig-clr will analyze the dependency code too.
By all means - reach out when it's ready and I'll give it a test.
I'm highly skeptical you can get it to work. If it was easy and optional and non-invasive and actually worked - the Zig team would almost certainly build it.
But, even if it just mostly works - that would still be very useful if it's non invasive.
2. The zig team is parsimonious about what they do and don't build. For example, they did not work on the language server, rather punting it to the community.
3. That you can do this with the zig compiler is a happy accident. The team was not designing towards this possibility, it's not really a part of the core zig ethos (and that's fine).
4. You certainly cannot do things exactly the way that rust does it without changing the language; because the rust conversation has sucked all of the air out of the discourse around safety, you really do have to make a paradigm shift away from "tagged ownership" to "data dependency tracking with ownership agnosticism" to do it with zig.
And functionally compiler analyses of whether conditions hold have to be trivial because using some form of theorem prover to decide of code is correct or not leads to code that is brittle against compiler version changes, and slow compile times. Thus opaque_cond could be as simple as `len == 0` and `other_opaque_cond` could be `len > 0` and it's unlikely you'd want the compiler to realize those are mutually exclusive (at the stage where it accepts programs, obviously during optimization it is very likely to take advantage of this).
Rust solves this by simply rejecting the pattern. Very roughly forcing you to write if opaque_cond() { free(x) } else if other_opaque_cond() { use_x } (or something else where the program structure and not just the logic in the conditions guarantees correctness). Zig simply allows it and leaves it up to the programmer not to make a mistake.
And as onlyrealcuzzo suggests aliases are where this type of analysis (accepting enough programs to be useful but still imposing enough structure you can prove correctness) is really tricky.