diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..c8624ad0c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,84 @@ +# GitHub Copilot Instructions for GSL (Guidelines Support Library) + +## Project Overview +This repository contains the Guidelines Support Library (GSL), a Microsoft implementation of types and functions +suggested for use by the C++ Core Guidelines. It's a header-only C++ library with emphasis on safety, +correctness, and zero overhead. + +## Coding Standards + +### General +- Follow C++ Core Guidelines wherever possible +- Use meaningful type, function, and template parameter names +- Keep functions small and focused with clear preconditions/postconditions +- Include comments for complex code, but prefer self-documenting code +- Use the Expects() and Ensures() macros for contract verification + +### Style Guidelines +- Use 4 spaces for indentation (not tabs) +- Maximum line length of 100 characters +- Follow GSL naming conventions (lowercase with underscores) +- Keep templates clean and readable with appropriate spacing +- Use C++14 features since this is the minimum standard supported + +### Error Handling +- Use Expects() for preconditions and Ensures() for postconditions +- Design for fail-fast semantics (std::terminate) on contract violations +- Template constraints should use static_assert or SFINAE +- Don't throw exceptions from basic operations + +### Testing +- Write thorough unit tests for every component using GTest +- Test for all edge cases and error conditions +- Ensure cross-platform compatibility in tests +- Maintain 100% code coverage for changed code + +## Project-Specific Conventions + +### Architecture +- All public types must be in the gsl namespace +- Design for zero overhead abstractions when possible +- Respect the distinction between Owners and Views +- Maintain backward compatibility with existing GSL code + +### Version Control +- Link all PRs to related issues +- Use clear commit messages explaining what and why +- Follow the contribution guidelines documented in CONTRIBUTING.md +- PRs should include appropriate tests with 100% coverage for changed code + +### Documentation +- Document all public APIs with clarity on preconditions and postconditions +- Keep header comments up-to-date +- Include examples for complex functionality in docs/headers.md + +## Technology Stack +- C++14 (minimum) for core implementation +- CMake build system (3.14+) +- Google Test for unit testing +- Support for multiple compilers (MSVC, GCC, Clang) + +## Security Considerations +- Bounds checking is a core principle - enforce it consistently +- Design for safety while minimizing overhead +- Ensure undefined behavior is explicitly detected where possible + +## Performance Guidelines +- Optimize for both safety and performance +- Constexpr-enable functions wherever possible +- Avoid hidden allocations +- Use noexcept appropriately for move operations and other performance-critical functions + +## Cross-Platform Support +- Code must work across: + - Windows (MSVC) + - Linux (GCC, Clang) + - macOS (AppleClang) + - Android and iOS where applicable + +## Copilot Tasks +- You can find the CMake artifacts for C++20 in build-cxx20 and C++14 in build-cxx14. +- Before publishing a PR, verify the following: + - There are no compiler warnings or errors when building the test suite. + - The test suite passes on all supported platforms and compilers. + - The test suite passes for both C++14 and C++20. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..c410c66ba --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,22 @@ +name: "Copilot Setup Steps" + +on: workflow_dispatch + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Build Dependencies + run: sudo apt-get update && sudo apt-get install -y clang cmake make + + - name: Configure CMake (C++14) + run: cmake -B build-cxx14 . -DGSL_CXX_STANDARD=14 -DGSL_TEST=ON -G "Unix Makefiles" + + - name: Configure CMake (C++20) + run: cmake -B build-cxx20 . -DGSL_CXX_STANDARD=20 -DGSL_TEST=ON -G "Unix Makefiles" + diff --git a/.gitignore b/.gitignore index 326971f03..4ca21edc9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ CMakeFiles -build +build*/ tests/CMakeFiles tests/Debug *.opensdf diff --git a/include/gsl/pointers b/include/gsl/pointers index 3a5409ada..5e69f589e 100644 --- a/include/gsl/pointers +++ b/include/gsl/pointers @@ -173,7 +173,7 @@ std::ostream& operator<<(std::ostream& os, const not_null& val) #endif // !defined(GSL_NO_IOSTREAMS) template -auto operator==(const not_null& lhs, +constexpr auto operator==(const not_null& lhs, const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) -> decltype(lhs.get() == rhs.get()) { @@ -181,7 +181,7 @@ auto operator==(const not_null& lhs, } template -auto operator!=(const not_null& lhs, +constexpr auto operator!=(const not_null& lhs, const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) -> decltype(lhs.get() != rhs.get()) { @@ -189,7 +189,7 @@ auto operator!=(const not_null& lhs, } template -auto operator<(const not_null& lhs, +constexpr auto operator<(const not_null& lhs, const not_null& rhs) noexcept(noexcept(std::less<>{}(lhs.get(), rhs.get()))) -> decltype(std::less<>{}(lhs.get(), rhs.get())) { @@ -197,7 +197,7 @@ auto operator<(const not_null& lhs, } template -auto operator<=(const not_null& lhs, +constexpr auto operator<=(const not_null& lhs, const not_null& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get()))) -> decltype(std::less_equal<>{}(lhs.get(), rhs.get())) { @@ -205,7 +205,7 @@ auto operator<=(const not_null& lhs, } template -auto operator>(const not_null& lhs, +constexpr auto operator>(const not_null& lhs, const not_null& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get()))) -> decltype(std::greater<>{}(lhs.get(), rhs.get())) { @@ -213,7 +213,7 @@ auto operator>(const not_null& lhs, } template -auto operator>=(const not_null& lhs, +constexpr auto operator>=(const not_null& lhs, const not_null& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get()))) -> decltype(std::greater_equal<>{}(lhs.get(), rhs.get())) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1f2bf7a5d..a8fd30ac3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -204,6 +204,7 @@ add_executable(gsl_tests assertion_tests.cpp at_tests.cpp byte_tests.cpp + constexpr_notnull_tests.cpp notnull_tests.cpp owner_tests.cpp pointers_tests.cpp diff --git a/tests/constexpr_notnull_tests.cpp b/tests/constexpr_notnull_tests.cpp new file mode 100644 index 000000000..9192f85fe --- /dev/null +++ b/tests/constexpr_notnull_tests.cpp @@ -0,0 +1,74 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2025 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 // for not_null +#include + +#include // for declval + +using namespace gsl; + +namespace +{ +constexpr bool comparison_test(const int* ptr1, const int* ptr2) +{ + const not_null p1(ptr1); + const not_null p1_same(ptr1); + const not_null p2(ptr2); + + // Testing operator== + const bool eq_result = (p1 == p1_same); // Should be true + const bool neq_result = (p1 != p2); // Should be true + + // Testing operator<= and operator>= + const bool le_result = (p1 <= p1_same); // Should be true + const bool ge_result = (p1 >= p1_same); // Should be true + + // The exact comparison results will depend on pointer ordering, + // but we can verify that the basic equality checks work as expected + return eq_result && neq_result && le_result && ge_result; +} + +constexpr bool workaround_test(const int* ptr1, const int* ptr2) +{ + const not_null p1(ptr1); + const not_null p1_same(ptr1); + const not_null p2(ptr2); + + // Using .get() to compare + const bool eq_result = (p1.get() == p1_same.get()); // Should be true + const bool neq_result = (p1.get() != p2.get()); // Should be true + + return eq_result && neq_result; +} +} // namespace + +constexpr int test_value1 = 1; +constexpr int test_value2 = 2; + +static_assert(comparison_test(&test_value1, &test_value2), "not_null comparison operators should be constexpr"); +static_assert(workaround_test(&test_value1, &test_value2), "not_null .get() comparison workaround should work"); + +TEST(notnull_constexpr_tests, TestNotNullConstexprComparison) +{ + // This test simply verifies that the constexpr functions compile and run + // If we got here, it means the constexpr comparison operators are working + static const int value1 = 1; + static const int value2 = 2; + EXPECT_TRUE(comparison_test(&value1, &value2)); + EXPECT_TRUE(workaround_test(&value1, &value2)); +} +