<p>In my answer to another question, <a href="https://langdev.stackexchange.com/questions/3079/what-was-the-purpose-of-tartans-disable-statement">What was the purpose of Tartan&#39;s &quot;disable&quot; statement?</a>, I asserted that it's anachronistic to use our modern C-derived definition of &quot;undefined behavior&quot;, when reading a document about another language from 1978. (EDIT: The wording used in that document was actually &quot;the consequences are not defined&quot;, not the exact phrase &quot;undefined behavior&quot;. I think my overall question here is still interesting.)</p>
<p>The C and C++ standards define &quot;undefined behavior&quot; as &quot;behavior for which this document imposes no requirements&quot;. The first instance of this definition I can find is in the first official C standard, which was apparently published as a draft in 1985, and officially in 1989. The unofficial standard for C before that was &quot;The C Programming Language&quot; by Kernighan and Ritchie, published in 1978. I don't completely trust my PDF copy of it, but I didn't find a definition (or even a mention) of &quot;undefined behavior&quot; in a quick search of that book, and it does not appear in the index.</p>
<p>So: Is the 1989 publication of the C89 standard the first origin of the notion that &quot;undefined behavior&quot; means &quot;anything can happen&quot;? (The phrase &quot;nasal demons&quot; was to my knowledge not coined until 1992: <a href="https://groups.google.com/g/comp.std.c/c/ycpVKxTZkgw/m/S2hHdTbv4d8J?hl=en" rel="noreferrer">https://groups.google.com/g/comp.std.c/c/ycpVKxTZkgw/m/S2hHdTbv4d8J?hl=en</a> ) What was the common understanding of &quot;undefined behavior&quot; in 1978, if there was one?</p>

<c><undefined-behavior>
When was our current definition of "undefined behavior" first used?
CC BY-SA 4.0
2023-11-04T22:19:56.690Z
The phrase "undefined behaviour" wasn't actually used in any of the documents in the other question, or even "undefined".
Michael Homer
CC BY-SA 4.0
2023-11-04T22:26:57.557Z
Hm, you're right, it said "the consequences are not defined". So my question here is not quite the thing relevant to know. I still think it's worth asking as I wrote it, so I'll leave it up (slightly edited) and see what I learn.
Glenn Willen
CC BY-SA 4.0
2023-11-05T03:49:16.377Z
Be sure to check for synonyms when researching. Appendix A Section 16 in my first edition of TCPL uses the phrase "unspecified" or "not specified" to refer to the order of evaluation of function arguments and side effects.
Ryan Cavanaugh
CC BY-SA 4.0
2023-11-05T07:03:58.227Z
Interesting find. "Unspecified" is very definitely not a synonym here, though. In the modern C standard, "unspecified" means something completely different from "undefined". In TCPL there I assume it is just taking the ordinary English meaning...
Glenn Willen
CC BY-SA 4.0
2023-11-20T10:50:17.863Z
**Comments have been [moved to chat](https://chat.stackexchange.com/rooms/149790/discussion-on-question-by-glenn-willen-when-was-our-current-definition-of-undef); please do not continue the discussion here.** Before posting a comment below this one, please review the [purposes of comments](/help/privileges/comment). Comments that do not request clarification or suggest improvements usually belong as an [answer](/help/how-to-answer), on [meta], or in [chat]. Comments continuing discussion may be removed.
lyxal
<p>To add to supercat's answer, in which they state that C89 did not define &quot;undefined behaviour&quot; in this way, I <a href="https://www.yodaiken.com/2021/05/19/undefined-behavior-in-c-is-a-reading-error/" rel="nofollow noreferrer">have seen the argument made</a> that this is a <em>possible</em> yet originally <em>unintended</em> reading of the standard, which was only adopted in C99.</p>
<p>To summarize the argument from the linked blog post, as I understand it, C89 defined the term as:</p>
<blockquote>
<p>Undefined behavior --- behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).</p>
</blockquote>
<p>Now, depending on how you interpret the commas, this can either be paraphrased as &quot;Undefined behaviour is the behaviour which occurs upon using certain constructs whose behaviour the standard does not otherwise define. This behaviour SHALL be any one of [a list of permissible behaviours]&quot;, or &quot;Undefined behaviour is the behaviour, which this standard does not define, which occurs upon using certain constructs. This behaviour SHALL be any one of [a list of permissible behaviours]&quot; (where I use &quot;SHALL&quot; in the <a href="https://datatracker.ietf.org/doc/html/rfc2119" rel="nofollow noreferrer">RFC 2119</a> sense)</p>
<p>Since the latter interpretation is contradictory (by claiming the standard places no requirements on what the behaviour may be immediately before defining such requirements), the argument goes that the former interpretation is evidently the intended one. Nevertheless, for C99 this ambiguity was resolved in favor of the latter interpretation, changing the definition to:</p>
<blockquote>
<p>1   undefined behavior<br />
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,<br />
for which this International Standard imposes no requirements<br />
2   NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable<br />
results, to behaving during translation or program execution in a documented manner characteristic of the<br />
environment (with or without the issuance of a diagnostic message), to terminating a translation or<br />
execution (with the issuance of a diagnostic message).</p>
</blockquote>
<p>The comma placement leaves less room for ambiguity, and the addition of &quot;NOTE&quot; and the use of &quot;Possible&quot; instead of &quot;Permissible&quot; changes what follows from normative to advisory. Giving us an approximate definition of &quot;Undefined behaviour is the behaviour, which this standard does not define, upon using certain constructs. This behaviour MAY be any one of [a list of permissible behaviours]&quot; (again, see <a href="https://datatracker.ietf.org/doc/html/rfc2119" rel="nofollow noreferrer">RFC 2119</a> for my use of &quot;MAY&quot;).</p>
<p>Granted, you are welcome to disagree with with that argument, you're evidently in plentiful company if you do.</p>
<h2>So if not C89, then when?</h2>
<p>Well, I also can't provide a specific date. But <em>if</em> the C89 authors (or even just a portion of them) intended to use the term differently, that tells us that they didn't expect the modern definition of the term to be what readers would assume in case of perceived ambiguity (and presumably they also wouldn't expect there to <em>be</em> any ambiguity in the first place). While this is certainly not proof that the term had <em>never</em> been used that way before, it does suggest that this definition at least did not become <em>dominant</em> until some time <em>after</em> the 1989 publication of the C89 standard.</p>

CC BY-SA 4.0
2023-11-05T22:29:12.863Z
2023-11-05T23:57:51.807Z
2023-11-05T23:57:51.807Z
Sara J
CC BY-SA 4.0
2023-11-05T23:08:03.857Z
I don't think the list of possible outcomes was intended to be exhaustive even in C89.  Far more significant is that the Standard was never intended to be the only thing which constrained how implementations would process various corner cases.  Market forces were expected to act as a check to gratuitously nonsensical behaviors.
supercat
CC BY-SA 4.0
2023-11-05T23:17:45.010Z
Fundamentally, the authors of C89 used UB as a catch-all for situations where *they didn't want to concern themselves with details of what exactly could or couldn't happen*. Trying to parse the definition of UB to decide what exactly is allowed to happen *misses the point*, which is that the authors of the Stanard wanted to avoid having to consider such issues in such cases.
supercat
CC BY-SA 4.0
2023-11-05T23:29:06.480Z
Thanks, this is exactly the sort of thing I'm interested in learning. I am hoping perhaps more people have older primary sources to chime in with, but existing arguments made later are also very welcome data.
Glenn Willen
CC BY-SA 4.0
2023-11-05T23:32:30.773Z
@supercat I'm told that ANSI and/or ISO standards do distinguish the use of "permissible" for requirements and the use of "possible" for advisory content, but I freely admit I don't read enough standards to be certain whether or not I've been misled. It's also possible that I could have misinterpreted the intended strictness, confusing (what RFC 2119 would call) SHOULD and MUST. Still, I'll leave the post as it is, based on how the standard's wording was explained to me, until someone convinces me that explanation was wrong
Sara J
CC BY-SA 4.0
2023-11-05T23:37:30.963Z
@SaraJ: A good standard for things that are supposed to work together (e.g. Acme plugs and Acme sockets) should be designed broadly enough that anything that can be accomplished by a device that fits an Acme socket should be possible with a  conforming Acme plug, anything that can be done by a device that fits an Acme plug should be possible with a conforming Acme socket.  It should, however, be tight enough to say something useful about arbitrary combinations of Acme plugs and Acme sockets.  Doing this with C programs and implementations would have been impossible...
supercat
CC BY-SA 4.0
2023-11-05T23:40:19.020Z
...without either recognizing more than two categories of C implementations, or else using one definition of "conforming C program" which is broad enough to encompass all programs that can operate with at least one conforming C implementation, and a definition of "strictly conforming C program" which was expected to exclude many of the tasks for which C was most uniquely useful.  Really, the C Standard has always been a description of some features that should be common to all C implementations, along with some standardese so it could pretend to be a "standard".
supercat
CC BY-SA 4.0
2023-11-05T23:45:26.893Z
@SaraJ: There are a few language design issues, such as the handling of variadic functions, where having all implementations follow a slightly imperfect standard woul be better than having each implementation do things in whatever way would work best on its target platform.  The existence of the Standard improved those parts of the language.  For many parts of the language, though, any good standard would need to recognize that the best way of treating a construct for one task might make an implementation unsuitable for some other tasks.
supercat
<p>No, C89 is not the first place that notion was articulated, because C89 <em>doesn't</em> articlate that notion (and nor does any subsequent version of the Standard).</p>
<p>As far as the Standard is concerned, the term &quot;Undefined Behavior&quot; means nothing more nor less than that the behavior of the construct or corner case falls outside its jurisdiction.  This implies that such case must not arise in any program whose behavior is to be fully specified by the Standard, i.e. <em>strictly</em> conforming C programs.  Outside of the narrow realm of strictly conforming programs, however, the range of ways in which most compilers could behave in cases where the Standard imposes no requirements was expected to be constrained <em>by a desire of compiler writers to sell compilers</em>, and a consequent <em>need to avoid behaving in a way customers and potential customers would view as unacceptable</em>.  While the Standard doesn't exclude the possibility that a compiler whose maintainers are hostile or indifferent to the needs of programmers targeting it might behave in completely unconstrained fashion in situations where the Stanard imposes no requirements, the &quot;UB means anything can happen&quot; notion would only be applicable to people who are stuck targeting such compilers.</p>
<p>A religion has formed among compiler writers who are sheltered from normal market forces, around the idea that the Standard only uses the phrase &quot;Undefined Behavior&quot; in situations which would never arise in any correct programs, interpreting the phrase &quot;non-portable or erroneous&quot; as &quot;non-portable, and therefore erroneous&quot;, but that ignores not only the published Rationale documents for C89 and C99, but also <em>every version of the C Standards Committee's charter to date</em>, every single one of which has explicitly recognized the legitimacy of non-portable C code, and stated that the Standard it not meant to preclude the use of C as a form of &quot;high-level assembly langauge&quot;.</p>
<p>I think the real shift came when proponents of clang and gcc were able to describe as &quot;mainstream&quot; behaviors which are unique to those two compilers and derivatives thereof, which for many tasks are effectively exempt from market competition by commercial compilers.  If a programmer buys a $500 compiler that totally wipes the floor with clang and gcc, but wants to distribute code in contexts which would require that the recipients rebuild it, such distribution would require that the code be reworked to run on lower-quality freely distributable compilers, effectively negating any advantage the programmer should have been able to reap from buying a better compiler.</p>
<p>Incidentally, the C89 Standard <em>does</em> make a key distinction between &quot;UB&quot; and other behaviors that aren't fully specified by the Standard in the &quot;as-if&quot; rule and a corollary to it whose effects were intended to be minimal but which have become disastrous.  Under the as-if rule, compilers may only perform optimizing transforms whose effects would not be observable under any defined program executions.  A consequence of this is that if such a transform would observably affect the behavior of some sequence of actions, <em>at least one of the actions involved must be characterized as invoking Undefined Behavior</em>.  This wouldn't be a problem if compilers interpreted the latitude they were given to be as narrow as required to perform specific useful tranforms.  Unfortunately, aggressive compilers go beyond that, performing transforms that are seldom useful and are often best counter-productive.</p>
<p>An unfortunate aspect of this situation is that in many cases it may not be impossible to predict <em>precisely</em> how the optimal machine code <em>that satisfies an application's requirements</em> would behave in certain corner cases.  Thus, allowing implementations to perform optimizing transforms in ways that might replace one acceptable corner-case behavior with another <em>equally acceptable</em> behavior would allow them to satisfy application requirements more efficiently than would otherwise be practical.  If integer overflow were characterized as Implementation-Defined Behavior rather than UB, a compiler for a platform where integer overflows are trapped, given something like:</p>
<pre><code>int test(int a, int b)
{
  int temp=a*b;
  if (f1())
    f2(temp,a,b);
}
</code></pre>
<p>would be required to perform the multiplication (or at least determine whether it would overflow) unconditionally, before calling <code>f1()</code>, even though code would likely not care about whether the computation of <code>a*b</code> would overflow in cases where <code>f1()</code> returned zero.  Most applications whose requirements would be satisfied by the above code if integer overflow were characterized as Implementation-Defined Behavior would be satisfied equally well if not better if the code were transformed into:</p>
<pre><code>int test(int a, int b)
{
  if (f1())
    f2(a*b,a,b);
}
</code></pre>
<p>even on platforms where the transformation would affect program behavior, but the Standard's abstraction model is incapable of allowing the above transformation without waiving jurisdiction over program behavior in case of integer overflow.  While some compiler writers claim this allows more efficient code generation, it actually does the opposite by forcing programmers to write code in a manner that would block any optimizations transforms that could yield behaviors which would satisfy application requirements despite being inconsistent with the behavior of non-transformed code.</p>

CC BY-SA 4.0
2023-11-04T23:34:57.833Z
2023-11-05T19:18:34.030Z
2023-11-05T19:18:34.030Z
supercat
CC BY-SA 4.0
2023-11-05T07:02:38.213Z
"...such distribution would require that.." require in which sense? The only one that I could infer assumes it is unreasonable to expect from the recipients of the distribution to purchase the compiler, but if this is the case it's not clear where this assumption comes from.
Andrew Savinykh
CC BY-SA 4.0
2023-11-05T07:05:36.607Z
Presumably supercat is referring to open source. Nobody is going to buy a \$500 compiler to build an open source project. Most people not building on behalf of an employer can't afford a \$500 compiler.
Glenn Willen
CC BY-SA 4.0
2023-11-05T17:01:45.600Z
@GlennWillen: Bingo.  Though back in the 1990s, a fair number of people did buy tools around that price point--in 1990s dollars--for personal use (\$500 woul have been about the upper limit; many more people would buy tools around the \$100 price point).  Linus Torvalds desparately needed a freely distributable C compiler to make Linux viable, and tolerated gcc not because of its aggressive attitude toward optimization, but in spite of it.  It's too bad that people didn't learn early on that the proper response to "would the Standard allow a compiler to do something really annoying"...
supercat
CC BY-SA 4.0
2023-11-05T17:07:09.770Z
...shouldn't have been to figure out some way of interpreting the Standard to forbid it, nor to figure out some way for programmers to work aroun the limitation imposed thereby, but instead to have said "The Standard would probably allow a garbage compiler to behave in that fashion.  Why--do you want to write one?"  Many things are left undefined by the Standard not because there wasn't any commonplace behavior, but rather because there *was* a behavior which it was obvious immplementations should follow *absent a good reason for doing otherwise*.
supercat
CC BY-SA 4.0
2023-11-05T17:39:30.393Z
What's sad is that free compiler back ends like LLVM all seem to use abstraction models that are incapable of expressing many applications' actual requirements without needlessly blocking what should be useful optimizations.  I suppose maybe someone could come up with a commercial compiler that uses its own open-source-licensed back-end which could be distributed, along with intermediate-language output from the compiler, to anyone wanting to run programs processed with the compiler, but I doubt such a thing could be developed profitably in a way that would yield a widely affordable product.
supercat
CC BY-SA 4.0
2023-11-05T18:34:21.477Z
-1: While tangentially relevant, this does not answer the question.
Alexis King
CC BY-SA 4.0
2023-11-05T19:09:15.973Z
@AlexisKing: The question asks "Is the 1989 publication of the C89 standard the first origin of the notion that "undefined behavior" means "anything can happen"?"  Showing that the Standard doesn't say that is responsive to the question (though maybe I should more explicitly say the answer is "No, since the Standard doesn't express that notion it cannot be the first expression thereof").  Anyone seeking to sell compilers would be need to make the compiler behave in a manner tolerable to customers even/*especially* in cases where the Standard imposes no requirements.
supercat
CC BY-SA 4.0
2023-11-20T09:44:46.230Z
@AlexisKing Unfortunately, this is not an isolated incident. Every time someone asks about something UB-related on the network, supercat uses this as an excuse to write an off-topic jeremiad about what UB was “supposed” to mean and how his unicorn interpretation of the relevant specifications that avoids running into the principle of explosion is totally achievable, guys. You may have to adjust your remedies accordingly to something more substantial than just downvoting.
user3840170
CC BY-SA 4.0
2023-11-20T16:31:05.173Z
@user3840170: What you call "unicorn" explanation is based upon what *the authors of the Standard wrote* in the published Rationale.  I have yet to find any *primary source* indicating that the authors of the Standard intended anything beyond "The Standard imposes no requirements", or that they intended to invite compiler writers to deviate from common practices *except in a bona fide effort to help programmers satisfy objectives*.  Achieving good performance is a fine objective, but only if it doesn't interfere with the primary objective of making a program satisfy requirements.
supercat
CC BY-SA 4.0
2023-11-20T16:55:25.067Z
@user3840170: If an optimizer is designed around the idea that if transforms which are safe individually will be safe in combination, then the Principle of Explosion will make program behavior unpredictable in any situations where a transform might affect program behavior in otherwise benign fashion.  One approach to preventing that is to require that programmers identify a combination of transforms to allow, and write code in a manner that blocks all others.  I would suggest that it would be more useful to specify allowable transforms in a manner that makes them consistent.
supercat
<p>The ANSI Common Lisp specification contains the following definition in its <a href="http://www.lispworks.com/documentation/HyperSpec/Body/01_db.htm" rel="nofollow noreferrer">Error Terminology</a> section:</p>
<blockquote>
<p><strong>The consequences are undefined</strong><br />
This means that the consequences are unpredictable. The consequences may range from harmless to fatal. No conforming code may depend on the results or effects. Conforming code must treat the consequences as unpredictable. In places where the words &quot;must,&quot; &quot;must not,&quot;
or &quot;may not&quot; are used, then &quot;the consequences are undefined&quot; if the stated requirement is not met and no specific consequence is explicitly stated. An implementation is permitted to signal an error in this case.<br />
For example: &quot;Once a name has been declared by <code>defconstant</code> to be constant, any further assignment or binding of that variable has undefined consequences.&quot;</p>
</blockquote>
<p>It also has a related &quot;consequences are unspecified&quot; designation. This stipulates that the consequences are unpredictable but harmless. &quot;harmless&quot; isn't defined rigorously, but we can presume this precludes it from launching a nuclear missile. It might prohibit nasal demons, but allow nasal angels.</p>
<p>This was published in 1994, but the standardization committee was formed in 1986. I think this terminology was determined early in the process.</p>

CC BY-SA 4.0
2023-11-07T21:41:47.470Z
2023-11-07T21:41:47.470Z
Barmar
CC BY-SA 4.0
2023-11-07T23:01:20.283Z
The phrasing "the consequences are undefined" and "no conforming code may depend on the results or effects" seems to suggest that it's only what happens *after* the offending operation that is undefined. That is, the behaviour of the program up to the point where the offending operation is executed, should still exactly match the specified behaviour of the same program without that operation. If my interpretation is correct then that's a subtle difference between this notion and the modern notion of UB, which allows [time travel](https://devblogs.microsoft.com/oldnewthing/20140627-00/).
kaya3
CC BY-SA 4.0
2023-11-08T04:28:37.137Z
I tend to think of Lisp as an interpreted language, in which case undefined behavior would be a runtime event, rather than a static fact about a program. As such the effects could of course only move forward in time. Of course, many Lisps could also be compiled. But I could imagine the mental model of undefined behavior as a dynamic event in an ongoing computation, rather than a static property of a program, could still apply.
Glenn Willen
CC BY-SA 4.0
2023-11-08T16:43:17.177Z
Lisp should be thought of as a compiled language that includes an embedded interpreter. But in several implementations, the interpreter actually uses the compiler internally. @GlennWillen
Barmar
CC BY-SA 4.0
2023-11-08T18:40:59.027Z
A unique trait of C is that when the Standard was written, there were many constructs which would have defined behavior on some C implementations (typically processing constructs in a manner characteristic of the environment, with whatever consequences would result from doing so) but not behave consistently on all, and *such constructs were the primary means of accomplishing many tasks that couldn't be done efficiently, if at all, using portable constructs*.  When the C Standard characterized an action as UB, it meant implementations weren't *required* to define the behavior...
supercat
CC BY-SA 4.0
2023-11-08T18:42:28.590Z
...and not that they shouldn't process the action in a documented manner characteristic of the environment in situations where doing so *might* be useful and there was no *particular* advantage to doing otherwise.  Nearly all controversies surrounding UB involve situations where processing in a document characteristic of the environment *would* be useful, and a compiler writer deviates from that behavior in a manner the compiler writer claims is more useful, but which the programmer recognizes as useless *for the task at hand*.
supercat
CC BY-SA 4.0
2023-11-08T18:47:05.640Z
@supercat If you follow the link in my answer you'll see that CL has a much finer grained taxonomy of erroneous situations. Lisp has traditionally had lots of runtime error checking (e.g. array bounds checks), CL has configuration capabilities to enable/disable this, and the spec says how the error checks are affected by this. There are also specific points that are noted as places for implementation extension. C tends to lump almost everything into UB, although it does have so implementation-dependent situations.
Barmar
CC BY-SA 4.0
2023-11-08T18:54:52.270Z
@Barmar: I am unaware of Lisp ever having been widely used for the kinds of tasks that would be impossible in platform-agnostic code.  In Ritchie's Language, the statement `a=byteArray[i];` would take the address of `byteArray`, add `i`, load a byte from that address, and store it into `a`.  If a program knew that `byteArray2` was in a contiguous memory region with `byteArray`, at an address `i` bytes higher, then an access to `byteArray[i]` would not be an "error", but rather an access to `byteArray2[0]`.  If that's what the programmer wanted, the action would be correct.
supercat
CC BY-SA 4.0
2023-11-08T18:58:20.130Z
@supercat Well, Lisp has been used to write the OS for Lisp Machines, which has lots of implementation dependencies (including primitives to access memory directly). And much of the runtime library of conventional Lisp implementations is written in Lisp, and they may have lots of implementation dependencies.
Barmar
CC BY-SA 4.0
2023-11-08T19:09:22.823Z
@Barmar: In a language which has efficient primitives for raw memory access, it makes sense to require that programmers wanting to access memory in "weird" ways make use of such primitives.  C was designed to *avoid the need* for such primitives, by making pointer computations and dereferencing operations agnostic as to whether they were being used to access things "normally" or in weird ways.
supercat
CC BY-SA 4.0
2023-11-08T20:07:30.210Z
@supercat Good point. The purpose of "consequences are undefined" in CL is mostly to allow for optimizations and/or unforeseen extensions even in supposedly "safe" code -- these are places where they didn't want to impose run-time checking.
Barmar
# When was our current definition of "undefined behavior" first used?
Posted by: username_0 on 2023-11-04
In my answer to another question, [What was the purpose of Tartan's "disable" statement?](https://langdev.stackexchange.com/questions/3079/what-was-the-purpose-of-tartans-disable-statement), I asserted that it's anachronistic to use our modern C-derived definition of "undefined behavior", when reading a document about another language from 1978. (EDIT: The wording used in that document was actually "the consequences are not defined", not the exact phrase "undefined behavior". I think my overall question here is still interesting.)

The C and C++ standards define "undefined behavior" as "behavior for which this document imposes no requirements". The first instance of this definition I can find is in the first official C standard, which was apparently published as a draft in 1985, and officially in 1989. The unofficial standard for C before that was "The C Programming Language" by Kernighan and Ritchie, published in 1978. I don't completely trust my PDF copy of it, but I didn't find a definition (or even a mention) of "undefined behavior" in a quick search of that book, and it does not appear in the index.

So: Is the 1989 publication of the C89 standard the first origin of the notion that "undefined behavior" means "anything can happen"? (The phrase "nasal demons" was to my knowledge not coined until 1992: <https://groups.google.com/g/comp.std.c/c/ycpVKxTZkgw/m/S2hHdTbv4d8J?hl=en> ) What was the common understanding of "undefined behavior" in 1978, if there was one?
**Tags:** c undefined-behavior
> **Comments:**
> **username_1**: The phrase "undefined behaviour" wasn't actually used in any of the documents in the other question, or even "undefined".  
> **username_0**: Hm, you're right, it said "the consequences are not defined". So my question here is not quite the thing relevant to know. I still think it's worth asking as I wrote it, so I'll leave it up (slightly edited) and see what I learn.  
> **username_2**: Be sure to check for synonyms when researching. Appendix A Section 16 in my first edition of TCPL uses the phrase "unspecified" or "not specified" to refer to the order of evaluation of function arguments and side effects.  
> **username_0**: Interesting find. "Unspecified" is very definitely not a synonym here, though. In the modern C standard, "unspecified" means something completely different from "undefined". In TCPL there I assume it is just taking the ordinary English meaning...  
> **username_3**: **Comments have been [moved to chat](https://chat.stackexchange.com/rooms/149790/discussion-on-question-by-glenn-willen-when-was-our-current-definition-of-undef); please do not continue the discussion here.** Before posting a comment below this one, please review the [purposes of comments](/help/privileges/comment). Comments that do not request clarification or suggest improvements usually belong as an [answer](/help/how-to-answer), on [meta], or in [chat]. Comments continuing discussion may be removed.  
---
## 3 answers
### Answer by username_9 on 2023-11-07. Score: 3
The ANSI Common Lisp specification contains the following definition in its [Error Terminology](http://www.lispworks.com/documentation/HyperSpec/Body/01_db.htm) section:

> **The consequences are undefined**  
> This means that the consequences are unpredictable. The consequences may range from harmless to fatal. No conforming code may depend on the results or effects. Conforming code must treat the consequences as unpredictable. In places where the words "must," "must not,"
> or "may not" are used, then "the consequences are undefined" if the stated requirement is not met and no specific consequence is explicitly stated. An implementation is permitted to signal an error in this case.  
> For example: "Once a name has been declared by `defconstant` to be constant, any further assignment or binding of that variable has undefined consequences."

It also has a related "consequences are unspecified" designation. This stipulates that the consequences are unpredictable but harmless. "harmless" isn't defined rigorously, but we can presume this precludes it from launching a nuclear missile. It might prohibit nasal demons, but allow nasal angels.

This was published in 1994, but the standardization committee was formed in 1986. I think this terminology was determined early in the process.
> **Comments:**
> **username_10** (2023-11-07): The phrasing "the consequences are undefined" and "no conforming code may depend on the results or effects" seems to suggest that it's only what happens *after* the offending operation that is undefined. That is, the behaviour of the program up to the point where the offending operation is executed, should still exactly match the specified behaviour of the same program without that operation. If my interpretation is correct then that's a subtle difference between this notion and the modern notion of UB, which allows [time travel](https://devblogs.microsoft.com/oldnewthing/20140627-00/).  
> **username_0** (2023-11-08): I tend to think of Lisp as an interpreted language, in which case undefined behavior would be a runtime event, rather than a static fact about a program. As such the effects could of course only move forward in time. Of course, many Lisps could also be compiled. But I could imagine the mental model of undefined behavior as a dynamic event in an ongoing computation, rather than a static property of a program, could still apply.  
> **username_9** (2023-11-08): Lisp should be thought of as a compiled language that includes an embedded interpreter. But in several implementations, the interpreter actually uses the compiler internally. @GlennWillen  
> **username_5** (2023-11-08): A unique trait of C is that when the Standard was written, there were many constructs which would have defined behavior on some C implementations (typically processing constructs in a manner characteristic of the environment, with whatever consequences would result from doing so) but not behave consistently on all, and *such constructs were the primary means of accomplishing many tasks that couldn't be done efficiently, if at all, using portable constructs*.  When the C Standard characterized an action as UB, it meant implementations weren't *required* to define the behavior...  
> **username_5** (2023-11-08): ...and not that they shouldn't process the action in a documented manner characteristic of the environment in situations where doing so *might* be useful and there was no *particular* advantage to doing otherwise.  Nearly all controversies surrounding UB involve situations where processing in a document characteristic of the environment *would* be useful, and a compiler writer deviates from that behavior in a manner the compiler writer claims is more useful, but which the programmer recognizes as useless *for the task at hand*.  
> **username_9** (2023-11-08): @supercat If you follow the link in my answer you'll see that CL has a much finer grained taxonomy of erroneous situations. Lisp has traditionally had lots of runtime error checking (e.g. array bounds checks), CL has configuration capabilities to enable/disable this, and the spec says how the error checks are affected by this. There are also specific points that are noted as places for implementation extension. C tends to lump almost everything into UB, although it does have so implementation-dependent situations.  
> **username_5** (2023-11-08): @Barmar: I am unaware of Lisp ever having been widely used for the kinds of tasks that would be impossible in platform-agnostic code.  In Ritchie's Language, the statement `a=byteArray[i];` would take the address of `byteArray`, add `i`, load a byte from that address, and store it into `a`.  If a program knew that `byteArray2` was in a contiguous memory region with `byteArray`, at an address `i` bytes higher, then an access to `byteArray[i]` would not be an "error", but rather an access to `byteArray2[0]`.  If that's what the programmer wanted, the action would be correct.  
> **username_9** (2023-11-08): @supercat Well, Lisp has been used to write the OS for Lisp Machines, which has lots of implementation dependencies (including primitives to access memory directly). And much of the runtime library of conventional Lisp implementations is written in Lisp, and they may have lots of implementation dependencies.  
> **username_5** (2023-11-08): @Barmar: In a language which has efficient primitives for raw memory access, it makes sense to require that programmers wanting to access memory in "weird" ways make use of such primitives.  C was designed to *avoid the need* for such primitives, by making pointer computations and dereferencing operations agnostic as to whether they were being used to access things "normally" or in weird ways.  
> **username_9** (2023-11-08): @supercat Good point. The purpose of "consequences are undefined" in CL is mostly to allow for optimizations and/or unforeseen extensions even in supposedly "safe" code -- these are places where they didn't want to impose run-time checking.  
---
### Answer by username_4 on 2023-11-05. Score: 2
To add to supercat's answer, in which they state that C89 did not define "undefined behaviour" in this way, I [have seen the argument made](https://www.yodaiken.com/2021/05/19/undefined-behavior-in-c-is-a-reading-error/) that this is a *possible* yet originally *unintended* reading of the standard, which was only adopted in C99.

To summarize the argument from the linked blog post, as I understand it, C89 defined the term as:

> Undefined behavior --- behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements. Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

Now, depending on how you interpret the commas, this can either be paraphrased as "Undefined behaviour is the behaviour which occurs upon using certain constructs whose behaviour the standard does not otherwise define. This behaviour SHALL be any one of [a list of permissible behaviours]", or "Undefined behaviour is the behaviour, which this standard does not define, which occurs upon using certain constructs. This behaviour SHALL be any one of [a list of permissible behaviours]" (where I use "SHALL" in the [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) sense)

Since the latter interpretation is contradictory (by claiming the standard places no requirements on what the behaviour may be immediately before defining such requirements), the argument goes that the former interpretation is evidently the intended one. Nevertheless, for C99 this ambiguity was resolved in favor of the latter interpretation, changing the definition to:

> 1 undefined behavior  
> behavior, upon use of a nonportable or erroneous program construct or of erroneous data,  
> for which this International Standard imposes no requirements  
> 2 NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable  
> results, to behaving during translation or program execution in a documented manner characteristic of the  
> environment (with or without the issuance of a diagnostic message), to terminating a translation or  
> execution (with the issuance of a diagnostic message).

The comma placement leaves less room for ambiguity, and the addition of "NOTE" and the use of "Possible" instead of "Permissible" changes what follows from normative to advisory. Giving us an approximate definition of "Undefined behaviour is the behaviour, which this standard does not define, upon using certain constructs. This behaviour MAY be any one of [a list of permissible behaviours]" (again, see [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) for my use of "MAY").

Granted, you are welcome to disagree with with that argument, you're evidently in plentiful company if you do.

## So if not C89, then when?

Well, I also can't provide a specific date. But *if* the C89 authors (or even just a portion of them) intended to use the term differently, that tells us that they didn't expect the modern definition of the term to be what readers would assume in case of perceived ambiguity (and presumably they also wouldn't expect there to *be* any ambiguity in the first place). While this is certainly not proof that the term had *never* been used that way before, it does suggest that this definition at least did not become *dominant* until some time *after* the 1989 publication of the C89 standard.
> **Comments:**
> **username_5** (2023-11-05): I don't think the list of possible outcomes was intended to be exhaustive even in C89.  Far more significant is that the Standard was never intended to be the only thing which constrained how implementations would process various corner cases.  Market forces were expected to act as a check to gratuitously nonsensical behaviors.  
> **username_5** (2023-11-05): Fundamentally, the authors of C89 used UB as a catch-all for situations where *they didn't want to concern themselves with details of what exactly could or couldn't happen*. Trying to parse the definition of UB to decide what exactly is allowed to happen *misses the point*, which is that the authors of the Stanard wanted to avoid having to consider such issues in such cases.  
> **username_0** (2023-11-05): Thanks, this is exactly the sort of thing I'm interested in learning. I am hoping perhaps more people have older primary sources to chime in with, but existing arguments made later are also very welcome data.  
> **username_4** (2023-11-05): @supercat I'm told that ANSI and/or ISO standards do distinguish the use of "permissible" for requirements and the use of "possible" for advisory content, but I freely admit I don't read enough standards to be certain whether or not I've been misled. It's also possible that I could have misinterpreted the intended strictness, confusing (what RFC 2119 would call) SHOULD and MUST. Still, I'll leave the post as it is, based on how the standard's wording was explained to me, until someone convinces me that explanation was wrong  
> **username_5** (2023-11-05): @SaraJ: A good standard for things that are supposed to work together (e.g. Acme plugs and Acme sockets) should be designed broadly enough that anything that can be accomplished by a device that fits an Acme socket should be possible with a  conforming Acme plug, anything that can be done by a device that fits an Acme plug should be possible with a conforming Acme socket.  It should, however, be tight enough to say something useful about arbitrary combinations of Acme plugs and Acme sockets.  Doing this with C programs and implementations would have been impossible...  
> **username_5** (2023-11-05): ...without either recognizing more than two categories of C implementations, or else using one definition of "conforming C program" which is broad enough to encompass all programs that can operate with at least one conforming C implementation, and a definition of "strictly conforming C program" which was expected to exclude many of the tasks for which C was most uniquely useful.  Really, the C Standard has always been a description of some features that should be common to all C implementations, along with some standardese so it could pretend to be a "standard".  
> **username_5** (2023-11-05): @SaraJ: There are a few language design issues, such as the handling of variadic functions, where having all implementations follow a slightly imperfect standard woul be better than having each implementation do things in whatever way would work best on its target platform.  The existence of the Standard improved those parts of the language.  For many parts of the language, though, any good standard would need to recognize that the best way of treating a construct for one task might make an implementation unsuitable for some other tasks.  
---
### Answer by username_5 on 2023-11-04. Score: 0
No, C89 is not the first place that notion was articulated, because C89 doesn't articlate that notion (and nor does any subsequent version of the Standard).
As far as the Standard is concerned, the term "Undefined Behavior" means nothing more nor less than that the behavior of the construct or corner case falls outside its jurisdiction.  This implies that such case must not arise in any program whose behavior is to be fully specified by the Standard, i.e. strictly conforming C programs.  Outside of the narrow realm of strictly conforming programs, however, the range of ways in which most compilers could behave in cases where the Standard imposes no requirements was expected to be constrained by a desire of compiler writers to sell compilers, and a consequent need to avoid behaving in a way customers and potential customers would view as unacceptable.  While the Standard doesn't exclude the possibility that a compiler whose maintainers are hostile or indifferent to the needs of programmers targeting it might behave in completely unconstrained fashion in situations where the Stanard imposes no requirements, the "UB means anything can happen" notion would only be applicable to people who are stuck targeting such compilers.
A religion has formed among compiler writers who are sheltered from normal market forces, around the idea that the Standard only uses the phrase "Undefined Behavior" in situations which would never arise in any correct programs, interpreting the phrase "non-portable or erroneous" as "non-portable, and therefore erroneous", but that ignores not only the published Rationale documents for C89 and C99, but also every version of the C Standards Committee's charter to date, every single one of which has explicitly recognized the legitimacy of non-portable C code, and stated that the Standard it not meant to preclude the use of C as a form of "high-level assembly langauge".
I think the real shift came when proponents of clang and gcc were able to describe as "mainstream" behaviors which are unique to those two compilers and derivatives thereof, which for many tasks are effectively exempt from market competition by commercial compilers.  If a programmer buys a $500 compiler that totally wipes the floor with clang and gcc, but wants to distribute code in contexts which would require that the recipients rebuild it, such distribution would require that the code be reworked to run on lower-quality freely distributable compilers, effectively negating any advantage the programmer should have been able to reap from buying a better compiler.
Incidentally, the C89 Standard does make a key distinction between "UB" and other behaviors that aren't fully specified by the Standard in the "as-if" rule and a corollary to it whose effects were intended to be minimal but which have become disastrous.  Under the as-if rule, compilers may only perform optimizing transforms whose effects would not be observable under any defined program executions.  A consequence of this is that if such a transform would observably affect the behavior of some sequence of actions, at least one of the actions involved must be characterized as invoking Undefined Behavior.  This wouldn't be a problem if compilers interpreted the latitude they were given to be as narrow as required to perform specific useful tranforms.  Unfortunately, aggressive compilers go beyond that, performing transforms that are seldom useful and are often best counter-productive.
An unfortunate aspect of this situation is that in many cases it may not be impossible to predict precisely how the optimal machine code that satisfies an application's requirements would behave in certain corner cases.  Thus, allowing implementations to perform optimizing transforms in ways that might replace one acceptable corner-case behavior with another equally acceptable behavior would allow them to satisfy application requirements more efficiently than would otherwise be practical.  If integer overflow were characterized as Implementation-Defined Behavior rather than UB, a compiler for a platform where integer overflows are trapped, given something like:
int test(int a, int b)
{
  int temp=a*b;
  if (f1())
    f2(temp,a,b);
}

would be required to perform the multiplication (or at least determine whether it would overflow) unconditionally, before calling f1(), even though code would likely not care about whether the computation of a*b would overflow in cases where f1() returned zero.  Most applications whose requirements would be satisfied by the above code if integer overflow were characterized as Implementation-Defined Behavior would be satisfied equally well if not better if the code were transformed into:
int test(int a, int b)
{
  if (f1())
    f2(a*b,a,b);
}

even on platforms where the transformation would affect program behavior, but the Standard's abstraction model is incapable of allowing the above transformation without waiving jurisdiction over program behavior in case of integer overflow.  While some compiler writers claim this allows more efficient code generation, it actually does the opposite by forcing programmers to write code in a manner that would block any optimizations transforms that could yield behaviors which would satisfy application requirements despite being inconsistent with the behavior of non-transformed code.
> **Comments:**
> **username_6** (2023-11-05): "...such distribution would require that.." require in which sense? The only one that I could infer assumes it is unreasonable to expect from the recipients of the distribution to purchase the compiler, but if this is the case it's not clear where this assumption comes from.  
> **username_0** (2023-11-05): Presumably supercat is referring to open source. Nobody is going to buy a \$500 compiler to build an open source project. Most people not building on behalf of an employer can't afford a \$500 compiler.  
> **username_5** (2023-11-05): @GlennWillen: Bingo.  Though back in the 1990s, a fair number of people did buy tools around that price point--in 1990s dollars--for personal use (\$500 woul have been about the upper limit; many more people would buy tools around the \$100 price point).  Linus Torvalds desparately needed a freely distributable C compiler to make Linux viable, and tolerated gcc not because of its aggressive attitude toward optimization, but in spite of it.  It's too bad that people didn't learn early on that the proper response to "would the Standard allow a compiler to do something really annoying"...  
> **username_5** (2023-11-05): ...shouldn't have been to figure out some way of interpreting the Standard to forbid it, nor to figure out some way for programmers to work aroun the limitation imposed thereby, but instead to have said "The Standard would probably allow a garbage compiler to behave in that fashion.  Why--do you want to write one?"  Many things are left undefined by the Standard not because there wasn't any commonplace behavior, but rather because there *was* a behavior which it was obvious immplementations should follow *absent a good reason for doing otherwise*.  
> **username_5** (2023-11-05): What's sad is that free compiler back ends like LLVM all seem to use abstraction models that are incapable of expressing many applications' actual requirements without needlessly blocking what should be useful optimizations.  I suppose maybe someone could come up with a commercial compiler that uses its own open-source-licensed back-end which could be distributed, along with intermediate-language output from the compiler, to anyone wanting to run programs processed with the compiler, but I doubt such a thing could be developed profitably in a way that would yield a widely affordable product.  
> **username_7** (2023-11-05): -1: While tangentially relevant, this does not answer the question.  
> **username_5** (2023-11-05): @AlexisKing: The question asks "Is the 1989 publication of the C89 standard the first origin of the notion that "undefined behavior" means "anything can happen"?"  Showing that the Standard doesn't say that is responsive to the question (though maybe I should more explicitly say the answer is "No, since the Standard doesn't express that notion it cannot be the first expression thereof").  Anyone seeking to sell compilers would be need to make the compiler behave in a manner tolerable to customers even/*especially* in cases where the Standard imposes no requirements.  
> **username_8** (2023-11-20): @AlexisKing Unfortunately, this is not an isolated incident. Every time someone asks about something UB-related on the network, supercat uses this as an excuse to write an off-topic jeremiad about what UB was “supposed” to mean and how his unicorn interpretation of the relevant specifications that avoids running into the principle of explosion is totally achievable, guys. You may have to adjust your remedies accordingly to something more substantial than just downvoting.  
> **username_5** (2023-11-20): @user3840170: What you call "unicorn" explanation is based upon what *the authors of the Standard wrote* in the published Rationale.  I have yet to find any *primary source* indicating that the authors of the Standard intended anything beyond "The Standard imposes no requirements", or that they intended to invite compiler writers to deviate from common practices *except in a bona fide effort to help programmers satisfy objectives*.  Achieving good performance is a fine objective, but only if it doesn't interfere with the primary objective of making a program satisfy requirements.  
> **username_5** (2023-11-20): @user3840170: If an optimizer is designed around the idea that if transforms which are safe individually will be safe in combination, then the Principle of Explosion will make program behavior unpredictable in any situations where a transform might affect program behavior in otherwise benign fashion.  One approach to preventing that is to require that programmers identify a combination of transforms to allow, and write code in a manner that blocks all others.  I would suggest that it would be more useful to specify allowable transforms in a manner that makes them consistent.  
---
