Enforcing function contract at compile time when possible

(this question was inspired by How can I generate a compilation error to prevent certain VALUE (not type) to go into the function?)

Let's say, we have a single-argument foo, semantically defined as

int foo(int arg) {
    int* parg;
    if (arg != 5) {
        parg = &arg;
    }

    return *parg;
}

The whole code above is used to illustrate a simple idea - function returns it's own argument unless the argument is equal to 5, in which case behavior is undefined.

Now, the challenge - modify the function in such a way, that if it's argument is known at compile time, a compiler diagnostic (warning or error) should be generated, and if not, behavior remains undefined in runtime. Solution could be compiler-dependent, as long as it is available in either one of the big 4 compilers.

Here are some potential routes which do not solve the problem:

  • Making function a template which takes it's argument as a template parameter - this doesn't solve the problem because it makes function ineligible for run-time arguments
  • Making function a constexpr - this doesn't solve the problem, because even when compilers see undefined behavior, they do not produce diagnostics in my tests - instead, gcc inserts ud2 instruction, which is not what I want.

I got error with constexpr when used in constant expression for:

constexpr int foo(int arg) {
    int* parg = nullptr;
    if (arg != 5) {
        parg = &arg;
    }
    return *parg;
}

Demo

We cannot know that argument value is known at compile type, but we can use type representing value with std::integral_constant

// alias to shorten name. 
template <int N>
using int_c = std::integral_constant<int, N>;

Possibly with UDL with operator "" _c to have 5_c, 42_c.

and then, add overload with that:

template <int N>
constexpr auto foo(int_c<N>) {
    return int_c<foo(N)>{};
}

So:

foo(int_c<42>{}); // OK
foo(int_c<5>{}); // Fail to compile

// and with previous constexpr:
foo(5); // Runtime error, No compile time diagnostic
constexpr auto r = foo(5); // Fail to compile

As I said, arguments are not known to be constant inside the function, and is_constexpr seems not possible in standard to allow dispatch, but some compiler provide built-in for that (__builtin_constant_p), so with MACRO, we can do the dispatch:

#define FOO(X) [&](){ \
    if constexpr (__builtin_constant_p(X)) {\
        return foo(int_c<__builtin_constant_p (X) ? X : 0>{});\
    } else {\
        return foo(X); \
    } \
}()

Demo

Note: Cannot use foo(int_c<X>{}) directly, even in if constexpr, as there is still some syntax check.

Enforcing function contract at compile time when possible, Now, the challenge - modify the function in such a way, that if it's argument is known at compile time, a compiler diagnostic (warning or error) should be  CTFE is a mechanism which allows the compiler to execute functions at compile time. There is no special set of the D language necessary to use this feature - whenever a function just depends on compile time known values the D compiler might decide to interpret it during compilation. // result will be calculated at compile // time.

Enforcing code contracts with [[nodiscard]], In other words, you can enforce the code contract for a function, That creates inconsistencies and can lead to some severe runtime errors. and you'll get a warning whenever you call any function that returns SuperImportantType. In other words, you can enforce the code contract for a function, so that the caller won't skip the returned value. Sometimes such omission might cause you a bug, so using [ [nodiscard]] will improve code safety. You can play with some code below:

It's not perfect and it requires us to use arguments in two different places, but it 'works':

template<int N = 0>
int foo(int arg = 0) {
    static_assert(N != 5, "N cannot be 5!");
    int* parg;
    if (arg != 5) {
        parg = &arg;
    }

    return *parg;
}

We can call it like so:

foo<5>();   // does not compile
foo(5);     // UB
foo<5>(5);  // does not compile
foo<5>(10); // does not compile
foo<10>(5); // UB
foo();      // fine
foo<10>();  // fine
foo(10);    // fine

Why aren't we researching more towards compile time guarantees , an example but I feel like there's a lot more that we can enforce at compile time. If you want to call that function, you then have to use parameters which are The above are very simple contracts, you can insert almost any assumption or so it might be possible that the compile time check of a program with contracts (or​  Function overloading is usually associated with statically-typed programming languages that enforce type checking in function calls. An overloaded function is really just a set of different functions that happen to have the same name. The determination of which function to use for a particular call is resolved at compile time.

Privacy Act Issuances Compilation, tory or law enforcement agencies or orgaizations , or regulatory or law an agreement between the parties in connection with litigation or administrative or entity is specifically designated to perform particular functions with respect and those matters appeared to be relevant at the time to the subject matter of the inquiry . We assume the interface functions of a contract are strongly typed, known at compilation time and static. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. This specification does not address contracts whose interface is dynamic or otherwise known only at run-time.

jfinkhaeuser/meta: C++ Metaprogramming Primivites and , By authorized SEC personnel in connection with their official functions including , but the conduct of investigations into possible violations of the Federal securities laws relating to the Commission ' s regulatory and law enforcement functions . the letting of a contract , or the issuance of a license , grant or other benefit . 4) Inline function may increase compile time overhead if someone changes the code inside the inline function then all the calling location has to be recompiled because compiler would require to replace all the code once again to reflect the changes, otherwise it will continue with old functionality.

Functions, for.h provides compile-time for loops; comparison.h provides compile-time to them; you can use them in a function prototype to enforce a contract, for example. LiquidHaskell (LH) refines Haskell's types with logical predicates that let you enforce critical properties at compile time. (Hover on images to animate.) (Hover on images to animate.) Guarantee Functions are Total

Comments
  • out of curiosity, why this Q is upvoted while linked-one is downvoted?
  • @appleapple Because this one is #1 well-formulated, #2 gives a short a suffisant context and #3 defines a precise (SMART) objective.
  • @YSC well, this question does not show any research effort; it's unclear or not useful. I'd give a downvote if I have to cast one.
  • @appleapple You don't have to cast one, but if you want to, feel free to do so. This is a matter of taste I guess. I find it useful (I never succeded at that task and I think it would be nice to have an API making a value-related contract enforced by a compiler error).
  • @IłyaBursov - not a duplicate, suggested answer doesn't solve the problem.
  • Constexpr version doesn't give me any diagnostic gcc.godbolt.org/z/ZCho3b when it's result is not used to initialize constant expression variable.
  • I do understand that, and this is exactly what I am referring to - unless the function is called in constant expression, no diagnostics are provided
  • That's why I provide in my answer way to pass compile time argument (with int_c) which allows to check at compile time , as they call it in constant expression.
  • Jarod, may be I am not making myself clear enough. The goal is to have a callable function, which would enforce contract in compile time when it possible (i.e. argument is known). Your solution effectively introduces two overloads, and successful compile-time check is predicated on developers discipline to call second overload.
  • Found a way with built-in of gcc supported by clang (and so works on clang but not with g++ ;-) )
  • No, it doesn't work. Behavior is dependent on programmer's discipline (making sure to provide template argument when it's known at compile time). Instead of this approach, if programmer is disciplined, I'd simply have two functions - templated and not.
  • Fair, thank you for the comment. I totally agree that this does not actually solve the entire problem, but I will leave it as a neutral hint / information for future visitors :>
  • You might add an additional runtime check that argument are equal or defaulted.