//
// gsl-lite is based on GSL: Guidelines Support Library.
// For more information see https://github.com/gsl-lite/gsl-lite
//
// Copyright (c) 2015-2019 Martin Moene
// Copyright (c) 2019-2021 Moritz Beutel
// Copyright (c) 2015-2018 Microsoft Corporation. All rights reserved.
// 
// This code is licensed under the MIT License (MIT). 
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
// THE SOFTWARE. 

#include "gsl-lite.t.hpp"

#include <vector>

namespace {

bool expects( bool x ) { gsl_Expects( x ); return x; } 
bool ensures( bool x ) { gsl_Ensures( x ); return x; }
bool assert_( bool x ) { gsl_Assert( x ); return x; }
void failFast() { gsl_FailFast(); }
bool expectsAudit( bool x ) { gsl_ExpectsAudit( x ); return x; } 
bool ensuresAudit( bool x ) { gsl_EnsuresAudit( x ); return x; }
bool assertAudit( bool x ) { gsl_AssertAudit( x ); return x; }

enum Color
#if gsl_CPP11_OR_GREATER
: int
#endif
{ red, green, blue };

#if gsl_COMPILER_NVHPC_VERSION && !gsl_CPP11_OR_GREATER
# pragma diag_suppress 941 // Suppress:  missing return statement at end of non-void function "..."
#endif
std::string colorToString( Color color )
{
    switch (color)
    {
    case red: return "red";
    case green: return "green";
    case blue: return "blue";
    }
    gsl_FailFast();  // this should keep the compiler from issuing a warning about not returning a value
}
#if gsl_COMPILER_NVHPC_VERSION && !gsl_CPP11_OR_GREATER
# pragma diag_default 941
#endif


struct ConvertibleToBool
{
#if gsl_CPP11_OR_GREATER
    explicit
#endif
    operator bool(void) const gsl_noexcept { return true; }
};


} // anonymous namespace

CASE( "gsl_Expects(): Allows a true expression" )
{
    EXPECT_NO_THROW( expects( true  ) );
}

CASE( "gsl_Ensures(): Allows a true expression" )
{
    EXPECT_NO_THROW( ensures( true  ) );
}

CASE( "gsl_Assert(): Allows a true expression" )
{
    EXPECT_NO_THROW( assert_( true  ) );
}

CASE( "gsl_Expects(): Terminates on a false expression" )
{
    EXPECT_THROWS( expects( false ) );
}

CASE( "gsl_Ensures(): Terminates on a false expression" )
{
    EXPECT_THROWS( ensures( false ) );
}

CASE( "gsl_Assert(): Terminates on a false expression" )
{
    EXPECT_THROWS( assert_( false ) );
}

CASE( "gsl_FailFast(): Suppresses compiler warning about missing return value" )
{
    EXPECT( colorToString(red) == "red" );
}

CASE( "gsl_FailFast(): Terminates" )
{
    EXPECT_THROWS( failFast() );
#if gsl_CPP11_OR_GREATER
    EXPECT_THROWS( colorToString( Color( 42 ) ) );
#endif
}

CASE( "gsl_ExpectsAudit(): Allows a true expression" )
{
    EXPECT_NO_THROW( expectsAudit( true  ) );
}

CASE( "gsl_EnsuresAudit(): Allows a true expression" )
{
    EXPECT_NO_THROW( ensuresAudit( true  ) );
}

CASE( "gsl_AssertAudit(): Allows a true expression" )
{
    EXPECT_NO_THROW( assertAudit( true  ) );
}

CASE( "gsl_ExpectsAudit(): Terminates on a false expression in AUDIT mode" )
{
#if defined( gsl_CONFIG_CONTRACT_CHECKING_AUDIT )
    EXPECT_THROWS( expectsAudit( false ) );
#else
    EXPECT_NO_THROW( expectsAudit( false ) );
#endif
}

CASE( "gsl_EnsuresAudit(): Terminates on a false expression in AUDIT mode" )
{
#if defined( gsl_CONFIG_CONTRACT_CHECKING_AUDIT )
    EXPECT_THROWS( ensuresAudit( false ) );
#else
    EXPECT_NO_THROW( ensuresAudit( false ) );
#endif
}

CASE( "gsl_AssertAudit(): Terminates on a false expression in AUDIT mode" )
{
#if defined( gsl_CONFIG_CONTRACT_CHECKING_AUDIT )
    EXPECT_THROWS( assertAudit( false ) );
#else
    EXPECT_NO_THROW( assertAudit( false ) );
#endif
}

int myAt( int i, std::vector<int> const& v )
{
    // The arguments to `__assume( x )` (MSVC) and `__builtin_assume( x )` (Clang) are never evaluated, so they cannot incur side-effects. We would like to implement
    // `gsl_ASSUME_()` in terms of these. However, Clang always emits a diagnostic if a potential side-effect is discarded, and every call to a function not annotated
    // `__attribute__ ((pure))` or `__attribute__ ((const))` is considered a potential side-effect (e.g. the call to `v.size()` below). In many cases Clang is capable
    // of inlining the expression and find it free of side-effects, cf. https://gcc.godbolt.org/z/ZcKfbp, but the warning is produced anyway.
    //
    // To avoid littering user code with warnings, we instead define `gsl_ASSUME_()` in terms of `__builtin_unreachable()`. The following `gsl_ASSUME_()` statement
    // should thus compile without any warnings.

    gsl_Expects( i >= 0 && static_cast<std::size_t>(i) < v.size() );
    return v.at( static_cast<std::size_t>(i) );
}

CASE( "gsl_Expects(): No warnings produced for function calls in precondition checks" )
{
    std::vector<int> v;
    v.push_back( 42 );
    EXPECT_NO_THROW( myAt( 0, v ) );
    EXPECT_THROWS( myAt( 1, v ) );
}

CASE( "gsl_Expects(): Supports explicit conversions to bool" )
{
    // `gsl_Expects()` should be compatible with explicit conversions to bool.
    gsl_Expects( ConvertibleToBool() );

    if ( ConvertibleToBool() ) { } // to get rid of weird NVCC warning about never-referenced conversion operator
}


// end of file
