diff --git a/.github/images/arch-linux/PKGBUILD.in b/.github/images/arch-linux/PKGBUILD.in index ee7f388d5a4..820ec37ed54 100644 --- a/.github/images/arch-linux/PKGBUILD.in +++ b/.github/images/arch-linux/PKGBUILD.in @@ -16,8 +16,8 @@ arch=('any') url="https://github.com/souffle-lang/souffle" license=('UPL') groups=() -depends=('mcpp' 'gcc>=8' 'openmp' 'sqlite') -makedepends=('git' 'cmake>=3.15' 'bison>=3.0.4' 'flex' 'libffi' 'ncurses' 'zlib') +depends=('mcpp' 'gcc>=8' 'openmp' 'sqlite' 'python3') +makedepends=('git' 'cmake>=3.15' 'bison>=3.0.4' 'flex' 'libffi' 'ncurses' 'zlib' 'python3') optdepends=('bash-completion') provides=('souffle') conflicts=('souffle-git') diff --git a/.github/workflows/VS-CI-Tests.yml b/.github/workflows/VS-CI-Tests.yml new file mode 100644 index 00000000000..1f06de3ea11 --- /dev/null +++ b/.github/workflows/VS-CI-Tests.yml @@ -0,0 +1,76 @@ +name: VS-CI-Tests + +on: + # TODO remove me before merge + push: + branches: + - '**' + pull_request: + types: [opened, synchronize] + workflow_dispatch: + +env: + CHOCO_CACHE_DIR: "${{ github.workspace }}/choco-cache" + +jobs: + + Windows-CMake-MSVC: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v2 + + - name: Dependencies Cache + uses: actions/cache@v2 + env: + cache-name: cache-chocolatey + with: + # cache Chocolatey packages to speed-up the deployment. + path: | + ${{ env.CHOCO_CACHE_DIR }} + key: windows-${{ hashFiles('choco-packages.config') }} + + # Use Chocolatey to install binary dependencies. + - name: Binary Dependencies (Chocolatey) + run: | + choco config set cacheLocation ${{ env.CHOCO_CACHE_DIR }} + choco install choco-packages.config --no-progress --installargs 'ADD_CMAKE_TO_PATH=""System""' + + # Use vcpkg to install devel library dependencies. + - name: Library Dependencies (vcpkg) + uses: lukka/run-vcpkg@v7 + with: + vcpkgGitCommitId: '3a28333d605f92f8659f3af1137324b2d9886101' + vcpkgTriplet: x64-windows + vcpkgArguments: 'sqlite3 zlib libffi' + + - name: Create Build Directory + working-directory: ${{github.workspace}} + run: mkdir build + + - name: Configure Build + working-directory: ${{github.workspace}} + run: | + $env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.." + Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" + refreshenv + cmake -S . -B build -G "Visual Studio 16 2019" -A x64 "-DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=/bigobj -DSOUFFLE_DOMAIN_64BIT=ON -DCMAKE_FIND_LIBRARY_PREFIXES=";lib" -DCMAKE_FIND_LIBRARY_SUFFIXES=".lib;.dll" -DSOUFFLE_USE_CURSES=OFF -DSOUFFLE_USE_ZLIB=ON -DCMAKE_FIND_DEBUG_MODE=FALSE -DSOUFFLE_BASH_COMPLETION=OFF + + - name: Build + working-directory: ${{github.workspace}} + run: cmake --build build --config Release -j4 + + # Run the tests, Visual Studio must be in the environment because cl.exe is required for compiled Souffle. + - name: Check interpreter + working-directory: ${{github.workspace}}/build + shell: cmd + run: | + pushd "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build" & call vcvars64.bat & popd + ctest --output-on-failure --build-config Release --progress -j4 -L interpreted + + - name: Check others + working-directory: ${{github.workspace}}/build + shell: cmd + run: | + pushd "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build" & call vcvars64.bat & popd + ctest --output-on-failure --build-config Release --progress -j2 -LE interpreted + diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml index 8912bf20d1a..d4fa2ba9d00 100644 --- a/.github/workflows/cancel.yml +++ b/.github/workflows/cancel.yml @@ -1,7 +1,7 @@ name: Cancel on: workflow_run: - workflows: ["CI-Tests"] + workflows: ["CI-Tests", "VS-CI-Tests"] types: - requested jobs: diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cdaa22e2fd..b02978ef337 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ option(SOUFFLE_GENERATE_DOXYGEN "Generate Doxygen files (html;htmlhelp;man;rtf;x option(SOUFFLE_CODE_COVERAGE "Enable coverage reporting" OFF) option(SOUFFLE_BASH_COMPLETION "Enable/Disable bash completion" OFF) option(SOUFFLE_USE_LIBFFI "Enable/Disable use of libffi" ON) +option(SOUFFLE_CUSTOM_GETOPTLONG "Enable/Disable custom getopt_long implementation" OFF) cmake_dependent_option(SOUFFLE_USE_LIBCPP "Link to libc++ instead of libstdc++" ON "CMAKE_CXX_COMPILER_ID STREQUAL Clang" OFF) @@ -120,6 +121,24 @@ if (SOUFFLE_USE_LIBCPP) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lc++abi") endif() +if (WIN32) + # Search libraries with and without 'lib' prefix. + set(CMAKE_FIND_LIBRARY_PREFIXES ";lib") + + # Prefix all shared libraries with 'lib'. + set(CMAKE_SHARED_LIBRARY_PREFIX "lib") + + # Prefix all static libraries with 'lib'. + set(CMAKE_STATIC_LIBRARY_PREFIX "lib") + + SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") + SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") + SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") + SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") + SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${OUTPUT_DIRECTORY}") + SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") +endif () + # -------------------------------------------------- # curses libraries for Provenance information # -------------------------------------------------- @@ -147,7 +166,7 @@ if (SOUFFLE_SWIG_PYTHON OR SOUFFLE_SWIG_JAVA) find_package(SWIG REQUIRED) if (SOUFFLE_SWIG_PYTHON) - find_package(Python "3.7" REQUIRED) + find_package(Python3 3.7 REQUIRED) endif() if (SOUFFLE_SWIG_JAVA) @@ -218,7 +237,10 @@ endif() # libffi # -------------------------------------------------- if (SOUFFLE_USE_LIBFFI) - find_package(LibFFI REQUIRED) + find_package(libffi CONFIG QUIET) + if (NOT libffi_FOUND) + find_package(LibFFI REQUIRED) + endif() endif() # -------------------------------------------------- @@ -232,11 +254,6 @@ find_package(Threads REQUIRED) # -------------------------------------------------- if (SOUFFLE_USE_OPENMP) find_package(OpenMP) - if (OPENMP_FOUND) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - endif() endif() # -------------------------------------------------- @@ -338,6 +355,7 @@ endif(SOUFFLE_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") add_subdirectory(src) if (SOUFFLE_ENABLE_TESTING) + find_package(Python3 3.7 REQUIRED) add_subdirectory(src/tests) add_subdirectory(tests) endif() @@ -367,6 +385,7 @@ IF (SOUFFLE_BASH_COMPLETION) endif() +if (NOT WIN32) # -------------------------------------------------- # CPack configuration # -------------------------------------------------- @@ -396,7 +415,7 @@ if (CHECK_OS_RESULT EQUAL 0) # -------------------------------------------------- # Specifying runtime dependencies - set(CPACK_DEBIAN_PACKAGE_DEPENDS "g++ (>= 8), libffi-dev, libncurses5-dev, libsqlite3-dev, mcpp, zlib1g-dev") + set(CPACK_DEBIAN_PACKAGE_DEPENDS "g++ (>= 8), libffi-dev, libncurses5-dev, libsqlite3-dev, mcpp, zlib1g-dev, python3") # Auto-generate any runtime dependencies that are required SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) @@ -407,22 +426,23 @@ if (CHECK_OS_RESULT EQUAL 0) endif() if (CHECK_OS_OUTPUT MATCHES "FEDORA" OR CHECK_OS_OUTPUT MATCHES "CENTOS") - SET(CPACK_RPM_PACKAGE_GROUP "Unspecified") - SET(CPACK_RPM_PACKAGE_LICENSE "UPL-1.0 License") - SET(CPACK_RPM_PACKAGE_VENDOR "Souffle-lang") + SET(CPACK_RPM_PACKAGE_GROUP "Unspecified") + SET(CPACK_RPM_PACKAGE_LICENSE "UPL-1.0 License") + SET(CPACK_RPM_PACKAGE_VENDOR "Souffle-lang") - # Generate both DEB and RPM packages - SET(CPACK_GENERATOR "RPM") + # Generate both DEB and RPM packages + SET(CPACK_GENERATOR "RPM") - # -------------------------------------------------- - # Variables relevent to RPM packages - # -------------------------------------------------- + # -------------------------------------------------- + # Variables relevent to RPM packages + # -------------------------------------------------- - # Specifying runtime dependencies - set(CPACK_RPM_PACKAGE_REQUIRES "gcc-c++ >= 8, libffi, libffi-devel, ncurses-devel, sqlite-devel, mcpp, zlib-devel") + # Specifying runtime dependencies + set(CPACK_RPM_PACKAGE_REQUIRES "gcc-c++ >= 8, libffi, libffi-devel, ncurses-devel, sqlite-devel, mcpp, zlib-devel, python3") - # Note: By default automatic dependency detection is enabled by rpm generator. - # SET(CPACK_RPM_PACKAGE_AUTOREQPROV "no") - INCLUDE(CPack) - endif() + # Note: By default automatic dependency detection is enabled by rpm generator. + # SET(CPACK_RPM_PACKAGE_AUTOREQPROV "no") + INCLUDE(CPack) + endif() +endif() endif() diff --git a/README.md b/README.md index 6d0340b8783..aca3a690f69 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ domain-specific language for analysis problems. [![License: UPL](https://img.shields.io/badge/License-UPL--1.0-blue.svg)](https://github.com/souffle-lang/souffle/blob/master/LICENSE) [![CI-Tests](https://github.com/souffle-lang/souffle/actions/workflows/CI-Tests.yml/badge.svg)](https://github.com/souffle-lang/souffle/actions/workflows/CI-Tests.yml) +[![MSVC-CI-Tests](https://github.com/souffle-lang/souffle/actions/workflows/VS-CI-Tests.yml/badge.svg)](https://github.com/souffle-lang/souffle/actions/workflows/VS-CI-Tests.yml) [![codecov](https://codecov.io/gh/souffle-lang/souffle/branch/master/graph/badge.svg)](https://codecov.io/gh/souffle-lang/souffle) ## Features of Soufflé diff --git a/choco-packages.config b/choco-packages.config new file mode 100644 index 00000000000..aebe6bf4350 --- /dev/null +++ b/choco-packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/cmake/SouffleTests.cmake b/cmake/SouffleTests.cmake index d20cde4ff3f..983a209092f 100644 --- a/cmake/SouffleTests.cmake +++ b/cmake/SouffleTests.cmake @@ -13,14 +13,18 @@ function(SOUFFLE_SETUP_INTEGRATION_TEST_DIR) ${ARGV} ) -if ("provenance" IN_LIST TEST_LABELS) - set (PARAM_EXTRA_DATA "provenance") - endif() + if ("provenance" IN_LIST TEST_LABELS) + set (PARAM_EXTRA_DATA "provenance") + endif() - # Set up the test directory +#Set up the test directory add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_setup - COMMAND "${PROJECT_SOURCE_DIR}/cmake/setup_test_dir.sh" "${PARAM_DATA_CHECK_DIR}" "${PARAM_OUTPUT_DIR}" - "${PARAM_TEST_NAME}" "${PARAM_EXTRA_DATA}") + COMMAND ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/cmake/setup_test_dir.py" + "${PARAM_DATA_CHECK_DIR}" + "${PARAM_OUTPUT_DIR}" + "${PARAM_TEST_NAME}" + "${PARAM_EXTRA_DATA}") + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_setup PROPERTIES LABELS "${PARAM_TEST_LABELS}" FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_setup) @@ -31,34 +35,37 @@ function(SOUFFLE_RUN_INTEGRATION_TEST) cmake_parse_arguments( PARAM "" - "TEST_NAME;QUALIFIED_TEST_NAME;INPUT_DIR;OUTPUT_DIR;FIXTURE_NAME;SOUFFLE_PARAMS;NEGATIVE" - "TEST_LABELS" + "TEST_NAME;QUALIFIED_TEST_NAME;INPUT_DIR;OUTPUT_DIR;FIXTURE_NAME;NEGATIVE" + "TEST_LABELS;SOUFFLE_PARAMS" ${ARGV} ) - # Run souffle (through the shell, so we can easily redirect) - set(SOUFFLE_CMD "set -e$ '$' ${PARAM_SOUFFLE_PARAMS} '${PARAM_INPUT_DIR}/${PARAM_TEST_NAME}.dl'\\ - 1> '${PARAM_TEST_NAME}.out'\\ - 2> '${PARAM_TEST_NAME}.err'") - if ("provenance" IN_LIST TEST_LABELS) - set (SOUFFLE_CMD "${SOUFFLE_CMD} < '${PARAM_TEST_NAME}.in'") + set (STDIN_ARGS "--in" "${PARAM_TEST_NAME}.in") endif() add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_run_souffle - COMMAND sh -c "${SOUFFLE_CMD}") + COMMAND + ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/cmake/redirect.py + --out ${PARAM_TEST_NAME}.out + --err ${PARAM_TEST_NAME}.err + ${STDIN_ARGS} + $ + ${PARAM_SOUFFLE_PARAMS} + "${PARAM_INPUT_DIR}/${PARAM_TEST_NAME}.dl" + COMMAND_EXPAND_LISTS) set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_souffle PROPERTIES - # Switch to output dir so that any "extra" files generated by - # souffle are dropped in there - WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" - LABELS "${PARAM_TEST_LABELS}" - FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_run_souffle - FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_setup) + #Switch to output dir so that any "extra" files generated by + #souffle are dropped in there + WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" + LABELS "${PARAM_TEST_LABELS}" + FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_run_souffle + FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_setup) if (PARAM_NEGATIVE) - # Mark the souffle run as "will fail" for negative tests - set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_souffle PROPERTIES WILL_FAIL TRUE) + #Mark the souffle run as "will fail" for negative tests + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_souffle PROPERTIES WILL_FAIL TRUE) endif() endfunction() @@ -73,7 +80,11 @@ function(SOUFFLE_COMPARE_STD_OUTPUTS) ) add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_compare_std_outputs - COMMAND "${PROJECT_SOURCE_DIR}/cmake/check_std_outputs.sh" "${PARAM_TEST_NAME}" "${PARAM_EXTRA_DATA}") + COMMAND + ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/cmake/check_std_outputs.py" + "${PARAM_TEST_NAME}" + "${PARAM_EXTRA_DATA}") + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_compare_std_outputs PROPERTIES WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" LABELS "${PARAM_TEST_LABELS}" @@ -90,7 +101,7 @@ function(SOUFFLE_COMPARE_CSV) ) if (NOT PARAM_NEGATIVE) - # If there are "extra outputs", handle them +#If there are "extra outputs", handle them if (PARAM_EXTRA_DATA) if (PARAM_EXTRA_DATA STREQUAL "gzip") set(EXTRA_BINARY "${GZIP_BINARY}") @@ -104,29 +115,31 @@ function(SOUFFLE_COMPARE_CSV) endif() add_test(NAME ${QUALIFIED_TEST_NAME}_compare_csv - COMMAND "${PROJECT_SOURCE_DIR}/cmake/check_test_results.sh" - "${PARAM_INPUT_DIR}" ${PARAM_EXTRA_DATA} "${EXTRA_BINARY}") + COMMAND ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/cmake/check_test_results.py" + "${PARAM_INPUT_DIR}" + ${PARAM_EXTRA_DATA} + "${EXTRA_BINARY}") set_tests_properties(${QUALIFIED_TEST_NAME}_compare_csv PROPERTIES - WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" - LABELS "${PARAM_TEST_LABELS}" - FIXTURES_REQUIRED ${PARAM_RUN_AFTER_FIXTURE}) + WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" + LABELS "${PARAM_TEST_LABELS}" + FIXTURES_REQUIRED ${PARAM_RUN_AFTER_FIXTURE}) endif() endfunction() function(SOUFFLE_RUN_TEST_HELPER) - # PARAM_CATEGORY - e.g. syntactic, example etc. - # PARAM_TEST_NAME - the name of the test, the short directory name under tests// - # PARAM_COMPILED - with or without -c - # PARAM_FUNCTORS - with -L for finding functor library in the testsuite - # PARAM_NEGATIVE - should it fail or not - # PARAM_MULTI_TEST - used to distinguish "multi-tests", sort of left over from automake - # Basically, the same test dir has multiple sets of facts/outputs - # We should just get rid of this and make multiple tests - # It also means we need to use slightly different naming for tests - # and input paths - # PARAM_FACTS_DIR_NAME - the name of the "facts" subdirectory in each test. - # Usually just "facts" but can be different when running multi-tests +#PARAM_CATEGORY - e.g.syntactic, example etc. +#PARAM_TEST_NAME - the name of the test, the short directory name under tests / / +#PARAM_COMPILED - with or without -c +#PARAM_FUNCTORS - with -L for finding functor library in the testsuite +#PARAM_NEGATIVE - should it fail or not +#PARAM_MULTI_TEST - used to distinguish "multi-tests", sort of left over from automake +#Basically, the same test dir has multiple sets of facts / outputs +#We should just get rid of this and make multiple tests +#It also means we need to use slightly different naming for tests +#and input paths +#PARAM_FACTS_DIR_NAME - the name of the "facts" subdirectory in each test. +#Usually just "facts" but can be different when running multi - tests cmake_parse_arguments( PARAM "COMPILED;FUNCTORS;NEGATIVE;MULTI_TEST" # Options @@ -135,8 +148,10 @@ function(SOUFFLE_RUN_TEST_HELPER) ${ARGV} ) + set(EXTRA_FLAGS) + if (PARAM_COMPILED) - set(EXTRA_FLAGS "-c") + list(APPEND EXTRA_FLAGS "-c") set(EXEC_STYLE "compiled") set(SHORT_EXEC_STYLE "_c") else() @@ -144,8 +159,16 @@ function(SOUFFLE_RUN_TEST_HELPER) set(SHORT_EXEC_STYLE "") endif() + if (MSVC) + list(APPEND EXTRA_FLAGS "--preprocessor" "cl -nologo -TC -E") + endif() + if (PARAM_FUNCTORS) - set(EXTRA_FLAGS "${EXTRA_FLAGS} '-L${CMAKE_CURRENT_BINARY_DIR}/${PARAM_TEST_NAME}'") + #if (MSVC) + # list(APPEND EXTRA_FLAGS "-L${CMAKE_CURRENT_BINARY_DIR}/${PARAM_TEST_NAME}/${CMAKE_BUILD_TYPE}") + #else () + list(APPEND EXTRA_FLAGS "-L${CMAKE_CURRENT_BINARY_DIR}/${PARAM_TEST_NAME}") + #endif () endif() if (NOT PARAM_FACTS_DIR_NAME) @@ -164,8 +187,8 @@ function(SOUFFLE_RUN_TEST_HELPER) endif() set(OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/${PARAM_TEST_NAME}${MT_EXTRA_SUFFIX}_${EXEC_STYLE}") - # Give the test a name which has good info about it when running - # People can then search for the test by the name, or the labels we create +#Give the test a name which has good info about it when running +#People can then search for the test by the name, or the labels we create set(QUALIFIED_TEST_NAME ${PARAM_CATEGORY}/${PARAM_TEST_NAME}${MT_EXTRA_SUFFIX}${SHORT_EXEC_STYLE}) set(FIXTURE_NAME ${QUALIFIED_TEST_NAME}_fixture) @@ -175,7 +198,7 @@ function(SOUFFLE_RUN_TEST_HELPER) set(POS_LABEL "positive") endif() - # Label the tests as e.g. "semantic", compiled/interpreted, positive/negative, and integration +#Label the tests as e.g."semantic", compiled / interpreted, positive / negative, and integration set(TEST_LABELS "${PARAM_CATEGORY};${EXEC_STYLE};${POS_LABEL};integration") souffle_setup_integration_test_dir(TEST_NAME ${PARAM_TEST_NAME} @@ -186,10 +209,11 @@ function(SOUFFLE_RUN_TEST_HELPER) FIXTURE_NAME ${FIXTURE_NAME} TEST_LABELS ${TEST_LABELS}) + set(SOUFFLE_PARAMS "-D" "." "-F" "${FACTS_DIR}") + list(PREPEND SOUFFLE_PARAMS ${EXTRA_FLAGS}) + if (OPENMP_FOUND) - set(SOUFFLE_PARAMS "${EXTRA_FLAGS} -j8 -D . -F '${FACTS_DIR}'") - else() - set(SOUFFLE_PARAMS "${EXTRA_FLAGS} -D . -F '${FACTS_DIR}'") + list(APPEND SOUFFLE_PARAMS "-j8") endif() souffle_run_integration_test(TEST_NAME ${PARAM_TEST_NAME} @@ -242,21 +266,32 @@ function(SOUFFLE_ADD_BINARY_TEST TEST_NAME CATEGORY) set(TARGET_NAME "test_${SHORT_TEST_NAME}") add_executable(${TARGET_NAME} ${TEST_NAME}.cpp) + set(CMAKE_CXX_STANDARD 17) if (PARAM_SOUFFLE_HEADERS_ONLY) get_target_property(SOUFFLE_COMPILE_EXTS libsouffle CXX_EXTENSIONS) get_target_property(SOUFFLE_COMPILE_DEFS libsouffle INTERFACE_COMPILE_DEFINITIONS) get_target_property(SOUFFLE_COMPILE_FEAT libsouffle INTERFACE_COMPILE_FEATURES) - get_target_property(SOUFFLE_COMPILE_OPTS libsouffle INTERFACE_COMPILE_OPTIONS) + get_target_property(SOUFFLE_COMPILE_OPTS libsouffle COMPILE_OPTIONS) get_target_property(SOUFFLE_INCLUDE_DIRS libsouffle INTERFACE_INCLUDE_DIRECTORIES) + if (OPENMP_FOUND) + target_link_libraries(${TARGET_NAME} PRIVATE OpenMP::OpenMP_CXX) + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + target_link_libraries(${TARGET_NAME} PRIVATE stdc++fs) + endif () + endif() + set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS SOUFFLE_COMPILE_EXTS) target_compile_definitions(${TARGET_NAME} PRIVATE ${SOUFFLE_COMPILE_DEFS}) target_compile_features(${TARGET_NAME} PRIVATE ${SOUFFLE_COMPILE_FEAT}) target_compile_options(${TARGET_NAME} PRIVATE ${SOUFFLE_COMPILE_OPTS}) target_include_directories(${TARGET_NAME} PRIVATE ${SOUFFLE_INCLUDE_DIRS}) else() - target_link_libraries(${TARGET_NAME} libsouffle) + target_link_libraries(${TARGET_NAME} libsouffle) endif() set(QUALIFIED_TEST_NAME ${SHORT_TEST_NAME}) diff --git a/cmake/check_std_outputs.py b/cmake/check_std_outputs.py new file mode 100644 index 00000000000..219926199ce --- /dev/null +++ b/cmake/check_std_outputs.py @@ -0,0 +1,35 @@ +# Souffle - A Datalog Compiler +# Copyright (c) 2022 The Souffle Developers. All rights reserved +# Licensed under the Universal Permissive License v 1.0 as shown at: +# - https://opensource.org/licenses/UPL +# - /licenses/SOUFFLE-UPL.txt + +import os +from common import * + +args = os.sys.argv +args.pop(0) + +if len(args) == 0: + raise RuntimeError("Missing TEST_NAME") + +test_name = args.pop(0) + +if len(args) > 0: + extra_data = args.pop(0) +else: + extra_data = None + +if len(args) != 0: + raise RuntimeError("Unexpected argument") + +sort_file("{}.out".format(test_name)) +compare_sorted_file("{}.out".format(test_name)) + +compare_file("{}.err".format(test_name)) + +if extra_data == "python" or extra_data == "java": + file_name = "{}-{}.out".format(test_name, extra_data) + sort_file(file_name) + compare_sorted_file(file_name) + diff --git a/cmake/check_std_outputs.sh b/cmake/check_std_outputs.sh deleted file mode 100755 index d03e4534ce6..00000000000 --- a/cmake/check_std_outputs.sh +++ /dev/null @@ -1,31 +0,0 @@ -# Souffle - A Datalog Compiler -# Copyright (c) 2021 The Souffle Developers. All rights reserved -# Licensed under the Universal Permissive License v 1.0 as shown at: -# - https://opensource.org/licenses/UPL -# - /licenses/SOUFFLE-UPL.txt - -set -e - -TEST_NAME="$1" -EXTRA_DATA="$2" - -if [ -z ${EXTRA_DATA} ]; then - EXTRA_DATA="nothing" -fi - -if [ ${EXTRA_DATA} = "json" ]; then - # The json tests require additional shenanigans - sed 's/\(@<:@@:>@,@:>@\)$/\\1/' "${TEST_NAME}.out" | sort > "${TEST_NAME}.out.sorted" -else - sort "${TEST_NAME}.out" > "${TEST_NAME}.out.sorted" -fi; - -diff "${TEST_NAME}.out.sorted" "${TEST_NAME}.out.expected.sorted" - -diff "${TEST_NAME}.err" "${TEST_NAME}.err.expected" - -if [ ${EXTRA_DATA} = "python" ] || [ ${EXTRA_DATA} = "java" ]; then - # We also want to check the output from the swig run - sort "${TEST_NAME}-${EXTRA_DATA}.out" > "${TEST_NAME}-${EXTRA_DATA}.out.sorted" - diff "${TEST_NAME}-${EXTRA_DATA}.out.sorted" "${TEST_NAME}-${EXTRA_DATA}.out.expected.sorted" -fi diff --git a/cmake/check_test_results.py b/cmake/check_test_results.py new file mode 100644 index 00000000000..20ccd4f8a32 --- /dev/null +++ b/cmake/check_test_results.py @@ -0,0 +1,83 @@ +# Souffle - A Datalog Compiler +# Copyright (c) 2022 The Souffle Developers. All rights reserved +# Licensed under the Universal Permissive License v 1.0 as shown at: +# - https://opensource.org/licenses/UPL +# - /licenses/SOUFFLE-UPL.txt + +import os +import glob +import subprocess +from common import * + +args = os.sys.argv +args.pop(0) + +if len(args) == 0: + raise RuntimeError("Missing INPUT_DIR") + +input_dir = args.pop(0) +extra_data = None +binary = None + +if len(args) > 0: + arg = args.pop(0) + if arg == "": + extra_data = None + elif arg == "sqlite3" or arg == "gzip": + extra_data = arg + if len(args) == 0: + raise RuntimeError("Missing BINARY") + binary = args.pop(0) + elif arg == "json": + extra_data = arg + if len(args) > 0: + args.pop(0) # unused last argument + else: + raise RuntimeError("Unknown processing type {}".format(arg)) + +if len(args) != 0: + raise RuntimeError("Unexpected argument") + +if extra_data == "gzip": + extra_file_pattern = "*.gz.output" +elif extra_data == "sqlite3": + extra_file_pattern = "*.sqlite.output" +elif extra_data == "json": + extra_file_pattern = "*.json" +else: + extra_file_pattern = None + +if extra_file_pattern: + extra_files = glob.glob(extra_file_pattern) + + for file in extra_files: + if extra_data == "gzip": + generated_file = os.path.basename(file).rstrip(".gz.output") + with open(generated_file, "w") as generated: + subprocess.run([binary, "-d", "-c", file], check=True, stdout=generated) + elif extra_data == "sqlite3": + generated_file = "{}.csv".format(os.path.basename(file).rstrip(".sqlite.output")) + script = os.path.join(input_dir, "{}.script".format(file)) + with open(generated_file, "w") as generated: + subprocess.run([binary,"-batch","-init",script,file,""], check=True, stdout=generated) + elif extra_data == "json": + generated_file = file + + sort_file(generated_file) + compare_sorted_file(generated_file) + +generated_csvs = glob.glob("*.csv") + +with open("num.generated", "w") as num_file: + num_file.write("{}\n".format(len(generated_csvs))) + +compare_files("num.expected", "num.generated") + +csvs = list(map( + lambda file: os.path.basename(file).rstrip(".expected.sorted"), + glob.glob("*.csv.expected.sorted"))) + +for csv_file in csvs: + sort_file(csv_file) + compare_sorted_file(csv_file) + diff --git a/cmake/check_test_results.sh b/cmake/check_test_results.sh deleted file mode 100755 index 470a6f5b83a..00000000000 --- a/cmake/check_test_results.sh +++ /dev/null @@ -1,57 +0,0 @@ -# Souffle - A Datalog Compiler -# Copyright (c) 2021 The Souffle Developers. All rights reserved -# Licensed under the Universal Permissive License v 1.0 as shown at: -# - https://opensource.org/licenses/UPL -# - /licenses/SOUFFLE-UPL.txt - -set -e - -INPUT_DIR="$1" -EXTRA_DATA="$2" -BINARY="$3" #BINARY will be set to gzip or sqlite3 - -find . -maxdepth 1 -name "*.csv.expected.sorted" | wc -l > num.generated - -diff num.generated num.expected - -for file in $(find . -maxdepth 1 -name "*.csv"); do - sort "${file}" > "${file}.generated.sorted" - diff "${file}.generated.sorted" "${file}.expected.sorted" -done - - -if [ -z ${EXTRA_DATA} ]; then - # We're done - exit 0 -elif [ ${EXTRA_DATA} = "gzip" ]; then - EXTRA_FILE_PAT="*.output" -elif [ ${EXTRA_DATA} = "sqlite3" ]; then - EXTRA_FILE_PAT="*.output" -elif [ ${EXTRA_DATA} = "json" ]; then - EXTRA_FILE_PAT="*.json" -else - echo "Unknown processing type '${EXTRA_DATA}'" - exit 1 -fi - -EXTRA_FILES=$(find . -maxdepth 1 -name "${EXTRA_FILE_PAT}") - -for file in ${EXTRA_FILES}; do - if [ ${EXTRA_DATA} = "gzip" ]; then - # Strip the .gz.output suffix. There should be a corresponding .csv file - # in the input directory - EXTRA_FILE=${file%%.gz.output} - "${BINARY}" -d -c "$file" > "${EXTRA_FILE}.generated.unsorted" - elif [ ${EXTRA_DATA} = "sqlite3" ]; then - # Strip the .sqlite.output suffix. There should be a corresponding .csv file - # in the input directory - EXTRA_FILE=${file%%.sqlite.output}.csv - "${BINARY}" -batch "$file" -init "${INPUT_DIR}/$file.script" "" > "${EXTRA_FILE}.generated.unsorted" - elif [ ${EXTRA_DATA} = "json" ]; then - EXTRA_FILE="${file}" - sed 's/\(@<:@@:>@,@:>@\)$/\\1/' "${file}" > "${file}.generated.unsorted" - fi - - sort "${EXTRA_FILE}.generated.unsorted" > "${EXTRA_FILE}.generated.sorted" - diff "${EXTRA_FILE}.generated.sorted" "${EXTRA_FILE}.expected.sorted" -done diff --git a/cmake/common.py b/cmake/common.py new file mode 100644 index 00000000000..9e999d04c68 --- /dev/null +++ b/cmake/common.py @@ -0,0 +1,65 @@ +# Souffle - A Datalog Compiler +# Copyright (c) 2022 The Souffle Developers. All rights reserved +# Licensed under the Universal Permissive License v 1.0 as shown at: +# - https://opensource.org/licenses/UPL +# - /licenses/SOUFFLE-UPL.txt + +import difflib +import re +import os + +## sort file `file_name` to file `file_name.sorted` +def sort_file(file_name): + if not os.path.isfile(file_name): + raise RuntimeError("Missing file '{}'".format(file_name)) + + output_file = "{}.sorted".format(file_name) + + with open(file_name) as f: + digits = re.compile(r'^\d+$') + def prepare_row(row): + return list(map( + # natural sort, transfor integers to string representation with leading zeroes + lambda field: (("{:0>20d}".format(int(field))) if digits.match(field) else field), + row.split("\t"))) + + rows = sorted([row.rstrip() for row in f], key=lambda row: prepare_row(row)) + + with open(output_file, "w") as f: + f.write("\n".join(rows)) + + return None + +## Compare two given files. Exit with error status if files do not match. +def compare_files(expected_file, actual_file): + if not os.path.isfile(expected_file): + raise RuntimeError("Missing file '{}'".format(expected_file)) + if not os.path.isfile(actual_file): + raise RuntimeError("Missing file '{}'".format(actual_file)) + + with open(expected_file) as f: + expected_lines = [line.rstrip() for line in f] + + with open(actual_file) as f: + actual_lines = [line.rstrip() for line in f] + + if actual_lines != expected_lines: + os.sys.stdout.writelines(difflib.unified_diff(open(expected_file).readlines(), open(actual_file).readlines(), fromfile=expected_file, tofile=actual_file)) + os.sys.exit("Found output difference, expected file:'{}', actual file:'{}".format(expected_file, actual_file)) + + return True + +## Compare `file_name` with `file_name.expected`. Exit with error status if files do not match. +def compare_file(file_name): + actual_file = file_name + expected_file = "{}.expected".format(file_name) + + return compare_files(expected_file, actual_file) + +## Compare `file_name.sorted` with `file_name.expected.sorted`. Exit with error status if files do not match. +def compare_sorted_file(file_name): + actual_file = "{}.sorted".format(file_name) + expected_file = "{}.expected.sorted".format(file_name) + + return compare_files(expected_file, actual_file) + diff --git a/cmake/redirect.py b/cmake/redirect.py new file mode 100644 index 00000000000..d1b703e61a3 --- /dev/null +++ b/cmake/redirect.py @@ -0,0 +1,44 @@ +# Souffle - A Datalog Compiler +# Copyright (c) 2022 The Souffle Developers. All rights reserved +# Licensed under the Universal Permissive License v 1.0 as shown at: +# - https://opensource.org/licenses/UPL +# - /licenses/SOUFFLE-UPL.txt + +import os +import argparse +import pathlib +import subprocess + +parser = argparse.ArgumentParser(description="Redirect standard streams") +parser.add_argument('--in', dest='in_file') +parser.add_argument('--out', dest='out_file') +parser.add_argument('--err', dest='err_file') +parser.add_argument('command', type=lambda p: pathlib.Path(p).absolute()) +parser.add_argument('arguments', nargs=argparse.REMAINDER) + +args = parser.parse_args() + +if args.in_file: + stdin = open(args.in_file) +else: + stdin = None + +if args.out_file: + stdout = open(args.out_file, "w") +else: + stdout = None + +if args.err_file: + stderr = open(args.err_file, "w") +else: + stderr = None + +status = subprocess.run([args.command] + args.arguments, stdin=stdin, stdout=stdout, stderr=stderr) + +if stdout: + stdout.close() + +if stderr: + stderr.close() + +os.sys.exit(status.returncode) diff --git a/cmake/setup_test_dir.py b/cmake/setup_test_dir.py new file mode 100644 index 00000000000..7a2fb1b300c --- /dev/null +++ b/cmake/setup_test_dir.py @@ -0,0 +1,88 @@ +# Souffle - A Datalog Compiler +# Copyright (c) 2022 The Souffle Developers. All rights reserved +# Licensed under the Universal Permissive License v 1.0 as shown at: +# - https://opensource.org/licenses/UPL +# - /licenses/SOUFFLE-UPL.txt + +import glob +import os +import shutil +from common import * + +args = os.sys.argv +args.pop(0) + +if len(args) == 0: + raise RuntimeError("Missing INPUT_DIR") +input_dir = args.pop(0) + +if len(args) == 0: + raise RuntimeError("Missing OUTPUT_DIR") +output_dir = args.pop(0) + +if len(args) == 0: + raise RuntimeError("Missing TEST_NAME") +test_name = args.pop(0) + +if len(args) > 0: + extra_data = args.pop(0) +else: + extra_data = None + +if len(args) != 0: + raise RuntimeError("Unexpected argument") + +if os.path.exists(output_dir) and (not os.path.isdir(output_dir)): + raise RuntimeError("Output path exists but is not a directory") + +# clean output directory +if os.path.isdir(output_dir): + for file in os.listdir(output_dir): + path = os.path.join(output_dir, file) + if os.path.isdir(path): + shutil.rmtree(path) # only remove directories + else: + os.remove(path) # only remove files +else: + os.makedirs(output_dir, exist_ok=True) + +if extra_data == "json": + for file in [os.path.basename(p) for p in glob.glob(os.path.join(input_dir,"*.json"))]: + shutil.copyfile( + os.path.join(input_dir, file), + os.path.join(output_dir, "{}.expected".format(file))) +elif extra_data == "python" or extra_data == "java": + shutil.copyfile( + os.path.join(input_dir, "{}-{}.out".format(test_name, extra_data)), + os.path.join(output_dir, "{}-{}.out.expected".format(test_name, extra_data))) +elif extra_data == "provenance": + shutil.copyfile( + os.path.join(input_dir, "{}.in".format(test_name)), + os.path.join(output_dir, "{}.in".format(test_name))) + +shutil.copyfile( + os.path.join(input_dir, "{}.out".format(test_name)), + os.path.join(output_dir, "{}.out.expected".format(test_name))) + +shutil.copyfile( + os.path.join(input_dir, "{}.err".format(test_name)), + os.path.join(output_dir, "{}.err.expected".format(test_name))) + +csvs = [os.path.basename(p) for p in glob.glob(os.path.join(input_dir,"*.csv"))] + +with open(os.path.join(output_dir, "num.expected"), "w") as num_file: + num_file.write("{}\n".format(len(csvs))) + +for file in csvs: + shutil.copyfile( + os.path.join(input_dir, file), + os.path.join(output_dir, "{}.expected".format(file))) + +owd = os.getcwd() +try: + os.chdir(output_dir) + for file in glob.glob("*.expected"): + sort_file(file) +finally: + os.chdir(owd) + diff --git a/cmake/setup_test_dir.sh b/cmake/setup_test_dir.sh deleted file mode 100755 index bf592fd8c70..00000000000 --- a/cmake/setup_test_dir.sh +++ /dev/null @@ -1,50 +0,0 @@ -# Souffle - A Datalog Compiler -# Copyright (c) 2021 The Souffle Developers. All rights reserved -# Licensed under the Universal Permissive License v 1.0 as shown at: -# - https://opensource.org/licenses/UPL -# - /licenses/SOUFFLE-UPL.txt - -set -e - -INPUT_DIR="$1" -OUTPUT_DIR="$2" -TEST_NAME="$3" -EXTRA_DATA="$4" - -mkdir -p "${OUTPUT_DIR}" -rm -rf "${OUTPUT_DIR}"/* - -if [ -z ${EXTRA_DATA} ]; then - EXTRA_DATA="nothing" -fi - -if [ ${EXTRA_DATA} = "json" ]; then - # The json tests require additional shenanigans - sed 's/\(@<:@@:>@,@:>@\)$/\\1/' "${INPUT_DIR}/${TEST_NAME}.out" > "${OUTPUT_DIR}/${TEST_NAME}.out.expected" - - for file in $(cd "${INPUT_DIR}" && find . -maxdepth 1 -name "*.json"); do - sed 's/\(@<:@@:>@,@:>@\)$/\\1/' "${INPUT_DIR}/${file}" > "${OUTPUT_DIR}/${file}.expected" - done -else - cp "${INPUT_DIR}/${TEST_NAME}.out" "${OUTPUT_DIR}/${TEST_NAME}.out.expected" - - if [ ${EXTRA_DATA} = "python" ] || [ ${EXTRA_DATA} = "java" ]; then - cp "${INPUT_DIR}/${TEST_NAME}-${EXTRA_DATA}.out" "${OUTPUT_DIR}/${TEST_NAME}-${EXTRA_DATA}.out.expected" - fi - - if [ ${EXTRA_DATA} = "provenance" ]; then - cp "${INPUT_DIR}/${TEST_NAME}.in" "${OUTPUT_DIR}/${TEST_NAME}.in" - fi -fi - -cp "${INPUT_DIR}/${TEST_NAME}".err "${OUTPUT_DIR}/${TEST_NAME}".err.expected - -for file in $(cd "${INPUT_DIR}" && find . -maxdepth 1 -name "*.csv"); do - cp "${INPUT_DIR}/$file" "${OUTPUT_DIR}/${file}.expected" -done - -find "${INPUT_DIR}" -maxdepth 1 -name "*.csv" | wc -l > "${OUTPUT_DIR}/num.expected" - -for file in $(cd "${OUTPUT_DIR}" && find . -maxdepth 1 -name "*.expected"); do - sort "${OUTPUT_DIR}/$file" > "${OUTPUT_DIR}/${file}.sorted" -done diff --git a/sh/setup/install_ubuntu_deps.sh b/sh/setup/install_ubuntu_deps.sh index 4bba4a6b557..68bd1a95b87 100755 --- a/sh/setup/install_ubuntu_deps.sh +++ b/sh/setup/install_ubuntu_deps.sh @@ -3,4 +3,4 @@ # Install Ubuntu dependencies on a Github Action VM apt-get update -q -apt-get install -y -q bash-completion bison build-essential clang debhelper default-jdk-headless devscripts doxygen fakeroot flex g++ gdb git graphviz libffi-dev libncurses5-dev libsqlite3-dev libtool make mcpp pkg-config python3-dev sqlite swig zlib1g-dev cmake +apt-get install -y -q bash-completion bison build-essential clang debhelper default-jdk-headless devscripts doxygen fakeroot flex g++ gdb git graphviz libffi-dev libncurses5-dev libsqlite3-dev libtool make mcpp pkg-config python3-dev sqlite swig zlib1g-dev cmake ruby diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fa0cc7625b..583e2988179 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ # - https://opensource.org/licenses/UPL # - /licenses/SOUFFLE-UPL.txt + set(SOUFFLE_SOURCES FunctorOps.cpp Global.cpp @@ -160,14 +161,25 @@ set(SOUFFLE_SOURCES # Flex/Bison # -------------------------------------------------- file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/parser") -flex_target(scanner parser/scanner.ll ${CMAKE_CURRENT_BINARY_DIR}/parser/scanner.cc) + +flex_target(scanner parser/scanner.ll ${CMAKE_CURRENT_BINARY_DIR}/parser/scanner.cc + COMPILE_FLAGS "${SCANNER_COMPILE_FLAGS} -d") + bison_target(parser parser/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc COMPILE_FLAGS "-Wall -Werror -Wno-error=deprecated -Wno-error=other -v -d") add_flex_bison_dependency(scanner parser) -# OSX compiler doesn't recognise `(void)var;` ideom -set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/scanner.cc PROPERTIES + +if (MSVC) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/scanner.cc PROPERTIES + COMPILE_FLAGS "/wd4005 /wd4996") + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc PROPERTIES + COMPILE_FLAGS "/wd4005 /wd26819") +else () + # OSX compiler doesn't recognise `(void)var;` ideom + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/scanner.cc PROPERTIES COMPILE_FLAGS "-Wno-error=unused-parameter") +endif () # -------------------------------------------------- # Souffle library @@ -190,57 +202,92 @@ target_include_directories(libsouffle # install souffle directory install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/souffle DESTINATION include) +add_library(compiled STATIC dummy.cpp) + +target_include_directories(compiled PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") + # Set C++ standard to C++17 -target_compile_features(libsouffle - PUBLIC cxx_std_17) +target_compile_features(libsouffle PUBLIC cxx_std_17) +target_compile_features(compiled PUBLIC cxx_std_17) + set_target_properties(libsouffle PROPERTIES CXX_EXTENSIONS OFF) +set_target_properties(compiled PROPERTIES CXX_EXTENSIONS OFF) + +if (NOT MSVC) + target_compile_options(libsouffle PUBLIC -Wall -Wextra -Werror -fwrapv) +else () + target_compile_options(libsouffle PUBLIC /W3 /WX) +endif () -target_compile_options(libsouffle - PUBLIC "-Wall;-Wextra;-Werror;-fwrapv") +target_compile_options(compiled PUBLIC "") + +if (Threads_FOUND) + target_link_libraries(libsouffle PUBLIC Threads::Threads) + target_link_libraries(compiled PUBLIC Threads::Threads) +endif () + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + target_link_libraries(libsouffle PUBLIC stdc++fs) + target_link_libraries(compiled PUBLIC stdc++fs) + endif () +endif () -target_link_libraries(libsouffle PRIVATE Threads::Threads) if(SOUFFLE_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_link_libraries(libsouffle PUBLIC coverage_config) -endif(SOUFFLE_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") +endif () -# FIXME: Better to have a library list -if (OPENMP_FOUND) - target_link_libraries(libsouffle - PUBLIC OpenMP::OpenMP_CXX - $<$:ZLIB::ZLIB> - $<$:SQLite::SQLite3> - $<$:Curses::NCurses> - $<$:LibFFI::LibFFI> - "${CMAKE_DL_LIBS}") -else() - target_link_libraries(libsouffle - PUBLIC $<$:ZLIB::ZLIB> - $<$:SQLite::SQLite3> - $<$:Curses::NCurses> - $<$:LibFFI::LibFFI> - "${CMAKE_DL_LIBS}") -endif() + +target_link_libraries(libsouffle PUBLIC "${CMAKE_DL_LIBS}") +target_link_libraries(compiled PUBLIC "${CMAKE_DL_LIBS}") if (SOUFFLE_DOMAIN_64BIT) + target_compile_definitions(libsouffle PUBLIC RAM_DOMAIN_SIZE=64) + target_compile_definitions(compiled PUBLIC RAM_DOMAIN_SIZE=64) +endif() + +if (SOUFFLE_USE_LIBFFI) +if (libffi_FOUND) + target_link_libraries(libsouffle PUBLIC libffi) +else () + target_link_libraries(libsouffle PUBLIC LibFFI::LibFFI) +endif () +endif () + +if (OPENMP_FOUND) + target_link_libraries(libsouffle PUBLIC OpenMP::OpenMP_CXX) + target_link_libraries(compiled PUBLIC OpenMP::OpenMP_CXX) +endif() + +if (SOUFFLE_CUSTOM_GETOPTLONG) target_compile_definitions(libsouffle - PUBLIC RAM_DOMAIN_SIZE=64) + PUBLIC USE_CUSTOM_GETOPTLONG) + target_compile_definitions(compiled + PUBLIC USE_CUSTOM_GETOPTLONG) endif() if (SOUFFLE_USE_CURSES) - target_compile_definitions(libsouffle - PUBLIC USE_NCURSES) + target_compile_definitions(libsouffle PUBLIC USE_NCURSES) + target_compile_definitions(compiled PUBLIC USE_NCURSES) + target_link_libraries(libsouffle PUBLIC Curses::NCurses) + target_link_libraries(compiled PUBLIC Curses::NCurses) endif() if (SOUFFLE_USE_ZLIB) - target_compile_definitions(libsouffle - PUBLIC USE_LIBZ) + target_compile_definitions(libsouffle PUBLIC USE_LIBZ) + target_compile_definitions(compiled PUBLIC USE_LIBZ) + target_link_libraries(libsouffle PUBLIC ZLIB::ZLIB) + target_link_libraries(compiled PUBLIC ZLIB::ZLIB) endif() if (SOUFFLE_USE_SQLITE) - target_compile_definitions(libsouffle - PUBLIC USE_SQLITE) + target_compile_definitions(libsouffle PUBLIC USE_SQLITE) + target_compile_definitions(compiled PUBLIC USE_SQLITE) + target_link_libraries(libsouffle PUBLIC SQLite::SQLite3) + target_link_libraries(compiled PUBLIC SQLite::SQLite3) + target_include_directories(compiled PUBLIC ${SQLite3_INCLUDE_DIRS}) endif() if (SOUFFLE_USE_LIBFFI) @@ -263,84 +310,231 @@ add_executable(souffle target_link_libraries(souffle libsouffle) install(TARGETS souffle DESTINATION bin) +# Copy the dlls in the same directory as Souffle so that they will +# be immediately found by the operating system. + +if (SOUFFLE_USE_SQLITE) +add_custom_command(TARGET souffle POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${SQLite3_LIBRARY} ${ZLIB_LIBRARY_RELEASE} + $) +endif () + +if (SOUFFLE_USE_ZLIB) +add_custom_command(TARGET souffle POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${SQLite3_LIBRARY} ${ZLIB_LIBRARY_RELEASE} + $) +endif () + +if (SOUFFLE_USE_CURSES) +add_custom_command(TARGET souffle POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CURSES_NCURSES_LIBRARY} + $) +endif () + # -------------------------------------------------- # Souffle's profiler binary # -------------------------------------------------- +if (NOT WIN32) + add_executable(souffleprof + souffle_prof.cpp) + target_include_directories(souffleprof PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) + install(TARGETS souffleprof DESTINATION bin) + + # Set C++ standard to C++17 + target_compile_features(souffleprof + PUBLIC cxx_std_17) +endif() + +if (MSVC) + target_compile_options(libsouffle PUBLIC /bigobj) + target_compile_options(compiled PUBLIC /bigobj) + + target_compile_options(libsouffle PUBLIC /wd5105) + target_compile_options(compiled PUBLIC /wd5105) + + target_compile_options(libsouffle PUBLIC /wd6326) + target_compile_options(compiled PUBLIC /wd6326) + + target_compile_options(libsouffle PUBLIC /wd26110) + target_compile_options(libsouffle PUBLIC /wd4065 /wd4200) + + # to prevent old versions of Flex from redeclaring int types + target_compile_definitions(libsouffle PRIVATE __STDC_LIMIT_MACROS) + + target_compile_definitions(libsouffle PUBLIC _CRT_SECURE_NO_WARNINGS) + + if (NOT (MSVC_VERSION LESS 1910)) + target_compile_options(libsouffle PUBLIC /permissive-) + target_compile_options(compiled PUBLIC /permissive-) + endif() + + target_compile_options(libsouffle PUBLIC /Zc:preprocessor) + target_compile_options(compiled PUBLIC /Zc:preprocessor) + + target_compile_options(libsouffle PUBLIC /EHsc) + target_compile_options(compiled PUBLIC /EHsc) + + target_compile_definitions(libsouffle PUBLIC USE_CUSTOM_GETOPTLONG) + target_compile_definitions(compiled PUBLIC USE_CUSTOM_GETOPTLONG) +endif (MSVC) -add_executable(souffleprof - souffle_prof.cpp) -target_include_directories(souffleprof PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) -install(TARGETS souffleprof DESTINATION bin) -# Set C++ standard to C++17 -target_compile_features(souffleprof - PUBLIC cxx_std_17) # -------------------------------------------------- -# Substitutions for souffle-compile +# Substitutions for souffle-compile.py # -------------------------------------------------- -# FIXME: This is a bit fragile but there is actually no better way -# to do this, AFAIK -# Maybe we could make a cmake script instead of using shell? -function(AUTOMAKE_COMPATIBLE_SUBST F_IN F_OUT) - # enclose in scope so we can create the - # values to substitute - - set(CXX ${CMAKE_CXX_COMPILER}) - - set(CMAKE_HEADER_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include") - - set(CPPFLAGS "") - - # Compile definitions - get_target_property(COMP_DEFS libsouffle COMPILE_DEFINITIONS) - set(SUBST_DEFS "") - foreach(DEF ${COMP_DEFS}) - string(APPEND SUBST_DEFS "-D${DEF} ") - endforeach() - - # Compile flags - string(TOUPPER ${CMAKE_BUILD_TYPE} BT_UPPER) - # Remove -Werror from the default flag set for compilation using `souffle-compile`. - # *) `-d` option in `souffle-compile` adds this flag back - # *) The compiler used by `souffle-compile` isn't neccesarily the same as the one used by `cmake`. - # Code that's free of warnings w/ the cmake compiler might not be w/ `souffle-compile`'s compiler. - # e.g. we might use clang-10, user might use gcc or some newer version of clang. - # *) Synth'd code isn't warning free for now anyways. - # TODO: Require/verify that test case synthesised code is warning free as part of test suite. - string(REGEX REPLACE "-Werror" "" SOUFFLE_CXXFLAGS "${CMAKE_CXX_FLAGS}") - set(SOUFFLE_CXXFLAGS "${SOUFFLE_CXXFLAGS} ${CMAKE_CXX_FLAGS_${BT_UPPER}} ${SUBST_DEFS}") - if (OPENMP_FOUND) - set(SOUFFLE_CXXFLAGS "${SOUFFLE_CXXFLAGS} -fopenmp") - endif() - - # Libraries - set(LIBS "-lpthread") - - if (CMAKE_DL_LIBS) - string(APPEND LIBS " -l${CMAKE_DL_LIBS}") - endif() - - if (SOUFFLE_USE_SQLITE) - string(APPEND LIBS " ${SQLite3_LIBRARY}") - endif() - - if (SOUFFLE_USE_ZLIB) - string(APPEND LIBS " ${ZLIB_LIBRARY_RELEASE}") - endif() - - if (SOUFFLE_USE_CURSES) - string(APPEND LIBS " ${CURSES_NCURSES_LIBRARY}") - endif() - - configure_file("${F_IN}" "${F_OUT}" @ONLY) -endfunction() - -automake_compatible_subst("${CMAKE_CURRENT_SOURCE_DIR}/souffle-compile.in" - "${CMAKE_CURRENT_BINARY_DIR}/souffle-compile") - -install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/souffle-compile DESTINATION bin) +set(SOUFFLE_COMPILED_CXX_COMPILER ${CMAKE_CXX_COMPILER}) +set(SOUFFLE_COMPILED_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}) +set(SOUFFLE_COMPILED_CXX_COMPILER_VERSION ${CMAKE_CXX_COMPILER_VERSION}) +set(SOUFFLE_COMPILED_CXX_STANDARD ${CMAKE_CXX17_STANDARD_COMPILE_OPTION}) +set(SOUFFLE_COMPILED_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +set(SOUFFLE_COMPILED_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS}) +set(SOUFFLE_COMPILED_RELEASE_CXX_FLAGS ${CMAKE_CXX_FLAGS_RELEASE}) +set(SOUFFLE_COMPILED_DEBUG_CXX_FLAGS ${CMAKE_CXX_FLAGS_DEBUG}) +get_target_property(SOUFFLE_COMPILED_DEFS compiled COMPILE_DEFINITIONS) +get_target_property(SOUFFLE_COMPILED_OPTS compiled COMPILE_OPTIONS) +get_target_property(SOUFFLE_COMPILED_INCS compiled INCLUDE_DIRECTORIES) + +set(SOUFFLE_COMPILED_LIBS "") +set(SOUFFLE_COMPILED_RPATHS "") + +if (THREADS_FOUND) + list(APPEND SOUFFLE_COMPILED_LIBS ${CMAKE_THREAD_LIBS_INIT}) +endif() + +if (OPENMP_FOUND) + string(APPEND SOUFFLE_COMPILED_CXX_FLAGS " ${OpenMP_CXX_FLAGS}") + list(APPEND SOUFFLE_COMPILED_INCS ${OpenMP_CXX_INCLUDE_DIRS}) +endif() + +if (CMAKE_DL_LIBS) + list(APPEND SOUFFLE_COMPILED_LIBS -l${CMAKE_DL_LIBS}) +endif() + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + list(APPEND SOUFFLE_COMPILED_LIBS -lstdc++fs) + endif () +endif() + +if (SOUFFLE_USE_SQLITE) + list(APPEND SOUFFLE_COMPILED_LIBS ${SQLite3_LIBRARY}) + if (COMMAND cmake_path) + cmake_path(GET SQLite3_LIBRARY PARENT_PATH SQLite3_RPATH) + else () + get_filename_component(SQLite3_RPATH ${SQLite3_LIBRARY} DIRECTORY) + endif () + list(APPEND SOUFFLE_COMPILED_RPATHS ${SQLite3_RPATH}) +endif() + +if (SOUFFLE_USE_ZLIB) + list(APPEND SOUFFLE_COMPILED_LIBS ${ZLIB_LIBRARY_RELEASE}) + if (COMMAND cmake_path) + cmake_path(GET ZLIB_LIBRARY_RELEASE PARENT_PATH ZLIB_RPATH) + else () + get_filename_component(ZLIB_RPATH ${ZLIB_LIBRARY_RELEASE} DIRECTORY) + endif () + list(APPEND SOUFFLE_COMPILED_RPATHS ${ZLIB_RPATH}) +endif() + +if (SOUFFLE_USE_CURSES) + list(APPEND SOUFFLE_COMPILED_LIBS ${CURSES_NCURSES_LIBRARY}) + if (COMMAND cmake_path) + cmake_path(GET CURSES_NCURSES_LIBRARY PARENT_PATH NCURSES_RPATH) + else () + get_filename_component(NCURSES_RPATH ${CURSES_NCURSES_LIBRARY} DIRECTORY) + endif () + list(APPEND SOUFFLE_COMPILED_RPATHS ${NCURSES_RPATH}) +endif() + +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + # using Python3 PEP 3101 Format String: + set(OUTNAME_FMT "-o {}") + set(LIBDIR_FMT "-L{}") + set(LIBNAME_FMT "-l{}") + set(RPATH_FMT "-Wl,-rpath,{}") + set(EXE_EXTENSION "") + set(OS_PATH_DELIMITER ":") +elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + # using Python3 PEP 3101 Format String: + set(OUTNAME_FMT "/Fe:{}") + set(LIBDIR_FMT "/libpath:{}") + set(LIBNAME_FMT "{}.lib") + set(RPATH_FMT "") + set(EXE_EXTENSION ".exe") + set(OS_PATH_DELIMITER ";") +endif () + +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + ## Is is very important that the Runtime Library match between + ## the Souffle compiled program and any dynamically loaded functor library. + if (CMAKE_MSVC_RUNTIME_LIBRARY MATCHES "^MultiThreaded(Debug)?$") + # static multi-threaded library + string(APPEND SOUFFLE_COMPILED_DEBUG_CXX_FLAGS " /MTd") + string(APPEND SOUFFLE_COMPILED_RELEASE_CXX_FLAGS " /MT") + else () + # dynamic multi-threaded library + string(APPEND SOUFFLE_COMPILED_DEBUG_CXX_FLAGS " /MDd") + string(APPEND SOUFFLE_COMPILED_RELEASE_CXX_FLAGS " /MD") + endif () +endif () + +list(JOIN SOUFFLE_COMPILED_OPTS " " SOUFFLE_COMPILED_CXX_OPTIONS) + +list(JOIN SOUFFLE_COMPILED_LIBS " " SOUFFLE_COMPILED_LINK_OPTIONS) + +if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(SOUFFLE_COMPILED_LINK_OPTIONS "/link ${SOUFFLE_COMPILED_LINK_OPTIONS}") +endif () + +list(JOIN SOUFFLE_COMPILED_RPATHS "${OS_PATH_DELIMITER}" SOUFFLE_COMPILED_RPATH_LIST) + +list(TRANSFORM SOUFFLE_COMPILED_INCS PREPEND "-I") +list(JOIN SOUFFLE_COMPILED_INCS " " SOUFFLE_COMPILED_INCLUDES) + +list(TRANSFORM SOUFFLE_COMPILED_DEFS PREPEND "-D") +list(JOIN SOUFFLE_COMPILED_DEFS " " SOUFFLE_COMPILED_DEFINITIONS) + +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/souffle-compile.template.py TEMPLATE) + +# json parameters injected at the begining of souffle-compile.py +set(SOUFFLE_COMPILE_PY +"#!/usr/bin/env python3 +JSON_DATA_TEXT = \"\"\"{ + \"compiler\": \"${SOUFFLE_COMPILED_CXX_COMPILER}\", + \"compiler_id\": \"${SOUFFLE_COMPILED_CXX_COMPILER_ID}\", + \"compiler_version\": \"${SOUFFLE_COMPILED_CXX_COMPILER_VERSION}\", + \"msvc_version\": \"${MSVC_VERSION}\", + \"includes\": \"${SOUFFLE_COMPILED_INCLUDES}\", + \"std_flag\": \"${SOUFFLE_COMPILED_CXX_STANDARD}\", + \"cxx_flags\": \"${SOUFFLE_COMPILED_CXX_FLAGS}\", + \"cxx_link_flags\": \"${SOUFFLE_COMPILED_CXX_LINK_FLAGS}\", + \"release_cxx_flags\": \"${SOUFFLE_COMPILED_RELEASE_CXX_FLAGS}\", + \"debug_cxx_flags\": \"${SOUFFLE_COMPILED_DEBUG_CXX_FLAGS}\", + \"definitions\": \"${SOUFFLE_COMPILED_DEFINITIONS}\", + \"compile_options\": \"${SOUFFLE_COMPILED_CXX_OPTIONS}\", + \"link_options\": \"${SOUFFLE_COMPILED_LINK_OPTIONS}\", + \"rpaths\": \"${SOUFFLE_COMPILED_RPATH_LIST}\", + \"outname_fmt\": \"${OUTNAME_FMT}\", + \"libdir_fmt\": \"${LIBDIR_FMT}\", + \"libname_fmt\": \"${LIBNAME_FMT}\", + \"rpath_fmt\": \"${RPATH_FMT}\", + \"path_delimiter\": \"${OS_PATH_DELIMITER}\", + \"exe_extension\": \"${EXE_EXTENSION}\", + \"source_include_dir\": \"${CMAKE_CURRENT_SOURCE_DIR}/include\", + \"jni_includes\": \"${JAVA_INCLUDE_PATH}${OS_PATH_DELIMITER}${JAVA_INCLUDE_PATH2}\" +}\"\"\" +${TEMPLATE} +") + +file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/souffle-compile.py" CONTENT "${SOUFFLE_COMPILE_PY}") +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/souffle-compile.py DESTINATION bin) + +# --------------------------------------- # FIXME: Ideally, eventually we will move these out to the "tests" subdirectory # now that we have a sane(er?) build system diff --git a/src/Global.cpp b/src/Global.cpp index 95bb1170306..f8436900dd8 100644 --- a/src/Global.cpp +++ b/src/Global.cpp @@ -15,6 +15,7 @@ ***********************************************************************/ #include "Global.h" +#include "souffle/utility/FileUtil.h" #include "souffle/utility/StreamUtil.h" #include "souffle/utility/StringUtil.h" #include @@ -25,8 +26,14 @@ #include #include #include +#include #include + +#ifdef USE_CUSTOM_GETOPTLONG +#include "souffle/utility/GetOptLongImpl.h" +#else #include +#endif namespace souffle { @@ -129,7 +136,7 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c // use the main options to define the global configuration { // array of long names for classic getopt processing - option longNames[mainOptions.size()]; + std::unique_ptr longNames = std::make_unique(mainOptions.size()); // string of short names for classic getopt processing std::string shortNames = ""; // table to map the short name to its option @@ -172,7 +179,7 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c // use getopt to process the arguments given to the command line, with the parameters being the // short and long names from above int c; - while ((c = getopt_long(argc, argv, shortNames.c_str(), longNames, nullptr)) != EOF) { + while ((c = getopt_long(argc, argv, shortNames.c_str(), longNames.get(), nullptr)) != -1) { // case for the unknown option if (c == '?') { std::cerr << Global::config().help(); @@ -186,6 +193,12 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c // define the value for the option in the global configuration as its argument or an empty string // if no argument exists std::string arg = optarg != nullptr ? std::string(optarg) : std::string(); + + if (iter->second->argument == "FILE" || iter->second->argument == "DIR") { + // convert to prefered directory separator + makePreferred(arg); + } + // if the option allows multiple arguments if (opt->takesMany) { // set the value of the option in the global config to the concatenation of its previous @@ -209,7 +222,7 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c // ensure that the optind is less than the total number of arguments if (argc > 1 && optind >= argc) { std::cerr << Global::config().help(); - throw std::runtime_error("Error: Unknown command line option."); + throw std::runtime_error("Error: Missing source file path."); } if (mainOptions[0].longName.empty()) { diff --git a/src/LogStatement.h b/src/LogStatement.h index 558ebe81571..498d9d8dd8d 100644 --- a/src/LogStatement.h +++ b/src/LogStatement.h @@ -69,7 +69,7 @@ class LogStatement { return line.str(); } - static const std::string tRecursiveRule(const std::string& relationName, const int version, + static const std::string tRecursiveRule(const std::string& relationName, const std::size_t version, const SrcLocation& srcLocation, const std::string& datalogText) { const char* messageType = "@t-recursive-rule"; std::stringstream line; @@ -78,7 +78,7 @@ class LogStatement { return line.str(); } - static const std::string nRecursiveRule(const std::string& relationName, const int version, + static const std::string nRecursiveRule(const std::string& relationName, const std::size_t version, const SrcLocation& srcLocation, const std::string& datalogText) { const char* messageType = "@n-recursive-rule"; std::stringstream line; diff --git a/src/ast/ExecutionOrder.h b/src/ast/ExecutionOrder.h index bb7c855d2e1..d5173d0cfcf 100644 --- a/src/ast/ExecutionOrder.h +++ b/src/ast/ExecutionOrder.h @@ -30,7 +30,7 @@ namespace souffle::ast { */ class ExecutionOrder : public Node { public: - using ExecOrder = std::vector; + using ExecOrder = std::vector; ExecutionOrder(ExecOrder order = {}, SrcLocation loc = {}); diff --git a/src/ast/ExecutionPlan.cpp b/src/ast/ExecutionPlan.cpp index d1df9e91029..0a888c8da47 100644 --- a/src/ast/ExecutionPlan.cpp +++ b/src/ast/ExecutionPlan.cpp @@ -17,14 +17,14 @@ namespace souffle::ast { /** Set execution order for a given rule version */ -void ExecutionPlan::setOrderFor(int version, Own plan) { +void ExecutionPlan::setOrderFor(std::size_t version, Own plan) { assert(plan != nullptr); plans[version] = std::move(plan); } /** Get orders */ -std::map ExecutionPlan::getOrders() const { - std::map result; +std::map ExecutionPlan::getOrders() const { + std::map result; for (auto& plan : plans) { result.insert(std::make_pair(plan.first, plan.second.get())); } diff --git a/src/ast/ExecutionPlan.h b/src/ast/ExecutionPlan.h index 66ca0c19b5f..7350608ba7a 100644 --- a/src/ast/ExecutionPlan.h +++ b/src/ast/ExecutionPlan.h @@ -40,10 +40,10 @@ class ExecutionPlan : public Node { using Node::Node; /** Set execution order for a given rule version */ - void setOrderFor(int version, Own plan); + void setOrderFor(std::size_t version, Own plan); /** Get orders */ - std::map getOrders() const; + std::map getOrders() const; void apply(const NodeMapper& map) override; @@ -59,7 +59,7 @@ class ExecutionPlan : public Node { private: /** Mapping versions of clauses to execution orders */ - std::map> plans; + std::map> plans; }; } // namespace souffle::ast diff --git a/src/ast/Program.h b/src/ast/Program.h index aeee4a94563..1316f0dcd32 100644 --- a/src/ast/Program.h +++ b/src/ast/Program.h @@ -271,7 +271,9 @@ class Program : public Node { namespace souffle { template void visit(ast::Program& program, F&& go) { +#ifndef NDEBUG program.clause_visit_in_progress++; +#endif for ([[maybe_unused]] auto&& [k, info] : program.relations) for (auto&& cl : info.clauses) { assert(k == ast::getName(*cl)); @@ -280,12 +282,16 @@ void visit(ast::Program& program, F&& go) { // e.g. `visit(program, [](Atom&) {})` assert(k == ast::getName(*cl)); } +#ifndef NDEBUG program.clause_visit_in_progress--; +#endif } template void visit(ast::Program const& program, F&& go) { +#ifndef NDEBUG program.clause_visit_in_progress++; +#endif for ([[maybe_unused]] auto&& [k, info] : program.relations) for (auto&& cl : info.clauses) { assert(k == ast::getName(*cl)); @@ -294,6 +300,8 @@ void visit(ast::Program const& program, F&& go) { // e.g. `visit(program, [](Atom&) {})` assert(k == ast::getName(*cl)); } +#ifndef NDEBUG program.clause_visit_in_progress--; +#endif } } // namespace souffle diff --git a/src/ast/analysis/ClauseNormalisation.cpp b/src/ast/analysis/ClauseNormalisation.cpp index 1ae1baa4bce..f59743260ba 100644 --- a/src/ast/analysis/ClauseNormalisation.cpp +++ b/src/ast/analysis/ClauseNormalisation.cpp @@ -49,7 +49,7 @@ NormalisedClause::NormalisedClause(const Clause* clause) { for (const auto* arg : clause->getHead()->getArguments()) { headVars.push_back(normaliseArgument(arg)); } - clauseElements.push_back({.name = name, .params = headVars}); + clauseElements.push_back({name, headVars}); // body for (const auto* lit : clause->getBodyLiterals()) { @@ -67,7 +67,7 @@ void NormalisedClause::addClauseAtom( for (const auto* arg : atom->getArguments()) { vars.push_back(normaliseArgument(arg)); } - clauseElements.push_back({.name = name, .params = vars}); + clauseElements.push_back({name, vars}); } void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Literal* lit) { @@ -82,7 +82,7 @@ void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Li vars.push_back(scopeID); vars.push_back(normaliseArgument(bc->getLHS())); vars.push_back(normaliseArgument(bc->getRHS())); - clauseElements.push_back({.name = name, .params = vars}); + clauseElements.push_back({name, vars}); } else { assert(lit != nullptr && "unexpected nullptr lit"); fullyNormalised = false; @@ -90,7 +90,7 @@ void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Li qualifier << "@min:unhandled:lit:" << scopeID; QualifiedName name(toString(*lit)); name.prepend(qualifier.str()); - clauseElements.push_back({.name = name, .params = std::vector()}); + clauseElements.push_back({name, std::vector()}); } } @@ -142,7 +142,7 @@ std::string NormalisedClause::normaliseArgument(const Argument* arg) { } // Type signature is its own special atom - clauseElements.push_back({.name = aggrTypeSignature.str(), .params = aggrTypeSignatureComponents}); + clauseElements.push_back({aggrTypeSignature.str(), aggrTypeSignatureComponents}); // Add each contained normalised clause literal, tying it with the new scope ID for (const auto* literal : aggr->getBodyLiterals()) { diff --git a/src/ast/analysis/typesystem/TypeConstraints.cpp b/src/ast/analysis/typesystem/TypeConstraints.cpp index 3f027221ac4..92fcbfd0d55 100644 --- a/src/ast/analysis/typesystem/TypeConstraints.cpp +++ b/src/ast/analysis/typesystem/TypeConstraints.cpp @@ -316,9 +316,9 @@ TypeConstraint isSubtypeOfComponent( struct C : public Constraint { TypeVar elementVariable; TypeVar recordVariable; - unsigned index; + std::size_t index; - C(TypeVar elementVariable, TypeVar recordVariable, int index) + C(TypeVar elementVariable, TypeVar recordVariable, std::size_t index) : elementVariable(std::move(elementVariable)), recordVariable(std::move(recordVariable)), index(index) {} diff --git a/src/ast/tests/ast_utils_test.cpp b/src/ast/tests/ast_utils_test.cpp index d6fb03a523c..6839e6b18df 100644 --- a/src/ast/tests/ast_utils_test.cpp +++ b/src/ast/tests/ast_utils_test.cpp @@ -156,13 +156,13 @@ TEST(AstUtils, ReorderClauseAtoms) { // Check trivial permutation Own reorderedClause0 = - Own(reorderAtoms(clause, std::vector({0, 1, 2, 3, 4}))); + Own(reorderAtoms(clause, std::vector({0, 1, 2, 3, 4}))); EXPECT_EQ("a(x) :- \n b(x),\n c(x),\n 1 != 2,\n d(y),\n !e(z),\n c(z),\n e(x).", toString(*reorderedClause0)); // Check more complex permutation Own reorderedClause1 = - Own(reorderAtoms(clause, std::vector({2, 3, 4, 1, 0}))); + Own(reorderAtoms(clause, std::vector({2, 3, 4, 1, 0}))); EXPECT_EQ("a(x) :- \n d(y),\n c(z),\n 1 != 2,\n e(x),\n !e(z),\n c(x),\n b(x).", toString(*reorderedClause1)); } diff --git a/src/ast/transform/ComponentInstantiation.cpp b/src/ast/transform/ComponentInstantiation.cpp index 4ab71bcf2e0..0bc38b13ddf 100644 --- a/src/ast/transform/ComponentInstantiation.cpp +++ b/src/ast/transform/ComponentInstantiation.cpp @@ -50,7 +50,8 @@ using namespace analysis; namespace { -static const unsigned int MAX_INSTANTIATION_DEPTH = 1000; +/* Used to be 1000 but stack-size on Windows does not allow such depth. */ +static const unsigned int MAX_INSTANTIATION_DEPTH = 100; /** * A container type for the (instantiated) content of a component. diff --git a/src/ast/transform/ExecutionPlanChecker.cpp b/src/ast/transform/ExecutionPlanChecker.cpp index 43ab2e30d2b..b3254a622ae 100644 --- a/src/ast/transform/ExecutionPlanChecker.cpp +++ b/src/ast/transform/ExecutionPlanChecker.cpp @@ -55,15 +55,15 @@ bool ExecutionPlanChecker::transform(TranslationUnit& translationUnit) { if (isA(clause)) { continue; } - int version = 0; + std::size_t version = 0; for (const auto* atom : getBodyLiterals(*clause)) { if (scc.count(program.getRelation(*atom)) != 0u) { version++; } } - int maxVersion = -1; + std::optional maxVersion; for (auto const& cur : clause->getExecutionPlan()->getOrders()) { - maxVersion = std::max(cur.first, maxVersion); + maxVersion = std::max(cur.first, maxVersion.value_or(cur.first)); bool isComplete = true; auto order = cur.second->getOrder(); @@ -79,7 +79,7 @@ bool ExecutionPlanChecker::transform(TranslationUnit& translationUnit) { } } - if (version <= maxVersion) { + if (version <= *maxVersion) { for (const auto& cur : clause->getExecutionPlan()->getOrders()) { if (cur.first >= version) { report.addDiagnostic(Diagnostic(Diagnostic::Type::ERROR, diff --git a/src/ast/transform/MagicSet.cpp b/src/ast/transform/MagicSet.cpp index b5fbc2f9545..0ac7dff86c6 100644 --- a/src/ast/transform/MagicSet.cpp +++ b/src/ast/transform/MagicSet.cpp @@ -929,7 +929,8 @@ bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) { } // Create the rules (from all previous strata) for the newly positive labelled literals - for (int preStratum = stratum - 1; preStratum >= 0; preStratum--) { + for (std::size_t pos = stratum; pos > 0; pos--) { + const std::size_t preStratum = pos - 1; if (contains(neglabelledStrata, preStratum)) continue; if (!contains(dependentStrata[preStratum], stratum)) continue; diff --git a/src/ast/transform/MinimiseProgram.cpp b/src/ast/transform/MinimiseProgram.cpp index 2b966fb3077..62dd623648e 100644 --- a/src/ast/transform/MinimiseProgram.cpp +++ b/src/ast/transform/MinimiseProgram.cpp @@ -45,12 +45,12 @@ namespace souffle::ast::transform { using namespace analysis; bool MinimiseProgramTransformer::existsValidPermutation(const NormalisedClause& left, - const NormalisedClause& right, const std::vector>& permutationMatrix) { + const NormalisedClause& right, const std::vector>& permutationMatrix) { std::size_t clauseSize = permutationMatrix.size(); // keep track of the possible end-positions of each atom in the first clause - std::vector> validMoves; + std::vector> validMoves; for (std::size_t i = 0; i < clauseSize; i++) { - std::vector currentRow; + std::vector currentRow; for (std::size_t j = 0; j < clauseSize; j++) { if (permutationMatrix[i][j] == 1) { currentRow.push_back(j); @@ -60,9 +60,9 @@ bool MinimiseProgramTransformer::existsValidPermutation(const NormalisedClause& } // extract the possible permutations, DFS style - std::vector seen(clauseSize); - std::vector currentPermutation; - std::stack> todoStack; + std::vector seen(clauseSize); + std::vector currentPermutation; + std::stack> todoStack; todoStack.push(validMoves[0]); @@ -92,7 +92,7 @@ bool MinimiseProgramTransformer::existsValidPermutation(const NormalisedClause& } // pull out the possibilities for the current point of the permutation - std::vector possibilities = todoStack.top(); + std::vector possibilities = todoStack.top(); todoStack.pop(); if (possibilities.empty()) { // no more possibilities at this point, so undo our last move @@ -111,7 +111,7 @@ bool MinimiseProgramTransformer::existsValidPermutation(const NormalisedClause& } // try the next possibility - unsigned int nextNum = possibilities[0]; + std::size_t nextNum = possibilities[0]; // update the possibility vector for the current position possibilities.erase(possibilities.begin()); @@ -158,7 +158,7 @@ bool MinimiseProgramTransformer::areEquivalentRelations( } bool MinimiseProgramTransformer::isValidPermutation(const NormalisedClause& left, - const NormalisedClause& right, const std::vector& permutation) { + const NormalisedClause& right, const std::vector& permutation) { const auto& leftElements = left.getElements(); const auto& rightElements = right.getElements(); @@ -235,9 +235,9 @@ bool MinimiseProgramTransformer::areBijectivelyEquivalent( // set up the n x n permutation matrix, where n is the number of clause elements std::size_t size = leftElements.size(); - auto permutationMatrix = std::vector>(size); + auto permutationMatrix = std::vector>(size); for (auto& i : permutationMatrix) { - i = std::vector(size); + i = std::vector(size); } // create permutation matrix diff --git a/src/ast/transform/MinimiseProgram.h b/src/ast/transform/MinimiseProgram.h index 1f54d917229..a9799c95fe5 100644 --- a/src/ast/transform/MinimiseProgram.h +++ b/src/ast/transform/MinimiseProgram.h @@ -52,7 +52,7 @@ class MinimiseProgramTransformer : public Transformer { // Check whether a valid variable mapping exists for the given permutation. static bool isValidPermutation(const analysis::NormalisedClause& left, - const analysis::NormalisedClause& right, const std::vector& permutation); + const analysis::NormalisedClause& right, const std::vector& permutation); // Check whether two relations have the same qualifiers, representation and attribute types. static bool areEquivalentRelations(const Relation* firstRelation, const Relation* secondRelation); @@ -60,7 +60,7 @@ class MinimiseProgramTransformer : public Transformer { // Checks whether a permutation encoded in the given matrix has a valid corresponding variable mapping. static bool existsValidPermutation(const analysis::NormalisedClause& left, const analysis::NormalisedClause& right, - const std::vector>& permutationMatrix); + const std::vector>& permutationMatrix); /** -- Sub-Transformations -- */ diff --git a/src/ast/transform/ReorderLiterals.cpp b/src/ast/transform/ReorderLiterals.cpp index 729606287c5..14a150a2fa1 100644 --- a/src/ast/transform/ReorderLiterals.cpp +++ b/src/ast/transform/ReorderLiterals.cpp @@ -44,11 +44,11 @@ Clause* ReorderLiteralsTransformer::reorderClauseWithSips(const SipsMetric& sips } // get the ordering corresponding to the SIPS - std::vector newOrdering = sips.getReordering(clause); + std::vector newOrdering = sips.getReordering(clause); // check if we need a change bool changeNeeded = false; - for (unsigned int i = 0; i < newOrdering.size(); i++) { + for (std::size_t i = 0; i < newOrdering.size(); i++) { if (newOrdering[i] != i) { changeNeeded = true; } diff --git a/src/ast/transform/ResolveAliases.cpp b/src/ast/transform/ResolveAliases.cpp index 6ef2cdf5ca9..427d21b185f 100644 --- a/src/ast/transform/ResolveAliases.cpp +++ b/src/ast/transform/ResolveAliases.cpp @@ -153,7 +153,7 @@ class Substitution { << "}"; } - friend std::ostream& operator<<(std::ostream& out, const Substitution& s) __attribute__((unused)) { + [[maybe_unused]] friend std::ostream& operator<<(std::ostream& out, const Substitution& s) { s.print(out); return out; } @@ -194,7 +194,7 @@ class Equation { out << *lhs << " = " << *rhs; } - friend std::ostream& operator<<(std::ostream& out, const Equation& e) __attribute__((unused)) { + [[maybe_unused]] friend std::ostream& operator<<(std::ostream& out, const Equation& e) { e.print(out); return out; } diff --git a/src/ast/transform/SemanticChecker.cpp b/src/ast/transform/SemanticChecker.cpp index 943f52c8c2c..8bfc4fb455d 100644 --- a/src/ast/transform/SemanticChecker.cpp +++ b/src/ast/transform/SemanticChecker.cpp @@ -117,7 +117,7 @@ struct SemanticCheckerImpl { void checkConstant(const Argument& argument); void checkFact(const Clause& fact); void checkClause(const Clause& clause); - void checkComplexRule(std::set multiRule); + void checkComplexRule(const std::set& multiRule); void checkRelationDeclaration(const Relation& relation); void checkRelationFunctionalDependencies(const Relation& relation); void checkRelation(const Relation& relation); @@ -503,27 +503,34 @@ void SemanticCheckerImpl::checkClause(const Clause& clause) { } } -void SemanticCheckerImpl::checkComplexRule(std::set multiRule) { +void SemanticCheckerImpl::checkComplexRule(const std::set& multiRule) { std::map var_count; std::map var_pos; + auto count_var = [&](const ast::Variable& var) { + const auto& varName = var.getName(); + if (0 == var_count[varName]++) { + var_pos[varName] = &var; + } else { + const auto& PrevLoc = var_pos[varName]->getSrcLoc(); + const auto& Loc = var.getSrcLoc(); + if (PrevLoc < Loc) { + var_pos[varName] = &var; + } + } + }; + // Count the variable occurrence for the body of a // complex rule only once. // TODO (b-scholz): for negation / disjunction this is not quite // right; we would need more semantic information here. for (auto literal : (*multiRule.begin())->getBodyLiterals()) { - visit(*literal, [&](const ast::Variable& var) { - var_count[var.getName()]++; - var_pos[var.getName()] = &var; - }); + visit(*literal, count_var); } // Count variable occurrence for each head separately for (auto clause : multiRule) { - visit(*(clause->getHead()), [&](const ast::Variable& var) { - var_count[var.getName()]++; - var_pos[var.getName()] = &var; - }); + visit(*(clause->getHead()), count_var); } // Check that a variables occurs more than once @@ -865,8 +872,8 @@ void SemanticCheckerImpl::checkInlining() { cycle << "{" << cycleOrigin->getQualifiedName(); // Print it backwards to preserve the initial cycle order - for (int i = result.size() - 2; i >= 0; i--) { - cycle << ", " << result[i]; + for (std::size_t i = result.size() - 1; i >= 1; i--) { + cycle << ", " << result[i - 1]; } cycle << "}"; diff --git a/src/ast/utility/SipsMetric.cpp b/src/ast/utility/SipsMetric.cpp index 679f1ae146a..ad76c55d882 100644 --- a/src/ast/utility/SipsMetric.cpp +++ b/src/ast/utility/SipsMetric.cpp @@ -29,16 +29,17 @@ namespace souffle::ast { -std::vector SipsMetric::getReordering(const Clause* clause) const { +std::vector SipsMetric::getReordering(const Clause* clause) const { BindingStore bindingStore(clause); auto atoms = getBodyLiterals(*clause); - std::vector newOrder(atoms.size()); + std::vector newOrder(atoms.size()); std::size_t numAdded = 0; while (numAdded < atoms.size()) { // grab the index of the next atom, based on the SIPS function const auto& costs = evaluateCosts(atoms, bindingStore); - auto minIdx = std::distance(costs.begin(), std::min_element(costs.begin(), costs.end())); + std::size_t minIdx = static_cast( + std::distance(costs.begin(), std::min_element(costs.begin(), costs.end()))); const auto* nextAtom = atoms[minIdx]; assert(nextAtom != nullptr && "nullptr atoms should have maximal cost"); @@ -103,8 +104,8 @@ std::vector AllBoundSips::evaluateCosts( continue; } - int arity = atom->getArity(); - int numBound = bindingStore.numBoundArguments(atom); + std::size_t arity = atom->getArity(); + std::size_t numBound = bindingStore.numBoundArguments(atom); cost.push_back(arity == numBound ? 0 : 1); } assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); @@ -121,8 +122,8 @@ std::vector NaiveSips::evaluateCosts( continue; } - int arity = atom->getArity(); - int numBound = bindingStore.numBoundArguments(atom); + std::size_t arity = atom->getArity(); + std::size_t numBound = bindingStore.numBoundArguments(atom); if (arity == numBound) { cost.push_back(0); } else if (numBound >= 1) { @@ -145,8 +146,8 @@ std::vector MaxBoundSips::evaluateCosts( continue; } - int arity = atom->getArity(); - int numBound = bindingStore.numBoundArguments(atom); + std::size_t arity = atom->getArity(); + std::size_t numBound = bindingStore.numBoundArguments(atom); if (arity == numBound) { // Always better than anything else cost.push_back(0); @@ -155,7 +156,7 @@ std::vector MaxBoundSips::evaluateCosts( cost.push_back(2); } else { // Between 0 and 1, decreasing with more num bound - cost.push_back(1 / numBound); + cost.push_back(1.0 / numBound); } } assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); @@ -172,8 +173,8 @@ std::vector MaxRatioSips::evaluateCosts( continue; } - int arity = atom->getArity(); - int numBound = bindingStore.numBoundArguments(atom); + std::size_t arity = atom->getArity(); + std::size_t numBound = bindingStore.numBoundArguments(atom); if (arity == 0) { // Always better than anything else cost.push_back(0); @@ -182,7 +183,7 @@ std::vector MaxRatioSips::evaluateCosts( cost.push_back(2); } else { // Between 0 and 1, decreasing as the ratio increases - cost.push_back(1 - numBound / arity); + cost.push_back(1.0 - numBound / arity); } } assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); @@ -199,7 +200,7 @@ std::vector LeastFreeSips::evaluateCosts( continue; } - cost.push_back(atom->getArity() - bindingStore.numBoundArguments(atom)); + cost.push_back((double)(atom->getArity() - bindingStore.numBoundArguments(atom))); } return cost; } @@ -221,7 +222,7 @@ std::vector LeastFreeVarsSips::evaluateCosts( freeVars.insert(var.getName()); } }); - cost.push_back(freeVars.size()); + cost.push_back((double)freeVars.size()); } return cost; } @@ -239,15 +240,15 @@ std::vector ProfileUseSips::evaluateCosts( } // prioritise propositions - int arity = atom->getArity(); + std::size_t arity = atom->getArity(); if (arity == 0) { cost.push_back(0); continue; } // calculate log(|R|) * #free/#args - int numBound = bindingStore.numBoundArguments(atom); - int numFree = arity - numBound; + std::size_t numBound = bindingStore.numBoundArguments(atom); + std::size_t numFree = arity - numBound; double value = log(profileUse.getRelationSize(atom->getQualifiedName())); value *= (numFree * 1.0) / arity; } @@ -265,8 +266,8 @@ std::vector InputSips::evaluateCosts( } const auto& relName = atom->getQualifiedName(); - int arity = atom->getArity(); - int numBound = bindingStore.numBoundArguments(atom); + std::size_t arity = atom->getArity(); + std::size_t numBound = bindingStore.numBoundArguments(atom); if (arity == numBound) { // prioritise all-bound cost.push_back(0); diff --git a/src/ast/utility/SipsMetric.h b/src/ast/utility/SipsMetric.h index df86ead31db..5366764e127 100644 --- a/src/ast/utility/SipsMetric.h +++ b/src/ast/utility/SipsMetric.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include namespace souffle::ast::analysis { @@ -45,7 +46,7 @@ class SipsMetric { * @param clause clause to reorder * @return the vector of new positions; v[i] = j iff atom j moves to pos i */ - std::vector getReordering(const Clause* clause) const; + std::vector getReordering(const Clause* clause) const; /** Create a SIPS metric based on a given heuristic. */ static std::unique_ptr create(const std::string& heuristic, const TranslationUnit& tu); diff --git a/src/ast/utility/Utils.cpp b/src/ast/utility/Utils.cpp index dd3663b8d63..e173ff86019 100644 --- a/src/ast/utility/Utils.cpp +++ b/src/ast/utility/Utils.cpp @@ -149,11 +149,11 @@ bool isProposition(const Atom* atom) { return atom->getArguments().empty(); } -std::vector reorderAtoms(const std::vector& atoms, const std::vector& newOrder) { +std::vector reorderAtoms(const std::vector& atoms, const std::vector& newOrder) { // Validate given order assert(newOrder.size() == atoms.size()); - std::vector nopOrder; - for (unsigned int i = 0; i < atoms.size(); i++) { + std::vector nopOrder; + for (std::size_t i = 0; i < atoms.size(); i++) { nopOrder.push_back(i); } assert(std::is_permutation(nopOrder.begin(), nopOrder.end(), newOrder.begin())); @@ -166,11 +166,11 @@ std::vector reorderAtoms(const std::vector& atoms, const std::vect return result; } -Clause* reorderAtoms(const Clause* clause, const std::vector& newOrder) { +Clause* reorderAtoms(const Clause* clause, const std::vector& newOrder) { // Find all atom positions - std::vector atomPositions; + std::vector atomPositions; std::vector bodyLiterals = clause->getBodyLiterals(); - for (unsigned int i = 0; i < bodyLiterals.size(); i++) { + for (std::size_t i = 0; i < bodyLiterals.size(); i++) { if (isA(bodyLiterals[i])) { atomPositions.push_back(i); } @@ -178,16 +178,16 @@ Clause* reorderAtoms(const Clause* clause, const std::vector& newO // Validate given order assert(newOrder.size() == atomPositions.size()); - std::vector nopOrder; - for (unsigned int i = 0; i < atomPositions.size(); i++) { + std::vector nopOrder; + for (std::size_t i = 0; i < atomPositions.size(); i++) { nopOrder.push_back(i); } assert(std::is_permutation(nopOrder.begin(), nopOrder.end(), newOrder.begin())); // Create a new clause with the given atom order, leaving the rest unchanged auto newClause = Own(clause->cloneHead()); - unsigned int currentAtom = 0; - for (unsigned int currentLiteral = 0; currentLiteral < bodyLiterals.size(); currentLiteral++) { + std::size_t currentAtom = 0; + for (std::size_t currentLiteral = 0; currentLiteral < bodyLiterals.size(); currentLiteral++) { Literal* literalToAdd = bodyLiterals[currentLiteral]; if (isA(literalToAdd)) { // Atoms should be reordered diff --git a/src/ast/utility/Utils.h b/src/ast/utility/Utils.h index f365f02fc1c..29b7b691eeb 100644 --- a/src/ast/utility/Utils.h +++ b/src/ast/utility/Utils.h @@ -146,7 +146,7 @@ bool isProposition(const Atom* atom); * @param clause clause to reorder atoms in * @param newOrder new order of atoms; atoms[i] = atoms[newOrder[i]] */ -Clause* reorderAtoms(const Clause* clause, const std::vector& newOrder); +Clause* reorderAtoms(const Clause* clause, const std::vector& newOrder); /** * Reorders a vector of atoms to be in the given order. @@ -154,7 +154,7 @@ Clause* reorderAtoms(const Clause* clause, const std::vector& newO * @param atoms atoms to reorder * @param newOrder new order of atoms; atoms[i] = atoms[newOrder[i]] */ -std::vector reorderAtoms(const std::vector& atoms, const std::vector& newOrder); +std::vector reorderAtoms(const std::vector& atoms, const std::vector& newOrder); /** * Negate an ast constraint diff --git a/src/ast/utility/Visitor.h b/src/ast/utility/Visitor.h index a59a36eecf4..ebdc85fdb0a 100644 --- a/src/ast/utility/Visitor.h +++ b/src/ast/utility/Visitor.h @@ -122,13 +122,13 @@ struct Visitor : souffle::detail::VisitorBase { } // -- types -- - SOUFFLE_VISITOR_LINK(SubsetType, Type); - SOUFFLE_VISITOR_LINK(AliasType, Type); - SOUFFLE_VISITOR_LINK(RecordType, Type); - SOUFFLE_VISITOR_LINK(AlgebraicDataType, Type); - SOUFFLE_VISITOR_LINK(UnionType, Type); - SOUFFLE_VISITOR_LINK(Type, Node); - SOUFFLE_VISITOR_LINK(BranchType, Node); + SOUFFLE_VISITOR_LINK(ast::SubsetType, Type); + SOUFFLE_VISITOR_LINK(ast::RecordType, Type); + SOUFFLE_VISITOR_LINK(ast::AliasType, Type); + SOUFFLE_VISITOR_LINK(ast::AlgebraicDataType, Type); + SOUFFLE_VISITOR_LINK(ast::UnionType, Type); + SOUFFLE_VISITOR_LINK(ast::Type, Node); + SOUFFLE_VISITOR_LINK(ast::BranchType, Node); // -- arguments -- SOUFFLE_VISITOR_LINK(Variable, Argument) diff --git a/src/ast2ram/provenance/ClauseTranslator.cpp b/src/ast2ram/provenance/ClauseTranslator.cpp index 230212e0b7b..c695fdd60db 100644 --- a/src/ast2ram/provenance/ClauseTranslator.cpp +++ b/src/ast2ram/provenance/ClauseTranslator.cpp @@ -85,7 +85,7 @@ void ClauseTranslator::indexAtoms(const ast::Clause& clause) { std::size_t atomIdx = 0; for (const auto* atom : getAtomOrdering(clause)) { // give the atom the current level - int scanLevel = addOperatorLevel(atom); + std::size_t scanLevel = addOperatorLevel(atom); indexNodeArguments(scanLevel, atom->getArguments()); // Add rule num variable @@ -122,8 +122,8 @@ Own ClauseTranslator::getLevelNumber(const ast::Clause& clause) return mk(FunctorOp::ADD, std::move(addArgs)); } -Own ClauseTranslator::addAtomScan( - Own op, const ast::Atom* atom, const ast::Clause& clause, int curLevel) const { +Own ClauseTranslator::addAtomScan(Own op, const ast::Atom* atom, + const ast::Clause& clause, std::size_t curLevel) const { // add constraints op = addConstantConstraints(curLevel, atom->getArguments(), std::move(op)); diff --git a/src/ast2ram/provenance/ClauseTranslator.h b/src/ast2ram/provenance/ClauseTranslator.h index a03645230b4..7f98ef41291 100644 --- a/src/ast2ram/provenance/ClauseTranslator.h +++ b/src/ast2ram/provenance/ClauseTranslator.h @@ -44,7 +44,7 @@ class ClauseTranslator : public ast2ram::seminaive::ClauseTranslator { Own createInsertion(const ast::Clause& clause) const override; void indexAtoms(const ast::Clause& clause) override; Own addAtomScan(Own op, const ast::Atom* atom, const ast::Clause& clause, - int curLevel) const override; + std::size_t curLevel) const override; private: Own getLevelNumber(const ast::Clause& clause) const; diff --git a/src/ast2ram/provenance/UnitTranslator.cpp b/src/ast2ram/provenance/UnitTranslator.cpp index 3387e2cd953..e08f49ae33a 100644 --- a/src/ast2ram/provenance/UnitTranslator.cpp +++ b/src/ast2ram/provenance/UnitTranslator.cpp @@ -119,8 +119,9 @@ VecOwn UnitTranslator::createRamRelations(const std::vector:symbol - for (std::size_t i = 0; i < clause->getBodyLiterals().size(); i++) { - const auto* literal = clause->getBodyLiterals().at(i); + auto bodyLiterals = clause->getBodyLiterals(); + for (std::size_t i = 0; i < bodyLiterals.size(); i++) { + const auto* literal = bodyLiterals.at(i); if (isA(literal) || isA(literal) || isA(literal)) { attributeNames.push_back("rel_" + std::to_string(i)); @@ -315,8 +316,8 @@ Own UnitTranslator::makeSubproofSubroutine(const ast::Clause& cl return SubproofGenerator(*context).translateNonRecursiveClause(clause); } -Own UnitTranslator::makeRamAtomExistenceCheck( - const ast::Atom* atom, const std::map& idToVarName, ValueIndex& valueIndex) const { +Own UnitTranslator::makeRamAtomExistenceCheck(const ast::Atom* atom, + const std::map& idToVarName, ValueIndex& valueIndex) const { auto relName = getConcreteRelationName(atom->getQualifiedName()); // Construct a query @@ -350,12 +351,13 @@ Own UnitTranslator::makeRamReturnFalse() const { } void UnitTranslator::transformVariablesToSubroutineArgs( - ram::Node* node, const std::map& idToVarName) const { + ram::Node* node, const std::map& idToVarName) const { // A mapper to replace variables with subroutine arguments struct VariablesToArguments : public ram::NodeMapper { - const std::map& idToVarName; + const std::map& idToVarName; - VariablesToArguments(const std::map& idToVarName) : idToVarName(idToVarName) {} + VariablesToArguments(const std::map& idToVarName) + : idToVarName(idToVarName) {} Own operator()(Own node) const override { if (const auto* tuple = as(node.get())) { @@ -411,7 +413,7 @@ Own UnitTranslator::makeNegationSubproofSubroutine(const ast::Cl // Keep track of references in a dummy index std::size_t count = 0; - std::map idToVarName; + std::map idToVarName; auto dummyValueIndex = mk(); visit(clause, [&](const ast::Variable& var) { if (dummyValueIndex->isDefined(var.getName()) || isPrefix("+underscore", var.getName())) { diff --git a/src/ast2ram/provenance/UnitTranslator.h b/src/ast2ram/provenance/UnitTranslator.h index d6b756ae7c7..c0fc6af51d3 100644 --- a/src/ast2ram/provenance/UnitTranslator.h +++ b/src/ast2ram/provenance/UnitTranslator.h @@ -67,10 +67,11 @@ class UnitTranslator : public ast2ram::seminaive::UnitTranslator { std::string getInfoRelationName(const ast::Clause* clause) const; Own makeRamAtomExistenceCheck(const ast::Atom* atom, - const std::map& idToVarName, ValueIndex& valueIndex) const; + const std::map& idToVarName, ValueIndex& valueIndex) const; Own makeRamReturnTrue() const; Own makeRamReturnFalse() const; - void transformVariablesToSubroutineArgs(ram::Node* node, const std::map& idToVar) const; + void transformVariablesToSubroutineArgs( + ram::Node* node, const std::map& idToVar) const; Own makeIfStatement( Own condition, Own trueOp, Own falseOp) const; }; diff --git a/src/ast2ram/seminaive/ClauseTranslator.cpp b/src/ast2ram/seminaive/ClauseTranslator.cpp index 096579e6f73..239c132d52f 100644 --- a/src/ast2ram/seminaive/ClauseTranslator.cpp +++ b/src/ast2ram/seminaive/ClauseTranslator.cpp @@ -253,8 +253,8 @@ Own ClauseTranslator::createInsertion(const ast::Clause& clause) return mk(headRelationName, std::move(values)); } -Own ClauseTranslator::addAtomScan( - Own op, const ast::Atom* atom, const ast::Clause& clause, int curLevel) const { +Own ClauseTranslator::addAtomScan(Own op, const ast::Atom* atom, + const ast::Clause& clause, std::size_t curLevel) const { const ast::Atom* head = clause.getHead(); // add constraints @@ -292,7 +292,7 @@ Own ClauseTranslator::addAtomScan( } Own ClauseTranslator::addRecordUnpack( - Own op, const ast::RecordInit* rec, int curLevel) const { + Own op, const ast::RecordInit* rec, std::size_t curLevel) const { // add constant constraints op = addConstantConstraints(curLevel, rec->getArguments(), std::move(op)); @@ -303,12 +303,12 @@ Own ClauseTranslator::addRecordUnpack( } Own ClauseTranslator::addAdtUnpack( - Own op, const ast::BranchInit* adt, int curLevel) const { + Own op, const ast::BranchInit* adt, std::size_t curLevel) const { assert(!context.isADTEnum(adt) && "ADT enums should not be unpacked"); std::vector branchArguments; - int branchLevel; + std::size_t branchLevel; // only for ADT with arity less than two (= simple) // add padding for branch id auto dummyArg = mk(); @@ -348,7 +348,8 @@ Own ClauseTranslator::addAdtUnpack( Own ClauseTranslator::addVariableIntroductions( const ast::Clause& clause, Own op) { - for (int i = operators.size() - 1; i >= 0; i--) { + for (std::size_t p = operators.size(); p > 0; p--) { + std::size_t i = p - 1; const auto* curOp = operators.at(i); if (const auto* atom = as(curOp)) { // add atom arguments through a scan @@ -362,7 +363,7 @@ Own ClauseTranslator::addVariableIntroductions( if (!context.isADTBranchSimple(adt)) { // for non-simple ADTs (arity > 1), we introduced two // nesting levels - i--; + p--; } } else { fatal("Unsupported AST node for creation of scan-level!"); @@ -371,8 +372,8 @@ Own ClauseTranslator::addVariableIntroductions( return op; } -Own ClauseTranslator::instantiateAggregator( - Own op, const ast::Clause& clause, const ast::Aggregator* agg, int curLevel) const { +Own ClauseTranslator::instantiateAggregator(Own op, const ast::Clause& clause, + const ast::Aggregator* agg, std::size_t curLevel) const { auto addAggEqCondition = [&](Own aggr, Own value, std::size_t pos) { if (isUndefValue(value.get())) return aggr; @@ -406,7 +407,7 @@ Own ClauseTranslator::instantiateAggregator( // referential variable bindings if (auto* var = as(arg)) { for (auto&& loc : valueIndex->getVariableReferences(var->getName())) { - if (curLevel != loc.identifier || (int)i != loc.element) { + if (curLevel != loc.identifier || i != loc.element) { aggCond = addAggEqCondition(std::move(aggCond), makeRamTupleElement(loc), i); break; } @@ -429,7 +430,7 @@ Own ClauseTranslator::instantiateAggregator( } Own ClauseTranslator::instantiateMultiResultFunctor( - Own op, const ast::IntrinsicFunctor& inf, int curLevel) const { + Own op, const ast::IntrinsicFunctor& inf, std::size_t curLevel) const { VecOwn args; for (auto&& x : inf.getArguments()) { args.push_back(context.translateValue(*valueIndex, x)); @@ -701,26 +702,26 @@ std::vector ClauseTranslator::getAtomOrdering(const ast::Clause& cla // get the imposed order, and change it to start at zero const auto& order = orders.at(version); - auto sz = order->getOrder().size(); - std::vector newOrder(sz); + std::vector newOrder(order->getOrder().size()); std::transform(order->getOrder().begin(), order->getOrder().end(), newOrder.begin(), - [](unsigned int i) -> unsigned int { return i - 1; }); + [](std::size_t i) -> std::size_t { return i - 1; }); return reorderAtoms(atoms, newOrder); } -int ClauseTranslator::addOperatorLevel(const ast::Node* node) { - int nodeLevel = operators.size() + generators.size(); +std::size_t ClauseTranslator::addOperatorLevel(const ast::Node* node) { + std::size_t nodeLevel = operators.size() + generators.size(); operators.push_back(node); return nodeLevel; } -int ClauseTranslator::addGeneratorLevel(const ast::Argument* arg) { - int generatorLevel = operators.size() + generators.size(); +std::size_t ClauseTranslator::addGeneratorLevel(const ast::Argument* arg) { + std::size_t generatorLevel = operators.size() + generators.size(); generators.push_back(arg); return generatorLevel; } -void ClauseTranslator::indexNodeArguments(int nodeLevel, const std::vector& nodeArgs) { +void ClauseTranslator::indexNodeArguments( + std::size_t nodeLevel, const std::vector& nodeArgs) { for (std::size_t i = 0; i < nodeArgs.size(); i++) { const auto& arg = nodeArgs.at(i); @@ -762,14 +763,14 @@ void ClauseTranslator::indexNodeArguments(int nodeLevel, const std::vectorsetGeneratorLoc(arg, Location({aggLoc, 0})); } void ClauseTranslator::indexAtoms(const ast::Clause& clause) { for (const auto* atom : getAtomOrdering(clause)) { // give the atom the current level - int scanLevel = addOperatorLevel(atom); + std::size_t scanLevel = addOperatorLevel(atom); indexNodeArguments(scanLevel, atom->getArguments()); } } @@ -788,7 +789,7 @@ void ClauseTranslator::indexAggregatorBody(const ast::Aggregator& agg) { for (std::size_t i = 0; i < aggAtomArgs.size(); i++) { const auto* arg = aggAtomArgs.at(i); if (const auto* var = as(arg)) { - valueIndex->addVarReference(var->getName(), aggLoc.identifier, (int)i); + valueIndex->addVarReference(var->getName(), aggLoc.identifier, i); } } } diff --git a/src/ast2ram/seminaive/ClauseTranslator.h b/src/ast2ram/seminaive/ClauseTranslator.h index 7f70375a655..d2901e4d8ce 100644 --- a/src/ast2ram/seminaive/ClauseTranslator.h +++ b/src/ast2ram/seminaive/ClauseTranslator.h @@ -91,7 +91,7 @@ class ClauseTranslator : public ast2ram::ClauseTranslator { virtual void indexAtoms(const ast::Clause& clause); void indexAggregators(const ast::Clause& clause); void indexMultiResultFunctors(const ast::Clause& clause); - void indexNodeArguments(int nodeLevel, const std::vector& nodeArgs); + void indexNodeArguments(std::size_t nodeLevel, const std::vector& nodeArgs); void indexAggregatorBody(const ast::Aggregator& agg); void indexGenerator(const ast::Argument& arg); @@ -104,11 +104,12 @@ class ClauseTranslator : public ast2ram::ClauseTranslator { Own addEntryPoint(const ast::Clause& clause, Own op) const; /** Levelling methods */ - virtual Own addAtomScan( - Own op, const ast::Atom* atom, const ast::Clause& clause, int curLevel) const; + virtual Own addAtomScan(Own op, const ast::Atom* atom, + const ast::Clause& clause, std::size_t curLevel) const; Own addRecordUnpack( - Own op, const ast::RecordInit* rec, int curLevel) const; - Own addAdtUnpack(Own op, const ast::BranchInit* adt, int curLevel) const; + Own op, const ast::RecordInit* rec, std::size_t curLevel) const; + Own addAdtUnpack( + Own op, const ast::BranchInit* adt, std::size_t curLevel) const; /** Helper methods */ Own addConstantConstraints( @@ -122,13 +123,13 @@ class ClauseTranslator : public ast2ram::ClauseTranslator { /** Generator instantiation */ Own instantiateAggregator(Own op, const ast::Clause& clause, - const ast::Aggregator* agg, int curLevel) const; + const ast::Aggregator* agg, std::size_t curLevel) const; Own instantiateMultiResultFunctor( - Own op, const ast::IntrinsicFunctor& inf, int curLevel) const; + Own op, const ast::IntrinsicFunctor& inf, std::size_t curLevel) const; /** Operation levelling */ - int addGeneratorLevel(const ast::Argument* arg); - int addOperatorLevel(const ast::Node* node); + std::size_t addGeneratorLevel(const ast::Argument* arg); + std::size_t addOperatorLevel(const ast::Node* node); private: std::vector generators; diff --git a/src/ast2ram/seminaive/UnitTranslator.cpp b/src/ast2ram/seminaive/UnitTranslator.cpp index 9a63598fbc3..5414762d73f 100644 --- a/src/ast2ram/seminaive/UnitTranslator.cpp +++ b/src/ast2ram/seminaive/UnitTranslator.cpp @@ -364,11 +364,11 @@ VecOwn UnitTranslator::generateClauseVersions( // Check that the correct number of versions have been created if (clause->getExecutionPlan() != nullptr) { - int maxVersion = -1; + std::optional maxVersion; for (const auto& cur : clause->getExecutionPlan()->getOrders()) { - maxVersion = std::max(cur.first, maxVersion); + maxVersion = std::max(cur.first, maxVersion.value_or(cur.first)); } - assert((int)sccAtoms.size() > maxVersion && "missing clause versions"); + assert(sccAtoms.size() > *maxVersion && "missing clause versions"); } return clauseVersions; diff --git a/src/ast2ram/utility/Location.h b/src/ast2ram/utility/Location.h index b6103ed2e60..4e10e3e01a5 100644 --- a/src/ast2ram/utility/Location.h +++ b/src/ast2ram/utility/Location.h @@ -23,11 +23,10 @@ namespace souffle::ast2ram { struct Location { - const int identifier; - const int element; + const std::size_t identifier; + const std::size_t element; - // TODO (azreika): change these to std::size_t - Location(int ident, int elem) : identifier(ident), element(elem) {} + Location(std::size_t ident, std::size_t elem) : identifier(ident), element(elem) {} Location(const Location& l) = default; diff --git a/src/ast2ram/utility/TranslatorContext.cpp b/src/ast2ram/utility/TranslatorContext.cpp index bbe77a69df3..05729b4187a 100644 --- a/src/ast2ram/utility/TranslatorContext.cpp +++ b/src/ast2ram/utility/TranslatorContext.cpp @@ -204,7 +204,7 @@ int TranslatorContext::getADTBranchId(const ast::BranchInit* adt) const { const ast::analysis::AlgebraicDataType::Branch& right) { return left.name < right.name; }); - return std::distance(std::begin(branches), iterToBranch); + return static_cast(std::distance(std::begin(branches), iterToBranch)); } bool TranslatorContext::isADTBranchSimple(const ast::BranchInit* adt) const { diff --git a/src/ast2ram/utility/ValueIndex.cpp b/src/ast2ram/utility/ValueIndex.cpp index a8aeda6c483..1c4e80a95c8 100644 --- a/src/ast2ram/utility/ValueIndex.cpp +++ b/src/ast2ram/utility/ValueIndex.cpp @@ -41,7 +41,7 @@ void ValueIndex::addVarReference(std::string varName, const Location& l) { locs.insert(l); } -void ValueIndex::addVarReference(std::string varName, int ident, int pos) { +void ValueIndex::addVarReference(std::string varName, std::size_t ident, std::size_t pos) { addVarReference(varName, Location({ident, pos})); } @@ -65,7 +65,7 @@ const Location& ValueIndex::getGeneratorLoc(const ast::Argument& arg) const { return generatorDefinitionPoints.at(&arg); } -void ValueIndex::setRecordDefinition(const ast::RecordInit& init, int ident, int pos) { +void ValueIndex::setRecordDefinition(const ast::RecordInit& init, std::size_t ident, std::size_t pos) { recordDefinitionPoints.insert({&init, Location({ident, pos})}); } @@ -74,7 +74,7 @@ const Location& ValueIndex::getDefinitionPoint(const ast::RecordInit& init) cons return recordDefinitionPoints.at(&init); } -void ValueIndex::setAdtDefinition(const ast::BranchInit& adt, int ident, int pos) { +void ValueIndex::setAdtDefinition(const ast::BranchInit& adt, std::size_t ident, std::size_t pos) { adtDefinitionPoints.insert({&adt, Location({ident, pos})}); } @@ -83,13 +83,13 @@ const Location& ValueIndex::getDefinitionPoint(const ast::BranchInit& adt) const return adtDefinitionPoints.at(&adt); } -bool ValueIndex::isGenerator(const int level) const { +bool ValueIndex::isGenerator(const std::size_t level) const { // check for aggregator definitions return any_of(generatorDefinitionPoints, [&level](const auto& location) { return location.second.identifier == level; }); } -bool ValueIndex::isSomethingDefinedOn(int level) const { +bool ValueIndex::isSomethingDefinedOn(std::size_t level) const { // check for variable definitions for (const auto& cur : varReferencePoints) { if (cur.second.begin()->identifier == level) { diff --git a/src/ast2ram/utility/ValueIndex.h b/src/ast2ram/utility/ValueIndex.h index bee35eaea6b..d85c473f142 100644 --- a/src/ast2ram/utility/ValueIndex.h +++ b/src/ast2ram/utility/ValueIndex.h @@ -46,25 +46,25 @@ class ValueIndex { const std::set& getVariableReferences(std::string var) const; void addVarReference(std::string varName, const Location& l); - void addVarReference(std::string varName, int ident, int pos); + void addVarReference(std::string varName, std::size_t ident, std::size_t pos); bool isDefined(const std::string& varName) const; const Location& getDefinitionPoint(const std::string& varName) const; // -- records -- - void setRecordDefinition(const ast::RecordInit& init, int ident, int pos); + void setRecordDefinition(const ast::RecordInit& init, std::size_t ident, std::size_t pos); const Location& getDefinitionPoint(const ast::RecordInit& init) const; // -- adts -- - void setAdtDefinition(const ast::BranchInit& adt, int ident, int pos); + void setAdtDefinition(const ast::BranchInit& adt, std::size_t ident, std::size_t pos); const Location& getDefinitionPoint(const ast::BranchInit& adt) const; // -- generators (aggregates & some functors) -- void setGeneratorLoc(const ast::Argument& arg, const Location& loc); const Location& getGeneratorLoc(const ast::Argument& arg) const; - bool isGenerator(const int level) const; + bool isGenerator(const std::size_t level) const; // -- others -- - bool isSomethingDefinedOn(int level) const; + bool isSomethingDefinedOn(std::size_t level) const; void print(std::ostream& out) const; private: diff --git a/src/dummy.cpp b/src/dummy.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/include/souffle/CompiledOptions.h b/src/include/souffle/CompiledOptions.h index e0fd40aa5fb..321a06cc29a 100644 --- a/src/include/souffle/CompiledOptions.h +++ b/src/include/souffle/CompiledOptions.h @@ -21,9 +21,14 @@ #include #include #include -#include #include +#ifdef USE_CUSTOM_GETOPTLONG +#include "souffle/utility/GetOptLongImpl.h" +#else +#include +#endif + namespace souffle { /** diff --git a/src/include/souffle/RecordTable.h b/src/include/souffle/RecordTable.h index e3ce502d4cd..9e7e8a55450 100644 --- a/src/include/souffle/RecordTable.h +++ b/src/include/souffle/RecordTable.h @@ -98,7 +98,7 @@ struct GenericRecordHash { std::size_t operator()(const T& Record) const { std::size_t Seed = 0; for (std::size_t I = 0; I < Arity; ++I) { - Seed ^= domainHash(Record[I]) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2); + Seed ^= domainHash(Record[(int)I]) + 0x9e3779b9UL + (Seed << 6U) + (Seed >> 2U); } return Seed; } diff --git a/src/include/souffle/SignalHandler.h b/src/include/souffle/SignalHandler.h index 9ad62917ec7..3e9beec20fc 100644 --- a/src/include/souffle/SignalHandler.h +++ b/src/include/souffle/SignalHandler.h @@ -26,12 +26,12 @@ #include #include -#ifdef _WIN32 -#include -#define STDERR_FILENO 2 /* Standard error output. */ -#else +#ifndef _MSC_VER #include -#endif //_WIN32 +#else +#include +#define STDERR_FILENO 2 +#endif namespace souffle { @@ -180,7 +180,13 @@ class SignalHandler { // assign to variable to suppress ignored-return-value error. // I don't think we care enough to handle this fringe failure mode. // Worse case we don't get an error message. - [[maybe_unused]] auto _ = ::write(STDERR_FILENO, msg, ::strlen(msg)); +#ifdef _MSC_VER + [[maybe_unused]] auto _ = + ::_write(STDERR_FILENO, msg, static_cast(::strlen(msg))); +#else + [[maybe_unused]] auto _ = + ::write(STDERR_FILENO, msg, static_cast(::strlen(msg))); +#endif } }; diff --git a/src/include/souffle/SouffleInterface.h b/src/include/souffle/SouffleInterface.h index 12b6c0b67ee..b606f5c0e14 100644 --- a/src/include/souffle/SouffleInterface.h +++ b/src/include/souffle/SouffleInterface.h @@ -43,7 +43,7 @@ class tuple; */ class Relation { public: - using arity_type = uint32_t; + using arity_type = std::size_t; protected: /** @@ -67,15 +67,15 @@ class Relation { * * TODO (Honghyw) : Provide a clear documentation of what id is used for. */ - uint32_t id; + std::size_t id; public: /** * Get the ID of the iterator_base object. * - * @return ID of the iterator_base object (unit32_t) + * @return ID of the iterator_base object (std::size_t) */ - virtual uint32_t getId() const { + virtual std::size_t getId() const { return id; } @@ -84,9 +84,9 @@ class Relation { * * Create an instance of iterator_base and set its ID to be arg_id. * - * @param arg_id ID of an iterator object (unit32_t) + * @param arg_id ID of an iterator object (std::size_t) */ - iterator_base(uint32_t arg_id) : id(arg_id) {} + iterator_base(std::size_t arg_id) : id(arg_id) {} /** * Destructor. diff --git a/src/include/souffle/SymbolTable.h b/src/include/souffle/SymbolTable.h index bb717785e34..31fce820fc6 100644 --- a/src/include/souffle/SymbolTable.h +++ b/src/include/souffle/SymbolTable.h @@ -115,7 +115,7 @@ class SymbolTable : protected FlyweightImpl { */ std::pair findOrInsert(const std::string& symbol) { auto Res = Base::findOrInsert(symbol); - return std::make_pair(Res.first, Res.second); + return std::make_pair(static_cast(Res.first), Res.second); } }; diff --git a/src/include/souffle/datastructure/BTree.h b/src/include/souffle/datastructure/BTree.h index c085636a679..47d2bc8226d 100644 --- a/src/include/souffle/datastructure/BTree.h +++ b/src/include/souffle/datastructure/BTree.h @@ -624,7 +624,7 @@ class btree { } } - pos = (i > other->numElements) ? 0 : i; + pos = (i > static_cast(other->numElements)) ? 0 : static_cast(i); other->insert_inner(root, root_lock, pos, predecessor, key, newNode, locked_nodes); #else other->insert_inner(root, root_lock, pos, predecessor, key, newNode); @@ -1340,7 +1340,8 @@ class btree { // split this node auto old_root = root; - idx -= cur->rebalance_or_split(const_cast(&root), root_lock, idx, parents); + idx -= cur->rebalance_or_split( + const_cast(&root), root_lock, static_cast(idx), parents); // release parent lock for (auto it = parents.rbegin(); it != parents.rend(); ++it) { @@ -1372,7 +1373,7 @@ class btree { assert(cur->numElements < node::maxKeys && "Split required!"); // move keys - for (int j = cur->numElements; j > idx; --j) { + for (int j = static_cast(cur->numElements); j > static_cast(idx); --j) { cur->keys[j] = cur->keys[j - 1]; } @@ -1960,7 +1961,7 @@ class btree { const int N = node::maxKeys; // divide range in N+1 sub-ranges - int length = (b - a) + 1; + int64_t length = (b - a) + 1; // terminal case: length is less then maxKeys if (length <= N) { @@ -1977,7 +1978,7 @@ class btree { // recursive case - compute step size int numKeys = N; - int step = ((length - numKeys) / (numKeys + 1)); + int64_t step = ((length - numKeys) / (numKeys + 1)); while (numKeys > 1 && (step < N / 2)) { numKeys--; diff --git a/src/include/souffle/datastructure/BTreeDelete.h b/src/include/souffle/datastructure/BTreeDelete.h index 3f9391bb901..be970ad5a32 100644 --- a/src/include/souffle/datastructure/BTreeDelete.h +++ b/src/include/souffle/datastructure/BTreeDelete.h @@ -904,8 +904,10 @@ class btree_delete { /** * The iterator type to be utilized for scanning through btree instances. */ - class iterator : public std::iterator { - public: + class iterator { + friend class souffle::detail::btree_delete; + // a pointer to the node currently referred to // node const* cur; node* cur; @@ -913,6 +915,7 @@ class btree_delete { // the index of the element currently addressed within the referenced node field_index_type pos = 0; + public: using iterator_category = std::forward_iterator_tag; using value_type = Key; using difference_type = ptrdiff_t; @@ -1578,7 +1581,7 @@ class btree_delete { } else { auto lower_iter = internal_lower_bound(k); if (lower_iter != end() && equal(*lower_iter, k)) { - return distance(lower_iter, internal_upper_bound(k)); + return std::distance(lower_iter, internal_upper_bound(k)); } else { return 0; } @@ -1605,7 +1608,7 @@ class btree_delete { } else { iterator lower_iter = internal_lower_bound(k); if (lower_iter != end() && equal(*lower_iter, k)) { - size_type count = distance(lower_iter, internal_upper_bound(k)); + size_type count = std::distance(lower_iter, internal_upper_bound(k)); for (size_type i = 0; i < count; i++) { erase(lower_iter); } diff --git a/src/include/souffle/datastructure/Brie.h b/src/include/souffle/datastructure/Brie.h index d01cf67ec3d..dbf579db8e8 100644 --- a/src/include/souffle/datastructure/Brie.h +++ b/src/include/souffle/datastructure/Brie.h @@ -1612,7 +1612,7 @@ class SparseBitMapIter { /** * A sparse bit-map is a bit map virtually assigning a bit value to every value if the - * uint32_t domain. However, only 1-bits are stored utilizing a nested sparse array + * uint64_t domain. However, only 1-bits are stored utilizing a nested sparse array * structure. * * @tparam BITS similar to the BITS parameter of the sparse array type @@ -1640,7 +1640,7 @@ class SparseBitMap { // some constants for manipulating stored values static constexpr std::size_t BITS_PER_ENTRY = sizeof(value_t) * CHAR_BIT; - static constexpr std::size_t LEAF_INDEX_WIDTH = __builtin_ctz(BITS_PER_ENTRY); + static constexpr std::size_t LEAF_INDEX_WIDTH = __builtin_ctz(static_cast(BITS_PER_ENTRY)); static constexpr uint64_t LEAF_INDEX_MASK = BITS_PER_ENTRY - 1; static uint64_t toMask(const value_t& value) { @@ -1936,8 +1936,8 @@ class TrieIterator { // remove ref-qual (if any); this can happen if we're a iterator-view using iter_core_arg_type = typename std::remove_reference_t::store_iter; - Value value; // the value currently pointed to - IterCore iter_core; // the wrapped iterator + Value value = {}; // the value currently pointed to + IterCore iter_core = {}; // the wrapped iterator // return an ephemeral nested iterator-view (view -> mutating us mutates our parent) // NB: be careful that the lifetime of this iterator-view doesn't exceed that of its parent. @@ -2887,9 +2887,9 @@ class Trie : public TrieBase> { if (this->empty()) return res; // use top-level elements for partitioning - int step = std::max(store.size() / chunks, std::size_t(1)); + size_t step = std::max(store.size() / chunks, std::size_t(1)); - int c = 1; + size_t c = 1; auto priv = begin(); for (auto it = store.begin(); it != store.end(); ++it, c++) { if (c % step != 0 || c == 1) { diff --git a/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h b/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h index 0f3d274d2bf..d9f074690e6 100644 --- a/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h +++ b/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h @@ -46,7 +46,7 @@ static uint64_t GreaterOrEqualPrime(const uint64_t LowerBound) { for (std::size_t I = 0; I < ToPrime.size(); ++I) { const uint64_t N = ToPrime[I].first; const uint64_t K = ToPrime[I].second; - const uint64_t Prime = (1UL << N) - K; + const uint64_t Prime = (1ULL << N) - K; if (Prime >= LowerBound) { return Prime; } @@ -145,7 +145,7 @@ class ConcurrentInsertOnlyHashMap { } LoadFactor = 1.0; Buckets = std::make_unique[]>(BucketCount); - MaxSizeBeforeGrow = std::ceil(LoadFactor * (double)BucketCount); + MaxSizeBeforeGrow = static_cast(std::ceil(LoadFactor * (double)BucketCount)); } ConcurrentInsertOnlyHashMap(const Hash& hash = Hash(), const KeyEqual& key_equal = KeyEqual(), @@ -412,12 +412,14 @@ class ConcurrentInsertOnlyHashMap { // Chose a prime number of buckets that ensures the desired load factor // given the current number of elements in the map. const std::size_t CurrentSize = Size; - const std::size_t NeededBucketCount = std::ceil((double)CurrentSize / LoadFactor); + assert(LoadFactor > 0); + const std::size_t NeededBucketCount = + static_cast(std::ceil(static_cast(CurrentSize) / LoadFactor)); std::size_t NewBucketCount = NeededBucketCount; for (std::size_t I = 0; I < details::ToPrime.size(); ++I) { const uint64_t N = details::ToPrime[I].first; const uint64_t K = details::ToPrime[I].second; - const uint64_t Prime = (1UL << N) - K; + const uint64_t Prime = (1ULL << N) - K; if (Prime >= NeededBucketCount) { NewBucketCount = Prime; break; @@ -451,7 +453,8 @@ class ConcurrentInsertOnlyHashMap { Buckets = std::move(NewBuckets); BucketCount = NewBucketCount; - MaxSizeBeforeGrow = ((double)NewBucketCount * LoadFactor); + MaxSizeBeforeGrow = + static_cast(std::ceil(static_cast(NewBucketCount) * LoadFactor)); } Lanes.beforeUnlockAllBut(H); diff --git a/src/include/souffle/datastructure/LambdaBTree.h b/src/include/souffle/datastructure/LambdaBTree.h index da51a1c69e8..8f7180c01dc 100644 --- a/src/include/souffle/datastructure/LambdaBTree.h +++ b/src/include/souffle/datastructure/LambdaBTree.h @@ -313,8 +313,8 @@ class LambdaBTree : public btreeroot; - idx -= cur->rebalance_or_split( - const_cast(&this->root), this->root_lock, idx, parents); + idx -= cur->rebalance_or_split(const_cast(&this->root), + this->root_lock, static_cast(idx), parents); // release parent lock for (auto it = parents.rbegin(); it != parents.rend(); ++it) { @@ -346,7 +346,7 @@ class LambdaBTree : public btreenumElements < parenttype::node::maxKeys && "Split required!"); // move keys - for (int j = cur->numElements; j > idx; --j) { + for (int j = static_cast(cur->numElements); j > idx; --j) { cur->keys[j] = cur->keys[j - 1]; } @@ -444,8 +444,8 @@ class LambdaBTree : public btreenumElements >= parenttype::node::maxKeys) { // split this node - idx -= cur->rebalance_or_split( - const_cast(&this->root), this->root_lock, idx); + idx -= cur->rebalance_or_split(const_cast(&this->root), + this->root_lock, static_cast(idx)); // insert element in right fragment if (((typename parenttype::size_type)idx) > cur->numElements) { diff --git a/src/include/souffle/datastructure/PiggyList.h b/src/include/souffle/datastructure/PiggyList.h index 3d8d2f1d0c5..91efd5b5895 100644 --- a/src/include/souffle/datastructure/PiggyList.h +++ b/src/include/souffle/datastructure/PiggyList.h @@ -9,18 +9,14 @@ #include #ifdef _WIN32 +#include /** * Some versions of MSVC do not provide a builtin for counting leading zeroes * like gcc, so we have to implement it ourselves. */ #if defined(_MSC_VER) -unsigned long __inline __builtin_clzll(unsigned long long value) { - unsigned long msb = 0; - - if (_BitScanReverse64(&msb, value)) - return 63 - msb; - else - return 64; +int __inline __builtin_clzll(unsigned long long value) { + return static_cast(__lzcnt64(value)); } #endif // _MSC_VER #endif // _WIN32 @@ -112,7 +108,7 @@ class RandomInsertPiggyList { numElements.store(0); } const std::size_t BLOCKBITS = 16ul; - const std::size_t INITIALBLOCKSIZE = (1ul << BLOCKBITS); + const std::size_t INITIALBLOCKSIZE = (((std::size_t)1ul) << BLOCKBITS); // number of elements currently stored within std::atomic numElements{0}; @@ -252,11 +248,17 @@ class PiggyList { container_size = 0; } - class iterator : std::iterator { + class iterator { std::size_t cIndex = 0; PiggyList* bl; public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = void; + using pointer = T*; + using reference = T&; + // default ctor, to silence iterator() = default; @@ -299,7 +301,7 @@ class PiggyList { return iterator(this, size()); } const std::size_t BLOCKBITS = 16ul; - const std::size_t BLOCKSIZE = (1ul << BLOCKBITS); + const std::size_t BLOCKSIZE = (((std::size_t)1ul) << BLOCKBITS); // number of inserted std::atomic num_containers = 0; diff --git a/src/include/souffle/datastructure/Table.h b/src/include/souffle/datastructure/Table.h index 08627d33da3..9e299ad8fae 100644 --- a/src/include/souffle/datastructure/Table.h +++ b/src/include/souffle/datastructure/Table.h @@ -49,11 +49,17 @@ class Table { std::size_t count = 0; public: - class iterator : public std::iterator { + class iterator { Block* block; unsigned pos; public: + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = void; + using pointer = T*; + using reference = T&; + iterator(Block* block = nullptr, unsigned pos = 0) : block(block), pos(pos) {} iterator(const iterator&) = default; diff --git a/src/include/souffle/io/ReadStreamCSV.h b/src/include/souffle/io/ReadStreamCSV.h index 223879eb4e7..5d96b751966 100644 --- a/src/include/souffle/io/ReadStreamCSV.h +++ b/src/include/souffle/io/ReadStreamCSV.h @@ -347,8 +347,8 @@ class ReadFileCSV : public ReadStreamCSV { */ static std::string getFileName(const std::map& rwOperation) { auto name = getOr(rwOperation, "filename", rwOperation.at("name") + ".facts"); - if (name.front() != '/') { - name = getOr(rwOperation, "fact-dir", ".") + "/" + name; + if (!isAbsolute(name)) { + name = getOr(rwOperation, "fact-dir", ".") + pathSeparator + name; } return name; } diff --git a/src/include/souffle/io/WriteStreamSQLite.h b/src/include/souffle/io/WriteStreamSQLite.h index 50fa9978efe..240b6da2252 100644 --- a/src/include/souffle/io/WriteStreamSQLite.h +++ b/src/include/souffle/io/WriteStreamSQLite.h @@ -65,9 +65,11 @@ class WriteStreamSQLite : public WriteStream { } #if RAM_DOMAIN_SIZE == 64 - if (sqlite3_bind_int64(insertStatement, i + 1, value) != SQLITE_OK) { + if (sqlite3_bind_int64(insertStatement, static_cast(i + 1), + static_cast(value)) != SQLITE_OK) { #else - if (sqlite3_bind_int(insertStatement, i + 1, value) != SQLITE_OK) { + if (sqlite3_bind_int(insertStatement, static_cast(i + 1), static_cast(value)) != + SQLITE_OK) { #endif throwError("SQLite error in sqlite3_bind_text: "); } @@ -102,7 +104,7 @@ class WriteStreamSQLite : public WriteStream { throw std::invalid_argument(error.str()); } - uint64_t getSymbolTableIDFromDB(int index) { + uint64_t getSymbolTableIDFromDB(std::size_t index) { if (sqlite3_bind_text(symbolSelectStatement, 1, symbolTable.decode(index).c_str(), -1, SQLITE_TRANSIENT) != SQLITE_OK) { throwError("SQLite error in sqlite3_bind_text: "); @@ -115,7 +117,7 @@ class WriteStreamSQLite : public WriteStream { sqlite3_reset(symbolSelectStatement); return rowid; } - uint64_t getSymbolTableID(int index) { + uint64_t getSymbolTableID(std::size_t index) { if (dbSymbolTable.count(index) != 0) { return dbSymbolTable[index]; } diff --git a/src/include/souffle/io/gzfstream.h b/src/include/souffle/io/gzfstream.h index f7ec488f5d5..bfa42a8adbd 100644 --- a/src/include/souffle/io/gzfstream.h +++ b/src/include/souffle/io/gzfstream.h @@ -91,8 +91,8 @@ class gzfstreambuf : public std::streambuf { *pptr() = c; pbump(1); } - int toWrite = pptr() - pbase(); - if (gzwrite(fileHandle, pbase(), toWrite) != toWrite) { + const int toWrite = static_cast(pptr() - pbase()); + if (gzwrite(fileHandle, pbase(), static_cast(toWrite)) != toWrite) { return EOF; } pbump(-toWrite); @@ -108,13 +108,14 @@ class gzfstreambuf : public std::streambuf { return traits_type::to_int_type(*gptr()); } - unsigned charsPutBack = gptr() - eback(); + std::size_t charsPutBack = gptr() - eback(); if (charsPutBack > reserveSize) { charsPutBack = reserveSize; } memcpy(buffer + reserveSize - charsPutBack, gptr() - charsPutBack, charsPutBack); - int charsRead = gzread(fileHandle, buffer + reserveSize, bufferSize - reserveSize); + int charsRead = + gzread(fileHandle, buffer + reserveSize, static_cast(bufferSize - reserveSize)); if (charsRead <= 0) { return EOF; } @@ -126,8 +127,8 @@ class gzfstreambuf : public std::streambuf { int sync() override { if ((pptr() != nullptr) && pptr() > pbase()) { - int toWrite = pptr() - pbase(); - if (gzwrite(fileHandle, pbase(), toWrite) != toWrite) { + const int toWrite = static_cast(pptr() - pbase()); + if (gzwrite(fileHandle, pbase(), static_cast(toWrite)) != toWrite) { return -1; } pbump(-toWrite); @@ -136,8 +137,8 @@ class gzfstreambuf : public std::streambuf { } private: - static constexpr unsigned int bufferSize = 65536; - static constexpr unsigned int reserveSize = 16; + static constexpr std::size_t bufferSize = 65536; + static constexpr std::size_t reserveSize = 16; char buffer[bufferSize] = {}; gzFile fileHandle = {}; diff --git a/src/include/souffle/profile/Iteration.h b/src/include/souffle/profile/Iteration.h index 54d629e5d9d..d2012fd259d 100644 --- a/src/include/souffle/profile/Iteration.h +++ b/src/include/souffle/profile/Iteration.h @@ -72,7 +72,7 @@ class Iteration { return numTuples; } - void setNumTuples(long numTuples) { + void setNumTuples(std::size_t numTuples) { this->numTuples = numTuples; } diff --git a/src/include/souffle/profile/ProfileEvent.h b/src/include/souffle/profile/ProfileEvent.h index b2fc175c413..4cc75c75dbf 100644 --- a/src/include/souffle/profile/ProfileEvent.h +++ b/src/include/souffle/profile/ProfileEvent.h @@ -43,10 +43,10 @@ namespace souffle { */ class ProfileEventSingleton { /** profile database */ - profile::ProfileDatabase database; + profile::ProfileDatabase database{}; std::string filename{""}; - ProfileEventSingleton() = default; + ProfileEventSingleton(){}; public: ~ProfileEventSingleton() { @@ -56,8 +56,8 @@ class ProfileEventSingleton { /** get instance */ static ProfileEventSingleton& instance() { - static ProfileEventSingleton singleton; - return singleton; + static std::unique_ptr singleton(new ProfileEventSingleton); + return *singleton; } /** create config record */ diff --git a/src/include/souffle/profile/Reader.h b/src/include/souffle/profile/Reader.h index f0450337330..1e93980c593 100644 --- a/src/include/souffle/profile/Reader.h +++ b/src/include/souffle/profile/Reader.h @@ -27,7 +27,9 @@ #include #include #include +#ifndef _MSC_VER #include +#endif #include namespace souffle { diff --git a/src/include/souffle/profile/Relation.h b/src/include/souffle/profile/Relation.h index a0c9accddae..b0bffd148a9 100644 --- a/src/include/souffle/profile/Relation.h +++ b/src/include/souffle/profile/Relation.h @@ -33,7 +33,7 @@ class Relation { std::chrono::microseconds endtime{}; std::chrono::microseconds loadtime{}; std::chrono::microseconds savetime{}; - long nonRecTuples = 0; + std::size_t nonRecTuples = 0; std::size_t preMaxRSS = 0; std::size_t postMaxRSS = 0; const std::string id; @@ -143,7 +143,7 @@ class Relation { endtime = time; } - void setNumTuples(long numTuples) { + void setNumTuples(std::size_t numTuples) { nonRecTuples = numTuples; } diff --git a/src/include/souffle/profile/Rule.h b/src/include/souffle/profile/Rule.h index cd3b848b0e2..c3754fb6ff2 100644 --- a/src/include/souffle/profile/Rule.h +++ b/src/include/souffle/profile/Rule.h @@ -49,7 +49,7 @@ class Rule { const std::string name; std::chrono::microseconds starttime{}; std::chrono::microseconds endtime{}; - long numTuples{0}; + std::size_t numTuples{0}; std::string identifier; std::string locator{}; std::set atoms; @@ -80,7 +80,7 @@ class Rule { return endtime; } - long size() { + std::size_t size() { return numTuples; } @@ -92,7 +92,7 @@ class Rule { endtime = time; } - void setNumTuples(long numTuples) { + void setNumTuples(std::size_t numTuples) { this->numTuples = numTuples; } diff --git a/src/include/souffle/profile/StringUtils.h b/src/include/souffle/profile/StringUtils.h index 2323cff673f..dd3eb6f1840 100644 --- a/src/include/souffle/profile/StringUtils.h +++ b/src/include/souffle/profile/StringUtils.h @@ -22,7 +22,10 @@ #include #include #include + +#ifndef _MSC_VER #include +#endif #include @@ -95,31 +98,31 @@ inline std::string formatNum(int precision, int64_t amount) { } inline std::string formatMemory(uint64_t kbytes) { - if (kbytes < 1024L * 2) { + if (kbytes < 1024UL * 2UL) { return std::to_string(kbytes) + "kB"; - } else if (kbytes < 1024L * 1024 * 2) { - return std::to_string(kbytes / 1024) + "MB"; - } else if (kbytes < 1024L * 1024 * 1024 * 2) { - return std::to_string(kbytes / (1024 * 1024)) + "GB"; + } else if (kbytes < 1024UL * 1024UL * 2UL) { + return std::to_string(kbytes / 1024UL) + "MB"; + } else if (kbytes < 1024UL * 1024UL * 1024UL * 2UL) { + return std::to_string(kbytes / (1024UL * 1024UL)) + "GB"; } - return std::to_string(kbytes / (1024 * 1024 * 1024)) + "TB"; + return std::to_string(kbytes / (1024UL * 1024UL * 1024UL)) + "TB"; } inline std::string formatTime(std::chrono::microseconds number) { uint64_t sec = number.count() / 1000000; if (sec >= 100) { - uint64_t min = std::floor(sec / 60); + uint64_t min = static_cast(std::floor(sec / 60)); if (min >= 100) { - uint64_t hours = std::floor(min / 60); + uint64_t hours = static_cast(std::floor(min / 60)); if (hours >= 100) { - uint64_t days = std::floor(hours / 24); + uint64_t days = static_cast(std::floor(hours / 24)); return std::to_string(days) + "D"; } return std::to_string(hours) + "h"; } if (min < 10) { // temp should always be 1 digit long - uint64_t temp = std::floor((sec - (min * 60.0)) * 10.0 / 6.0); + uint64_t temp = static_cast(std::floor((sec - (min * 60.0)) * 10.0 / 6.0)); return std::to_string(min) + "." + std::to_string(temp).substr(0, 1) + "m"; } return std::to_string(min) + "m"; diff --git a/src/include/souffle/profile/htmlJsChartistMin.h b/src/include/souffle/profile/htmlJsChartistMin.h index 8714242b42e..3dbd5ccfbc2 100644 --- a/src/include/souffle/profile/htmlJsChartistMin.h +++ b/src/include/souffle/profile/htmlJsChartistMin.h @@ -11,10 +11,11 @@ namespace souffle { namespace profile { namespace html { std::string jsChartistMin = R"___( -!function(a,b){"function"==typeof define&&define.amd?define("Chartist",[],function(){return a.Chartist=b()}):"object"==typeof exports?module.exports=b():a.Chartist=b()}(this,function(){var a={version:"0.10.0"};return function(a,b,c){"use strict";c.namespaces={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns/",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",ct:"http://gionkunz.github.com/chartist-js/ct"},c.noop=function(a){return a},c.alphaNumerate=function(a){return String.fromCharCode(97+a%26)},c.extend=function(a){var b,d,e;for(a=a||{},b=1;b":">",'"':""","'":"'"},c.serialize=function(a){return null===a||void 0===a?a:("number"==typeof a?a=""+a:"object"==typeof a&&(a=JSON.stringify({data:a})),Object.keys(c.escapingMap).reduce(function(a,b){return c.replaceAll(a,b,c.escapingMap[b])},a))},c.deserialize=function(a){if("string"!=typeof a)return a;a=Object.keys(c.escapingMap).reduce(function(a,b){return c.replaceAll(a,c.escapingMap[b],b)},a);try{a=JSON.parse(a),a=void 0!==a.data?a.data:a}catch(b){}return a},c.createSvg=function(a,b,d,e){var f;return b=b||"100%",d=d||"100%",Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function(a){return a.getAttributeNS(c.namespaces.xmlns,"ct")}).forEach(function(b){a.removeChild(b)}),f=new c.Svg("svg").attr({width:b,height:d}).addClass(e).attr({style:"width: "+b+"; height: "+d+";"}),a.appendChild(f._node),f},c.normalizeData=function(a,b,d){var e,f={raw:a,normalized:{}};return f.normalized.series=c.getDataArray({series:a.series||[]},b,d),e=f.normalized.series.every(function(a){return a instanceof Array})?Math.max.apply(null,f.normalized.series.map(function(a){return a.length})):f.normalized.series.length,f.normalized.labels=(a.labels||[]).slice(),Array.prototype.push.apply(f.normalized.labels,c.times(Math.max(0,e-f.normalized.labels.length)).map(function(){return""})),b&&c.reverseData(f.normalized),f},c.safeHasProperty=function(a,b){return null!==a&&"object"==typeof a&&a.hasOwnProperty(b)},c.isDataHoleValue=function(a){return null===a||void 0===a||"number"==typeof a&&isNaN(a)},c.reverseData=function(a){a.labels.reverse(),a.series.reverse();for(var b=0;bf.high&&(f.high=c),h&&c0?f.low=0:(f.high=1,f.low=0)),f},c.isNumeric=function(a){return null!==a&&isFinite(a)},c.isFalseyButZero=function(a){return!a&&0!==a},c.getNumberOrUndefined=function(a){return c.isNumeric(a)?+a:void 0},c.isMultiValue=function(a){return"object"==typeof a&&("x"in a||"y"in a)},c.getMultiValue=function(a,b){return c.isMultiValue(a)?c.getNumberOrUndefined(a[b||"y"]):c.getNumberOrUndefined(a)},c.rho=function(a){function b(a,c){return a%c===0?c:b(c,a%c)}function c(a){return a*a+1}if(1===a)return a;var d,e=2,f=2;if(a%2===0)return 2;do e=c(e)%a,f=c(c(f))%a,d=b(Math.abs(e-f),a);while(1===d);return d},c.getBounds=function(a,b,d,e){function f(a,b){return a===(a+=b)&&(a*=1+(b>0?o:-o)),a}var g,h,i,j=0,k={high:b.high,low:b.low};k.valueRange=k.high-k.low,k.oom=c.orderOfMagnitude(k.valueRange),k.step=Math.pow(10,k.oom),k.min=Math.floor(k.low/k.step)*k.step,k.max=Math.ceil(k.high/k.step)*k.step,k.range=k.max-k.min,k.numberOfSteps=Math.round(k.range/k.step);var l=c.projectLength(a,k.step,k),m=l=d)k.step=1;else if(e&&n=d)k.step=n;else for(;;){if(m&&c.projectLength(a,k.step,k)<=d)k.step*=2;else{if(m||!(c.projectLength(a,k.step/2,k)>=d))break;if(k.step/=2,e&&k.step%1!==0){k.step*=2;break}}if(j++>1e3)throw new Error("Exceeded maximum number of iterations while optimizing scale step!")}var o=2.221e-16;for(k.step=Math.max(k.step,o),h=k.min,i=k.max;h+k.step<=k.low;)h=f(h,k.step);for(;i-k.step>=k.high;)i=f(i,-k.step);k.min=h,k.max=i,k.range=k.max-k.min;var p=[];for(g=k.min;g<=k.max;g=f(g,k.step)){var q=c.roundWithPrecision(g);q!==p[p.length-1]&&p.push(q)}return k.values=p,k},c.polarToCartesian=function(a,b,c,d){var e=(d-90)*Math.PI/180;return{x:a+c*Math.cos(e),y:b+c*Math.sin(e)}},c.createChartRect=function(a,b,d){var e=!(!b.axisX&&!b.axisY),f=e?b.axisY.offset:0,g=e?b.axisX.offset:0,h=a.width()||c.quantity(b.width).value||0,i=a.height()||c.quantity(b.height).value||0,j=c.normalizePadding(b.chartPadding,d);h=Math.max(h,f+j.left+j.right),i=Math.max(i,g+j.top+j.bottom);var k={padding:j,width:function(){return this.x2-this.x1},height:function(){return this.y1-this.y2}};return e?("start"===b.axisX.position?(k.y2=j.top+g,k.y1=Math.max(i-j.bottom,k.y2+1)):(k.y2=j.top,k.y1=Math.max(i-j.bottom-g,k.y2+1)),"start"===b.axisY.position?(k.x1=j.left+f,k.x2=Math.max(h-j.right,k.x1+1)):(k.x1=j.left,k.x2=Math.max(h-j.right-f,k.x1+1))):(k.x1=j.left,k.x2=Math.max(h-j.right,k.x1+1),k.y2=j.top,k.y1=Math.max(i-j.bottom,k.y2+1)),k},c.createGrid=function(a,b,d,e,f,g,h,i){var j={};j[d.units.pos+"1"]=a,j[d.units.pos+"2"]=a,j[d.counterUnits.pos+"1"]=e,j[d.counterUnits.pos+"2"]=e+f;var k=g.elem("line",j,h.join(" "));i.emit("draw",c.extend({type:"grid",axis:d,index:b,group:g,element:k},j))},c.createGridBackground=function(a,b,c,d){var e=a.elem("rect",{x:b.x1,y:b.y2,width:b.width(),height:b.height()},c,!0);d.emit("draw",{type:"gridBackground",group:a,element:e})},c.createLabel=function(a,b,d,e,f,g,h,i,j,k,l){var m,n={};if(n[f.units.pos]=a+h[f.units.pos],n[f.counterUnits.pos]=h[f.counterUnits.pos],n[f.units.len]=b,n[f.counterUnits.len]=Math.max(0,g-10),k){var o=''+e[d]+"";m=i.foreignObject(o,c.extend({style:"overflow: visible;"},n))}else m=i.elem("text",n,j.join(" ")).text(e[d]);l.emit("draw",c.extend({type:"label",axis:f,index:d,group:i,element:m,text:e[d]},n))},c.getSeriesOption=function(a,b,c){if(a.name&&b.series&&b.series[a.name]){var d=b.series[a.name];return d.hasOwnProperty(c)?d[c]:b[c]}return b[c]},c.optionsProvider=function(b,d,e){function f(b){var f=h;if(h=c.extend({},j),d)for(i=0;i=2&&a[h]<=a[h-2]&&(g=!0),g&&(f.push({pathCoordinates:[],valueData:[]}),g=!1),f[f.length-1].pathCoordinates.push(a[h],a[h+1]),f[f.length-1].valueData.push(b[h/2]));return f}}(window,document,a),function(a,b,c){"use strict";c.Interpolation={},c.Interpolation.none=function(a){var b={fillHoles:!1};return a=c.extend({},b,a),function(b,d){for(var e=new c.Svg.Path,f=!0,g=0;g1){var i=[];return h.forEach(function(a){i.push(f(a.pathCoordinates,a.valueData))}),c.Svg.Path.join(i)}if(b=h[0].pathCoordinates,g=h[0].valueData,b.length<=4)return c.Interpolation.none()(b,g);for(var j,k=(new c.Svg.Path).move(b[0],b[1],!1,g[0]),l=0,m=b.length;m-2*!j>l;l+=2){var n=[{x:+b[l-2],y:+b[l-1]},{x:+b[l],y:+b[l+1]},{x:+b[l+2],y:+b[l+3]},{x:+b[l+4],y:+b[l+5]}];j?l?m-4===l?n[3]={x:+b[0],y:+b[1]}:m-2===l&&(n[2]={x:+b[0],y:+b[1]},n[3]={x:+b[2],y:+b[3]}):n[0]={x:+b[m-2],y:+b[m-1]}:m-4===l?n[3]=n[2]:l||(n[0]={x:+b[l],y:+b[l+1]}),k.curve(d*(-n[0].x+6*n[1].x+n[2].x)/6+e*n[2].x,d*(-n[0].y+6*n[1].y+n[2].y)/6+e*n[2].y,d*(n[1].x+6*n[2].x-n[3].x)/6+e*n[2].x,d*(n[1].y+6*n[2].y-n[3].y)/6+e*n[2].y,n[2].x,n[2].y,!1,g[(l+2)/2])}return k}return c.Interpolation.none()([])}},c.Interpolation.monotoneCubic=function(a){var b={fillHoles:!1};return a=c.extend({},b,a),function d(b,e){var f=c.splitIntoSegments(b,e,{fillHoles:a.fillHoles,increasingX:!0});if(f.length){if(f.length>1){var g=[];return f.forEach(function(a){g.push(d(a.pathCoordinates,a.valueData))}),c.Svg.Path.join(g)}if(b=f[0].pathCoordinates,e=f[0].valueData,b.length<=4)return c.Interpolation.none()(b,e);var h,i,j=[],k=[],l=b.length/2,m=[],n=[],o=[],p=[];for(h=0;h0!=n[h]>0?m[h]=0:(m[h]=3*(p[h-1]+p[h])/((2*p[h]+p[h-1])/n[h-1]+(p[h]+2*p[h-1])/n[h]),isFinite(m[h])||(m[h]=0));for(i=(new c.Svg.Path).move(j[0],k[0],!1,e[0]),h=0;h1}).map(function(a){var b=a.pathElements[0],c=a.pathElements[a.pathElements.length-1];return a.clone(!0).position(0).remove(1).move(b.x,r).line(b.x,b.y).position(a.pathElements.length+1).line(c.x,r)}).forEach(function(c){var h=i.elem("path",{d:c.stringify()},a.classNames.area,!0);this.eventEmitter.emit("draw",{type:"area",values:b.normalized.series[g],path:c.clone(),series:f,seriesIndex:g,axisX:d,axisY:e,chartRect:j,index:g,group:i,element:h})}.bind(this))}}.bind(this)),this.eventEmitter.emit("created",{bounds:e.bounds,chartRect:j,axisX:d,axisY:e,svg:this.svg,options:a})}function e(a,b,d,e){c.Line["super"].constructor.call(this,a,b,f,c.extend({},f,d),e)}var f={axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,type:void 0},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,type:void 0,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,showLine:!0,showPoint:!0,showArea:!1,areaBase:0,lineSmooth:!0,showGridBackground:!1,low:void 0,high:void 0,chartPadding:{top:15,right:15,bottom:5,left:10},fullWidth:!1,reverseData:!1,classNames:{chart:"ct-chart-line",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",line:"ct-line",point:"ct-point",area:"ct-area",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}};c.Line=c.Base.extend({constructor:e,createChart:d})}(window,document,a),function(a,b,c){"use strict";function d(a){var b,d;a.distributeSeries?(b=c.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),b.normalized.series=b.normalized.series.map(function(a){return[a]})):b=c.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),this.svg=c.createSvg(this.container,a.width,a.height,a.classNames.chart+(a.horizontalBars?" "+a.classNames.horizontalBars:""));var e=this.svg.elem("g").addClass(a.classNames.gridGroup),g=this.svg.elem("g"),h=this.svg.elem("g").addClass(a.classNames.labelGroup);if(a.stackBars&&0!==b.normalized.series.length){var i=c.serialMap(b.normalized.series,function(){return Array.prototype.slice.call(arguments).map(function(a){ +!function(a,b){"function"==typeof define&&define.amd?define("Chartist",[],function(){return a.Chartist=b()}):"object"==typeof exports?module.exports=b():a.Chartist=b()}(this,function(){var a={version:"0.10.0"};return function(a,b,c){"use strict";c.namespaces={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns/",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",ct:"http://gionkunz.github.com/chartist-js/ct"},c.noop=function(a){return a},c.alphaNumerate=function(a){return String.fromCharCode(97+a%26)},c.extend=function(a){var b,d,e;for(a=a||{},b=1;b":">",'"':""","'":"'"},c.serialize=function(a){return null===a||void 0===a?a:("number"==typeof a?a=""+a:"object"==typeof a&&(a=JSON.stringify({data:a})),Object.keys(c.escapingMap).reduce(function(a,b){return c.replaceAll(a,b,c.escapingMap[b])},a))},c.deserialize=function(a){if("string"!=typeof a)return a;a=Object.keys(c.escapingMap).reduce(function(a,b){return c.replaceAll(a,c.escapingMap[b],b)},a);try{a=JSON.parse(a),a=void 0!==a.data?a.data:a}catch(b){}return a},c.createSvg=function(a,b,d,e){var f;return b=b||"100%",d=d||"100%",Array.prototype.slice.call(a.querySelectorAll("svg")).filter(function(a){return a.getAttributeNS(c.namespaces.xmlns,"ct")}).forEach(function(b){a.removeChild(b)}),f=new c.Svg("svg").attr({width:b,height:d}).addClass(e).attr({style:"width: "+b+"; height: "+d+";"}),a.appendChild(f._node),f},c.normalizeData=function(a,b,d){var e,f={raw:a,normalized:{}};return f.normalized.series=c.getDataArray({series:a.series||[]},b,d),e=f.normalized.series.every(function(a){return a instanceof Array})?Math.max.apply(null,f.normalized.series.map(function(a){return a.length})):f.normalized.series.length,f.normalized.labels=(a.labels||[]).slice(),Array.prototype.push.apply(f.normalized.labels,c.times(Math.max(0,e-f.normalized.labels.length)).map(function(){return""})),b&&c.reverseData(f.normalized),f},c.safeHasProperty=function(a,b){return null!==a&&"object"==typeof a&&a.hasOwnProperty(b)},c.isDataHoleValue=function(a){return null===a||void 0===a||"number"==typeof a&&isNaN(a)},c.reverseData=function(a){a.labels.reverse(),a.series.reverse();for(var b=0;bf.high&&(f.high=c),h&&c0?f.low=0:(f.high=1,f.low=0)),f},c.isNumeric=function(a){return null!==a&&isFinite(a)},c.isFalseyButZero=function(a){return!a&&0!==a},c.getNumberOrUndefined=function(a){return c.isNumeric(a)?+a:void 0},c.isMultiValue=function(a){return"object"==typeof a&&("x"in a||"y"in a)},c.getMultiValue=function(a,b){return c.isMultiValue(a)?c.getNumberOrUndefined(a[b||"y"]):c.getNumberOrUndefined(a)},c.rho=function(a){function b(a,c){return a%c===0?c:b(c,a%c)}function c(a){return a*a+1}if(1===a)return a;var d,e=2,f=2;if(a%2===0)return 2;do e=c(e)%a,f=c(c(f))%a,d=b(Math.abs(e-f),a);while(1===d);return d},c.getBounds=function(a,b,d,e){function f(a,b){return a===(a+=b)&&(a*=1+(b>0?o:-o)),a}var g,h,i,j=0,k={high:b.high,low:b.low};k.valueRange=k.high-k.low,k.oom=c.orderOfMagnitude(k.valueRange),k.step=Math.pow(10,k.oom),k.min=Math.floor(k.low/k.step)*k.step,k.max=Math.ceil(k.high/k.step)*k.step,k.range=k.max-k.min,k.numberOfSteps=Math.round(k.range/k.step);var l=c.projectLength(a,k.step,k),m=l=d)k.step=1;else if(e&&n=d)k.step=n;else for(;;){if(m&&c.projectLength(a,k.step,k)<=d)k.step*=2;else{if(m||!(c.projectLength(a,k.step/2,k)>=d))break;if(k.step/=2,e&&k.step%1!==0){k.step*=2;break}}if(j++>1e3)throw new Error("Exceeded maximum number of iterations while optimizing scale step!")}var o=2.221e-16;for(k.step=Math.max(k.step,o),h=k.min,i=k.max;h+k.step<=k.low;)h=f(h,k.step);for(;i-k.step>=k.high;)i=f(i,-k.step);k.min=h,k.max=i,k.range=k.max-k.min;var p=[];for(g=k.min;g<=k.max;g=f(g,k.step)){var q=c.roundWithPrecision(g);q!==p[p.length-1]&&p.push(q)}return k.values=p,k},c.polarToCartesian=function(a,b,c,d){var e=(d-90)*Math.PI/180;return{x:a+c*Math.cos(e),y:b+c*Math.sin(e)}},c.createChartRect=function(a,b,d){var e=!(!b.axisX&&!b.axisY),f=e?b.axisY.offset:0,g=e?b.axisX.offset:0,h=a.width()||c.quantity(b.width).value||0,i=a.height()||c.quantity(b.height).value||0,j=c.normalizePadding(b.chartPadding,d);h=Math.max(h,f+j.left+j.right),i=Math.max(i,g+j.top+j.bottom);var k={padding:j,width:function(){return this.x2-this.x1},height:function(){return this.y1-this.y2}};return e?("start"===b.axisX.position?(k.y2=j.top+g,k.y1=Math.max(i-j.bottom,k.y2+1)):(k.y2=j.top,k.y1=Math.max(i-j.bottom-g,k.y2+1)),"start"===b.axisY.position?(k.x1=j.left+f,k.x2=Math.max(h-j.right,k.x1+1)):(k.x1=j.left,k.x2=Math.max(h-j.right-f,k.x1+1))):(k.x1=j.left,k.x2=Math.max(h-j.right,k.x1+1),k.y2=j.top,k.y1=Math.max(i-j.bottom,k.y2+1)),k},c.createGrid=function(a,b,d,e,f,g,h,i){var j={};j[d.units.pos+"1"]=a,j[d.units.pos+"2"]=a,j[d.counterUnits.pos+"1"]=e,j[d.counterUnits.pos+"2"]=e+f;var k=g.elem("line",j,h.join(" "));i.emit("draw",c.extend({type:"grid",axis:d,index:b,group:g,element:k},j))},c.createGridBackground=function(a,b,c,d){var e=a.elem("rect",{x:b.x1,y:b.y2,width:b.width(),height:b.height()},c,!0);d.emit("draw",{type:"gridBackground",group:a,element:e})},c.createLabel=function(a,b,d,e,f,g,h,i,j,k,l){var m,n={};if(n[f.units.pos]=a+h[f.units.pos],n[f.counterUnits.pos]=h[f.counterUnits.pos],n[f.units.len]=b,n[f.counterUnits.len]=Math.max(0,g-10),k){var o=''+e[d]+"";m=i.foreignObject(o,c.extend({style:"overflow: visible;"},n))}else m=i.elem("text",n,j.join(" ")).text(e[d]);l.emit("draw",c.extend({type:"label",axis:f,index:d,group:i,element:m,text:e[d]},n))},c.getSeriesOption=function(a,b,c){if(a.name&&b.series&&b.series[a.name]){var d=b.series[a.name];return d.hasOwnProperty(c)?d[c]:b[c]}return b[c]},c.optionsProvider=function(b,d,e){function f(b){var f=h;if(h=c.extend({},j),d)for(i=0;i=2&&a[h]<=a[h-2]&&(g=!0),g&&(f.push({pathCoordinates:[],valueData:[]}),g=!1),f[f.length-1].pathCoordinates.push(a[h],a[h+1]),f[f.length-1].valueData.push(b[h/2]));return f}}(window,document,a),function(a,b,c){"use strict";c.Interpolation={},c.Interpolation.none=function(a){var b={fillHoles:!1};return a=c.extend({},b,a),function(b,d){for(var e=new c.Svg.Path,f=!0,g=0;g1){var i=[];return h.forEach(function(a){i.push(f(a.pathCoordinates,a.valueData))}),c.Svg.Path.join(i)}if(b=h[0].pathCoordinates,g=h[0].valueData,b.length<=4)return c.Interpolation.none()(b,g);for(var j,k=(new c.Svg.Path).move(b[0],b[1],!1,g[0]),l=0,m=b.length;m-2*!j>l;l+=2){var n=[{x:+b[l-2],y:+b[l-1]},{x:+b[l],y:+b[l+1]},{x:+b[l+2],y:+b[l+3]},{x:+b[l+4],y:+b[l+5]}];j?l?m-4===l?n[3]={x:+b[0],y:+b[1]}:m-2===l&&(n[2]={x:+b[0],y:+b[1]},n[3]={x:+b[2],y:+b[3]}):n[0]={x:+b[m-2],y:+b[m-1]}:m-4===l?n[3]=n[2]:l||(n[0]={x:+b[l],y:+b[l+1]}),k.curve(d*(-n[0].x+6*n[1].x+n[2].x)/6+e*n[2].x,d*(-n[0].y+6*n[1].y+n[2].y)/6+e*n[2].y,d*(n[1].x+6*n[2].x-n[3].x)/6+e*n[2].x,d*(n[1].y+6*n[2].y-n[3].y)/6+e*n[2].y,n[2].x,n[2].y,!1,g[(l+2)/2])}return k}return c.Interpolation.none()([])}},c.Interpolation.monotoneCubic=function(a){var b={fillHoles:!1};return a=c.extend({},b,a),function d(b,e){var f=c.splitIntoSegments(b,e,{fillHoles:a.fillHoles,increasingX:!0});if(f.length){if(f.length>1){var g=[];return f.forEach(function(a){g.push(d(a.pathCoordinates,a.valueData))}),c.Svg.Path.join(g)}if(b=f[0].pathCoordinates,e=f[0].valueData,b.length<=4)return c.Interpolation.none()(b,e);var h,i,j=[],k=[],l=b.length/2,m=[],n=[],o=[],p=[];for(h=0;h0!=n[h]>0?m[h]=0:(m[h]=3*(p[h-1]+p[h])/((2*p[h]+p[h-1])/n[h-1]+(p[h]+2*p[h-1])/n[h]),isFinite(m[h])||(m[h]=0));for(i=(new c.Svg.Path).move(j[0],k[0],!1,e[0]),h=0;h1}).map(function(a){var b=a.pathElements[0],c=a.pathElements[a.pathElements.length-1];return a.clone(!0).position(0).remove(1).move(b.x,r).line(b.x,b.y).position(a.pathElements.length+1).line(c.x,r)}).forEach(function(c){var h=i.elem("path",{d:c.stringify()},a.classNames.area,!0);this.eventEmitter.emit("draw",{type:"area",values:b.normalized.series[g],path:c.clone(),series:f,seriesIndex:g,axisX:d,axisY:e,chartRect:j,index:g,group:i,element:h})}.bind(this))}}.bind(this)),this.eventEmitter.emit("created",{bounds:e.bounds,chartRect:j,axisX:d,axisY:e,svg:this.svg,options:a})}function e(a,b,d,e){c.Line["super"].constructor.call(this,a,b,f,c.extend({},f,d),e)}var f={axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,type:void 0},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,type:void 0,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,showLine:!0,showPoint:!0,showArea:!1,areaBase:0,lineSmooth:!0,showGridBackground:!1,low:void 0,high:void 0,chartPadding:{top:15,right:15,bottom:5,left:10},fullWidth:!1,reverseData:!1,)___" + R"___(classNames:{chart:"ct-chart-line",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",line:"ct-line",point:"ct-point",area:"ct-area",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}};c.Line=c.Base.extend({constructor:e,createChart:d})}(window,document,a),function(a,b,c){"use strict";function d(a){var b,d;a.distributeSeries?(b=c.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),b.normalized.series=b.normalized.series.map(function(a){return[a]})):b=c.normalizeData(this.data,a.reverseData,a.horizontalBars?"x":"y"),this.svg=c.createSvg(this.container,a.width,a.height,a.classNames.chart+(a.horizontalBars?" "+a.classNames.horizontalBars:""));var e=this.svg.elem("g").addClass(a.classNames.gridGroup),g=this.svg.elem("g"),h=this.svg.elem("g").addClass(a.classNames.labelGroup);if(a.stackBars&&0!==b.normalized.series.length){var i=c.serialMap(b.normalized.series,function(){return Array.prototype.slice.call(arguments).map(function(a){ return a}).reduce(function(a,b){return{x:a.x+(b&&b.x)||0,y:a.y+(b&&b.y)||0}},{x:0,y:0})});d=c.getHighLow([i],a,a.horizontalBars?"x":"y")}else d=c.getHighLow(b.normalized.series,a,a.horizontalBars?"x":"y");d.high=+a.high||(0===a.high?0:d.high),d.low=+a.low||(0===a.low?0:d.low);var j,k,l,m,n,o=c.createChartRect(this.svg,a,f.padding);k=a.distributeSeries&&a.stackBars?b.normalized.labels.slice(0,1):b.normalized.labels,a.horizontalBars?(j=m=void 0===a.axisX.type?new c.AutoScaleAxis(c.Axis.units.x,b.normalized.series,o,c.extend({},a.axisX,{highLow:d,referenceValue:0})):a.axisX.type.call(c,c.Axis.units.x,b.normalized.series,o,c.extend({},a.axisX,{highLow:d,referenceValue:0})),l=n=void 0===a.axisY.type?new c.StepAxis(c.Axis.units.y,b.normalized.series,o,{ticks:k}):a.axisY.type.call(c,c.Axis.units.y,b.normalized.series,o,a.axisY)):(l=m=void 0===a.axisX.type?new c.StepAxis(c.Axis.units.x,b.normalized.series,o,{ticks:k}):a.axisX.type.call(c,c.Axis.units.x,b.normalized.series,o,a.axisX),j=n=void 0===a.axisY.type?new c.AutoScaleAxis(c.Axis.units.y,b.normalized.series,o,c.extend({},a.axisY,{highLow:d,referenceValue:0})):a.axisY.type.call(c,c.Axis.units.y,b.normalized.series,o,c.extend({},a.axisY,{highLow:d,referenceValue:0})));var p=a.horizontalBars?o.x1+j.projectValue(0):o.y1-j.projectValue(0),q=[];l.createGridAndLabels(e,h,this.supportsForeignObject,a,this.eventEmitter),j.createGridAndLabels(e,h,this.supportsForeignObject,a,this.eventEmitter),a.showGridBackground&&c.createGridBackground(e,o,a.classNames.gridBackground,this.eventEmitter),b.raw.series.forEach(function(d,e){var f,h,i=e-(b.raw.series.length-1)/2;f=a.distributeSeries&&!a.stackBars?l.axisLength/b.normalized.series.length/2:a.distributeSeries&&a.stackBars?l.axisLength/2:l.axisLength/b.normalized.series[e].length/2,h=g.elem("g"),h.attr({"ct:series-name":d.name,"ct:meta":c.serialize(d.meta)}),h.addClass([a.classNames.series,d.className||a.classNames.series+"-"+c.alphaNumerate(e)].join(" ")),b.normalized.series[e].forEach(function(g,k){var r,s,t,u;if(u=a.distributeSeries&&!a.stackBars?e:a.distributeSeries&&a.stackBars?0:k,r=a.horizontalBars?{x:o.x1+j.projectValue(g&&g.x?g.x:0,k,b.normalized.series[e]),y:o.y1-l.projectValue(g&&g.y?g.y:0,u,b.normalized.series[e])}:{x:o.x1+l.projectValue(g&&g.x?g.x:0,u,b.normalized.series[e]),y:o.y1-j.projectValue(g&&g.y?g.y:0,k,b.normalized.series[e])},l instanceof c.StepAxis&&(l.options.stretch||(r[l.units.pos]+=f*(a.horizontalBars?-1:1)),r[l.units.pos]+=a.stackBars||a.distributeSeries?0:i*a.seriesBarDistance*(a.horizontalBars?-1:1)),t=q[k]||p,q[k]=t-(p-r[l.counterUnits.pos]),void 0!==g){var v={};v[l.units.pos+"1"]=r[l.units.pos],v[l.units.pos+"2"]=r[l.units.pos],!a.stackBars||"accumulate"!==a.stackMode&&a.stackMode?(v[l.counterUnits.pos+"1"]=p,v[l.counterUnits.pos+"2"]=r[l.counterUnits.pos]):(v[l.counterUnits.pos+"1"]=t,v[l.counterUnits.pos+"2"]=q[k]),v.x1=Math.min(Math.max(v.x1,o.x1),o.x2),v.x2=Math.min(Math.max(v.x2,o.x1),o.x2),v.y1=Math.min(Math.max(v.y1,o.y2),o.y1),v.y2=Math.min(Math.max(v.y2,o.y2),o.y1);var w=c.getMetaData(d,k);s=h.elem("line",v,a.classNames.bar).attr({"ct:value":[g.x,g.y].filter(c.isNumeric).join(","),"ct:meta":c.serialize(w)}),this.eventEmitter.emit("draw",c.extend({type:"bar",value:g,index:k,meta:w,series:d,seriesIndex:e,axisX:m,axisY:n,chartRect:o,group:h,element:s},v))}}.bind(this))}.bind(this)),this.eventEmitter.emit("created",{bounds:j.bounds,chartRect:o,axisX:m,axisY:n,svg:this.svg,options:a})}function e(a,b,d,e){c.Bar["super"].constructor.call(this,a,b,f,c.extend({},f,d),e)}var f={axisX:{offset:30,position:"end",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,scaleMinSpace:30,onlyInteger:!1},axisY:{offset:40,position:"start",labelOffset:{x:0,y:0},showLabel:!0,showGrid:!0,labelInterpolationFnc:c.noop,scaleMinSpace:20,onlyInteger:!1},width:void 0,height:void 0,high:void 0,low:void 0,referenceValue:0,chartPadding:{top:15,right:15,bottom:5,left:10},seriesBarDistance:15,stackBars:!1,stackMode:"accumulate",horizontalBars:!1,distributeSeries:!1,reverseData:!1,showGridBackground:!1,classNames:{chart:"ct-chart-bar",horizontalBars:"ct-horizontal-bars",label:"ct-label",labelGroup:"ct-labels",series:"ct-series",bar:"ct-bar",grid:"ct-grid",gridGroup:"ct-grids",gridBackground:"ct-grid-background",vertical:"ct-vertical",horizontal:"ct-horizontal",start:"ct-start",end:"ct-end"}};c.Bar=c.Base.extend({constructor:e,createChart:d})}(window,document,a),function(a,b,c){"use strict";function d(a,b,c){var d=b.x>a.x;return d&&"explode"===c||!d&&"implode"===c?"start":d&&"implode"===c||!d&&"explode"===c?"end":"middle"}function e(a){var b,e,f,h,i,j=c.normalizeData(this.data),k=[],l=a.startAngle;this.svg=c.createSvg(this.container,a.width,a.height,a.donut?a.classNames.chartDonut:a.classNames.chartPie),e=c.createChartRect(this.svg,a,g.padding),f=Math.min(e.width()/2,e.height()/2),i=a.total||j.normalized.series.reduce(function(a,b){return a+b},0);var m=c.quantity(a.donutWidth);"%"===m.unit&&(m.value*=f/100),f-=a.donut?m.value/2:0,h="outside"===a.labelPosition||a.donut?f:"center"===a.labelPosition?0:f/2,h+=a.labelOffset;var n={x:e.x1+e.width()/2,y:e.y2+e.height()/2},o=1===j.raw.series.filter(function(a){return a.hasOwnProperty("value")?0!==a.value:0!==a}).length;j.raw.series.forEach(function(a,b){k[b]=this.svg.elem("g",null,null)}.bind(this)),a.showLabel&&(b=this.svg.elem("g",null,null)),j.raw.series.forEach(function(e,g){if(0!==j.normalized.series[g]||!a.ignoreEmptyValues){k[g].attr({"ct:series-name":e.name}),k[g].addClass([a.classNames.series,e.className||a.classNames.series+"-"+c.alphaNumerate(g)].join(" "));var p=i>0?l+j.normalized.series[g]/i*360:0,q=Math.max(0,l-(0===g||o?0:.2));p-q>=359.99&&(p=q+359.99);var r=c.polarToCartesian(n.x,n.y,f,q),s=c.polarToCartesian(n.x,n.y,f,p),t=new c.Svg.Path((!a.donut)).move(s.x,s.y).arc(f,f,0,p-l>180,0,r.x,r.y);a.donut||t.line(n.x,n.y);var u=k[g].elem("path",{d:t.stringify()},a.donut?a.classNames.sliceDonut:a.classNames.slicePie);if(u.attr({"ct:value":j.normalized.series[g],"ct:meta":c.serialize(e.meta)}),a.donut&&u.attr({style:"stroke-width: "+m.value+"px"}),this.eventEmitter.emit("draw",{type:"slice",value:j.normalized.series[g],totalDataSum:i,index:g,meta:e.meta,series:e,group:k[g],element:u,path:t.clone(),center:n,radius:f,startAngle:l,endAngle:p}),a.showLabel){var v;v=1===j.raw.series.length?{x:n.x,y:n.y}:c.polarToCartesian(n.x,n.y,h,l+(p-l)/2);var w;w=j.normalized.labels&&!c.isFalseyButZero(j.normalized.labels[g])?j.normalized.labels[g]:j.normalized.series[g];var x=a.labelInterpolationFnc(w,g);if(x||0===x){var y=b.elem("text",{dx:v.x,dy:v.y,"text-anchor":d(n,v,a.labelDirection)},a.classNames.label).text(""+x);this.eventEmitter.emit("draw",{type:"label",index:g,group:b,element:y,text:""+x,x:v.x,y:v.y})}}l=p}}.bind(this)),this.eventEmitter.emit("created",{chartRect:e,svg:this.svg,options:a})}function f(a,b,d,e){c.Pie["super"].constructor.call(this,a,b,g,c.extend({},g,d),e)}var g={width:void 0,height:void 0,chartPadding:5,classNames:{chartPie:"ct-chart-pie",chartDonut:"ct-chart-donut",series:"ct-series",slicePie:"ct-slice-pie",sliceDonut:"ct-slice-donut",label:"ct-label"},startAngle:0,total:void 0,donut:!1,donutWidth:60,showLabel:!0,labelOffset:0,labelPosition:"inside",labelInterpolationFnc:c.noop,labelDirection:"neutral",reverseData:!1,ignoreEmptyValues:!1};c.Pie=c.Base.extend({constructor:f,createChart:e,determineAnchorPosition:d})}(window,document,a),a}); - )___"; } } // namespace profile diff --git a/src/include/souffle/profile/htmlJsMain.h b/src/include/souffle/profile/htmlJsMain.h index 7feb05a9d10..0f43d2e7d2a 100644 --- a/src/include/souffle/profile/htmlJsMain.h +++ b/src/include/souffle/profile/htmlJsMain.h @@ -454,7 +454,8 @@ function genRulVer() { document.getElementById("rulver").style.display = "block"; } - +)___" + R"___( function genAtomVer() { var atoms = data.atoms[selected.rul]; var table_body = document.getElementById('atoms_body'); diff --git a/src/include/souffle/utility/ContainerUtil.h b/src/include/souffle/utility/ContainerUtil.h index e08d99cc30e..6e250dbe136 100644 --- a/src/include/souffle/utility/ContainerUtil.h +++ b/src/include/souffle/utility/ContainerUtil.h @@ -219,6 +219,20 @@ bool equal_targets(const Container>& a, const Container>& b) { return equal_targets(a, b, comp_deref>()); } +#ifdef _MSC_VER +// issue: +// https://developercommunity.visualstudio.com/t/c-template-template-not-recognized-as-class-templa/558979 +template +bool equal_targets(const std::vector>& a, const std::vector>& b) { + return equal_targets(a, b, comp_deref>()); +} + +template +bool equal_targets(const std::vector& a, const std::vector& b) { + return equal_targets(a, b, comp_deref()); +} +#endif + /** * A function testing whether two maps of unique pointers are referencing to equivalent * targets. diff --git a/src/include/souffle/utility/FileUtil.h b/src/include/souffle/utility/FileUtil.h index df7e9c63d8a..fbb349788d8 100644 --- a/src/include/souffle/utility/FileUtil.h +++ b/src/include/souffle/utility/FileUtil.h @@ -16,36 +16,39 @@ #pragma once +#include #include #include #include +#include #include +#include +#include #include #include #include #include +// ------------------------------------------------------------------------------- +// File Utils +// ------------------------------------------------------------------------------- + #ifndef _WIN32 #include #else +#define NOMINMAX +#define NOGDI #include #include #include #include // ------------------------------------------------------------------------------- -// File Utils +// Windows // ------------------------------------------------------------------------------- -#define X_OK 1 /* execute permission - unsupported in windows*/ - #define PATH_MAX 260 -/** - * access and realpath are missing on windows, we use their windows equivalents - * as work-arounds. - */ -#define access _access inline char* realpath(const char* path, char* resolved_path) { return _fullpath(resolved_path, path, PATH_MAX); } @@ -57,19 +60,52 @@ inline char* realpath(const char* path, char* resolved_path) { #define pclose _pclose #endif +// ------------------------------------------------------------------------------- +// All systems +// ------------------------------------------------------------------------------- + namespace souffle { +// The separator in the PATH variable +#ifdef _MSC_VER +const char PATHdelimiter = ';'; +const char pathSeparator = '/'; +#else +const char PATHdelimiter = ':'; +const char pathSeparator = '/'; +#endif + +inline std::string& makePreferred(std::string& name) { + std::replace(name.begin(), name.end(), '\\', '/'); + // std::replace(name.begin(), name.end(), '/', pathSeparator); + return name; +} + +inline bool isAbsolute(const std::string& path) { + std::filesystem::path P(path); + return P.is_absolute(); +} + /** * Check whether a file exists in the file system */ inline bool existFile(const std::string& name) { + static std::map existFileCache{}; + auto it = existFileCache.find(name); + if (it != existFileCache.end()) { + return it->second; + } + std::filesystem::path P(name); + bool result = std::filesystem::exists(P); + /*bool result = false; struct stat buffer = {}; - if (stat(name.c_str(), &buffer) == 0) { + if (stat(P.native().c_str(), &buffer) == 0) { if ((buffer.st_mode & S_IFMT) != 0) { - return true; + result = true; } - } - return false; + }*/ + existFileCache[name] = result; + return result; } /** @@ -88,16 +124,24 @@ inline bool existDir(const std::string& name) { /** * Check whether a given file exists and it is an executable */ +#ifdef _WIN32 +inline bool isExecutable(const std::string& name) { + return existFile( + name); // there is no EXECUTABLE bit on Windows, so theoretically any file may be executable +} +#else inline bool isExecutable(const std::string& name) { return existFile(name) && (access(name.c_str(), X_OK) == 0); } +#endif /** * Simple implementation of a which tool */ inline std::string which(const std::string& name) { // Check if name has path components in it and if so return it immediately - if (name.find('/') != std::string::npos) { + std::filesystem::path P(name); + if (P.has_parent_path()) { return name; } // Get PATH from environment, if it exists. @@ -111,8 +155,8 @@ inline std::string which(const std::string& name) { std::string sub; // Check for existence of a binary called 'name' in PATH - while (std::getline(sstr, sub, ':')) { - std::string path = sub + "/" + name; + while (std::getline(sstr, sub, PATHdelimiter)) { + std::string path = sub + pathSeparator + name; if ((::realpath(path.c_str(), buf) != nullptr) && isExecutable(path) && !existDir(path)) { return buf; } @@ -127,19 +171,27 @@ inline std::string dirName(const std::string& name) { if (name.empty()) { return "."; } - std::size_t lastNotSlash = name.find_last_not_of('/'); + + std::filesystem::path P(name); + if (P.has_parent_path()) { + return P.parent_path().string(); + } else { + return "."; + } + + std::size_t lastNotSlash = name.find_last_not_of(pathSeparator); // All '/' if (lastNotSlash == std::string::npos) { return "/"; } - std::size_t leadingSlash = name.find_last_of('/', lastNotSlash); + std::size_t leadingSlash = name.find_last_of(pathSeparator, lastNotSlash); // No '/' if (leadingSlash == std::string::npos) { return "."; } // dirname is '/' if (leadingSlash == 0) { - return "/"; + return std::string(1, pathSeparator); } return name.substr(0, leadingSlash); } @@ -157,15 +209,17 @@ inline std::string absPath(const std::string& path) { * Join two paths together; note that this does not resolve overlaps or relative paths. */ inline std::string pathJoin(const std::string& first, const std::string& second) { - unsigned firstPos = static_cast(first.size()) - 1; - while (first.at(firstPos) == '/') { + return (std::filesystem::path(first) / std::filesystem::path(second)).string(); + + /*unsigned firstPos = static_cast(first.size()) - 1; + while (first.at(firstPos) == pathSeparator) { firstPos--; } unsigned secondPos = 0; - while (second.at(secondPos) == '/') { + while (second.at(secondPos) == pathSeparator) { secondPos++; } - return first.substr(0, firstPos + 1) + '/' + second.substr(secondPos); + return first.substr(0, firstPos + 1) + pathSeparator + second.substr(secondPos);*/ } /* @@ -173,18 +227,19 @@ inline std::string pathJoin(const std::string& first, const std::string& second) * relative to the directory given by @ base. A path here refers a * colon-separated list of directories. */ -inline std::string findTool(const std::string& tool, const std::string& base, const std::string& path) { - std::string dir = dirName(base); +inline std::optional findTool( + const std::string& tool, const std::string& base, const std::string& path) { + std::filesystem::path dir(dirName(base)); std::stringstream sstr(path); std::string sub; while (std::getline(sstr, sub, ':')) { - std::string subpath = dir + "/" + sub + '/' + tool; - if (isExecutable(subpath)) { - return absPath(subpath); + auto subpath = (dir / sub / tool); + if (std::filesystem::exists(subpath)) { + return absPath(subpath.string()); } } - return ""; + return {}; } /* @@ -195,12 +250,12 @@ inline std::string baseName(const std::string& filename) { return "."; } - std::size_t lastNotSlash = filename.find_last_not_of('/'); + std::size_t lastNotSlash = filename.find_last_not_of(pathSeparator); if (lastNotSlash == std::string::npos) { - return "/"; + return std::string(1, pathSeparator); } - std::size_t lastSlashBeforeBasename = filename.find_last_of('/', lastNotSlash - 1); + std::size_t lastSlashBeforeBasename = filename.find_last_of(pathSeparator, lastNotSlash - 1); if (lastSlashBeforeBasename == std::string::npos) { lastSlashBeforeBasename = static_cast(-1); } @@ -217,7 +272,7 @@ inline std::string simpleName(const std::string& path) { if (lastDot == std::string::npos) { return name; } - const std::size_t lastSlash = name.find_last_of('/'); + const std::size_t lastSlash = name.find_last_of(pathSeparator); // last slash occurs after last dot, so no extension if (lastSlash != std::string::npos && lastSlash > lastDot) { return name; @@ -236,7 +291,7 @@ inline std::string fileExtension(const std::string& path) { if (lastDot == std::string::npos) { return std::string(); } - const std::size_t lastSlash = name.find_last_of('/'); + const std::size_t lastSlash = name.find_last_of(pathSeparator); // last slash occurs after last dot, so no extension if (lastSlash != std::string::npos && lastSlash > lastDot) { return std::string(); @@ -250,10 +305,11 @@ inline std::string fileExtension(const std::string& path) { */ inline std::string tempFile() { #ifdef _WIN32 + char ctempl[L_tmpnam]; std::string templ; std::FILE* f = nullptr; while (f == nullptr) { - templ = std::tmpnam(nullptr); + templ = std::tmpnam(ctempl); f = fopen(templ.c_str(), "wx"); } fclose(f); @@ -268,13 +324,16 @@ inline std::string tempFile() { inline std::stringstream execStdOut(char const* cmd) { FILE* in = popen(cmd, "r"); std::stringstream data; - while (in != nullptr) { + + if (in == nullptr) { + return data; + } + + while (!feof(in)) { int c = fgetc(in); - if (feof(in) != 0) { - break; - } data << static_cast(c); } + pclose(in); return data; } diff --git a/src/include/souffle/utility/GetOptLong.h b/src/include/souffle/utility/GetOptLong.h new file mode 100644 index 00000000000..d8756d7f9b2 --- /dev/null +++ b/src/include/souffle/utility/GetOptLong.h @@ -0,0 +1,35 @@ +/* + * Souffle - A Datalog Compiler + * Copyright (c) 2022, The Souffle Developers. All rights reserved + * Licensed under the Universal Permissive License v 1.0 as shown at: + * - https://opensource.org/licenses/UPL + * - /licenses/SOUFFLE-UPL.txt + */ +#ifndef GET_OPT_LONG_H +#define GET_OPT_LONG_H + +// Points to the argument of the last option found. +extern char* optarg; + +// Index of the next element to processed in argv. +extern int optind; + +// Enables error message printing if opterr!=0. +extern int opterr; + +extern int optopt; + +// The long option descriptor, as described by man page for getopt_long. +struct option { + const char* name; // name of the long option. + int has_arg; // 0=no argument, 1=requires argument, 2=optional argument. + int* flag; // if non-null, the variable pointed by `flag` is set to `val` when getopt_long finds this + // option. + int val; // value to return or to load in the variable pointed by `flag` when this option is found. +}; + +// A limited implementation of POSIX getopt_long. +extern int getopt_long( + int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex); + +#endif diff --git a/src/include/souffle/utility/GetOptLongImpl.h b/src/include/souffle/utility/GetOptLongImpl.h new file mode 100644 index 00000000000..1e211e3b956 --- /dev/null +++ b/src/include/souffle/utility/GetOptLongImpl.h @@ -0,0 +1,289 @@ +/* + * Souffle - A Datalog Compiler + * Copyright (c) 2022, The Souffle Developers. All rights reserved + * Licensed under the Universal Permissive License v 1.0 as shown at: + * - https://opensource.org/licenses/UPL + * - /licenses/SOUFFLE-UPL.txt + */ + +// Implementation of getopt_long for Windows. +#pragma once +#ifdef USE_CUSTOM_GETOPTLONG + +#include "GetOptLong.h" +#include +#include + +char* optarg = nullptr; + +// the index of the next element to be processed in argv +int optind = 0; +int opterr = 1; +int optopt = 0; + +enum { no_argument = 0, required_argument = 1, optional_argument = 2 }; + +namespace { + +// nextchar points to the next option character in an element of argv +char* nextchar = nullptr; + +// value of optind at the previous call of getopt_long +int previous_optind = -1; + +// the number of non-options elements of argv skipped last time getopt was called +int nonopt_count = 0; + +int parse_long_option(const int argc, char* const argv[], const struct option* longopts, int* longindex, + const int print_error_message, const int missing_argument) { + char* const current = nextchar; + ++optind; + + char* const hasequal = strchr(current, '='); + size_t namelength = (hasequal ? (hasequal - current) : strlen(current)); + + int i; + int match = -1; + for (i = 0; longopts[i].name != nullptr; ++i) { + if (strncmp(longopts[i].name, current, namelength)) { + continue; + } + if (strlen(longopts[i].name) != namelength) { + continue; + } + + match = i; + break; + } + + if (match == -1) { + // cannot find long option + if (print_error_message) { + fprintf(stderr, "unknown option -- %.*s\n", static_cast(namelength), current); + } + optopt = 0; + return (int)'?'; + } + + if (longopts[match].has_arg == no_argument) { + // no argument expected + if (hasequal) { + if (print_error_message) { + fprintf(stderr, "unexpected argument -- %.*s\n", static_cast(namelength), current); + } + if (longopts[match].flag == nullptr) { + optopt = longopts[match].val; + } else { + optopt = 0; + } + return (int)'?'; + } + } + + if (longopts[match].has_arg == required_argument || longopts[match].has_arg == optional_argument) { + if (hasequal) { + // argument is in the same argv after '=' sign + optarg = hasequal + 1; + } else if (optind < argc) { + // Argument may be in next argv + // If argument is optional, leave optarg to null, user is in charge + // of verifying the value of argv[optind] and increment optind + // if the argument is valid. + if (longopts[match].has_arg == required_argument) { + // mandatory argument + optarg = argv[optind++]; + } + } else { + // no argument found + if (longopts[match].has_arg == required_argument) { + if (print_error_message) { + fprintf(stderr, "missing mandatory argument -- %.*s\n", static_cast(namelength), + current); + } + optopt = 0; + return missing_argument; + } + } + } // unexpected value of has_arg is not verified + + if (longindex) *longindex = match; + if (longopts[match].flag) { + *longopts[match].flag = longopts[match].val; + return 0; + } else { + return longopts[match].val; + } +} + +// permute argv[last] and argv[last-1] and recurse +void permute(char* argv[], int first, int last) { + if (first >= last) return; + char* tmp = argv[last]; + argv[last] = argv[last - 1]; + argv[last - 1] = tmp; + permute(argv, first, last - 1); +} + +void shift(char* argv[]) { + // done with reading options from argv[previous_optind]..argv[optind-1] + + int start = previous_optind; + for (int mv = previous_optind + nonopt_count; mv < optind; ++mv) { + permute(argv, start, mv); + ++start; + } + + optind -= nonopt_count; + previous_optind = optind; + nonopt_count = 0; +} + +} // anonymous namespace + +int getopt_long( + int argc, char* const argv[], const char* optstring, const struct option* longopts, int* longindex) { + if (optind == 0) { // full reset + nextchar = nullptr; + nonopt_count = 0; + optarg = nullptr; + optind = 1; + previous_optind = optind; + } + + int missing_argument = (int)'?'; + int print_error_message = opterr; + + optarg = nullptr; + + if (*optstring == '+' || *optstring == '-') { + throw "Mode +/- of optstring is not supported."; + ++optstring; + } + + if (*optstring == ':') { + missing_argument = (int)':'; + print_error_message = 0; + ++optstring; + } + + if (nextchar == nullptr) { // scan starting at argv[optind] + if (nonopt_count > 0) { // previous scan skipped over some non-option arguments + shift((char**)argv); + } else { + previous_optind = optind; + } + } + + if (optind >= argc) { + // all command-line arguments have been scanned + return -1; + } + + if (nextchar == nullptr) { // scan starting at argv[optind], skip over any non-option elements + while ((optind + nonopt_count < argc) && + (argv[optind + nonopt_count][0] != '-' || argv[optind + nonopt_count][1] == 0)) { + ++nonopt_count; + } + + if (optind + nonopt_count == argc) { + // no more options + nonopt_count = 0; + return -1; + } + + optind += nonopt_count; + } + + if (nextchar == nullptr && optind < argc) { // scan starting at argv[optind] + nextchar = argv[optind]; + } + + if (nextchar == argv[optind] && *nextchar == '-') { + ++nextchar; + if (*nextchar == '-' && nextchar[1] == 0) { + // double-dash marks the end of the option scan + nextchar = nullptr; + shift((char**)argv); + return -1; + } else if (*nextchar == '-' && *(++nextchar)) { + // search long option + optopt = + parse_long_option(argc, argv, longopts, longindex, print_error_message, missing_argument); + nextchar = nullptr; + return optopt; + } else if (*nextchar == 0) { + // missing option character + optind += 1; + nextchar = nullptr; + return -1; + } + } + + // search short option + const char* option; + optopt = *nextchar++; + if ((option = strchr(optstring, optopt)) == nullptr) { + // cannot find option + if (print_error_message) { + fprintf(stderr, "unknown option -- %c\n", optopt); + } + return (int)'?'; + } + ++option; + + if (*option++ != ':') { + // no argument required + if (!*nextchar) { + ++optind; + nextchar = nullptr; + } + } else { + if (*nextchar) { + // if argument is in the same argv, always set optarg + optarg = nextchar; + ++optind; + nextchar = nullptr; + } else if (argc <= ++optind) { + // no argument found + nextchar = nullptr; + optarg = nullptr; + + if (*option != ':') { + // mandatory argument is missing + if (print_error_message) { + fprintf(stderr, "missing mandatory argument -- %c\n", optopt); + } + return missing_argument; + } + } else { + // argument is in next argv + nextchar = nullptr; + + if (*option != ':' && + ((argv[optind][0] == '-' && (argv[optind][1] != 0 && argv[optind][1] != '-')) || + (argv[optind][0] == '-' && argv[optind][1] == '-' && argv[optind][2] != 0))) { + // argument is mandatory, but must not start with a dash or a double-dash + // or must be exactly dash or double-dash. + if (print_error_message) { + fprintf(stderr, "missing mandatory argument -- %c\n", optopt); + } + optarg = nullptr; + return missing_argument; + } + + if (*option != ':') { + // argument is mandatory + optarg = argv[optind++]; + } else { + // Argument is optional but not in the same argv, set optarg to null. + // User is in charge of interpreting argv[optind] and increment it + // if it considers its a valid argument. + optarg = nullptr; + } + } + } + + return optopt; +} + +#endif diff --git a/src/include/souffle/utility/Iteration.h b/src/include/souffle/utility/Iteration.h index 15d9d7c502e..1a37bcd46cb 100644 --- a/src/include/souffle/utility/Iteration.h +++ b/src/include/souffle/utility/Iteration.h @@ -26,7 +26,18 @@ namespace souffle { namespace detail { - +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4172) +#elif defined(__GNUC__) && (__GNUC__ >= 7) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-local-addr" +#elif defined(__has_warning) +#pragma clang diagnostic push +#if __has_warning("-Wreturn-stack-address") +#pragma clang diagnostic ignored "-Wreturn-stack-address" +#endif +#endif // This is a helper in the cases when the lambda is stateless template F makeFun() { @@ -34,9 +45,16 @@ F makeFun() { // Even thought the lambda is stateless, it has no default ctor // Is this gross? Yes, yes it is. // FIXME: Remove after C++20 - typename std::aligned_storage::type fakeLam; + typename std::aligned_storage::type fakeLam{}; return reinterpret_cast(fakeLam); } +#ifdef _MSC_VER +#pragma warning(pop) +#elif defined(__GNUC__) && (__GNUC__ >= 7) +#pragma GCC diagnostic pop +#elif defined(__has_warning) +#pragma clang diagnostic pop +#endif } // namespace detail // ------------------------------------------------------------- @@ -101,7 +119,7 @@ class TransformIterator { } /* Support for the pointer operator. */ - auto operator-> () const { + auto operator->() const { return &**this; } @@ -265,9 +283,9 @@ struct range { } // splits up this range into the given number of partitions - std::vector partition(int np = 100) { + std::vector partition(std::size_t np = 100) { // obtain the size - int n = 0; + std::size_t n = 0; for (auto i = a; i != b; ++i) { n++; } @@ -279,8 +297,8 @@ struct range { res.reserve(np); auto cur = a; auto last = cur; - int i = 0; - int p = 0; + std::size_t i = 0; + std::size_t p = 0; while (cur != b) { ++cur; i++; diff --git a/src/include/souffle/utility/MiscUtil.h b/src/include/souffle/utility/MiscUtil.h index 3f8348eeac2..c405e659d81 100644 --- a/src/include/souffle/utility/MiscUtil.h +++ b/src/include/souffle/utility/MiscUtil.h @@ -30,18 +30,13 @@ #include #ifdef _WIN32 +#define NOMINMAX +#define NOGDI #include #include #include #include -/** - * Windows headers define these and they interfere with the standard library - * functions. - */ -#undef min -#undef max - /** * On windows, the following gcc builtins are missing. * @@ -54,21 +49,34 @@ #define __builtin_popcountll __popcnt64 #if defined(_MSC_VER) -constexpr unsigned long __builtin_ctz(unsigned long value) { +// return the number of trailing zeroes in value, or 32 if value is zero. +inline constexpr unsigned long __builtin_ctz(unsigned long value) { unsigned long trailing_zeroes = 0; + if (value == 0) return 32; while ((value = value >> 1) ^ 1) { ++trailing_zeroes; } return trailing_zeroes; } -inline unsigned long __builtin_ctzll(unsigned long long value) { - unsigned long trailing_zero = 0; +// return the number of trailing zeroes in value, or 64 if value is zero. +inline constexpr int __builtin_ctzll_constexpr(unsigned long long value) { + int trailing_zeroes = 0; + + if (value == 0) return 64; + while ((value = value >> 1) ^ 1) { + ++trailing_zeroes; + } + return trailing_zeroes; +} + +inline int __builtin_ctzll(unsigned long long value) { + unsigned long trailing_zeroes = 0; - if (_BitScanForward64(&trailing_zero, value)) { - return trailing_zero; + if (_BitScanForward64(&trailing_zeroes, value)) { + return static_cast(trailing_zeroes); } else { - return 64; + return 64; // return 64 like GCC would when value == 0 } } #endif // _MSC_VER diff --git a/src/include/souffle/utility/ParallelUtil.h b/src/include/souffle/utility/ParallelUtil.h index 3cf6c060047..7d8a5cdc998 100644 --- a/src/include/souffle/utility/ParallelUtil.h +++ b/src/include/souffle/utility/ParallelUtil.h @@ -48,14 +48,28 @@ constexpr std::size_t hardware_destructive_interference_size = 2 * sizeof(max_al #include // pthread_yield is deprecated and should be replaced by sched_yield #define pthread_yield sched_yield +#elif defined _MSC_VER +#include +#define NOMINMAX +#include +#define pthread_yield std::this_thread::yield #endif +#ifdef _MSC_VER +// support for a parallel region +#define PARALLEL_START __pragma(omp parallel) { +#define PARALLEL_END } + +// support for parallel loops +#define pfor __pragma(omp for schedule(dynamic)) for +#else // support for a parallel region #define PARALLEL_START _Pragma("omp parallel") { #define PARALLEL_END } // support for parallel loops #define pfor _Pragma("omp for schedule(dynamic)") for +#endif // spawn and sync are processed sequentially (overhead to expensive) #define task_spawn @@ -221,11 +235,15 @@ class Lock { namespace detail { /* Pause instruction to prevent excess processor bus usage */ +#if defined _MSC_VER +#define cpu_relax() YieldProcessor() +#else #ifdef __x86_64__ #define cpu_relax() asm volatile("pause\n" : : : "memory") #else #define cpu_relax() asm volatile("" : : : "memory") #endif +#endif /** * A utility class managing waiting operations for spin locks. diff --git a/src/include/souffle/utility/SubProcess.h b/src/include/souffle/utility/SubProcess.h index 280ca4a75c6..7c17548507a 100644 --- a/src/include/souffle/utility/SubProcess.h +++ b/src/include/souffle/utility/SubProcess.h @@ -23,8 +23,14 @@ #include #include #include + +#ifdef _MSC_VER +#define NOMINMAX +#include +#else #include #include +#endif namespace souffle { @@ -59,6 +65,7 @@ template >, typename = std::enable_if_t const>>> std::optional execute( std::string const& program, span argv = {}, Envp&& envp = {}) { +#ifndef _MSC_VER using EC = detail::LinuxExitCode; auto pid = ::fork(); @@ -103,6 +110,63 @@ std::optional execute( return status; } } +#else + STARTUPINFOW si; + PROCESS_INFORMATION pi; + DWORD exit_code = 0; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); + + std::size_t l; + std::wstring program_w(program.length() + 1, L' '); + ::mbstowcs_s(&l, program_w.data(), program_w.size(), program.data(), program.size()); + program_w.resize(l - 1); + + WCHAR FoundPath[PATH_MAX]; + int64_t Found = (int64_t)FindExecutableW(program_w.c_str(), nullptr, FoundPath); + if (Found <= 32) { + std::cerr << "Cannot find executable '" << program << "'.\n"; + return {}; + } + + std::wstringstream args_w; + args_w << program_w; + for (const auto& arg : argv) { + std::string arg_s(arg); + std::wstring arg_w(arg_s.length() + 1, L' '); + ::mbstowcs_s(&l, arg_w.data(), arg_w.size(), arg_s.data(), arg_s.size()); + arg_w.resize(l - 1); + args_w << L' ' << arg_w; + } + + std::string envir; + for (const auto& couple : envp) { + envir += couple.first; + envir += '='; + envir += couple.second; + envir += '\0'; + } + envir += '\0'; + + if (!CreateProcessW(FoundPath, args_w.str().data(), NULL, NULL, FALSE, 0, /*envir.data()*/ nullptr, NULL, + &si, &pi)) { + return {}; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + if (!GetExitCodeProcess(pi.hProcess, &exit_code)) { + return {}; + } + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return static_cast(exit_code); + +#endif } /** @@ -127,10 +191,13 @@ std::optional execute( return span>{dst, dst + src.size()}; }; - char const* argv_temp[argv.size()]; - std::pair envp_temp[envp.size()]; - auto argv_ptr = go(argv_temp, argv, [](auto&& x) { return x.c_str(); }); - auto envp_ptr = go(envp_temp, envp, [](auto&& kv) { return std::pair{kv.first, kv.second.c_str()}; }); + std::unique_ptr argv_temp = std::make_unique(argv.size()); + std::unique_ptr[]> envp_temp = + std::make_unique[]>(envp.size()); + auto argv_ptr = go(argv_temp.get(), argv, [](auto&& x) { return x.c_str(); }); + auto envp_ptr = go(envp_temp.get(), envp, [](auto&& kv) { + return std::pair{kv.first, kv.second.c_str()}; + }); return souffle::execute(program, argv_ptr, envp_ptr); } diff --git a/src/include/souffle/utility/tinyformat.h b/src/include/souffle/utility/tinyformat.h index b26a9df4d8f..aa06c04875f 100644 --- a/src/include/souffle/utility/tinyformat.h +++ b/src/include/souffle/utility/tinyformat.h @@ -792,27 +792,29 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode break; case 'X': out.setf(std::ios::uppercase); - // Falls through - case 'x': case 'p': + [[fallthrough]]; + case 'x': + [[fallthrough]]; + case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); - // Falls through + [[fallthrough]]; case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); - // Falls through + [[fallthrough]]; case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'A': out.setf(std::ios::uppercase); - // Falls through + [[fallthrough]]; case 'a': # ifdef _MSC_VER // Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html @@ -824,7 +826,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode break; case 'G': out.setf(std::ios::uppercase); - // Falls through + [[fallthrough]]; case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. diff --git a/src/interpreter/Engine.cpp b/src/interpreter/Engine.cpp index 83b3ff7094d..bf9d004dc32 100644 --- a/src/interpreter/Engine.cpp +++ b/src/interpreter/Engine.cpp @@ -111,7 +111,13 @@ #include #include #include + +#ifdef _MSC_VER +#define dlopen(libname, flags) LoadLibrary((libname)) +#define dlsym(lib, fn) GetProcAddress(static_cast(lib), (fn)) +#else #include +#endif #ifdef USE_LIBFFI #include @@ -123,16 +129,11 @@ namespace souffle::interpreter { #ifdef __APPLE__ #define dynamicLibSuffix ".dylib"; #else +#ifdef _MSC_VER +#define dynamicLibSuffix ".dll"; +#else #define dynamicLibSuffix ".so"; #endif - -// Aliases for foreign function interface. -#if RAM_DOMAIN_SIZE == 64 -#define EXP_RamUnsigned RamUnsigned -#define EXP_RamSigned RamSigned -#else -#define EXP_RamUnsigned int64_t -#define EXP_RamSigned int64_t #endif namespace { @@ -141,7 +142,7 @@ constexpr RamDomain RAM_BIT_SHIFT_MASK = RAM_DOMAIN_SIZE - 1; #ifdef _OPENMP std::size_t number_of_threads(const std::size_t user_specified) { if (user_specified > 0) { - omp_set_num_threads(user_specified); + omp_set_num_threads(static_cast(user_specified)); return user_specified; } else { return omp_get_max_threads(); @@ -299,7 +300,7 @@ void Engine::swapRelation(const std::size_t ramRel1, const std::size_t ramRel2) std::swap(rel1, rel2); } -int Engine::incCounter() { +RamDomain Engine::incCounter() { return counter++; } @@ -364,12 +365,12 @@ const std::vector& Engine::loadDLL() { auto paths = Global::config().getMany("library-dir"); // Set up our paths to have a library appended for (std::string& path : paths) { - if (path.back() != '/') { - path += '/'; + if (path.back() != pathSeparator) { + path += pathSeparator; } } - if (library.find('/') != std::string::npos) { + if (library.find(pathSeparator) != std::string::npos) { paths.clear(); } @@ -453,7 +454,8 @@ void Engine::executeMain() { ProfileEventSingleton::instance().stopTimer(); for (auto const& cur : frequencies) { for (std::size_t i = 0; i < cur.second.size(); ++i) { - ProfileEventSingleton::instance().makeQuantityEvent(cur.first, cur.second[i], i); + ProfileEventSingleton::instance().makeQuantityEvent( + cur.first, cur.second[i], static_cast(i)); } } for (auto const& cur : reads) { @@ -613,8 +615,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { #define CONV_TO_STRING(op, ty) \ case FunctorOp::op: return getSymbolTable().encode(std::to_string(EVAL_CHILD(ty, 0))); #define CONV_FROM_STRING(op, ty) \ - case FunctorOp::op: return evaluator::symbol2numeric( \ - getSymbolTable().decode(EVAL_CHILD(RamDomain, 0))); + case FunctorOp::op: return ramBitCast(evaluator::symbol2numeric( \ + getSymbolTable().decode(EVAL_CHILD(RamDomain, 0)))); // clang-format on const auto& args = cur.getArguments(); @@ -676,22 +678,27 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { // clang-format on case FunctorOp::EXP: { - return ramBitCast(static_cast(static_cast( - std::pow(execute(shadow.getChild(0), ctxt), execute(shadow.getChild(1), ctxt))))); + auto first = ramBitCast(execute(shadow.getChild(0), ctxt)); + auto second = ramBitCast(execute(shadow.getChild(1), ctxt)); + // std::pow return a double + static_assert(std::is_same_v); + return ramBitCast(static_cast(std::pow(first, second))); } case FunctorOp::UEXP: { auto first = ramBitCast(execute(shadow.getChild(0), ctxt)); auto second = ramBitCast(execute(shadow.getChild(1), ctxt)); - // Extra casting required: pow returns a floating point. - return ramBitCast( - static_cast(static_cast(std::pow(first, second)))); + // std::pow return a double + static_assert(std::is_same_v); + return ramBitCast(static_cast(std::pow(first, second))); } case FunctorOp::FEXP: { auto first = ramBitCast(execute(shadow.getChild(0), ctxt)); auto second = ramBitCast(execute(shadow.getChild(1), ctxt)); - return ramBitCast(static_cast(std::pow(first, second))); + // std::pow return the same type as the float arguments + static_assert(std::is_same_v); + return ramBitCast(std::pow(first, second)); } // clang-format off @@ -820,8 +827,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { } #ifdef USE_LIBFFI // prepare dynamic call environment - void* values[arity + 2]; - RamDomain intVal[arity]; + std::unique_ptr values = std::make_unique(arity + 2); + std::unique_ptr intVal = std::make_unique(arity); RamDomain rc; /* Initialize arguments for ffi-call */ @@ -834,7 +841,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { values[i + 2] = &intVal[i]; } - ffi_call(shadow.getFFIcif(), userFunctor, &rc, values); + ffi_call(shadow.getFFIcif(), userFunctor, &rc, values.get()); return rc; #else fatal("unsupported stateful functor arity without libffi support"); @@ -852,11 +859,11 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { #ifdef USE_LIBFFI // prepare dynamic call environment - void* values[arity]; - RamDomain intVal[arity]; - RamUnsigned uintVal[arity]; - RamFloat floatVal[arity]; - const char* strVal[arity]; + std::unique_ptr values = std::make_unique(arity); + std::unique_ptr intVal = std::make_unique(arity); + std::unique_ptr uintVal = std::make_unique(arity); + std::unique_ptr floatVal = std::make_unique(arity); + std::unique_ptr strVal = std::make_unique(arity); /* Initialize arguments for ffi-call */ for (std::size_t i = 0; i < arity; i++) { @@ -891,7 +898,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { ffi_arg dummy; // ensures minium size } rvalue; - ffi_call(shadow.getFFIcif(), userFunctor, &rvalue, values); + ffi_call(shadow.getFFIcif(), userFunctor, &rvalue, values.get()); switch (cur.getReturnType()) { case TypeAttribute::Signed: return static_cast(rvalue.s); @@ -912,11 +919,11 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { CASE(PackRecord) auto values = cur.getArguments(); std::size_t arity = values.size(); - RamDomain data[arity]; + std::unique_ptr data = std::make_unique(arity); for (std::size_t i = 0; i < arity; ++i) { data[i] = execute(shadow.getChild(i), ctxt); } - return getRecordTable().pack(data, arity); + return getRecordTable().pack(data.get(), arity); ESAC(PackRecord) CASE(SubroutineArgument) @@ -1318,7 +1325,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { CASE(LogSize) const auto& rel = *shadow.getRelation(); ProfileEventSingleton::instance().makeQuantityEvent( - cur.getMessage(), rel.size(), getIterationNumber()); + cur.getMessage(), rel.size(), static_cast(getIterationNumber())); return true; ESAC(LogSize) @@ -1523,7 +1530,14 @@ RamDomain Engine::evalParallelScan( for (const auto& info : viewInfo) { newCtxt.createView(*getRelationHandle(info[0]), info[1], info[2]); } +#if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(pStream.begin(), pStream.end()); + auto b = pStream.begin(); + pfor(int i = 0; i < count; i++) { + auto it = b + i; +#else pfor(auto it = pStream.begin(); it < pStream.end(); it++) { +#endif for (const auto& tuple : *it) { newCtxt[cur.getTupleId()] = tuple.data(); if (!execute(shadow.getNestedOperation(), newCtxt)) { @@ -1576,7 +1590,14 @@ RamDomain Engine::evalParallelIndexScan( for (const auto& info : viewInfo) { newCtxt.createView(*getRelationHandle(info[0]), info[1], info[2]); } +#if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(pStream.begin(), pStream.end()); + auto b = pStream.begin(); + pfor(int i = 0; i < count; i++) { + auto it = b + i; +#else pfor(auto it = pStream.begin(); it < pStream.end(); it++) { +#endif for (const auto& tuple : *it) { newCtxt[cur.getTupleId()] = tuple.data(); if (!execute(shadow.getNestedOperation(), newCtxt)) { @@ -1614,7 +1635,14 @@ RamDomain Engine::evalParallelIfExists( for (const auto& info : viewInfo) { newCtxt.createView(*getRelationHandle(info[0]), info[1], info[2]); } +#if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(pStream.begin(), pStream.end()); + auto b = pStream.begin(); + pfor(int i = 0; i < count; i++) { + auto it = b + i; +#else pfor(auto it = pStream.begin(); it < pStream.end(); it++) { +#endif for (const auto& tuple : *it) { newCtxt[cur.getTupleId()] = tuple.data(); if (execute(shadow.getCondition(), newCtxt)) { @@ -1671,7 +1699,14 @@ RamDomain Engine::evalParallelIndexIfExists(const Rel& rel, const ram::ParallelI for (const auto& info : viewInfo) { newCtxt.createView(*getRelationHandle(info[0]), info[1], info[2]); } +#if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(pStream.begin(), pStream.end()); + auto b = pStream.begin(); + pfor(int i = 0; i < count; i++) { + auto it = b + i; +#else pfor(auto it = pStream.begin(); it < pStream.end(); it++) { +#endif for (const auto& tuple : *it) { newCtxt[cur.getTupleId()] = tuple.data(); if (execute(shadow.getCondition(), newCtxt)) { diff --git a/src/interpreter/Engine.h b/src/interpreter/Engine.h index 208450251e6..82c0b16cb6c 100644 --- a/src/interpreter/Engine.h +++ b/src/interpreter/Engine.h @@ -91,7 +91,7 @@ class Engine { /** @brief Reset iteration number */ void resetIterationNumber(); /** @brief Increment the counter */ - int incCounter(); + RamDomain incCounter(); /** @brief Return the relation map. */ VecOwn& getRelationMap(); /** @brief Create and add relation into the runtime environment. */ diff --git a/src/interpreter/Generator.cpp b/src/interpreter/Generator.cpp index 531f30a8ac8..4f9f9fd4d59 100644 --- a/src/interpreter/Generator.cpp +++ b/src/interpreter/Generator.cpp @@ -146,7 +146,8 @@ NodePtr NodeGenerator::visit_(type_identity, const ram } } - const auto prepStatus = ffi_prep_cif(cif.get(), FFI_DEFAULT_ABI, nbArgs, codomain, args.get()); + const auto prepStatus = + ffi_prep_cif(cif.get(), FFI_DEFAULT_ABI, static_cast(nbArgs), codomain, args.get()); if (prepStatus != FFI_OK) { fatal("Failed to prepare CIF for user-defined operator `%s`; error code = %d", op.getName(), prepStatus); @@ -835,11 +836,11 @@ SuperInstruction NodeGenerator::getEraseSuperInstInfo(const ram::Erase& exist) { NodeGenerator::OrderingContext::OrderingContext(NodeGenerator& generator) : generator(generator) {} void NodeGenerator::OrderingContext::addNewTuple(std::size_t tupleId, std::size_t arity) { - std::vector order; + std::vector order; for (std::size_t i = 0; i < arity; ++i) { - order.push_back((uint32_t)i); + order.push_back(i); } - insertOrder(tupleId, std::move(order)); + insertOrder(tupleId, order); } template @@ -865,7 +866,7 @@ void NodeGenerator::OrderingContext::insertOrder(std::size_t tupleId, const Orde tupleOrders.resize(tupleId + 1); } - std::vector decodeOrder(order.size()); + std::vector decodeOrder(order.size()); for (std::size_t i = 0; i < order.size(); ++i) { decodeOrder[order[i]] = i; } diff --git a/src/interpreter/Index.h b/src/interpreter/Index.h index 37fcf825d68..3fc693de8b4 100644 --- a/src/interpreter/Index.h +++ b/src/interpreter/Index.h @@ -40,7 +40,7 @@ namespace souffle::interpreter { * component to be considered in sorting tuples. */ class Order { - using Attribute = uint32_t; + using Attribute = std::size_t; using AttributeOrder = std::vector; AttributeOrder order; @@ -69,8 +69,8 @@ class Order { */ bool valid() const { // Check that all indices are in range. - for (int i : order) { - if (i < 0 || i >= int(order.size())) { + for (auto i : order) { + if (i >= order.size()) { return false; } } @@ -279,7 +279,7 @@ class Index { /** * Retruns a partitioned list of iterators for parallel computation */ - std::vector> partitionScan(int partitionCount) const { + std::vector> partitionScan(std::size_t partitionCount) const { auto chunks = data.partition(partitionCount); std::vector> res; res.reserve(chunks.size()); @@ -293,7 +293,7 @@ class Index { * Returns a partitioned list of iterators coving elements in range [low, high] */ std::vector> partitionRange( - const Tuple& low, const Tuple& high, int partitionCount) const { + const Tuple& low, const Tuple& high, std::size_t partitionCount) const { auto ranges = this->range(low, high); auto chunks = ranges.partition(partitionCount); std::vector> res; @@ -330,12 +330,20 @@ class Index<0, Structure> { Index(Order /* order */) {} // Specialized iterator class for nullary. - class iterator : public std::iterator { + class iterator { bool value; const Tuple dummy{}; public: - iterator(bool v = false) : value(v) {} + using iterator_category = std::forward_iterator_tag; + using value_type = Tuple; + using difference_type = int64_t; + using pointer = Tuple*; + using reference = Tuple&; + + iterator() : value(false) {} + iterator(bool v) : value(v) {} + iterator(const iterator& other) : value(other.value), dummy(other.dummy) {} const Tuple& operator*() { return dummy; @@ -425,14 +433,14 @@ class Index<0, Structure> { return {this->begin(), this->end()}; } - std::vector> partitionScan(int /* partitionCount */) const { + std::vector> partitionScan(std::size_t /* partitionCount */) const { std::vector> res; res.push_back(scan()); return res; } std::vector> partitionRange( - const Tuple& /* l */, const Tuple& /* h */, int /* partitionCount */) const { + const Tuple& /* l */, const Tuple& /* h */, std::size_t /* partitionCount */) const { return this->partitionScan(0); } diff --git a/src/interpreter/ProgInterface.h b/src/interpreter/ProgInterface.h index 6d67997b4cf..d680fb5f1ec 100644 --- a/src/interpreter/ProgInterface.h +++ b/src/interpreter/ProgInterface.h @@ -48,7 +48,7 @@ namespace souffle::interpreter { class RelInterface : public souffle::Relation { public: RelInterface(RelationWrapper& r, SymbolTable& s, std::string n, std::vector t, - std::vector an, uint32_t i) + std::vector an, std::size_t i) : relation(r), symTable(s), name(std::move(n)), types(std::move(t)), attrNames(std::move(an)), id(i) {} ~RelInterface() override = default; @@ -121,7 +121,7 @@ class RelInterface : public souffle::Relation { */ class iterator_base : public souffle::Relation::iterator_base { public: - iterator_base(uint32_t arg_id, const RelInterface* r, RelationWrapper::Iterator i) + iterator_base(std::size_t arg_id, const RelInterface* r, RelationWrapper::Iterator i) : Relation::iterator_base(arg_id), ramRelationInterface(r), it(i), tup(r) {} ~iterator_base() override = default; @@ -196,7 +196,7 @@ class RelInterface : public souffle::Relation { std::vector attrNames; /** Unique id for wrapper */ - uint32_t id; + std::size_t id; }; /** @@ -207,7 +207,7 @@ class ProgInterface : public SouffleProgram { explicit ProgInterface(Engine& interp) : prog(interp.getTranslationUnit().getProgram()), exec(interp), symTable(interp.getSymbolTable()), recordTable(interp.getRecordTable()) { - uint32_t id = 0; + std::size_t id = 0; // Retrieve AST Relations and store them in a map std::map map; diff --git a/src/interpreter/Relation.h b/src/interpreter/Relation.h index 5aa209d803c..9d05d8b2a90 100644 --- a/src/interpreter/Relation.h +++ b/src/interpreter/Relation.h @@ -155,7 +155,7 @@ template typename Structure> class Relation : public RelationWrapper { public: static constexpr std::size_t Arity = _Arity; - using Attribute = uint32_t; + using Attribute = std::size_t; using AttributeSet = std::set; using Index = interpreter::Index; using Tuple = souffle::Tuple; diff --git a/src/interpreter/tests/ram_arithmetic_test.cpp b/src/interpreter/tests/ram_arithmetic_test.cpp index 1562a7323f0..728b56f21d3 100644 --- a/src/interpreter/tests/ram_arithmetic_test.cpp +++ b/src/interpreter/tests/ram_arithmetic_test.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -46,8 +47,6 @@ namespace souffle::interpreter::test { using namespace ram; -#define TESTS_PER_OPERATION 20 - /** Function to evaluate a single Expression. */ RamDomain evalExpression(Own expression) { // Set up Program and translation unit @@ -107,7 +106,7 @@ TEST(SignedConstant, ArithmeticEvaluation) { } TEST(Unary, Neg) { - for (RamDomain randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (RamDomain randomNumber : testutil::generateValues()) { EXPECT_EQ(evalUnary(FunctorOp::NEG, randomNumber), -randomNumber); } } @@ -115,7 +114,7 @@ TEST(Unary, Neg) { TEST(Unary, FloatNeg) { FunctorOp functor = FunctorOp::FNEG; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { auto result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), -randomNumber); } @@ -124,7 +123,7 @@ TEST(Unary, FloatNeg) { TEST(Unary, BinaryNot) { FunctorOp functor = FunctorOp::BNOT; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { EXPECT_EQ(evalUnary(functor, randomNumber), ~randomNumber); } } @@ -132,7 +131,7 @@ TEST(Unary, BinaryNot) { TEST(Unary, UnsignedBinaryNot) { FunctorOp functor = FunctorOp::UBNOT; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), ~randomNumber); } @@ -141,7 +140,7 @@ TEST(Unary, UnsignedBinaryNot) { TEST(Unary, LogicalNeg) { FunctorOp functor = FunctorOp::LNOT; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { EXPECT_EQ(evalUnary(functor, randomNumber), !randomNumber); } } @@ -149,7 +148,7 @@ TEST(Unary, LogicalNeg) { TEST(Unary, UnsignedLogicalNeg) { FunctorOp functor = FunctorOp::ULNOT; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), static_cast(!randomNumber)); } @@ -158,7 +157,7 @@ TEST(Unary, UnsignedLogicalNeg) { TEST(Unary, SingedTpUnsigned) { FunctorOp functor = FunctorOp::I2U; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, randomNumber); EXPECT_EQ(ramBitCast(result), static_cast(randomNumber)); } @@ -167,7 +166,7 @@ TEST(Unary, SingedTpUnsigned) { TEST(Unary, UnsignedToSigned) { FunctorOp functor = FunctorOp::U2I; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(result, static_cast(randomNumber)); } @@ -176,7 +175,7 @@ TEST(Unary, UnsignedToSigned) { TEST(Unary, SignedToFloat) { FunctorOp functor = FunctorOp::I2F; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), static_cast(randomNumber)); } @@ -185,7 +184,7 @@ TEST(Unary, SignedToFloat) { TEST(Unary, FloatToSigned) { FunctorOp functor = FunctorOp::F2I; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(result, static_cast(randomNumber)); } @@ -194,7 +193,7 @@ TEST(Unary, FloatToSigned) { TEST(Unary, UnsignedToFloat) { FunctorOp functor = FunctorOp::U2F; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), static_cast(randomNumber)); } @@ -203,7 +202,7 @@ TEST(Unary, UnsignedToFloat) { TEST(Unary, FloatToUnsigned) { FunctorOp functor = FunctorOp::F2U; - for (auto randomNumber : testutil::generateRandomVector(TESTS_PER_OPERATION)) { + for (auto randomNumber : testutil::generateValues()) { RamDomain result = evalUnary(functor, ramBitCast(randomNumber)); EXPECT_EQ(ramBitCast(result), static_cast(randomNumber)); } @@ -212,141 +211,141 @@ TEST(Unary, FloatToUnsigned) { TEST(Binary, SignedAdd) { FunctorOp functor = FunctorOp::ADD; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (std::size_t i = 0; i < TESTS_PER_OPERATION; ++i) { - RamDomain arg1 = vecArg1[i]; - RamDomain arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 + arg2); + for (auto i : vecArg1) { + for (auto j : vecArg2) { + RamDomain result = evalBinary(functor, i, j); + EXPECT_EQ(result, i + j); + } } } TEST(Binary, UnsignedAdd) { FunctorOp functor = FunctorOp::UADD; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - RamUnsigned arg1 = vecArg1[i]; - RamUnsigned arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 + arg2); + for (auto i : vecArg1) { + for (auto j : vecArg2) { + RamDomain result = evalBinary(functor, i, j); + EXPECT_EQ(ramBitCast(result), i + j); + } } } TEST(Binary, FloatAdd) { FunctorOp functor = FunctorOp::FADD; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 + arg2); + for (auto i : vecArg1) { + for (auto j : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(i), ramBitCast(j)); + EXPECT_EQ(ramBitCast(result), i + j); + } } } TEST(Binary, SignedSub) { FunctorOp functor = FunctorOp::SUB; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 - arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 - arg2); + } } } TEST(Binary, UnsignedSub) { FunctorOp functor = FunctorOp::USUB; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 - arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 - arg2); + } } } TEST(Binary, FloatSub) { FunctorOp functor = FunctorOp::FSUB; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 - arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 - arg2); + } } } TEST(Binary, SignedMul) { FunctorOp functor = FunctorOp::MUL; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 * arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 * arg2); + } } } TEST(Binary, UnsignedMul) { FunctorOp functor = FunctorOp::UMUL; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 * arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 * arg2); + } } } TEST(Binary, FloatMul) { FunctorOp functor = FunctorOp::FMUL; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 * arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 * arg2); + } } } TEST(Binary, SignedDiv) { FunctorOp functor = FunctorOp::DIV; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - if (arg2 != 0) { - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 / arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + if (arg2 != 0 && !(arg1 == std::numeric_limits::min() && arg2 == -1)) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 / arg2); + } } } } @@ -354,15 +353,15 @@ TEST(Binary, SignedDiv) { TEST(Binary, UnsignedDiv) { FunctorOp functor = FunctorOp::UDIV; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - if (arg2 != 0) { - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 / arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + if (arg2 != 0) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 / arg2); + } } } } @@ -370,15 +369,15 @@ TEST(Binary, UnsignedDiv) { TEST(Binary, FloatDiv) { FunctorOp functor = FunctorOp::FDIV; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - if (arg2 != 0) { - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 / arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + if (arg2 != 0) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 / arg2); + } } } } @@ -386,211 +385,219 @@ TEST(Binary, FloatDiv) { TEST(Binary, SignedExp) { FunctorOp functor = FunctorOp::EXP; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, static_cast(static_cast(std::pow(arg1, arg2)))); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + const RamSigned result = ramBitCast(evalBinary(functor, arg1, arg2)); + const RamSigned expected = static_cast(std::pow(arg1, arg2)); + EXPECT_EQ(result, expected); + } } } TEST(Binary, UnsignedExp) { FunctorOp functor = FunctorOp::UEXP; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), static_cast(std::pow(arg1, arg2))); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + const RamUnsigned result = + ramBitCast(evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2))); + const RamUnsigned expected = static_cast(std::pow(arg1, arg2)); + EXPECT_EQ(result, expected); + } } } TEST(Binary, FloatExp) { FunctorOp functor = FunctorOp::FEXP; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - auto result = ramBitCast(evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2))); - auto expected = static_cast(std::pow(arg1, arg2)); - EXPECT_TRUE((std::isnan(result) && std::isnan(expected)) || result == expected); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + const RamFloat result = + ramBitCast(evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2))); + const RamFloat expected = static_cast(std::pow(arg1, arg2)); + EXPECT_TRUE((std::isnan(result) && std::isnan(expected)) || result == expected); + } } } TEST(Binary, SignedMod) { FunctorOp functor = FunctorOp::MOD; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 % arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + if (arg2 > 0) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 % arg2); + } + } } } TEST(Binary, UnsignedMod) { FunctorOp functor = FunctorOp::UMOD; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 % arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + if (arg2 > 0) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 % arg2); + } + } } } TEST(Binary, SignedBinaryAnd) { FunctorOp functor = FunctorOp::BAND; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 & arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 & arg2); + } } } TEST(Binary, UnsignedBinaryAnd) { FunctorOp functor = FunctorOp::UBAND; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 & arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 & arg2); + } } } TEST(Binary, SignedBinaryOr) { FunctorOp functor = FunctorOp::BOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 | arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 | arg2); + } } } TEST(Binary, UnsignedBinaryOr) { FunctorOp functor = FunctorOp::UBOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 | arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 | arg2); + } } } TEST(Binary, SignedBinaryXor) { FunctorOp functor = FunctorOp::BXOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 ^ arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 ^ arg2); + } } } TEST(Binary, UnsignedBinaryXor) { FunctorOp functor = FunctorOp::UBXOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 ^ arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 ^ arg2); + } } } TEST(Binary, SignedLogicalAnd) { FunctorOp functor = FunctorOp::LAND; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 || arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 && arg2); + } } } TEST(Binary, UnsignedLogicalAnd) { FunctorOp functor = FunctorOp::ULAND; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 || arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 && arg2); + } } } TEST(Binary, SignedLogicalOr) { FunctorOp functor = FunctorOp::LOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, arg1, arg2); - EXPECT_EQ(result, arg1 || arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, arg1, arg2); + EXPECT_EQ(result, arg1 || arg2); + } } } TEST(Binary, UnsignedLogicalOr) { FunctorOp functor = FunctorOp::ULOR; - auto vecArg1 = testutil::generateRandomVector(TESTS_PER_OPERATION); - auto vecArg2 = testutil::generateRandomVector(TESTS_PER_OPERATION); + auto vecArg1 = testutil::generateValues(); + auto vecArg2 = testutil::generateValues(); - for (int i = 0; i < TESTS_PER_OPERATION; ++i) { - auto arg1 = vecArg1[i]; - auto arg2 = vecArg2[i]; - RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); - EXPECT_EQ(ramBitCast(result), arg1 || arg2); + for (auto arg1 : vecArg1) { + for (auto arg2 : vecArg2) { + RamDomain result = evalBinary(functor, ramBitCast(arg1), ramBitCast(arg2)); + EXPECT_EQ(ramBitCast(result), arg1 || arg2); + } } } diff --git a/src/interpreter/tests/ram_relation_test.cpp b/src/interpreter/tests/ram_relation_test.cpp index ecd88ff92fd..5fa09128714 100644 --- a/src/interpreter/tests/ram_relation_test.cpp +++ b/src/interpreter/tests/ram_relation_test.cpp @@ -54,8 +54,6 @@ using namespace ram; using json11::Json; -#define RANDOM_TESTS 12 - const std::string testInterpreterStore( std::vector attribs, std::vector attribsTypes, VecOwn exprs) { Global::config().set("jobs", "1"); @@ -121,15 +119,16 @@ test } TEST(IO_store, Signed) { - std::vector randomNumbers = testutil::generateRandomVector(RANDOM_TESTS); + std::vector randomNumbers = testutil::generateValues(); + const std::size_t len = randomNumbers.size(); // a0 a1 a2... - std::vector attribs(RANDOM_TESTS, "a"); - for (std::size_t i = 0; i < RANDOM_TESTS; ++i) { + std::vector attribs(len, "a"); + for (std::size_t i = 0; i < len; ++i) { attribs[i].append(std::to_string(i)); } - std::vector attribsTypes(RANDOM_TESTS, "i"); + std::vector attribsTypes(len, "i"); VecOwn exprs; for (RamDomain i : randomNumbers) { @@ -145,7 +144,7 @@ TEST(IO_store, Signed) { << "\n" << randomNumbers[0]; - for (std::size_t i = 1; i < randomNumbers.size(); ++i) { + for (std::size_t i = 1; i < len; ++i) { expected << "\t" << randomNumbers[i]; } expected << "\n" @@ -157,15 +156,16 @@ TEST(IO_store, Signed) { } TEST(IO_store, Float) { - std::vector randomNumbers = testutil::generateRandomVector(RANDOM_TESTS); + const std::vector randomNumbers = testutil::generateValues(); + const std::size_t len = randomNumbers.size(); // a0 a1 a2... - std::vector attribs(RANDOM_TESTS, "a"); - for (std::size_t i = 0; i < RANDOM_TESTS; ++i) { + std::vector attribs(len, "a"); + for (std::size_t i = 0; i < len; ++i) { attribs[i].append(std::to_string(i)); } - std::vector attribsTypes(RANDOM_TESTS, "f"); + std::vector attribsTypes(len, "f"); VecOwn exprs; for (RamFloat f : randomNumbers) { @@ -195,15 +195,16 @@ TEST(IO_store, Float) { } TEST(IO_store, Unsigned) { - std::vector randomNumbers = testutil::generateRandomVector(RANDOM_TESTS); + const std::vector randomNumbers = testutil::generateValues(); + const std::size_t len = randomNumbers.size(); // a0 a1 a2... - std::vector attribs(RANDOM_TESTS, "a"); - for (std::size_t i = 0; i < RANDOM_TESTS; ++i) { + std::vector attribs(len, "a"); + for (std::size_t i = 0; i < len; ++i) { attribs[i].append(std::to_string(i)); } - std::vector attribsTypes(RANDOM_TESTS, "u"); + std::vector attribsTypes(len, "u"); VecOwn exprs; for (RamUnsigned u : randomNumbers) { @@ -232,7 +233,8 @@ TEST(IO_store, Unsigned) { // Test (store) with different delimiter TEST(IO_store, SignedChangedDelimiter) { - std::vector randomNumbers = testutil::generateRandomVector(RANDOM_TESTS); + const std::vector randomNumbers = testutil::generateValues(); + const std::size_t len = randomNumbers.size(); const std::string delimiter{", "}; Global::config().set("jobs", "1"); @@ -240,15 +242,15 @@ TEST(IO_store, SignedChangedDelimiter) { VecOwn rels; // a0 a1 a2... - std::vector attribs(RANDOM_TESTS, "a"); - for (std::size_t i = 0; i < RANDOM_TESTS; ++i) { + std::vector attribs(len, "a"); + for (std::size_t i = 0; i < len; ++i) { attribs[i].append(std::to_string(i)); } - std::vector attribsTypes(RANDOM_TESTS, "i"); + std::vector attribsTypes(len, "i"); Own myrel = - mk("test", RANDOM_TESTS, 0, attribs, attribsTypes, RelationRepresentation::BTREE); + mk("test", len, 0, attribs, attribsTypes, RelationRepresentation::BTREE); Json types = Json::object{ {"relation", Json::object{{"arity", static_cast(attribsTypes.size())}, diff --git a/src/main.cpp b/src/main.cpp index 00b211617f1..b87597e591a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,6 +15,7 @@ ***********************************************************************/ #include "Global.h" +#include "ast/Clause.h" #include "ast/Node.h" #include "ast/Program.h" #include "ast/TranslationUnit.h" @@ -91,8 +92,10 @@ #include "reports/DebugReport.h" #include "reports/ErrorReport.h" #include "souffle/RamTypes.h" +#ifndef _MSC_VER #include "souffle/profile/Tui.h" #include "souffle/provenance/Explain.h" +#endif #include "souffle/utility/ContainerUtil.h" #include "souffle/utility/FileUtil.h" #include "souffle/utility/MiscUtil.h" @@ -128,11 +131,27 @@ namespace souffle { std::map env; if (Global::config().has("library-dir")) { auto escapeLdPath = [](auto&& xs) { return escape(xs, {':', ' '}, "\\"); }; - auto ld_path = toString(join(map(Global::config().getMany("library-dir"), escapeLdPath), ":")); + auto ld_path = toString(join( + map(Global::config().getMany("library-dir"), escapeLdPath), std::string(1, PATHdelimiter))); +#if defined(_MSC_VER) + std::size_t l; + std::wstring env_path(ld_path.length() + 1, L' '); + ::mbstowcs_s(&l, env_path.data(), env_path.size(), ld_path.data(), ld_path.size()); + env_path.resize(l - 1); + + DWORD n = GetEnvironmentVariableW(L"PATH", nullptr, 0); + if (n > 0) { + // append path + std::unique_ptr orig(new wchar_t[n]); + GetEnvironmentVariableW(L"PATH", orig.get(), n); + env_path = env_path + L";" + std::wstring(orig.get()); + } + SetEnvironmentVariableW(L"PATH", env_path.c_str()); - env["LD_LIBRARY_PATH"] = ld_path; -#ifdef __APPLE__ +#elif defined(__APPLE__) env["DYLD_LIBRARY_PATH"] = ld_path; +#else + env["LD_LIBRARY_PATH"] = ld_path; #endif } @@ -152,6 +171,14 @@ namespace souffle { */ void compileToBinary(const std::string& command, std::string_view sourceFilename) { std::vector argv; + + argv.push_back(command); + +#ifndef NDEBUG + // compile with debug + argv.push_back("-g"); +#endif + if (Global::config().has("swig")) { argv.push_back("-s"); argv.push_back(Global::config().get("swig")); @@ -174,8 +201,13 @@ void compileToBinary(const std::string& command, std::string_view sourceFilename argv.push_back(std::string(sourceFilename)); - auto exit = execute(command, argv); - if (!exit) throw std::invalid_argument(tfm::format("unable to execute tool <%s>", command)); +#if defined(_MSC_VER) + const char* interpreter = "python"; +#else + const char* interpreter = "python3"; +#endif + auto exit = execute(interpreter, argv); + if (!exit) throw std::invalid_argument(tfm::format("unable to execute tool ", command)); if (exit != 0) throw std::invalid_argument(tfm::format("failed to compile C++ source <%s>", sourceFilename)); } @@ -266,7 +298,8 @@ int main(int argc, char** argv) { "\ttype-analysis"}, {"parse-errors", '\5', "", "", false, "Show parsing errors, if any, then exit."}, {"help", 'h', "", "", false, "Display this help message."}, - {"legacy", '\6', "", "", false, "Enable legacy support."}}; + {"legacy", '\6', "", "", false, "Enable legacy support."}, + {"preprocessor", '\7', "CMD", "", false, "C preprocessor to use."}}; Global::config().processArgs(argc, argv, header.str(), footer.str(), options); // ------ command line arguments ------------- @@ -383,28 +416,48 @@ int main(int argc, char** argv) { // ------ start souffle ------------- - std::string souffleExecutable = which(argv[0]); + const std::string souffleExecutable = which(argv[0]); if (souffleExecutable.empty()) { throw std::runtime_error("failed to determine souffle executable path"); } /* Create the pipe to establish a communication between cpp and souffle */ - std::string cmd = which("mcpp"); - if (!isExecutable(cmd)) { - throw std::runtime_error("failed to locate mcpp pre-processor"); + std::string cmd; + + if (Global::config().has("preprocessor")) { + cmd = Global::config().get("preprocessor"); + } else { + cmd = which("mcpp"); + if (isExecutable(cmd)) { + cmd += " -e utf8 -W0"; + } else { + cmd = which("gcc"); + if (isExecutable(cmd)) { + cmd += " -x c -E"; + } else { + std::cerr << "failed to locate mcpp or gcc pre-processors\n"; + throw std::runtime_error("failed to locate mcpp or gcc pre-processors"); + } + } } - cmd += " -e utf8 -W0 "; - cmd += toString(join(Global::config().getMany("include-dir"), " ", - [&](auto&& os, auto&& dir) { tfm::format(os, "'-I%s'", dir); })); + cmd += " " + toString(join(Global::config().getMany("include-dir"), " ", + [&](auto&& os, auto&& dir) { tfm::format(os, "-I \"%s\"", dir); })); + if (Global::config().has("macro")) { cmd += " " + Global::config().get("macro"); } // Add RamDomain size as a macro cmd += " -DRAM_DOMAIN_SIZE=" + std::to_string(RAM_DOMAIN_SIZE); - cmd += " '" + Global::config().get("") + "'"; + cmd += " \"" + Global::config().get("") + "\""; +#if defined(_MSC_VER) + // cl.exe prints the input file name on the standard error stream, + // we must silent it in order to preserve an empty error output + // because Souffle test-suite is sensible to error outputs. + cmd += " 2> nul"; +#endif FILE* in = popen(cmd.c_str(), "r"); /* Time taking for parsing */ @@ -423,6 +476,10 @@ int main(int argc, char** argv) { if (preprocessor_status == -1) { perror(nullptr); throw std::runtime_error("failed to close pre-processor pipe"); + } else if (preprocessor_status != 0) { + std::cerr << "Pre-processors command failed with code " << preprocessor_status << ": '" << cmd + << "'\n"; + throw std::runtime_error("Pre-processor command failed"); } /* Report run-time of the parser if verbose flag is set */ @@ -442,7 +499,7 @@ int main(int argc, char** argv) { } std::cout << astTranslationUnit->getErrorReport(); - return astTranslationUnit->getErrorReport().getNumErrors(); + return static_cast(astTranslationUnit->getErrorReport().getNumErrors()); } // ------- check for parse errors ------------- @@ -661,7 +718,7 @@ int main(int argc, char** argv) { const bool must_interpret = !execute_mode && !compile_mode && !generate_mode && !Global::config().has("swig"); const bool must_execute = execute_mode; - const bool must_compile = must_execute || compile_mode; + const bool must_compile = must_execute || compile_mode || Global::config().has("swig"); try { if (must_interpret) { @@ -670,7 +727,11 @@ int main(int argc, char** argv) { std::thread profiler; // Start up profiler if needed if (Global::config().has("live-profile")) { +#ifdef _MSC_VER + throw("No live-profile on Windows\n."); +#else profiler = std::thread([]() { profile::Tui().runProf(); }); +#endif } // configure and execute interpreter @@ -681,6 +742,9 @@ int main(int argc, char** argv) { profiler.join(); } if (Global::config().has("provenance")) { +#ifdef _MSC_VER + throw("No explain/explore provenance on Windows\n."); +#else // only run explain interface if interpreted interpreter::ProgInterface interface(*interpreter); if (Global::config().get("provenance") == "explain") { @@ -688,6 +752,7 @@ int main(int argc, char** argv) { } else if (Global::config().get("provenance") == "explore") { explain(interface, true); } +#endif } } else { // ------- compiler ------------- @@ -726,6 +791,7 @@ int main(int argc, char** argv) { else { std::ofstream os{sourceFilename}; synthesiser->generateCode(os, baseIdentifier, withSharedLibrary); + os.close(); } if (Global::config().has("verbose")) { auto synthesisEnd = std::chrono::high_resolution_clock::now(); @@ -744,12 +810,11 @@ int main(int argc, char** argv) { if (must_compile) { /* Fail if a souffle-compile executable is not found */ - auto souffle_compile = findTool("souffle-compile", souffleExecutable, "."); - if (!isExecutable(souffle_compile)) - throw std::runtime_error("failed to locate souffle-compile"); + const auto souffle_compile = findTool("souffle-compile.py", souffleExecutable, "."); + if (!souffle_compile) throw std::runtime_error("failed to locate souffle-compile.py"); auto t_bgn = std::chrono::high_resolution_clock::now(); - compileToBinary(souffle_compile, sourceFilename); + compileToBinary(*souffle_compile, sourceFilename); auto t_end = std::chrono::high_resolution_clock::now(); if (Global::config().has("verbose")) { @@ -760,7 +825,11 @@ int main(int argc, char** argv) { // run compiled C++ program if requested. if (must_execute) { - executeBinaryAndExit(baseFilename); + std::string binaryFilename = baseFilename; +#if defined(_MSC_VER) + binaryFilename += ".exe"; +#endif + executeBinaryAndExit(binaryFilename); } } } catch (std::exception& e) { diff --git a/src/parser/ParserDriver.cpp b/src/parser/ParserDriver.cpp index 52a5e87d886..b53b5ffa0f5 100644 --- a/src/parser/ParserDriver.cpp +++ b/src/parser/ParserDriver.cpp @@ -45,6 +45,7 @@ extern YY_BUFFER_STATE yy_scan_string(const char*, yyscan_t scanner); extern int yylex_destroy(yyscan_t scanner); extern int yylex_init_extra(ScannerInfo* data, yyscan_t* scanner); extern void yyset_in(FILE* in_str, yyscan_t scanner); +extern void yyset_debug(int, yyscan_t scanner); namespace souffle { @@ -55,6 +56,7 @@ Own ParserDriver::parse( ScannerInfo data; data.yyfilename = filename; yylex_init_extra(&data, &scanner); + yyset_debug(0, scanner); yyset_in(in, scanner); yy::parser parser(*this, scanner); @@ -73,6 +75,7 @@ Own ParserDriver::parse( data.yyfilename = ""; yyscan_t scanner; yylex_init_extra(&data, &scanner); + yyset_debug(0, scanner); yy_scan_string(code.c_str(), scanner); yy::parser parser(*this, scanner); parser.parse(); diff --git a/src/parser/SrcLocation.cpp b/src/parser/SrcLocation.cpp index ab2dd767470..60110a4b2db 100644 --- a/src/parser/SrcLocation.cpp +++ b/src/parser/SrcLocation.cpp @@ -14,13 +14,20 @@ * ***********************************************************************/ +#ifdef _MSC_VER +// do not define min and max otherwise cannot use std::min std::max +#define NOMINMAX +#endif + #include "parser/SrcLocation.h" #include "souffle/utility/FileUtil.h" + #include #include #include #include #include +#include namespace souffle { @@ -30,11 +37,11 @@ std::string getCurrentFilename(const std::vector& filenames) { } std::string path = "."; - for (std::string filename : filenames) { - if (!filename.empty() && filename[0] == '/') { + for (const std::string& filename : filenames) { + if (!filename.empty() && isAbsolute(filename)) { path = dirName(filename); - } else if (existFile(path + "/" + filename)) { - path = dirName(path + "/" + filename); + } else if (existFile(path + pathSeparator + filename)) { + path = dirName(path + pathSeparator + filename); } else if (existFile(filename)) { path = dirName(filename); } else { @@ -42,7 +49,7 @@ std::string getCurrentFilename(const std::vector& filenames) { } } - return path + "/" + baseName(filenames.back()); + return path + pathSeparator + baseName(filenames.back()); } bool SrcLocation::operator<(const SrcLocation& other) const { @@ -70,6 +77,7 @@ bool SrcLocation::operator<(const SrcLocation& other) const { } void SrcLocation::setFilename(std::string filename) { + makePreferred(filename); if (filenames.empty()) { filenames.emplace_back(filename); return; diff --git a/src/parser/parser.yy b/src/parser/parser.yy index 7ff243c27bf..c1f93be33e2 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -152,8 +152,8 @@ %token MAX "max aggregator" %token COUNT "count aggregator" %token SUM "sum aggregator" -%token TRUE "true literal constraint" -%token FALSE "false literal constraint" +%token TRUELIT "true literal constraint" +%token FALSELIT "false literal constraint" %token PLAN "plan keyword" %token CHOICEDOMAIN "choice-domain" %token IF ":-" @@ -849,11 +849,11 @@ constraint } /* zero-arity constraints */ - | TRUE + | TRUELIT { $$ = mk(true , @$); } - | FALSE + | FALSELIT { $$ = mk(false, @$); } @@ -1503,11 +1503,11 @@ kvp_value { $$ = $NUMBER; } - | TRUE + | TRUELIT { $$ = "true"; } - | FALSE + | FALSELIT { $$ = "false"; } diff --git a/src/parser/scanner.ll b/src/parser/scanner.ll index 7bc08772738..dd24ecf3f50 100644 --- a/src/parser/scanner.ll +++ b/src/parser/scanner.ll @@ -24,18 +24,26 @@ #endif #include + #ifndef _MSC_VER #include + #endif #include #include #include #include #include #include + #ifndef _MSC_VER #include + #endif #include #include "ast/Program.h" + #ifdef _MSC_VER + #define YY_NO_UNISTD_H + #endif + #include "parser/parser.hh" #include "parser/SrcLocation.h" #include "parser/ParserDriver.h" @@ -134,8 +142,8 @@ WS [ \t\r\v\f] "_" { return yy::parser::make_UNDERSCORE(yylloc); } "count" { return yy::parser::make_COUNT(yylloc); } "sum" { return yy::parser::make_SUM(yylloc); } -"true" { return yy::parser::make_TRUE(yylloc); } -"false" { return yy::parser::make_FALSE(yylloc); } +"true" { return yy::parser::make_TRUELIT(yylloc); } +"false" { return yy::parser::make_FALSELIT(yylloc); } "to_float" { return yy::parser::make_TOFLOAT(yylloc); } "to_number" { return yy::parser::make_TONUMBER(yylloc); } "to_string" { return yy::parser::make_TOSTRING(yylloc); } @@ -235,16 +243,42 @@ WS [ \t\r\v\f] return yy::parser::make_STRING(result, yylloc); } \#.*$ { - char fname[yyleng+1]; + std::unique_ptr fname_ptr = std::make_unique(yyleng+1); + char* fname = fname_ptr.get(); int lineno; - if(sscanf(yytext,"# %d \"%[^\"]",&lineno,fname)>=2) { - assert(strlen(fname) > 0 && "failed conversion"); - fname[strlen(fname)]='\0'; - yycolumn = 1; yylineno = lineno-1; - yyfilename = fname; - } else if(sscanf(yytext,"#line %d \"%[^\"]",&lineno,fname)>=2) { - assert(strlen(fname) > 0 && "failed conversion"); - fname[strlen(fname)]='\0'; + if ((sscanf(yytext,"# %d \"%[^\"]",&lineno,fname)>=2) || + (sscanf(yytext,"#line %d \"%[^\"]",&lineno,fname)>=2)) { + std::size_t fnamelen = strlen(fname); + assert(fnamelen > 0 && "failed conversion"); + fname[fnamelen]='\0'; + + // fname is a literal string with escape sequences + if (strchr(fname,'\\')) { + std::string filename; + std::size_t i; + for (i = 0; i < fnamelen; ++i) { + if (fname[i] == '\\' && (i + 1) < fnamelen) { + switch(fname[i+1]) { + case '"': filename += '"'; break; + case '\'': filename += '\''; break; + case '\\': filename += '\\'; break; + case 'a': filename += '\a'; break; + case 'b': filename += '\b'; break; + case 'f': filename += '\f'; break; + case 'n': filename += '\n'; break; + case 'r': filename += '\r'; break; + case 't': filename += '\t'; break; + case 'v': filename += '\v'; break; + } + ++i; + } else { + filename += fname[i]; + } + } + std::copy(filename.begin(), filename.end(), fname); + fname[filename.size()] = '\0'; + } + yycolumn = 1; yylineno = lineno-1; yyfilename = fname; } @@ -258,7 +292,7 @@ WS [ \t\r\v\f] \n { } } \n { yycolumn = 1; } -{WS}* { } +{WS}+ { } <> { return yy::parser::make_END(yylloc); } . { driver.error(yylloc, std::string("unexpected ") + yytext); } %% diff --git a/src/ram/Aggregate.h b/src/ram/Aggregate.h index 656c777fdc0..9e75bdedd7b 100644 --- a/src/ram/Aggregate.h +++ b/src/ram/Aggregate.h @@ -48,7 +48,7 @@ namespace souffle::ram { class Aggregate : public RelationOperation, public AbstractAggregate { public: Aggregate(Own nested, AggregateOp fun, std::string rel, Own expression, - Own condition, int ident) + Own condition, std::size_t ident) : RelationOperation(rel, ident, std::move(nested)), AbstractAggregate(fun, std::move(expression), std::move(condition)) {} diff --git a/src/ram/IndexAggregate.h b/src/ram/IndexAggregate.h index e28cdee27b6..c07e33067e5 100644 --- a/src/ram/IndexAggregate.h +++ b/src/ram/IndexAggregate.h @@ -46,7 +46,7 @@ namespace souffle::ram { class IndexAggregate : public IndexOperation, public AbstractAggregate { public: IndexAggregate(Own nested, AggregateOp fun, std::string rel, Own expression, - Own condition, RamPattern queryPattern, int ident) + Own condition, RamPattern queryPattern, std::size_t ident) : IndexOperation(rel, ident, std::move(queryPattern), std::move(nested)), AbstractAggregate(fun, std::move(expression), std::move(condition)) {} diff --git a/src/ram/IndexIfExists.h b/src/ram/IndexIfExists.h index 10bd3024ba1..9feb2f805ec 100644 --- a/src/ram/IndexIfExists.h +++ b/src/ram/IndexIfExists.h @@ -52,7 +52,7 @@ namespace souffle::ram { */ class IndexIfExists : public IndexOperation, public AbstractIfExists { public: - IndexIfExists(std::string rel, int ident, Own cond, RamPattern queryPattern, + IndexIfExists(std::string rel, std::size_t ident, Own cond, RamPattern queryPattern, Own nested, std::string profileText = "") : IndexOperation(rel, ident, std::move(queryPattern), std::move(nested), std::move(profileText)), diff --git a/src/ram/IndexOperation.h b/src/ram/IndexOperation.h index 4054bcde557..49413aad010 100644 --- a/src/ram/IndexOperation.h +++ b/src/ram/IndexOperation.h @@ -42,7 +42,7 @@ using RamPattern = std::pair; */ class IndexOperation : public RelationOperation { public: - IndexOperation(std::string rel, int ident, RamPattern queryPattern, Own nested, + IndexOperation(std::string rel, std::size_t ident, RamPattern queryPattern, Own nested, std::string profileText = "") : RelationOperation(rel, ident, std::move(nested), std::move(profileText)), queryPattern(std::move(queryPattern)) { @@ -88,7 +88,7 @@ class IndexOperation : public RelationOperation { void printIndex(std::ostream& os) const { // const auto& attrib = getRelation().getAttributeNames(); bool first = true; - for (unsigned int i = 0; i < queryPattern.first.size(); ++i) { + for (std::size_t i = 0; i < queryPattern.first.size(); ++i) { // early exit if no upper/lower bounds are defined if (isUndefValue(queryPattern.first[i].get()) && isUndefValue(queryPattern.second[i].get())) { continue; diff --git a/src/ram/IndexScan.h b/src/ram/IndexScan.h index 5dbb8594d3c..4ba65a34912 100644 --- a/src/ram/IndexScan.h +++ b/src/ram/IndexScan.h @@ -47,7 +47,7 @@ using RamPattern = std::pair; */ class IndexScan : public IndexOperation { public: - IndexScan(std::string rel, int ident, RamPattern queryPattern, Own nested, + IndexScan(std::string rel, std::size_t ident, RamPattern queryPattern, Own nested, std::string profileText = "") : IndexOperation(rel, ident, std::move(queryPattern), std::move(nested), std::move(profileText)) { } diff --git a/src/ram/NestedIntrinsicOperator.h b/src/ram/NestedIntrinsicOperator.h index 3cdf6e3ec48..a25c36df574 100644 --- a/src/ram/NestedIntrinsicOperator.h +++ b/src/ram/NestedIntrinsicOperator.h @@ -56,7 +56,8 @@ inline std::ostream& operator<<(std::ostream& os, NestedIntrinsicOp e) { */ class NestedIntrinsicOperator : public TupleOperation { public: - NestedIntrinsicOperator(NestedIntrinsicOp op, VecOwn args, Own nested, int ident) + NestedIntrinsicOperator( + NestedIntrinsicOp op, VecOwn args, Own nested, std::size_t ident) : TupleOperation(ident, std::move(nested)), args(std::move(args)), op(op) {} NestedIntrinsicOp getFunction() const { diff --git a/src/ram/ParallelAggregate.h b/src/ram/ParallelAggregate.h index a8b388c362f..577a7000192 100644 --- a/src/ram/ParallelAggregate.h +++ b/src/ram/ParallelAggregate.h @@ -49,7 +49,7 @@ namespace souffle::ram { class ParallelAggregate : public Aggregate, public AbstractParallel { public: ParallelAggregate(Own nested, AggregateOp fun, std::string rel, Own expression, - Own condition, int ident) + Own condition, std::size_t ident) : Aggregate(std::move(nested), fun, rel, std::move(expression), std::move(condition), ident) {} ParallelAggregate* cloning() const override { diff --git a/src/ram/ParallelIndexAggregate.h b/src/ram/ParallelIndexAggregate.h index f7b5cc91877..1c8eeda7151 100644 --- a/src/ram/ParallelIndexAggregate.h +++ b/src/ram/ParallelIndexAggregate.h @@ -48,7 +48,7 @@ namespace souffle::ram { class ParallelIndexAggregate : public IndexAggregate, public AbstractParallel { public: ParallelIndexAggregate(Own nested, AggregateOp fun, std::string rel, - Own expression, Own condition, RamPattern queryPattern, int ident) + Own expression, Own condition, RamPattern queryPattern, std::size_t ident) : IndexAggregate(std::move(nested), fun, rel, std::move(expression), std::move(condition), std::move(queryPattern), ident) {} diff --git a/src/ram/ParallelIndexIfExists.h b/src/ram/ParallelIndexIfExists.h index 4a1d326e5d1..3bfc8437f74 100644 --- a/src/ram/ParallelIndexIfExists.h +++ b/src/ram/ParallelIndexIfExists.h @@ -53,7 +53,7 @@ namespace souffle::ram { */ class ParallelIndexIfExists : public IndexIfExists, public AbstractParallel { public: - ParallelIndexIfExists(std::string rel, int ident, Own cond, RamPattern queryPattern, + ParallelIndexIfExists(std::string rel, std::size_t ident, Own cond, RamPattern queryPattern, Own nested, std::string profileText = "") : IndexIfExists( rel, ident, std::move(cond), std::move(queryPattern), std::move(nested), profileText) {} diff --git a/src/ram/ParallelIndexScan.h b/src/ram/ParallelIndexScan.h index debde71c672..5847e6e12b5 100644 --- a/src/ram/ParallelIndexScan.h +++ b/src/ram/ParallelIndexScan.h @@ -52,7 +52,7 @@ namespace souffle::ram { */ class ParallelIndexScan : public IndexScan, public AbstractParallel { public: - ParallelIndexScan(std::string rel, int ident, RamPattern queryPattern, Own nested, + ParallelIndexScan(std::string rel, std::size_t ident, RamPattern queryPattern, Own nested, std::string profileText = "") : IndexScan(rel, ident, std::move(queryPattern), std::move(nested), profileText) {} diff --git a/src/ram/ParallelScan.h b/src/ram/ParallelScan.h index fc3421d1efb..198d7ea7502 100644 --- a/src/ram/ParallelScan.h +++ b/src/ram/ParallelScan.h @@ -45,7 +45,7 @@ namespace souffle::ram { */ class ParallelScan : public Scan, public AbstractParallel { public: - ParallelScan(std::string rel, int ident, Own nested, std::string profileText = "") + ParallelScan(std::string rel, std::size_t ident, Own nested, std::string profileText = "") : Scan(rel, ident, std::move(nested), profileText) {} ParallelScan* cloning() const override { diff --git a/src/ram/Relation.h b/src/ram/Relation.h index 2aaf9f9060b..50a364bccb2 100644 --- a/src/ram/Relation.h +++ b/src/ram/Relation.h @@ -80,12 +80,12 @@ class Relation : public Node { } /** @brief Get arity of relation */ - unsigned getArity() const { + std::size_t getArity() const { return arity; } /** @brief Get number of auxiliary attributes */ - unsigned getAuxiliaryArity() const { + std::size_t getAuxiliaryArity() const { return auxiliaryArity; } @@ -103,7 +103,7 @@ class Relation : public Node { out << name; if (arity > 0) { out << "(" << attributeNames[0] << ":" << attributeTypes[0]; - for (unsigned i = 1; i < arity; i++) { + for (std::size_t i = 1; i < arity; i++) { out << ","; out << attributeNames[i] << ":" << attributeTypes[i]; if (i >= arity - auxiliaryArity) { diff --git a/src/ram/RelationOperation.h b/src/ram/RelationOperation.h index 486abcdba30..1d5366df47d 100644 --- a/src/ram/RelationOperation.h +++ b/src/ram/RelationOperation.h @@ -36,7 +36,7 @@ namespace souffle::ram { */ class RelationOperation : public TupleOperation { public: - RelationOperation(std::string rel, int ident, Own nested, std::string profileText = "") + RelationOperation(std::string rel, std::size_t ident, Own nested, std::string profileText = "") : TupleOperation(ident, std::move(nested), std::move(profileText)), relation(std::move(rel)) {} RelationOperation* cloning() const override = 0; diff --git a/src/ram/Scan.h b/src/ram/Scan.h index 508a5a71d94..cbc0553310e 100644 --- a/src/ram/Scan.h +++ b/src/ram/Scan.h @@ -42,7 +42,7 @@ namespace souffle::ram { */ class Scan : public RelationOperation { public: - Scan(std::string rel, int ident, Own nested, std::string profileText = "") + Scan(std::string rel, std::size_t ident, Own nested, std::string profileText = "") : RelationOperation(rel, ident, std::move(nested), std::move(profileText)) {} Scan* cloning() const override { diff --git a/src/ram/TupleElement.h b/src/ram/TupleElement.h index 9a5007b006f..d1fa4f027b5 100644 --- a/src/ram/TupleElement.h +++ b/src/ram/TupleElement.h @@ -38,7 +38,7 @@ class TupleElement : public Expression { public: TupleElement(std::size_t ident, std::size_t elem) : identifier(ident), element(elem) {} /** @brief Get identifier */ - int getTupleId() const { + std::size_t getTupleId() const { return identifier; } diff --git a/src/ram/TupleOperation.h b/src/ram/TupleOperation.h index 38ba80281a8..e752751f8e5 100644 --- a/src/ram/TupleOperation.h +++ b/src/ram/TupleOperation.h @@ -31,18 +31,18 @@ namespace souffle::ram { */ class TupleOperation : public NestedOperation { public: - TupleOperation(int ident, Own nested, std::string profileText = "") + TupleOperation(std::size_t ident, Own nested, std::string profileText = "") : NestedOperation(std::move(nested), std::move(profileText)), identifier(ident) {} TupleOperation* cloning() const override = 0; /** @brief Get identifier */ - int getTupleId() const { + std::size_t getTupleId() const { return identifier; } /** @brief Set identifier */ - void setTupleId(int id) { + void setTupleId(std::size_t id) { identifier = id; } @@ -60,7 +60,7 @@ class TupleOperation : public NestedOperation { * Identifier for the tuple, corresponding to * its position in the loop nest */ - int identifier; + std::size_t identifier; }; } // namespace souffle::ram diff --git a/src/ram/UnpackRecord.h b/src/ram/UnpackRecord.h index 8461480eb41..d2a8cb8a47a 100644 --- a/src/ram/UnpackRecord.h +++ b/src/ram/UnpackRecord.h @@ -45,7 +45,7 @@ namespace souffle::ram { */ class UnpackRecord : public TupleOperation { public: - UnpackRecord(Own nested, int ident, Own expr, std::size_t arity) + UnpackRecord(Own nested, std::size_t ident, Own expr, std::size_t arity) : TupleOperation(ident, std::move(nested)), expression(std::move(expr)), arity(arity) { assert(expression != nullptr && "Expression is a null-pointer"); } diff --git a/src/ram/analysis/Index.cpp b/src/ram/analysis/Index.cpp index cafa8bd64be..795eb2b6dc1 100644 --- a/src/ram/analysis/Index.cpp +++ b/src/ram/analysis/Index.cpp @@ -265,7 +265,7 @@ IndexCluster MinIndexSelectionStrategy::solve(const SearchSet& searches) const { // Should never get no chains back as we never call calculate on an empty graph assert(!chains.empty()); for (const auto& chain : chains) { - std::vector ids; + std::vector ids; SearchSignature initDelta = *(chain.begin()); insertIndex(ids, initDelta); @@ -283,7 +283,7 @@ IndexCluster MinIndexSelectionStrategy::solve(const SearchSet& searches) const { // Validate the lex-order for (auto chain : chains) { for (auto search : chain) { - int idx = map(search, orders, chains); + std::size_t idx = map(search, orders, chains); // Rebuild the search from the order SearchSignature k(search.arity()); @@ -316,9 +316,9 @@ IndexCluster MinIndexSelectionStrategy::solve(const SearchSet& searches) const { return IndexCluster(indexSelection, searches, orders); } -Chain MinIndexSelectionStrategy::getChain(const SearchSignature umn, const MaxMatching::Matchings& match, +Chain MinIndexSelectionStrategy::getChain(const SearchSignature& umn, const MaxMatching::Matchings& match, const SearchBipartiteMap& mapping) const { - SearchSignature start = umn; // start at an unmatched node + const SearchSignature* start = &umn; // start at an unmatched node Chain chain; // given an unmapped node from set A we follow it from set B until it cannot be matched from B // if not matched from B then umn is a chain @@ -326,10 +326,10 @@ Chain MinIndexSelectionStrategy::getChain(const SearchSignature umn, const MaxMa // Assume : no circular mappings, i.e. a in A -> b in B -> ........ -> a in A is not allowed. // Given this, the loop will terminate while (true) { - auto mit = match.find(mapping.getRightNode(start)); // we start from B side + auto mit = match.find(mapping.getRightNode(*start)); // we start from B side // on each iteration we swap sides when collecting the chain so we use the corresponding index map - if (std::find(chain.begin(), chain.end(), start) == chain.end()) { - chain.push_back(start); + if (std::find(chain.begin(), chain.end(), *start) == chain.end()) { + chain.push_back(*start); } if (mit == match.end()) { @@ -337,11 +337,11 @@ Chain MinIndexSelectionStrategy::getChain(const SearchSignature umn, const MaxMa return chain; } - SearchSignature a = mapping.getSearch(mit->second); + const SearchSignature& a = mapping.getSearch(mit->second); if (std::find(chain.begin(), chain.end(), a) == chain.end()) { chain.push_back(a); } - start = a; + start = &a; } } diff --git a/src/ram/analysis/Index.h b/src/ram/analysis/Index.h index 58c8c882483..e7f7ddef89c 100644 --- a/src/ram/analysis/Index.h +++ b/src/ram/analysis/Index.h @@ -98,10 +98,13 @@ class SearchSignature { return Iterator(const_cast(constraints.data() + constraints.size())); } - class Iterator : public std::iterator { + class Iterator { public: - using difference_type = - typename std::iterator::difference_type; + using iterator_category = std::random_access_iterator_tag; + using value_type = AttributeConstraint; + using difference_type = int64_t; + using pointer = AttributeConstraint*; + using reference = AttributeConstraint&; Iterator() : it(nullptr) {} Iterator(AttributeConstraint* rhs) : it(rhs) {} @@ -205,11 +208,11 @@ std::ostream& operator<<(std::ostream& out, const SearchSignature& signature); */ class MaxMatching { public: - using Node = uint32_t; + using Node = std::size_t; /* The nodes of the bi-partite graph are index signatures of RAM operation */ using Nodes = std::unordered_set; /* Distance between nodes */ - using Distance = int; + using Distance = int64_t; /** * Matching represent a solution of the matching, i.e., which node in the bi-partite * graph maps to another node. If no map exist for a node, there is no adjacent edge @@ -233,7 +236,7 @@ class MaxMatching { * @Brief get number of matches in the solution * @return number of matches */ - int getNumMatchings() const { + std::size_t getNumMatchings() const { return match.size() / 2; } @@ -299,7 +302,7 @@ class MaxMatching { * */ -using AttributeIndex = uint32_t; +using AttributeIndex = std::size_t; using AttributeSet = std::unordered_set; using SignatureMap = std::unordered_map; using SearchNodeMap = std::unordered_map; @@ -333,7 +336,7 @@ class IndexCluster; */ class SearchBipartiteMap { public: - void addSearch(SearchSignature s) { + void addSearch(const SearchSignature& s) { // Map the signature to its node in the left and right bi-partitions signatureToNodeA.insert({s, currentIndex}); signatureToNodeB.insert({s, currentIndex + 1}); @@ -343,15 +346,15 @@ class SearchBipartiteMap { currentIndex += 2; } - AttributeIndex getLeftNode(SearchSignature s) const { + AttributeIndex getLeftNode(const SearchSignature& s) const { return signatureToNodeA.at(s); } - AttributeIndex getRightNode(SearchSignature s) const { + AttributeIndex getRightNode(const SearchSignature& s) const { return signatureToNodeB.at(s); } - SearchSignature getSearch(AttributeIndex node) const { + const SearchSignature& getSearch(AttributeIndex node) const { return nodeToSignature.at(node); } @@ -381,7 +384,7 @@ class MinIndexSelectionStrategy : public IndexSelectionStrategy { protected: /** @Brief maps a provided search to its corresponding lexicographical ordering **/ - std::size_t map(SearchSignature cols, [[maybe_unused]] const OrderCollection& orders, + std::size_t map(const SearchSignature& cols, [[maybe_unused]] const OrderCollection& orders, const ChainOrderMap& chainToOrder) const { assert(orders.size() == chainToOrder.size() && "Order and Chain Sizes do not match!!"); @@ -400,7 +403,7 @@ class MinIndexSelectionStrategy : public IndexSelectionStrategy { } /** @Brief insert an index based on the delta */ - void insertIndex(LexOrder& ids, SearchSignature delta) const { + void insertIndex(LexOrder& ids, const SearchSignature& delta) const { LexOrder backlog; // add inequalities at the end for (std::size_t pos = 0; pos < delta.arity(); pos++) { if (delta[pos] == AttributeConstraint::Equal) { @@ -420,7 +423,7 @@ class MinIndexSelectionStrategy : public IndexSelectionStrategy { * we follow it from set B until it cannot be matched from B * if not matched from B then umn is a chain. */ - Chain getChain(const SearchSignature umn, const MaxMatching::Matchings& match, + Chain getChain(const SearchSignature& umn, const MaxMatching::Matchings& match, const SearchBipartiteMap& mapping) const; /** @Brief get all chains from the matching */ @@ -433,7 +436,7 @@ class MinIndexSelectionStrategy : public IndexSelectionStrategy { SearchSet unmatched; // For all nodes n such that n is not in match - for (auto node : nodes) { + for (const auto& node : nodes) { if (match.find(mapping.getLeftNode(node)) == match.end()) { unmatched.insert(node); } @@ -459,17 +462,18 @@ class IndexCluster { const SearchCollection getSearches() const { return searches; } - const LexOrder getLexOrder(SearchSignature cols) const { + const LexOrder getLexOrder(const SearchSignature& cols) const { return indexSelection.at(cols); } - int getLexOrderNum(SearchSignature cols) const { + std::size_t getLexOrderNum(const SearchSignature& cols) const { // get the corresponding order auto order = getLexOrder(cols); // find the order in the collection auto it = std::find(orders.begin(), orders.end(), order); + assert(it != orders.end()); // return its relative index - return std::distance(orders.begin(), it); + return static_cast(std::distance(orders.begin(), it)); } private: diff --git a/src/ram/analysis/Level.cpp b/src/ram/analysis/Level.cpp index f7676b685ed..380a4fe40f5 100644 --- a/src/ram/analysis/Level.cpp +++ b/src/ram/analysis/Level.cpp @@ -56,222 +56,240 @@ namespace souffle::ram::analysis { -int LevelAnalysis::getLevel(const Node* node) const { +std::optional LevelAnalysis::getLevel(const Node* node) const { // visitor - class ValueLevelVisitor : public Visitor { - using Visitor::visit_; + using maybe_level = std::optional; + class ValueLevelVisitor : public Visitor { + using Visitor::visit_; + + static maybe_level max(const maybe_level& a, const maybe_level& b) { + if (a.has_value() && b.has_value()) { + return std::max(*a, *b); + } else if (a.has_value()) { + return a; + } else if (b.has_value()) { + return b; + } else { + return std::nullopt; + } + } public: // string constant - int visit_(type_identity, const StringConstant&) override { - return -1; + maybe_level visit_(type_identity, const StringConstant&) override { + return std::nullopt; } // number constant - int visit_(type_identity, const NumericConstant&) override { - return -1; + maybe_level visit_(type_identity, const NumericConstant&) override { + return std::nullopt; } // true - int visit_(type_identity, const True&) override { - return -1; + maybe_level visit_(type_identity, const True&) override { + return std::nullopt; } // false - int visit_(type_identity, const False&) override { - return -1; + maybe_level visit_(type_identity, const False&) override { + return std::nullopt; } // tuple element access - int visit_(type_identity, const TupleElement& elem) override { + maybe_level visit_(type_identity, const TupleElement& elem) override { return elem.getTupleId(); } // scan - int visit_(type_identity, const Scan&) override { - return -1; + maybe_level visit_(type_identity, const Scan&) override { + return std::nullopt; } // index scan - int visit_(type_identity, const IndexScan& indexScan) override { - int level = -1; + maybe_level visit_(type_identity, const IndexScan& indexScan) override { + maybe_level level = std::nullopt; for (auto& index : indexScan.getRangePattern().first) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } for (auto& index : indexScan.getRangePattern().second) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } return level; } // choice - int visit_(type_identity, const IfExists& choice) override { - return std::max(-1, dispatch(choice.getCondition())); + maybe_level visit_(type_identity, const IfExists& choice) override { + return max(-1, dispatch(choice.getCondition())); } // index choice - int visit_(type_identity, const IndexIfExists& indexIfExists) override { - int level = -1; + maybe_level visit_(type_identity, const IndexIfExists& indexIfExists) override { + maybe_level level = std::nullopt; for (auto& index : indexIfExists.getRangePattern().first) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } for (auto& index : indexIfExists.getRangePattern().second) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } - return std::max(level, dispatch(indexIfExists.getCondition())); + return max(level, dispatch(indexIfExists.getCondition())); } // aggregate - int visit_(type_identity, const Aggregate& aggregate) override { - return std::max(dispatch(aggregate.getExpression()), dispatch(aggregate.getCondition())); + maybe_level visit_(type_identity, const Aggregate& aggregate) override { + return max(dispatch(aggregate.getExpression()), dispatch(aggregate.getCondition())); } // index aggregate - int visit_(type_identity, const IndexAggregate& indexAggregate) override { - int level = -1; + maybe_level visit_(type_identity, const IndexAggregate& indexAggregate) override { + maybe_level level = std::nullopt; for (auto& index : indexAggregate.getRangePattern().first) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } for (auto& index : indexAggregate.getRangePattern().second) { - level = std::max(level, dispatch(*index)); + level = max(level, dispatch(*index)); } - level = std::max(dispatch(indexAggregate.getExpression()), level); - return std::max(level, dispatch(indexAggregate.getCondition())); + level = max(dispatch(indexAggregate.getExpression()), level); + return max(level, dispatch(indexAggregate.getCondition())); } // unpack record - int visit_(type_identity, const UnpackRecord& unpack) override { + maybe_level visit_(type_identity, const UnpackRecord& unpack) override { return dispatch(unpack.getExpression()); } // filter - int visit_(type_identity, const Filter& filter) override { + maybe_level visit_(type_identity, const Filter& filter) override { return dispatch(filter.getCondition()); } // break - int visit_(type_identity, const Break& b) override { + maybe_level visit_(type_identity, const Break& b) override { return dispatch(b.getCondition()); } // guarded insert - int visit_(type_identity, const GuardedInsert& guardedInsert) override { - int level = -1; + maybe_level visit_(type_identity, const GuardedInsert& guardedInsert) override { + maybe_level level = std::nullopt; for (auto& exp : guardedInsert.getValues()) { - level = std::max(level, dispatch(*exp)); + level = max(level, dispatch(*exp)); } - level = std::max(level, dispatch(*guardedInsert.getCondition())); + level = max(level, dispatch(*guardedInsert.getCondition())); return level; } // insert - int visit_(type_identity, const Insert& insert) override { - int level = -1; + maybe_level visit_(type_identity, const Insert& insert) override { + maybe_level level = std::nullopt; for (auto& exp : insert.getValues()) { - level = std::max(level, dispatch(*exp)); + level = max(level, dispatch(*exp)); } return level; } // return - int visit_(type_identity, const SubroutineReturn& ret) override { - int level = -1; + maybe_level visit_(type_identity, const SubroutineReturn& ret) override { + maybe_level level = std::nullopt; for (auto& exp : ret.getValues()) { - level = std::max(level, dispatch(*exp)); + level = max(level, dispatch(*exp)); } return level; } // auto increment - int visit_(type_identity, const AutoIncrement&) override { - return -1; + maybe_level visit_(type_identity, const AutoIncrement&) override { + return std::nullopt; } // undef value - int visit_(type_identity, const UndefValue&) override { - return -1; + maybe_level visit_(type_identity, const UndefValue&) override { + return std::nullopt; } // intrinsic functors - int visit_(type_identity, const IntrinsicOperator& op) override { - int level = -1; + maybe_level visit_(type_identity, const IntrinsicOperator& op) override { + maybe_level level = std::nullopt; for (const auto& arg : op.getArguments()) { - level = std::max(level, dispatch(*arg)); + level = max(level, dispatch(*arg)); } return level; } // pack operator - int visit_(type_identity, const PackRecord& pack) override { - int level = -1; + maybe_level visit_(type_identity, const PackRecord& pack) override { + maybe_level level = std::nullopt; for (const auto& arg : pack.getArguments()) { - level = std::max(level, dispatch(*arg)); + level = max(level, dispatch(*arg)); } return level; } // argument - int visit_(type_identity, const SubroutineArgument&) override { - return -1; + maybe_level visit_(type_identity, const SubroutineArgument&) override { + return std::nullopt; } // user defined operator - int visit_(type_identity, const UserDefinedOperator& op) override { - int level = -1; + maybe_level visit_(type_identity, const UserDefinedOperator& op) override { + maybe_level level = std::nullopt; for (const auto& arg : op.getArguments()) { - level = std::max(level, dispatch(*arg)); + level = max(level, dispatch(*arg)); } return level; } // conjunction - int visit_(type_identity, const Conjunction& conj) override { - return std::max(dispatch(conj.getLHS()), dispatch(conj.getRHS())); + maybe_level visit_(type_identity, const Conjunction& conj) override { + return max(dispatch(conj.getLHS()), dispatch(conj.getRHS())); } // negation - int visit_(type_identity, const Negation& neg) override { + maybe_level visit_(type_identity, const Negation& neg) override { return dispatch(neg.getOperand()); } // constraint - int visit_(type_identity, const Constraint& binRel) override { - return std::max(dispatch(binRel.getLHS()), dispatch(binRel.getRHS())); + maybe_level visit_(type_identity, const Constraint& binRel) override { + return max(dispatch(binRel.getLHS()), dispatch(binRel.getRHS())); } // existence check - int visit_(type_identity, const ExistenceCheck& exists) override { - int level = -1; + maybe_level visit_(type_identity, const ExistenceCheck& exists) override { + maybe_level level = std::nullopt; for (const auto& cur : exists.getValues()) { - level = std::max(level, dispatch(*cur)); + level = max(level, dispatch(*cur)); } return level; } // provenance existence check - int visit_(type_identity, + maybe_level visit_(type_identity, const ProvenanceExistenceCheck& provExists) override { - int level = -1; + maybe_level level = std::nullopt; for (const auto& cur : provExists.getValues()) { - level = std::max(level, dispatch(*cur)); + level = max(level, dispatch(*cur)); } return level; } // emptiness check - int visit_(type_identity, const EmptinessCheck&) override { - return -1; // can be in the top level + maybe_level visit_(type_identity, const EmptinessCheck&) override { + return std::nullopt; // can be in the top level } // default rule - int visit_(type_identity, const Node&) override { + maybe_level visit_(type_identity, const Node&) override { fatal("Node not implemented!"); } }; assert((isA(node) || isA(node) || isA(node)) && "not an expression/condition/operation"); - return ValueLevelVisitor().dispatch(*node); + auto res = ValueLevelVisitor().dispatch(*node); + return res; +} + +bool LevelAnalysis::hasLevel(const Node* value) const { + return getLevel(value).has_value(); } } // namespace souffle::ram::analysis diff --git a/src/ram/analysis/Level.h b/src/ram/analysis/Level.h index 9f0f99e3e04..85980701dab 100644 --- a/src/ram/analysis/Level.h +++ b/src/ram/analysis/Level.h @@ -22,6 +22,8 @@ #include "ram/TranslationUnit.h" #include "ram/analysis/Relation.h" +#include + namespace souffle::ram::analysis { /** @@ -34,7 +36,7 @@ namespace souffle::ram::analysis { * smallest tuple-id and the most inner-loop has the largest tuple-id number. * * If an expression/condition does not contain an TupleElement accessing an element - * of a tuple, the analysis yields -1 for denoting that the expression/condition + * of a tuple, the analysis yields nullopt for denoting that the expression/condition * can be executed outside of the loop-nest, i.e., the expression/condition is * independent of data stemming from relations. * @@ -52,7 +54,9 @@ class LevelAnalysis : public Analysis { /** * @brief Get level of a RAM expression/condition */ - int getLevel(const Node* value) const; + std::optional getLevel(const Node* value) const; + + bool hasLevel(const Node* value) const; protected: RelationAnalysis* ra{nullptr}; diff --git a/src/ram/tests/max_matching_test.cpp b/src/ram/tests/max_matching_test.cpp index 79ed0785043..9e019fe68a4 100644 --- a/src/ram/tests/max_matching_test.cpp +++ b/src/ram/tests/max_matching_test.cpp @@ -40,7 +40,7 @@ TEST(Matching, StaticTest_1) { match.addEdge(6, 12); match.solve(); - int num = match.getNumMatchings(); + std::size_t num = match.getNumMatchings(); EXPECT_EQ(num, 5); } @@ -60,7 +60,7 @@ TEST(Matching, StaticTest_2) { match.addEdge(5, 9); match.solve(); - int num = match.getNumMatchings(); + std::size_t num = match.getNumMatchings(); EXPECT_EQ(num, 5); } diff --git a/src/ram/transform/HoistAggregate.cpp b/src/ram/transform/HoistAggregate.cpp index 70ed354d555..673e0c7a9d4 100644 --- a/src/ram/transform/HoistAggregate.cpp +++ b/src/ram/transform/HoistAggregate.cpp @@ -45,7 +45,7 @@ bool HoistAggregateTransformer::hoistAggregate(Program& program) { if (isA(node)) { auto* tupleOp = as(node); assert(tupleOp != nullptr && "aggregate conversion to tuple operation failed"); - if (rla->getLevel(tupleOp) == -1 && !priorTupleOp) { + if (!rla->hasLevel(tupleOp) && !priorTupleOp) { changed = true; newAgg = clone(tupleOp); assert(newAgg != nullptr && "failed to make a cloning"); @@ -68,16 +68,16 @@ bool HoistAggregateTransformer::hoistAggregate(Program& program) { // hoist a single aggregate to an outer scope that is data-dependent on a prior operation. forEachQuery(program, [&](Query& query) { - int newLevel = -1; + std::optional newLevel; Own newAgg; - int priorOpLevel = -1; + std::optional priorOpLevel; query.apply(nodeMapper([&](auto&& go, Own node) -> Own { if (as(node)) { auto* tupleOp = as(node); assert(tupleOp != nullptr && "aggregate conversion to nested operation failed"); - int dataDepLevel = rla->getLevel(tupleOp); - if (dataDepLevel != -1 && dataDepLevel < tupleOp->getTupleId() - 1) { + const auto dataDepLevel = rla->getLevel(tupleOp); + if (dataDepLevel.has_value() && (*dataDepLevel + 1) < tupleOp->getTupleId()) { // If all tuple ops between the data-dependence level and agg // are aggregates, then we do not hoist, i.e., we would // continuously swap their positions. @@ -95,7 +95,7 @@ bool HoistAggregateTransformer::hoistAggregate(Program& program) { node->apply(go); if (auto* search = as(node)) { - if (newAgg != nullptr && search->getTupleId() == newLevel) { + if (newAgg != nullptr && newLevel.has_value() && search->getTupleId() == *newLevel) { newAgg->rewrite(&newAgg->getOperation(), clone(search->getOperation())); search->rewrite(&search->getOperation(), std::move(newAgg)); } diff --git a/src/ram/transform/HoistConditions.cpp b/src/ram/transform/HoistConditions.cpp index 0f53dff081e..334d62ab8c2 100644 --- a/src/ram/transform/HoistConditions.cpp +++ b/src/ram/transform/HoistConditions.cpp @@ -49,7 +49,7 @@ bool HoistConditionsTransformer::hoistConditions(Program& program) { const Condition& condition = filter->getCondition(); // if filter condition is independent of any TupleOperation, // delete the filter operation and collect condition - if (rla->getLevel(&condition) == -1) { + if (!rla->hasLevel(&condition)) { changed = true; newCondition = addCondition(std::move(newCondition), clone(condition)); node->apply(go); diff --git a/src/ram/transform/IfExistsConversion.cpp b/src/ram/transform/IfExistsConversion.cpp index 64c7c99576c..63e320558cb 100644 --- a/src/ram/transform/IfExistsConversion.cpp +++ b/src/ram/transform/IfExistsConversion.cpp @@ -52,7 +52,7 @@ Own IfExistsConversionTransformer::rewriteScan(const Scan* scan) { // Convert the Scan/If pair into a IfExists if (transformTuple) { const auto* filter = as(scan->getOperation()); - const int identifier = scan->getTupleId(); + const std::size_t identifier = scan->getTupleId(); return mk(scan->getRelation(), identifier, clone(filter->getCondition()), clone(filter->getOperation()), scan->getProfileText()); @@ -100,7 +100,7 @@ Own IfExistsConversionTransformer::rewriteIndexScan(const IndexScan* RamPattern newValues = make_pair( clone(indexScan->getRangePattern().first), clone(indexScan->getRangePattern().second)); const auto* filter = as(indexScan->getOperation()); - const int identifier = indexScan->getTupleId(); + const std::size_t identifier = indexScan->getTupleId(); const std::string& rel = indexScan->getRelation(); return mk(rel, identifier, clone(filter->getCondition()), std::move(newValues), clone(filter->getOperation()), indexScan->getProfileText()); diff --git a/src/ram/transform/MakeIndex.cpp b/src/ram/transform/MakeIndex.cpp index 0837be977e2..2bde8093138 100644 --- a/src/ram/transform/MakeIndex.cpp +++ b/src/ram/transform/MakeIndex.cpp @@ -44,7 +44,7 @@ namespace souffle::ram::transform { using ExpressionPair = std::pair, Own>; ExpressionPair MakeIndexTransformer::getExpressionPair( - const Constraint* binRelOp, std::size_t& element, int identifier) { + const Constraint* binRelOp, std::size_t& element, const std::optional& identifier) { if (isLessEqual(binRelOp->getOperator())) { // Tuple[level, element] <= if (const auto* lhs = as(binRelOp->getLHS())) { @@ -87,8 +87,8 @@ ExpressionPair MakeIndexTransformer::getExpressionPair( // Retrieves the <= Tuple[level, element] <= part of the constraint as a pair { , // } -ExpressionPair MakeIndexTransformer::getLowerUpperExpression( - Condition* c, std::size_t& element, int identifier, RelationRepresentation rep) { +ExpressionPair MakeIndexTransformer::getLowerUpperExpression(Condition* c, std::size_t& element, + const std::optional& identifier, RelationRepresentation rep) { if (auto* binRelOp = as(c)) { bool interpreter = !Global::config().has("compile") && !Global::config().has("dl-program") && !Global::config().has("generate") && !Global::config().has("swig"); @@ -139,7 +139,7 @@ ExpressionPair MakeIndexTransformer::getLowerUpperExpression( } Own MakeIndexTransformer::constructPattern(const std::vector& attributeTypes, - RamPattern& queryPattern, bool& indexable, VecOwn conditionList, int identifier, + RamPattern& queryPattern, bool& indexable, VecOwn conditionList, std::size_t identifier, RelationRepresentation rep) { // Remaining conditions which cannot be handled by an index Own condition; @@ -405,9 +405,9 @@ Own MakeIndexTransformer::constructPattern(const std::vector MakeIndexTransformer::rewriteAggregate(const Aggregate* agg) { if (!isA(agg->getCondition())) { const Relation& rel = relAnalysis->lookup(agg->getRelation()); - int identifier = agg->getTupleId(); + std::size_t identifier = agg->getTupleId(); RamPattern queryPattern; - for (unsigned int i = 0; i < rel.getArity(); ++i) { + for (std::size_t i = 0; i < rel.getArity(); ++i) { queryPattern.first.push_back(mk()); queryPattern.second.push_back(mk()); } @@ -427,9 +427,9 @@ Own MakeIndexTransformer::rewriteAggregate(const Aggregate* agg) { Own MakeIndexTransformer::rewriteScan(const Scan* scan) { if (const auto* filter = as(scan->getOperation())) { const Relation& rel = relAnalysis->lookup(scan->getRelation()); - const int identifier = scan->getTupleId(); + const std::size_t identifier = scan->getTupleId(); RamPattern queryPattern; - for (unsigned int i = 0; i < rel.getArity(); ++i) { + for (std::size_t i = 0; i < rel.getArity(); ++i) { queryPattern.first.push_back(mk()); queryPattern.second.push_back(mk()); } @@ -452,7 +452,7 @@ Own MakeIndexTransformer::rewriteScan(const Scan* scan) { Own MakeIndexTransformer::rewriteIndexScan(const IndexScan* iscan) { if (const auto* filter = as(iscan->getOperation())) { const Relation& rel = relAnalysis->lookup(iscan->getRelation()); - const int identifier = iscan->getTupleId(); + const std::size_t identifier = iscan->getTupleId(); RamPattern strengthenedPattern; strengthenedPattern.first = clone(iscan->getRangePattern().first); diff --git a/src/ram/transform/MakeIndex.h b/src/ram/transform/MakeIndex.h index 9d204004518..55b799c5b2c 100644 --- a/src/ram/transform/MakeIndex.h +++ b/src/ram/transform/MakeIndex.h @@ -79,9 +79,10 @@ class MakeIndexTransformer : public Transformer { */ using ExpressionPair = std::pair, Own>; - ExpressionPair getExpressionPair(const Constraint* binRelOp, std::size_t& element, int identifier); - ExpressionPair getLowerUpperExpression( - Condition* c, std::size_t& element, int level, RelationRepresentation rep); + ExpressionPair getExpressionPair( + const Constraint* binRelOp, std::size_t& element, const std::optional& identifier); + ExpressionPair getLowerUpperExpression(Condition* c, std::size_t& element, + const std::optional& level, RelationRepresentation rep); /** * @param AttributeTypes to indicate type of each attribute in the relation @@ -93,7 +94,8 @@ class MakeIndexTransformer : public Transformer { * @result Remaining conditions that could not be transformed to an index */ Own constructPattern(const std::vector& attributeTypes, RamPattern& queryPattern, - bool& indexable, VecOwn conditionList, int identifier, RelationRepresentation rep); + bool& indexable, VecOwn conditionList, std::size_t identifier, + RelationRepresentation rep); /** * @brief Rewrite a scan operation to an indexed scan operation diff --git a/src/ram/transform/TupleId.cpp b/src/ram/transform/TupleId.cpp index fbfc31fed60..7a78c09e2f9 100644 --- a/src/ram/transform/TupleId.cpp +++ b/src/ram/transform/TupleId.cpp @@ -31,8 +31,8 @@ bool TupleIdTransformer::reorderOperations(Program& program) { bool changed = false; forEachQuery(program, [&](Query& query) { // Maps old tupleIds to new tupleIds - std::map reorder; - int ctr = 0; + std::map reorder; + std::size_t ctr = 0; visit(query, [&](TupleOperation& search) { if (ctr != search.getTupleId()) { diff --git a/src/reports/ErrorReport.h b/src/reports/ErrorReport.h index aefac81260d..043dc78afe6 100644 --- a/src/reports/ErrorReport.h +++ b/src/reports/ErrorReport.h @@ -149,17 +149,17 @@ class ErrorReport { ErrorReport(const ErrorReport& other) = default; - unsigned getNumErrors() const { + std::size_t getNumErrors() const { return std::count_if(diagnostics.begin(), diagnostics.end(), - [](Diagnostic d) -> bool { return d.getType() == Diagnostic::Type::ERROR; }); + [](const Diagnostic& d) -> bool { return d.getType() == Diagnostic::Type::ERROR; }); } - unsigned getNumWarnings() const { + std::size_t getNumWarnings() const { return std::count_if(diagnostics.begin(), diagnostics.end(), - [](Diagnostic d) -> bool { return d.getType() == Diagnostic::Type::WARNING; }); + [](const Diagnostic& d) -> bool { return d.getType() == Diagnostic::Type::WARNING; }); } - unsigned getNumIssues() const { + std::size_t getNumIssues() const { return diagnostics.size(); } diff --git a/src/souffle-compile.in b/src/souffle-compile.in deleted file mode 100755 index eaf44b3d3bc..00000000000 --- a/src/souffle-compile.in +++ /dev/null @@ -1,215 +0,0 @@ -#!/bin/bash -# -# Souffle - A Datalog Compiler -# Copyright (c) 2013-14, Oracle and/or its affiliates. All rights reserved. -# Licensed under the Universal Permissive License v 1.0 as shown at: -# - https://opensource.org/licenses/UPL -# - /licenses/SOUFFLE-UPL.txt - -# -# script that compiles a generated C++ program and executes it -# - -set -e - -# Show usage -usage() { - printf "Name: - souffle-compile - compile a C++ source file generated by souffle -Usage: - souffle-compile [options] .cpp -Options: - -h show usage - -g build in debug mode - -l additional shared libraries - -L library paths - -t build in test mode, implies '-gw' and compiles using '-Werror' - -v verbose output - -w enable warnings - -s use SWIG interface to generate into language\n" - - exit 1; -} - -# Print a message to STDERR and exit. If the second argument (exit code) -# is provided and it is '0' then do nothing, otherwise print the error message -# along with the usage (if the third argument is defined) -error() { - if [ -z "$2" ] || ! [ "$2" = 0 ]; then - echo "souffle-compile error: $1" 1>&2 - if [ -n "$3" ]; then - usage; - fi - exit 1; - fi -} - -# set by autoconf -CXX="$(printenv CXX || true)" -test -z "$CXX" && CXX="@CXX@" - -read -ra CPPFLAGS < <(printenv CPPFLAGS || true) || true -read -ra CXXFLAGS < <(printenv CXXFLAGS || true) || true -read -ra LDFLAGS < <(printenv LDFLAGS || true) || true -read -ra LIBS < <(printenv LIBS || true) || true - -CPPFLAGS+=( "-std=c++17" @CPPFLAGS@ ) -CXXFLAGS+=( "-march=native" @SOUFFLE_CXXFLAGS@ ) -LDFLAGS+=( @LDFLAGS@ ) -LIBS+=( @LIBS@ ) - -# set by command flags -WARNINGS="" -SWIGLANG="" - -# find header files of souffle -DISTRO_DIR="$(dirname "$0")" -HEADER_DIRS=( - "-I$DISTRO_DIR/../include" - "-I$DISTRO_DIR/include" - "-I$DISTRO_DIR/../include/souffle/swig" - "-I$DISTRO_DIR/include/souffle/swig" -) - -# This is ugly. Once we switch to cmake, we need to figure out a cleaner -# way to handle things like these. The headers, and souffle's dependence -# on this shell script are just a bit strange -CMAKE_HEADER_DIRS="@CMAKE_HEADER_DIRS@" -if [ -n "$CMAKE_HEADER_DIRS" ]; then - HEADER_DIRS+=("-I$CMAKE_HEADER_DIRS") -fi - -# Options processing via getopts builtin, it is very limiting but on OSX the -# default getopt is an old BSD getopt, so need this for portability -while getopts "hwtl:L:vgs:" opt; do - case "$opt" in - h|\?) # Show usage and exit - usage; - ;; - g) # enable debug mode - CXXFLAGS=( "${CXXFLAGS[@]/#-O[0-9s]/-O0}" -g -O0 ) - ;; - L) # enable shared library - LDFLAGS+=("-L${OPTARG}") - ;; - l) # enable shared library - LIBS+=("-l${OPTARG}") - ;; - t) # enable testing mode (debug + warnings + `-Werror`) - CXXFLAGS=( "${CXXFLAGS[@]/#-O[0-9s]/-O0}" -g -O0 -Wall -Wextra -Werror ) - WARNINGS="1" - ;; - w) # enable warnings - CXXFLAGS+=( -Wall -Wextra ) - WARNINGS="1" - ;; - v) # Verbose output - set -x - ;; - s) # Set swig language - SWIGLANG="${OPTARG}"; - ;; - esac -done - -# Shift positional arguments -shift $((OPTIND - 1)) - -# Show usage if no input is given -test -n "$1" || error "no input file" $? 1 || true -# Check if the input file exists -test -f "$1" || error "cannot open source file: '$1'" $? 1 || true -# Check if the input file has a valid extension -exe=$(basename "$1" .cpp) -test "$1" != "$exe" || error "source file is not a .cpp file: '$1'" $? 1 || true - -# Ensure binary is compiled to same directory as cpp file -cd "$(dirname "$1")" -dir="$PWD" -cd "$OLDPWD" - -# Make temp folder and copy relevant files there -if [ -n "$SWIGLANG" ] -then - # set environment variables - # directory where swig interface files are in souffle - TMP_DIR="$(mktemp -d)" - BASE_DIR="$(dirname "$0")" - - if [ -n "$CMAKE_HEADER_DIRS" ]; then - cp "$CMAKE_HEADER_DIRS/souffle/swig/SwigInterface.h" "$TMP_DIR" - cp "$CMAKE_HEADER_DIRS/souffle/swig/SwigInterface.i" "$TMP_DIR" - elif [ -d "$BASE_DIR/include/souffle/swig" ]; then - cp "$BASE_DIR/include/souffle/swig/SwigInterface.h" "$TMP_DIR" - cp "$BASE_DIR/include/souffle/swig/SwigInterface.i" "$TMP_DIR" - else - cp "$BASE_DIR/../include/souffle/swig/SwigInterface.h" "$TMP_DIR" - cp "$BASE_DIR/../include/souffle/swig/SwigInterface.i" "$TMP_DIR" - fi - - cd "$TMP_DIR" - swig -c++ -"$SWIGLANG" "SwigInterface.i" - - # Checks input language and compiles files with local python3 config or local java_home config - if [ "$SWIGLANG" = "python" ] - then - # Python adds -Wall -Wextra - read -ra PYTHON_FLAGS < <(python3-config --cflags | sed 's/-Wall\|-Wextra//g') - read -ra PYTHON_LDFLAGS < <(python3-config --ldflags) - $CXX -fPIC -c -D__EMBEDDED_SOUFFLE__ "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" SwigInterface_wrap.cxx "$dir/$exe.cpp" -Wformat "${PYTHON_FLAGS[@]}" "${HEADER_DIRS[@]}" 2>&1 - $CXX -shared "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" SwigInterface_wrap.o "$exe.o" "${PYTHON_LDFLAGS[@]}" -o _SwigInterface.so "${LDFLAGS[@]}" "${LIBS[@]}" -lstdc++ - elif [ "$SWIGLANG" = "java" ] - then - readarray -t -d ';' JNI_INCLUDES < <(printf "%s" "@SOUFFLE_JAVA_INCLUDE_PATH@") - - if [ ${#JNI_INCLUDES[@]} -eq 0 ]; then - JAVADIR="$(printenv JAVA_HOME || java -XshowSettings:properties -version 2>&1 | grep java.home | sed 's/^.*= //')" - - if [ -z "$JAVADIR" ] - then - error "JAVA_HOME environment variable has not been set" - fi - - JNI_INCLUDES=( - "-I$JAVADIR" - "-I$JAVADIR/include" - "-I$JAVADIR/include/linux" - "-I$JAVADIR/include/darwin" - ) - fi - - $CXX -fPIC -c -D__EMBEDDED_SOUFFLE__ "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" SwigInterface_wrap.cxx "$dir/$exe.cpp" "${JNI_INCLUDES[@]}" -I/usr/lib/x86_64-linux-gnu/ "${HEADER_DIRS[@]}" 2>&1 - $CXX -shared "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" SwigInterface_wrap.o "$exe.o" -o libSwigInterface.so "${LDFLAGS[@]}" "${LIBS[@]}" -lstdc++ - fi - - # move generated files to same directory as cpp file - for file in $(ls | grep -v "SwigInterface.i" | grep -v "SwigInterface.h") - do - mv "$file" "$dir" - done - - rm -rf "$TMP_DIR" - exit 0 -fi - -# Compile -rm -f "$dir/$exe" -CCERR=$(mktemp) -# HACK: don't exit if the compile fails, we need to report the error -( "$CXX" "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" "-o$dir/$exe" "$1" "${HEADER_DIRS[@]}" $OMP_FLAG "${LDFLAGS[@]}" "${LIBS[@]}" 2> "$CCERR" ) || true - -if [ ! -f "$dir/$exe" ]; then - printf "compiler error: cannot compile source file \"%s\"\n" "$1" 1>&2 - printf "%s" "$(printf "\"%s\" " "$CXX" "${CXXFLAGS[@]}" "${CPPFLAGS[@]}" "-o$dir/$exe" "$1" "${HEADER_DIRS[@]}" $OMP_FLAG "${LDFLAGS[@]}" "${LIBS[@]}")" - echo "" -fi - -if [ ! -f "$dir/$exe" ] || [ "$WARNINGS" = 1 ]; then - cat "$CCERR" 1>&2 -fi - -rm "$CCERR" - -if [ ! -f "$dir/$exe" ]; then - exit 1 -fi diff --git a/src/souffle-compile.template.py b/src/souffle-compile.template.py new file mode 100644 index 00000000000..0180bda3ae3 --- /dev/null +++ b/src/souffle-compile.template.py @@ -0,0 +1,215 @@ + +# --- JSON_DATA_TEXT variable is inserted before this line --- + +## Example of JSON_DATA_TEXT +if not JSON_DATA_TEXT: + JSON_DATA_TEXT = """{ + "compiler": "/usr/bin/c++", + "compiler_id": "GNU", + "compiler_version": "8.3.0", + "msvc_version": "", + "includes": "-I/usr/include", + "std_flag": "-std=c++17", + "cxx_flags": " -fopenmp", + "cxx_link_flags": "", + "release_cxx_flags": "-O3 ", + "debug_cxx_flags": "-g", + "definitions": "-DRAM_DOMAIN_SIZE=64 -DUSE_NCURSES -DUSE_LIBZ -DUSE_SQLITE", + "compile_options": "", + "link_options": "-pthread -ldl -lstdc++fs /usr/lib/x86_64-linux-gnu/libsqlite3.so /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libncurses.so", + "rpaths": "/usr/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu", + "outname_fmt": "-o {}", + "libdir_fmt": "-L{}", + "libname_fmt": "-l{}", + "rpath_fmt": "-Wl,-rpath,{}", + "path_delimiter": ":", + "exe_extension": "", + "source_include_dir": "", + "jni_includes": "" + }""" + +import argparse +import json +import os +import pathlib +import shutil +import subprocess +import sys +import tempfile + +# run command and return status object +def launch_command(cmd, descr, verbose=False): + if verbose: + sys.stdout.write(cmd + "\n") + status = subprocess.run(cmd, capture_output=True, text=True, shell=True) + if status.returncode != 0: + sys.stdout.write(status.stdout) + sys.stderr.write(status.stderr) + raise RuntimeError("Error: {}. Command: {}".format(descr, cmd)) + return status + +# run command and return the standard output as a string +def capture_command_output(cmd, descr, verbose=False): + status = launch_command(cmd, descr, verbose) + return status.stdout + + +conf = json.loads(JSON_DATA_TEXT) +OUTNAME_FMT = conf['outname_fmt'] +LIBDIR_FMT = conf['libdir_fmt'] +LIBNAME_FMT = conf['libname_fmt'] +RPATH_FMT = conf['rpath_fmt'] +PATH_DELIMITER = conf['path_delimiter'] +RPATHS = conf['rpaths'].split(PATH_DELIMITER) +exeext = conf['exe_extension'] +SOURCE_INCLUDE_DIR = conf['source_include_dir'] +JNI_INCLUDES = conf['jni_includes'].split(PATH_DELIMITER) + +workdir = os.getcwd() +scriptdir = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) + +parser = argparse.ArgumentParser(description="Compile a C++ source file generated by Souffle") +parser.add_argument('-l', action='append', default=[], metavar='LIBNAME', dest='lib_names', type=str, help="Basename of a functors library. eg: `-l functors` => libfunctors.dll") +parser.add_argument('-L', action='append', default=[], metavar='LIBDIR', dest='lib_dirs', type=lambda p: pathlib.Path(p).absolute(), help="Search directory for functors libraries") +parser.add_argument('-g', action='store_true', dest='debug', help="Debug build type") +parser.add_argument('-s', metavar='LANG', dest='swiglang', choices=["java", "python"], help="use SWIG interface to generate into LANG language") +parser.add_argument('-v', action='store_true', dest='verbose', help="Verbose output") +parser.add_argument('source', metavar='SOURCE', type=lambda p: pathlib.Path(p).absolute(), help="C++ source file") + +args = parser.parse_args() + +stemname = args.source.stem +dirname = args.source.parent + +if not os.path.isfile(args.source): + raise RuntimeError("Cannot open source file: '{}'".format(args.source)) + +# Check if the input file has a valid extension +extname = args.source.suffix +if extname != ".cpp": + raise RuntimeError("Source file is not a .cpp file: '{}'".format(args.source)) + +# Search for Souffle includes directory +souffle_include_dir = None +if (scriptdir / "include" / "souffle").exists(): + souffle_include_dir = scriptdir / "include" / "souffle" +elif (scriptdir / ".." / "include" / "souffle").exists(): + souffle_include_dir = scriptdir / ".." / "include" / "souffle" +elif SOURCE_INCLUDE_DIR and (pathlib.Path(SOURCE_INCLUDE_DIR) / "souffle").exists(): + souffle_include_dir = (pathlib.Path(SOURCE_INCLUDE_DIR) / "souffle") + +if args.swiglang: + if not (souffle_include_dir and (souffle_include_dir / "swig").exists()): + raise RuntimeError("Cannot find 'souffle/swig' include directory") + + swig_include_dir = (souffle_include_dir / "swig") + with tempfile.TemporaryDirectory() as tmpdir: + shutil.copy(swig_include_dir / "SwigInterface.h", tmpdir) + shutil.copy(swig_include_dir / "SwigInterface.i", tmpdir) + + os.chdir(tmpdir) + launch_command("swig -c++ -\"{}\" SwigInterface.i".format(args.swiglang), "SWIG generation", verbose=args.verbose) + + if args.swiglang == "python": + swig_flags = capture_command_output("python3-config --cflags", "Python config", verbose=args.verbose) + swig_ldflags = capture_command_output("python3-config --ldflags", "Python config", verbose=args.verbose) + swig_outname = "_SwigInterface.so" + elif args.swiglang == "java": + swig_flags = " ".join(["-I{}".format(dir) for dir in JNI_INCLUDES]) + swig_ldflags = "" + swig_outname = "libSwigInterface.so" + + # compile swig interface and program + cmd = [] + cmd.append('"{}"'.format(conf['compiler'])) + cmd.append("-fPIC") + cmd.append("-c") + cmd.append("-D__EMBEDDED_SOUFFLE__") + cmd.append("SwigInterface_wrap.cxx") + cmd.append(str(args.source)) + cmd.append(conf['definitions']) + cmd.append(conf['compile_options']) + cmd.append(conf['includes']) + cmd.append(conf['std_flag']) + cmd.append(conf['cxx_flags']) + if args.debug: + cmd.append(conf['debug_cxx_flags']) + else: + cmd.append(conf['release_cxx_flags']) + cmd.append(swig_flags) + cmd = " ".join(cmd) + launch_command(cmd, "Compilation of SWIG C++", verbose=args.verbose) + + # link swig interface and program + cmd = [] + cmd.append('"{}"'.format(conf['compiler'])) + cmd.append("-shared") + cmd.append("SwigInterface_wrap.o") + cmd.append("{}{}".format(stemname, ".o")) + cmd.append("-o") + cmd.append(swig_outname) + cmd.append(conf['definitions']) + cmd.append(conf['compile_options']) + cmd.append(conf['includes']) + cmd.append(conf['std_flag']) + cmd.append(conf['cxx_flags']) + if args.debug: + cmd.append(conf['debug_cxx_flags']) + else: + cmd.append(conf['release_cxx_flags']) + cmd.append(conf['link_options']) + cmd.extend(list(map(lambda rpath: RPATH_FMT.format(rpath), RPATHS))) + cmd.extend(list(map(lambda libdir: LIBDIR_FMT.format(libdir), args.lib_dirs))) + cmd.extend(list(map(lambda libname: LIBNAME_FMT.format(libname), args.lib_names))) + cmd.append(swig_ldflags) + cmd = " ".join(cmd) + launch_command(cmd, "Link of SWIG C++", verbose=args.verbose) + + if args.swiglang == "python": + shutil.copy("_SwigInterface.so", workdir) + shutil.copy("SwigInterface.py", workdir) + elif args.swiglang == "java": + shutil.copy("libSwigInterface.so", workdir) + for javasrc in pathlib.Path(tmpdir).glob("*.java"): + shutil.copy(javasrc, workdir) + + # move generated files to same directory as cpp file + os.sys.exit(0) +else: + exepath = pathlib.Path(dirname.joinpath("{}{}".format(stemname, exeext))) + + cmd = [] + cmd.append('"{}"'.format(conf['compiler'])) + cmd.append(conf['definitions']) + cmd.append(conf['compile_options']) + cmd.append(conf['includes']) + cmd.append(conf['std_flag']) + cmd.append(conf['cxx_flags']) + + if args.debug: + cmd.append(conf['debug_cxx_flags']) + else: + cmd.append(conf['release_cxx_flags']) + + cmd.append(OUTNAME_FMT.format(exepath)) + cmd.append(str(args.source)) + + cmd.append(conf['link_options']) + cmd.extend(list(map(lambda rpath: RPATH_FMT.format(rpath), RPATHS))) + cmd.extend(list(map(lambda libdir: LIBDIR_FMT.format(libdir), args.lib_dirs))) + cmd.extend(list(map(lambda libname: LIBNAME_FMT.format(libname), args.lib_names))) + + cmd = " ".join(cmd) + + if args.verbose: + sys.stderr.write(cmd + "\n") + + if exepath.exists(): + exepath.unlink() + + status = subprocess.run(cmd, capture_output=True, text=True, shell=True) + if status.returncode != 0: + sys.stdout.write(status.stdout) + sys.stderr.write(status.stderr) + + os.sys.exit(status.returncode) diff --git a/src/synthesiser/Relation.cpp b/src/synthesiser/Relation.cpp index 6cc2e92aded..554c4ea0139 100644 --- a/src/synthesiser/Relation.cpp +++ b/src/synthesiser/Relation.cpp @@ -28,7 +28,7 @@ using ram::analysis::LexOrder; using ram::analysis::SearchSignature; std::string Relation::getTypeAttributeString(const std::vector& attributeTypes, - const std::unordered_set& attributesUsed) const { + const std::unordered_set& attributesUsed) const { std::stringstream type; for (std::size_t i = 0; i < attributeTypes.size(); ++i) { // consider only attributes used in a lex-order @@ -128,7 +128,7 @@ void DirectRelation::computeIndices() { // expand all search orders to be full for (auto& ind : inds) { // use a set as a cache for fast lookup - std::set curIndexElems(ind.begin(), ind.end()); + std::set curIndexElems(ind.begin(), ind.end()); // If this relation is used with provenance, // we must expand all search orders to be full indices, @@ -166,7 +166,7 @@ void DirectRelation::computeIndices() { /** Generate type name of a direct indexed relation */ std::string DirectRelation::getTypeName() { // collect all attributes used in the lex-order - std::unordered_set attributesUsed; + std::unordered_set attributesUsed; for (auto& ind : getIndices()) { for (auto& attr : ind) { attributesUsed.insert(attr); @@ -199,7 +199,7 @@ void DirectRelation::generateTypeStruct(std::ostream& out) { auto types = relation.getAttributeTypes(); const auto& inds = getIndices(); std::size_t numIndexes = inds.size(); - std::map indexToNumMap; + std::map indexToNumMap; // struct definition out << "struct " << getTypeName() << " {\n"; @@ -562,7 +562,7 @@ void IndirectRelation::computeIndices() { /** Generate type name of a indirect indexed relation */ std::string IndirectRelation::getTypeName() { // collect all attributes used in the lex-order - std::unordered_set attributesUsed; + std::unordered_set attributesUsed; for (auto& ind : getIndices()) { for (auto& attr : ind) { attributesUsed.insert(attr); @@ -589,7 +589,7 @@ void IndirectRelation::generateTypeStruct(std::ostream& out) { const auto& inds = getIndices(); auto types = relation.getAttributeTypes(); std::size_t numIndexes = inds.size(); - std::map indexToNumMap; + std::map indexToNumMap; // struct definition out << "struct " << getTypeName() << " {\n"; @@ -887,7 +887,7 @@ void BrieRelation::computeIndices() { for (auto& ind : inds) { if (ind.size() != getArity()) { // use a set as a cache for fast lookup - std::set curIndexElems(ind.begin(), ind.end()); + std::set curIndexElems(ind.begin(), ind.end()); // expand index to be full for (std::size_t i = 0; i < getArity(); i++) { @@ -907,7 +907,7 @@ void BrieRelation::computeIndices() { /** Generate type name of a brie relation */ std::string BrieRelation::getTypeName() { // collect all attributes used in the lex-order - std::unordered_set attributesUsed; + std::unordered_set attributesUsed; for (auto& ind : getIndices()) { for (auto& attr : ind) { attributesUsed.insert(attr); @@ -933,7 +933,7 @@ void BrieRelation::generateTypeStruct(std::ostream& out) { std::size_t arity = getArity(); const auto& inds = getIndices(); std::size_t numIndexes = inds.size(); - std::map indexToNumMap; + std::map indexToNumMap; // struct definition out << "struct " << getTypeName() << " {\n"; diff --git a/src/synthesiser/Relation.h b/src/synthesiser/Relation.h index 413f138bf09..d3c48560e99 100644 --- a/src/synthesiser/Relation.h +++ b/src/synthesiser/Relation.h @@ -49,7 +49,7 @@ class Relation { return computedIndices; } - std::set getProvenenceIndexNumbers() const { + std::set getProvenenceIndexNumbers() const { return provenanceIndexNumbers; } @@ -63,7 +63,7 @@ class Relation { /** Helper function to convert attribute types to a single string */ std::string getTypeAttributeString(const std::vector& attributeTypes, - const std::unordered_set& attributesUsed) const; + const std::unordered_set& attributesUsed) const; /** Generate relation type struct */ virtual void generateTypeStruct(std::ostream& out) = 0; @@ -86,10 +86,10 @@ class Relation { ram::analysis::OrderCollection computedIndices; /** The list of indices added for provenance computation */ - std::set provenanceIndexNumbers; + std::set provenanceIndexNumbers; /** The number of the master index */ - std::size_t masterIndex = -1; + std::size_t masterIndex; /** Is this relation used with provenance */ const bool isProvenance; diff --git a/src/synthesiser/Synthesiser.cpp b/src/synthesiser/Synthesiser.cpp index b4cf59e8510..210d25f7e47 100644 --- a/src/synthesiser/Synthesiser.cpp +++ b/src/synthesiser/Synthesiser.cpp @@ -671,7 +671,16 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { out << "auto part = " << relName << "->partition();\n"; out << "PARALLEL_START\n"; out << preamble.str(); - out << "pfor(auto it = part.begin(); itpartition();\n"; out << "PARALLEL_START\n"; out << preamble.str(); - out << "pfor(auto it = part.begin(); it= " << omp_min_ver << "\n"; out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n"; + out << "#endif\n"; + out << "for(const auto& env" << identifier << " : " << "*" << relName << ") {\n"; } else { @@ -1060,9 +1104,29 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { << rangeBounds.second.str() << "," << ctxName << ");\n"; out << "auto part = range.partition();\n"; + + // old OpenMP versions cannot loop on iterators + out << R"cpp( + #if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(part.begin(), part.end()); + auto base = part.begin(); + #endif + )cpp"; + + // OMP reduction is not available on all versions of OpenMP + out << "#if defined _OPENMP && _OPENMP >= " << omp_min_ver << "\n"; out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n"; + out << "#endif\n"; + // iterate over each part - out << "for (auto it = part.begin(); it < part.end(); ++it) {\n"; + out << R"cpp( + #if defined _OPENMP && _OPENMP < 200805 + for(int index = 0; index < count; index++) { + auto it = base + index; + #else + for(auto it = part.begin(); it < part.end(); ++it) { + #endif + )cpp"; // iterate over tuples in each part out << "for (const auto& env" << identifier << ": *it) {\n"; } @@ -1363,11 +1427,13 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { // Set reduction operation std::string op; + std::string omp_min_ver; switch (aggregate.getFunction()) { case AggregateOp::MIN: case AggregateOp::FMIN: case AggregateOp::UMIN: { op = "min"; + omp_min_ver = "200805"; // from OMP 3.0 break; } @@ -1375,6 +1441,7 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { case AggregateOp::FMAX: case AggregateOp::UMAX: { op = "max"; + omp_min_ver = "200805"; // from OMP 3.0 break; } @@ -1384,6 +1451,7 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { case AggregateOp::COUNT: case AggregateOp::SUM: { op = "+"; + omp_min_ver = "0"; break; } @@ -1413,10 +1481,29 @@ void Synthesiser::emitCode(std::ostream& out, const Statement& stmt) { out << "auto part = " << relName << "->partition();\n"; out << "PARALLEL_START\n"; out << preamble.str(); + + // old OpenMP versions cannot loop on iterators + out << R"cpp( + #if defined _OPENMP && _OPENMP < 200805 + auto count = std::distance(part.begin(), part.end()); + auto base = part.begin(); + #endif + )cpp"; + // pragma statement + out << "#if defined _OPENMP && _OPENMP >= " << omp_min_ver << "\n"; out << "#pragma omp for reduction(" << op << ":" << sharedVariable << ")\n"; + out << "#endif\n"; + // iterate over each part - out << "for (auto it = part.begin(); it < part.end(); ++it) {\n"; + out << R"cpp( + #if defined _OPENMP && _OPENMP < 200805 + for(int index = 0; index < count; index++) { + auto it = base + index; + #else + for(auto it = part.begin(); it < part.end(); ++it) { + #endif + )cpp"; // iterate over tuples in each part out << "for (const auto& env" << identifier << ": *it) {\n"; @@ -2667,7 +2754,7 @@ void runFunction(std::string inputDirectoryArg, // set default threads (in embedded mode) // if this is not set, and omp is used, the default omp setting of number of cores is used. #if defined(_OPENMP) - if (0 < getNumThreads()) { omp_set_num_threads(getNumThreads()); } + if (0 < getNumThreads()) { omp_set_num_threads(static_cast(getNumThreads())); } #endif signalHandler->set(); diff --git a/src/synthesiser/Synthesiser.h b/src/synthesiser/Synthesiser.h index 08c20d7a860..0ef4fc534f9 100644 --- a/src/synthesiser/Synthesiser.h +++ b/src/synthesiser/Synthesiser.h @@ -59,7 +59,7 @@ class Synthesiser { std::map relationMap; /** Symbol map */ - mutable std::map symbolMap; + mutable std::map symbolMap; /** Symbol map */ mutable std::vector symbolIndex; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c0d76c1393b..8c46e812c3d 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -22,3 +22,4 @@ souffle_add_binary_test(symbol_table_test src SOUFFLE_HEADERS_ONLY) souffle_add_binary_test(table_test src SOUFFLE_HEADERS_ONLY) souffle_add_binary_test(util_test src SOUFFLE_HEADERS_ONLY) souffle_add_binary_test(visitor_test src SOUFFLE_HEADERS_ONLY) +souffle_add_binary_test(getopt_long_test src SOUFFLE_HEADERS_ONLY) diff --git a/src/tests/binary_relation_test.cpp b/src/tests/binary_relation_test.cpp index b2c1cca3ace..8b1c0ddea49 100644 --- a/src/tests/binary_relation_test.cpp +++ b/src/tests/binary_relation_test.cpp @@ -191,7 +191,7 @@ TEST(EqRelTest, Shuffled) { std::size_t N = 100; // test inserting data "out of order" keeps isolation - std::vector data; + std::vector data; for (std::size_t i = 0; i < N; i++) { data.push_back(i); } diff --git a/src/tests/brie_test.cpp b/src/tests/brie_test.cpp index 6b891e4c8d1..b635b31a089 100644 --- a/src/tests/brie_test.cpp +++ b/src/tests/brie_test.cpp @@ -73,24 +73,29 @@ TEST(SparseArray, Basic) { TEST(SparseArray, Limits) { SparseArray map; + using index_type = SparseArray::index_type; map.update(std::numeric_limits::index_type>::min(), 10); map.update(std::numeric_limits::index_type>::max(), 20); map.dump(); - std::vector> present; + std::vector> present; for (const auto& cur : map) { present.push_back(cur); } - EXPECT_EQ("[(0,10),(4294967295,20)]", toString(present)); + std::vector> expected; + expected.emplace_back(std::numeric_limits::index_type>::min(), 10); + expected.emplace_back(std::numeric_limits::index_type>::max(), 20); + EXPECT_EQ(toString(expected), toString(present)); } TEST(SparseArray, Iterator) { SparseArray map; + using index_type = SparseArray::index_type; - std::set> should; + std::set> should; should.insert(std::make_pair(14, 4)); should.insert(std::make_pair(0, 1)); should.insert(std::make_pair(4, 2)); @@ -102,7 +107,7 @@ TEST(SparseArray, Iterator) { map.update(cur.first, cur.second); } - std::set> is; + std::set> is; for (const auto& cur : map) { is.insert(cur); } @@ -113,17 +118,23 @@ TEST(SparseArray, Iterator) { TEST(SparseArray, IteratorStress) { const static int N = 10000; - SparseArray map; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + + SparseArray map; + using index_type = SparseArray::index_type; - std::vector pos; + std::vector pos; while (pos.size() < N) { - int n = random() % (N * 10); + RamDomain n = rnd(); if (!contains(pos, n)) { pos.push_back(n); } } - std::set> should; + std::set> should; for (int i = 0; i < N; i++) { should.insert(std::make_pair(pos[i], i + 1)); } @@ -133,7 +144,7 @@ TEST(SparseArray, IteratorStress) { ASSERT_TRUE(map[cur.first] == cur.second); } - std::set> is; + std::set> is; for (const auto& cur : map) { is.insert(cur); } @@ -144,22 +155,28 @@ TEST(SparseArray, IteratorStress) { TEST(SparseArray, IteratorStress2) { const static int N = 1000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + bool log = false; for (unsigned j = 0; j < N; j++) { - SparseArray map; + SparseArray map; + using index_type = SparseArray::index_type; if (log) std::cout << "Creating " << j << " random numbers ..\n"; - std::vector pos; + std::vector pos; while (pos.size() < j) { - int n = random() % (N * 10); + RamDomain n = rnd(); if (!contains(pos, n)) { pos.push_back(n); } } if (log) std::cout << "Creating input list ..\n"; - std::set> should; + std::set> should; for (unsigned i = 0; i < j; i++) { should.insert(std::make_pair(pos[i], i + 1)); } @@ -173,7 +190,7 @@ TEST(SparseArray, IteratorStress2) { if (log) std::cout << "Sort should list ..\n"; if (log) std::cout << "Collect is list ..\n"; - std::set> is; + std::set> is; unsigned i = 0; for (const auto& cur : map) { is.insert(cur); @@ -270,13 +287,14 @@ TEST(SparseArray, Merge) { SparseArray m1; SparseArray m2; + using index_type = SparseArray::index_type; m1.update(500, 2); m2.update(100, 1); m1.addAll(m2); - std::vector> data; + std::vector> data; for (const auto& it : m1) { data.push_back(it); } @@ -508,13 +526,18 @@ TEST(SparseBitMap, Basic) { } TEST(SparseBitMap, Stress) { - const static int N = 10000; + const static RamDomain N = 10000; + + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; SparseBitMap<> map; - std::vector should; + std::vector should; while (should.size() < N) { - int n = random() % (N * 10); + RamDomain n = rnd(); if (!contains(should, n)) { should.push_back(n); } @@ -526,7 +549,7 @@ TEST(SparseBitMap, Stress) { } // check all the entries - for (int i = 0; i < N * 10; i++) { + for (RamDomain i = 0; i < N * 10; i++) { EXPECT_EQ(map[i], contains(should, i)); } } @@ -534,7 +557,7 @@ TEST(SparseBitMap, Stress) { TEST(SparseBitMap, Iterator) { SparseBitMap<> map; - std::set vals; + std::set vals; for (const auto& cur : map) { vals.insert(cur); } @@ -571,17 +594,22 @@ TEST(SparseBitMap, Iterator) { } TEST(SparseBitMap, IteratorStress2) { - const static int N = 1000; + const static RamDomain N = 1000; + + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; bool log = false; - for (unsigned j = 0; j < N; j++) { + for (RamDomain j = 0; j < N; j++) { SparseBitMap<> map; if (log) std::cout << "Creating " << j << " random numbers ..\n"; - std::set should; - while (should.size() < j) { - int n = random() % (N * 10); + std::set should; + while (static_cast(should.size()) < j) { + RamDomain n = rnd(); if (!contains(should, n)) { should.insert(n); } @@ -594,8 +622,8 @@ TEST(SparseBitMap, IteratorStress2) { } if (log) std::cout << "Collect is list ..\n"; - std::set is; - unsigned i = 0; + std::set is; + RamDomain i = 0; for (const auto& cur : map) { is.insert(cur); i++; @@ -760,23 +788,21 @@ TEST(Trie, Iterator) { EXPECT_EQ(3, card(set)); } -namespace { - -RamDomain rand(RamDomain max) { - return random() % max; -} -} // namespace - TEST(Trie, IteratorStress_1D) { using tuple = std::array; const int N = 10000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + Trie<1> set; std::set data; while (data.size() < N) { - tuple cur{(RamDomain)(rand(N * 10))}; + tuple cur{(RamDomain)(rnd())}; if (data.insert(cur).second) { EXPECT_FALSE(set.contains(cur)); set.insert(cur); @@ -798,13 +824,18 @@ TEST(Trie, IteratorStress_2D) { const int N = 10000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + Trie<2> set; std::set data; while (data.size() < N) { tuple cur; - cur[0] = (RamDomain)(rand(N * 10)); - cur[1] = (RamDomain)(rand(N * 10)); + cur[0] = (RamDomain)(rnd()); + cur[1] = (RamDomain)(rnd()); if (data.insert(cur).second) { EXPECT_FALSE(set.contains(cur)); set.insert(cur); @@ -826,14 +857,19 @@ TEST(Trie, IteratorStress_3D) { const int N = 10000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + Trie<3> set; std::set data; while (data.size() < N) { tuple cur; - cur[0] = (RamDomain)(rand(N * 10)); - cur[1] = (RamDomain)(rand(N * 10)); - cur[2] = (RamDomain)(rand(N * 10)); + cur[0] = (RamDomain)(rnd()); + cur[1] = (RamDomain)(rnd()); + cur[2] = (RamDomain)(rnd()); if (data.insert(cur).second) { EXPECT_FALSE(set.contains(cur)); set.insert(cur); @@ -855,15 +891,20 @@ TEST(Trie, IteratorStress_4D) { const int N = 10000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, 10 * N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + Trie<4> set; std::set data; while (data.size() < N) { tuple cur; - cur[0] = (RamDomain)(rand(N * 10)); - cur[1] = (RamDomain)(rand(N * 10)); - cur[2] = (RamDomain)(rand(N * 10)); - cur[3] = (RamDomain)(rand(N * 10)); + cur[0] = (RamDomain)(rnd()); + cur[1] = (RamDomain)(rnd()); + cur[2] = (RamDomain)(rnd()); + cur[3] = (RamDomain)(rnd()); if (data.insert(cur).second) { EXPECT_FALSE(set.contains(cur)); set.insert(cur); @@ -1554,14 +1595,19 @@ TEST(Trie, Merge_Stress) { const int N = 1000; const int M = 100; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, N / 2 - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + std::set ref; Trie<2> a; for (int i = 0; i < M; i++) { Trie<2> b; for (int i = 0; i < N; i++) { - RamDomain x = rand(N / 2); - RamDomain y = rand(N / 2); + RamDomain x = rnd(); + RamDomain y = rnd(); if (!a.contains({x, y})) { b.insert({x, y}); ref.insert(entry_t{x, y}); @@ -1674,13 +1720,18 @@ TEST(Trie, Limits) { TEST(Trie, Parallel) { const int N = 10000; + std::default_random_engine randomGenerator(3); + std::uniform_int_distribution distribution(0, N - 1); + auto random = std::bind(distribution, randomGenerator); + auto rnd = [&]() { return random(); }; + // get a unordered list of test data using entry_t = typename Trie<2>::entry_type; std::vector list; Trie<2> filter; while (filter.size() < N) { - entry_t entry{(RamDomain)(random() % N), (RamDomain)(random() % N)}; + entry_t entry{(RamDomain)(rnd()), (RamDomain)(rnd())}; if (filter.insert(entry)) { list.push_back(entry); } @@ -1707,8 +1758,8 @@ TEST(Trie, Parallel) { #ifdef _OPENMP #pragma omp parallel for #endif - for (auto it = full.begin(); it < full.end(); ++it) { - res.insert(*it); + for (int idx = 0; idx < static_cast(full.size()); ++idx) { + res.insert(full[idx]); } // check resulting values diff --git a/src/tests/btree_multiset_test.cpp b/src/tests/btree_multiset_test.cpp index ad395ddd383..eff6f9928d1 100644 --- a/src/tests/btree_multiset_test.cpp +++ b/src/tests/btree_multiset_test.cpp @@ -382,7 +382,7 @@ TEST(BTreeMultiSet, Clear) { EXPECT_TRUE(t.empty()); } -using Entry = std::tuple; +using Entry = std::tuple; std::vector getData(unsigned numEntries) { std::vector res(numEntries); @@ -403,12 +403,12 @@ time_point now() { return std::chrono::high_resolution_clock::now(); } -long duration(const time_point& start, const time_point& end) { +int64_t duration(const time_point& start, const time_point& end) { return std::chrono::duration_cast(end - start).count(); } template -long time(const std::string& name, const Op& operation) { +int64_t time(const std::string& name, const Op& operation) { std::cout << "\t" << std::setw(30) << std::setiosflags(std::ios::left) << name << std::resetiosflags(std::ios::left) << " ... " << std::flush; auto a = now(); @@ -421,14 +421,14 @@ long time(const std::string& name, const Op& operation) { template struct reserver { - void operator()(C&, unsigned) const { + void operator()(C&, std::size_t) const { // default: no action } }; template struct reserver> { - void operator()(std::unordered_set& set, unsigned size) const { + void operator()(std::unordered_set& set, std::size_t size) const { set.reserve(size); } }; diff --git a/src/tests/btree_set_test.cpp b/src/tests/btree_set_test.cpp index 7a38289c882..438977a6f0e 100644 --- a/src/tests/btree_set_test.cpp +++ b/src/tests/btree_set_test.cpp @@ -576,7 +576,7 @@ TEST(BTreeSet, ChunkSplitStress) { } } -using Entry = std::tuple; +using Entry = std::tuple; std::vector getData(unsigned numEntries) { std::vector res(numEntries); @@ -597,32 +597,32 @@ time_point now() { return std::chrono::high_resolution_clock::now(); } -long duration(const time_point& start, const time_point& end) { +int64_t duration(const time_point& start, const time_point& end) { return std::chrono::duration_cast(end - start).count(); } template -long time(const std::string& name, const Op& operation) { +int64_t time(const std::string& name, const Op& operation) { std::cout << "\t" << std::setw(30) << std::setiosflags(std::ios::left) << name << std::resetiosflags(std::ios::left) << " ... " << std::flush; auto a = now(); operation(); auto b = now(); - long time = duration(a, b); + int64_t time = duration(a, b); std::cout << " done [" << std::setw(5) << time << "ms]\n"; return time; } template struct reserver { - void operator()(C&, unsigned) const { + void operator()(C&, std::size_t) const { // default: no action } }; template struct reserver> { - void operator()(std::unordered_set& set, unsigned size) const { + void operator()(std::unordered_set& set, std::size_t size) const { set.reserve(size); } }; @@ -766,8 +766,8 @@ TEST(BTreeSet, Parallel) { #ifdef _OPENMP #pragma omp parallel for // schedule(static,1) #endif - for (auto it = full.begin(); it < full.end(); ++it) { - res.insert(*it); + for (int idx = 0; idx < static_cast(full.size()); ++idx) { + res.insert(full[idx]); } EXPECT_TRUE(res.check()); diff --git a/src/tests/disjoint_set_property_test.cpp b/src/tests/disjoint_set_property_test.cpp index d9d06d636f1..bcdd011ff9f 100644 --- a/src/tests/disjoint_set_property_test.cpp +++ b/src/tests/disjoint_set_property_test.cpp @@ -56,7 +56,7 @@ static void check_parent_rank(souffle::DisjointSet& ds, souffle::PiggyList(nodes.size()); ++i) { auto& b = ds.get(nodes.get(i)); LOCAL_ASSERT_LE(DisjointSet::b2r(b), DisjointSet::b2r(ds.get(DisjointSet::b2p(b)))); } @@ -67,7 +67,7 @@ static void check_max_rank(souffle::DisjointSet& ds, souffle::PiggyList(nodes.size()); ++i) { auto& b = ds.get(nodes.get(i)); LOCAL_ASSERT_LT(DisjointSet::b2r(b), nodes.size()); } @@ -78,7 +78,7 @@ static void check_pr2b_undoes_b2r_b2p(souffle::DisjointSet& ds, souffle::PiggyLi #ifdef _OPENMP #pragma omp parallel for #endif - for (std::size_t i = 0; i < nodes.size(); ++i) { + for (int i = 0; i < static_cast(nodes.size()); ++i) { auto& b = ds.get(nodes.get(i)); LOCAL_ASSERT_EQ(DisjointSet::pr2b(DisjointSet::b2p(b), DisjointSet::b2r(b)), b); } @@ -97,23 +97,23 @@ static souffle::PiggyList randomize_disjoint_set( souffle::DisjointSet& ds, std::size_t min_size, std::size_t max_size) { assert(max_size > 0); souffle::PiggyList nodes; - auto random_node = [&nodes] { return nodes.get(random() % nodes.size()); }; + auto random_node = [&nodes] { return nodes.get(rand() % nodes.size()); }; // Must be non-empty nodes.append(DisjointSet::b2p(ds.makeNode())); // TODO: When this loop is made parallel, there's a segfault in the // `unionNodes` operation, probably because of how random_node is // implemented. However, it would be great to have this loop be parallel. - for (std::size_t i = 0; i < (random() % max_size) + min_size; ++i) { + for (int i = 0; i < static_cast((rand() % max_size) + min_size); ++i) { // Insertions should be 2x as likely as other operations so we get // reasonably sized sets with more than 1 equivalence class. - if (random() % 2) { + if (rand() % 2) { nodes.append(DisjointSet::b2p(ds.makeNode())); } else { - if (random() % 2) { + if (rand() % 2) { ds.unionNodes(random_node(), random_node()); } else { // Other mutating methods - switch (random() % 3) { + switch (rand() % 3) { case 0: { ds.findNode(random_node()); break; @@ -125,7 +125,7 @@ static souffle::PiggyList randomize_disjoint_set( default: { // Non-mutating methods (queries) - no need to perform these // often, they can all be about as likely as one another. - if (random() % 2) { + if (rand() % 2) { ds.size(); } else { ds.get(random_node()); @@ -140,7 +140,7 @@ static souffle::PiggyList randomize_disjoint_set( nodes.append(DisjointSet::b2p(ds.makeNode())); } check(ds, nodes); - return nodes; + return souffle::PiggyList{nodes}; } // When creating a maximally-disjoint set, each node has rank 0. @@ -149,14 +149,14 @@ TEST(DjTest, MakeNode) { #ifdef _OPENMP #pragma omp parallel for #endif - for (std::size_t i = 0; i < RANDOM_TEST_SIZE; ++i) { + for (int i = 0; i < RANDOM_TEST_SIZE; ++i) { souffle::block_t n = ds.makeNode(); // rank should be 0 EXPECT_EQ(DisjointSet::b2r(n), 0); // parent should be itself EXPECT_EQ(n, ds.get(DisjointSet::b2p(n))); #ifndef _OPENMP - EXPECT_EQ(ds.size(), i + 1); + EXPECT_EQ(ds.size(), static_cast(i + 1)); #endif // ifndef _OPENMP } EXPECT_EQ(ds.size(), RANDOM_TEST_SIZE); @@ -168,11 +168,13 @@ TEST(DjTest, FindSize) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 1, RANDOM_TEST_SIZE); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel + srand(omp_get_thread_num()); +#pragma omp for #endif for (int j = 0; j < RANDOM_TEST_SIZE; ++j) { auto size0 = ds.size(); - ds.findNode(nodes.get(random() % nodes.size())); + ds.findNode(nodes.get(rand() % nodes.size())); EXPECT_EQ(size0, ds.size()); } } @@ -184,11 +186,13 @@ TEST(DjTest, UnionSize) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 1, RANDOM_TEST_SIZE); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel + srand(omp_get_thread_num()); +#pragma omp for #endif for (int j = 0; j < RANDOM_TEST_SIZE; ++j) { auto size0 = ds.size(); - ds.unionNodes(nodes.get(random() % nodes.size()), nodes.get(random() % nodes.size())); + ds.unionNodes(nodes.get(rand() % nodes.size()), nodes.get(rand() % nodes.size())); EXPECT_EQ(size0, ds.size()); } } @@ -200,13 +204,15 @@ TEST(DjTest, FindSameSet) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 1, RANDOM_TEST_SIZE); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel + srand(omp_get_thread_num()); +#pragma omp for #endif for (int j = 0; j < RANDOM_TEST_SIZE; ++j) { - parent_t node1 = nodes.get(random() % nodes.size()); - parent_t node2 = nodes.get(random() % nodes.size()); + parent_t node1 = nodes.get(rand() % nodes.size()); + parent_t node2 = nodes.get(rand() % nodes.size()); bool are_same = ds.sameSet(node1, node2); - ds.findNode(nodes.get(random() % nodes.size())); + ds.findNode(nodes.get(rand() % nodes.size())); EXPECT_EQ(are_same, ds.sameSet(node1, node2)); } } @@ -218,10 +224,12 @@ TEST(DjTest, FindTwice) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 1, RANDOM_TEST_SIZE); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel + srand(omp_get_thread_num()); +#pragma omp for #endif for (int j = 0; j < RANDOM_TEST_SIZE; ++j) { - parent_t node = nodes.get(random() % nodes.size()); + parent_t node = nodes.get(rand() % nodes.size()); EXPECT_EQ(ds.findNode(node), ds.findNode(node)); } } @@ -237,11 +245,13 @@ TEST(DjTest, SameSet) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 1, RANDOM_TEST_SIZE); #ifdef _OPENMP -#pragma omp parallel for +#pragma omp parallel + srand(omp_get_thread_num()); +#pragma omp for #endif for (int j = 0; j < RANDOM_TEST_SIZE; ++j) { - parent_t node1 = nodes.get(random() % nodes.size()); - parent_t node2 = nodes.get(random() % nodes.size()); + parent_t node1 = nodes.get(rand() % nodes.size()); + parent_t node2 = nodes.get(rand() % nodes.size()); EXPECT_TRUE(ds.sameSet(node1, node1)); EXPECT_EQ(ds.sameSet(node1, node2), ds.sameSet(node2, node1)); EXPECT_EQ(ds.sameSet(node1, node2), ds.sameSet(node1, node2)); @@ -255,9 +265,9 @@ TEST(DjTest, UnionRoot) { for (int i = 0; i < RANDOM_TESTS; ++i) { souffle::DisjointSet ds; auto nodes = randomize_disjoint_set(ds, 0, RANDOM_TEST_SIZE); - for (int i = 0; i < random() % RANDOM_TEST_SIZE; ++i) { - souffle::block_t x = ds.get(nodes.get(random() % nodes.size())); - souffle::block_t y = ds.get(nodes.get(random() % nodes.size())); + for (int i = 0; i < rand() % RANDOM_TEST_SIZE; ++i) { + souffle::block_t x = ds.get(nodes.get(rand() % nodes.size())); + souffle::block_t y = ds.get(nodes.get(rand() % nodes.size())); souffle::parent_t px = DisjointSet::b2p(x); souffle::parent_t py = DisjointSet::b2p(y); // Save original roots diff --git a/src/tests/eqrel_datastructure_test.cpp b/src/tests/eqrel_datastructure_test.cpp index f0689511c00..eee9646c354 100644 --- a/src/tests/eqrel_datastructure_test.cpp +++ b/src/tests/eqrel_datastructure_test.cpp @@ -85,7 +85,7 @@ TEST(RandomInsertPiggyTest, ParallelInsert) { // insert in parallel (no element should be overridden, because this breaks the datastructure) #pragma omp parallel for - for (std::size_t i = 0; i < limit; ++i) { + for (int i = 0; i < static_cast(limit); ++i) { pl.insertAt(i, i); } @@ -221,7 +221,7 @@ TEST(PiggyTest, ParallelElementSpawning) { souffle::PiggyList pl; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { std::size_t pos = pl.createNode(); pl.get(pos) = pos; } @@ -237,7 +237,7 @@ TEST(PiggyTest, ParallelAppend) { souffle::PiggyList pl; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { pl.append(i); } EXPECT_EQ(N, pl.size()); @@ -309,7 +309,7 @@ TEST(DjTest, ParallelScaling) { constexpr std::size_t N = 10000; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { ds.makeNode(); } // check we didn't miss any @@ -317,7 +317,7 @@ TEST(DjTest, ParallelScaling) { // union everything #pragma omp parallel for - for (std::size_t i = 0; i < N - 1; ++i) { + for (int i = 0; i < static_cast(N - 1); ++i) { ds.unionNodes(i, i + 1); } EXPECT_EQ(ds.size(), N); @@ -399,7 +399,7 @@ TEST(SparseDjTest, ParallelDense) { // call toDense for a load of sparse values, and hope to god they're the same #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { std::size_t val = data_source[i]; std::size_t a = sds.toDense(val); std::size_t b = sds.toDense(val); @@ -435,7 +435,7 @@ TEST(SparseDjTest, ParallelScaling) { constexpr std::size_t N = 1000000; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { // make things which are relatively sparse (hence why we mul by 50) sds.makeNode(i * 50); } @@ -443,7 +443,7 @@ TEST(SparseDjTest, ParallelScaling) { EXPECT_EQ(sds.size(), N); // union everything #pragma omp parallel for - for (std::size_t i = 0; i < N - 1; ++i) { + for (int i = 0; i < static_cast(N - 1); ++i) { sds.unionNodes(i * 50, (i + 1) * 50); } @@ -459,7 +459,7 @@ TEST(SparseDjTest, ParallelTest) { constexpr std::size_t N = 1000000; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { sds.unionNodes(i, i); } @@ -548,7 +548,7 @@ TEST(LambdaBTreeTest, ParallelInsert) { TestLambdaTree t; #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { TestPair tp = {i, 123132}; t.insert(tp, update_fn); } @@ -588,7 +588,7 @@ TEST(LambdaBTreeTest, ParallelInsert) { TestLambdaTree t; #pragma omp parallel for - for (std::size_t i = 0; i < N2; ++i) { + for (int i = 0; i < static_cast(N2); ++i) { TestPair tp = {i / num_threads, 213812309}; // by doing this, we pretty much make the same insertion num_thread times, for the next num_thread // loops @@ -644,7 +644,7 @@ TEST(LambdaBTree, ContendParallel) { std::shuffle(data_source.begin(), data_source.end(), std::random_device()); #pragma omp parallel for - for (std::size_t i = 0; i < N; ++i) { + for (int i = 0; i < static_cast(N); ++i) { std::size_t val = data_source[i]; // two pairs, the second part of the pair is updated within the functor if it doesn't exist // in both cases, the now-existing value is returned. diff --git a/src/tests/flyweight_test.cpp b/src/tests/flyweight_test.cpp index 555276f5834..92dcde6874f 100644 --- a/src/tests/flyweight_test.cpp +++ b/src/tests/flyweight_test.cpp @@ -81,7 +81,7 @@ class FindOrInsertCopyBase { void parametrizedFindOrInsertCopy( const std::size_t LaneCount, const std::size_t InitialCapacity, std::size_t MaxThreadCount) { #ifdef _OPENMP - omp_set_num_threads(std::min(MaxThreadCount, (std::size_t)omp_get_max_threads())); + omp_set_num_threads(static_cast(std::min(MaxThreadCount, (std::size_t)omp_get_max_threads()))); #else testutil::ignore(MaxThreadCount); #endif @@ -118,6 +118,7 @@ class FindOrInsertCopyBase { std::vector::index_type, std::string>> random_flyweight( FlyweightImpl& flyweight, std::size_t min_size, std::size_t max_size) { assert(min_size < max_size); + assert(static_cast(std::numeric_limits::max()) >= max_size); std::vector::index_type, std::string>> values; values.reserve(max_size); @@ -133,13 +134,14 @@ class FindOrInsertCopyBase { values.emplace_back(key, s); } }; + #ifdef _OPENMP #pragma omp parallel { srand(omp_get_thread_num()); #pragma omp for #endif - for (std::size_t i = 0; i < max_size; ++i) { + for (int i = 0; i < static_cast(max_size); ++i) { const auto sel = rand() % 3; if (sel == 0) { add_new(); diff --git a/src/tests/getopt_long_test.cpp b/src/tests/getopt_long_test.cpp new file mode 100644 index 00000000000..839592ee175 --- /dev/null +++ b/src/tests/getopt_long_test.cpp @@ -0,0 +1,494 @@ +/* + * Souffle - A Datalog Compiler + * Copyright (c) 2022, The Souffle Developers. All rights reserved + * Licensed under the Universal Permissive License v 1.0 as shown at: + * - https://opensource.org/licenses/UPL + * - /licenses/SOUFFLE-UPL.txt + */ + +/************************************************************************ + * + * @file getopt_long_test.cpp + * + * A test case testing the custom getopt_long implementation + * + ***********************************************************************/ + +#ifndef USE_CUSTOM_GETOPTLONG +#define USE_CUSTOM_GETOPTLONG +#endif + +#include "tests/test.h" + +#include "souffle/utility/GetOptLongImpl.h" + +namespace souffle::test { + +TEST(GetOptLong, Short) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-z"), const_cast("-x"), + const_cast("argx"), const_cast("-yargy"), const_cast("-z"), + const_cast("argz"), const_cast("-zargz")}; + + const int argc = 8; + const char* optstr = "x:y:z::"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'z'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'x'); + EXPECT_EQ(std::string(optarg), std::string("argx")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'y'); + EXPECT_EQ(std::string(optarg), std::string("argy")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'z'); + // z has an optional argument and it's not in the same argv cell so optarg is set to null + // and optind is the position of the possible argument in argv + EXPECT_EQ(std::string(argv[optind]), std::string("argz")); + // accept the argument + optind += 1; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'z'); + // z has an optional argument and it in the same argv cell so optarg is set + EXPECT_EQ(std::string(optarg), std::string("argz")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); +} + +TEST(GetOptLong, SimplePermutation) { + optind = 0; + opterr = 0; + + // expect arguments to be permuted and show up as if -x, -y, arg1, arg2, arg3, arg4 + char* const argv[] = { + const_cast("test"), + const_cast("arg1"), + const_cast("-x"), + const_cast("arg2"), + const_cast("arg3"), + const_cast("-y"), + const_cast("arg4"), + }; + + const int argc = 7; + + struct option longopts[1]; + const char* optstr = "xy"; + memset(longopts, 0, sizeof(struct option)); + + char c; + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'x'); + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'y'); + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + EXPECT_EQ(optind, 3); + EXPECT_EQ(std::string(argv[optind]), std::string("arg1")); + EXPECT_EQ(std::string(argv[optind + 1]), std::string("arg2")); + EXPECT_EQ(std::string(argv[optind + 2]), std::string("arg3")); + EXPECT_EQ(std::string(argv[optind + 3]), std::string("arg4")); +} + +TEST(GetOptLong, ComplexPermutation) { + optind = 0; + opterr = 0; + + // expect arguments to be permuted and show up as if -x argx, -y, arg1 + char* const argv[] = { + const_cast("test"), + const_cast("arg1"), + const_cast("-x"), + const_cast("argx"), + const_cast("-y"), + }; + + const int argc = 5; + const char* optstr = "x:y::"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'x'); + EXPECT_NE(optarg, nullptr); + EXPECT_EQ(std::string(optarg), std::string("argx")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'y'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + EXPECT_EQ(optarg, nullptr); + EXPECT_EQ(optind, 4); + EXPECT_EQ(std::string(argv[optind]), std::string("arg1")); +} + +TEST(GetOptLong, Short2) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-nnntarg")}; + + const int argc = 2; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + // reset + optind = 0; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 't'); + EXPECT_EQ(std::string(optarg), std::string("arg")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); +} + +TEST(GetOptLong, Empty) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test")}; + + const int argc = 1; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); +} + +TEST(GetOptLong, NonOpts) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("nonopt1"), + const_cast("nonopt2"), const_cast("nonopt3"), const_cast("-n")}; + + const int argc = 5; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + EXPECT_EQ(optind, 5); + EXPECT_EQ(optarg, nullptr); + + for (int i = 0; i < 3; ++i) { + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + EXPECT_EQ(optind, 2); + EXPECT_EQ(std::string(argv[optind]), std::string("nonopt1")); + EXPECT_EQ(std::string(argv[optind + 1]), std::string("nonopt2")); + EXPECT_EQ(std::string(argv[optind + 2]), std::string("nonopt3")); + } +} + +TEST(GetOptLong, MissingArg) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-t")}; + + const int argc = 2; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, MissingArg2) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-t")}; + + const int argc = 2; + const char* optstr = ":nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, ':'); +} + +TEST(GetOptLong, MissingArg3) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-t"), const_cast("-t")}; + + const int argc = 3; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, MissingArg4) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-t"), const_cast("-n")}; + + const int argc = 3; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, MissingArg5) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-t"), const_cast("--long")}; + + const int argc = 3; + const char* optstr = "nt:"; + struct option longopts[2]; + memset(longopts, 0, 2 * sizeof(struct option)); + longopts[1].name = "long"; + longopts[1].val = 1; + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, DashArg) { + optind = 0; + opterr = 0; + + char* const argv[] = { + const_cast("test"), + const_cast("-t"), + const_cast("-"), + const_cast("-t"), + const_cast("--"), + }; + + const int argc = 5; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 't'); + EXPECT_EQ(optind, 3); + EXPECT_EQ(std::string(optarg), std::string("-")); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 't'); + EXPECT_EQ(optind, 5); + EXPECT_EQ(std::string(optarg), std::string("--")); +} + +TEST(GetOptLong, NonOptionDash) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-"), const_cast("-n")}; + + const int argc = 3; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + EXPECT_EQ(optind, 2); + EXPECT_EQ(std::string(argv[2]), std::string("-")); +} + +TEST(GetOptLong, DashDashEndScan) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-n"), const_cast("--"), + const_cast("-t"), const_cast("opt")}; + + const int argc = 5; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, 'n'); + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, -1); + EXPECT_EQ(optind, 2); + EXPECT_EQ(std::string(argv[2]), std::string("--")); + EXPECT_EQ(std::string(argv[3]), std::string("-t")); + EXPECT_EQ(std::string(argv[4]), std::string("opt")); +} + +TEST(GetOptLong, NotAnOption) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-x")}; + + const int argc = 2; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, NotAnOption2) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("test"), const_cast("-x")}; + + const int argc = 2; + const char* optstr = "nt:"; + struct option longopts[1]; + memset(longopts, 0, sizeof(struct option)); + + char c; + + c = getopt_long(argc, argv, optstr, longopts, nullptr); + EXPECT_EQ(c, '?'); +} + +TEST(GetOptLong, LongOptions) { + optind = 0; + opterr = 0; + + char* const argv[] = {const_cast("souffle"), const_cast("--fact-dir"), + const_cast("-"), const_cast("--version"), const_cast("input.dl"), + const_cast("--generate"), const_cast("--output-dir=out"), + const_cast("--jobs=4")}; + + int jobs_flag = -1; + int version_flag = -1; + int show_flag = -1; + + const int argc = 8; + const char* optstr = "F:D:j:vg::"; + struct option longopts[] = {{"fact-dir", required_argument, nullptr, 'F'}, + {"output-dir", required_argument, nullptr, 'D'}, {"jobs", required_argument, &jobs_flag, 'j'}, + {"version", no_argument, &version_flag, 'v'}, {"generate", optional_argument, nullptr, 'g'}, + {"show", required_argument, &show_flag, 0x4}, {nullptr, 0, nullptr, 0}}; + + char c; + int longindex; + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, 'F'); + EXPECT_EQ(longindex, 0); + EXPECT_EQ(std::string(optarg), std::string("-")); + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, 0); + EXPECT_EQ(longindex, 3); + EXPECT_EQ(version_flag, 'v'); + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, 'g'); + EXPECT_EQ(longindex, 4); + EXPECT_EQ(optind, 6); + EXPECT_EQ(optarg, nullptr); + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, 'D'); + EXPECT_EQ(longindex, 1); + EXPECT_EQ(std::string(optarg), std::string("out")); + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, 0); + EXPECT_EQ(longindex, 2); + EXPECT_EQ(jobs_flag, 'j'); + EXPECT_EQ(std::string(optarg), std::string("4")); + + c = getopt_long(argc, argv, optstr, longopts, &longindex); + EXPECT_EQ(c, -1); + EXPECT_EQ(optind, 7); + EXPECT_EQ(std::string(argv[7]), std::string("input.dl")); + + EXPECT_EQ(show_flag, -1); +} + +} // namespace souffle::test diff --git a/src/tests/profile_util_test.cpp b/src/tests/profile_util_test.cpp index de14d545782..1687a9535db 100644 --- a/src/tests/profile_util_test.cpp +++ b/src/tests/profile_util_test.cpp @@ -64,9 +64,9 @@ TEST(StringUtil, formatMemory) { EXPECT_EQ("1000GB", Tools::formatMemory(1000 * 1024 * 1024)); EXPECT_EQ("1900GB", Tools::formatMemory(1900 * 1024 * 1024)); - EXPECT_EQ("2TB", Tools::formatMemory(2L * 1024 * 1024 * 1024)); - EXPECT_EQ("1000TB", Tools::formatMemory(1000L * 1024 * 1024 * 1024)); - EXPECT_EQ("1900TB", Tools::formatMemory(1900L * 1024 * 1024 * 1024)); + EXPECT_EQ("2TB", Tools::formatMemory(2UL * 1024UL * 1024UL * 1024UL)); + EXPECT_EQ("1000TB", Tools::formatMemory(1000ULL * 1024ULL * 1024ULL * 1024ULL)); + EXPECT_EQ("1900TB", Tools::formatMemory(1900ULL * 1024ULL * 1024ULL * 1024ULL)); } TEST(StringUtil, formatTime) { diff --git a/src/tests/symbol_table_test.cpp b/src/tests/symbol_table_test.cpp index cf327d63c43..946f06bede1 100644 --- a/src/tests/symbol_table_test.cpp +++ b/src/tests/symbol_table_test.cpp @@ -92,11 +92,11 @@ TEST(SymbolTable, Basics) { TEST(SymbolTable, Inserts) { for (int i = 0; i < RANDOM_TESTS; ++i) { SymbolTable X; - std::size_t size = random() % RANDOM_TEST_SIZE; + std::size_t size = rand() % RANDOM_TEST_SIZE; #ifdef _OPENMP #pragma omp parallel for #endif - for (std::size_t j = 0; j < size; ++j) { + for (int j = 0; j < static_cast(size); ++j) { // Guarantee uniqueness by appending something not in the character set // and then the index. X.encode(random_string() + "~" + std::to_string(j)); diff --git a/src/tests/test.h b/src/tests/test.h index e52d8af5e87..287b2623b3e 100644 --- a/src/tests/test.h +++ b/src/tests/test.h @@ -36,7 +36,7 @@ namespace testutil { template -std::vector generateRandomVector(const std::size_t vectorSize, const int seed = 3) { +std::vector generateRandomVector(const std::size_t vectorSize, const int seed) { std::vector values(vectorSize); std::default_random_engine randomGenerator(seed); @@ -59,6 +59,56 @@ std::vector generateRandomVector(const std::size_t vectorSize, const int seed } } +/** Generate some corner-case test values. */ +template +std::vector generateValues() { + std::vector values; + + if constexpr (std::is_floating_point::value) { + // corner cases + values.push_back(std::numeric_limits::min()); + values.push_back(0.0); + values.push_back(1.0); + values.push_back(1.0); + values.push_back(3.0); + values.push_back(12.0); + values.push_back(1.0 + std::numeric_limits::epsilon()); + values.push_back(1.0 - std::numeric_limits::epsilon()); + values.push_back(std::numeric_limits::max()); + values.push_back(std::numeric_limits::max() / 2.0); + + if constexpr (std::is_signed::value) { + values.push_back(std::numeric_limits::lowest()); + values.push_back(std::numeric_limits::lowest() / 2.0); + values.push_back(-1.0); + values.push_back(-3.0); + values.push_back(-12.0); + } + + return values; + } else { + // corner cases + values.push_back(0); + values.push_back(1); + values.push_back(3); + values.push_back(12); + values.push_back(0xAAAAAAAA); + values.push_back(0x55555555); + values.push_back(std::numeric_limits::max()); + values.push_back(std::numeric_limits::max() / 2); + + if constexpr (std::is_signed::value) { + values.push_back(std::numeric_limits::lowest()); + values.push_back(std::numeric_limits::lowest() / 2); + values.push_back(-1); + values.push_back(-3); + values.push_back(-12); + } + + return values; + } +} + // easy function to suppress unused var warnings (when we REALLY don't need to use them!) template void ignore(const T&) {} diff --git a/tests/evaluation/CMakeLists.txt b/tests/evaluation/CMakeLists.txt index fa510eaabab..96e6f099ff3 100644 --- a/tests/evaluation/CMakeLists.txt +++ b/tests/evaluation/CMakeLists.txt @@ -68,7 +68,11 @@ positive_test(functor_arity) positive_test(grammar) positive_test(hex) positive_test(independent_body1) +if (NOT MSVC) + # the semantics checker does not produce a deterministic warning + # message location "variable occurs only once" in complex rules. positive_test(independent_body2) +endif () positive_test(index) positive_test(indexed_inequalities) positive_test(indirect_negation) @@ -97,7 +101,14 @@ positive_test(magic_movies) positive_test(magic_names1) positive_test(magic_neglabel) positive_test(magic_negignored) +if (NOT (MSVC AND (CMAKE_BUILD_TYPE MATCHES Debug))) + # When compiled in Debug with Visual Studio with the default stack size of 1MB, + # the interpreter fails with a stack overflow in Engine::execute(). + # The cause is the number of lambda functions that are allocated as locals on the stack. + # The stack frame of Engine::execute is close to 30KB. + # The 1MB stack will overflow at depth ~34 of Engine::execute(). positive_test(magic_nqueens) +endif () positive_test(magic_perfect_numbers) positive_test(magic_posignored) positive_test(magic_poslabel) diff --git a/tests/interface/CMakeLists.txt b/tests/interface/CMakeLists.txt index 73629c4c36e..65085e828a8 100644 --- a/tests/interface/CMakeLists.txt +++ b/tests/interface/CMakeLists.txt @@ -23,24 +23,63 @@ function(SOUFFLE_RUN_CPP_TEST) ${ARGV} ) - - add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_compile_cpp - COMMAND sh -c "set -e; '${CMAKE_CXX_COMPILER}' --std=c++17 -D__EMBEDDED_SOUFFLE__ '-I${CMAKE_SOURCE_DIR}/src/include' '${TEST_NAME}.cpp' '${PARAM_INPUT_DIR}/driver.cpp' -o '${TEST_NAME}'") - set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_compile_cpp PROPERTIES - WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" - LABELS "${PARAM_TEST_LABELS}" - FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_compile_cpp - FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_run_souffle - ) - - add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_run_cpp - COMMAND sh -c "set -e; './${TEST_NAME}' '${FACTS_DIR}' > '${TEST_NAME}.out' 2> '${TEST_NAME}.err'") - set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_cpp PROPERTIES - WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" - LABELS "${PARAM_TEST_LABELS}" - FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_run_cpp - FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_compile_cpp - ) + set(ADDITIONNAL_FLAGS) + + if (OPENMP_FOUND) + list(APPEND ADDITIONNAL_FLAGS ${OpenMP_CXX_FLAGS}) + endif() + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + list(APPEND ADDITIONNAL_FLAGS "-lstdc++fs") + endif () + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(OUTNAME ${TEST_NAME}) + set(OUT_OPTION "-o" "${TEST_NAME}") + elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + set(OUTNAME "${TEST_NAME}.exe") + set(OUT_OPTION "/Fe:${TEST_NAME}") + list(APPEND ADDITIONNAL_FLAGS "/Zc:preprocessor") + list(APPEND ADDITIONNAL_FLAGS "/permissive-") + list(APPEND ADDITIONNAL_FLAGS "/EHsc") + list(APPEND ADDITIONNAL_FLAGS "/D_CRT_SECURE_NO_WARNINGS") + list(APPEND ADDITIONNAL_FLAGS "/nologo") + endif () + + # compilation + add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_compile_cpp + COMMAND + ${CMAKE_CXX_COMPILER} + ${CMAKE_CXX17_STANDARD_COMPILE_OPTION} + ${OUT_OPTION} + -D__EMBEDDED_SOUFFLE__ + "-I${CMAKE_SOURCE_DIR}/src/include" + "${TEST_NAME}.cpp" + "${PARAM_INPUT_DIR}/driver.cpp" + ${ADDITIONNAL_FLAGS}) + + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_compile_cpp PROPERTIES + WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" + LABELS "${PARAM_TEST_LABELS}" + FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_compile_cpp + FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_run_souffle) + + # execution + add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_run_cpp + COMMAND + ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/cmake/redirect.py + --out "${TEST_NAME}.out" + --err "${TEST_NAME}.err" + "${OUTNAME}" "${FACTS_DIR}") + + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_cpp PROPERTIES + WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" + LABELS "${PARAM_TEST_LABELS}" + FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_run_cpp + FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_compile_cpp + ) endfunction() function(SOUFFLE_RUN_CPP_TEST_HELPER) @@ -62,6 +101,12 @@ function(SOUFFLE_RUN_CPP_TEST_HELPER) set(FIXTURE_NAME ${QUALIFIED_TEST_NAME}_fixture) set(TEST_LABELS "positive;integration") + set(PP_FLAGS) + if (MSVC) + list(APPEND PP_FLAGS "--preprocessor" "cl -nologo -TC -E") + endif() + + souffle_setup_integration_test_dir(TEST_NAME ${PARAM_TEST_NAME} QUALIFIED_TEST_NAME ${QUALIFIED_TEST_NAME} DATA_CHECK_DIR ${INPUT_DIR} @@ -76,7 +121,7 @@ function(SOUFFLE_RUN_CPP_TEST_HELPER) OUTPUT_DIR ${OUTPUT_DIR} FIXTURE_NAME ${FIXTURE_NAME} TEST_LABELS "${TEST_LABELS}" - SOUFFLE_PARAMS "-g '${OUTPUT_DIR}/${TEST_NAME}.cpp'") + SOUFFLE_PARAMS "-g" "${OUTPUT_DIR}/${TEST_NAME}.cpp" ${PP_FLAGS}) souffle_run_cpp_test(TEST_NAME ${PARAM_TEST_NAME} QUALIFIED_TEST_NAME ${QUALIFIED_TEST_NAME} diff --git a/tests/interface/functors/CMakeLists.txt b/tests/interface/functors/CMakeLists.txt index 352ff468e95..e76a2988b2e 100644 --- a/tests/interface/functors/CMakeLists.txt +++ b/tests/interface/functors/CMakeLists.txt @@ -9,10 +9,39 @@ target_include_directories(functors PRIVATE "${CMAKE_SOURCE_DIR}/src/include") target_compile_features(functors PUBLIC cxx_std_17) -set_target_properties(functors PROPERTIES CXX_EXTENSIONS OFF) -target_compile_options(functors - PUBLIC "-Wall;-Wextra;-Werror;-fwrapv") +set_target_properties(functors PROPERTIES + CXX_EXTENSIONS OFF + WINDOWS_EXPORT_ALL_SYMBOLS ON) + +if (OPENMP_FOUND) + target_link_libraries(functors PUBLIC OpenMP::OpenMP_CXX) +endif() + +if (Threads_FOUND) + target_link_libraries(functors PUBLIC Threads::Threads) +endif () + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + target_link_libraries(functors PUBLIC stdc++fs) + endif () +endif () + +if (NOT MSVC) + target_compile_options(functors + PUBLIC "-Wall;-Wextra;-Werror;-fwrapv") +else () + target_compile_options(functors PUBLIC /W3 /WX /EHsc) +endif() + +if (WIN32) + # Prefix all shared libraries with 'lib'. + set(CMAKE_SHARED_LIBRARY_PREFIX "lib") + + # Prefix all static libraries with 'lib'. + set(CMAKE_STATIC_LIBRARY_PREFIX "lib") +endif () if (SOUFFLE_DOMAIN_64BIT) target_compile_definitions(functors diff --git a/tests/interface/functors/functors.cpp b/tests/interface/functors/functors.cpp index a73d93af36a..c21e33e880c 100644 --- a/tests/interface/functors/functors.cpp +++ b/tests/interface/functors/functors.cpp @@ -74,7 +74,7 @@ FF_int factorial(FF_uint x) { } FF_int rnd(FF_float x) { - return round(x); + return static_cast(round(x)); } FF_float incr(FF_float x) { diff --git a/tests/interface/graph_coloring/CMakeLists.txt b/tests/interface/graph_coloring/CMakeLists.txt index 9b36f92a76e..74332746208 100644 --- a/tests/interface/graph_coloring/CMakeLists.txt +++ b/tests/interface/graph_coloring/CMakeLists.txt @@ -9,11 +9,25 @@ target_include_directories(gcfunctors PRIVATE "${CMAKE_SOURCE_DIR}/src/include") target_compile_features(gcfunctors PUBLIC cxx_std_17) + set_target_properties(gcfunctors PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(gcfunctors PROPERTIES OUTPUT_NAME "functors") +set_target_properties(gcfunctors PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) +if (NOT MSVC) target_compile_options(gcfunctors PUBLIC "-Wall;-Wextra;-Werror;-fwrapv") +else () + target_compile_options(functors PUBLIC /W3 /WX) +endif() + +if (WIN32) + # Prefix all shared libraries with 'lib'. + set(CMAKE_SHARED_LIBRARY_PREFIX "lib") + + # Prefix all static libraries with 'lib'. + set(CMAKE_STATIC_LIBRARY_PREFIX "lib") +endif () if (SOUFFLE_DOMAIN_64BIT) target_compile_definitions(functors diff --git a/tests/profile/CMakeLists.txt b/tests/profile/CMakeLists.txt index 617e306f4b4..37f37a54b4d 100644 --- a/tests/profile/CMakeLists.txt +++ b/tests/profile/CMakeLists.txt @@ -87,7 +87,7 @@ function(SOUFFLE_RUN_PROF_TEST_HELPER) OUTPUT_DIR ${OUTPUT_DIR} FIXTURE_NAME ${FIXTURE_NAME} TEST_LABELS "${TEST_LABELS}" - SOUFFLE_PARAMS "-p '${OUTPUT_DIR}/${TEST_NAME}.prof'") + SOUFFLE_PARAMS "-p" "${OUTPUT_DIR}/${TEST_NAME}.prof") souffle_run_prof_test(TEST_NAME ${PARAM_TEST_NAME} QUALIFIED_TEST_NAME ${QUALIFIED_TEST_NAME} @@ -118,5 +118,7 @@ function(SOUFFLE_POSITIVE_PROF_TEST TEST_NAME) souffle_run_prof_test_helper(TEST_NAME ${TEST_NAME} ${ARGN}) endfunction() +if (NOT MSVC) souffle_positive_prof_test(lrg_attr_id) souffle_positive_prof_test(recursive) +endif () diff --git a/tests/provenance/CMakeLists.txt b/tests/provenance/CMakeLists.txt index 0eb9b1b9f61..dd3bb88c0f9 100644 --- a/tests/provenance/CMakeLists.txt +++ b/tests/provenance/CMakeLists.txt @@ -12,6 +12,7 @@ function(SOUFFLE_PROVENANCE_TEST TEST_NAME) souffle_run_test_helper(TEST_NAME ${TEST_NAME} COMPILED CATEGORY provenance) endfunction() +if (NOT MSVC) souffle_provenance_test(components) souffle_provenance_test(constraints) souffle_provenance_test(cprog1) @@ -28,3 +29,4 @@ souffle_provenance_test(query_3) souffle_provenance_test(query_4) souffle_provenance_test(query_float_unsigned) souffle_provenance_test(same_gen) +endif () diff --git a/tests/semantic/CMakeLists.txt b/tests/semantic/CMakeLists.txt index 34abe261c81..2501f754471 100644 --- a/tests/semantic/CMakeLists.txt +++ b/tests/semantic/CMakeLists.txt @@ -98,7 +98,9 @@ positive_test(limitsize) positive_test(load2) positive_test(load3) positive_test(load4) +if (SOUFFLE_USE_ZLIB) positive_test(load6) +endif () positive_test(load7) positive_test(load8) positive_test(load9) @@ -230,3 +232,8 @@ positive_test(type_as4) positive_test(record_alias) positive_test(record_alias2) positive_test(adt_alias) +if (FALSE) +# semantics checker of complex rule does not catch this +# "variable only occurs once" issue. +positive_test(complex_rule) +endif () \ No newline at end of file diff --git a/tests/semantic/comp_types2/comp_types2.dl b/tests/semantic/comp_types2/comp_types2.dl index ada578357c2..36c98becded 100644 --- a/tests/semantic/comp_types2/comp_types2.dl +++ b/tests/semantic/comp_types2/comp_types2.dl @@ -79,6 +79,7 @@ myA2.R($myA2.b(1)). .comp F { .type T = b {x:number} .decl R(x:T) + .output R R($b(1)). .printsize R } diff --git a/tests/semantic/complex_rule/complex_rule.dl b/tests/semantic/complex_rule/complex_rule.dl new file mode 100644 index 00000000000..4c6f76852f4 --- /dev/null +++ b/tests/semantic/complex_rule/complex_rule.dl @@ -0,0 +1,8 @@ +.decl a(x:number) +.decl f(x:number) +.decl query(x:number) +.output query() + +query(x) :- a(x), (f(x); a(y) ; f(y)). +a(1). +f(1). diff --git a/tests/semantic/complex_rule/complex_rule.err b/tests/semantic/complex_rule/complex_rule.err new file mode 100644 index 00000000000..2f73d671590 --- /dev/null +++ b/tests/semantic/complex_rule/complex_rule.err @@ -0,0 +1,3 @@ +Warning: Variable y only occurs once in file complex_rule.dl at line 6 +query(x) :- a(x), (f(x); a(y) ; f(y)). +---------------------------^----------- diff --git a/tests/semantic/complex_rule/complex_rule.out b/tests/semantic/complex_rule/complex_rule.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/semantic/complex_rule/query.csv b/tests/semantic/complex_rule/query.csv new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/semantic/complex_rule/query.csv @@ -0,0 +1 @@ +1 diff --git a/tests/semantic/pragma2/CMakeLists.txt b/tests/semantic/pragma2/CMakeLists.txt index 9180e39e41a..6b1b0d554e6 100644 --- a/tests/semantic/pragma2/CMakeLists.txt +++ b/tests/semantic/pragma2/CMakeLists.txt @@ -27,9 +27,22 @@ function(SOUFFLE_TEST_SEMANTIC_PRAGMA2_SETUP FILE SO_NAME) PROPERTIES OUTPUT_NAME "'${SO_NAME}'" CXX_EXTENSIONS OFF + WINDOWS_EXPORT_ALL_SYMBOLS ON ) - target_compile_options(${FILE} PUBLIC "-Wall;-Wextra;-fwrapv") + if (NOT MSVC) + target_compile_options(${FILE} PUBLIC "-Wall;-Wextra;-fwrapv") + else () + target_compile_options(${FILE} PUBLIC /W3 /WX) + endif() + + if (WIN32) + # Prefix all shared libraries with 'lib'. + set(CMAKE_SHARED_LIBRARY_PREFIX "lib") + + # Prefix all static libraries with 'lib'. + set(CMAKE_STATIC_LIBRARY_PREFIX "lib") + endif () if (SOUFFLE_DOMAIN_64BIT) target_compile_definitions(${FILE} PUBLIC RAM_DOMAIN_SIZE=64) diff --git a/tests/semantic/rel_redundant/rel_redundant.dl b/tests/semantic/rel_redundant/rel_redundant.dl index 16ff3aa6071..4ae938b45c4 100644 --- a/tests/semantic/rel_redundant/rel_redundant.dl +++ b/tests/semantic/rel_redundant/rel_redundant.dl @@ -56,7 +56,7 @@ lItv(e), anHG(dXn) :- wvhB(dXn), hnwC(e), !HhJv(_). .decl fX(A:number) .decl outX(A:number) -cX(x) :- aX(x), y=0. +cX(x) :- aX(x), y=0. dX(q,V,O) :- bX(O,V), aX(q). fX(x) :- cX(x). outX(Y) :- dX(f,Y,f), fX(c), eX(a,b). diff --git a/tests/semantic/rel_redundant/rel_redundant.err b/tests/semantic/rel_redundant/rel_redundant.err index 96457976c18..1ae1ef9068a 100644 --- a/tests/semantic/rel_redundant/rel_redundant.err +++ b/tests/semantic/rel_redundant/rel_redundant.err @@ -14,8 +14,8 @@ Warning: No rules/facts defined for relation eX in file rel_redundant.dl at line .decl eX(A:number, B:number) ------^---------------------- Warning: Variable y only occurs once in file rel_redundant.dl at line 59 -cX(x) :- aX(x), y=0. ------------------^---- +cX(x) :- aX(x), y=0. +----------------^---- Warning: Variable c only occurs once in file rel_redundant.dl at line 62 outX(Y) :- dX(f,Y,f), fX(c), eX(a,b). -------------------------^------------ diff --git a/tests/semantic/rule_typecompat/rule_typecompat.dl b/tests/semantic/rule_typecompat/rule_typecompat.dl index f8d6983f4e2..219849e05c4 100644 --- a/tests/semantic/rule_typecompat/rule_typecompat.dl +++ b/tests/semantic/rule_typecompat/rule_typecompat.dl @@ -54,12 +54,12 @@ F(x,y) :- F(x,v), F(w,y), !H(w), !D(w). F(x,y) :- F(x,v), F(w,y), !H(w), !D(v). // Type clash in binary relation -F(x,y) :- F(x,y), x > y. +F(x,y) :- F(x,y), x > y. // Type clash of derived types in binary relation F(x,y) :- F(x,v), F(w,y), w != v. // Undefined type -F(x,y) :- F(x,y), x > t. -F(x,y) :- F(x,y), t < x. +F(x,y) :- F(x,y), x > t. +F(x,y) :- F(x,y), t < x. diff --git a/tests/semantic/rule_typecompat/rule_typecompat.err b/tests/semantic/rule_typecompat/rule_typecompat.err index acf0def19c2..53b9075cac9 100644 --- a/tests/semantic/rule_typecompat/rule_typecompat.err +++ b/tests/semantic/rule_typecompat/rule_typecompat.err @@ -125,23 +125,23 @@ Warning: Variable v only occurs once in file rule_typecompat.dl at line 53 F(x,y) :- F(x,v), F(w,y), !H(w), !D(w). --------------^------------------------- Error: Unable to deduce type for variable x in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. ---^----------------------- +F(x,y) :- F(x,y), x > y. +--^---------------------- Error: Unable to deduce type for variable y in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. -----^--------------------- +F(x,y) :- F(x,y), x > y. +----^-------------------- Error: Unable to deduce type for variable x in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. -------------^------------- +F(x,y) :- F(x,y), x > y. +------------^------------ Error: Unable to deduce type for variable y in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. ---------------^----------- +F(x,y) :- F(x,y), x > y. +--------------^---------- Error: Unable to deduce type for variable x in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. --------------------^------ +F(x,y) :- F(x,y), x > y. +------------------^------ Error: Unable to deduce type for variable y in file rule_typecompat.dl at line 57 -F(x,y) :- F(x,y), x > y. ------------------------^-- +F(x,y) :- F(x,y), x > y. +----------------------^-- Error: Unable to deduce type for variable v in file rule_typecompat.dl at line 59 F(x,y) :- F(x,v), F(w,y), w != v. --------------^------------------- @@ -155,15 +155,15 @@ Error: Unable to deduce type for variable v in file rule_typecompat.dl at line 5 F(x,y) :- F(x,v), F(w,y), w != v. -------------------------------^-- Error: Ungrounded variable t in file rule_typecompat.dl at line 62 -F(x,y) :- F(x,y), x > t. ------------------------^-- +F(x,y) :- F(x,y), x > t. +----------------------^-- Warning: Variable t only occurs once in file rule_typecompat.dl at line 62 -F(x,y) :- F(x,y), x > t. ------------------------^-- +F(x,y) :- F(x,y), x > t. +----------------------^-- Error: Ungrounded variable t in file rule_typecompat.dl at line 63 -F(x,y) :- F(x,y), t < x. --------------------^------ +F(x,y) :- F(x,y), t < x. +------------------^------ Warning: Variable t only occurs once in file rule_typecompat.dl at line 63 -F(x,y) :- F(x,y), t < x. --------------------^------ +F(x,y) :- F(x,y), t < x. +------------------^------ 36 errors generated, evaluation aborted diff --git a/tests/semantic/underscore/underscore.dl b/tests/semantic/underscore/underscore.dl index e787a296c8b..8bc23d2c719 100644 --- a/tests/semantic/underscore/underscore.dl +++ b/tests/semantic/underscore/underscore.dl @@ -5,7 +5,7 @@ // Underscores can not appear in // ... binary relations -F(x,y) :- F(x,y), x > _. +F(x,y) :- F(x,y), x > _. // ... or in facts F("string",_). // ... or in heads of rules diff --git a/tests/semantic/underscore/underscore.err b/tests/semantic/underscore/underscore.err index 58b4050e285..3987096d689 100644 --- a/tests/semantic/underscore/underscore.err +++ b/tests/semantic/underscore/underscore.err @@ -1,6 +1,6 @@ Error: Underscore in binary relation in file underscore.dl at line 8 -F(x,y) :- F(x,y), x > _. ------------------------^-- +F(x,y) :- F(x,y), x > _. +----------------------^-- Error: Argument in fact is not constant in file underscore.dl at line 10 F("string",_). -----------^--- diff --git a/tests/swig/CMakeLists.txt b/tests/swig/CMakeLists.txt index 2a7f0a2297e..13bcf263992 100644 --- a/tests/swig/CMakeLists.txt +++ b/tests/swig/CMakeLists.txt @@ -16,7 +16,7 @@ function(SOUFFLE_RUN_PYTHON_SWIG_TEST) ) add_test(NAME ${PARAM_QUALIFIED_TEST_NAME}_run_swig - COMMAND sh -c "set -e; PYTHONPATH=. '${Python_EXECUTABLE}' '${PARAM_INPUT_DIR}/driver.py' '${PARAM_FACTS_DIR}' + COMMAND sh -c "set -e; PYTHONPATH=. '${Python3_EXECUTABLE}' '${PARAM_INPUT_DIR}/driver.py' '${PARAM_FACTS_DIR}' \\ 1> '${PARAM_TEST_NAME}-python.out' \\ 2> '${PARAM_TEST_NAME}-python.err'") set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_swig PROPERTIES @@ -96,7 +96,7 @@ function(SOUFFLE_RUN_SWIG_TEST_HELPER) OUTPUT_DIR ${OUTPUT_DIR} FIXTURE_NAME ${FIXTURE_NAME} TEST_LABELS "${TEST_LABELS}" - SOUFFLE_PARAMS "-s '${PARAM_LANGUAGE}'") + SOUFFLE_PARAMS "-s" "${PARAM_LANGUAGE}") if (PARAM_LANGUAGE STREQUAL "python") souffle_run_python_swig_test(TEST_NAME ${PARAM_TEST_NAME} diff --git a/tests/swig/java/family/family-java.out b/tests/swig/java/family/family-java.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/swig/python/family/family-python.out b/tests/swig/python/family/family-python.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/syntactic/CMakeLists.txt b/tests/syntactic/CMakeLists.txt index 959824a54c2..d76864c875e 100644 --- a/tests/syntactic/CMakeLists.txt +++ b/tests/syntactic/CMakeLists.txt @@ -17,6 +17,10 @@ endfunction() positive_test(binary) positive_test(comment) positive_test(comment2) +if (FALSE) +# Fails because warning/error reporting has incorrect column when the line contains an inline comment. +positive_test(comment3) +endif () positive_test(comment_rule) positive_test(cpp_keywords) positive_test(duplicates) @@ -61,3 +65,7 @@ positive_test(union_comp_type) positive_test(dot_identifiers) positive_test(input_adt_names1) positive_test(input_directive_rfc4180) +if (NOT MSVC) +# does not pass with Visual Studio pre-processor because it preserves all whitespaces +positive_test(whitespaces) +endif () diff --git a/tests/syntactic/comment3/A.csv b/tests/syntactic/comment3/A.csv new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/syntactic/comment3/comment3.dl b/tests/syntactic/comment3/comment3.dl new file mode 100644 index 00000000000..c25de7adbf7 --- /dev/null +++ b/tests/syntactic/comment3/comment3.dl @@ -0,0 +1,13 @@ +/* + * Souffle - A Datalog Compiler + * Copyright (c) 2022 The Souffle Development Team + * Licensed under the Universal Permissive License v 1.0 as shown at: + * - https://opensource.org/licenses/UPL + * - souffle/LICENSE + */ + +.decl A(x:number) +.output A() + +A(1) :- /* inline comment */ A(x). + diff --git a/tests/syntactic/comment3/comment3.err b/tests/syntactic/comment3/comment3.err new file mode 100644 index 00000000000..e4b19951ef7 --- /dev/null +++ b/tests/syntactic/comment3/comment3.err @@ -0,0 +1,3 @@ +Warning: Variable x only occurs once in file comment3.dl at line 12 +A(1) :- /* inline comment */ A(x). +-------------------------------^--- diff --git a/tests/syntactic/comment3/comment3.out b/tests/syntactic/comment3/comment3.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/syntactic/cpp_keywords/cpp_keywords.dl b/tests/syntactic/cpp_keywords/cpp_keywords.dl index 7f1aa8607b3..276cce23eb6 100644 --- a/tests/syntactic/cpp_keywords/cpp_keywords.dl +++ b/tests/syntactic/cpp_keywords/cpp_keywords.dl @@ -212,12 +212,19 @@ line(1). error(1). .decl pragma(x:number) pragma(1). +#ifndef _MSC_VER .decl _Pragma(x:number) _Pragma(1). +#endif .decl worked(x:number) .output worked() worked(X) :- workaround([X]). // TODO (#467, #468): the following is a hack to disable stratification as over a hundred subprograms are produced for this test .type number_record = [ x:number ] .decl workaround(x:number_record) -workaround([X]) :- alignas(X), alignof(X), asm(X), atomic_cancel(X), atomic_commit(X), atomic_noexcept(X), auto(X), bool(X), break(X), case(X), catch(X), char(X), char16_t(X), char32_t(X), class(X), concept(X), const(X), constexpr(X), const_cast(X), continue(X), decltype(X), default(X), delete(X), do(X), double(X), dynamic_cast(X), enum(X), explicit(X), export(X), extern(X), float(X), for(X), friend(X), goto(X), int(X), import(X), long(X), module(X), mutable(X), namespace(X), new(X), noexcept(X), nullptr(X), operator(X), or(X), private(X), protected(X), public(X), register(X), reinterpret_cast(X), requires(X), return(X), short(X), signed(X), sizeof(X), static(X), static_assert(X), static_cast(X), struct(X), switch(X), synchronized(X), template(X), this(X), thread_local(X), throw(X), try(X), typedef(X), typeid(X), typename(X), union(X), unsigned(X), using(X), virtual(X), void(X), volatile(X), wchar_t(X), while(X), xor(X), compl(X), bitor(X), bitand(X), and_eq(X), or_eq(X), xor_eq(X), not(X), and(X), not_eq(X), override(X), final(X), transaction_safe(X), transaction_safe_dynamic(X), if(X), elif(X), else(X), endif(X), defined(X), ifdef(X), ifndef(X), define(X), undef(X), include(X), line(X), error(X), pragma(X), _Pragma(X). +workaround([X]) :- alignas(X), alignof(X), asm(X), atomic_cancel(X), atomic_commit(X), atomic_noexcept(X), auto(X), bool(X), break(X), case(X), catch(X), char(X), char16_t(X), char32_t(X), class(X), concept(X), const(X), constexpr(X), const_cast(X), continue(X), decltype(X), default(X), delete(X), do(X), double(X), dynamic_cast(X), enum(X), explicit(X), export(X), extern(X), float(X), for(X), friend(X), goto(X), int(X), import(X), long(X), module(X), mutable(X), namespace(X), new(X), noexcept(X), nullptr(X), operator(X), or(X), private(X), protected(X), public(X), register(X), reinterpret_cast(X), requires(X), return(X), short(X), signed(X), sizeof(X), static(X), static_assert(X), static_cast(X), struct(X), switch(X), synchronized(X), template(X), this(X), thread_local(X), throw(X), try(X), typedef(X), typeid(X), typename(X), union(X), unsigned(X), using(X), virtual(X), void(X), volatile(X), wchar_t(X), while(X), xor(X), compl(X), bitor(X), bitand(X), and_eq(X), or_eq(X), xor_eq(X), not(X), and(X), not_eq(X), override(X), final(X), transaction_safe(X), transaction_safe_dynamic(X), if(X), elif(X), else(X), endif(X), defined(X), ifdef(X), ifndef(X), define(X), undef(X), include(X), line(X), error(X), pragma(X) +#ifndef _MSC_VER +, _Pragma(X) +#endif +. + diff --git a/tests/syntactic/syntax2/syntax2.dl b/tests/syntactic/syntax2/syntax2.dl index 0d2ef59e629..6c20d7b6354 100644 --- a/tests/syntactic/syntax2/syntax2.dl +++ b/tests/syntactic/syntax2/syntax2.dl @@ -5,7 +5,7 @@ .decl edge (node1:Node, node2:Node) reachable(X,Y) :- edge(X,Y). -reachable(X,Y) :- edge(X,Z), / +reachable(X,Y) :- edge(X,Z), / reachable(Z,Y). same_clique(X,Y) :- reachable(X,Y), reachable(Y,X). diff --git a/tests/syntactic/syntax2/syntax2.err b/tests/syntactic/syntax2/syntax2.err index 8c9bfd3be23..827a0e68883 100644 --- a/tests/syntactic/syntax2/syntax2.err +++ b/tests/syntactic/syntax2/syntax2.err @@ -1,4 +1,4 @@ Error: syntax error, unexpected / in file syntax2.dl at line 8 -reachable(X,Y) :- edge(X,Z), / -------------------------------------^- +reachable(X,Y) :- edge(X,Z), / +-----------------------------^- 1 errors generated, evaluation aborted diff --git a/tests/syntactic/syntax9/syntax9.dl b/tests/syntactic/syntax9/syntax9.dl index 5a54b87bffa..ff6a348c3dc 100644 --- a/tests/syntactic/syntax9/syntax9.dl +++ b/tests/syntactic/syntax9/syntax9.dl @@ -1,9 +1,9 @@ .type N <: symbol -.decl Reachable (a:N x b:N) -.decl Same (a:N, b:N) -.output Same () -.decl Edge (a:N, b:N) +.decl Reachable (a:N x b:N) +.decl Same (a:N, b:N) +.output Same () +.decl Edge (a:N, b:N) Reachable(x,y) :- Edge(x,y). Reachable(x,y) :- Edge(x,z), Reachable(z,y). diff --git a/tests/syntactic/syntax9/syntax9.err b/tests/syntactic/syntax9/syntax9.err index 90fd58d3989..4ac5af09329 100644 --- a/tests/syntactic/syntax9/syntax9.err +++ b/tests/syntactic/syntax9/syntax9.err @@ -1,4 +1,4 @@ Error: syntax error, unexpected identifier, expecting ) or "," in file syntax9.dl at line 3 -.decl Reachable (a:N x b:N) ------------------------^------ +.decl Reachable (a:N x b:N) +---------------------^------ 1 errors generated, evaluation aborted diff --git a/tests/syntactic/whitespaces/A.csv b/tests/syntactic/whitespaces/A.csv new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/syntactic/whitespaces/whitespaces.dl b/tests/syntactic/whitespaces/whitespaces.dl new file mode 100644 index 00000000000..2e3df9c6a55 --- /dev/null +++ b/tests/syntactic/whitespaces/whitespaces.dl @@ -0,0 +1,13 @@ +/* + * Souffle - A Datalog Compiler + * Copyright (c) 2022 The Souffle Development Team + * Licensed under the Universal Permissive License v 1.0 as shown at: + * - https://opensource.org/licenses/UPL + * - souffle/LICENSE + */ + +.decl A(x:number) +.output A() + +A(1) :- A(x). + diff --git a/tests/syntactic/whitespaces/whitespaces.err b/tests/syntactic/whitespaces/whitespaces.err new file mode 100644 index 00000000000..fd4c808ec53 --- /dev/null +++ b/tests/syntactic/whitespaces/whitespaces.err @@ -0,0 +1,3 @@ +Warning: Variable x only occurs once in file whitespaces.dl at line 12 +A(1) :- A(x). +-------------------------------------------^--- diff --git a/tests/syntactic/whitespaces/whitespaces.out b/tests/syntactic/whitespaces/whitespaces.out new file mode 100644 index 00000000000..e69de29bb2d