5 ms·
This sounds like Regehr's "Proposal for a Friendly Dialect of C" [1]. I believe that was a well thought out argument, but ultimately did not make headway [2].
by raphlinus 8y ago
This sounds like Regehr's "Proposal for a Friendly Dialect of C" [1]. I believe that was a well thought out argument, but ultimately did not make headway [2].
[1] https://blog.regehr.org/archives/1180 https://blog.regehr.org/archives/1180
[2] https://blog.regehr.org/archives/1287 https://blog.regehr.org/archives/1287
- iainmerrick 8y agoI haven’t seen a good explanation of the arguments against this kind of thing. Who doesn’t want to fix the UB weirdness? Are there really many people worried it will completely break optimization? If so I’d love to hear about it. Even if you don’t agree with this proposal, or with Regehr’s, surely a reasonable response would be “okay, yeah, it would be good to do something along those lines, but not that; how about this?”
- pjmlp 8y agoCheck Chandler Carruth's talks about UB, he already has a couple of them.
- vyodaiken 8y agoThey exhibit a really badly mistaken idea of the role of the compiler.
- raphlinus 8y agoI don't think there are good arguments against it, and I agree it would be great to fix it. But I think it's very important to understand the reasons why Regehr's proposal failed. I also think it's incumbent on new proposals to argue why their effort is likely to succeed where Regehr's did not. If I were more cynical, I'd say that the C-standards community has simply ceded the ecosystem niche of a low-level language which has common-sense behavior, creating an opportunity for other languages such as Rust, D, Zig, etc. But I don't really believe that. Almost everything we do relies on C at the bottom, and it's pretty much the only viable way to publish an API that can be consumed by multiple languages.
- geofft 8y ago> Almost everything we do relies on C at the bottom, and it's pretty much the only viable way to publish an API that can be consumed by multiple languages. I think it's true that almost everything we do relies on the C ABI at the bottom. That's different from relying on C, and I think these are actually the same problem: the C ABI is straightforward and has been implicitly stable (whereas e.g. the Rust ABI is explicitly not stable) because the C ABI has very few types and very little information in types (aliasing, ownership, alignment, thread-safety, parameter types of functions, etc.) and the more common-sense lower-level languages are obligated to have meaningful types. You can certainly publish a C API / ABI from a Rust library and consume it from a D program without either side knowing that the other side isn't actually written in C. I am curious what is the next meaningful step beyond the C ABI and the C type system: even stabilizing the Rust ABI (which I would love to see) isn't going to help a lot for writing D libraries that implement them or Python programs that consume them, partly because Rust has unique ideas of things like lifetimes that don't immediately translate to other languages. What's the subset of stuff that's in all these languages' type systems that isn't in the C ABI? I'm reminded of Vala (which I kind of miss), which was GNOME's attempt a couple years ago at a better-than-C language that was very explicitly C-ABI-compatible. It used the GObject protocols for types - is GObject the shape of thing to standardize?
- jcranmer 8y ago> I am curious what is the next meaningful step beyond the C ABI and the C type system The C ABI and type system omits a lot of stuff that could be useful. Some points off the top of my head: * Vector types * Multiple return values * Functions with bound environments * Coroutines * Dynamically-sized callee-allocated structures (most notably lists) * Distinguishing between "byte array" and "string" types * Vtables * Something that lets GC'd languages cooperate more nicely. I'm not sure how exactly to specify this, but this is the sort of thing we could have if we stopped insisting that everything must be in terms of C ABI.
- geofft 8y agoHm, are there any cases of two GC'd languages that interop well that were not designed to interop from day one (that is, JVM languages don't count)? The only one that comes to mind is https://research.mozilla.org/2014/08/26/javascript-servos-only-garbage-collector/ https://research.mozilla.org/2014/08/26/javascript-servos-on... , which is Rust (pre-1.0) and JavaScript, but that somewhat doesn't count because Rust isn't really a GC'd language. (This article was written about a year after the special syntax for garbage-collected references was removed and Gc<T> became just a thing in the standard library, and about a month before that type was also removed because it wasn't a very good GC.) It seems to me that if you make your GC a purely refcounting system this is easier: you just need to specify how to incref and decref objects, and put a well-known function in the vtable for destroying and deallocating the object. I'd guess that's how things like GObject or Qt bindings in Python or similar languages work.
- geofft 8y agoFWIW, my personal response is "C does not carry enough information with variables to make productive high-performance programming possible; how about switching to a language that does?" The trouble is that "carry information with variables" means one of two things, runtime checks (which often - but not always! - rule out "high performance") or types. Rust is my personal preference here, but it's hardly the only option - Go and idiomatic C++14 are also good direct competitors, Java and PyPy can be faster than AOT-compiled code if you don't mind startup time, many applications actually aren't in need of a language that aggressively optimizes (e.g., it's relatively short logic on top of a database that someone else is writing in C), etc.
- vyodaiken 8y agoIt's perfectly reasonable to argue that C is not as good as XYZ. You may even be right. But it is not reasonable to conclude from that, that it ok for C implementations to make C programming more hazardous than it needs to be. Go out and make Rust a more appealing alternative to C and good luck with it. But don't tell me C can be turned into mush because you don't much like C.
- pjmlp 8y agoRest assured, it is quite clear to us that until we get rid of UNIX clones, C isn't going anywhere. So the only alternative is to improve the band-aids and keep sponsoring PhDs on security research related to C.
- geofft 8y agoDon't get me wrong, I like C. I don't want to write production software in C any more, but I do actually like the language a lot. Le cœur a ses raisons. C is already mush. Maybe it wasn't in the 1980s, but it certainly has been since I first started learning C (around 2000). I'm simply responding out of rational self-interest to recognizing that C cannot be what I want it to be, and what I want it to be looks an awful lot like Rust (or Go, or Swift, or...). C doesn't even have sum types with a language construct for exhaustive matching. That, by itself, makes C programming dangerous. For every NULL check unexpectedly deleted by a compiler, there are a thousand NULL checks that weren't written in the first place, because C has no mechanism to require a NULL check before using a pointer.
- jcranmer 8y ago> I haven’t seen a good explanation of the arguments against this kind of thing. Who doesn’t want to fix the UB weirdness? Are there really many people worried it will completely break optimization? If so I’d love to hear about it. If you're not a compiler writer, you probably have a mental model of undefined behavior that works like this: if (expr->invokes_undefined_behavior()) { // Yay! Silly user! shit_on_users_code(); } Sure, you probably don't think it's so brazenly stated, but the general sentiment from compiler users tends to be that all you have to do is delete that if statement and everyone's happy. But that's not how compilers work, not in the slightest. Instead, the undefined behavior effects tend to work like a chain: you dereferenced a pointer, therefore it's now safe to assume on any subsequent path that it will always be safe to dereference that pointer, therefore we can move that later load out of that loop even though we don't know if the loop will be executed in the first place. And the reason that logic works is because we're allowed to optimize under the assumption of undefined behavior in one of those steps. Now in some cases--signed integer overflow and strict aliasing come most readily to mind--there is a single knob you can twist to disable the undefined behavior (and most compilers let you twist that knob with command-line flags). Arguing whether or not the standard itself should dictate a different position on that knob is one thing, and it's by no means a bad discussion to have. But instead arguing "let's let you keep undefined behavior, but let's try to specify what you can do with undefined behavior" runs into the problem that it's very difficult to actually specify what somewhat-constrained undefined behavior really means. Look up the massive email threads on LLVM that try to tease what poison and undef actually mean.
- vyodaiken 8y agoSince -fno-delete-null-pointer-checks works in both GCC and clang and, nevertheless, the compilers are able to do significant optimization, you might want to think it out again.
- comex 8y agoYes, by removing the undefined behavior. The parent post differentiated this from keeping the behavior undefined but somehow constraining it, which is what your proposal tries to do.
- vyodaiken 8y agoIt is indeed influenced by Regehr's proposal, but is written as a concrete proposal to the WG14 committee.