Live data from Hacker News

Contracts for C

gustedt.wordpress.com

101–110 of 114 posts

Re: Contracts for C

#101

Earlier quoted context omitted.

Adding conditions to the type signature would be an ABI breaking change in C++ and have nasty interactions with templates. In general though, the compiler can't optimize across the translation unit boundary without something like LTO. The code for the callee might have already been generated by the time the caller sees that the precondition is statically satisfied.

My suggestion was for C where types are for example not encoded in the name, so I thought it only matters for type checking and optimization. > In general though, the compiler can't optimize across the translation unit boundary Which is why I would put it in the function signature, so it is available in both translation units. Making the code match the function signature is currently generally the responsibility of t…

The ABI thing is because Lisa's talk was about C++. In C, a function can have multiple declarations as long as they're compatible (as opposed to identical).

So these declarations might coexist without issue even though they have different signatures:

   extern int foo(a, b); // in include/lib.h
   int foo(int a, int b); // in src/foo.h
whereas this would be incompatible

   const int foo(int a, int b); // "nearly" compatible
If you attach things to the prototype, then you need to sort out the compatibility rules. If contract_assume(a > 0) changes the type, the extern shouldn't be compatible. This is frequently used to allow linking against libraries compatible with older language standards while allowing newer code to benefit from newer standards like C99, C11, or C23.

The C23 committee ran into this issue when introducing attributes. Their solution was just exclude attributes from the signature and say they're always compatible:

    Although semantically attached to a function type, the attributes described are not part of the prototype of such a function, and redeclarations and conversions that drop such an attribute are valid and constitute compatible types.

Re: Contracts for C

#102

Earlier quoted context omitted.

I like how it's handled in Herb's cpp2/cppfront. If the type implements certain methods (like size()), and you turn on bounds checking, then the indexing operations (which are unsafe) are wrapped in i . This way the indexing operation itself doesn't need to have bounds checks and it's easier for the compiler to optimize out the checks or for an "unchecked" section to be requested by the programmer.

> I like how it's handled in Herb's cpp2/cppfront. If the type implements certain methods (like size()), and you turn on bounds checking, then the indexing operations (which are unsafe) are wrapped in i . For what it's worth, something similar was proposed in the C++26 core profiles paper [0] and that particular bit got some pushback. From a reponse paper [1]: > The paper’s suggested approach includes adding bounds c…

Interesting, thanks for sharing!

Re: Contracts for C

#103

Earlier quoted context omitted.

My suggestion was for C where types are for example not encoded in the name, so I thought it only matters for type checking and optimization. > In general though, the compiler can't optimize across the translation unit boundary Which is why I would put it in the function signature, so it is available in both translation units. Making the code match the function signature is currently generally the responsibility of t…

The ABI thing is because Lisa's talk was about C++. In C, a function can have multiple declarations as long as they're compatible (as opposed to identical ). So these declarations might coexist without issue even though they have different signatures: extern int foo(a, b); // in include/lib.h int foo(int a, int b); // in src/foo.h whereas this would be incompatible const int foo(int a, int b); // "nearly" compatible…

Maybe I'm missing something, but how does this change anything to how it's now?

I can happily declare two completely incompatible functions with the same symbol name, as long as they are in separate TUs and I don't use -flto, neither the compiler nor the linker will complain and my program will simply be garbage. This won't change with incompatible contracts.

When I both show them to the compiler, when they contradict, the compiler will complain, that also doesn't change.

Of course this will not work:

    extern int foo(a, b);
    int foo(int a, int b) contract_assume(a > 0);
However this will:

    extern int foo(a, b) contract_assume(a > 0);
    int foo(int a, int b);
But this isn't a problem, since this is precisely the feature we want to introduce contracts for: catching function call mismatches that are not yet expressible in the language.

> while allowing newer code to benefit from newer standards

Having no contract specified should of course result in no additional restrictions being exposed beside this already present now. This wouldn't be possible:

   foo(unsigned int a) contract_assume(possible(a 
But I don't think anybody is arguing for that.

Re: Contracts for C

#104

Earlier quoted context omitted.

> D is the result of lack of interest by the C++ committee, and I had little interest in spending literally years trying to get useful things adopted into C++. I think you are leaving out the fact that your comment applies to the post-C++98/pre-C++11 hiatus. Once C++11 was released, the truth of the matter is that whatever steam D managed to build up, it fizzed out. I'm also not sure if it's accurate to frame the pro…

C++ is still trying to catch up with: - compile time function execution - modules - no preprocessor - memory safe arrays - preprocessor replacement - ranges and so on.

> C++ is still trying to catch up with (...)

C++ modules are indeed a mess, but you are fooling yourself if you believe that the preprocessor of all things is a compelling reason to switch. In fact, I think you unwittingly proved my point on how interest in D fizzed out the moment C++11 was released.

Re: Contracts for C

#105

Earlier quoted context omitted.

> If it's such a great language, when will we see a useful program written in it? I think it should have been simple enough to find examples, though I suppose there might be some dependence on what you mean by "useful". For standalone stuff, some examples might be Ripgrep, ruff, uv, Alacritty, and Polars. Rust is also used internally by some major companies, such as Amazon, Dropbox, Mozilla, Microsoft, Google, Volvo,…

> there might be some dependence on what you mean by "useful". I should've been clearer about that, but what I mean by that is pretty much what a normal non-technical person would consider an useful piece of software - Photoshop, Figma, Excel, Chrome, Windows, Android, Blender, AutoCAD, Unreal Engine, any Office Suite... Since this is a technical forum I think we'd both easily agree on a bunch of very technically imp…

> but what I mean by that is pretty much what a normal non-technical person would consider an useful piece of software

That seems like an... interesting... definition of "useful" to me. Why that definition?

> Photoshop, Figma, Excel, Chrome, Windows, Android, Blender, AutoCAD, Unreal Engine, any Office Suite...

To be fair, there is Rust in Windows and Android, and IIRC there's movement towards using it in Chrome as well.

> It would be a stretch to put any of the tool on either of those lists.

OK, but your second list has a different set of qualifications than the first. You originally just asked for "useful" programs, and that's what you asked for in the original comment I responded to. Now it's "very technically impressive". So which do you want?

I feel like it's probably not a bad idea to ask exactly what you mean by "technically impressive" as well, since I think it's hard to argue that ripgrep and polars don't at least have technically impressive parts in them.

> Given the popularity of Rust, and that it's now over 10 years old, I'd expect at least one major program that can serve as an example of "here's this very useful, complex software package, as proof that our methodology works and you can do cool things this way."

That seems like a bit of a questionable metric to me.

Given that Rust was explicitly designed and intended to be incrementally adoptable in existing codebases, it doesn't make sense to me to solely look for standalone programs since incremental adoption is very much part of "our methodology". This also sort of ties into your expectation in the first place - I'm not sure I'd expect the same given Rust's design and niche, as well as the general software landscape now vs. when C/C++ were a similar age.

Re: Contracts for C

#106

Earlier quoted context omitted.

> D is the result of lack of interest by the C++ committee, and I had little interest in spending literally years trying to get useful things adopted into C++. I think you are leaving out the fact that your comment applies to the post-C++98/pre-C++11 hiatus. Once C++11 was released, the truth of the matter is that whatever steam D managed to build up, it fizzed out. I'm also not sure if it's accurate to frame the pro…

C++ is still trying to catch up with: - compile time function execution - modules - no preprocessor - memory safe arrays - preprocessor replacement - ranges and so on.

As I am oblivious to D, may I ask if there are suitable GUI toolkits for it, or bindings? I typically use wxWidgets in C++ land.

Re: Contracts for C

#107

Earlier quoted context omitted.

> If it's such a great language, when will we see a useful program written in it? I think it should have been simple enough to find examples, though I suppose there might be some dependence on what you mean by "useful". For standalone stuff, some examples might be Ripgrep, ruff, uv, Alacritty, and Polars. Rust is also used internally by some major companies, such as Amazon, Dropbox, Mozilla, Microsoft, Google, Volvo,…

> there might be some dependence on what you mean by "useful". I should've been clearer about that, but what I mean by that is pretty much what a normal non-technical person would consider an useful piece of software - Photoshop, Figma, Excel, Chrome, Windows, Android, Blender, AutoCAD, Unreal Engine, any Office Suite... Since this is a technical forum I think we'd both easily agree on a bunch of very technically imp…

Classic example of moving the goalposts. You'll keep doing it no matter what examples people give you.

Re: Contracts for C

#108

Earlier quoted context omitted.

I'm not convinced. If something is going to cause a crash, like a nullptr, I'd rather crash near where the error happened with a nice error message, than hitting some UB crash god knows where. Do I want my app to crash at all? No, of course not. If it's crashing, there's a serious bug. At least now I know where to look for it. Should we pass back up an error signal instead of crashing? Yes, if it all possible, do tha…

I think the point is about e.g. malloc as shown in the blog post. The stdlib function already have return values to indicate invalid arguments from the caller. These exist to allow the caller to decide what to do in this case. Replacing them by invoking UB or panicing, means the user looses control. Having the stdlib expose both ways, means either having two stdlibs (like on MS Windows), or dynamic checks in the stdl…

Precisely. The barn door is already open as far as the C library goes. And since C's error mechanism is all in-band (another bad idea in hindsight), the only thing the extra invariant checks will do is add even more (now uncontrollable) crash points to an already intolerably crash-prone app.

Panics are like goto: Only useful in VERY rare circumstances. Every use of a panic should require a rock-solid justification for why you're choosing to crash the process - similar to how every call to abort() requires justification in any professional C codebase. assert() in production code was a horrible idea because liberal use was actually encouraged.

Rust's panic mechanism was almost good. Unfortunately, they chose to give them all innocuous names like unwrap() rather than or_panic(). So now you have to check for it all the time using clippy because it's too easy for a human to accidentally gloss it over. Linting usually points to a design failure in the language UX.

Re: Contracts for C

#109

Earlier quoted context omitted.

C++ is still trying to catch up with: - compile time function execution - modules - no preprocessor - memory safe arrays - preprocessor replacement - ranges and so on.

> C++ is still trying to catch up with (...) C++ modules are indeed a mess, but you are fooling yourself if you believe that the preprocessor of all things is a compelling reason to switch. In fact, I think you unwittingly proved my point on how interest in D fizzed out the moment C++11 was released.

> if you believe that the preprocessor of all things is a compelling reason to switch

The preprocessor is an unhygienic, ugly mess. Just look at the system .h files, which should be a showcase on how to use it correctly. I stand by my assessment of it.

Re: Contracts for C

#110

Earlier quoted context omitted.

C++ is still trying to catch up with: - compile time function execution - modules - no preprocessor - memory safe arrays - preprocessor replacement - ranges and so on.

As I am oblivious to D, may I ask if there are suitable GUI toolkits for it, or bindings? I typically use wxWidgets in C++ land.

https://wiki.dlang.org/GUI_Libraries

wxD is the wxWidgets library.

Post reply on HN