5

As I wrote an answer to How is it possible to use pow without including cmath library I fear to have proven that missing an include of a needed header is actually undefined behavior, but since I have not found any consent of that fact I like to impose the formal question:

Is missing a required header i.e.

#include <iostream>

int main()
{
    std::cout << std::pow(10, 2);
}
  1. Ill-formed ( [defns.ill.formed] ) code?
  2. Invoking undefined behavior ( [defns.undefined] )?
  3. If it is not 1 and 2, is it unspecified behavior [defns.unspecified] or implementation-defined behavior [defns.impl.defined]?
  4. If not 1. i.e. if this code is well-formed, wouldn't that contradict [using.headers] and [intro.compliance] "accept and correctly execute a well-formed program"?

As in my answer I tend to affirm both questions, but [using.headers] is very confusing because of Difference between Undefined Behavior and Ill-formed, no diagnostic message required . As [defns.well.formed] implies that a program constructed to the ODR is well formed, and there is specification of whenever the for example iostream must not define pow, one could argue this is still unspecified behavior ( [defns.unspecified]). I don't want to rely only of my standard interpretation skills for a definitive answer for such an important question. Note that the accepted i.e. the only other answer does not answer if the code is UB nor does the question asks it.

Superlokkus
  • 4,731
  • 1
  • 25
  • 57
  • 1
    May want to change that to `std::cout << std::pow(10.0, 2.0);` – Eljay Apr 20 '20 at 16:36
  • This should be a compile time error. Also, you are missing `using namespace std;` or `using std::pow`. – Luke Apr 20 '20 at 16:37
  • 2
    @LuketheGeek A compile time error does not exclude UB. And MSVC seems to be still standard conform by not throwing an error but just compiling it. – Superlokkus Apr 20 '20 at 16:39
  • 1
    You are probably in the area of _"...implementation-defined behavior..."_. Header files __may__ include other header files but are not required to do so. So you program may compile on one system but not on another. See also: https://en.cppreference.com/w/cpp/language/ub – Richard Critten Apr 20 '20 at 16:39
  • @RichardCritten I was considering that, but implementation defined behaviour requires a well formed program. – Superlokkus Apr 20 '20 at 16:40
  • 1
    It is a well formed program, but only on your system. It looks like is including (directly or indirectly) – Richard Critten Apr 20 '20 at 16:41
  • @RichardCritten Isn"t that the definition of UB? – Superlokkus Apr 20 '20 at 16:44
  • @Superlokkus "Isn"t that the definition of UB" No. You are depending on transitive includes. That's *implementation defined* behaviour, not *undefined* behaviour. As long as the implementation ends up including everything you need, all is well. You just cannot rely on that being the case without explicitly including what you need yourself. – Jesper Juhl Apr 20 '20 at 16:48
  • If all the header files are included then there is no UB. If on another system the header files do not include each other then you have (on that system) an __ill-formed__ program. There is no UB. – Richard Critten Apr 20 '20 at 16:49
  • @JesperJuhl I considered that too: If that would be true, MSVC would be in violation of the standard, since [defns.impl.defined] requires documentation, and MSVCs standard library does not document that it includes cmath not even transtively – Superlokkus Apr 20 '20 at 16:49
  • 1
    It does not have to, it only needs to tell you what the correct header to include is`` . That you don't do it is not a failure by MS to conform to the standard, – Richard Critten Apr 20 '20 at 16:52
  • @RichardCritten On a system were the header does not include it, it would violate the ODR, which ends that a violation to it implies UB. – Superlokkus Apr 20 '20 at 16:52
  • @Superlokkus The header files are public (and, since recently, so is their entire standard library), one could argue that that counts as documentation - you can read and see what's being included. – Jesper Juhl Apr 20 '20 at 16:53
  • 1
    The program would just fail to compile (missing definition) so no ODR violation. – Richard Critten Apr 20 '20 at 16:53
  • @RichardCritten So according to which paragraph in the standard is a diagnostic required? Name lookup? – Superlokkus Apr 20 '20 at 17:03
  • Wouldn't that mean the code is ill-formed but since of [using.headers] no diagnostic required, MSVC is still standard compliant? – Superlokkus Apr 20 '20 at 17:05
  • Live example - https://godbolt.org/z/6cB3Pd – Richard Critten Apr 20 '20 at 17:07
  • Undefined behavior means that the same execution CAN have multiple behaviors. Here you are at COMPILE level, your program will compile and link ALWAYS in the same maner. you will have always the same binaries. with missing header you can : have compile error, use another namespace, disable compile option, ... but the COMPILE behaviour is defined – Landstalker Apr 20 '20 at 17:07
  • @RichardCritten I know that it compiles and runs under MSVC that is not the question. – Superlokkus Apr 20 '20 at 17:09
  • @Landstalker I think you described [defns.unspecified] which excludes UB in my understatement. – Superlokkus Apr 20 '20 at 17:12
  • @Superlokkus see the other 2 compilers diagnosing the problem. – Richard Critten Apr 20 '20 at 17:17
  • @RichardCritten In another way: If this code is not UB, and is not a violation to the ODR, that it is not ill-formed, which would mean per definition, well-formed and the implementations would have to accept it. i.e. is 4.1.2.1 not == UB? – Superlokkus Apr 20 '20 at 18:48

1 Answers1

2

It is unspecified whether this program is well-formed or ill-formed (with a required diagnostic, because name lookup doesn’t find pow). The possibilities arise from the statement that one C++ header may include another, which grants permission to the implementation to give this program either of just two possible interpretations.

Several similar rules (e.g., that a template must have at least one valid potential specialization) are described as rendering the program ill-formed, no diagnostic required, but in this situation that freedom is not extended to the implementation (which is arguably preferable). That said, an implementation is allowed to process an ill-formed program in an arbitrary fashion so long as it issues at least one diagnostic message, so it’s not completely unreasonable to group this situation with true undefined behavior even though the symptoms differ usefully in practice.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
  • 1
    Do you want to say that possible «transitive» inclusion by another header satisfies _«A translation unit ... shall include the header lexically before the first reference in that translation unit to any of the entities declared in that header»_? – Language Lawyer Apr 21 '20 at 04:19
  • So 1. Unspecified 2. Yes ? But doesn't 2 imply 1? – Superlokkus Apr 21 '20 at 09:00
  • I've got the bad feeling that your comitee colleague @PeteBecker seem to have said that it is not UB, but maybe he was just giving me some rope: https://stackoverflow.com/questions/61324712/how-is-it-possible-to-use-pow-without-including-cmath-library#comment108493637_61324814 – Superlokkus Apr 21 '20 at 10:34
  • @LanguageLawyer: Sure: it counts if you `#include` a library header via your own header *file*, after all. – Davis Herring Apr 21 '20 at 14:31
  • 1
    @Superlokkus: It’s not UB except that *any* ill-formed program may be accepted, with arbitrary behavior, so long as at least one diagnostic is issued. This is not the sort of program, of course, for which real implementations do anything interesting with that permission. – Davis Herring Apr 21 '20 at 14:34
  • @DavisHerring I think I begin to understand now: If I am not mistaken, your point is, that [intro.compliance] 4.1.2.2. does not requires to not accept and execute such an ill formed program. Assuming that people are indeed counting on implementions to give diagnostics of most ill formed code, i.e. ill formend code without "without diag. req.." one could say 4.1.2.2. is not UB, while 4.1.2.3. is. Given that, if there would only be [using.headers] we would have 4.1.2.3 and so UB. But thanks to [basic.lookup] we either have well formed code, or ill formed code but in context of 4.1.2.2., right? – Superlokkus Apr 21 '20 at 15:11
  • 1
    @Superlokkus: To be precise, UB is a dynamic event in the execution of a program, not the lack of requirements on the implementation for executions that contain it. /2.3 (not “4.1.2.3”, which would be a subclause) **overrides** /2.2 when both apply, but my reading is that [using.headers]/3 doesn’t mean to render programs IFNDR when name lookup fails to connect a name to an entity in the first place. (To be fair, it’s not clear what other normative meaning that rule could have; I think it’s trying to say how to write a correct program more than whether a program is correct.) – Davis Herring Apr 22 '20 at 00:27
  • Thanks for the clarification, I suggest to add a TL;DR part of "1. unspecified 2. not in the common sense, accepts or rejects but at least gives a diagnostic in the rejection case" to your answer – Superlokkus Apr 22 '20 at 09:18