Is it mandatory that compilers keep the behavior consistent between versions of what the C or C++ standard describes as implementation-defined behavior?
For example the result of some bitwise operations on signed integers must be the same in gcc 4.6 and say in gcc 4.8?
It does not have to be consistent, it just needs to be documented, the draft C99 standard section 3 Terms, definitions, and symbols defines it like so:
implementation-defined behavior
unspecified behavior where each implementation documents how the choice is made
For example the gcc docs has a section C Implementation-defined behavior.
The C++ draft standard also has a similar definition and in section 1.9 Program execution paragraph 2 says:
Certain aspects and operations of the abstract machine are described in this International Standard as implementation-defined (for example, sizeof(int)). These constitute the parameters of the abstract machine. Each implementation shall include documentation describing its characteristics and behavior in these respects.6[...]
No.
The C standard mandates some things. For the ones which are implementation-defined, it does not mandate that you are consistent between versions.
This would not be very useful anyway - what stops you to create "Extra Gcc" instead of "gcc 5"? Who determines what version is actually an upgrade from a previous one?
Implementation defined means that the compiler writer chooses what happens, and that is just it. There's nothing stopping you from changing your mind and do it another way of you want. Nothing forces you to be consistent among different versions of the same compiler.
From the standpoint of the C standard, two versions of the "same" compiler are two different implementations. (Well, it's likely that the earlier is known not to be a C implementation at all due to known conformance errors, and the latter is also likely not to be a C implementation due to unknown conformance errors...) In general, implementors will provide their own guarantees that implementation-defined behavior will remain the same between versions, however. This is because they have already defined the behavior, and if they change it, they're creating an explicit incompatibility with something they already documented as behavior you could rely upon with their product.
Related
In the first paragraph cppreference.com clearly states that throw(T1, ..., Tn) is removed in C++17.
It confuses me, that some compilers support throw(T1, ..., Tn) in C++17 mode (see demo).
MSVC supports it by default, but you can turn on a warning for it, see C5040. It can be turned into an error with /we5040.
Clang reports it as an error by default, but the error can be turned off with -Wno-dynamic-exception-spec.
GCC leaves you with no choice: it's an error.
Are compilers allowed to support a feature, that is removed in the standard? For what purpose?
Or is this just a compiler extension, like void foo(int size) { char a[size]; } in GCC, see demo.
Are compilers allowed to support a feature, that is removed in the standard?
The standard doesn't allow that. AFAIK in general it doesn't give any special treatment to features that used to be in the language (doesn't separate them from non-existent features).
If a compiler doesn't diagnose this error (i.e. doesn't give an error or a warning) with a specific configuration (i.e. specific flags), then it doesn't conform to the standard in that configuration.
For what purpose?
Backward compatibility (what else could it be). More specifically, it lets you use both old and new features in a same translation unit.
This can be useful if you're using a library that uses a removed feature in its headers, but want to use the new language features in your own code.
Or if you want to use the removed feature in your own code along with the new features, for some reason.
Note that absolute conformance to the standard is not practically possible to achieve.
Some compiler vendors care about conformance more than the others. Microsoft tends to care less about it (or at least used to, they've been working on that).
There is no single answer for this.
Some things outside the Standard can be treated as pure enhancements. Some of these enhancements are suggested by the Standard ("It's implementation-dependent if X"), some are not even mentioned at all (#include <windows.h>).
For other things, the Standard does require that a compiler flags the violation of the Standard. But the Standard doesn't talk about errors or warnings. Instead it says "Diagnostic Required", which is understood to mean either an error or a warning. And in other cases it even says "No Diagnostic Required" (NDR), which means the compiler is not obliged to flag non-standard code.
So depending on the removed feature, it may or may not require a diagnostic. And if it does require a diagnostic, you can often tell the compiler that you're not interested in that particular diagnostic anyway.
Are compilers allowed to support a feature, that is removed in the standard? For what purpose?
Compilers can do whatever they want. The C++ standard dictates rules for the C++ language, and while they do consult compiler vendors to ensure its rules are implementable, the vendors themselves will do what they feel is best for them and their users. Compilers have many non-standard features that developers use all the time. And I don't think any of these compilers fully adhere to the standard by default.
That said, I wouldn't call a compiler+settings "C++17 compliant" if they allowed non-C++17 code or rejected valid C++17 code (as dictated by the standard). Most compilers have settings that can be set if full compliance is desired.
If you want to be pedantic though MSVC isn't even C++11 compliant due to lacking preprocessor. The standard isn't everything.
The C++ standards defines well-formed programs as
C ++ program constructed according to the syntax rules, diagnosable
semantic rules, and the one-definition rule
I am wondering if all well-formed program compile or not (if it is not the case, what types of error make the difference between a well-formed program and a compilable problem). For example would a program containing ambiguity errors considered as well-formed?
A well-formed program can have undefined behaviour.
It's in a note, and thus not technically authoritative, but it seems that it is intention that termination of compilation (or "translation" as the standard calls it) is within the scope of possible UB:
[intro.defs]
undefined behavior
behavior for which this document imposes no requirements
[ Note: Undefined behavior may be expected when this document omits any explicit definition of behavior or when a program uses an erroneous construct or erroneous data.
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).
Many erroneous program constructs do not engender undefined behavior; they are required to be diagnosed.
Evaluation of a constant expression never exhibits behavior explicitly specified as undefined in [intro] through [cpp] of this document ([expr.const]).
— end note
]
There are also practical implementation limits:
[implemits]
Because computers are finite, C++ implementations are inevitably limited in the size of the programs they can successfully process.
Every implementation shall document those limitations where known. This documentation may cite fixed limits where they exist, say how to compute variable limits as a function of available resources, or say that fixed limits do not exist or are unknown.
Furthermore, compilers can have, and do have bugs. Well-formed simply means the a standard conforming compiler should compile it (within the limitations mentioned above). A buggy compiler does not necessarily conform to the standard.
Lastly, the standard document itself is not perfect. If there is disagreement about what the rules mean, then it is possible for a program to be well-formed under one interpretation, and ill-formed under another interpretation.
If a compiler disagrees with the programmer or another compiler, then it might fail to compile a program that is believed to be well-formed by the other party.
I am wondering if all well-formed programs compile or not
Of course not, in practice.
A typical example is when you ask for optimizations on a huge translation unit containing long C++ functions.
(but in theory, yes)
See of course the n3337 C++11 standard, or the C++17 standard.
This happened to me in the (old) GCC MELT project. I was generating C++ code compiled by GCC, basically using transpiler (or source to source compilation) techniques on Lispy DSL of my invention to generate the C++ code of GCC plugins. See also this and that.
In practice, if you generate a single C++ function of a hundred thousand statements, the compiler has trouble in optimizing it.
Large generated C++ functions are possible in GUI code generators (e.g. FLUID), or with some parser generators such as ANTLR (when the underlying input grammar is badly designed), interface generators such as SWIG, or by using preprocessors such as GPP or GNU m4 (like GNU autoconf does). C++ template expansion may also produce arbitrarily large functions (e.g. when you combine several C++ container templates and ask the GCC compiler to optimize at link-time with g++ -flto -O2)
I did benchmark, and experimentally observed in the previous decade that compiling a C++ function of n statements may take O(n2) time (and IIRC O(n log n) space) with g++ -O3. Notice that a good optimizing C++ compiler has to do register allocation, loop unrolling, inline expansion, that some ABIs (including on Linux/x86-64) mandate passing or returning small struct-s (or instances of small class-s) thru registers. All these optimizations require trade-offs and are hitting some combinatorial explosion wall: in practice, compiler optimization is at least an intractable problem, and probably an undecidable one. See also the related Rice's theorem and read the Dragon Book.
You could adapt my manydl.c program (generating more or less random C code compiled as several plugins then dlopen-ing them on Linux) to emit C++. You'll then be able to do some GCC compiler benchmarks, since that manydl program is able to generate hundred thousands plugins containing lots of more or less random C functions. See Drepper's paper how to write shared libraries and be aware of libgccjit.
See also the blog of the late Jacques Pitrat (1934-oct.2019) for an example of a C program generating the half millions lines of its own C code, whose design is explained in this paper and that book.
Read Thriving in a crowded and changing world: C++ 2006--2020
In the C++17 filesystem library, we got std::filesystem::remove(path), which — as I understand it — is a direct port of boost::filesystem::remove(path) from Boost.Filesystem.
But C++ inherited from C89 a very similar function called std::remove(path), which is also documented as a way to remove a file from the filesystem. I'm vaguely aware of some pitfalls with this function, e.g. I believe I have heard that on Windows std::remove cannot be used to remove a file that is still being held open by the current process.
Does std::filesystem::remove fix these issues with std::remove? Should I prefer std::filesystem::remove over std::remove? Or is the former just a namespaced synonym for the latter, with the same warts and pitfalls?
The title of my question asks for the difference between boost::filesystem::remove(path) and std::remove(path) because I figure that std::filesystem::remove(path) may not have been implemented by a lot of library vendors yet, but my understanding is that it's supposed to be basically a direct copy of the Boost version. So if you know about Boost.Filesystem on Windows, you probably know enough to answer this question too.
Checking the standard library sources installed with my MSVC, std::experimental::filesystem::remove calls its internal _Unlink helper, which simply calls _wremove, which simply calls Windows DeleteFileW. Similarly, boost::filesystem::remove also just calls DeleteFileW on Windows.
std::filesystem::remove is specified by reference to POSIX remove, but the global wording in [fs.conform.9945] makes clear that implementations are not required to provide the exact POSIX behavior:
Implementations should provide such behavior as it is defined by
POSIX. Implementations shall document any behavior that differs from
the behavior defined by POSIX. Implementations that do not support
exact POSIX behavior should provide behavior as close to POSIX
behavior as is reasonable given the limitations of actual operating
systems and file systems. If an implementation cannot provide any reasonable behavior, the implementation shall report an error as specified in [fs.err.report]. [ Note: [...] ]
Implementations are not required to provide behavior that is not supported by a particular file system. [ Example: [...] ]
Any quirks in ::remove (that is about the actual act of removing rather than identification of the file to be removed) are likely due to limitations of the underlying OS API. I see no reason to think that an implementation of std::filesystem::remove on the same operating system will magically do better.
Is it possible to portablize all #pragma commands? #pragma once can be portablize as the following:
#ifndef FOO
#define FOO
//Code here
#endif
However, I am not aware of universal implementations for #pragma pack and others. Is this possible?
The specification of pragmas in the standard is essentially an implementation-defined hook - the results of every pragma is implementation-defined. The results can include causing translation to fail, the implementation or emitted code behaving in a non-conforming manner (i.e. do things with any code construct differently than the standard requires). Implementations are required to ignore pragmas they don't recognise.
By definition, therefore, there are no universal definitions. There is also nothing preventing two different compilers from doing completely different things with the same pragma.
To "portabilize" such things, it is necessary to have a consistent specification of the features and behaviours that compiler vendors can implement (and, then, that programmers can consistently use). Getting such a specification is challenging but not impossible. Firstly, a specification needs to be shared - formally (e.g. in a standard) or informally (e.g. a specification agreed between some vendors, even if it is not actually in a standard). Second, it requires requires all vendors to agree to implement it - which is not a given, even if it is in a ratified standard. Third, it requires any variations between implementations to be understood, and accepted by developers (or, at the least, a reasonable majority).
When agreement is required between multiple players (vendors, developers, committees, the list goes on) it takes more effort to get agreement. With C++, there is no body that has sufficient "pull" to guarantee that some feature will be successfully "portabilized" - or, to put it another way, there is nobody with a trump vote who can be convinced to make something universal. Getting 100% "portabilization" will be hard - practically, there will often be a "near enough" result rather than 100%.
No, pragma implementations are left up to the preprocessor or compiler author and are not specified in the C++ Standard.
See https://isocpp.org/std/the-standard.
It's easier to read than one might expect.
What you posted will not result in precisely the same behavior. #pragma once in VS does not equate to the inclusion guard #ifdef you wrote.
My question (Single line comment continuation) got me wondering about compiler compliance and warning messages, particularly with warnings-as-error feature in many compilers.
From the C++ spec, § 1.4.2.1 states:
If a program contains no violations of the rules in this International Standard, a conforming implementation shall, within its resource limits, accept and correctly execute that program.
If a warning message is issued for code which technically conforms to the standard (as in my linked question), even if I specifically asked for a warning to be issued (again, in my example if I used -Wcomments with gcc), and I use warning-as-errors (-Werror), the program will not compile. I realize that this is rather obtuse, given that I could workaround the issue in several ways. However, given the quote, is this a violation of the standard which is generally permitted, or is somehow otherwise explicitly allowed?
Yes, if you tell the implementation not to conform with the standard (for example, by requesting errors for well-formed code constructs), then its behaviour won't be conforming.