| |
| ▲ | krig 6 days ago | parent | next [-] | | Well, sure. If that’s what you mean by having UB then it’s trivially true for any language with a C FFI for example, or rust with unsafe. I guess what I consider UB is when the compiler exploits UB for optimizations, like discarding code that could invoke UB, which as far as I know odin doesn’t do. | | |
| ▲ | jibal 6 days ago | parent [-] | | > I guess what I consider UB is when the compiler exploits UB for optimizations, like discarding code that could invoke UB, which as far as I know odin doesn’t do. This statement is incoherent. UB is undefined behavior, and it existed long before any compiler exploited it and isn't (circularly) defined by whether the Odin compiler exploits it. | | |
| ▲ | krig 6 days ago | parent [-] | | Well, the statement is circular, colloquial speech doesn't have to be coherent. It's my understanding of what people generally mean when they complain about UB in the C standard, and it seems to be what gingerbill means too. My take on what he is saying is that the odin compiler won't try to exploit that there is some behavior which is platform-defined or only knowable at runtime to do aggressive optimizations etc. https://xcancel.com/TheGingerBill/status/1495004577531367425 To point out that use after free is possible in Odin is not really a gotcha unless you really are just arguing semantics. That's by design, just like use after free is possible in C or C++ or Rust too. | | |
| ▲ | tialaramex 6 days ago | parent | next [-] | | > To point out that use after free is possible in Odin is not really a gotcha unless you really are just arguing semantics In a language with UB, the use after free is UB. Which explains the nonsensical results. If you're pretty sure this all makes sense, I recommend one tiny tweak to further unsettle you, change either (but not both) of the int types in my example to u8 instead. Now the use after free also results in type confusion - Odin has no idea this isn't the same type and so the machine code generated is for one type but the actual bits are for a different type with a different layout. Bill's go-to is to blame somebody else, it's the operating system, or even the CPU which should define what happens and so it's not his fault. The thread you linked does this. But for type confusion those are Odin's types, nobody else can define how Odin works, the answer must come from Bill. What is supposed to happen here? Linux didn't define your programming language, Intel didn't define your programming language, this is your fault Bill. | |
| ▲ | jibal 6 days ago | parent | prev [-] | | Your understanding is completely wrong. UB means "undefined behavior" -- behavior that is not specified by the language standard or the implementation. UB being exploited by the compiler is a separate issue. Saying that there is no UB is saying that there's no undefined behavior; it is certainly not merely saying that the compiler doesn't exploit it. I programmed in C for over 30 years and was a member of the C Standards Committee, which originated the language about undefined behavior ... I know what I'm talking about. > To point out that use after free is possible in Odin is not really a gotcha unless you really are just arguing semantics. That's by design, just like use after free is possible in C or C++ or Rust too. This completely misses the point and is a failure to understand at every level. Being able to use memory after being freed is not by design -- no one intends it, no one wants it. It's undefined behavior, and a program that does it is buggy. The reason that it's possible is because it's so hard to detect or prevent. To do so requires escape analysis, lifetime declarations, borrow checking, etc. etc. And no, use after free is not possible in Rust--not in safe code. It's hard to respond to that statement without being rude, so I will say no more. | | |
| ▲ | krig 6 days ago | parent | next [-] | | Well, first of all, I guess I am wrong! Hey, I'm not Bill, just a user of the language. A couple of clarifications, though: I did mean unsafe rust, not the safe subset. No need to get rude! Second of all, I am of course not under the illusion that Odin prevents use-after-free (and thus, technically, it does allow UB I guess). I just don't think Bill is either. So clearly he doesn't mean UB by the same definition as you do. _My_ use of UB has always been in the context of what a compiler will do during optimization, and the discussion I've seen in the context of C compilers is that they perform optimizations that remove code or change code in surprising ways because the way the code was written technically resulted in UB. But I'm neither a spec writer or a compiler author, so I don't really care that much about the actual definition of the term. Anyway, best of luck in convincing Bill to use the term correctly as well! I won't mention UB when talking about the benefits of Odin in the future. :) | | |
| ▲ | jibal 6 days ago | parent [-] | | > So clearly he doesn't mean UB by the same definition as you do. Wrong. > so I don't really care that much about the actual definition of the term. Yes, it's evident that you don't care what's true or about being accurate. > Anyway, best of luck in convincing Bill to use the term correctly as well! He does use it correctly, but his claims that Odin has no UB are incorrect. Over and out. |
| |
| ▲ | flysand7 5 days ago | parent | prev [-] | | As someone who knows what they're talking about, I'm curious to hear from you, why you consider use-after-free an undefined behavior and not an unspecified behavior instead? Because as far as I know both undefined behavior and unspecified behavior are the behaviors that aren't specified in the language standard nor the implementation. So what's the difference? | | |
| ▲ | tialaramex 4 days ago | parent [-] | | A useful example to consider might be an uninitialized integer in C++ In C++ 98 "int x; foo(x);" is Undefined Behaviour. We said there's an integer named x, then, without initializing x, we passed it as a parameter to the function foo, evaluating the uninitialized value, literally anything is allowed to happen. Program crashes, deletes all the JPEGs large than 15 kilobytes, displays "Boo!" on the screen, anything the program could have done is something it might now do. In C++ 26 "int x; foo(x);" is merely Erroneous Behaviour. The value of x is Unspecified, but it does have a value. This program might pass any integer to foo -- perhaps your compiler provides a nice compiler setting to choose one, maybe the person building the program picked 814 and so this calls foo(814) This constraint is an enormous difference. In a sense the Unspecified behaviour is defined, it's just not specific. That variable x will have some integer value, so, maybe zero, or 814, but it can't be a string, or negative infinity, and evaluating it will just do what it would do for its value, whatever that was. If you don't find that example illuminating enough, try another from a very different language, a data race in Java Now, in C++ (or Odin though of course Ginger Bill will insist otherwise ad nauseum) the data race is UB. Game over, you lose, anything might happen. But in Java the data race has Unspecified behaviour. Specifically, when the race happens the variable we raced has some definite value and it's a value it definitely could have had at or before this moment in a sequentially consistent program. So e.g. maybe we're counting clowns, we started from zero, thread A is counting 800 clowns, thread B is counting 600 clowns, but the threads race the same counter, legitimate values we might see at the end include zero all the way through 1400 clowns. -1 isn't possible, 1600 isn't possible, but 1234 is entirely plausible. That's Unspecified behaviour. | | |
| ▲ | gingerBill 3 days ago | parent [-] | | Sorry to pop in but I kind of need to clarify my own position even further here: Odin has "undefined behaviour". SHOCK! HORROR! The thing I bring up that people think I go "ad nauseum" about (like yourself) is just that the term "undefined behaviour" is in itself "undefined". This is why I try to make as many distinctions about the different kinds of "UB" out there, and even what the "U" means in "UB". To quote you from earlier: > ...Ginger Bill will engage in pointless semantic arguments in which he defines away your problem. Yes... the entire discussion about "UB" is fundamentally a semantic thing, LITERALLY it's an argument about semantics. And if I can "define away [the] problem" in certain cases, then it's literally not Undefined Behaviour. I am not sure what else to say to your point here. The entire discussion around UB is fundamentally a semantics game and always will be. And when people complain about the "semantics game" part, they have not understood the problem in the first place. Interestingly, the C++26 example you give is a brilliant example of this, it's an Erroneous Behaviour, not necessarily "undefined" or "unspecified". In the case of race conditions in Odin? This is pretty much a brilliant example of _Unobservable_ behaviour because Odin shares a similar memory model to C, and thus it has to make assumptions. And when the programmer violates certain assumptions, the compiler cannot observe the broken code that easily, and thus it can easily produce unexpected behaviour due to it being unobservable. I know have said things in passing, but it's a question of what to tell people. A lot of the people who bring up examples of UB bring up are usually TRIVIALLY definable, and things we have probably already defined in Odin (e.g. integer (under|over)flowing, integer division by zero, etc), but when I say what Odin's defined it to, they usually go one of two ways, either "I don't like that definition" or "but I wanted to use these specific passes in the compiler backed to do 'optimizations' with", both of which actually annoys me because I am not discussing with someone serious. In the latter case, "passes" are just algorithms that do things; they are not intrinsically "optimizations". To "optimize" something, you need well defined rules to optimize within, if the rules are ill-defined or arbitrary chosen, then how the heck can anyone call that "optimization"? > In a language with UB, the use after free is UB. Which explains the nonsensical results. [https://odin.godbolt.org/z/8onn4hxP1] Even the case of "use after free" that you brought up earlier, it is something which is not a language thing in certain cases, especially when Odin has custom allocators which are defined at the user-level. The language itself knows nothing about of the allocators and how they work, it cannot. So yes, Odin allows for "use after free" but and? We are not enforcing a specific memory allocation strategy at the language level, like other languages. But it could be technically well defined at the algorithm level of that specific allocator. Which leads to my many problem with this discussion: are flawed algorithms/code considered Undefined Behaviour now and thus make the language "UB"? Why is not just shitty code? And I will literally go... your "nonsensical results" are just... allocator specific behaviour. Yes it's "nonsensical" to you, but Odin isn't C so stop applying its approach to things to it. C has its standard library as part of its spec and defines how `malloc` et al work. Odin doesn't have its core library as part of the spec and never will (base library will to a certain extent with room for implementation specified behaviour for certain defaults, e.g. what is the default `context.allocator` or `context.assertion_failure_proc`, etc). > Bill's go-to is to blame somebody else... No... my go-to response is to say where it should be defined, and if it is possible to define at _any_ level (if at all). So even in your case, you are blaming the compiler for something it cannot know if this is undefined or erroneous because it cannot know what you are doing, but the allocator itself could allow for it. `delete` could be a complete no-op and is for many allocators. It's all about responsibility. What is responsible for what. So if you thin that's "blame", whatever, but that's the question. Is the compiler _responsible_ for "use-after-free" bugs? In some languages, yes. In other languages, no. > Linux didn't define your programming language, Intel didn't define your programming language, this is your fault Bill. No but they all put bounds on what is possible in the first place. They literally define the arena the language takes place in, and thus you are subject to their whims, regardless of my feelings about them. No it's not "my fault" anywhere, it's just a thing. ---------- If you want to discuss with me further on this, I'd be happy to do so. Other mediums other than a comment on Hacker News would be preferrable. ---------- P.S. nowhere on the Odin website itself do we say Odin has no "UB" nor even suggest it. So there is no official position on "UB" for Odin anywhere. My comments on Twitter or other social medias should not be taken as official in any way, and be considered closer to "conversation", especially Twitter. P.P.S. Some of possible meanings of "U": Undefined (i.e. not-defined), Undefinable, Unspecified, Underspecified, Unobservable, Unknown, Erroneous (I know it's not a U), Unimplemented, Ill-Defined (different to Undefined or Underspecified, and again not a U), Defined-At-A-Different-Level (e.g. language, compiler, platform, core library, user's code, etc, and again it's not a U). It's an absolutely mess and things just need to be defined better about what you are talking about. |
|
|
|
|
|
| |
| ▲ | ixwt 6 days ago | parent | prev [-] | | This is a simple use after free on the stack. Is that UB? | | |
| ▲ | jibal 6 days ago | parent [-] | | Yes, of course ... the content of freed memory, on the stack or otherwise, is not defined. (And this is not in fact on the stack.) | | |
| ▲ | ixwt 6 days ago | parent | next [-] | | 1. I'm fairly certain you have to use make to get into heap. 2. Odin 0s out memory when declaring a variable unless you explicitly state so with ---. This defines the state of memory when allocated. | | |
| ▲ | tialaramex 6 days ago | parent [-] | | Ordinarily you'd be correct that you need the weird make overload, but I had no cause to invoke make I just told Odin that we don't care and it's fine, check the first line. Whether that feature is a good idea in this language I couldn't say. Odin's decision to zero initialize local variables isn't relevant here. | | |
| ▲ | ixwt 6 days ago | parent | next [-] | | Huh. Wasn't aware of that feature. Good to know. I didn't fully flesh out the initializing local variables: What part of your code is undefined? You deleted the memory, and the compiler reused it. Then you re-accessed that same memory. That's just part of working with computers. The initialization comment was supposed to be from creating data to releasing it is defined. To be compliant with the Odin compiler spec, it's defined from start to end. | | |
| ▲ | SkiFire13 6 days ago | parent [-] | | Not OP but: > What part of your code is undefined? Using a variable (`some_map` in this case) after `delete`ing it doesn't seem something languages usually define in their specification. Does Odin define that? |
| |
| ▲ | krig 6 days ago | parent | prev [-] | | I don't really get the distinction between adding the dynamic-literals feature flag and using unsafe in Rust? Like, if he had called it #+unsafe dynamic-literals, would that have been better? | | |
| ▲ | tialaramex 6 days ago | parent | next [-] | | I'm going to guess that you've never written, and perhaps never read, any unsafe Rust, because you (in common with several Rust critics) seem to be imagining it like a switch you can turn on somehow to disable the safety rules, and that's not what it is at all. This Odin feature flag just allows me to write what I meant in Odin, I can write it all out by hand using make and so on without the feature flag, but it's more annoying to spell that all out which presumably was the impetus for this feature flag in the first place. The flag didn't somehow "cause" the unsafety, that's an insane take. Odin isn't very well documented, so as somebody who was writing Odin just to explain the problem here, the easier option avoids trying to guess which of a dozen undocumented functions with names that may be related to hash maps is the "right" function to do what I meant, I can just write what I meant and turn on the feature flag to acknowledge that it involved allocation. | |
| ▲ | krig 6 days ago | parent | prev [-] | | I can't reply to the reply to this one (guess the thread is getting too deep), but I just wanted to clarify that I'm not a rust critic, I use rust, I like rust, I have no problem with rust. OK great, moving on... | | |
| ▲ | tialaramex 6 days ago | parent [-] | | The lack of reply is probably because the thing you wanted to respond to was new. This is a deliberate HN behaviour to discourage frantic conversations, like you might have if this was a Zoom call for example, or we'd had this disagreement in a bar (presumably a bar where there's a Compiler Explorer built in, or maybe literally Matt Godbolt is sat there with a laptop?) Maybe it works? I have had some positive experiences. If you know unsafe Rust the comparison to this Odin feature feels odd, but OK. Surely the Odin feature is more like when let-chains was unstable but you could tell the Rust nightly compiler you want that feature anyway ? Or... explicit_tail_calls which gives you a way to write explicit tail calls† in nightly Rust ? † This is a very cool feature if you're a functional programmer using the "become" keyword, you get tail call optimisation but it's mandatory, if what you've written can't work as a tail call then it won't compile, if it could work it does. |
|
|
|
| |
| ▲ | psychoslave 6 days ago | parent | prev [-] | | Isn't there a cheap way to implement a gentle refusal to compile a code if it is not garantee that it won't prevent such a behavior? | | |
| ▲ | jibal 5 days ago | parent | next [-] | | No, definitely not. The solution is something like Rust, with its lifetimes and borrow checker ... not cheap at all. | |
| ▲ | Hemospectrum 5 days ago | parent | prev [-] | | You mean a borrow checker? That exists, but not for Odin. |
|
|
|
|