C++20: Validate Template Bodies Against Concepts - c++

C++20 introduces concepts, which allows us to specify in the declaration of a template that the template parameters must provide certain capabilities. If a template is instantiated with a type that does not satisfy the constraints, compilation will fail at instantiation instead of while compiling the template's body and noticing an invalid expression after substitution.
This is great, but it begs the question: is there a way to have the compiler look at the template body, before instantiation (i.e. looking at it as a template and not a particular instantiation of a template), and check that all the expressions involving template parameters are guaranteed by the constraints to exist?
Example:
template<typename T>
concept Fooer = requires(T t)
{
{ t.foo() };
};
template<Fooer F>
void callFoo(F&& fooer)
{
fooer.foo();
}
The concept prevents me from instantiating callFoo with a type that doesn't support the expression that's inside the template body. However, if I change the function to this:
template<Fooer F>
void callFoo(F&& fooer)
{
fooer.foo();
fooer.bar();
}
This will fail if I instantiate callFoo with a type that defines foo (and therefore satisfies the constraints) but not bar. In principal, the concept should enable the compiler to look at this template and reject it before instantiation because it includes the expression fooer.bar(), which is not guaranteed by the constraint to exist.
I assume there's probably backward compatibility issues with doing this, although if this validation is only done with parameters that are constrained (not just typename/class/etc. parameters), it should only affect new code.
This could be very useful because the resulting errors could be used to guide the design of constraints. Write the template implementation, compile (with no instantiations yet), then on each error, add whatever requirement is needed to the constraint. Or, in the opposite direction, when hitting an error, adjust the implementation to use only what the constraints provide.
Do any compilers support an option to enable this type of validation, or is there a plan to add this at any point? Is it part of the specification for concepts to do this validation, now or in the future?

Do any compilers support an option to enable this type of validation, or is there a plan to add this at any point? Is it part of the specification for concepts to do this validation, now or in the future?
No, no, and no.
The feature you're looking for is called definition checking. That is, the compiler checks the definition of the template at the point of its definition based on the provided concepts, and issues errors if anything doesn't validate. This is how, for instance, Rust Traits, Swift Protocols, and Haskell Typeclasses work.
But C++ concepts don't work like that, and it seems completely infeasible to ever add support for such a thing given that C++ concepts can be arbitrary expressions rather than function signatures (as they are in other languages).
The best you can do is thoroughly unit test your templates with aggressively exotic types that meet your requirements as minimally as possible (the term here is archetype) and hope for the best.

TL;DR: no.
The design for the original C++11 concepts included validation. But when that was abandoned, the new version was designed to be much more narrow in scope. The new design was originally built on constexpr boolean conditions. The eventual requires expression was added to make these boolean checks easier to write and to bring some sanity to relationships between concepts.
But the fundamentals of the design of C++20 concepts makes it basically impossible to do full validation. Even if a concept is built entirely out of atomic requires expressions, there isn't a way to really tell if an expression is being used exactly in the code the way it is in the requires expression.
For example, consider this concept:
template<typename T, typename U>
concept func_to_u = requires(T const t)
{
{t.func()} -> std::convertible_to<U>;
};
Now, let's imagine the following template:
template<typename T, typename U> requires func_to_u<T, U>
void foo(T const &t)
{
std::optional<U> u(std::in_place, t.func());
}
If you look at std::optional, you find that the in_place_t constructor doesn't take a U. So... is this a legitimate use of that concept? After all, the concept says that code guarded by this concept will call func() and will convert the result to a U. But this template does not do this.
It instead takes the return type, instantiates a template that is not guarded by func_to_u, and that template does whatever it wants. Now, it turns out that this template does perform a conversion operation to U.
So on the one hand, it's clear that our code does conform to the intent of func_to_u. But that is only because it happened to pass the result to some other function that conformed to the func_to_u concept. But that template had no idea it was subject to the limitations of convertible_to<U>.
So... how is the compiler supposed to detect whether this is OK? The trigger condition for failure would be somewhere in optional's constructor. But that constructor is not subject to the concept; it's our outer code that is subject to the concept. So the compiler would basically have to unwind every template your code uses and apply the concept to it. Only it wouldn't even be applying the whole concept; it would just be applying the convertible_to<U> part.
The complexity of doing that quickly spirals out of control.

Related

Bounded Type Parameters in C++, any reason for the lack of it?

Java and I guess C#(and others) support Bounded Type Parameters which lets us restrict what types may be used in template classes/functions.
I am curious if there is a reason, official or otherwise, for not adding native support for bounded types to C++? Anything to do with how templates are currently processed? Multiple inheritance issues?
I would expect it to be quite useful.
C++ has SFINAE which can be exploited via std::enable_if fairly easily. In conjunction with type_traits it is actually, IMO, more powerful than the bounded types that Java and C# have. With a little work you can also make some nice constexpr functions to test these things out for you. Combine that with some macros and you have something that looks sorta like it
#include <iostream>
#include <type_traits>
#define ENABLE_IF typename std::enable_if<
#define THEN(T) ,T>::type
class foo {};
class bar : public foo {};
template<class T, class U>
constexpr bool extends() {
return std::is_base_of<
typename std::remove_reference<U>::type,
typename std::remove_reference<T>::type
>::value;
}
template<class T>
ENABLE_IF extends<T, foo>() THEN(void) test(T&& v) {
std::cout << "T extends foo!!";
}
int main() {
test(bar{});
}
Now I'm not sure I would recommenced this but it is doable and as of now I see no issue in doing it beyond SFINAE being hard to debug
The simple fact is, the reason why this is not in is because nobody has come up with a feature that would make it work without horrific side effects. The Committee has been working on the problem for a decade or more, and the latest iteration still isn't fit for purpose.
Also, the generic restrictions you refer to are not bounded types at all. The only bound they support is "X inherits from Y", essentially, and frankly, SFINAE with std::is_base_of covers this situation just fine. C++ would need something far more powerful to be useful, since run-time inheritance is one of the least useful features.
Most of the time, the constraints on a template argument should not be on the type, but on the operations that the template needs. C++ does that, in a somewhat awkward way, simply because you get an error message if an operation that the template uses isn't there. For example:
template <class T>
void show(T t) {
std::cout << t << std::endl;
}
If you call this template function with a type that doesn't implement operator<< you'll get an error. The Java approach would be to define an interface with a print method, and require that the user pass an object of a type that implements that interface. The C++ approach doesn't require all that mechanism.
The problem with doing this in C++ is that you can get error messages that are very confusing. Often the missing operation is used in some low-level part of another template, and the error message has no clear relation to the code that you wrote. That's one of the drives behind concepts: the author of the template can set out what operations it uses, and passing an object whose type doesn't support those operations will result in a violation of the concept right at the interface, instead of deep within the implementation, so you will probably get a more useful error message.
The purpose of the bounded type parameter is to raise a compile-time error in case of a mismatch of the supplied type and a desired base-class, so this is easily achievable in C++11 and up, with a static_assert and supplying to it the value of the std::is_base_of as follows:
template <typename T>
class C {
static_assert(std::is_base_of<SomeBoundedBaseClass, T>::value, "Bounded type parameter violation!");
//...the rest of the class C
};
where SomeBoundedBaseClass is your class to which you want to bound the type parameter T to be a descendant of or match exactly.
Also note that this way you can mention any custom message to be shown as a compile error, so it has even an advantage over the Java's built-in functionality. Needless to say that C++ is more verbose, but it gives also more freedom.

C++11 static_assert (and functions to be used therein)

static_assert seems to be a very nice feature together with templates.
However, I have trouble finding functions in the standard library for doing various tests at compile time.
For example, I am looking for a function to check whether a type is a subtype of another one. boost::is_base_of does the job, however, is a comparable function in std, so I do not need to rely on boost.
Basically, is there a good source for a list of functions which can be used in static_assert and are contained in the standard library of C++11?
When is static_assert executed? Can I put it anywhere in a template and it is evaluated for each template instanciation? Could it be used to constrain template parameters to be a specific subtype of a class?
Take a look at the final C++11 draft, section 20.7, particularly the <type_traits> header.
What you are asking is: std::is_base_of<base, derived>::value;
Regarding your question: static_assert can be evaluated whenever the compiler sees fit, but it will usually:
In a template: if the expression uses dependent names, in instatiation time; else, in definition time.
Out of template: in definition time.
In addition to #rodrigo’s answer (he was faster …),
When is static assert executed? Can I put it anywhere in a template and it is evaluated for each template instanciation? Could it be used to constrain template parameters to be a specific subtype of a class?
Unfortunately, no. For instance, a static_assert(false, "bummer"); is always executed, no matter the template. This in particular fails if you want to (partially) specialise a template.
The standard (§7.4) says:
[If the condition to static_assert is false] the program is ill-formed, and the resulting diagnostic message (1.4) shall include the text of the string-literal, […]
Which is unfortunately quite unspecific but this lack of specificity is in fact exactly how static_assert behaves when it’s not dependent on a template type.
You need to make the condition in a static_assert depend on the template argument to bind its execution to the particular template argument.
So the following would fail:
template <typename T>
struct some_type {
static_assert(false, "T must be a pointer type");
};
template <typename T>
struct some_type<T*> {
// …
};
Finally, I heartily recommend you read Marthino’s article on More type traits which details this process more, and gives hints on how to solve many trait-related problems elegantly.

C++ Compile-time Duck Typing with Interfaces

Is there a way to do something like the following in C++
template<typename TAnimal>
bool can_eat(TAnimal& animal) where bool TAnimal::IsAlive() exists
{
return !animal.IsAlive();
}
//...
Duck duck;
assert(can_eat(duck) == true); //compiles
Wood wood;
can_eat(wood); // fails to compile because wood doesn't have IsAlive()
The explicit interface, in my opinion, makes it more clear what the function expects. However, I don't want to create an actual interface class (very tedious).
Do not use enable_if to enforce your requirements. Using enable_if will make the function 'disappear', which can be quite confusing for the user. Typical symptom is an error message such as error: no matching function for call to expression. That doesn't exactly convey to the user that a requirement violated.
You should instead enforce your requirements using static_assert, assuming C++0x. If you're using C++03, whether you should be using an emulation of static_assert (e.g. Boost's STATIC_ASSERT) or not is a toss-up since that usually means trading one error message for the other.
Contrast:
// SFINAE for types that do not decay to int
template<
typename T
, typename = typename std::enable_if<
std::is_same<
typename std::decay<T>::type
, int
>::value
>::type
>
void
f(T&&)
{}
// using static assert instead
template<
typename T
>
void
g(T&&)
{
static_assert( std::is_same<typename std::decay<T>::type, int>::value
, "Constraints violation" );
}
Using GCC I get the following error for doing f("violation") (both messages come with filename and line number):
error: no matching function for call to 'f(const char [10])'
On the other hand, g("violation") yields:
error: static assertion failed: "Constraints violation"
Now imagine that you use clear, explicit messages in your assertions such as foo: parameter type must be CopyConstructible inside template foo.
With that said, SFINAE and static_assert are somewhat antagonistic, thus having both explicit constraint violation messages and clever overloads isn't always possible and/or easy.
What you want to do is easily achieved using Boost.ConceptCheck. It does however require to write out-of-line code: the constraints class. I also don't think it uses static_assert where available, so the error messages might not be as nice. This could be changed in the future.
Another possibility is to use static_assert + type traits. What's interesting with that approach is that with C++0x the library comes with a bevy of useful traits, which you can use right out of the box without writing out-of-line code. Even more interesting is that the use of traits is not limited to writing constraints, they can also be used with SFINAE to make clever overloads.
However, there is no trait that is available off-hand to check whether a type supports a particular member of operation, possibly due to the way C++ handles the names of functions. We can't use something like has_member<T, &T::member_to_test_for> either because that would only make sense if the member we were testing for existed in the first place (disregarding things like overloads and the fact that we also need to pass the signature of the member to the trait).
Here's how to transform an arbitrary expression into a trait:
template<typename T>
struct void_ {
typedef void type;
};
template<typename T>
struct trait {
private:
typedef char yes[1];
typedef char no[2];
template<typename U>
static
yes&
test(U&&
, typename void_<decltype( std::declval<U&>().member() )>::type* = 0);
static
no&
test(...);
public:
static constexpr bool value = sizeof test(std::declval<T>()) == sizeof(yes);
};
Notice how sizable this is. Writing a Boost.ConceptCheck constraints class might be easier (but remember, not reusable for SFINAE).
The arbitrary expression is std::declval<U&>().member(). Here, the requirements are that given an lvalue reference of U (or T for the case where the trait is true, if you will), then calling member() on it is valid.
You could also check that the type of that expression (i.e. the result type of whatever overload of member has been picked for this expression) is convertible to a type (do not check whether it is that type; that's too restrictive for no good reason). That would inflate the trait however, again making this in favour of a constraints class.
I do not know of a way to make a static_assert part of the signature of a function template (this seems to be something you want), but it can appear inside a class template. Boost.ConceptCheck doesn't support that either.
This is something that concepts were intended to solve.
Concepts were a proposed addition to the latest C++ standard, but were dropped because the commitee wasn't convinced that they were solid enough to include in the language. See what Herb Sutter wrote about their exclusion from the standard.
Technically, concepts are unneeded, as template simply use whatever they can (i.e. lose the where clause, and you have what you're asking for). If the required method isn't there at compile time, then the code simply will not compile. But concepts would give the coder more explicit control over the type's interface, and would give much more reasonable error message than currently provided by most compilers.
Boost offers BOOST_STATIC_ASSERT for this. The just recently approved version of the C++ standard will offer a built-in version of a static_assert macro.
enable_if, on the other hand, isn’t really well suited for this. It can be used but the primary purpose of enable_if is to distinguish between otherwise ambiguous overloads.
where void TAnimal::IsAlive() exists
I assume you mean bool TAnimal::IsAlive()? If so, C++ already does what you are asking. If Duck has the IsAlive() method, then this will compile:
Duck duck;
assert(can_eat(duck) == true); //compiles
If Wood does not have the IsAlive() method, this will not compile:
Wood wood;
can_eat(wood); // fails to compile because wood doesn't have IsAlive()
That's what you're asking for right?
You don't have to do anything--just omit the hypothetical "where ... exists" from your example and it is normal C++ code that works.
If you insist on having the function be available only under some condition, you might try combining boost::enable_if with has_member from here: http://lists.boost.org/Archives/boost/2002/03/27229.php
The idea being that you would only allow the template function to be instantiated if some condition was met...but since SFINAE the compiler is basically going to do that for you already in the case where the condition is the same as the actual compile-time needs of the function (as in your example).
As others have said this will just work. it won't be able to instantiate the template if the function does not exist.
The boost library contains some classes to assist with this kind of thing though, for example enable_if which can be used to only "enable" a template where a condition is true. There is also the type traits library which is kind of related, you might be able to use this to determine at compile time if the function you want to call exists.
I have to admit I've not used any of this myself, but it looks to me like you should be able to use it to achieve what you want...

How to reduce compile time with C++ templates

I'm in the process of changing part of my C++ app from using an older C type array to a templated C++ container class. See this question for details. While the solution is working very well, each minor change I make to the templated code causes a very large amount of recompilation to take place, and hence drastically slows build time. Is there any way of getting template code out of the header and back into a cpp file, so that minor implementation changes don't cause major rebuilds?
Several approaches:
The export keyword could theoretically help, but it was poorly supported and was officially removed in C++11.
Explicit template instantiation (see here or here) is the most straightforward approach, if you can predict ahead of time which instantiations you'll need (and if you don't mind maintaining this list).
Extern templates, which are already supported by several compilers as extensions. It's my understanding that extern templates don't necessarily let you move the template definitions out of the header file, but they do make compiling and linking faster (by reducing the number of times that template code must be instantiated and linked).
Depending on your template design, you may be able to move most of its complexity into a .cpp file. The standard example is a type-safe vector template class that merely wraps a type-unsafe vector of void*; all of the complexity goes in the void* vector that resides in a .cpp file. Scott Meyers gives a more detailed example in Effective C++ (item 42, "Use private inheritance judiciously", in the 2nd edition).
I think the general rules apply. Try to reduce coupling between parts of the code. Break up too large template headers into smaller groups of functions used together, so the whole thing won't have to be included in each and every source file.
Also, try to get the headers into a stable state fast, perhaps testing them out against a smaller test program, so they wouldn't need changing (too much) when integrated into a larger program.
(As with any optimization, it might be less worth to optimize for the compiler's speed when dealing with templates, rather than finding an "algorithmic" optimization that reduces the work-load drastically in the first place.)
First of all, for completeness, I'll cover the straightforward solution: only use templated code when necessary, and base it on non-template code (with implementation in its own source file).
However, I suspect that the real issue is that you use generic programming as you would use typical OO-programming and end up with a bloated class.
Let's take an example:
// "bigArray/bigArray.hpp"
template <class T, class Allocator>
class BigArray
{
public:
size_t size() const;
T& operator[](size_t index);
T const& operator[](size_t index) const;
T& at(size_t index);
T const& at(size_t index);
private:
// impl
};
Does this shock you ? Probably not. It seems pretty minimalist after all. The thing is, it's not. The at methods can be factored out without any loss of generality:
// "bigArray/at.hpp"
template <class Container>
typename Container::reference_type at(Container& container,
typename Container::size_type index)
{
if (index >= container.size()) throw std::out_of_range();
return container[index];
}
template <class Container>
typename Container::const_reference_type at(Container const& container,
typename Container::size_type index)
{
if (index >= container.size()) throw std::out_of_range();
return container[index];
}
Okay, this changes the invocation slightly:
// From
myArray.at(i).method();
// To
at(myArray,i).method();
However, thanks to Koenig's lookup, you can call them unqualified as long as you put them in the same namespace, so it's just a matter of habit.
The example is contrived but the general point stands. Note that because of its genericity at.hpp never had to include bigArray.hpp and will still produce as tight code as if it were a member method, it's just that we can invoke it on other containers if we wish.
And now, a user of BigArray does not need to include at.hpp if she does not uses it... thus reducing her dependencies and not being impacted if you change the code in that file: for example alter std::out_of_range call to feature the file name and line number, the address of the container, its size and the index we tried to access.
The other (not so obvious) advantage, is that if ever integrity constraint of BigArray is violated, then at is obviously out of cause since it cannot mess with the internals of the class, thus reducing the number of suspects.
This is recommended by many authors, such as Herb Sutters in C++ Coding Standards:
Item 44: Prefer writing nonmember nonfriend functions
and has been extensively used in Boost... But you do have to change your coding habits!
Then of course you need to only include what you do depend on, there ought to be static C++ code analyzers that report included but unused header files which can help figuring this out.
Using templates as a problem solving technique can create compilation slowdowns. A classical example of this is the std::sort vs. qsort function from C. The C++ version of this function takes longer to compile because it needs to be parsed in every translation unit and because almost every use of this function creates a different instance of this template (assuming that closure types are usually provided as sorting predicate).
Although these slowdowns are to be expected, there are some rules that can help you to write efficient templates. Four of them are described below.
The Rule of Chiel
The Rule of Chiel, presented below, describes which C++ constructs are the most difficult ones for the compiler. If possible, it’s best to avoid those constructs to reduce compilation times.
The following C++ features/constructs are sorted in descending order by compile time:
SFINAE
Instantiating a function template
Instantiating a type
Calling an alias
Adding a parameter to a type
Adding a parameter to an alias call
Looking up a memorized type
Optimizations based on the above rules were used when Boost.TMP was designed and developed. As much as possible, avoid top constructs for quick template compilation.
Below are some examples illustrating how to make use of the rules listed above.
Reduce Template Instantiations
Let's have a look at std::conditional. Its declaration is:
template< bool B, typename T, typename F >
struct conditional;
Whenever we change any of three arguments given to that template, the compiler will have to create a new instance of it. For example, imagine the following types:
struct first{};
struct second{};
Now, all the following will end up in instantiations of different types:
using type1 = conditional<true, first, second>;
using type2 = conditional<true, second, first>;
std::is_same_v<type1, type2>; // it’s false
using type3 = conditional<false, first, second>;
using type4 = conditional<false, second, first>;
std::is_same_v<type1, type2>; // it’s false
We can reduce the number of instantiations by changing the implementation of conditional to:
template <bool>
struct conditional{
template <typename T, typename F>
using type = T;
};
template <>
struct conditional<false>{
template <typename T, typename F>
using type = F;
};
In this case, the compiler will create only two instantiations of type “conditional” for all possible arguments. For more details about this example, check out Odin Holmes' talk about the Kvasir library.
Create Explicit Template Instantiations
Whenever you suspect that an instance of a template is going to be used often, it’s a good idea to explicitly instantiate it. Usually, std::string is an explicit instantiation of std::basic_string<char>.
Create Specializations for Compile-time Algorithms
Kvasir-MPL specializes algorithms for long lists of types to speed them up. You can see an example of this here. In this header file, the sorting algorithm is manually specialized for a list of 255 types. Manual specialization speeds up compilations for long lists.
You can use explicit instantiation; however, only the template types you instantiate will compile ahead of time.
You might be able to take advantage of c++20's modules.
If you can factor out the templated types from your algorithm, you can put it in its own .cc file.
I wouldn't suggest this unless it's a major problem but: you may be able to provide a template container interface that is implemented with calls to a void* implementation that you are free to change at will.
Before c++11 you could use a compiler that supports the export keyword.
You can define a base class without templates and move most of the implementation there. The templated array would then define only proxy methods, that use base class for everything.

Hypothetical, formerly-C++0x concepts questions

(Preamble: I am a late follower to the C++0x game and the recent controversy regarding the removal of concepts from the C++0x standard has motivated me to learn more about them. While I understand that all of my questions are completely hypothetical -- insofar as concepts won't be valid C++ code for some time to come, if at all -- I am still interested in learning more about concepts, especially given how it would help me understand more fully the merits behind the recent decision and the controversy that has followed)
After having read some introductory material on concepts as C++0x (until recently) proposed them, I am having trouble wrapping my mind around some syntactical issues. Without further ado, here are my questions:
1) Would a type that supports a particular derived concept (either implicitly, via the auto keyword, or explicitly via concept_maps) also need to support the base concept indepdendently? In other words, does the act of deriving a concept from another (e.g. concept B<typename T> : A<T>) implicitly include an 'invisible' requires statement (within B, requires A<T>;)? The confusion arises from the Wikipedia page on concepts which states:
Like in class inheritance, types that
meet the requirements of the derived
concept also meet the requirements of
the base concept.
That seems to say that a type only needs to satisfy the derived concept's requirements and not necessarily the base concept's requirements, which makes no sense to me. I understand that Wikipedia is far from a definitive source; is the above description just a poor choice of words?
2) Can a concept which lists typenames be 'auto'? If so, how would the compiler map these typenames automatically? If not, are there any other occasions where it would be invalid to use 'auto' on a concept?
To clarify, consider the following hypothetical code:
template<typename Type>
class Dummy {};
class Dummy2 { public: typedef int Type; };
auto concept SomeType<typename T>
{
typename Type;
}
template<typename T> requires SomeType<T>
void function(T t)
{}
int main()
{
function(Dummy<int>()); //would this match SomeType?
function(Dummy2()); //how about this?
return 0;
}
Would either of those classes match SomeType? Or is a concept_map necessary for concepts involving typenames?
3) Finally, I'm having a hard time understanding what axioms would be allowed to define. For example, could I have a concept define an axiom which is logically inconsistent, such as
concept SomeConcept<typename T>
{
T operator*(T&, int);
axiom Inconsistency(T a)
{
a * 1 == a * 2;
}
}
What would that do? Is that even valid?
I appreciate that this is a very long set of questions and so I thank you in advance.
I've used the most recent C++0x draft, N2914 (which still has concepts wording in it) as a reference for the following answer.
1) Concepts are like interfaces in that. If your type supports a concept, it should also support all "base" concepts. Wikipedia statement you quote makes sense from the point of view of a type's client - if he knows that T satisfies concept Derived<T>, then he also knows that it satisfies concept Base<T>. From type author perspective, this naturally means that both have to be implemented. See 14.10.3/2.
2) Yes, a concept with typename members can be auto. Such members can be automatically deduced if they are used in definitions of function members in the same concept. For example, value_type for iterator can be deduced as a return type of its operator*. However, if a type member is not used anywhere, it will not be deduced, and thus will not be implicitly defined. In your example, there's no way to deduce SomeType<T>::Type for either Dummy or Dummy1, as Type isn't used by other members of the concept, so neither class will map to the concept (and, in fact, no class could possibly auto-map to it). See 14.10.1.2/11 and 14.10.2.2/4.
3) Axioms were a weak point of the spec, and they were being constantly updated to make some (more) sense. Just before concepts were pulled from the draft, there was a paper that changed quite a bit - read it and see if it makes more sense to you, or you still have questions regarding it.
For your specific example (accounting for syntactic difference), it would mean that compiler would be permitted to consider expression (a*1) to be the same as (a*2), for the purpose of the "as-if" rule of the language (i.e. the compiler permitted to do any optimizations it wants, so long as the result behaves as if there were none). However, the compiler is not in any way required to validate the correctness of axioms (hence why they're called axioms!) - it just takes them for what they are.