diff --git a/.github/workflows/godel_build.yml b/.github/workflows/godel_build.yml new file mode 100644 index 00000000..efe613fb --- /dev/null +++ b/.github/workflows/godel_build.yml @@ -0,0 +1,62 @@ +name: GodelScript Build + +on: + push: + branches: [ main, lhk_dev ] + pull_request: + branches: [ main ] + +jobs: + mac-aarch64-build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Update Git Env + run: | + git config --global user.name "$(git log -n 1 --pretty=format:%an)" + git config --global user.email "$(git log -n 1 --pretty=format:%ae)" + - name: Build + run: | + cd godel-script + cd godel-backend/souffle + git am ../0001-init-self-used-souffle-from-public-souffle.patch + cd ../.. + mkdir build + cd build + cmake .. + make -j6 + - name: Test + run: | + cd godel-script + ./build/godel + ./build/godel --version + ./build/godel -h --color-off + + linux-x86_64-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Update Git Env + run: | + git config --global user.name "$(git log -n 1 --pretty=format:%an)" + git config --global user.email "$(git log -n 1 --pretty=format:%ae)" + - name: Build + run: | + cd godel-script + cd godel-backend/souffle + git am ../0001-init-self-used-souffle-from-public-souffle.patch + cd ../.. + mkdir build + cd build + cmake .. + make -j6 + - name: Test + run: | + cd godel-script + ./build/godel + ./build/godel --version + ./build/godel -h --color-off \ No newline at end of file diff --git a/.gitignore b/.gitignore index 35885f72..81174ae3 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,9 @@ build/ ### VS Code ### .vscode/ .cloudide + +### Bazel ### +bazel-bin +bazel-CodeFuse-Query +bazel-out +bazel-testlogs diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..df26c052 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "godel-script/godel-backend/souffle"] + path = godel-script/godel-backend/souffle + url = https://github.com/souffle-lang/souffle.git diff --git a/godel-script/.gitignore b/godel-script/.gitignore new file mode 100644 index 00000000..38202cac --- /dev/null +++ b/godel-script/.gitignore @@ -0,0 +1,3 @@ +# build directory +build +cmake-build \ No newline at end of file diff --git a/godel-script/CMakeLists.txt b/godel-script/CMakeLists.txt new file mode 100644 index 00000000..84109426 --- /dev/null +++ b/godel-script/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.1) + +project("GodelScript" VERSION 0.1 DESCRIPTION "GodelScript compiler") + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if(UNIX AND NOT APPLE) + set(LINUX TRUE) +endif() + +set(GODEL_FRONTEND_HDR_FILES + godel-frontend/src/cli.h + godel-frontend/src/engine.h + godel-frontend/src/lexer.h + godel-frontend/src/parse.h + godel-frontend/src/semantic.h + godel-frontend/src/symbol.h + godel-frontend/src/ir/aggregator_inline_remark.h + godel-frontend/src/ir/flatten_block.h + godel-frontend/src/ir/ir_gen.h + godel-frontend/src/ir/ir_context.h + godel-frontend/src/ir/lir.h + godel-frontend/src/ir/inst_combine.h + godel-frontend/src/ir/name_mangling.h + godel-frontend/src/ir/pass.h + godel-frontend/src/ir/pass_manager.h + godel-frontend/src/ir/remove_unused.h + godel-frontend/src/error/error.h + godel-frontend/src/ast/ast_node.h + godel-frontend/src/ast/ast_root.h + godel-frontend/src/ast/ast_visitor.h + godel-frontend/src/ast/decl.h + godel-frontend/src/ast/expr.h + godel-frontend/src/ast/stmt.h + godel-frontend/src/ast/ast_dumper.h + godel-frontend/src/ast/template_extractor.h + godel-frontend/src/sema/ungrounded_checker.h + godel-frontend/src/sema/fact_statement_checker.h + godel-frontend/src/sema/self_reference_check.h + godel-frontend/src/sema/context.h + godel-frontend/src/sema/global_symbol_loader.h + godel-frontend/src/sema/symbol_import.h + godel-frontend/src/sema/data_structure_construct.h + godel-frontend/src/sema/inherit_schema.h + godel-frontend/src/sema/function_declaration.h + godel-frontend/src/sema/annotation_checker.h + godel-frontend/src/util/util.h + godel-frontend/src/package/package.h + godel-frontend/src/package/module_tree.h) + +set(GODEL_FRONTEND_SRC_FILES + godel-frontend/src/cli.cpp + godel-frontend/src/engine.cpp + godel-frontend/src/lexer.cpp + godel-frontend/src/parse.cpp + godel-frontend/src/semantic.cpp + godel-frontend/src/symbol.cpp + godel-frontend/src/ir/aggregator_inline_remark.cpp + godel-frontend/src/ir/flatten_block.cpp + godel-frontend/src/ir/ir_gen.cpp + godel-frontend/src/ir/ir_context.cpp + godel-frontend/src/ir/lir.cpp + godel-frontend/src/ir/inst_combine.cpp + godel-frontend/src/ir/name_mangling.cpp + godel-frontend/src/ir/pass.cpp + godel-frontend/src/ir/pass_manager.cpp + godel-frontend/src/ir/remove_unused.cpp + godel-frontend/src/error/error.cpp + godel-frontend/src/ast/ast_visitor.cpp + godel-frontend/src/ast/ast_root.cpp + godel-frontend/src/ast/decl.cpp + godel-frontend/src/ast/expr.cpp + godel-frontend/src/ast/stmt.cpp + godel-frontend/src/ast/ast_dumper.cpp + godel-frontend/src/ast/template_extractor.cpp + godel-frontend/src/sema/ungrounded_checker.cpp + godel-frontend/src/sema/fact_statement_checker.cpp + godel-frontend/src/sema/self_reference_check.cpp + godel-frontend/src/sema/context.cpp + godel-frontend/src/sema/global_symbol_loader.cpp + godel-frontend/src/sema/symbol_import.cpp + godel-frontend/src/sema/data_structure_construct.cpp + godel-frontend/src/sema/inherit_schema.cpp + godel-frontend/src/sema/function_declaration.cpp + godel-frontend/src/sema/annotation_checker.cpp + godel-frontend/src/util/util.cpp + godel-frontend/src/package/package.cpp + godel-frontend/src/package/module_tree.cpp) + +execute_process(COMMAND mkdir -p install) +set(ENV{CC} cc) +set(ENV{CXX} c++) +# build bison +set(BISON_PKG bison-3.8.2) +execute_process(COMMAND tar -xf ${CMAKE_CURRENT_SOURCE_DIR}/godel-backend/tools/${BISON_PKG}.tar) +execute_process(COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/install WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${BISON_PKG}) +execute_process(COMMAND make -j install WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${BISON_PKG}) + +# build flex +set(FLEX_PKG flex-2.6.4) +execute_process(COMMAND tar -xf ${CMAKE_CURRENT_SOURCE_DIR}/godel-backend/tools/${FLEX_PKG}.tar) +execute_process(COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/install WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${FLEX_PKG}) +execute_process(COMMAND make -j install WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${FLEX_PKG}) + +# set variables for souffle target +set(FLEX_EXECUTABLE ${CMAKE_BINARY_DIR}/install/bin/flex) +set(BISON_EXECUTABLE ${CMAKE_BINARY_DIR}/install/bin/bison) + +set(SOUFFLE_DOMAIN_64BIT ON) +set(SOUFFLE_USE_CURSES OFF) +set(SOUFFLE_ENABLE_TESTING OFF) + +add_subdirectory(godel-backend/souffle) +add_subdirectory(godel-backend/extension) + +add_library(godel-frontend STATIC + ${GODEL_FRONTEND_SRC_FILES}) + +target_link_libraries(godel-frontend PUBLIC + libsouffle souffle_ext) + +target_include_directories(godel-frontend PUBLIC + ${PROJECT_SOURCE_DIR}) + +# add binary target godel +add_executable(godel godel-frontend/src/main.cpp) +# avoid easylogging to generate myeasylog.log automatically +add_definitions(-DELPP_NO_DEFAULT_LOG_FILE) +# link static library +target_link_libraries(godel + PRIVATE godel-frontend) +# link dynamic library +target_link_libraries(godel PUBLIC + libsouffle-shared souffle_ext) diff --git a/godel-script/README.md b/godel-script/README.md new file mode 100644 index 00000000..1087a50a --- /dev/null +++ b/godel-script/README.md @@ -0,0 +1,110 @@ +# GödelScript + +## Content + +* 简介 | [Introduction](#introduction) +* 文档 | [Documents](#documents) +* 编译 | [Compilation](#compilation) +* 用法 | [Usage](#usage) + +## Introduction + +GödelScript is designed for creating code analysis libraries and programs, +and compiling them to soufflé more easily. With it's Object-Oriented features, +it has great maintainability and readability. + +```rust +@output +pub fn hello() -> string { + return "Hello World!" +} +``` + +## Documents + +* GödelScript Language Reference + * GödelScript [Program](./docs/language-reference/program.md) + * GödelScript [Type](./docs/language-reference/type.md) + * GödelScript [Schema](./docs/language-reference/schemas.md) + * GödelScript [Database](./docs/language-reference/databases.md) + * GödelScript [Enum](./docs/language-reference/enums.md) + * GödelScript [Impl](./docs/language-reference/impl.md) + * GödelScript [Function](./docs/language-reference/functions.md) + * GödelScript [Import](./docs/language-reference/import.md) + * GödelScript [Query](./docs/language-reference/queries.md) + * GödelScript [Statement](./docs/language-reference/functions.md#statement) + * GödelScript [Expression](./docs/language-reference/functions.md#expression) +* GödelScript [Query Example](../example) +* GödelScript [Syntax Definition](./docs/syntax.md) + +## Compilation + +Structure of this project: + +``` +. +|-- docs godel-script documents +|-- godel-backend godel-script backend +| |-- extension godel-script souffle extension +| |-- souffle souffle source code +| +-- tools souffle build tools ++-- godel-frontend godel-script frontend + +-- src godel-frontend source code +``` + +Need C++ standard at least `-std=c++17`. + +### Apply Patch On Soufflé Submodule + +GödelScript uses a self-modified soufflé from a much older branch of public soufflé, +now we use patch to make sure it could be built successfully. + +Use this command to apply patch: + +```bash +cd souffle +git am ../../0001-init-self-used-souffle-from-public-souffle.patch +``` + +Use these commands to revert: + +```bash +cd souffle +git apply -R ../0001-init-self-used-souffle-from-public-souffle.patch +git reset HEAD~ +``` + +### Build GödelScript + +Use command below: + +```bash +mkdir build +cd build +cmake .. +make -j +``` + +After building, you'll find `build/godel` in the `build` folder. + +## Usage + +Use this command for help: + +> ./build/godel -h + +### Compile Target Soufflé + +> ./build/godel -p {godel library directory} {input file} -s {soufflé output file} -Of + +`-Of` is an optimization for join order, we suggest to switch it on. + +### Directly Run Soufflé + +> ./build/godel -p {godel library directory} {input file} -r -Of -f {database directory} + +`-Of` is an optimization for join order, we suggest to switch it on. + +`-r` means directly run soufflé. + +`-v` could be used for getting verbose info. diff --git a/godel-script/docs/language-reference/databases.md b/godel-script/docs/language-reference/databases.md new file mode 100644 index 00000000..609384e6 --- /dev/null +++ b/godel-script/docs/language-reference/databases.md @@ -0,0 +1,50 @@ +# GödelScript Database + +Back to [README.md](../../README.md#documents) + +## Declaration + +```rust +database School { + student: *Student, + classes: *Class as "class" + ... +} +``` + +Tables in databases should be set type, which uses `*` before the type name. +And the table type must be `schema`. +And for table name `student`, when running soufflé directly, we read sqlite database +using the same table name. + +If the table name conflicts with a keyword, try using `as "real_table"`, and +GödelScript will find the data from sqlite table `real_table`. + +## Initializing + +Database has a native method `fn load(dbname: string) -> Self`. +The argument string must be a string literal. + +```rust +fn default_db() -> School { + return School::load("example_db_school.db") // must use string literal +} +``` + +Then GödelScript will give you the input database. + +## Get Schema From Database + +It's quite easy to fetch schema data from database. +For example in `Student::__all__(db: School) -> *School`, +we could fetch the data by directly using `db.student`. + +```rust +impl Student { + pub fn __all__(db: School) -> *Student { + return db.student + } +} +``` + +Back to [README.md](../../README.md#documents) \ No newline at end of file diff --git a/godel-script/docs/language-reference/enums.md b/godel-script/docs/language-reference/enums.md new file mode 100644 index 00000000..4011f5d3 --- /dev/null +++ b/godel-script/docs/language-reference/enums.md @@ -0,0 +1,25 @@ +# GödelScript Enum + +Back to [README.md](../../README.md#documents) + +## Declaration + +```rust +enum Status { + exited, // 0 + running, // 1 + suspend // 2 +} +``` + +Usage: + +```rust +fn example() -> Status { + Status::exited + Status::running + Status::suspend +} +``` + +Back to [README.md](../../README.md#documents) diff --git a/godel-script/docs/language-reference/functions.md b/godel-script/docs/language-reference/functions.md new file mode 100644 index 00000000..39b14ecc --- /dev/null +++ b/godel-script/docs/language-reference/functions.md @@ -0,0 +1,360 @@ +# GödelScript Function + +Back to [README.md](../../README.md#documents) + +## Content + +* Function [Declaration](#function-declaration) +* Function [Implement](#function-implement) +* [Statement](#statement) +* [Expression](#expression) + +## Function Declaration + +GödelScript function declaration should include parameters, type of parameters +and the return type. +Only `main` function does not need return type. + +```rust +fn func_name(param0: int, param1: string) -> ReturnValueType {...} + +// deprecated +fn main() {...} +``` + +All the functions muse be [implemented](#function-implement)。 + +## Function Implementation + +Implement functions: + +```rust +fn func_name(param0: int, param1: string) -> int { + return param0 + param1.to_int() +} +``` + +Multiple statements in the same code block has the `or` condition. +They do not share variables and conditions through each other. And the execution +is not ordered (scheduled by soufflé). + +## Statement + +GödelScript supports the following statements: + +* [For Statement](#for-statement) +* [Let Statement](#let-statement) +* [Condition Statement](#condition-statement) +* [Match Statement](#match-statement) +* [Fact Statement(Experimental Feature)](#fact-statement) +* [Return Statement](#return-statement) + +Here's an example for nested statements +```rust +for(...) { + let (...) { + if (...) { + return true + } + } + let (...) { + if (...) { + return false + } + } +} +``` + +### For Statement + +For statement is used to define a variable from a set/collection. +Initialization expression is used after keyword `in`, and the type must be a set. +Initialization order is from left to right. + +```rust +for (a in Annotation(db), c in Class(db), m in Method(db)) { + ... +} +``` + +### Let Statement + +Let statement is used to define a variable initialized by single value. +initial value after `=` must not be a set type. +Initialization order is from left to right. + +```rust +let (file = c.getLocation.getFile(), anno = a.getName(), line = 1004) { + ... +} +``` + +### Condition Statement + +Condition statement does not support `else`, for the reason that this branch +often causes `ungrounded error` in soufflé. + +```rust +if (xxxxx) { + ... +} +``` + +### Match Statement + +Match statement requires the matched variable/value is type of `int` or `string`. +And must use literals for matching. + +```rust +match(type) { + 0 => if (anno.contains("a")) { + return true + }, + 1 => return false, + 2 => for(b: BinaryOperator in BinaryOperator(db)) { + if (b.getOperatorType() = "+") { + return true + } + } +} +``` + +### Fact Statement + +Fact statement is used to generate a collection of temporary data. +Once it is used in the function, other statements are not allowed. +All the data inside the fact statement must be `int` or `string` literals. +And each record of data must satisfy the parameter list: + +```rust +fn multi_input_test(a: int, b: string, c: int) -> bool { + [{1, "1", 1}, + {2, "2", 2}, + {3, "3", 3}, + {4, "4" ,4}] +} + +@output +fn out(a: int) -> bool { + for(num in int::range(0, 100)) { + if (multi_input_test(num, num.to_string(), num) && a = num) { + return true + } + } +} +``` + +### Return Statement + +Return statement uses two keywords, and values are required after them. + +```rust +return 0 // for return of single return type +yield 0 // for return of set return type +``` + +`return` is often used for return single value: + +```rust +fn get() -> int { + return 0 +} +``` + +`yield` is only allowed to return a set of value: + +```rust +fn int_set() -> *int { + yield 0 + yield 1 + yield 2 +} + +fn getm() -> *int { + // fn int_set() -> *int; + yield int_set() + yield 3 + yield 4 +} +``` + +## Expression + +GödelScript supports the following expressions: + +* [Call Expression](#call-expression) +* [Binary Operator](#binary-operator) + * [Mathematic Operator](#mathematic-operator) + * [Compare Operator](#compare-operator) + * [Logic Operator](#logic-operator) +* [Unary Operator](#unary-operator) + +### Call Expression + +Main components of call expressions are as follows: + +* First Expression + * [Function Call](#function-call) + * [Literal | Bracketed Expression](#literal-or-bracketed-expression) + * [Initializer List(Struct Expression)](#initializer-list) +* [Field Call](#field-call) +* [Path Call](#path-call) + +#### Function Call + +GödelScript function call is the same as other programming languages: + +```rust +global_function(arg0, arg1, arg2) +``` + +#### Literal or Bracketed Expression + +GödelScript Literal includes `int` `string` `float` `bool` literals. +These literals could be used as the first expression in call chains: + +```rust +fn example() -> *int { + yield "hello".len() + yield 1.add(2).sub(4) + yield "123".to_int().add(0) +} +``` + +Also bracketed expressions are allowed to be the first expression: + +```rust +fn example() -> *int { + yield ("hello" + "world").len() + yield (1 + 0).add(2).sub(4) +} +``` + +#### Initializer List + +GödelScript allows initializer for creating schema instance, +but the precondition is that this instance should be in the universal set of +the schema. +Initialization order of fields is not required. + +```rust +schema Student { + @primary id: int, + name: string +} + +impl Student { + pub fn __all__(db: DB) -> *Student { + return db.students + } +} + +fn example() -> Student { + return Student {id: 0, name: "xxx"} +} +``` + +#### Field Call + +Field call using `.`: + +1. get field from schema instance +```rust +fn example(stu: Student) -> string { + return stu.name + // ^^^^^ +} +``` +2. get table from database instance +```rust +impl Student { + @data_constraint + fn __all__(db: DB) -> *Student { + return db.students + // ^^^^^^^^^ + } +} +``` +3. call method from basic type instance or schema instance +```rust +fn example() -> *int { + yield 1.add(2) + // ^^^^^^^ + yield Student {id: 0, name: "xxx"}.getId() + // ^^^^^^^^ +} +``` + +#### Path Call + +Path call using `::`: + +1. call static method from schema +```rust +impl Student { + fn type() -> string { + return "Student" + } +} + +fn example() -> string { + return Student::type() + // ^^^^^^^^ +} +``` +2. call load method from database: `load(str: string) -> database` +```rust +fn example() -> *int { + let (db = DB::load("example_src.db")) { + // ^^^^^^^^^^^^^^^^^^^^^^^^ + for(stu in Student(db)) { + yield stu.id + } + } +} +``` +3. get member from enum +```rust +enum Status { + running, + suspend +} + +fn example() -> int { + Status::running + // ^^^^^^^^^ + Status::suspend + // ^^^^^^^^^ +} +``` + +### Binary Operator + +#### Mathematic Operator + +|`+`|`-`|`*`|`/`| +|:--|:--|:--|:--| +|add|sub|mul|div| + +#### Compare Operator + +Result must be `bool`. `=` will do binding operation if the left-value is not grounded when doing this +comparison, and this expression returns `true`. + +|`=`|`<`|`>`|`<=`|`>=`|`!=`| +|:--|:--|:--|:--|:--|:--| +|eq|lt|gt|le|ge|ne| + +#### Logic Operator + +|`&&`|`\|\|`| +|:--|:--| +|and|or| + +### Unary Operator + +|`!`|`-`| +|:--|:--| +|not|neg| + +Back to [README.md](../../README.md#documents) diff --git a/godel-script/docs/language-reference/impl.md b/godel-script/docs/language-reference/impl.md new file mode 100644 index 00000000..8f0de63e --- /dev/null +++ b/godel-script/docs/language-reference/impl.md @@ -0,0 +1,18 @@ +# GödelScript Impl + +Back to [README.md](../../README.md#documents) + +All functions in impl block should be implemented. + +```rust +impl SchemaA { + pub fn __all__(db: DB) -> *SchemaA { + ... + } + pub fn getName(self) -> string { + ... + } +} +``` + +Back to [README.md](../../README.md#documents) diff --git a/godel-script/docs/language-reference/import.md b/godel-script/docs/language-reference/import.md new file mode 100644 index 00000000..25b65a46 --- /dev/null +++ b/godel-script/docs/language-reference/import.md @@ -0,0 +1,102 @@ +# GödelScript Import/Use + +Back to [README.md](../../README.md#documents) + +## Content + +* [Import All Symbol](#import-all-symbol) +* [Partial Import](#partial-import) +* [Package Management](#package-management) + +## Import All Symbol + +```rust +use coref::java::* +``` + +## Partial Import + +```rust +use coref::java::{Annotation, Class, Method, JavaDB} +use coref::xml::XmlElement +``` + +## Package Management + +GödelScript package manager is enabled when command line arguments including +`-p {package dir path}`. + +### Path Mapping + +Package manager will scan the structure of given directory, finding all the files +with `.gdl` or `.gs`. Then mapping the relative path to package path. + +If illegal characters exist in relative path, for example `-`, or only numbers for +the file name, this path will not be accepted by package manager. But package +manager may not report error, instead, it will ignore them. + +If want to get the ignored relative paths, try using `-v` for verbose info. +Package manager will report these paths by using warning info. + +But if package path confliction occurs after scan, package manager will report +error and terminate the compilation process. + +### Example + +``` +Library +|-- coref.java.gdl +|-- coref.xml.gdl +|-- coref.python.gdl ++-- coref + |-- go.gdl + +-- a + +-- b.gdl + +=> + +coref::java +coref::xml +coref::python +coref::go +coref::a::b +``` + +Path confliction occurs in the example below: + +``` +Library +|-- coref +| |-- java.gdl +| +-- python.gdl ++-- coref.python.gdl + +=> + +coref::java +coref::python -- \ + > confliction occurs +coref::python -- / +``` + +Illegal characters detected in this example: + +``` +Library +|-- 0123.gdl +|-- my-godel-lib +| +-- js.gdl ++-- lib-file.123.gdl + +=> +0123 +^^^^ number + +my-godel-lib::js + ^ ^ character `-` included + +lib-file::123 + ^ ^^^ path segment including number and `-` +``` + +Back to [README.md](../../README.md#documents) diff --git a/godel-script/docs/language-reference/program.md b/godel-script/docs/language-reference/program.md new file mode 100644 index 00000000..0bd5c77d --- /dev/null +++ b/godel-script/docs/language-reference/program.md @@ -0,0 +1,111 @@ +# GödelScript Program + +Back to [README.md](../../README.md#documents) + +## Content +* 程序组成 | [Program Component](#program-component) +* 程序注释 | [Notes](#notes) +* 程序入口 | [Main](#main) + +## Program Component + +GödelScript programs may include: + +1. [package/symbol import](./import.md) +2. [enum declaration](./enums.md) +3. [schema declaration](./schemas.md) +4. [database declaration](./databases.md) +6. [schema method implementation](./impl.md) +7. [function declaration and implementation](./functions.md) +8. [query declaration](./queries.md) + +Here is an example including all the components mentioned above: + +```rust +// package import +use coref::java::{Annotation, JavaDB} + +// function declaration +fn default_db() -> JavaDB { + return JavaDB::load("example.db") +} + +// enum declaration +Enum status { + killed, + running, + suspend +} + +// schema declaration +Schema File { + @primary id: int +} + +// database declaration +database NewDB { + file: *File +} + +impl File { + pub fn __all__() -> *File { + yield File {id: 1} + yield File {id: 2} + } + + pub fn getId(self) -> int { + return self.id + } +} + +// query declaration +query get_all_anno from + anno in Annotation(default_db()) +select + anno.id as id +``` + +## Notes + +GödelScript uses C-like notes/comments。 + +```c +// single line comment + +/* + * multi line comment +*/ +``` + +## Main + +__[Warning] Deprecated: Better use `@output`__ + +Query output of GödelScript will output by main function. +Main function is the only one that does not need return value in GödelScript. + +Query output uses native function `output`. +This function only needs functions that you want to output the result as the argument. +And the argument function __does not need arguments__. +`output` can only be called in `main`. + +```rust +fn query_0(a: int, b: int) -> bool { + ... +} + +fn query_1(a: int, b: string) -> bool { + ... +} + +fn main() { + // output table structure: a (int) b (int) + output(query_0()) + + // output table structure: a (int) b (string) + output(query_1()) + ... +} +``` + +Back to [README.md](../../README.md#documents) \ No newline at end of file diff --git a/godel-script/docs/language-reference/queries.md b/godel-script/docs/language-reference/queries.md new file mode 100644 index 00000000..aa88e650 --- /dev/null +++ b/godel-script/docs/language-reference/queries.md @@ -0,0 +1,93 @@ +# GödelScript Query + +Back to [README.md](../../README.md#documents) + +## Query Name + +After keyword `query`, there requires a query name: + +```rust +query this_is_example_query +``` + +## From + +GödelScript query uses keyword `from` for variable definition. +Declared variables must be initialized, the way of initializing variables is the same +as in `for` statement. +But we do not need to consider whether the variable is a collection or not. + +Initialization is executed by order, so using variables before when initializing +other variables is allowed. + +```rust +from + anno in Annotation(db()), + class in Class(db()), + loc in class.getLocation() +``` + +## Where + +GödelScript query uses `where` with conditional expression for filtering data. + +```rust +where + anno = class.getAnnotation() && + loc.getFile().getRelativePath().contains(".java") +``` + +## Select + +GödelScript query uses `select` to generate the final result. +Each select data has an expression and it's corresponding column name after keyword `as`. If column name is not given, GödelScript will generate a random column name automatically. + +```rust +select + anno.getName() as annotationName, + loc.getFile() as fileName, + class.getName() // random column name: column_543771021 +``` + +The total query declaration is as follows: + +```rust +query this_is_example_query from + anno in Annotation(db()), + class in Class(db()), + loc in class.getLocation() +where + anno = class.getAnnotation() && + loc.getFile().getRelativePath().contains(".java") +select + anno.getName() as annotationName, + loc.getFile() as fileName, + class.getName() +``` + +And it is equivalent to: + +```rust +@output +fn this_is_example_query( + annotationName: string, + fileName: string, + column_543771021: string +) -> bool { + for(anno in Annotation(db()), class in Class(db())) { + let (loc in c.getLocation()) { + if (anno = c.getAnnotation() && + loc.getFile().getRelativePath().contains(".java")) { + if (annotationName = anno.getName() && + fileName = loc.getFile() && + column_543771021 = class.getName()) { + return true + } + } + } + } +} + +``` + +Back to [README.md](../../README.md#documents) diff --git a/godel-script/docs/language-reference/schemas.md b/godel-script/docs/language-reference/schemas.md new file mode 100644 index 00000000..f45fe6e0 --- /dev/null +++ b/godel-script/docs/language-reference/schemas.md @@ -0,0 +1,208 @@ +# GödelScript Schema + +Back to [README.md](../../README.md#documents) + +## Declaration + +Here's a schema declaration example, +the field declaration format is `field : type`, +primary key is annotated by `@primary`. + +```rust +schema Student { + @primary id: int, + name: string, + phone: string +} +``` + +## Initializing + +GödelScript requires universal set for schema. +We could simply think it as the constructor of schema. +The constructor is declared in `impl` block, and should be public. +GödelScript only accept `__all__` as the constructor. + +### From Database + +Schema's universal set could be fetched from a database: + +```rust +database DB { + students: *Student +} + +impl Student { + pub fn __all__(db: DB) -> *Student { + return db.students + } +} + +fn getStudents() -> *Student { + let (db = DB::load("example.db")) { + for (student in Student(db)) { + if (student.name.contains("von")) { + yield student + } + } + } +} +``` + +Inherited schema could use [this](#initializing-inherited-schema) to initialize. + +### From Initializer + +Schema's universal set could be initialized from initializers: + +```rust +impl Student { + pub fn __all__() -> *Student { + yield Student {id: 1, name: "zxj", phone: "11451419"} + yield Student {id: 2, name: "fxj", phone: "11451419"} + yield Student {id: 3, name: "lyn", phone: "11451419"} + } +} +``` + +This example also shows the feature of `initializer`, +by this way we could create a schema instance. +Although it's an instance created temporarily, this instance also should be +included in the universal set. Otherwise the creation is failed. + +In this example, the `initializer` is used in constructor, so all the created +instance must be included in universal set. + +## Inheritance + +### Inherit Fields + +Schema could be inherited, after inheritance, parent schema's fields will be +add at the front of the child schema structure. +All the inheritable methods will also be inherited from parent schema, except +`__all__`. + +```rust +schema Lee extends Student { + // parent fields added here. + + // @primary id: int, + // name: string, + // phone: string, + for_example: int +} +``` + +### Inherit Methods + +If parent schema has these two methods, child schema will inherit them. + +```rust +impl Student { + // method, first parameter is self, do not need type declaration. + fn getName(self) -> string { + return self.name + } + // static method, without self parameter + fn getType() -> string { + return "Student" + } +} +``` + +### Method Override + +GödelScript allows child schema implements methods that +having the same names of parent methods. +Methods of parent schema will be overridden. +Overridden methods share the same name, +but there's no need to share the same parameters and return type. + +```rust +impl Lee { + fn getType() -> string { + return "Lee Student" + } +} +``` + +### Initializing Inherited Schema + +We often initialize child schema by universal set of parent schema: + +```rust +schema Lee extends Student { + // @primary id: int, + // name: string, + // phone: string, + for_example: int +} + +impl Lee { + pub fn __all__(db: DB) -> *Lee { + // schema(db) will call schema::__all__(db), this is a syntactic sugar + // also it is correct to directly use schema::__all__(db) + for (parent in Student(db)) { + // ..parent here is a spread syntax + yield Lee { ..parent, for_example: 114 } + } + } +} +``` + +And from the example above, we use another initializer feature `spread`, +this syntax will expand the input schema instance. GödelScript only do duck-type +check for the schema instance, so if the structure is correct, the program is +correct. It is not a must to use parent schema to initialize... + +```rust +yield Lee { ..parent, for_example: 114 } +// this is equivalent to +// yield Lee { id: parent.id, name: parent.name, phone: parent.phone, for_example: 114 } +``` + +Here is an example, get `Class` in a specified file from universal set of `Class`, +by using inheritance: + +```rust +schema ClassInIDKFile extends Class {} + +impl ClassInIDKFile { + fn __all__(db: JavaDB) -> *ClassInIDKFile { + for(c in Class(db)) { + if (c.getLocation().getFile().getRelativePath() = "./com/xxx/xxx.java") { + yield ClassInIDKFile { ..c } + } + } + } +} +``` + +### Comparison and Type Casting + +#### `fn key_eq(self, T) -> bool` | `fn key_neq(self, T) -> bool` + +For primary key comparisons, require schemas having `int` type primary key. + +```rust +method.key_eq(function) +method.key_neq(function) +``` + +#### `fn to(self) -> T` + +Convert schema instance to another schema instance, duck type check. + +```rust +stmt.to() +``` + +#### `fn is(self) -> bool` + +Judge if this schema instance in universal set of another schema, duck type check. + +```rust +stmt.is() +``` + +Back to [README.md](../../README.md#documents) \ No newline at end of file diff --git a/godel-script/docs/language-reference/type.md b/godel-script/docs/language-reference/type.md new file mode 100644 index 00000000..5f097686 --- /dev/null +++ b/godel-script/docs/language-reference/type.md @@ -0,0 +1,112 @@ +# GödelScript Types + +Back to [README.md](../../README.md#documents) + +GödelScript reserved some symbols for basic types, +other types defined by users could also be used: + +* [Enum](./enums.md) +* [Schema](./schemas.md) +* [Database](./databases.md) + +Some type definitions like this below may also exists: + +```rust +*int +*string +*Annotation +``` + +Type with `*` means it is a __set__ type: + +```rust +*int // int set +*string // string set +*Annotation // schema Annotation set +``` + +## GödelScript Basic Types + +### Bool + +`bool` literal should be `true` or `false`. + +### String + +`string` includes these native methods: + +```rust +fn to_int(self: string) -> int; +fn substr(self: string, begin: int, length: int) -> string; +fn len(self: string) -> int; +fn get_regex_match_result(self: string, str: string, num: int) -> string; +fn matches(self: string, str: string) -> bool; +fn contains(self: string, str: string) -> bool; +fn ne(self: string, str: string) -> string; +fn eq(self: string, str: string) -> string; +fn add(self: string, str: string) -> string; +fn to_set(self) -> *string; +fn to_upper(self) -> string; +fn to_lower(self) -> string; +fn replace_all(self, pattern: string, replacement: string) -> string; +fn replace_once(self, pattern: string, replacement: string, index: int) -> string; +``` + +### Float + +`float` includes these native methods: + +```rust +fn rem(self: float, num: float) -> float; +fn pow(self: float, num: float) -> float; +fn le(self: float, num: float) -> float; +fn gt(self: float, num: float) -> float; +fn ne(self: float, num: float) -> float; +fn ge(self: float, num: float) -> float; +fn eq(self: float, num: float) -> float; +fn div(self: float, num: float) -> float; +fn sub(self: float, num: float) -> float; +fn mul(self: float, num: float) -> float; +fn lt(self: float, num: float) -> float; +fn add(self: float, num: float) -> float; +fn neg(self: float) -> float; +``` + +### Int + +`int` includes these native methods: + +```rust +fn bitxor(self: int, num: int) -> int; +fn bitor(self: int, num: int) -> int; +fn bitand(self: int, num: int) -> int; +fn rem(self: int, num: int) -> int; +fn pow(self: int, num: int) -> int; +fn le(self: int, num: int) -> int; +fn lt(self: int, num: int) -> int; +fn gt(self: int, num: int) -> int; +fn ne(self: int, num: int) -> int; +fn ge(self: int, num: int) -> int; +fn eq(self: int, num: int) -> int; +fn div(self: int, num: int) -> int; +fn mul(self: int, num: int) -> int; +fn sub(self: int, num: int) -> int; +fn add(self: int, num: int) -> int; +fn bitnot(self: int) -> int; +fn neg(self: int) -> int; +fn to_string(self: int) -> string; +fn range(begin: int, end: int) -> *int; +fn to_set(self) -> *int; +``` + +### Aggregator for Set Types + +```rust +fn len(self: *T) -> int; +fn sum(self: *int) -> int; +fn min(self: *int) -> int; +fn max(self: *int) -> int; +fn find(self: *T0, instance: T1) -> T0; +``` + +Back to [README.md](../../README.md#documents) \ No newline at end of file diff --git a/godel-script/docs/syntax.md b/godel-script/docs/syntax.md new file mode 100644 index 00000000..0073ee53 --- /dev/null +++ b/godel-script/docs/syntax.md @@ -0,0 +1,250 @@ +# GödelScript Syntax + +Back to [README.md](../README.md#documents) + +GödelScript Syntax Spec, +Usage Document see [GödelScript Program](./language-reference/program.md)。 + +## Identifier / Literal / Annotation +```ebnf +identifier = (* basic token *); +number_literal = (* basic token *); +string_literal = (* basic token *); +literal = number_literal | string_literal; +``` + +## Annotation +```ebnf +prop_pair = identifier "=" string_literal; +annotation = + "@" identifier ["(" string_literal ")"] | + "@" identifier ["(" [prop_pair [{"," prop_pair}]] ")"]; +``` + +Example: + +```rust +this_is_an_identifier +12345 +"string literal" +``` + +Annotation example: + +```rust +@inline +fn get(a: Method) -> bool { + ... +} +``` + +## Program + +About main program, [Click](./language-reference/program.md)。 + +```ebnf +program = {use_stmt} { + {function_decl} | + {enum_decl} | + {schema_decl} | + {use_decl} | + {impl_decl} | + {database_decl} | + {query_decl} +}; +``` + +## Declaration + +### Function + +Function Usage [Documents](./language-reference/functions.md)。 + +```ebnf +function_decl = + [{annotation}] + "fn" id "(" [params] ")" ["->" type_def] + [ ";" | ("{" block_stmt "}")]; +params = param {"," param}; +param = identifier [":" param_type_def]; +param_type_def = ["*"] identifier; +block_stmt = {statement}; +``` + +### Enum + +Enum Usage [Documents](./language-reference/enums.md)。 + +```ebnf +enum_decl = "enum" identifier "{" [identifier {"," identifier}] "}"; +``` + +### Schema + +Schema Usage [Documents](./language-reference/schemas.md)。 + +```ebnf +schema_decl = + "schema" identifier ["extends" type_def] "{" [schema_members] "}"; +schema_members = schema_member {"," schema_member}; +schema_member = [anno] id ":" type_def; +``` + +### Database + +Database Usage [Documents](./language-reference/databases.md)。 + +```ebnf +database_decl = "database" identifier "{" [database_tables] "}"; +database_tables = database_table {"," database_table}; +database_table = identifier ":" type_def ["as" string_literal]; +``` + +### Use / Import + +Package Manager / Symbol Import +[Documents](./language-reference/import.md)。 + +```ebnf +use_stmt = "use" identifier {"::" identifier} ["::" ("*"|multi_use)]; +multi_use = "{" identifier {"," identifier} "}"; +``` + +### Implement + +Impl Usage [Documents](./language-reference/impl.md)。 + +```ebnf +impl_decl = "impl" identifier ["for" identifier] "{" {function_decl} "}"; +``` + +### GödelScript Query + +Query Usage [Documents](./language-reference/queries.md)。 + +```ebnf +query_decl = + "query" identifier + "from" from_list + ["where" or_expr] + "select" select_list; +from_list = from_def {"," from_def}; +from_def = identifier "in" or_expr; +select_list = select_column {"," select_column}; +select_column = or_expr ["as" identifier]; +``` + +## Statement + +GödelScript statement +[Documents](./language-reference/functions.md#statement)。 + +```ebnf +statement = + let_stmt | + cond_stmt | + for_stmt | + match_stmt | + fact_stmt | + ret_stmt | + in_block_expr; + +def = identifier [":" type_def]; +type_def = identifier ["::" identifier]; +``` + +### Let Statement +```ebnf +let_stmt = "let" "(" let_def ")" "{" [statement] "}"; +let_def = def "=" expression {"," def "=" expression}; +``` + +### Condition Statement +```ebnf +cond_stmt = if_stmt {elsif_stmt} [else_stmt]; +if_stmt = "if" "(" expression ")" "{" [statement] "}"; +elsif_stmt = "else" "if" "(" expression ")" "{" [statement] "}"; +else_stmt = "else" "{" [statement] "}"; +``` + +### For Statement +```ebnf +for_stmt = "for" "(" for_def ")" "{" [statement] "}"; +for_def = def "in" expression {"," def "in" expression}; +``` + +### Match Statement +```ebnf +match_stmt = "match" "(" expression ")" "{" [match_pairs] "}"; +match_pairs = match_pair {"," match_pair}; +match_pair = literal "=>" statement; +``` + +### Fact Statement +```ebnf +fact_stmt = "[" fact_data {"," fact_data} "]"; +fact_data = "{" + (number_literal | string_literal) + {"," (number_literal | string_literal)} +"}"; +``` + +### Return Statement +```ebnf +ret_stmt = ("return" | "yield") or_expr; +``` + +### In Block Expression (as Statement) +```ebnf +in_block_expr = expression; +``` + +## Expression + +GödelScript Expression [Documents](./language-reference/functions.md#expression)。 + +```ebnf +expression = or_expr; +``` + +### Calculation +```ebnf +or_expr = and_expr {"||" and_expr}; +and_expr = (not_expr | cmp_expr) {"&&" (not_expr | cmp_expr)}; +curved_expr = "(" or_expr ")"; +unary_expr = "-" (symcall | curved_calc_expr | unary_expr); +not_expr = "!" cmp_expr; +cmp_expr = + additive_expr + [("=" | "!=" | "<" | "<=" | ">" | ">=" | "in") additive_expr]; +additive_expr = multiple_expr {("+" | "-") multiple_expr}; +multiple_expr = + (symcall | curved_expr | unary_expr) + {("*" | "/") (symcall | curved_expr | unary_expr)}; +``` + +### Call Expression +```ebnf +symcall = symhead {sympath | symget}; +symhead = + identifier [initializer|funcall] + | literal + | curved_expr + ; +sympath = "::" identifier [funcall]; +symget = "." identifier [funcall]; + +funcall = "(" [arglist] ")"; +arglist = or_expr {"," or_expr}; +``` + +### Initializer + +Usage: [Initializer List](./language-reference/functions.md#initializer-list) + +```ebnf +initializer = "{" [initializer_pair ","] "}"; +initializer_pair = identifier ":" or_expr; +``` + +Back to [README.md](../README.md#documents) diff --git a/godel-script/godel-backend/.gitignore b/godel-script/godel-backend/.gitignore new file mode 100644 index 00000000..e342e976 --- /dev/null +++ b/godel-script/godel-backend/.gitignore @@ -0,0 +1,2 @@ +# build directory +build \ No newline at end of file diff --git a/godel-script/godel-backend/0001-init-self-used-souffle-from-public-souffle.patch b/godel-script/godel-backend/0001-init-self-used-souffle-from-public-souffle.patch new file mode 100644 index 00000000..0ca2b88a --- /dev/null +++ b/godel-script/godel-backend/0001-init-self-used-souffle-from-public-souffle.patch @@ -0,0 +1,32680 @@ +From 9cd9cafbc050f6a2ce04a2aaf7ed3267f32cc2db Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=E6=81=95=E5=B1=B1?= +Date: Mon, 24 Jun 2024 16:55:26 +0800 +Subject: [PATCH] init self-used souffle from public souffle + +--- + .github/actions/cmake-test/action.yml | 2 +- + .github/actions/set-test-ids/action.yml | 2 +- + .github/images/arch-linux/entrypoint.sh | 2 +- + .github/scripts/updatePPA.sh | 8 +- + .github/workflows/CI-Tests.yml | 67 +- + .github/workflows/VS-CI-Tests.yml | 20 +- + .github/workflows/create-packages.yml | 28 +- + CMakeLists.txt | 29 +- + README.md | 6 +- + choco-packages.config | 5 +- + cmake/SouffleTests.cmake | 54 +- + cmake/common.py | 1 - + cmake/redirect.py | 4 - + debian/changelog | 33 - + debian/souffle.bash-completion | 2 +- + sh/checkStyle.sh | 4 +- + sh/check_os.sh | 2 +- + src/CMakeLists.txt | 104 +- + src/DynamicLibrary.cpp | 72 + + src/ExtraConfig.h | 24 + + src/FunctorOps.cpp | 4 - + src/FunctorOps.h | 5 - + src/Global.cpp | 6 +- + src/Global.h | 11 +- + src/GraphUtils.h | 76 +- + src/LogStatement.h | 31 +- + src/MainEntry.cpp | 5 + + src/RelationTag.h | 2 + + src/TranslationUnitBase.h | 22 +- + src/ast/Aggregator.cpp | 29 +- + src/ast/Aggregator.h | 27 +- + src/ast/AlgebraicDataType.cpp | 7 +- + src/ast/AlgebraicDataType.h | 2 - + src/ast/AliasType.cpp | 6 +- + src/ast/AliasType.h | 2 - + src/ast/Argument.h | 5 - + src/ast/Atom.cpp | 7 +- + src/ast/Atom.h | 2 - + src/ast/Attribute.cpp | 22 +- + src/ast/Attribute.h | 8 - + src/ast/BinaryConstraint.cpp | 7 +- + src/ast/BinaryConstraint.h | 2 - + src/ast/BooleanConstraint.cpp | 6 +- + src/ast/BooleanConstraint.h | 2 - + src/ast/BranchInit.cpp | 5 +- + src/ast/BranchInit.h | 2 - + src/ast/BranchType.cpp | 11 +- + src/ast/BranchType.h | 4 - + src/ast/Clause.cpp | 27 +- + src/ast/Clause.h | 8 - + src/ast/Component.cpp | 19 - + src/ast/Component.h | 40 +- + src/ast/ComponentInit.cpp | 9 +- + src/ast/ComponentInit.h | 2 - + src/ast/ComponentType.cpp | 6 +- + src/ast/ComponentType.h | 2 - + src/ast/Constant.cpp | 11 +- + src/ast/Constant.h | 4 +- + src/ast/Constraint.h | 9 - + src/ast/Counter.cpp | 10 - + src/ast/Counter.h | 6 - + src/ast/Directive.cpp | 7 +- + src/ast/Directive.h | 2 - + src/ast/ExecutionOrder.cpp | 6 +- + src/ast/ExecutionOrder.h | 2 - + src/ast/ExecutionPlan.cpp | 7 - + src/ast/ExecutionPlan.h | 4 - + src/ast/FunctionalConstraint.cpp | 9 +- + src/ast/FunctionalConstraint.h | 2 - + src/ast/Functor.h | 6 - + src/ast/FunctorDeclaration.cpp | 13 +- + src/ast/FunctorDeclaration.h | 2 - + src/ast/IntrinsicFunctor.cpp | 6 +- + src/ast/IntrinsicFunctor.h | 7 +- + src/ast/Literal.h | 9 - + src/ast/Negation.cpp | 7 +- + src/ast/Negation.h | 2 - + src/ast/NilConstant.cpp | 6 +- + src/ast/NilConstant.h | 2 - + src/ast/Node.cpp | 194 +- + src/ast/Node.h | 191 +- + src/ast/NumericConstant.cpp | 11 +- + src/ast/NumericConstant.h | 2 - + src/ast/Pragma.cpp | 6 +- + src/ast/Pragma.h | 2 - + src/ast/Program.cpp | 52 +- + src/ast/Program.h | 61 +- + src/ast/QualifiedName.cpp | 156 +- + src/ast/QualifiedName.h | 136 +- + src/ast/RecordInit.cpp | 7 +- + src/ast/RecordInit.h | 2 - + src/ast/RecordType.cpp | 7 +- + src/ast/RecordType.h | 2 - + src/ast/Relation.cpp | 20 +- + src/ast/Relation.h | 50 +- + src/ast/StringConstant.cpp | 6 +- + src/ast/StringConstant.h | 3 - + src/ast/SubsetType.cpp | 7 +- + src/ast/SubsetType.h | 2 - + src/ast/SubsumptiveClause.cpp | 12 +- + src/ast/SubsumptiveClause.h | 2 - + src/ast/Term.cpp | 9 +- + src/ast/Term.h | 15 +- + src/ast/TranslationUnit.cpp | 8 +- + src/ast/TranslationUnit.h | 3 +- + src/ast/Type.cpp | 10 +- + src/ast/Type.h | 4 +- + src/ast/TypeCast.cpp | 7 +- + src/ast/TypeCast.h | 2 - + src/ast/UnionType.cpp | 7 +- + src/ast/UnionType.h | 2 - + src/ast/UnnamedVariable.cpp | 10 - + src/ast/UnnamedVariable.h | 6 - + src/ast/UserDefinedFunctor.cpp | 9 +- + src/ast/UserDefinedFunctor.h | 2 - + src/ast/Variable.cpp | 8 +- + src/ast/Variable.h | 2 - + src/ast/analysis/Aggregate.cpp | 12 +- + src/ast/analysis/ClauseNormalisation.cpp | 11 +- + src/ast/analysis/ComponentLookup.cpp | 6 +- + src/ast/analysis/ComponentLookup.h | 2 +- + src/ast/analysis/Constraint.h | 37 +- + src/ast/analysis/ConstraintSystem.h | 50 +- + src/ast/analysis/Functor.cpp | 10 - + src/ast/analysis/Functor.h | 3 - + src/ast/analysis/Ground.cpp | 35 +- + src/ast/analysis/Ground.h | 3 +- + src/ast/analysis/IOType.h | 8 +- + src/ast/analysis/PrecedenceGraph.cpp | 8 - + src/ast/analysis/PrecedenceGraph.h | 12 +- + src/ast/analysis/ProfileUse.cpp | 15 +- + src/ast/analysis/ProfileUse.h | 4 +- + src/ast/analysis/RecursiveClauses.cpp | 99 +- + src/ast/analysis/RecursiveClauses.h | 3 + + src/ast/analysis/RedundantRelations.cpp | 4 +- + src/ast/analysis/RedundantRelations.h | 4 +- + src/ast/analysis/RelationSchedule.cpp | 50 +- + src/ast/analysis/RelationSchedule.h | 18 +- + src/ast/analysis/SCCGraph.cpp | 3 +- + src/ast/analysis/SCCGraph.h | 39 +- + .../analysis/TopologicallySortedSCCGraph.cpp | 140 +- + .../analysis/TopologicallySortedSCCGraph.h | 29 +- + src/ast/analysis/UniqueKeys.cpp | 581 ++++++ + src/ast/analysis/UniqueKeys.h | 79 + + .../typesystem/PolymorphicObjects.cpp | 2 +- + .../analysis/typesystem/PolymorphicObjects.h | 4 +- + src/ast/analysis/typesystem/SumTypeBranches.h | 2 +- + src/ast/analysis/typesystem/Type.cpp | 82 +- + src/ast/analysis/typesystem/Type.h | 31 +- + .../typesystem/TypeConstrainsAnalysis.cpp | 24 +- + .../typesystem/TypeConstrainsAnalysis.h | 4 +- + .../analysis/typesystem/TypeConstraints.cpp | 8 - + src/ast/analysis/typesystem/TypeConstraints.h | 9 - + .../analysis/typesystem/TypeEnvironment.cpp | 26 +- + src/ast/analysis/typesystem/TypeEnvironment.h | 8 +- + src/ast/analysis/typesystem/TypeSystem.cpp | 18 +- + src/ast/analysis/typesystem/TypeSystem.h | 107 +- + src/ast/tests/CMakeLists.txt | 1 - + src/ast/tests/ast_print_test.cpp | 35 +- + src/ast/tests/ast_program_test.cpp | 54 +- + src/ast/tests/ast_transformers_test.cpp | 98 +- + src/ast/tests/ast_utils_test.cpp | 44 +- + src/ast/tests/type_system_test.cpp | 80 +- + .../AddNullariesToAtomlessAggregates.cpp | 5 +- + src/ast/transform/ComponentChecker.cpp | 9 +- + src/ast/transform/ComponentInstantiation.cpp | 66 +- + src/ast/transform/DebugReporter.cpp | 13 +- + src/ast/transform/ExecutionPlanChecker.cpp | 21 +- + src/ast/transform/IOAttributes.h | 4 +- + src/ast/transform/IODefaults.h | 15 +- + src/ast/transform/InlineRelations.cpp | 59 +- + src/ast/transform/InlineRelations.h | 4 +- + src/ast/transform/MagicSet.cpp | 80 +- + src/ast/transform/MagicSet.h | 25 +- + .../MaterializeAggregationQueries.cpp | 74 +- + .../MaterializeSingletonAggregation.cpp | 4 +- + src/ast/transform/Meta.cpp | 10 +- + src/ast/transform/MinimiseProgram.cpp | 2 +- + src/ast/transform/PartitionBodyLiterals.cpp | 4 +- + src/ast/transform/PragmaChecker.cpp | 11 +- + src/ast/transform/PragmaChecker.h | 5 +- + src/ast/transform/ReduceExistentials.cpp | 22 +- + src/ast/transform/RemoveEmptyRelations.cpp | 5 +- + src/ast/transform/RemoveRedundantSums.cpp | 6 +- + src/ast/transform/RemoveRelationCopies.cpp | 6 +- + src/ast/transform/ResolveAliases.cpp | 31 +- + src/ast/transform/SemanticChecker.cpp | 325 +-- + .../SimplifyAggregateTargetExpression.cpp | 8 +- + src/ast/transform/TypeChecker.cpp | 39 +- + src/ast/utility/BindingStore.cpp | 1 + + src/ast/utility/SipsMetric.cpp | 797 ++++++++ + src/ast/utility/SipsMetric.h | 221 ++ + src/ast/utility/Utils.cpp | 6 +- + src/ast/utility/Utils.h | 6 +- + src/ast/utility/Visitor.h | 12 +- + src/ast2ram/ClauseTranslator.h | 7 +- + src/ast2ram/provenance/ClauseTranslator.cpp | 2 +- + src/ast2ram/provenance/UnitTranslator.cpp | 38 +- + src/ast2ram/provenance/UnitTranslator.h | 4 +- + src/ast2ram/seminaive/ClauseTranslator.cpp | 196 +- + src/ast2ram/seminaive/ClauseTranslator.h | 4 +- + src/ast2ram/seminaive/UnitTranslator.cpp | 404 +--- + src/ast2ram/seminaive/UnitTranslator.h | 39 +- + src/ast2ram/seminaive/ValueTranslator.cpp | 6 - + src/ast2ram/seminaive/ValueTranslator.h | 2 - + src/ast2ram/utility/TranslatorContext.cpp | 119 +- + src/ast2ram/utility/TranslatorContext.h | 55 +- + src/ast2ram/utility/Utils.cpp | 67 +- + src/ast2ram/utility/Utils.h | 10 +- + src/include/souffle/CompiledOptions.h | 14 +- + src/include/souffle/CompiledSouffle.h | 332 ++- + src/include/souffle/RamTypes.h | 3 - + src/include/souffle/RecordTable.h | 6 - + src/include/souffle/SignalHandler.h | 24 +- + src/include/souffle/SouffleInterface.h | 2 - + src/include/souffle/datastructure/BTree.h | 34 +- + .../souffle/datastructure/BTreeDelete.h | 49 +- + src/include/souffle/datastructure/BTreeUtil.h | 6 +- + .../datastructure/ConcurrentFlyweight.h | 13 +- + .../ConcurrentInsertOnlyHashMap.h | 28 +- + .../datastructure/EquivalenceRelation.h | 16 +- + src/include/souffle/datastructure/PiggyList.h | 4 - + .../souffle/datastructure/RecordTableImpl.h | 36 - + src/include/souffle/io/IOSystem.h | 1 + + src/include/souffle/io/ReadStream.h | 6 +- + src/include/souffle/io/ReadStreamCSV.h | 58 +- + src/include/souffle/io/ReadStreamSQLite.h | 26 +- + src/include/souffle/io/SerialisationStream.h | 2 +- + src/include/souffle/io/WriteStream.h | 2 +- + src/include/souffle/io/WriteStreamSQLite.h | 30 +- + src/include/souffle/profile/Cell.h | 16 +- + src/include/souffle/profile/CellInterface.h | 2 +- + src/include/souffle/profile/Cli.h | 17 +- + src/include/souffle/profile/EventProcessor.h | 32 +- + src/include/souffle/profile/OutputProcessor.h | 37 +- + src/include/souffle/profile/ProfileEvent.h | 8 +- + src/include/souffle/profile/ProgramRun.h | 4 +- + src/include/souffle/profile/Reader.h | 34 +- + src/include/souffle/profile/Relation.h | 18 +- + src/include/souffle/profile/Tui.h | 99 +- + src/include/souffle/profile/UserInputReader.h | 12 - + src/include/souffle/profile/htmlJsMain.h | 36 +- + src/include/souffle/utility/ContainerUtil.h | 8 +- + src/include/souffle/utility/DynamicCasting.h | 128 +- + src/include/souffle/utility/EvaluatorUtil.h | 4 +- + src/include/souffle/utility/FileUtil.h | 14 +- + src/include/souffle/utility/FunctionalUtil.h | 6 +- + src/include/souffle/utility/MiscUtil.h | 48 +- + src/include/souffle/utility/StreamUtil.h | 14 - + src/include/souffle/utility/StringUtil.h | 23 - + src/include/souffle/utility/SubProcess.h | 18 +- + src/include/souffle/utility/Types.h | 15 - + src/include/souffle/utility/Visitor.h | 7 +- + src/include/souffle/utility/json11.h | 12 +- + src/interpreter/BTreeDeleteIndex.cpp | 18 +- + src/interpreter/BTreeIndex.cpp | 19 +- + src/interpreter/Context.h | 9 - + src/interpreter/Engine.cpp | 564 +++--- + src/interpreter/Engine.h | 45 +- + src/interpreter/EqrelIndex.cpp | 3 +- + src/interpreter/Generator.cpp | 144 +- + src/interpreter/Generator.h | 14 +- + src/interpreter/Index.h | 32 +- + src/interpreter/Node.h | 122 +- + src/interpreter/ProvenanceIndex.cpp | 19 +- + src/interpreter/Relation.h | 43 +- + src/interpreter/Util.h | 283 +-- + .../tests/interpreter_relation_test.cpp | 18 +- + src/interpreter/tests/ram_arithmetic_test.cpp | 9 +- + src/interpreter/tests/ram_relation_test.cpp | 63 +- + src/main.cpp | 985 +++++++++ + src/parser/Helper.h | 163 ++ + src/parser/ParserDriver.cpp | 232 +-- + src/parser/ParserDriver.h | 54 +- + src/parser/ParserUtils.cpp | 154 +- + src/parser/ParserUtils.h | 80 +- + src/parser/SrcLocation.cpp | 47 +- + src/parser/SrcLocation.h | 74 +- + src/parser/parser.yy | 807 ++------ + src/parser/scanner.ll | 238 +-- + src/ram/AbstractAggregate.h | 39 +- + src/ram/AbstractConditional.h | 17 +- + src/ram/AbstractExistenceCheck.h | 16 +- + src/ram/AbstractOperator.h | 19 +- + src/ram/Aggregate.h | 25 +- + src/ram/AutoIncrement.h | 6 - + src/ram/BinRelationStatement.h | 13 +- + src/ram/Break.h | 6 +- + src/ram/Call.h | 6 +- + src/ram/Clear.h | 6 +- + src/ram/Condition.h | 12 - + src/ram/Conjunction.h | 7 +- + src/ram/Constraint.h | 6 +- + src/ram/CountUniqueKeys.h | 107 + + src/ram/DebugInfo.h | 7 +- + src/ram/EmptinessCheck.h | 6 +- + src/ram/Erase.h | 8 +- + src/ram/ExistenceCheck.h | 7 +- + src/ram/Exit.h | 6 +- + src/ram/Expression.h | 13 - + src/ram/False.h | 6 - + src/ram/Filter.h | 6 +- + src/ram/FloatConstant.h | 6 +- + src/ram/GuardedInsert.h | 6 +- + src/ram/IO.h | 6 +- + src/ram/IfExists.h | 17 +- + src/ram/IndexAggregate.h | 23 +- + src/ram/IndexIfExists.h | 25 +- + src/ram/IndexOperation.h | 27 +- + src/ram/IndexScan.h | 20 +- + src/ram/Insert.h | 17 +- + src/ram/IntrinsicOperator.h | 9 +- + src/ram/ListStatement.h | 21 +- + src/ram/LogRelationTimer.h | 7 +- + src/ram/LogSize.h | 7 +- + src/ram/LogTimer.h | 7 +- + src/ram/Loop.h | 6 +- + src/ram/MergeExtend.h | 7 +- + src/ram/Negation.h | 6 +- + src/ram/NestedIntrinsicOperator.h | 11 +- + src/ram/NestedOperation.h | 16 +- + src/ram/Node.cpp | 6 +- + src/ram/Node.h | 155 +- + src/ram/NumericConstant.h | 9 +- + src/ram/Operation.h | 9 - + src/ram/PackRecord.h | 10 +- + src/ram/Parallel.h | 10 +- + src/ram/ParallelAggregate.h | 13 +- + src/ram/ParallelIfExists.h | 6 +- + src/ram/ParallelIndexAggregate.h | 12 +- + src/ram/ParallelIndexIfExists.h | 8 +- + src/ram/ParallelIndexScan.h | 7 +- + src/ram/ParallelScan.h | 6 +- + src/ram/Program.h | 9 +- + src/ram/ProvenanceExistenceCheck.h | 6 +- + src/ram/Query.h | 6 +- + src/ram/Relation.h | 26 +- + src/ram/RelationOperation.h | 11 +- + src/ram/RelationSize.h | 6 +- + src/ram/RelationStatement.h | 11 +- + src/ram/Scan.h | 15 +- + src/ram/Sequence.h | 11 +- + src/ram/SignedConstant.h | 6 +- + src/ram/Statement.h | 9 - + src/ram/StringConstant.h | 8 +- + src/ram/SubroutineArgument.h | 6 +- + src/ram/SubroutineReturn.h | 10 +- + src/ram/Swap.h | 6 +- + src/ram/TranslationUnit.cpp | 3 +- + src/ram/True.h | 6 - + src/ram/TupleElement.h | 7 +- + src/ram/TupleOperation.h | 12 +- + src/ram/UndefValue.h | 6 - + src/ram/UnpackRecord.h | 7 +- + src/ram/UnsignedConstant.h | 6 +- + src/ram/UserDefinedOperator.h | 18 +- + src/ram/analysis/Complexity.cpp | 16 +- + src/ram/analysis/Index.cpp | 12 +- + src/ram/analysis/Index.h | 8 +- + src/ram/analysis/Level.cpp | 5 - + src/ram/transform/MakeIndex.cpp | 50 +- + src/ram/transform/MakeIndex.h | 13 +- + src/ram/transform/Parallel.cpp | 20 +- + src/ram/transform/ReorderConditions.cpp | 7 +- + src/ram/transform/Transformer.cpp | 4 +- + src/ram/utility/Visitor.h | 14 +- + src/reports/DebugReport.cpp | 270 +-- + src/reports/DebugReport.h | 22 +- + src/reports/ErrorReport.h | 69 +- + src/souffle-compile.template.py | 30 +- + src/souffle_prof.cpp | 4 +- + src/synthesiser/Relation.cpp | 1053 +++++----- + src/synthesiser/Relation.h | 26 +- + src/synthesiser/Synthesiser.cpp | 1780 ++++++++--------- + src/synthesiser/Synthesiser.h | 47 +- + src/tests/graph_utils_test.cpp | 1 - + src/tests/record_table_test.cpp | 21 - + src/tests/visitor_test.cpp | 12 +- + tests/CMakeLists.txt | 2 - + tests/evaluation/CMakeLists.txt | 14 +- + tests/example/CMakeLists.txt | 2 +- + tests/interface/CMakeLists.txt | 9 - + tests/interface/functors/CMakeLists.txt | 6 - + tests/interface/functors/functors.dl | 9 +- + tests/interface/insert_print/driver.cpp | 2 +- + tests/profile/CMakeLists.txt | 2 - + tests/provenance/CMakeLists.txt | 9 +- + tests/scheduler/CMakeLists.txt | 3 +- + tests/semantic/CMakeLists.txt | 48 +- + tests/semantic/agg_checks/agg_checks.err | 17 +- + .../bad_functor_types/bad_functor_types.err | 8 +- + .../comp_params_inheritance.err | 6 +- + tests/semantic/comp_types/comp_types.err | 38 +- + .../semantic/execution_plan/execution_plan.dl | 25 - + .../execution_plan/execution_plan.err | 17 +- + tests/semantic/fact_plus/fact_plus.err | 3 + + .../semantic/fact_variable/fact_variable.err | 3 + + tests/semantic/leq/leq.dl | 2 +- + tests/semantic/load10/load10.err | 8 + + tests/semantic/load10/oFloat.csv | 2 + + tests/semantic/load10/oSigned.csv | 3 + + tests/semantic/load3/load3.out | 1 + + tests/semantic/load4/load4.out | 1 + + tests/semantic/plan1/plan1.err | 2 +- + .../rule_typecompat/rule_typecompat.err | 99 +- + tests/semantic/strconv/A.csv | 8 +- + tests/semantic/strconv/strconv.dl | 6 - + tests/semantic/subsumption/subsumption.err | 2 +- + .../subsumption_multiple_rules.dl | 2 + + tests/semantic/union_types/union_types.err | 23 +- + tests/semantic/var_single/var_single.err | 3 + + tests/syntactic/CMakeLists.txt | 82 +- + .../dot_identifiers/dot_identifiers.dl | 2 - + tests/syntactic/qualifiers4/qualifiers4.err | 2 +- + tests/syntactic/syntax1/syntax1.err | 2 +- + tests/syntactic/syntax10/syntax10.err | 2 +- + tests/syntactic/syntax5/syntax5.err | 2 +- + tests/syntactic/syntax6/syntax6.err | 2 +- + tests/syntactic/syntax8/syntax8.err | 2 +- + 419 files changed, 7762 insertions(+), 9621 deletions(-) + create mode 100644 src/DynamicLibrary.cpp + create mode 100644 src/ExtraConfig.h + create mode 100644 src/MainEntry.cpp + create mode 100644 src/ast/analysis/UniqueKeys.cpp + create mode 100644 src/ast/analysis/UniqueKeys.h + create mode 100644 src/ast/utility/SipsMetric.cpp + create mode 100644 src/ast/utility/SipsMetric.h + create mode 100644 src/main.cpp + create mode 100644 src/parser/Helper.h + create mode 100644 src/ram/CountUniqueKeys.h + +diff --git a/.github/actions/cmake-test/action.yml b/.github/actions/cmake-test/action.yml +index 90ddfab..460a1ab 100644 +--- a/.github/actions/cmake-test/action.yml ++++ b/.github/actions/cmake-test/action.yml +@@ -14,7 +14,7 @@ runs: + using: "composite" + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + + - name: setup-env + run: | +diff --git a/.github/actions/set-test-ids/action.yml b/.github/actions/set-test-ids/action.yml +index 2cc54e6..d87f49f 100644 +--- a/.github/actions/set-test-ids/action.yml ++++ b/.github/actions/set-test-ids/action.yml +@@ -13,5 +13,5 @@ runs: + - id: set-test-ids + run: | + CHUNKS=$(python3 -c "print(list(range(${{ inputs.n-chunks }})))") +- echo "chunks=${CHUNKS}" >> $GITHUB_OUTPUT ++ echo "::set-output name=chunks::${CHUNKS}" + shell: bash +diff --git a/.github/images/arch-linux/entrypoint.sh b/.github/images/arch-linux/entrypoint.sh +index 7aa8d7a..751c90b 100755 +--- a/.github/images/arch-linux/entrypoint.sh ++++ b/.github/images/arch-linux/entrypoint.sh +@@ -3,6 +3,6 @@ + set -e + set -x + +-envsubst '${RELEASE_TAG},${REPO_OWNER}' < PKGBUILD.in > PKGBUILD ++envsubst '${RELEASE_TAG}' < PKGBUILD.in > PKGBUILD + makepkg + makepkg --printsrcinfo > .SRCINFO +diff --git a/.github/scripts/updatePPA.sh b/.github/scripts/updatePPA.sh +index 5eeb9f8..d4588f3 100755 +--- a/.github/scripts/updatePPA.sh ++++ b/.github/scripts/updatePPA.sh +@@ -46,17 +46,17 @@ sudo apt-get install createrepo rpm + echo "%_gpg_name Bot\n%__gpg_sign_cmd %{__gpg} gpg --force-v3-sigs --batch --verbose --no-armor --no-secmem-warning -u \"%{_gpg_name}\" -sbo %{__signature_filename} --digest-algo sha256 %{__plaintext_filename}'" > ~/.rpmmacros + + ## Fedora +-mkdir -p $TMPDIR/ppa/fedora/39/x86_64 ++mkdir -p $TMPDIR/ppa/fedora/34/x86_64 + cd $TMPDIR/ppa/fedora + +-for i in $DEBPATH/*fedora-39*/*rpm ++for i in $DEBPATH/*fedora-34*/*rpm + do + rpm --addsign $i + done + +-cp $DEBPATH/*fedora-39*/*rpm 39/x86_64/ ++cp $DEBPATH/*fedora-34*/*rpm 34/x86_64/ + +-createrepo 39/x86_64 ++createrepo 34/x86_64 + + git add . + git commit -m "Added fedora rpm files for $SOUFFLE_TAG" +diff --git a/.github/workflows/CI-Tests.yml b/.github/workflows/CI-Tests.yml +index f8b865f..0eda992 100644 +--- a/.github/workflows/CI-Tests.yml ++++ b/.github/workflows/CI-Tests.yml +@@ -3,9 +3,6 @@ on: + pull_request: + types: [opened, synchronize] + workflow_dispatch: +- push: +- branches: +- - 'master' + + jobs: + Code-Style: +@@ -13,7 +10,7 @@ jobs: + + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + with: + fetch-depth: 2 + +@@ -31,7 +28,7 @@ jobs: + chunks: ${{ steps.set-test-ids.outputs.chunks }} + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + - id: set-test-ids + uses: ./.github/actions/set-test-ids + with: +@@ -54,7 +51,7 @@ jobs: + + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + + - name: install-lcov + if: ${{ matrix.domain == '32bit' }} +@@ -97,7 +94,7 @@ jobs: + + - name: upload-coverage-artifact + if: ${{ matrix.domain == '32bit' }} +- uses: actions/upload-artifact@v4 ++ uses: actions/upload-artifact@v2 + with: + name: coverage-${{ matrix.domain }}-${{ matrix.chunk }} + path: coverage.info +@@ -113,17 +110,11 @@ jobs: + matrix: + chunk: ${{ fromJSON(needs.Test-Setup.outputs.chunks) }} + +- runs-on: macos-12 ++ runs-on: macos-latest + + steps: +- - name: Select XCode version +- uses: maxim-lobanov/setup-xcode@v1 +- with: +- # Pending https://github.com/actions/runner-images/issues/6350 +- xcode-version: '13.4' +- + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + + - name: install-deps + run: sh/setup/install_macos_deps.sh +@@ -135,39 +126,6 @@ jobs: + n-chunks: ${{ needs.Test-Setup.outputs.n-chunks }} + chunk: ${{ matrix.chunk }} + +- AppleM-CMake: +- needs: Test-Setup +- timeout-minutes: 150 +- +- name: AppleM-CMake (chunk ${{ matrix.chunk }}) +- +- strategy: +- fail-fast: false +- matrix: +- chunk: ${{ fromJSON(needs.Test-Setup.outputs.chunks) }} +- +- runs-on: macos-14 +- +- steps: +- - name: Select XCode version +- uses: maxim-lobanov/setup-xcode@v1 +- with: +- xcode-version: '15.2' +- +- - name: checkout +- uses: actions/checkout@v4 +- +- - name: install-deps +- run: sh/setup/install_macos_arm_deps.sh +- +- - name: cmake-test-64bit +- uses: ./.github/actions/cmake-test +- with: +- # disable openmp on ARM architecture, see souffle-lang/souffle#2476 +- cmake-flags: -DSOUFFLE_DOMAIN_64BIT=ON -DSOUFFLE_USE_OPENMP=OFF +- n-chunks: ${{ needs.Test-Setup.outputs.n-chunks }} +- chunk: ${{ matrix.chunk }} +- + Memory-Check: + needs: Test-Setup + timeout-minutes: 150 +@@ -183,16 +141,11 @@ jobs: + + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + + - name: install-deps + run: sudo sh/setup/install_ubuntu_deps.sh + +- - name: fix mmap_rnd_bits +- # issue with ubuntu:latest runner and ASAN +- # https://github.com/actions/runner-images/issues/9491 +- run: sudo sysctl vm.mmap_rnd_bits=28 +- + - name: cmake-test-32bit + uses: ./.github/actions/cmake-test + with: +@@ -208,7 +161,7 @@ jobs: + + steps: + - name: checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + with: + fetch-depth: 0 + +@@ -216,13 +169,13 @@ jobs: + run: sudo apt-get update && sudo apt-get install lcov + + - name: download-coverage-artifacts +- uses: actions/download-artifact@v4 ++ uses: actions/download-artifact@v2 + + - name: merge-coverage-report + run: lcov $(for i in coverage-*-*/coverage.info; do echo -a $i; done) --output-file coverage.info + + - name: upload-coverage-report +- uses: codecov/codecov-action@v4 ++ uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.info +diff --git a/.github/workflows/VS-CI-Tests.yml b/.github/workflows/VS-CI-Tests.yml +index 3955a6f..3ba0030 100644 +--- a/.github/workflows/VS-CI-Tests.yml ++++ b/.github/workflows/VS-CI-Tests.yml +@@ -4,9 +4,6 @@ on: + pull_request: + types: [opened, synchronize] + workflow_dispatch: +- push: +- branches: +- - 'master' + + env: + CHOCO_CACHE_DIR: "${{ github.workspace }}/choco-cache" +@@ -16,10 +13,10 @@ jobs: + Windows-CMake-MSVC: + runs-on: windows-2019 + steps: +- - uses: actions/checkout@v4 ++ - uses: actions/checkout@v2 + + - name: Dependencies Cache +- uses: actions/cache@v3 ++ uses: actions/cache@v2 + env: + cache-name: cache-chocolatey + with: +@@ -32,18 +29,19 @@ jobs: + - 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' ++ 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@v11 ++ uses: lukka/run-vcpkg@v7 + with: +- vcpkgGitCommitId: '56954f1db97f38635782d5ad7cdfd45d2731c854' ++ vcpkgGitCommitId: 'af2287382b1991dbdcb7e5112d236f3323b9dd7a' ++ vcpkgTriplet: x64-windows ++ vcpkgArguments: 'sqlite3 zlib libffi' + + - name: Create Build Directory + working-directory: ${{github.workspace}} +- run: | +- mkdir build ++ run: mkdir build + + - name: Configure Build + working-directory: ${{github.workspace}} +@@ -51,7 +49,7 @@ jobs: + $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 -DSOUFFLE_USE_SQLITE=ON -DCMAKE_FIND_DEBUG_MODE=FALSE -DSOUFFLE_BASH_COMPLETION=OFF ++ 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}} +diff --git a/.github/workflows/create-packages.yml b/.github/workflows/create-packages.yml +index 35531db..46f76c4 100644 +--- a/.github/workflows/create-packages.yml ++++ b/.github/workflows/create-packages.yml +@@ -15,14 +15,18 @@ jobs: + - release: oraclelinux-8 + extension: ".rpm" + OS-name: "el/8" +- - release: fedora-39 ++ - release: fedora-34 + extension: ".rpm" +- OS-name: "fedora/39" ++ OS-name: "fedora/34" ++ # build issue on fedora 35 ++ # - release: fedora-35 ++ # extension: ".rpm" ++ # OS-name: "fedora/35" + + runs-on: ubuntu-latest + steps: + - name: Checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + with: + fetch-depth: 0 + +@@ -37,14 +41,14 @@ jobs: + run: |- + docker cp container:/souffle/build/ . && + cd build && +- echo "pkg_name=$(ls *${{ matrix.extension }} | head -n1)" >> $GITHUB_OUTPUT +- echo "artifact_name=x86_64-${{ matrix.release }}-$(ls *${{ matrix.extension }} | head -n1)" >> $GITHUB_OUTPUT ++ echo "::set-output name=pkg_name::$(ls *${{ matrix.extension }} | head -n1)" ++ echo "::set-output name=artifact_name::x86_64-${{ matrix.release }}-$(ls *${{ matrix.extension }} | head -n1)" + + - name: Naming Artifact + run: cp build/${{ steps.extract_pkg.outputs.pkg_name }} build/${{ steps.extract_pkg.outputs.artifact_name }} + + - name: Upload Artifact +- uses: actions/upload-artifact@v3 ++ uses: actions/upload-artifact@v2 + with: + name: ${{ steps.extract_pkg.outputs.artifact_name }} + path: build/${{ steps.extract_pkg.outputs.artifact_name }} +@@ -53,14 +57,14 @@ jobs: + runs-on: ubuntu-latest + steps: + - name: Checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Prepare + id: prepare + run: |- +- echo "release_tag=$(git describe --tags --always | sed "s/-.*$//")" >> $GITHUB_OUTPUT ++ echo "::set-output name=release_tag::$(git describe --tags --always | sed "s/-.*$//")" + + - name: Build Container + run: docker build ./.github/images/arch-linux/ -t package_builder +@@ -105,7 +109,7 @@ jobs: + runs-on: ubuntu-latest + steps: + - name: Download All Artifacts +- uses: actions/download-artifact@v3 ++ uses: actions/download-artifact@v2 + with: + path: ./downloads + +@@ -126,16 +130,16 @@ jobs: + needs: CPack-Package-Build + if: ${{ always() }} + +- runs-on: ubuntu-latest ++ runs-on: ubuntu-18.04 + steps: + - name: Checkout +- uses: actions/checkout@v4 ++ uses: actions/checkout@v2 + with: + fetch-depth: 0 + clean: false + + - name: Download All Artifacts +- uses: actions/download-artifact@v3 ++ uses: actions/download-artifact@v2 + with: + path: ./downloads + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 5ab514d..1204593 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -6,10 +6,6 @@ + + cmake_minimum_required(VERSION 3.15) + +-set(CMAKE_CXX_STANDARD 17) +-set(CMAKE_CXX_STANDARD_REQUIRED ON) +-set(CMAKE_CXX_EXTENSIONS OFF) +- + include(CMakeDependentOption) + + option(SOUFFLE_GIT "Enable/Disable git completion" ON) +@@ -34,7 +30,7 @@ if (SOUFFLE_GIT) + else() + string(REGEX REPLACE "\n$" "" PACKAGE_VERSION "${GIT_PACKAGE_VERSION}") + message(STATUS "Building souffle version ${PACKAGE_VERSION}") +- ++ + # SOUFFLE_VERSION only includes the major/minor triplet + string(REGEX REPLACE "-.*$" "" SOUFFLE_VERSION "${PACKAGE_VERSION}") + +@@ -143,15 +139,6 @@ if (WIN32) + SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${OUTPUT_DIRECTORY}") + endif () + +- +-if(EMSCRIPTEN) +- set(SOUFFLE_USE_SQLITE off) +- set(SOUFFLE_USE_OPENMP off) +- set(SOUFFLE_USE_ZLIB off) +- set(SOUFFLE_USE_LIBFFI off) +- set(SOUFFLE_USE_CURSES off) +- set(SOUFFLE_ENABLE_TESTING off) +-endif () + # -------------------------------------------------- + # curses libraries for Provenance information + # -------------------------------------------------- +@@ -202,7 +189,7 @@ if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") + OUTPUT_VARIABLE BREW_BISON_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +- if (BREW_BISON EQUAL 0 AND EXISTS "${BREW_BISON_PREFIX}") ++ if (NOT DEFINED BISON_EXECUTABLE AND BREW_BISON EQUAL 0 AND EXISTS "${BREW_BISON_PREFIX}") + message(STATUS "Found Bison keg installed by Homebrew at ${BREW_BISON_PREFIX}") + set(BISON_EXECUTABLE "${BREW_BISON_PREFIX}/bin/bison") + endif() +@@ -213,13 +200,13 @@ if (CMAKE_HOST_SYSTEM_NAME MATCHES "Darwin") + OUTPUT_VARIABLE BREW_FLEX_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +- if (BREW_FLEX EQUAL 0 AND EXISTS "${BREW_FLEX_PREFIX}") ++ if (NOT DEFINED FLEX_EXECUTABLE AND BREW_FLEX EQUAL 0 AND EXISTS "${BREW_FLEX_PREFIX}") + message(STATUS "Found Flex keg installed by Homebrew at ${BREW_FLEX_PREFIX}") + set(FLEX_EXECUTABLE "${BREW_FLEX_PREFIX}/bin/flex") + endif() + endif() + find_package(FLEX REQUIRED) +-find_package(BISON "3.2" REQUIRED) ++find_package(BISON "3.0.4" REQUIRED) + + # -------------------------------------------------- + # mcpp +@@ -319,7 +306,7 @@ if (SOUFFLE_GENERATE_DOXYGEN) + + add_custom_target(doxygen + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CFG} +- WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ++ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating API documentation with Doxygen") + endif() + +@@ -391,7 +378,7 @@ IF (SOUFFLE_BASH_COMPLETION) + endif() + + install( +- FILES "${PROJECT_SOURCE_DIR}/debian/souffle.bash-completion" ++ FILES "${CMAKE_CURRENT_SOURCE_DIR}/debian/souffle.bash-completion" + DESTINATION ${BASH_COMPLETION_COMPLETIONSDIR} + RENAME "souffle" + ) +@@ -402,7 +389,7 @@ if (NOT WIN32) + # -------------------------------------------------- + # CPack configuration + # -------------------------------------------------- +-execute_process(COMMAND bash "${PROJECT_SOURCE_DIR}/sh/check_os.sh" ++execute_process(COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/sh/check_os.sh" + RESULT_VARIABLE CHECK_OS_RESULT + OUTPUT_VARIABLE CHECK_OS_OUTPUT) + +@@ -414,7 +401,7 @@ SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A Datalog Compiler") + SET(CPACK_THREADS 0) + + # Make sure changelog, bash-completion and other important files in debian directory also packaged +-SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_SOURCE_DIR}/debian/changelog" "${PROJECT_SOURCE_DIR}/debian/souffle.bash-completion" "${PROJECT_SOURCE_DIR}/debian/copyright") ++SET(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${CMAKE_CURRENT_SOURCE_DIR}/debian/changelog" "${CMAKE_CURRENT_SOURCE_DIR}/debian/souffle.bash-completion" "${CMAKE_CURRENT_SOURCE_DIR}/debian/copyright") + + # -------------------------------------------------- + # CPack configuration +diff --git a/README.md b/README.md +index 05af771..aca3a69 100644 +--- a/README.md ++++ b/README.md +@@ -5,8 +5,8 @@ The Soufflé language is similar to Datalog (but has terms known as records), an + 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?event=push)](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?event=push)](https://github.com/souffle-lang/souffle/actions/workflows/VS-CI-Tests.yml) ++[![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é +@@ -46,7 +46,7 @@ domain-specific language for analysis problems. + + Use git to obtain the source code of Soufflé. + +- $ git clone https://github.com/souffle-lang/souffle.git ++ $ git clone git://github.com/souffle-lang/souffle.git + + Build instructions can be found [here](https://souffle-lang.github.io/build). + +diff --git a/choco-packages.config b/choco-packages.config +index 9c8d432..aebe6bf 100644 +--- a/choco-packages.config ++++ b/choco-packages.config +@@ -1,7 +1,6 @@ + + + +- +- +- ++ ++ + +diff --git a/cmake/SouffleTests.cmake b/cmake/SouffleTests.cmake +index 2162f9b..148e344 100644 +--- a/cmake/SouffleTests.cmake ++++ b/cmake/SouffleTests.cmake +@@ -63,14 +63,6 @@ function(SOUFFLE_RUN_INTEGRATION_TEST) + FIXTURES_SETUP ${PARAM_FIXTURE_NAME}_run_souffle + FIXTURES_REQUIRED ${PARAM_FIXTURE_NAME}_setup) + +- if (WIN32) +- string(REPLACE ";" "\\;" escaped_path "$ENV{PATH}") +- cmake_path(GET CMAKE_CXX_COMPILER PARENT_PATH CL_DIR) +- set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_run_souffle PROPERTIES +- ENVIRONMENT "PATH=${CL_DIR}\\;$>\\;${escaped_path}" +- ) +- endif() +- + 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) +@@ -91,21 +83,12 @@ function(SOUFFLE_COMPARE_STD_OUTPUTS) + COMMAND + ${Python3_EXECUTABLE} "${PROJECT_SOURCE_DIR}/cmake/check_std_outputs.py" + "${PARAM_TEST_NAME}" +- "${PARAM_EXTRA_DATA}" +- ) ++ "${PARAM_EXTRA_DATA}") + + set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_compare_std_outputs PROPERTIES +- WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" +- LABELS "${PARAM_TEST_LABELS}" +- FIXTURES_REQUIRED ${PARAM_RUN_AFTER_FIXTURE} +- ) +- +- if (WIN32) +- string(REPLACE ";" "\\;" escaped_path "$ENV{PATH}") +- set_tests_properties(${PARAM_QUALIFIED_TEST_NAME}_compare_std_outputs PROPERTIES +- ENVIRONMENT "$>\\;${escaped_path}" +- ) +- endif() ++ WORKING_DIRECTORY "${PARAM_OUTPUT_DIR}" ++ LABELS "${PARAM_TEST_LABELS}" ++ FIXTURES_REQUIRED ${PARAM_RUN_AFTER_FIXTURE}) + endfunction() + + function(SOUFFLE_COMPARE_CSV) +@@ -151,8 +134,6 @@ function(SOUFFLE_RUN_TEST_HELPER) + #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 +-#PARAM_NO_PROCESSOR - should the C preprocessor be disabled or not +-#PARAM_INCLUDE_DIRS - list of include directory paths, relative to the test input directory + #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 +@@ -161,9 +142,9 @@ function(SOUFFLE_RUN_TEST_HELPER) + #Usually just "facts" but can be different when running multi - tests + cmake_parse_arguments( + PARAM +- "COMPILED;COMPILED_SPLITTED;FUNCTORS;NEGATIVE;MULTI_TEST;NO_PREPROCESSOR;OUTPUT_STDOUT" # Options ++ "COMPILED;FUNCTORS;NEGATIVE;MULTI_TEST;NO_PREPROCESSOR" # Options + "TEST_NAME;CATEGORY;FACTS_DIR_NAME;EXTRA_DATA" #Single valued options +- "INCLUDE_DIRS" # Multi-valued options ++ "" + ${ARGV} + ) + +@@ -173,10 +154,6 @@ function(SOUFFLE_RUN_TEST_HELPER) + list(APPEND EXTRA_FLAGS "-c") + set(EXEC_STYLE "compiled") + set(SHORT_EXEC_STYLE "_c") +- elseif(PARAM_COMPILED_SPLITTED) +- list(APPEND EXTRA_FLAGS "-C") +- set(EXEC_STYLE "compiled-splitted") +- set(SHORT_EXEC_STYLE "_C") + else() + set(EXEC_STYLE "interpreted") + set(SHORT_EXEC_STYLE "") +@@ -205,13 +182,6 @@ function(SOUFFLE_RUN_TEST_HELPER) + set(INPUT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${PARAM_TEST_NAME}") + set(FACTS_DIR "${INPUT_DIR}/${PARAM_FACTS_DIR_NAME}") + +- if (PARAM_INCLUDE_DIRS) +- ## generate -I include directory options +- list(TRANSFORM PARAM_INCLUDE_DIRS PREPEND "${INPUT_DIR}/") +- list(TRANSFORM PARAM_INCLUDE_DIRS PREPEND "-I") +- list(APPEND EXTRA_FLAGS ${PARAM_INCLUDE_DIRS}) +- endif() +- + if (PARAM_MULTI_TEST) + set(DATA_CHECK_DIR "${INPUT_DIR}/${PARAM_FACTS_DIR_NAME}") + set(MT_EXTRA_SUFFIX "_${PARAM_FACTS_DIR_NAME}") +@@ -243,11 +213,7 @@ function(SOUFFLE_RUN_TEST_HELPER) + FIXTURE_NAME ${FIXTURE_NAME} + TEST_LABELS ${TEST_LABELS}) + +- if(PARAM_OUTPUT_STDOUT) +- set(SOUFFLE_PARAMS "-D-" "-F" "${FACTS_DIR}") +- else() +- set(SOUFFLE_PARAMS "-D" "." "-F" "${FACTS_DIR}") +- endif() ++ set(SOUFFLE_PARAMS "-D" "." "-F" "${FACTS_DIR}") + list(PREPEND SOUFFLE_PARAMS ${EXTRA_FLAGS}) + + if (OPENMP_FOUND) +@@ -348,12 +314,6 @@ endfunction() + function(SOUFFLE_POSITIVE_TEST TEST_NAME CATEGORY) + souffle_run_test(TEST_NAME ${TEST_NAME} + CATEGORY ${CATEGORY}) +- if (${ARGN} MATCHES "COMPILED_SPLITTED") +- souffle_run_test_helper( +- TEST_NAME ${TEST_NAME} +- CATEGORY ${CATEGORY} +- COMPILED_SPLITTED) +- endif() + endfunction() + + # A helper to make it easier to specify the category positionally +diff --git a/cmake/common.py b/cmake/common.py +index 28fe8f1..9e999d0 100644 +--- a/cmake/common.py ++++ b/cmake/common.py +@@ -45,7 +45,6 @@ def compare_files(expected_file, actual_file): + + 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.stdout.write("\n") + os.sys.exit("Found output difference, expected file:'{}', actual file:'{}".format(expected_file, actual_file)) + + return True +diff --git a/cmake/redirect.py b/cmake/redirect.py +index 6e83a1d..d1b703e 100644 +--- a/cmake/redirect.py ++++ b/cmake/redirect.py +@@ -41,8 +41,4 @@ if stdout: + if stderr: + stderr.close() + +-if status.returncode != 0 and args.err_file: +- with open(args.err_file, "r") as f: +- os.sys.stderr.write(f.read()) +- + os.sys.exit(status.returncode) +diff --git a/debian/changelog b/debian/changelog +index 97c45fd..87595ff 100644 +--- a/debian/changelog ++++ b/debian/changelog +@@ -1,36 +1,3 @@ +-souffle (2.4.1) stable; urgency=low +- * Remove escapes in arch build (XiaowenHu96) +- * fix windows CI (quentin) +- * Fix subsumption (julienhenry) +- * remove -Werror from CMakeLists.txt (ammkrn) +- * Provide way to debug delta relations and expose iteration counter (julienhenry) +- * fix "variable only occurs once" checker (quentin) +- * fix bad source location for END token (quentin) +- * Fix generated code for eqrel when emitting statistics (fangyi-zhou) +- * do not consider debug_delta relations as empty (julienhenry) +- * Fix auto-scheduler profile mismatch (fangyi-zhou) +- * fixes souffleprof and enable build on Windows (quentin) +- * Enabling souffle-lang/souffle GitHub dependency graph with vcpk (#2427) (quentin) +- * fix sqlite reading of empty values (dbueno) +- * symbol concatenation with binary `+` (quentin) +- +- -- Quentin Sabah Web, 11 Oct 2023 11:00:00 +0100 +- +-souffle (2.4) stable; urgency=low +- * New option --generate-many to split the generated C++ Code into multiple files (julienhenry) +- * Purge input and intermediate relations (adamjseitz) +- * Emscripten Build (philzook58) +- * Improved parallel load balance in the interpreter (quentin) +- * User defined aggregate (julienhenry) +- * Improved loading of CSV (strRM, bmeck) +- * Configurable warnings (langston-barrett) +- * Performance improvements for regex matching (strRM) +- * Expose main driver interface (quentin) +- * Allow linking against multiple compiled Souffle programs (ruricolist, quentin) +- * Misc fixes and refactoring (adamjseitz, ruricolist, quentin, b-scholz, mingodad, bmeck, strRM, fgauthie, julienhenry, SamArch27, luc-tielen, XiaowenHu96, Gueckmooh) +- +- -- Quentin Sabah Sat, 18 Feb 2023 11:00:00 +0100 +- + souffle (2.3) stable; urgency=low + * Auto-scheduler for rules (SamArch27) + * Better scheduling heuristic (julienhenry) +diff --git a/debian/souffle.bash-completion b/debian/souffle.bash-completion +index 5b6b2f0..a849194 100644 +--- a/debian/souffle.bash-completion ++++ b/debian/souffle.bash-completion +@@ -35,7 +35,7 @@ _souffle() + return + ;; + --disable-transformers) +- COMPREPLY=( $(compgen -W "AstComponentChecker AstExecutionPlanChecker AstPragmaChecker AstSemanticChecker AstUserDefinedFunctorsTransformer ComponentInstantiationTransformer FoldAnonymousRecords GroundedTermsChecker InlineRelationsTransformer MagicSetTransformer MaterializeAggregationQueriesTransformer MaterializeSingletonAggregationTransformer MetaTransformer MinimiseProgramTransformer NameUnnamedVariablesTransformer NormaliseConstraintsTransformer PartitionBodyLiteralsTransformer PolymorphicObjectsTransformer ProvenanceTransformer ReduceExistentialsTransformer RemoveBooleanConstraintsTransformer RemoveEmptyRelationsTransformer RemoveRedundantRelationsTransformer RemoveRedundantSumsTransformer RemoveRelationCopiesTransformer RemoveTypecastsTransformer ReorderLiteralsTransformer ReplaceSingletonVariablesTransformer ResolveAliasesTransformer ResolveAnonymousRecordAliases SimplifyConstantBinaryConstraintsTransformer UniqueAggregationVariablesTransformer" -- "$cur" ) ) ++ COMPREPLY=( $(compgen -W "AstComponentChecker AstExecutionPlanChecker AstPragmaChecker AstSemanticChecker AstUserDefinedFunctorsTransformer ComponentInstantiationTransformer FoldAnonymousRecords GroundedTermsChecker InlineRelationsTransformer MagicSetTransformer MaterializeAggregationQueriesTransformer MaterializeSingletonAggregationTransformer MetaTransformer MinimiseProgramTransformer NameUnnamedVariablesTransformer NormaliseConstraintsTransformer PartitionBodyLiteralsTransformer PolymorphicObjectsTransformer ProvenanceTransformer ReduceExistentialsTransformer RemoveBooleanConstraintsTransformer RemoveEmptyRelationsTransformer RemoveRedundantRelationsTransformer RemoveRedundantSumsTransformer RemoveRelationCopiesTransformer RemoveTypecastsTransformer ReorderLiteralsTransformer ReplaceSingletonVariablesTransformer ResolveAliasesTransformer ResolveAnonymousRecordAliases UniqueAggregationVariablesTransformer" -- "$cur" ) ) + return + ;; + esac +diff --git a/sh/checkStyle.sh b/sh/checkStyle.sh +index 6c46809..c6ca15e 100755 +--- a/sh/checkStyle.sh ++++ b/sh/checkStyle.sh +@@ -16,7 +16,7 @@ cd "$(git rev-parse --show-toplevel)" + + # Find all changed files in the diff + for f in $(git diff --name-only --diff-filter=ACMRTUXB $1); do +- if echo "$f" | grep -E -q "[.](cpp|h)$"; then ++ if echo "$f" | egrep -q "[.](cpp|h)$"; then + $CLANGFORMAT -style=file "$f" -i + d=$(git diff --minimal --color=always --ws-error-highlight=all $f) || true + if [ -n "$d" ]; then +@@ -27,7 +27,7 @@ for f in $(git diff --name-only --diff-filter=ACMRTUXB $1); do + echo + fail=1 + fi +- elif echo "$f" | grep -E -q "[.](dl)$"; then ++ elif echo "$f" | egrep -q "[.](dl)$"; then + sed -i 's/[ \t]*$//' "$f" || true + d=$(git diff --minimal --color=always --ws-error-highlight=all $f) || true + if [ -n "$d" ]; then +diff --git a/sh/check_os.sh b/sh/check_os.sh +index 5b0f8a3..b8eb63a 100755 +--- a/sh/check_os.sh ++++ b/sh/check_os.sh +@@ -11,7 +11,7 @@ fi + ID=$(grep -G "^ID_LIKE=" $file | tr a-z A-Z) + + #Fedora is special and has no ID_LIKE +-if [ -z "$ID" ]; then ++if [ -z $ID ]; then + ID=$(grep -G "^ID=" $file | tr a-z A-Z) + fi + +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt +index c2ce37b..5c07576 100644 +--- a/src/CMakeLists.txt ++++ b/src/CMakeLists.txt +@@ -8,12 +8,7 @@ + set(SOUFFLE_SOURCES + FunctorOps.cpp + Global.cpp +- GraphUtils.cpp +- MainDriver.cpp +- ast/Annotation.cpp + ast/Aggregator.cpp +- ast/IntrinsicAggregator.cpp +- ast/UserDefinedAggregator.cpp + ast/AlgebraicDataType.cpp + ast/AliasType.cpp + ast/Atom.cpp +@@ -35,8 +30,6 @@ set(SOUFFLE_SOURCES + ast/FunctorDeclaration.cpp + ast/FunctionalConstraint.cpp + ast/IntrinsicFunctor.cpp +- ast/IterationCounter.cpp +- ast/Lattice.cpp + ast/Negation.cpp + ast/NilConstant.cpp + ast/Node.cpp +@@ -50,7 +43,6 @@ set(SOUFFLE_SOURCES + ast/StringConstant.cpp + ast/SubsetType.cpp + ast/Term.cpp +- ast/TokenTree.cpp + ast/TranslationUnit.cpp + ast/Type.cpp + ast/TypeCast.cpp +@@ -69,7 +61,7 @@ set(SOUFFLE_SOURCES + ast/analysis/RecursiveClauses.cpp + ast/analysis/RedundantRelations.cpp + ast/analysis/RelationSchedule.cpp +- ast/analysis/JoinSize.cpp ++ ast/analysis/UniqueKeys.cpp + ast/analysis/SCCGraph.cpp + ast/analysis/TopologicallySortedSCCGraph.cpp + ast/analysis/typesystem/PolymorphicObjects.cpp +@@ -84,14 +76,12 @@ set(SOUFFLE_SOURCES + ast/transform/ComponentChecker.cpp + ast/transform/ComponentInstantiation.cpp + ast/transform/DebugReporter.cpp +- ast/transform/DebugDeltaRelation.cpp + ast/transform/ExecutionPlanChecker.cpp + ast/transform/ExpandEqrels.cpp + ast/transform/FoldAnonymousRecords.cpp + ast/transform/GroundedTermsChecker.cpp + ast/transform/GroundWitnesses.cpp + ast/transform/InlineRelations.cpp +- ast/transform/InsertLatticeOperations.cpp + ast/transform/MagicSet.cpp + ast/transform/MaterializeAggregationQueries.cpp + ast/transform/MaterializeSingletonAggregation.cpp +@@ -111,15 +101,13 @@ set(SOUFFLE_SOURCES + ast/transform/ResolveAliases.cpp + ast/transform/ResolveAnonymousRecordAliases.cpp + ast/transform/SemanticChecker.cpp +- ast/transform/SimplifyConstantBinaryConstraints.cpp + ast/transform/SubsumptionQualifier.cpp + ast/transform/SimplifyAggregateTargetExpression.cpp + ast/transform/Transformer.cpp + ast/transform/TypeChecker.cpp + ast/transform/UniqueAggregationVariables.cpp + ast/utility/BindingStore.cpp +- ast2ram/utility/SipsMetric.cpp +- ast2ram/utility/SipGraph.cpp ++ ast/utility/SipsMetric.cpp + ast/utility/Utils.cpp + ast2ram/provenance/ClauseTranslator.cpp + ast2ram/provenance/ConstraintTranslator.cpp +@@ -144,7 +132,6 @@ set(SOUFFLE_SOURCES + parser/ParserDriver.cpp + parser/ParserUtils.cpp + parser/SrcLocation.cpp +- parser/VirtualFileSystem.cpp + ram/Node.cpp + ram/TranslationUnit.cpp + ram/analysis/Complexity.cpp +@@ -165,12 +152,10 @@ set(SOUFFLE_SOURCES + ram/transform/Transformer.cpp + ram/transform/TupleId.cpp + ram/utility/NodeMapper.cpp +- reports/ErrorReport.cpp + reports/DebugReport.cpp + synthesiser/Synthesiser.cpp + synthesiser/Relation.cpp +- synthesiser/Utils.cpp +- synthesiser/GenDb.cpp ++ main.cpp + ) + + # -------------------------------------------------- +@@ -182,7 +167,7 @@ flex_target(scanner parser/scanner.ll ${CMAKE_CURRENT_BINARY_DIR}/parser/scanner + COMPILE_FLAGS "${SCANNER_COMPILE_FLAGS} -d") + + bison_target(parser parser/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc +- COMPILE_FLAGS "-Wall -Wno-error=deprecated -Wno-error=other -v -d") ++ COMPILE_FLAGS "-Wall -Werror -Wno-error=deprecated -Wno-error=other -v -d") + add_flex_bison_dependency(scanner parser) + + +@@ -191,15 +176,10 @@ if (MSVC) + COMPILE_FLAGS "/wd4005 /wd4996") + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc PROPERTIES + COMPILE_FLAGS "/wd4005 /wd26819") +-elseif (EMSCRIPTEN) +- set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc PROPERTIES +- COMPILE_FLAGS "-Wno-error=unused-but-set-variable") + 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") +- set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/parser/parser.cc PROPERTIES +- COMPILE_FLAGS "-Wno-error=unused-but-set-variable") + endif () + + # -------------------------------------------------- +@@ -211,6 +191,10 @@ add_library(libsouffle STATIC + ${BISON_parser_OUTPUTS} + ) + ++add_library(libsouffle-shared SHARED DynamicLibrary.cpp) ++target_link_libraries(libsouffle-shared libsouffle) ++set_target_properties(libsouffle-shared PROPERTIES OUTPUT_NAME "souffle") ++ + # Make most of the attributes public. Note that that's strictly not "correct" but + # this library is not going to be installed and it makes it easier to build + # the other targets in this project. It encapsulates things nicely. +@@ -232,15 +216,12 @@ 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(libsouffle PROPERTIES POSITION_INDEPENDENT_CODE ON) +- + set_target_properties(compiled PROPERTIES CXX_EXTENSIONS OFF) +-set_target_properties(compiled PROPERTIES POSITION_INDEPENDENT_CODE ON) + + if (NOT MSVC) +- target_compile_options(libsouffle PUBLIC -Wall -Wextra -fwrapv) ++ target_compile_options(libsouffle PUBLIC -Wall -Wextra -fwrapv -fPIC) + else () +- target_compile_options(libsouffle PUBLIC /W3) ++ target_compile_options(libsouffle PUBLIC /W3 /WX) + endif () + + target_compile_options(compiled PUBLIC "") +@@ -330,36 +311,24 @@ set_target_properties(libsouffle PROPERTIES OUTPUT_NAME "souffle") + # Souffle binary + # -------------------------------------------------- + add_executable(souffle +- souffle.cpp) ++ MainEntry.cpp) + target_link_libraries(souffle libsouffle) + install(TARGETS souffle DESTINATION bin) + +-if (EMSCRIPTEN) +-target_link_libraries(souffle -sMODULARIZE=1 +- -s'EXPORTED_RUNTIME_METHODS=["FS"]' -sEXPORT_NAME="SOUFFLE") +-endif() +- + # 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} +- $) +- if (WIN32) +- cmake_path(GET SQLite3_LIBRARY PARENT_PATH SQLite3_LIBRARY_DIR) +- add_custom_command(TARGET souffle POST_BUILD +- COMMAND ${CMAKE_COMMAND} -E copy_if_different +- ${SQLite3_LIBRARY_DIR}/../bin/sqlite3.dll +- $) +- endif () ++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 +- ${ZLIB_LIBRARY_RELEASE} ++ ${SQLite3_LIBRARY} ${ZLIB_LIBRARY_RELEASE} + $) + endif () + +@@ -374,21 +343,18 @@ 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) + +-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) ++ # Set C++ standard to C++17 ++ target_compile_features(souffleprof ++ PUBLIC cxx_std_17) ++endif() + + if (MSVC) +- target_compile_options(libsouffle PUBLIC /Zc:__cplusplus) +- target_compile_options(compiled PUBLIC /Zc:__cplusplus) +- target_compile_options(souffleprof PUBLIC /Zc:__cplusplus) +- + target_compile_options(libsouffle PUBLIC /bigobj) + target_compile_options(compiled PUBLIC /bigobj) + +@@ -413,27 +379,15 @@ if (MSVC) + + target_compile_options(libsouffle PUBLIC /Zc:preprocessor) + target_compile_options(compiled PUBLIC /Zc:preprocessor) +- target_compile_options(souffleprof PUBLIC /Zc:preprocessor) + + target_compile_options(libsouffle PUBLIC /EHsc) + target_compile_options(compiled PUBLIC /EHsc) +- target_compile_options(souffleprof PUBLIC /EHsc) + + target_compile_definitions(libsouffle PUBLIC USE_CUSTOM_GETOPTLONG) + target_compile_definitions(compiled PUBLIC USE_CUSTOM_GETOPTLONG) +- target_compile_definitions(souffleprof PUBLIC USE_CUSTOM_GETOPTLONG) + endif (MSVC) + +-if (APPLE) +- if ("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64") +- # work around a known issue with xcode15 linker +- # c++ exceptions handling is broken otherwise +- set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wl,-ld_classic") +- target_link_options(libsouffle PUBLIC "-ld_classic") +- target_link_options(compiled PUBLIC "-ld_classic") +- target_link_options(souffleprof PUBLIC "-ld_classic") +- endif() +-endif() ++ + + # -------------------------------------------------- + # Substitutions for souffle-compile.py +@@ -449,7 +403,6 @@ 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) +-get_property(SOUFFLE_COMPILED_LINK_OPTS TARGET compiled PROPERTY LINK_OPTIONS) + + set(SOUFFLE_COMPILED_LIBS "") + set(SOUFFLE_COMPILED_RPATHS "") +@@ -537,10 +490,7 @@ endif () + + list(JOIN SOUFFLE_COMPILED_OPTS " " SOUFFLE_COMPILED_CXX_OPTIONS) + +-list(JOIN SOUFFLE_COMPILED_LIBS " " SOUFFLE_COMPILED_LINK_OPTIONS1) +-list(JOIN SOUFFLE_COMPILED_LINK_OPTS " " SOUFFLE_COMPILED_LINK_OPTIONS2) +-set(SOUFFLE_COMPILED_LINK_OPTIONS "") +-string(APPEND SOUFFLE_COMPILED_LINK_OPTIONS " ${SOUFFLE_COMPILED_LINK_OPTIONS1}" " ${SOUFFLE_COMPILED_LINK_OPTIONS2}") ++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}") +diff --git a/src/DynamicLibrary.cpp b/src/DynamicLibrary.cpp +new file mode 100644 +index 0000000..aeea973 +--- /dev/null ++++ b/src/DynamicLibrary.cpp +@@ -0,0 +1,72 @@ ++#include ++#include ++ ++#include "ExtraConfig.h" ++ ++extern ExtraConfig extraConfig; ++namespace souffle { int main(int argc, char **argv); } ++ ++// This function is a C API for foreign functions. ++extern "C" int souffle_main(int argc, char **argv) { ++ try { ++ return souffle::main(argc, argv); ++ } catch (std::exception &e) { ++ std::cerr << "Exception Occur: " << e.what() << std::endl; ++ return 1; ++ } ++} ++ ++// Soufflé 定制化入口,与 Gödel 互通 ++// program_path : 程序路径,对应 argv[0],Soufflé 会根据这个路径搜索库或者头文件 ++// query_file : Query 文件的路径 ++// query_content : 【可空】Query 的内容,开启后不再从 query_file 中读取内容 ++// fact_search_path : 【可空】Fact 文件搜索路径 ++// library_search_path : 【可空】库搜索路径 ++// library_name : 【可空】库名 ++// enable_warning : 开启警告,非 0 为开,0 为关 ++// verbose : 开启 Verbose 信息 ++// extra_args : 【可空】额外参数,以 nullptr 结尾的数组 ++ ++extern "C" int souffle_entry( ++ const char *program_path, ++ const char *query_file, ++ const char *query_content, ++ const char *fact_search_path, ++ const char *library_search_path, ++ const char *library_name, ++ int enable_warning, ++ int verbose, ++ const char *extra_args[]) { ++ std::vector args; ++ args.push_back(program_path); ++ args.push_back(query_file); ++ if (query_content != nullptr) { ++ extraConfig.setContent(query_content); ++ } ++ args.push_back("--no-preprocessor"); ++ if (fact_search_path) { ++ args.push_back("-F"); ++ args.push_back(fact_search_path); ++ } ++ if (library_search_path) { ++ args.push_back("-L"); ++ args.push_back(library_search_path); ++ } ++ if (library_name) { ++ args.push_back("-l"); ++ args.push_back(library_name); ++ } ++ if (!enable_warning) { ++ args.push_back("-w"); ++ } ++ if (verbose) { ++ args.push_back("-v"); ++ } ++ if (extra_args) { ++ for (const char **parg = extra_args; *parg != nullptr; ++parg) { ++ args.push_back(*parg); ++ } ++ } ++ ++ return souffle_main(args.size(), const_cast(args.data())); ++} +diff --git a/src/ExtraConfig.h b/src/ExtraConfig.h +new file mode 100644 +index 0000000..24bdd29 +--- /dev/null ++++ b/src/ExtraConfig.h +@@ -0,0 +1,24 @@ ++#pragma once ++ ++#ifndef GODEL_EXTRACONFIG_H ++#define GODEL_EXTRACONFIG_H ++#include ++#include ++ ++// 针对嵌入 Gödel 的 Soufflé 程序进行的额外配置 ++struct ExtraConfig ++{ ++private: ++ std::unique_ptr content; // 解析的 Soufflé 代码内容,非 nullptr 时表示直接解析内容而不是从文件读取 ++ ++public: ++ const std::unique_ptr& getContent() const { ++ return content; ++ } ++ ++ void setContent(const std::string &message) { ++ this->content = std::make_unique(message); ++ } ++}; ++ ++#endif //GODEL_EXTRACONFIG_H +diff --git a/src/FunctorOps.cpp b/src/FunctorOps.cpp +index 394357b..178dcd8 100644 +--- a/src/FunctorOps.cpp ++++ b/src/FunctorOps.cpp +@@ -48,7 +48,6 @@ char const* functorOpNameLegacy(FunctorOp op) { + /** Binary Functor Operators */ + case FunctorOp::ADD: + case FunctorOp::FADD: +- case FunctorOp::SSADD: + case FunctorOp::UADD: return "+"; + case FunctorOp::SUB: + case FunctorOp::USUB: +@@ -224,9 +223,6 @@ const std::vector FUNCTOR_INTRINSICS = { + VARIADIC(CAT, Symbol), + OP_1(STRLEN, Symbol, Signed), + OP_3(SUBSTR, Symbol, Signed, Signed, Symbol, false), +- +- {functorOpNameSymbol(FOp::SSADD), {TAttr::Symbol, TAttr::Symbol}, TAttr::Symbol, FOp::SSADD, false, +- false}, + }; + + template +diff --git a/src/FunctorOps.h b/src/FunctorOps.h +index 6caebcc..2ad9895 100644 +--- a/src/FunctorOps.h ++++ b/src/FunctorOps.h +@@ -101,7 +101,6 @@ enum class FunctorOp { + FMIN, // min of two floats + SMAX, // max of two symbols + SMIN, // min of two symbols +- SSADD, // string-string concatenation + + // Produces values within a numeric range. Format is `range(bgn, endExcl, step = 1)`. + // e.g. `range(0, 5)` produces the sequence `0, 1, 2, 3, 4`. +@@ -165,8 +164,4 @@ bool isInfixFunctorOp(FunctorOp op); + + FunctorOp getMinOp(const std::string& type); + FunctorOp getMaxOp(const std::string& type); +- +-/// Indicate if a functor can yield multiple results +-bool isFunctorMultiResult(FunctorOp op); +- + } // end of namespace souffle +diff --git a/src/Global.cpp b/src/Global.cpp +index 68f673a..f843690 100644 +--- a/src/Global.cpp ++++ b/src/Global.cpp +@@ -182,7 +182,7 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c + while ((c = getopt_long(argc, argv, shortNames.c_str(), longNames.get(), nullptr)) != -1) { + // case for the unknown option + if (c == '?') { +- std::cerr << help(); ++ std::cerr << Global::config().help(); + throw std::runtime_error("Error: Unknown command line option."); + } + // obtain an iterator to the option in the table referenced by the current short name +@@ -217,11 +217,11 @@ void MainConfig::processArgs(int argc, char** argv, const std::string& header, c + } + + // obtain the name of the datalog file, and store it in the option with the empty key +- if (argc > 1 && !has("help") && !has("version")) { ++ if (argc > 1 && !Global::config().has("help") && !Global::config().has("version")) { + std::string filename = ""; + // ensure that the optind is less than the total number of arguments + if (argc > 1 && optind >= argc) { +- std::cerr << help(); ++ std::cerr << Global::config().help(); + throw std::runtime_error("Error: Missing source file path."); + } + +diff --git a/src/Global.h b/src/Global.h +index 5e63334..866fd61 100644 +--- a/src/Global.h ++++ b/src/Global.h +@@ -104,21 +104,18 @@ private: + * used to isolate all globals. */ + class Global { + public: +- Global() = default; + /* Deleted copy constructor. */ + Global(const Global&) = delete; + /* Deleted assignment operator. */ + Global& operator=(const Global&) = delete; + /* Obtain the global configuration. */ +- MainConfig& config() { +- return _config; +- } +- +- const MainConfig& config() const { ++ static MainConfig& config() { ++ static MainConfig _config; + return _config; + } + + private: +- MainConfig _config; ++ /* Private empty constructor, there is only one global instance. */ ++ Global() = default; + }; + } // namespace souffle +diff --git a/src/GraphUtils.h b/src/GraphUtils.h +index 104fe2b..aab91cf 100644 +--- a/src/GraphUtils.h ++++ b/src/GraphUtils.h +@@ -13,14 +13,84 @@ + * A simple utility graph for conducting simple, graph-based operations. + * + ***********************************************************************/ ++ + #pragma once + ++#include "souffle/datastructure/Graph.h" ++#include "souffle/utility/FileUtil.h" ++#include ++#include + #include ++#include + #include ++#include + #include + + namespace souffle { +-std::string toBase64(const std::string& data); +-std::string convertDotToSVG(const std::string& dotSpec); +-void printHTMLGraph(std::ostream& out, const std::string& dotSpec, const std::string& id); ++ ++inline std::string toBase64(const std::string& data) { ++ static const std::vector table = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', ++ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', ++ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', ++ 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; ++ std::string result; ++ std::string tmp = data; ++ unsigned int padding = 0; ++ if (data.size() % 3 == 2) { ++ padding = 1; ++ } else if (data.size() % 3 == 1) { ++ padding = 2; ++ } ++ ++ for (unsigned int i = 0; i < padding; i++) { ++ tmp.push_back(0); ++ } ++ for (unsigned int i = 0; i < tmp.size(); i += 3) { ++ auto c1 = static_cast(tmp[i]); ++ auto c2 = static_cast(tmp[i + 1]); ++ auto c3 = static_cast(tmp[i + 2]); ++ unsigned char index1 = c1 >> 2; ++ unsigned char index2 = ((c1 & 0x03) << 4) | (c2 >> 4); ++ unsigned char index3 = ((c2 & 0x0F) << 2) | (c3 >> 6); ++ unsigned char index4 = c3 & 0x3F; ++ ++ result.push_back(table[index1]); ++ result.push_back(table[index2]); ++ result.push_back(table[index3]); ++ result.push_back(table[index4]); ++ } ++ if (padding == 1) { ++ result[result.size() - 1] = '='; ++ } else if (padding == 2) { ++ result[result.size() - 1] = '='; ++ result[result.size() - 2] = '='; ++ } ++ return result; ++} ++ ++inline std::string convertDotToSVG(const std::string& dotSpec) { ++ // Check if dot is present ++ std::string cmd = which("dot"); ++ if (!isExecutable(cmd)) { ++ return ""; ++ } ++ ++ TempFileStream dotFile; ++ dotFile << dotSpec; ++ dotFile.flush(); ++ return execStdOut("dot -Tsvg < " + dotFile.getFileName()).str(); ++} ++ ++inline void printHTMLGraph(std::ostream& out, const std::string& dotSpec, const std::string& id) { ++ std::string data = convertDotToSVG(dotSpec); ++ ++ if (data.find("
\n"; ++ } else { ++ out << "
\n
" << dotSpec << "
\n"; ++ out << "
\n"; ++ } ++} ++ + } // end of namespace souffle +diff --git a/src/LogStatement.h b/src/LogStatement.h +index 1a84c08..498d9d8 100644 +--- a/src/LogStatement.h ++++ b/src/LogStatement.h +@@ -21,26 +21,19 @@ + namespace souffle { + + class LogStatement { +-private: +- static std::string str(const SrcLocation& loc) { +- return loc.getReportedPath() + " [" + std::to_string(loc.start.line) + ":" + +- std::to_string(loc.start.column) + "-" + std::to_string(loc.end.line) + ":" + +- std::to_string(loc.end.column) + "]"; +- } +- + public: + static const std::string tNonrecursiveRelation( + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@t-nonrecursive-relation"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";"; + return line.str(); + } + static const std::string tRelationLoadTime( + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@t-relation-loadtime"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";loadtime;"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";loadtime;"; + return line.str(); + } + +@@ -48,7 +41,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@t-relation-savetime"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";savetime;"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";savetime;"; + return line.str(); + } + +@@ -56,7 +49,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@n-nonrecursive-relation"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";"; + return line.str(); + } + +@@ -64,7 +57,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation, const std::string& datalogText) { + const char* messageType = "@t-nonrecursive-rule"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";" << datalogText << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";" << datalogText << ";"; + return line.str(); + } + +@@ -72,7 +65,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation, const std::string& datalogText) { + const char* messageType = "@n-nonrecursive-rule"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";" << datalogText << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";" << datalogText << ";"; + return line.str(); + } + +@@ -80,7 +73,7 @@ public: + const SrcLocation& srcLocation, const std::string& datalogText) { + const char* messageType = "@t-recursive-rule"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << version << ";" << str(srcLocation) << ";" ++ line << messageType << ";" << relationName << ";" << version << ";" << srcLocation << ";" + << datalogText << ";"; + return line.str(); + } +@@ -89,7 +82,7 @@ public: + const SrcLocation& srcLocation, const std::string& datalogText) { + const char* messageType = "@n-recursive-rule"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << version << ";" << str(srcLocation) << ";" ++ line << messageType << ";" << relationName << ";" << version << ";" << srcLocation << ";" + << datalogText << ";"; + return line.str(); + } +@@ -98,7 +91,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@t-recursive-relation"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";"; + return line.str(); + } + +@@ -106,7 +99,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@n-recursive-relation"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";"; + return line.str(); + } + +@@ -114,7 +107,7 @@ public: + const std::string& relationName, const SrcLocation& srcLocation) { + const char* messageType = "@c-recursive-relation"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";"; + return line.str(); + } + +@@ -124,7 +117,7 @@ public: + // messages are ignored + const char* messageType = "#p-proof-counter"; + std::stringstream line; +- line << messageType << ";" << relationName << ";" << str(srcLocation) << ";" << datalogText << ";"; ++ line << messageType << ";" << relationName << ";" << srcLocation << ";" << datalogText << ";"; + // TODO (#590): the additional semicolon is added to maintain backwards compatibility and should + // eventually be removed + line << ";"; +diff --git a/src/MainEntry.cpp b/src/MainEntry.cpp +new file mode 100644 +index 0000000..948655e +--- /dev/null ++++ b/src/MainEntry.cpp +@@ -0,0 +1,5 @@ ++namespace souffle { int main(int argc, char **argv); } ++ ++int main(int argc, char** argv) { ++ return souffle::main(argc, argv); ++} +diff --git a/src/RelationTag.h b/src/RelationTag.h +index c6d51ca..5a85097 100644 +--- a/src/RelationTag.h ++++ b/src/RelationTag.h +@@ -58,6 +58,7 @@ enum class RelationRepresentation { + BTREE, // use btree data-structure + BTREE_DELETE, // use btree_delete data-structure + EQREL, // use union data-structure ++ PROVENANCE, // use custom btree data-structure with provenance extras + INFO, // info relation for provenance + }; + +@@ -167,6 +168,7 @@ inline std::ostream& operator<<(std::ostream& os, RelationRepresentation represe + case RelationRepresentation::BTREE_DELETE: return os << "btree_delete"; + case RelationRepresentation::BRIE: return os << "brie"; + case RelationRepresentation::EQREL: return os << "eqrel"; ++ case RelationRepresentation::PROVENANCE: return os << "provenance"; + case RelationRepresentation::INFO: return os << "info"; + case RelationRepresentation::DEFAULT: return os; + } +diff --git a/src/TranslationUnitBase.h b/src/TranslationUnitBase.h +index b4dcc14..e97ebec 100644 +--- a/src/TranslationUnitBase.h ++++ b/src/TranslationUnitBase.h +@@ -16,13 +16,11 @@ + + #pragma once + +-#include "Global.h" + #include "reports/DebugReport.h" + #include "reports/ErrorReport.h" + #include "souffle/utility/DynamicCasting.h" + #include "souffle/utility/Types.h" + #include +-#include + #include + #include + #include +@@ -50,14 +48,9 @@ public: + return name_as_cstr_literal; + } + +- /** @brief Print the analysis result in textual format */ ++ /** @brief Print the analysis result in HTML format */ + virtual void print(std::ostream& /* os */) const {} + +- /** @brief Print the analysis result in HTML format (or fallback to textual format) */ +- virtual void printHTML(std::ostream& os) const { +- print(os); +- } +- + private: + char const* const name_as_cstr_literal; + }; +@@ -79,8 +72,8 @@ struct TranslationUnitBase { + virtual void run(Impl const&) = 0; + }; + +- TranslationUnitBase(Global& g, Own prog, ErrorReport& e, DebugReport& d) +- : glb(g), program(std::move(prog)), errorReport(e), debugReport(d) { ++ TranslationUnitBase(Own prog, ErrorReport& e, DebugReport& d) ++ : program(std::move(prog)), errorReport(e), debugReport(d) { + assert(program != nullptr && "program is a null-pointer"); + } + +@@ -94,7 +87,7 @@ struct TranslationUnitBase { + it = analyses.insert({A::name, mk()}).first; + + auto& analysis = *it->second; +- assert((std::strcmp(analysis.getName(), A::name) == 0) && "must be same pointer"); ++ assert(analysis.getName() == A::name && "must be same pointer"); + analysis.run(static_cast(*this)); + logAnalysis(analysis); + } +@@ -116,11 +109,6 @@ struct TranslationUnitBase { + analyses.clear(); + } + +- /** @brief Get the global configuration */ +- Global& global() const { +- return glb; +- } +- + /** @brief Get the RAM Program of the translation unit */ + Program& getProgram() const { + return *program; +@@ -145,8 +133,6 @@ protected: + // Using `std::string` appears to suppress the issue (bug?). + mutable std::map> analyses; + +- Global& glb; +- + /* RAM program */ + Own program; + +diff --git a/src/ast/Aggregator.cpp b/src/ast/Aggregator.cpp +index 981ea55..acc6f25 100644 +--- a/src/ast/Aggregator.cpp ++++ b/src/ast/Aggregator.cpp +@@ -16,23 +16,21 @@ + #include + + namespace souffle::ast { +-Aggregator::Aggregator(NodeKind kind, Own expr, VecOwn body, SrcLocation loc) +- : Argument(kind, std::move(loc)), targetExpression(std::move(expr)), body(std::move(body)) { ++Aggregator::Aggregator(AggregateOp baseOperator, Own expr, VecOwn body, SrcLocation loc) ++ : Argument(std::move(loc)), baseOperator(baseOperator), targetExpression(std::move(expr)), ++ body(std::move(body)) { + // NOTE: targetExpression can be nullptr - it's used e.g. when aggregator + // has no parameters, such as count: { body } + assert(allValidPtrs(this->body)); +- assert(kind >= NK_Aggregator && kind < NK_LastAggregator); + } + + std::vector Aggregator::getBodyLiterals() const { + return toPtrVector(body); + } + +-VecOwn Aggregator::setBodyLiterals(VecOwn bodyLiterals) { ++void Aggregator::setBodyLiterals(VecOwn bodyLiterals) { + assert(allValidPtrs(body)); +- auto oldBody = std::move(body); + body = std::move(bodyLiterals); +- return oldBody; + } + + void Aggregator::apply(const NodeMapper& map) { +@@ -43,9 +41,22 @@ void Aggregator::apply(const NodeMapper& map) { + mapAll(body, map); + } + +-bool Aggregator::classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Aggregator && kind < NK_LastAggregator); ++void Aggregator::print(std::ostream& os) const { ++ os << baseOperator; ++ if (targetExpression) { ++ os << " " << *targetExpression; ++ } ++ os << " : { " << join(body) << " }"; ++} ++ ++bool Aggregator::equal(const Node& node) const { ++ const auto& other = asAssert(node); ++ return baseOperator == other.baseOperator && equal_ptr(targetExpression, other.targetExpression) && ++ equal_targets(body, other.body); ++} ++ ++Aggregator* Aggregator::cloning() const { ++ return new Aggregator(baseOperator, clone(targetExpression), clone(body), getSrcLoc()); + } + + Node::NodeVec Aggregator::getChildren() const { +diff --git a/src/ast/Aggregator.h b/src/ast/Aggregator.h +index 54f58c5..4e07556 100644 +--- a/src/ast/Aggregator.h ++++ b/src/ast/Aggregator.h +@@ -38,7 +38,13 @@ namespace souffle::ast { + */ + class Aggregator : public Argument { + public: +- Aggregator(NodeKind Kind, Own expr = {}, VecOwn body = {}, SrcLocation loc = {}); ++ Aggregator(AggregateOp baseOperator, Own expr = {}, VecOwn body = {}, ++ SrcLocation loc = {}); ++ ++ /** Return the (base type) operator of the aggregator */ ++ AggregateOp getBaseOperator() const { ++ return baseOperator; ++ } + + /** Return target expression */ + const Argument* getTargetExpression() const { +@@ -52,18 +58,25 @@ public: + /** Return body literals */ + std::vector getBodyLiterals() const; + +- /** Set body literals, returns previous body literals */ +- VecOwn setBodyLiterals(VecOwn bodyLiterals); ++ /** Set body literals */ ++ void setBodyLiterals(VecOwn bodyLiterals); + + void apply(const NodeMapper& map) override; + +- virtual std::string getBaseOperatorName() const = 0; +- +- static bool classof(const Node*); +- + protected: ++ void print(std::ostream& os) const override; ++ + NodeVec getChildren() const override; + ++private: ++ bool equal(const Node& node) const override; ++ ++ Aggregator* cloning() const override; ++ ++private: ++ /** Aggregate (base type) operator */ ++ AggregateOp baseOperator; ++ + /** Aggregate expression */ + Own targetExpression; + +diff --git a/src/ast/AlgebraicDataType.cpp b/src/ast/AlgebraicDataType.cpp +index 716c94d..77d678d 100644 +--- a/src/ast/AlgebraicDataType.cpp ++++ b/src/ast/AlgebraicDataType.cpp +@@ -16,7 +16,7 @@ + namespace souffle::ast { + + AlgebraicDataType::AlgebraicDataType(QualifiedName name, VecOwn branches, SrcLocation loc) +- : Type(NK_AlgebraicDataType, std::move(name), std::move(loc)), branches(std::move(branches)) { ++ : Type(std::move(name), std::move(loc)), branches(std::move(branches)) { + assert(!this->branches.empty()); + assert(allValidPtrs(this->branches)); + } +@@ -31,7 +31,6 @@ Node::NodeVec AlgebraicDataType::getChildren() const { + } + + void AlgebraicDataType::print(std::ostream& os) const { +- printAnnotations(os); + os << tfm::format(".type %s = %s", getQualifiedName(), join(branches, " | ")); + } + +@@ -44,8 +43,4 @@ AlgebraicDataType* AlgebraicDataType::cloning() const { + return new AlgebraicDataType(getQualifiedName(), clone(branches), getSrcLoc()); + } + +-bool AlgebraicDataType::classof(const Node* n) { +- return n->getKind() == NK_AlgebraicDataType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/AlgebraicDataType.h b/src/ast/AlgebraicDataType.h +index fb63b0c..c6e73d3 100644 +--- a/src/ast/AlgebraicDataType.h ++++ b/src/ast/AlgebraicDataType.h +@@ -44,8 +44,6 @@ public: + + std::vector getBranches() const; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/AliasType.cpp b/src/ast/AliasType.cpp +index f60dea5..e5e43ea 100644 +--- a/src/ast/AliasType.cpp ++++ b/src/ast/AliasType.cpp +@@ -14,7 +14,7 @@ + namespace souffle::ast { + + AliasType::AliasType(QualifiedName name, QualifiedName aliasTypeName, SrcLocation loc) +- : Type(NK_AliasType, std::move(name), std::move(loc)), aliasType(std::move(aliasTypeName)) {} ++ : Type(std::move(name), std::move(loc)), aliasType(std::move(aliasTypeName)) {} + + void AliasType::print(std::ostream& os) const { + os << ".type " << getQualifiedName() << " = " << getAliasType(); +@@ -29,8 +29,4 @@ AliasType* AliasType::cloning() const { + return new AliasType(getQualifiedName(), getAliasType(), getSrcLoc()); + } + +-bool AliasType::classof(const Node* n) { +- return n->getKind() == NK_AliasType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/AliasType.h b/src/ast/AliasType.h +index 9813667..4b79965 100644 +--- a/src/ast/AliasType.h ++++ b/src/ast/AliasType.h +@@ -44,8 +44,6 @@ public: + aliasType = type; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Argument.h b/src/ast/Argument.h +index b202230..cb8b259 100644 +--- a/src/ast/Argument.h ++++ b/src/ast/Argument.h +@@ -27,11 +27,6 @@ namespace souffle::ast { + class Argument : public Node { + public: + using Node::Node; +- +- static bool classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Argument && kind < NK_LastArgument); +- } + }; + + } // namespace souffle::ast +diff --git a/src/ast/Atom.cpp b/src/ast/Atom.cpp +index f6505a0..7fe72ab 100644 +--- a/src/ast/Atom.cpp ++++ b/src/ast/Atom.cpp +@@ -26,7 +26,7 @@ namespace souffle::ast { + * e.g., parent(x,y), !parent(x,y), ... + */ + Atom::Atom(QualifiedName name, VecOwn args, SrcLocation loc) +- : Literal(NK_Atom, std::move(loc)), name(std::move(name)), arguments(std::move(args)) { ++ : Literal(std::move(loc)), name(std::move(name)), arguments(std::move(args)) { + assert(allValidPtrs(arguments)); + } + +@@ -53,7 +53,6 @@ Node::NodeVec Atom::getChildren() const { + } + + void Atom::print(std::ostream& os) const { +- printAnnotations(os); + os << getQualifiedName() << "(" << join(arguments) << ")"; + } + +@@ -66,8 +65,4 @@ Atom* Atom::cloning() const { + return new Atom(name, clone(arguments), getSrcLoc()); + } + +-bool Atom::classof(const Node* n) { +- return n->getKind() == NK_Atom; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Atom.h b/src/ast/Atom.h +index 9e52a32..795f5dd 100644 +--- a/src/ast/Atom.h ++++ b/src/ast/Atom.h +@@ -60,8 +60,6 @@ public: + + void apply(const NodeMapper& map) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Attribute.cpp b/src/ast/Attribute.cpp +index 6a8a4bd..a06fdab 100644 +--- a/src/ast/Attribute.cpp ++++ b/src/ast/Attribute.cpp +@@ -14,39 +14,23 @@ + namespace souffle::ast { + + Attribute::Attribute(std::string n, QualifiedName t, SrcLocation loc) +- : Node(NK_Attribute, std::move(loc)), name(std::move(n)), typeName(std::move(t)), isLattice(false) {} +- +-Attribute::Attribute(std::string n, QualifiedName t, bool isLattice, SrcLocation loc) +- : Node(NK_Attribute, std::move(loc)), name(std::move(n)), typeName(std::move(t)), +- isLattice(isLattice) {} ++ : Node(std::move(loc)), name(std::move(n)), typeName(std::move(t)) {} + + void Attribute::setTypeName(QualifiedName name) { + typeName = std::move(name); + } + + void Attribute::print(std::ostream& os) const { +- printAnnotations(os); + os << name << ":" << typeName; +- if (isLattice) { +- os << "<>"; +- } + } + + bool Attribute::equal(const Node& node) const { + const auto& other = asAssert(node); +- return name == other.name && typeName == other.typeName && isLattice == other.isLattice; ++ return name == other.name && typeName == other.typeName; + } + + Attribute* Attribute::cloning() const { +- return new Attribute(name, typeName, isLattice, getSrcLoc()); +-} +- +-bool Attribute::classof(const Node* n) { +- return n->getKind() == NK_Attribute; +-} +- +-bool Attribute::getIsLattice() const { +- return isLattice; ++ return new Attribute(name, typeName, getSrcLoc()); + } + + } // namespace souffle::ast +diff --git a/src/ast/Attribute.h b/src/ast/Attribute.h +index 3d41fa1..ecea916 100644 +--- a/src/ast/Attribute.h ++++ b/src/ast/Attribute.h +@@ -35,7 +35,6 @@ namespace souffle::ast { + class Attribute : public Node { + public: + Attribute(std::string n, QualifiedName t, SrcLocation loc = {}); +- Attribute(std::string n, QualifiedName t, bool isLattice, SrcLocation loc = {}); + + /** Return attribute name */ + const std::string& getName() const { +@@ -50,10 +49,6 @@ public: + /** Set type name */ + void setTypeName(QualifiedName name); + +- bool getIsLattice() const; +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +@@ -68,9 +63,6 @@ private: + + /** Type name */ + QualifiedName typeName; +- +- /** Is lattice element */ +- bool isLattice; + }; + + } // namespace souffle::ast +diff --git a/src/ast/BinaryConstraint.cpp b/src/ast/BinaryConstraint.cpp +index 4f0cc8f..0104815 100644 +--- a/src/ast/BinaryConstraint.cpp ++++ b/src/ast/BinaryConstraint.cpp +@@ -15,8 +15,7 @@ + namespace souffle::ast { + + BinaryConstraint::BinaryConstraint(BinaryConstraintOp o, Own ls, Own rs, SrcLocation loc) +- : Constraint(NK_BinaryConstraint, std::move(loc)), operation(o), lhs(std::move(ls)), +- rhs(std::move(rs)) { ++ : Constraint(std::move(loc)), operation(o), lhs(std::move(ls)), rhs(std::move(rs)) { + assert(lhs != nullptr); + assert(rhs != nullptr); + } +@@ -47,8 +46,4 @@ BinaryConstraint* BinaryConstraint::cloning() const { + return new BinaryConstraint(operation, clone(lhs), clone(rhs), getSrcLoc()); + } + +-bool BinaryConstraint::classof(const Node* n) { +- return n->getKind() == NK_BinaryConstraint; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/BinaryConstraint.h b/src/ast/BinaryConstraint.h +index 3a02814..911fbf7 100644 +--- a/src/ast/BinaryConstraint.h ++++ b/src/ast/BinaryConstraint.h +@@ -61,8 +61,6 @@ public: + + void apply(const NodeMapper& map) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/BooleanConstraint.cpp b/src/ast/BooleanConstraint.cpp +index 1dba292..0a8251e 100644 +--- a/src/ast/BooleanConstraint.cpp ++++ b/src/ast/BooleanConstraint.cpp +@@ -24,7 +24,7 @@ namespace souffle::ast { + * Boolean constraint representing either the 'true' or the 'false' value + */ + BooleanConstraint::BooleanConstraint(bool truthValue, SrcLocation loc) +- : Constraint(NK_BooleanConstraint, std::move(loc)), truthValue(truthValue) {} ++ : Constraint(std::move(loc)), truthValue(truthValue) {} + + void BooleanConstraint::print(std::ostream& os) const { + os << (truthValue ? "true" : "false"); +@@ -39,8 +39,4 @@ BooleanConstraint* BooleanConstraint::cloning() const { + return new BooleanConstraint(truthValue, getSrcLoc()); + } + +-bool BooleanConstraint::classof(const Node* n) { +- return n->getKind() == NK_BooleanConstraint; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/BooleanConstraint.h b/src/ast/BooleanConstraint.h +index f4f6b23..f6ac7cf 100644 +--- a/src/ast/BooleanConstraint.h ++++ b/src/ast/BooleanConstraint.h +@@ -45,8 +45,6 @@ public: + truthValue = value; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/BranchInit.cpp b/src/ast/BranchInit.cpp +index 7a0a9c5..6813df7 100644 +--- a/src/ast/BranchInit.cpp ++++ b/src/ast/BranchInit.cpp +@@ -18,7 +18,7 @@ + namespace souffle::ast { + + BranchInit::BranchInit(QualifiedName name, VecOwn args, SrcLocation loc) +- : Term(NK_BranchInit, std::move(args), std::move(loc)), name(std::move(name)) {} ++ : Term(std::move(args), std::move(loc)), name(std::move(name)) {} + + void BranchInit::print(std::ostream& os) const { + os << tfm::format("$%s(%s)", name, join(args, ", ")); +@@ -33,7 +33,4 @@ BranchInit* BranchInit::cloning() const { + return new BranchInit(name, clone(args), getSrcLoc()); + } + +-bool BranchInit::classof(const Node* n) { +- return n->getKind() == NK_BranchInit; +-} + } // namespace souffle::ast +diff --git a/src/ast/BranchInit.h b/src/ast/BranchInit.h +index 9585b73..c948fea 100644 +--- a/src/ast/BranchInit.h ++++ b/src/ast/BranchInit.h +@@ -49,8 +49,6 @@ public: + this->name = name; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/BranchType.cpp b/src/ast/BranchType.cpp +index cb73677..1237314 100644 +--- a/src/ast/BranchType.cpp ++++ b/src/ast/BranchType.cpp +@@ -16,7 +16,7 @@ + namespace souffle::ast { + + BranchType::BranchType(QualifiedName name, VecOwn fields, SrcLocation loc) +- : Node(NK_BranchType, std::move(loc)), name(std::move(name)), fields(std::move(fields)) { ++ : Node(std::move(loc)), name(std::move(name)), fields(std::move(fields)) { + assert(allValidPtrs(this->fields)); + } + +@@ -25,7 +25,6 @@ std::vector BranchType::getFields() { + } + + void BranchType::print(std::ostream& os) const { +- printAnnotations(os); + os << tfm::format("%s {%s}", name, join(fields, ", ")); + } + +@@ -37,12 +36,4 @@ void BranchType::setFieldType(std::size_t idx, QualifiedName type) { + fields.at(idx)->setTypeName(std::move(type)); + } + +-bool BranchType::classof(const Node* n) { +- return n->getKind() == NK_BranchType; +-} +- +-bool BranchType::equal(const Node& /* other */) const { +- return false; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/BranchType.h b/src/ast/BranchType.h +index a69e4c9..ab86de5 100644 +--- a/src/ast/BranchType.h ++++ b/src/ast/BranchType.h +@@ -58,14 +58,10 @@ public: + /** Set field type */ + void setFieldType(std::size_t idx, QualifiedName type); + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + + private: +- bool equal(const Node& /* other */) const override; +- + BranchType* cloning() const override; + + private: +diff --git a/src/ast/Clause.cpp b/src/ast/Clause.cpp +index e682bac..818fa6b 100644 +--- a/src/ast/Clause.cpp ++++ b/src/ast/Clause.cpp +@@ -17,19 +17,14 @@ + + namespace souffle::ast { + +-Clause::Clause( +- NodeKind kind, Own head, VecOwn bodyLiterals, Own plan, SrcLocation loc) +- : Node(kind, std::move(loc)), head(std::move(head)), bodyLiterals(std::move(bodyLiterals)), ++Clause::Clause(Own head, VecOwn bodyLiterals, Own plan, SrcLocation loc) ++ : Node(std::move(loc)), head(std::move(head)), bodyLiterals(std::move(bodyLiterals)), + plan(std::move(plan)) { + assert(this->head != nullptr); + assert(allValidPtrs(this->bodyLiterals)); +- assert(kind >= NK_Clause && kind < NK_LastClause); + // Execution plan can be null + } + +-Clause::Clause(Own head, VecOwn bodyLiterals, Own plan, SrcLocation loc) +- : Clause(NK_Clause, std::move(head), std::move(bodyLiterals), std::move(plan), std::move(loc)) {} +- + Clause::Clause(Own head, SrcLocation loc) : Clause(std::move(head), {}, {}, std::move(loc)) {} + + Clause::Clause(QualifiedName name, SrcLocation loc) : Clause(mk(name), std::move(loc)) {} +@@ -74,7 +69,6 @@ Node::NodeVec Clause::getChildren() const { + } + + void Clause::print(std::ostream& os) const { +- printAnnotations(os); + os << *head; + if (!bodyLiterals.empty()) { + os << " :- \n " << join(bodyLiterals, ",\n "); +@@ -85,14 +79,6 @@ void Clause::print(std::ostream& os) const { + } + } + +-void Clause::printForDebugInfo(std::ostream& os) const { +- os << *head; +- if (!bodyLiterals.empty()) { +- os << " :- \n " << join(bodyLiterals, ",\n "); +- } +- os << "."; +-} +- + bool Clause::equal(const Node& node) const { + const auto& other = asAssert(node); + return equal_ptr(head, other.head) && equal_targets(bodyLiterals, other.bodyLiterals) && +@@ -100,8 +86,7 @@ bool Clause::equal(const Node& node) const { + } + + Clause* Clause::cloning() const { +- auto* cl = new Clause(clone(head), clone(bodyLiterals), clone(plan), getSrcLoc()); +- return cl; ++ return new Clause(clone(head), clone(bodyLiterals), clone(plan), getSrcLoc()); + } + + Clause* Clause::cloneHead() const { +@@ -109,13 +94,7 @@ Clause* Clause::cloneHead() const { + if (getExecutionPlan() != nullptr) { + myClone->setExecutionPlan(clone(getExecutionPlan())); + } +- myClone->setAnnotationsFrom(*this); + return myClone; + } + +-bool Clause::classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Clause && kind < NK_LastClause); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Clause.h b/src/ast/Clause.h +index b7b1eb7..5533335 100644 +--- a/src/ast/Clause.h ++++ b/src/ast/Clause.h +@@ -37,9 +37,6 @@ namespace souffle::ast { + */ + class Clause : public Node { + public: +- Clause(NodeKind kind, Own head, VecOwn bodyLiterals, Own plan = {}, +- SrcLocation loc = {}); +- + Clause(Own head, VecOwn bodyLiterals, Own plan = {}, SrcLocation loc = {}); + + Clause(Own head, SrcLocation loc = {}); +@@ -84,11 +81,6 @@ public: + + void apply(const NodeMapper& map) override; + +- /** Print this clause for `ram::DebugInfo` without annotations */ +- void printForDebugInfo(std::ostream& os) const; +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Component.cpp b/src/ast/Component.cpp +index 43a9cca..97129a0 100644 +--- a/src/ast/Component.cpp ++++ b/src/ast/Component.cpp +@@ -16,9 +16,6 @@ + #include + + namespace souffle::ast { +- +-Component::Component(SrcLocation loc) : Node(NK_Component, loc) {} +- + void Component::setComponentType(Own other) { + assert(other != nullptr); + componentType = std::move(other); +@@ -42,15 +39,6 @@ std::vector Component::getTypes() const { + return toPtrVector(types); + } + +-void Component::addLattice(Own t) { +- assert(t != nullptr); +- lattices.push_back(std::move(t)); +-} +- +-std::vector Component::getLattices() const { +- return toPtrVector(lattices); +-} +- + void Component::copyBaseComponents(const Component& other) { + baseComponents = clone(other.baseComponents); + } +@@ -130,7 +118,6 @@ void Component::print(std::ostream& os) const { + os << prefix << join(xs, sep) << "\n"; + }; + +- printAnnotations(os); + os << ".comp " << *componentType << " "; + show(baseComponents, ",", ": "); + os << "{\n"; +@@ -159,7 +146,6 @@ bool Component::equal(const Node& node) const { + equal_targets(relations, other.relations) && + equal_targets(clauses, other.clauses) && + equal_targets(directives, other.directives) && +- equal_targets(lattices, other.lattices) && + overrideRules != other.overrideRules); + // clang-format off + } +@@ -174,13 +160,8 @@ Component* Component::cloning() const { + res->relations = clone(relations); + res->clauses = clone(clauses); + res->directives = clone(directives); +- res->lattices = clone(lattices); + res->overrideRules = overrideRules; + return res; + } + +-bool Component::classof(const Node* n) { +- return n->getKind() == NK_Component; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Component.h b/src/ast/Component.h +index bd3fb0f..99befb8 100644 +--- a/src/ast/Component.h ++++ b/src/ast/Component.h +@@ -20,8 +20,6 @@ + #include "ast/ComponentInit.h" + #include "ast/ComponentType.h" + #include "ast/Directive.h" +-#include "ast/ItemContainer.h" +-#include "ast/Lattice.h" + #include "ast/Node.h" + #include "ast/Relation.h" + #include "ast/Type.h" +@@ -44,10 +42,8 @@ namespace souffle::ast { + * + * Component consists of type declaration, relations, rules, etc. + */ +-class Component : public Node, public ItemContainer { ++class Component : public Node { + public: +- Component(SrcLocation loc = {}); +- + /** Get component type */ + const ComponentType* getComponentType() const { + return componentType.get(); +@@ -63,48 +59,43 @@ public: + void addBaseComponent(Own component); + + /** Add type */ +- void addType(Own t) override; ++ void addType(Own t); + + /** Get types */ +- std::vector getTypes() const override; +- +- /** Add lattice */ +- void addLattice(Own lat) override; +- +- std::vector getLattices() const override; ++ std::vector getTypes() const; + + /** Copy base components */ + void copyBaseComponents(const Component& other); + + /** Add relation */ +- void addRelation(Own r) override; ++ void addRelation(Own r); + + /** Get relations */ +- std::vector getRelations() const override; ++ std::vector getRelations() const; + + /** Add clause */ +- void addClause(Own c) override; ++ void addClause(Own c); + + /** Get clauses */ +- std::vector getClauses() const override; ++ std::vector getClauses() const; + + /** Add directive */ +- void addDirective(Own directive) override; ++ void addDirective(Own directive); + + /** Get directive statements */ +- std::vector getDirectives() const override; ++ std::vector getDirectives() const; + + /** Add components */ +- void addComponent(Own c) override; ++ void addComponent(Own c); + + /** Get components */ +- std::vector getComponents() const override; ++ std::vector getComponents() const; + + /** Add instantiation */ +- void addInstantiation(Own i) override; ++ void addInstantiation(Own i); + + /** Get instantiation */ +- std::vector getInstantiations() const override; ++ std::vector getInstantiations() const; + + /** Add override */ + void addOverride(const std::string& name) { +@@ -118,8 +109,6 @@ public: + + void apply(const NodeMapper& mapper) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +@@ -140,9 +129,6 @@ private: + /** Types declarations */ + VecOwn types; + +- /** Types declarations */ +- VecOwn lattices; +- + /** Relations */ + VecOwn relations; + +diff --git a/src/ast/ComponentInit.cpp b/src/ast/ComponentInit.cpp +index 2bfd92a..0406977 100644 +--- a/src/ast/ComponentInit.cpp ++++ b/src/ast/ComponentInit.cpp +@@ -15,8 +15,7 @@ + namespace souffle::ast { + + ComponentInit::ComponentInit(std::string name, Own type, SrcLocation loc) +- : Node(NK_ComponentInit, std::move(loc)), instanceName(std::move(name)), +- componentType(std::move(type)) { ++ : Node(std::move(loc)), instanceName(std::move(name)), componentType(std::move(type)) { + assert(componentType); + } + +@@ -38,7 +37,6 @@ Node::NodeVec ComponentInit::getChildren() const { + } + + void ComponentInit::print(std::ostream& os) const { +- printAnnotations(os); + os << ".init " << instanceName << " = " << *componentType; + } + +@@ -50,9 +48,4 @@ bool ComponentInit::equal(const Node& node) const { + ComponentInit* ComponentInit::cloning() const { + return new ComponentInit(instanceName, clone(componentType), getSrcLoc()); + } +- +-bool ComponentInit::classof(const Node* n) { +- return n->getKind() == NK_ComponentInit; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/ComponentInit.h b/src/ast/ComponentInit.h +index af3e5c9..8a6fbaf 100644 +--- a/src/ast/ComponentInit.h ++++ b/src/ast/ComponentInit.h +@@ -55,8 +55,6 @@ public: + + void apply(const NodeMapper& mapper) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/ComponentType.cpp b/src/ast/ComponentType.cpp +index f831517..07236e4 100644 +--- a/src/ast/ComponentType.cpp ++++ b/src/ast/ComponentType.cpp +@@ -15,7 +15,7 @@ + namespace souffle::ast { + + ComponentType::ComponentType(std::string name, std::vector params, SrcLocation loc) +- : Node(NK_ComponentType, std::move(loc)), name(std::move(name)), typeParams(std::move(params)) {} ++ : Node(std::move(loc)), name(std::move(name)), typeParams(std::move(params)) {} + + void ComponentType::print(std::ostream& os) const { + os << name; +@@ -33,8 +33,4 @@ ComponentType* ComponentType::cloning() const { + return new ComponentType(name, typeParams, getSrcLoc()); + } + +-bool ComponentType::classof(const Node* n) { +- return n->getKind() == NK_ComponentType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/ComponentType.h b/src/ast/ComponentType.h +index 4d7e45b..92ff83b 100644 +--- a/src/ast/ComponentType.h ++++ b/src/ast/ComponentType.h +@@ -56,8 +56,6 @@ public: + typeParams = params; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Constant.cpp b/src/ast/Constant.cpp +index 04e7e1d..f3fbd0d 100644 +--- a/src/ast/Constant.cpp ++++ b/src/ast/Constant.cpp +@@ -12,10 +12,8 @@ + #include + + namespace souffle::ast { +-Constant::Constant(NodeKind kind, std::string value, SrcLocation loc) +- : Argument(kind, std::move(loc)), constant(std::move(value)) { +- assert(kind >= NK_Constant && kind < NK_LastConstant); +-}; ++Constant::Constant(std::string value, SrcLocation loc) ++ : Argument(std::move(loc)), constant(std::move(value)){}; + + void Constant::print(std::ostream& os) const { + os << getConstant(); +@@ -26,9 +24,4 @@ bool Constant::equal(const Node& node) const { + return constant == other.constant; + } + +-bool Constant::classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Constant && kind < NK_LastConstant); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Constant.h b/src/ast/Constant.h +index 80bef18..47f291f 100644 +--- a/src/ast/Constant.h ++++ b/src/ast/Constant.h +@@ -34,10 +34,8 @@ public: + return constant; + } + +- static bool classof(const Node*); +- + protected: +- Constant(NodeKind kind, std::string value, SrcLocation loc = {}); ++ Constant(std::string value, SrcLocation loc = {}); + + void print(std::ostream& os) const override; + +diff --git a/src/ast/Constraint.h b/src/ast/Constraint.h +index 0b0bb91..fe7b2cc 100644 +--- a/src/ast/Constraint.h ++++ b/src/ast/Constraint.h +@@ -27,15 +27,6 @@ namespace souffle::ast { + class Constraint : public Literal { + public: + using Literal::Literal; +- +- Constraint(NodeKind kind, SrcLocation loc = {}) : Literal(kind, loc) { +- assert(kind >= NK_Constraint && kind < NK_LastConstraint); +- } +- +- static bool classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Constraint && kind < NK_LastConstraint); +- } + }; + + } // namespace souffle::ast +diff --git a/src/ast/Counter.cpp b/src/ast/Counter.cpp +index 0f2fd91..a372e9a 100644 +--- a/src/ast/Counter.cpp ++++ b/src/ast/Counter.cpp +@@ -11,8 +11,6 @@ + + namespace souffle::ast { + +-Counter::Counter(SrcLocation loc) : Argument(NK_Counter, std::move(loc)) {} +- + void Counter::print(std::ostream& os) const { + os << "$"; + } +@@ -21,12 +19,4 @@ Counter* Counter::cloning() const { + return new Counter(getSrcLoc()); + } + +-bool Counter::classof(const Node* n) { +- return n->getKind() == NK_Counter; +-} +- +-bool Counter::equal(const Node&) const { +- return true; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Counter.h b/src/ast/Counter.h +index 0f5551d..aaac742 100644 +--- a/src/ast/Counter.h ++++ b/src/ast/Counter.h +@@ -29,16 +29,10 @@ class Counter : public Argument { + public: + using Argument::Argument; + +- Counter(SrcLocation = {}); +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + + private: +- bool equal(const Node&) const override; +- + Counter* cloning() const override; + }; + +diff --git a/src/ast/Directive.cpp b/src/ast/Directive.cpp +index bc3cba7..693faac 100644 +--- a/src/ast/Directive.cpp ++++ b/src/ast/Directive.cpp +@@ -26,7 +26,7 @@ std::ostream& operator<<(std::ostream& os, DirectiveType e) { + } + + Directive::Directive(DirectiveType type, QualifiedName name, SrcLocation loc) +- : Node(NK_Directive, std::move(loc)), type(type), name(std::move(name)) {} ++ : Node(std::move(loc)), type(type), name(std::move(name)) {} + + void Directive::setQualifiedName(QualifiedName name) { + this->name = std::move(name); +@@ -37,7 +37,6 @@ void Directive::addParameter(const std::string& key, std::string value) { + } + + void Directive::print(std::ostream& os) const { +- printAnnotations(os); + os << "." << type << " " << name; + if (!parameters.empty()) { + os << "(" << join(parameters, ",", [](std::ostream& out, const auto& arg) { +@@ -57,8 +56,4 @@ Directive* Directive::cloning() const { + return res; + } + +-bool Directive::classof(const Node* n) { +- return n->getKind() == NK_Directive; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Directive.h b/src/ast/Directive.h +index 360d0a3..94c03a6 100644 +--- a/src/ast/Directive.h ++++ b/src/ast/Directive.h +@@ -75,8 +75,6 @@ public: + return parameters; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/ExecutionOrder.cpp b/src/ast/ExecutionOrder.cpp +index 188a53f..3a3a1bd 100644 +--- a/src/ast/ExecutionOrder.cpp ++++ b/src/ast/ExecutionOrder.cpp +@@ -15,7 +15,7 @@ + namespace souffle::ast { + + ExecutionOrder::ExecutionOrder(ExecOrder order, SrcLocation loc) +- : Node(NK_ExecutionOrder, std::move(loc)), order(std::move(order)) {} ++ : Node(std::move(loc)), order(std::move(order)) {} + + void ExecutionOrder::print(std::ostream& out) const { + out << "(" << join(order) << ")"; +@@ -30,8 +30,4 @@ ExecutionOrder* ExecutionOrder::cloning() const { + return new ExecutionOrder(order, getSrcLoc()); + } + +-bool ExecutionOrder::classof(const Node* n) { +- return n->getKind() == NK_ExecutionOrder; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/ExecutionOrder.h b/src/ast/ExecutionOrder.h +index 31829c5..d5173d0 100644 +--- a/src/ast/ExecutionOrder.h ++++ b/src/ast/ExecutionOrder.h +@@ -39,8 +39,6 @@ public: + return order; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& out) const override; + +diff --git a/src/ast/ExecutionPlan.cpp b/src/ast/ExecutionPlan.cpp +index 219effe..0a888c8 100644 +--- a/src/ast/ExecutionPlan.cpp ++++ b/src/ast/ExecutionPlan.cpp +@@ -16,8 +16,6 @@ + + namespace souffle::ast { + +-ExecutionPlan::ExecutionPlan(SrcLocation loc) : Node(NK_ExecutionPlan, std::move(loc)) {} +- + /** Set execution order for a given rule version */ + void ExecutionPlan::setOrderFor(std::size_t version, Own plan) { + assert(plan != nullptr); +@@ -64,9 +62,4 @@ ExecutionPlan* ExecutionPlan::cloning() const { + } + return res.release(); + } +- +-bool ExecutionPlan::classof(const Node* n) { +- return n->getKind() == NK_ExecutionPlan; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/ExecutionPlan.h b/src/ast/ExecutionPlan.h +index 5ad3830..7350608 100644 +--- a/src/ast/ExecutionPlan.h ++++ b/src/ast/ExecutionPlan.h +@@ -39,8 +39,6 @@ class ExecutionPlan : public Node { + public: + using Node::Node; + +- ExecutionPlan(SrcLocation = {}); +- + /** Set execution order for a given rule version */ + void setOrderFor(std::size_t version, Own plan); + +@@ -51,8 +49,6 @@ public: + + NodeVec getChildren() const override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& out) const override; + +diff --git a/src/ast/FunctionalConstraint.cpp b/src/ast/FunctionalConstraint.cpp +index 3872014..e50328a 100644 +--- a/src/ast/FunctionalConstraint.cpp ++++ b/src/ast/FunctionalConstraint.cpp +@@ -18,12 +18,11 @@ + namespace souffle::ast { + + FunctionalConstraint::FunctionalConstraint(VecOwn keys, SrcLocation loc) +- : Constraint(NK_FunctionalConstraint, std::move(loc)), keys(std::move(keys)) { ++ : Constraint(std::move(loc)), keys(std::move(keys)) { + assert(allValidPtrs(this->keys)); + } + +-FunctionalConstraint::FunctionalConstraint(Own key, SrcLocation loc) +- : Constraint(NK_FunctionalConstraint, std::move(loc)) { ++FunctionalConstraint::FunctionalConstraint(Own key, SrcLocation loc) : Constraint(std::move(loc)) { + assert(key != nullptr); + keys.push_back(std::move(key)); + } +@@ -70,8 +69,4 @@ FunctionalConstraint* FunctionalConstraint::cloning() const { + return new FunctionalConstraint(clone(keys), getSrcLoc()); + } + +-bool FunctionalConstraint::classof(const Node* n) { +- return n->getKind() == NK_FunctionalConstraint; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/FunctionalConstraint.h b/src/ast/FunctionalConstraint.h +index be60d7e..758f2ef 100644 +--- a/src/ast/FunctionalConstraint.h ++++ b/src/ast/FunctionalConstraint.h +@@ -56,8 +56,6 @@ public: + + bool equivalentConstraint(const FunctionalConstraint& other) const; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Functor.h b/src/ast/Functor.h +index e9cd81d..9bafe2b 100644 +--- a/src/ast/Functor.h ++++ b/src/ast/Functor.h +@@ -28,12 +28,6 @@ namespace souffle::ast { + class Functor : public Term { + protected: + using Term::Term; +- +- Functor(NodeKind kind, SrcLocation loc = {}) : Term(kind, loc) { +- assert(kind > NK_FirstFunctor && kind < NK_LastFunctor); +- } +- +- static bool classof(const Node*); + }; + + } // namespace souffle::ast +diff --git a/src/ast/FunctorDeclaration.cpp b/src/ast/FunctorDeclaration.cpp +index 7a38072..380f5c3 100644 +--- a/src/ast/FunctorDeclaration.cpp ++++ b/src/ast/FunctorDeclaration.cpp +@@ -20,7 +20,7 @@ namespace souffle::ast { + + FunctorDeclaration::FunctorDeclaration( + std::string name, VecOwn params, Own returnType, bool stateful, SrcLocation loc) +- : Node(NK_FunctorDeclaration, std::move(loc)), name(std::move(name)), params(std::move(params)), ++ : Node(std::move(loc)), name(std::move(name)), params(std::move(params)), + returnType(std::move(returnType)), stateful(stateful) { + assert(this->name.length() > 0 && "functor name is empty"); + assert(allValidPtrs(this->params)); +@@ -28,8 +28,11 @@ FunctorDeclaration::FunctorDeclaration( + } + + void FunctorDeclaration::print(std::ostream& out) const { +- printAnnotations(out); +- tfm::format(out, ".functor %s(%s): %s", name, join(params, ","), returnType->getTypeName()); ++ auto convert = [&](Own const& attr) { ++ return attr->getName() + ": " + attr->getTypeName().toString(); ++ }; ++ ++ tfm::format(out, ".functor %s(%s): %s", name, join(map(params, convert), ","), returnType->getTypeName()); + if (stateful) { + out << " stateful"; + } +@@ -45,8 +48,4 @@ FunctorDeclaration* FunctorDeclaration::cloning() const { + return new FunctorDeclaration(name, clone(params), clone(returnType), stateful, getSrcLoc()); + } + +-bool FunctorDeclaration::classof(const Node* n) { +- return n->getKind() == NK_FunctorDeclaration; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/FunctorDeclaration.h b/src/ast/FunctorDeclaration.h +index e8ac183..f65f68c 100644 +--- a/src/ast/FunctorDeclaration.h ++++ b/src/ast/FunctorDeclaration.h +@@ -64,8 +64,6 @@ public: + return stateful; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& out) const override; + +diff --git a/src/ast/IntrinsicFunctor.cpp b/src/ast/IntrinsicFunctor.cpp +index 96feb84..19f6298 100644 +--- a/src/ast/IntrinsicFunctor.cpp ++++ b/src/ast/IntrinsicFunctor.cpp +@@ -16,7 +16,7 @@ + namespace souffle::ast { + + IntrinsicFunctor::IntrinsicFunctor(std::string op, VecOwn args, SrcLocation loc) +- : Functor(NK_IntrinsicFunctor, std::move(args), std::move(loc)), function(std::move(op)) {} ++ : Functor(std::move(args), std::move(loc)), function(std::move(op)) {} + + void IntrinsicFunctor::print(std::ostream& os) const { + if (isInfixFunctorOp(function)) { +@@ -41,8 +41,4 @@ IntrinsicFunctor* IntrinsicFunctor::cloning() const { + return new IntrinsicFunctor(function, clone(args), getSrcLoc()); + } + +-bool IntrinsicFunctor::classof(const Node* n) { +- return n->getKind() == NK_IntrinsicFunctor; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/IntrinsicFunctor.h b/src/ast/IntrinsicFunctor.h +index 7df8498..bf562ae 100644 +--- a/src/ast/IntrinsicFunctor.h ++++ b/src/ast/IntrinsicFunctor.h +@@ -34,12 +34,11 @@ class IntrinsicFunctor : public Functor { + public: + template + IntrinsicFunctor(std::string op, Operands&&... operands) +- : Functor(NK_IntrinsicFunctor, std::forward(operands)...), function(std::move(op)) {} ++ : Functor(std::forward(operands)...), function(std::move(op)) {} + + template + IntrinsicFunctor(SrcLocation loc, std::string op, Operands&&... operands) +- : Functor(NK_IntrinsicFunctor, std::move(loc), std::forward(operands)...), +- function(std::move(op)) {} ++ : Functor(std::move(loc), std::forward(operands)...), function(std::move(op)) {} + + IntrinsicFunctor(std::string op, VecOwn args, SrcLocation loc = {}); + +@@ -53,8 +52,6 @@ public: + function = std::move(functor); + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Literal.h b/src/ast/Literal.h +index 758b077..b7aea67 100644 +--- a/src/ast/Literal.h ++++ b/src/ast/Literal.h +@@ -30,15 +30,6 @@ namespace souffle::ast { + class Literal : public Node { + public: + using Node::Node; +- +- explicit Literal(NodeKind kind, SrcLocation loc = {}) : Node(kind, loc) { +- assert(kind >= NK_Literal && kind < NK_LastLiteral); +- } +- +- static bool classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Literal && kind < NK_LastLiteral); +- } + }; + + } // namespace souffle::ast +diff --git a/src/ast/Negation.cpp b/src/ast/Negation.cpp +index 5e29a02..ca702f9 100644 +--- a/src/ast/Negation.cpp ++++ b/src/ast/Negation.cpp +@@ -14,8 +14,7 @@ + + namespace souffle::ast { + +-Negation::Negation(Own atom, SrcLocation loc) +- : Literal(NK_Negation, std::move(loc)), atom(std::move(atom)) { ++Negation::Negation(Own atom, SrcLocation loc) : Literal(std::move(loc)), atom(std::move(atom)) { + assert(this->atom != nullptr); + } + +@@ -40,8 +39,4 @@ Negation* Negation::cloning() const { + return new Negation(clone(atom), getSrcLoc()); + } + +-bool Negation::classof(const Node* n) { +- return n->getKind() == NK_Negation; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Negation.h b/src/ast/Negation.h +index 5fe14d0..b87a036 100644 +--- a/src/ast/Negation.h ++++ b/src/ast/Negation.h +@@ -43,8 +43,6 @@ public: + + void apply(const NodeMapper& map) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/NilConstant.cpp b/src/ast/NilConstant.cpp +index 87a3e96..ca2ad5e 100644 +--- a/src/ast/NilConstant.cpp ++++ b/src/ast/NilConstant.cpp +@@ -10,14 +10,10 @@ + + namespace souffle::ast { + +-NilConstant::NilConstant(SrcLocation loc) : Constant(NK_NilConstant, "nil", std::move(loc)) {} ++NilConstant::NilConstant(SrcLocation loc) : Constant("nil", std::move(loc)) {} + + NilConstant* NilConstant::cloning() const { + return new NilConstant(getSrcLoc()); + } + +-bool NilConstant::classof(const Node* n) { +- return n->getKind() == NK_NilConstant; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/NilConstant.h b/src/ast/NilConstant.h +index 701d8d9..ebb9659 100644 +--- a/src/ast/NilConstant.h ++++ b/src/ast/NilConstant.h +@@ -31,8 +31,6 @@ class NilConstant : public Constant { + public: + NilConstant(SrcLocation loc = {}); + +- static bool classof(const Node*); +- + private: + NilConstant* cloning() const override; + }; +diff --git a/src/ast/Node.cpp b/src/ast/Node.cpp +index 18acab4..9fc128b 100644 +--- a/src/ast/Node.cpp ++++ b/src/ast/Node.cpp +@@ -6,12 +6,11 @@ + * - /licenses/SOUFFLE-UPL.txt + */ + #include "ast/Node.h" +- +-#include ++#include + #include + + namespace souffle::ast { +-Node::Node(NodeKind kind, SrcLocation loc) : Kind(kind), location(std::move(loc)) {} ++Node::Node(SrcLocation loc) : location(std::move(loc)) {} + + /** Set source location for the Node */ + void Node::setSrcLoc(SrcLocation l) { +@@ -23,25 +22,11 @@ bool Node::operator==(const Node& other) const { + return true; + } + +- if constexpr (/* we ignore annotations in equality test */ false) { +- const bool has_annotes = ((bool)annotations) == true && !annotations->empty(); +- const bool other_has_annotes = ((bool)other.annotations) == true && !other.annotations->empty(); +- if (has_annotes != other_has_annotes) { +- return false; +- } +- +- if (has_annotes && (*annotations != *(other.annotations))) { +- return false; +- } +- } +- +- return this->Kind == other.Kind && equal(other); ++ return typeid(*this) == typeid(*&other) && equal(other); + } + + Own Node::cloneImpl() const { +- auto Res = Own(cloning()); +- Res->setAnnotationsFrom(*this); +- return Res; ++ return Own(cloning()); + } + + /** Apply the mapper to all child nodes */ +@@ -60,176 +45,13 @@ std::ostream& operator<<(std::ostream& out, const Node& node) { + return out; + } + +-// bool Node::equal(const Node& other) const { +-// return this == &other; +-// } ++bool Node::equal(const Node&) const { ++ // FIXME: Change to this == &other? ++ return true; ++} + + Node::NodeVec Node::getChildren() const { + return {}; + } + +-Node::NodeKind Node::getKind() const { +- return Kind; +-} +- +-const SrcLocation& Node::getSrcLoc() const { +- return location; +-} +- +-std::string Node::extloc() const { +- return location.extloc(); +-} +- +-bool Node::operator!=(const Node& other) const { +- return !(*this == other); +-} +- +-AnnotationList& Node::ensureAnnotations() { +- if (!annotations) { +- annotations = std::make_unique(); +- } +- return *annotations; +-} +- +-void Node::addAnnotation(Annotation annote) { +- ensureAnnotations().emplace_back(std::move(annote)); +-} +- +-void Node::addAnnotations(AnnotationList annotes) { +- auto& annotations = ensureAnnotations(); +- annotations.splice(annotations.end(), annotes); +-} +- +-void Node::prependAnnotation(Annotation annote) { +- ensureAnnotations().emplace_front(std::move(annote)); +-} +- +-void Node::prependAnnotations(AnnotationList annotes) { +- auto& annotations = ensureAnnotations(); +- annotations.splice(annotations.begin(), annotes); +-} +- +-void Node::setAnnotationsFrom(const Node& other) { +- if (other.annotations && !other.annotations->empty()) { +- annotations = std::make_unique(*other.annotations); +- } else { +- annotations.release(); +- } +-} +- +-void Node::setAnnotations(AnnotationList annotes) { +- if (!annotes.empty()) { +- annotations = std::make_unique(std::move(annotes)); +- } else { +- annotations.release(); +- } +-} +- +-void Node::setAnnotations(std::unique_ptr annotes) { +- if (annotes && !annotes->empty()) { +- annotations = std::move(annotes); +- } else { +- annotations.release(); +- } +-} +- +-void Node::stealAnnotationsFrom(Node& other) { +- annotations = std::move(other.annotations); +-} +- +-void Node::eachAnnotation(const std::function& f) const { +- if (!annotations) { +- return; +- } +- +- for (const auto& annote : *annotations) { +- f(annote); +- } +-} +- +-void Node::eachAnnotation( +- const QualifiedName& label, const std::function& f) const { +- if (!annotations) { +- return; +- } +- +- for (const auto& annote : *annotations) { +- if (annote.getLabel() == label) { +- f(annote.getTokens()); +- } +- } +-} +- +-void Node::eachAnnotation(const QualifiedName& label, const std::function& f) const { +- if (!annotations) { +- return; +- } +- +- for (const auto& annote : *annotations) { +- if (annote.getLabel() == label) { +- f(annote); +- } +- } +-} +- +-void Node::eachAnnotation(const std::function& f) const { +- if (!annotations) { +- return; +- } +- +- for (const auto& annote : *annotations) { +- f(annote.getLabel(), annote.getTokens()); +- } +-} +- +-std::size_t Node::countAnnotations(const QualifiedName& label) const { +- if (!annotations) { +- return 0; +- } +- +- return std::count_if(annotations->cbegin(), annotations->cend(), +- [&](const Annotation& a) -> bool { return a.getLabel() == label; }); +-} +- +-const Annotation& Node::getAnnotation(const QualifiedName& label) const { +- if (!annotations) { +- throw std::out_of_range("No such annotation"); +- } +- +- auto it = std::find_if(annotations->cbegin(), annotations->cend(), +- [&](const Annotation& a) -> bool { return a.getLabel() == label; }); +- if (it == annotations->cend()) { +- throw std::out_of_range("No such annotation"); +- } +- +- return *it; +-} +- +-AnnotationList* Node::getAnnotations() { +- if (annotations) { +- return annotations.get(); +- } else { +- return nullptr; +- } +-} +- +-const AnnotationList* Node::getAnnotations() const { +- if (annotations) { +- return annotations.get(); +- } else { +- return nullptr; +- } +-} +- +-/// print annotations of this node, except documentation +-void Node::printAnnotations(std::ostream& os) const { +- eachAnnotation([&](const Annotation& annotation) { +- if (annotation.getKind() == Annotation::Kind::DocComment) { +- return; +- } +- annotation.printAsOuter(os); +- os << "\n"; +- }); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Node.h b/src/ast/Node.h +index bd215d8..570f850 100644 +--- a/src/ast/Node.h ++++ b/src/ast/Node.h +@@ -16,18 +16,13 @@ + + #pragma once + +-#include "Annotation.h" +-#include "QualifiedName.h" +-#include "TokenTree.h" + #include "parser/SrcLocation.h" + #include "souffle/utility/Iteration.h" + #include "souffle/utility/NodeMapperFwd.h" + #include "souffle/utility/Types.h" + #include "souffle/utility/VisitorFwd.h" +- + #include + #include +-#include + #include + #include + +@@ -57,129 +52,32 @@ struct ConstCaster { + */ + class Node { + public: +- /// LLVM-style RTTI +- /// +- /// Each class under the ast::Node hierarchy must appear here and must implement +- /// `static bool classof(const Node*)`. +- /// +- /// When class T is final, we must provide a single enum: +- /// +- /// ... +- /// NK_T, +- /// ... +- /// +- /// When class T is non-final, we must provide enums like this: +- /// +- /// NK_T, +- /// NK_Child1, +- /// ... +- /// NK_ChildN, +- /// NK_LastT +- /// +- /// +- // clang-format off +- enum NodeKind { +- NK_NONE, +- NK_Argument, +- NK_Constant, +- NK_NilConstant, +- NK_NumericConstant, +- NK_StringConstant, +- NK_LastConstant, +- +- NK_Counter, +- NK_ExecutionOrder, +- NK_ExecutionPlan, +- NK_IterationCounter, +- +- NK_Aggregator, +- NK_IntrinsicAggregator, +- NK_UserDefinedAggregator, +- NK_LastAggregator, +- +- NK_Term, +- NK_BranchInit, +- NK_FirstFunctor, +- NK_IntrinsicFunctor, +- NK_UserDefinedFunctor, +- NK_LastFunctor, +- NK_RecordInit, +- NK_LastTerm, +- +- NK_UnnamedVariable, +- NK_Variable, +- NK_TypeCast, +- NK_LastArgument, +- +- NK_Attribute, +- NK_BranchType, +- NK_Clause, +- NK_SubsumptiveClause, +- NK_LastClause, +- NK_Component, +- NK_ComponentInit, +- NK_ComponentType, +- NK_Directive, +- NK_FunctorDeclaration, +- NK_Lattice, +- +- NK_Literal, +- NK_Atom, +- +- NK_Constraint, +- NK_BinaryConstraint, +- NK_BooleanConstraint, +- NK_FunctionalConstraint, +- NK_LastConstraint, +- +- NK_Negation, +- NK_LastLiteral, +- +- NK_Pragma, +- NK_Program, +- NK_Relation, +- +- NK_Type, +- NK_AlgebraicDataType, +- NK_AliasType, +- NK_RecordType, +- NK_SubsetType, +- NK_UnionType, +- NK_LastType, +- }; +- // clang-format on +-private: +- const NodeKind Kind; +- +-public: +- explicit Node(NodeKind K, SrcLocation loc = {}); ++ Node(SrcLocation loc = {}); + virtual ~Node() = default; + // Make sure we don't accidentally copy/slice + Node(Node const&) = delete; + Node& operator=(Node const&) = delete; + +- NodeKind getKind() const; +- + /** Return source location of the Node */ +- const SrcLocation& getSrcLoc() const; ++ const SrcLocation& getSrcLoc() const { ++ return location; ++ } + + /** Set source location for the Node */ + void setSrcLoc(SrcLocation l); + + /** Return source location of the syntactic element */ +- std::string extloc() const; ++ std::string extloc() const { ++ return location.extloc(); ++ } + +- /** Equivalence check for two AST nodes +- * +- * Annotations are ignored. +- */ ++ /** Equivalence check for two AST nodes */ + bool operator==(const Node& other) const; + +- /** Inequality check for two AST nodes +- * +- * Annotations are ignored. +- */ +- bool operator!=(const Node& other) const; ++ /** Inequality check for two AST nodes */ ++ bool operator!=(const Node& other) const { ++ return !(*this == other); ++ } + + /** Create a clone (i.e. deep copy) of this node */ + Own cloneImpl() const; +@@ -200,63 +98,6 @@ public: + using ChildNodes = OwningTransformRange; + ChildNodes getChildNodes(); + +- /** Add an annotation to this object. */ +- void addAnnotation(Annotation); +- +- /** Add a list of annotations to this object. */ +- void addAnnotations(AnnotationList); +- +- void prependAnnotation(Annotation); +- +- void prependAnnotations(AnnotationList); +- +- /** Reset annotations using annotations from the other object */ +- void setAnnotationsFrom(const Node& other); +- +- /** Reset annotations using the given list */ +- void setAnnotations(AnnotationList); +- +- /** Reset annotations using the given list */ +- void setAnnotations(std::unique_ptr); +- +- /// Replace this node annotations with annotations stollen from other node. +- void stealAnnotationsFrom(Node& other); +- +- /** Enumerate annotations attached to this object */ +- void eachAnnotation(const std::function& f) const; +- +- /** Enumerate annotations attached to this object that have the given annotation */ +- void eachAnnotation(const QualifiedName& label, const std::function& f) const; +- +- /** Enumerate annotations attached to this object that have the given annotation */ +- void eachAnnotation(const QualifiedName& label, const std::function& f) const; +- +- /** Enumerate annotations attached to this object */ +- void eachAnnotation(const std::function& f) const; +- +- /** Return the number of attached annotations that have the given label */ +- std::size_t countAnnotations(const QualifiedName& label) const; +- +- /** Return a pointer to the list of annotations or nullptr */ +- AnnotationList* getAnnotations(); +- +- /** Return a pointer to the list of annotations or nullptr */ +- const AnnotationList* getAnnotations() const; +- +- /** +- * Return the first attached annotation that have the given label. +- * +- * Throws an exception if no such annotation exists. +- */ +- const Annotation& getAnnotation(const QualifiedName& label) const; +- +- /** +- * Print annotations, except doc comments +- * +- * Inner annotations are printed like outer annotations +- */ +- void printAnnotations(std::ostream& os) const; +- + /** Print node onto an output stream */ + friend std::ostream& operator<<(std::ostream& out, const Node& node); + +@@ -268,19 +109,13 @@ protected: + + private: + /** Abstract equality check for two AST nodes */ +- virtual bool equal(const Node& /* other */) const = 0; ++ virtual bool equal(const Node& /* other */) const; + + virtual Node* cloning() const = 0; + +- /** Ensure that this node has an annotations list and return it by reference. */ +- AnnotationList& ensureAnnotations(); +- + private: + /** Source location of a syntactic element */ + SrcLocation location; +- +- /** The attached annotations */ +- std::unique_ptr annotations; + }; + + } // namespace souffle::ast +diff --git a/src/ast/NumericConstant.cpp b/src/ast/NumericConstant.cpp +index ff6513a..802efbb 100644 +--- a/src/ast/NumericConstant.cpp ++++ b/src/ast/NumericConstant.cpp +@@ -13,14 +13,13 @@ + + namespace souffle::ast { + +-NumericConstant::NumericConstant(RamSigned value) +- : Constant(NK_NumericConstant, std::to_string(value)), fixedType(Type::Int) {} ++NumericConstant::NumericConstant(RamSigned value) : Constant(std::to_string(value)), fixedType(Type::Int) {} + + NumericConstant::NumericConstant(std::string constant, SrcLocation loc) +- : Constant(NK_NumericConstant, std::move(constant), std::move(loc)) {} ++ : Constant(std::move(constant), std::move(loc)) {} + + NumericConstant::NumericConstant(std::string constant, std::optional fixedType, SrcLocation loc) +- : Constant(NK_NumericConstant, std::move(constant), std::move(loc)), fixedType(fixedType) {} ++ : Constant(std::move(constant), std::move(loc)), fixedType(fixedType) {} + + bool NumericConstant::equal(const Node& node) const { + const auto& other = asAssert(node); +@@ -31,8 +30,4 @@ NumericConstant* NumericConstant::cloning() const { + return new NumericConstant(getConstant(), getFixedType(), getSrcLoc()); + } + +-bool NumericConstant::classof(const Node* n) { +- return n->getKind() == NK_NumericConstant; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/NumericConstant.h b/src/ast/NumericConstant.h +index 1f38f25..bf9751e 100644 +--- a/src/ast/NumericConstant.h ++++ b/src/ast/NumericConstant.h +@@ -45,8 +45,6 @@ public: + return fixedType; + } + +- static bool classof(const Node*); +- + private: + bool equal(const Node& node) const override; + +diff --git a/src/ast/Pragma.cpp b/src/ast/Pragma.cpp +index 0426890..4e6cae9 100644 +--- a/src/ast/Pragma.cpp ++++ b/src/ast/Pragma.cpp +@@ -14,7 +14,7 @@ + namespace souffle::ast { + + Pragma::Pragma(std::string key, std::string value, SrcLocation loc) +- : Node(NK_Pragma, std::move(loc)), key(std::move(key)), value(std::move(value)) {} ++ : Node(std::move(loc)), key(std::move(key)), value(std::move(value)) {} + + void Pragma::print(std::ostream& os) const { + os << ".pragma " << key << " " << value << "\n"; +@@ -29,8 +29,4 @@ Pragma* Pragma::cloning() const { + return new Pragma(key, value, getSrcLoc()); + } + +-bool Pragma::classof(const Node* n) { +- return n->getKind() == NK_Pragma; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Pragma.h b/src/ast/Pragma.h +index 989b1f5..b9340a2 100644 +--- a/src/ast/Pragma.h ++++ b/src/ast/Pragma.h +@@ -37,8 +37,6 @@ public: + return std::make_pair(key, value); + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Program.cpp b/src/ast/Program.cpp +index 4cda12e..d67db8f 100644 +--- a/src/ast/Program.cpp ++++ b/src/ast/Program.cpp +@@ -110,8 +110,6 @@ namespace souffle::ast { + + using souffle::clone; + +-Program::Program() : Node(NK_Program) {} +- + Program::RelationInfo clone(Program::RelationInfo const& x) { + return {clone(x.decls), clone(x.clauses), clone(x.directives)}; + } +@@ -120,10 +118,6 @@ std::vector Program::getTypes() const { + return toPtrVector(types); + } + +-std::vector Program::getLattices() const { +- return toPtrVector(lattices); +-} +- + std::vector Program::getRelations() const { + return toPtrVector(relations, &RelationInfo::decls); + } +@@ -237,15 +231,6 @@ void Program::addType(Own type) { + types.push_back(std::move(type)); + } + +-void Program::addLattice(Own lattice) { +- assert(lattice != nullptr); +- [[maybe_unused]] auto* existingLattice = getIf(getLattices(), [&](const Lattice* current) { +- return current->getQualifiedName() == lattice->getQualifiedName(); +- }); +- assert(existingLattice == nullptr && "Redefinition of lattice!"); +- lattices.push_back(std::move(lattice)); +-} +- + void Program::addPragma(Own pragma) { + assert(pragma && "NULL pragma"); + pragmas.push_back(std::move(pragma)); +@@ -259,7 +244,7 @@ void Program::addFunctorDeclaration(Own f) { + functors.push_back(std::move(f)); + } + +-std::vector Program::getInstantiations() const { ++std::vector Program::getComponentInstantiations() const { + return toPtrVector(instantiations); + } + +@@ -274,7 +259,6 @@ void Program::apply(const NodeMapper& map) { + mapAll(instantiations, map); + mapAll(functors, map); + mapAll(types, map); +- mapAll(lattices, map); + mapAll(relations, &RelationInfo::decls, map); + mapAll(relations, &RelationInfo::clauses, map); + mapAll(relations, &RelationInfo::directives, map); +@@ -287,7 +271,6 @@ Node::NodeVec Program::getChildren() const { + append(res, makePtrRange(instantiations)); + append(res, makePtrRange(functors)); + append(res, makePtrRange(types)); +- append(res, makePtrRange(lattices)); + append(res, relations, &RelationInfo::decls); + append(res, relations, &RelationInfo::clauses); + append(res, relations, &RelationInfo::directives); +@@ -303,13 +286,28 @@ void Program::print(std::ostream& os) const { + show(components); + show(instantiations); + show(types); +- show(lattices); + show(functors); + show(getRelations()); + show(getClauses(), "\n\n"); + show(getDirectives(), "\n\n"); + } + ++bool Program::equal(const Node& node) const { ++ const auto& other = asAssert(node); ++ // clang-format off ++ return equal_targets(pragmas, other.pragmas) && ++ equal_targets(components, other.components) && ++ equal_targets(instantiations, other.instantiations) && ++ equal_targets(functors, other.functors) && ++ equal_targets(types, other.types) && ++ equal_targets_map(relations, other.relations, [](auto& a, auto& b) { ++ return equal_targets(a.decls , b.decls ) && ++ equal_targets(a.clauses , b.clauses ) && ++ equal_targets(a.directives, b.directives); ++ }); ++ // clang-format on ++} ++ + void Program::addComponent(Own component) { + assert(component && "NULL component"); + components.push_back(std::move(component)); +@@ -326,25 +324,9 @@ Program* Program::cloning() const { + res->components = clone(components); + res->instantiations = clone(instantiations); + res->types = clone(types); +- res->lattices = clone(lattices); + res->functors = clone(functors); + res->relations = clone(relations); + return res; + } + +-bool Program::classof(const Node* n) { +- return n->getKind() == NK_Program; +-} +- +-bool Program::equal(const Node& node) const { +- const auto& other = asAssert(node); +- // strict ordered comparisons of vectors +- return equal_targets(types, other.types) && equal_targets(lattices, other.lattices) && +- equal_targets(functors, other.functors) && equal_targets(components, other.components) && +- equal_targets(instantiations, other.instantiations) && equal_targets(pragmas, other.pragmas) && +- equal_targets(getClauses(), other.getClauses()) && +- equal_targets(getRelations(), other.getRelations()) && +- equal_targets(getDirectives(), other.getDirectives()); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Program.h b/src/ast/Program.h +index 598b827..1316f0d 100644 +--- a/src/ast/Program.h ++++ b/src/ast/Program.h +@@ -16,13 +16,11 @@ + + #pragma once + +-#include "Global.h" + #include "ast/Clause.h" + #include "ast/Component.h" + #include "ast/ComponentInit.h" + #include "ast/Directive.h" + #include "ast/FunctorDeclaration.h" +-#include "ast/ItemContainer.h" + #include "ast/Node.h" + #include "ast/Pragma.h" + #include "ast/QualifiedName.h" +@@ -60,7 +58,7 @@ namespace souffle::ast { + * @class Program + * @brief The program class consists of relations, clauses and types. + */ +-class Program : public Node, public ItemContainer { ++class Program : public Node { + public: + // Requirements for storing top-level nodes: + // 1) remove-by-identity needs to be at least `O(lg n)` +@@ -87,9 +85,7 @@ public: + VecOwn directives; + }; + +- using RelationInfoMap = OrderedQualifiedNameMap; +- +- Program(); ++ using RelationInfoMap = std::map; + + RelationInfoMap& getRelationInfo() { + return relations; +@@ -110,13 +106,10 @@ public: + } + + /** Return types */ +- std::vector getTypes() const override; +- +- /** Return lattices */ +- std::vector getLattices() const override; ++ std::vector getTypes() const; + + /** Return relations */ +- std::vector getRelations() const override; ++ std::vector getRelations() const; + + /** Returns the first `Relation` declartion for a given name, if any */ + Relation* getRelation(QualifiedName const&) const; +@@ -132,7 +125,7 @@ public: + std::vector getRelationAll(QualifiedName const&) const; + + /** Return clauses */ +- std::vector getClauses() const override; ++ std::vector getClauses() const; + + /** Return clauses for a given relation */ + std::vector getClauses(QualifiedName const&) const; +@@ -146,7 +139,7 @@ public: + std::vector getFunctorDeclarations() const; + + /** Return relation directives */ +- std::vector getDirectives() const override; ++ std::vector getDirectives() const; + + /** Return relation directives for a relation */ + std::vector getDirectives(QualifiedName const&) const; +@@ -157,7 +150,7 @@ public: + } + + /** Add relation directive */ +- void addDirective(Own directive) override; ++ void addDirective(Own directive); + + /** Return pragma directives */ + const VecOwn& getPragmaDirectives() const { +@@ -165,7 +158,7 @@ public: + } + + /* Add relation */ +- void addRelation(Own relation) override; ++ void addRelation(Own relation); + + /** + * Remove a relation entirely, including the declaration(s), clauses, and directives. +@@ -181,16 +174,13 @@ public: + void removeRelation(Relation const&); + + /** Add a clause */ +- void addClause(Own clause) override; ++ void addClause(Own clause); + + // Common case helper. + void addClauses(VecOwn clauses); + + /** Add a type declaration */ +- void addType(Own type) override; +- +- /** Add a lattice declaration */ +- void addLattice(Own lattice) override; ++ void addType(Own type); + + /** + * Remove a clause by identity. The clause must be owned by the program. +@@ -213,28 +203,16 @@ public: + void removeDirective(const Directive&); + + /** Return components */ +- std::vector getComponents() const override; ++ std::vector getComponents() const; + + /** Return component instantiation */ +- std::vector getInstantiations() const override; +- +- void addPragma(Own pragma); +- +- void addFunctorDeclaration(Own functor); +- +- /** Add component */ +- void addComponent(Own component) override; +- +- /** Add component instantiation */ +- void addInstantiation(Own instantiation) override; ++ std::vector getComponentInstantiations() const; + + /** Remove components and components' instantiations */ + void clearComponents(); + + void apply(const NodeMapper& map) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +@@ -242,8 +220,18 @@ protected: + + friend class souffle::ParserDriver; + ++ void addPragma(Own pragma); ++ ++ void addFunctorDeclaration(Own functor); ++ ++ /** Add component */ ++ void addComponent(Own component); ++ ++ /** Add component instantiation */ ++ void addInstantiation(Own instantiation); ++ + private: +- bool equal(const Node&) const override; ++ bool equal(const Node& node) const override; + + Program* cloning() const override; + +@@ -251,9 +239,6 @@ private: + /** Program types */ + VecOwn types; + +- /** Program lattices */ +- VecOwn lattices; +- + /** Program relation declartions, clauses, and directives */ + RelationInfoMap relations; + +diff --git a/src/ast/QualifiedName.cpp b/src/ast/QualifiedName.cpp +index 38d71f3..192c90a 100644 +--- a/src/ast/QualifiedName.cpp ++++ b/src/ast/QualifiedName.cpp +@@ -8,165 +8,47 @@ + + #include "ast/QualifiedName.h" + #include "souffle/utility/StreamUtil.h" +-#include "souffle/utility/StringUtil.h" +- + #include +-#include +-#include +-#include + #include + #include +-#include + #include + + namespace souffle::ast { + +-/// Container of qualified names, provides interning by associating a unique +-/// numerical index to each qualified name. +-struct QNInterner { +-public: +- explicit QNInterner() { +- qualifiedNames.emplace_back(QualifiedNameData{{}, ""}); +- qualifiedNameToIndex.emplace("", 0); +- } +- +- /// Return the qualified name object for the given string. +- /// +- /// Each `.` character is treated as a separator. +- QualifiedName intern(std::string_view qn) { +- const auto It = qualifiedNameToIndex.find(qn); +- if (It != qualifiedNameToIndex.end()) { +- return QualifiedName{It->second}; +- } +- +- const uint32_t index = static_cast(qualifiedNames.size()); +- +- QualifiedNameData qndata{splitString(qn, '.'), std::string{qn}}; +- qualifiedNames.emplace_back(std::move(qndata)); +- qualifiedNameToIndex.emplace(qualifiedNames.back().qualified, index); +- +- return QualifiedName{index}; +- } +- +- /// Return the qualified name data object from the given index. +- const QualifiedNameData& at(uint32_t index) { +- return qualifiedNames.at(index); +- } +- +-private: +- /// Store the qualified name data of interned qualified names. +- std::deque qualifiedNames; +- +- /// Mapping from a qualified name string representation to its index in +- /// `qualifiedNames`. +- std::unordered_map qualifiedNameToIndex; +-}; +- +-namespace { +-/// The default qualified name interner instance. +-QNInterner Interner; +-} // namespace +- +-QualifiedName::QualifiedName() : index(0) {} +-QualifiedName::QualifiedName(uint32_t idx) : index(idx) {} +- +-const QualifiedNameData& QualifiedName::data() const { +- return Interner.at(index); ++QualifiedName::QualifiedName() {} ++QualifiedName::QualifiedName(std::string name) { ++ qualifiers.emplace_back(std::move(name)); + } ++QualifiedName::QualifiedName(const char* name) : QualifiedName(std::string(name)) {} ++QualifiedName::QualifiedName(std::vector qualifiers) : qualifiers(std::move(qualifiers)) {} + +-bool QualifiedName::operator==(const QualifiedName& other) const { +- return index == other.index; ++void QualifiedName::append(std::string name) { ++ qualifiers.push_back(std::move(name)); + } + +-bool QualifiedName::operator!=(const QualifiedName& other) const { +- return index != other.index; +-} +- +-void QualifiedName::append(const std::string& segment) { +- assert(segment.find('.') == std::string::npos); +- *this = Interner.intern(data().qualified + "." + segment); +-} +- +-void QualifiedName::prepend(const std::string& segment) { +- assert(segment.find('.') == std::string::npos); +- *this = Interner.intern(segment + "." + data().qualified); +-} +- +-void QualifiedName::append(const QualifiedName& rhs) { +- if (rhs.empty()) { +- return; +- } +- if (empty()) { +- index = rhs.index; +- return; +- } +- *this = Interner.intern(toString() + "." + rhs.toString()); ++void QualifiedName::prepend(std::string name) { ++ qualifiers.insert(qualifiers.begin(), std::move(name)); + } + + /** convert to a string separated by fullstop */ +-const std::string& QualifiedName::toString() const { +- return data().qualified; ++std::string QualifiedName::toString() const { ++ std::stringstream ss; ++ print(ss); ++ return ss.str(); + } + +-QualifiedName QualifiedName::fromString(std::string_view qname) { +- return Interner.intern(qname); +-} +- +-bool QualifiedName::lexicalLess(const QualifiedName& other) const { +- if (index == other.index) { +- return false; +- } +- return data().lexicalLess(other.data()); ++bool QualifiedName::operator<(const QualifiedName& other) const { ++ return std::lexicographical_compare( ++ qualifiers.begin(), qualifiers.end(), other.qualifiers.begin(), other.qualifiers.end()); + } + + void QualifiedName::print(std::ostream& out) const { +- out << toString(); ++ out << join(qualifiers, "."); + } + +-std::ostream& operator<<(std::ostream& out, const QualifiedName& qn) { +- out << qn.toString(); ++std::ostream& operator<<(std::ostream& out, const QualifiedName& id) { ++ id.print(out); + return out; + } + +-const std::vector& QualifiedName::getQualifiers() const { +- return data().segments; +-} +- +-uint32_t QualifiedName::getIndex() const { +- return index; +-} +- +-bool QualifiedName::empty() const { +- return index == 0; +-} +- +-QualifiedName QualifiedName::head() const { +- if (empty()) { +- return QualifiedName(); +- } +- return fromString(data().segments.front()); +-} +- +-QualifiedName QualifiedName::tail() const { +- const QualifiedNameData& qdata = data(); +- if (qdata.segments.size() < 2) { +- return QualifiedName(); +- } else { +- std::stringstream ss; +- ss << join(qdata.segments.begin() + 1, qdata.segments.end(), ""); +- return fromString(ss.str()); +- } +-} +- +-bool QualifiedNameData::lexicalLess(const QualifiedNameData& other) const { +- return std::lexicographical_compare( +- segments.begin(), segments.end(), other.segments.begin(), other.segments.end()); +-} +- +-QualifiedName operator+(const std::string& head, const QualifiedName& tail) { +- QualifiedName res = tail; +- res.prepend(head); +- return res; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/QualifiedName.h b/src/ast/QualifiedName.h +index 9692dfe..85f5e81 100644 +--- a/src/ast/QualifiedName.h ++++ b/src/ast/QualifiedName.h +@@ -16,151 +16,71 @@ + + #pragma once + +-#include + #include +-#include +-#include + #include +-#include +-#include + #include + + namespace souffle::ast { + +-struct QualifiedNameData { +- using Segment = std::string; +- std::vector segments; +- +- /// the whole qualified name with segments glued with dot +- std::string qualified; +- +- bool lexicalLess(const QualifiedNameData& other) const; +-}; +- +-struct QNInterner; +- + /** ++ * @class QualifiedName + * @brief Qualified Name class defines fully/partially qualified names + * to identify objects in components. + */ + class QualifiedName { +-private: +- friend struct QNInterner; +- explicit QualifiedName(uint32_t); +- + public: +- /** Build a QualifiedName from a dot-separated qualified name */ +- static QualifiedName fromString(std::string_view qualname); +- +- /// The empty qualified name + QualifiedName(); +- ++ QualifiedName(std::string name); ++ QualifiedName(const char* name); ++ QualifiedName(std::vector qualifiers); + QualifiedName(const QualifiedName&) = default; + QualifiedName(QualifiedName&&) = default; + QualifiedName& operator=(const QualifiedName&) = default; + QualifiedName& operator=(QualifiedName&&) = default; + +- const QualifiedNameData& data() const; ++ /** append qualifiers */ ++ void append(std::string name); + +- /** append one qualifier */ +- void append(const std::string& name); +- +- /** append another qualified name */ +- void append(const QualifiedName& name); +- +- /** prepend one qualifier */ +- void prepend(const std::string& name); ++ /** prepend qualifiers */ ++ void prepend(std::string name); + + /** check for emptiness */ +- bool empty() const; ++ bool empty() const { ++ return qualifiers.empty(); ++ } + + /** get qualifiers */ +- const std::vector& getQualifiers() const; ++ const std::vector& getQualifiers() const { ++ return qualifiers; ++ } + + /** convert to a string separated by fullstop */ +- const std::string& toString() const; +- +- QualifiedName head() const; ++ std::string toString() const; + +- QualifiedName tail() const; +- +- bool operator==(const QualifiedName& other) const; ++ bool operator==(const QualifiedName& other) const { ++ return qualifiers == other.qualifiers; ++ } + +- bool operator!=(const QualifiedName& other) const; ++ bool operator!=(const QualifiedName& other) const { ++ return !(*this == other); ++ } + +- /// Lexicographic less comparison. +- /// +- /// We don't offer `operator<` because it's a costly operation +- /// that should only be used when ordering is required. +- /// +- /// See type definitions of containers below. +- bool lexicalLess(const QualifiedName& other) const; ++ bool operator<(const QualifiedName& other) const; + + /** print qualified name */ + void print(std::ostream& out) const; + + friend std::ostream& operator<<(std::ostream& out, const QualifiedName& id); + +- /// Return the unique identifier of the interned qualified name. +- uint32_t getIndex() const; +- + private: +- /// index of this qualified name in the qualified-name interner +- uint32_t index; ++ /* array of name qualifiers */ ++ std::vector qualifiers; + }; + +-/// Return the qualified name by the adding prefix segment in head of the qualified name. +-QualifiedName operator+(const std::string& head, const QualifiedName& tail); +- +-struct OrderedQualifiedNameLess { +- bool operator()(const QualifiedName& lhs, const QualifiedName& rhs) const { +- return lhs.lexicalLess(rhs); +- } +-}; +- +-struct UnorderedQualifiedNameLess { +- bool operator()(const QualifiedName& lhs, const QualifiedName& rhs) const { +- return lhs.getIndex() < rhs.getIndex(); +- } +-}; +- +-struct QualifiedNameHash { +- std::size_t operator()(const QualifiedName& qn) const { +- return static_cast(qn.getIndex()); +- } +-}; +- +-/// a map from qualified name to T where qualified name keys are ordered in +-/// lexicographic order. +-template +-using OrderedQualifiedNameMap = std::map; +- +-/// a map from qualified name to T where qualified name keys are not ordered in +-/// any deterministic order. +-template +-using UnorderedQualifiedNameMap = std::unordered_map; +- +-/// a multi-map from qualified name to T where qualified name keys are not ordered in +-/// any deterministic order. +-template +-using UnorderedQualifiedNameMultimap = std::unordered_multimap; +- +-/// an ordered set of qualified name ordered in lexicographic order. +-using OrderedQualifiedNameSet = std::set; +- +-/// an unordered set of qualified name. +-using UnorderedQualifiedNameSet = std::unordered_set; +- +-template +-OrderedQualifiedNameSet orderedQualifiedNameSet(const Container& cont) { +- return OrderedQualifiedNameSet(cont.cbegin(), cont.cend()); ++inline QualifiedName operator+(const std::string& name, const QualifiedName& id) { ++ QualifiedName res = id; ++ res.prepend(name); ++ return res; + } + + } // namespace souffle::ast +- +-template <> +-struct std::hash { +- std::size_t operator()(const souffle::ast::QualifiedName& qn) const noexcept { +- return static_cast(qn.getIndex()); +- } +-}; +diff --git a/src/ast/RecordInit.cpp b/src/ast/RecordInit.cpp +index f3ee050..2881b62 100644 +--- a/src/ast/RecordInit.cpp ++++ b/src/ast/RecordInit.cpp +@@ -14,7 +14,7 @@ + + namespace souffle::ast { + RecordInit::RecordInit(VecOwn operands, SrcLocation loc) +- : Term(NK_RecordInit, std::move(operands), std::move(loc)) {} ++ : Term(std::move(operands), std::move(loc)) {} + + void RecordInit::print(std::ostream& os) const { + os << "[" << join(args) << "]"; +@@ -23,9 +23,4 @@ void RecordInit::print(std::ostream& os) const { + RecordInit* RecordInit::cloning() const { + return new RecordInit(clone(args), getSrcLoc()); + } +- +-bool RecordInit::classof(const Node* n) { +- return n->getKind() == NK_RecordInit; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/RecordInit.h b/src/ast/RecordInit.h +index 648eb4d..c9196ba 100644 +--- a/src/ast/RecordInit.h ++++ b/src/ast/RecordInit.h +@@ -32,8 +32,6 @@ class RecordInit : public Term { + public: + RecordInit(VecOwn operands = {}, SrcLocation loc = {}); + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/RecordType.cpp b/src/ast/RecordType.cpp +index de31aa2..6ee5cda 100644 +--- a/src/ast/RecordType.cpp ++++ b/src/ast/RecordType.cpp +@@ -17,7 +17,7 @@ + + namespace souffle::ast { + RecordType::RecordType(QualifiedName name, VecOwn fields, SrcLocation loc) +- : Type(NK_RecordType, std::move(name), std::move(loc)), fields(std::move(fields)) { ++ : Type(std::move(name), std::move(loc)), fields(std::move(fields)) { + assert(allValidPtrs(this->fields)); + } + +@@ -34,7 +34,6 @@ void RecordType::setFieldType(std::size_t idx, QualifiedName type) { + } + + void RecordType::print(std::ostream& os) const { +- printAnnotations(os); + os << tfm::format(".type %s = [%s]", getQualifiedName(), join(fields, ", ")); + } + +@@ -47,8 +46,4 @@ RecordType* RecordType::cloning() const { + return new RecordType(getQualifiedName(), clone(fields), getSrcLoc()); + } + +-bool RecordType::classof(const Node* n) { +- return n->getKind() == NK_RecordType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/RecordType.h b/src/ast/RecordType.h +index 01e7b59..cfdc405 100644 +--- a/src/ast/RecordType.h ++++ b/src/ast/RecordType.h +@@ -45,8 +45,6 @@ public: + /** Set field type */ + void setFieldType(std::size_t idx, QualifiedName type); + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Relation.cpp b/src/ast/Relation.cpp +index 010960d..8f790c8 100644 +--- a/src/ast/Relation.cpp ++++ b/src/ast/Relation.cpp +@@ -17,10 +17,7 @@ + + namespace souffle::ast { + +-Relation::Relation(SrcLocation loc) : Node(NK_Relation, loc) {} +- +-Relation::Relation(QualifiedName name, SrcLocation loc) +- : Node(NK_Relation, std::move(loc)), name(std::move(name)) {} ++Relation::Relation(QualifiedName name, SrcLocation loc) : Node(std::move(loc)), name(std::move(name)) {} + + void Relation::setQualifiedName(QualifiedName n) { + name = std::move(n); +@@ -53,25 +50,17 @@ void Relation::apply(const NodeMapper& map) { + mapAll(attributes, map); + } + +-bool Relation::classof(const Node* n) { +- return n->getKind() == NK_Relation; +-} +- + Node::NodeVec Relation::getChildren() const { + auto rn = makePtrRange(attributes); + return {rn.begin(), rn.end()}; + } + + void Relation::print(std::ostream& os) const { +- printAnnotations(os); + os << ".decl " << getQualifiedName() << "(" << join(attributes, ", ") << ")" << join(qualifiers, " ") + << " " << representation; + if (!functionalDependencies.empty()) { + os << " choice-domain " << join(functionalDependencies, ", "); + } +- if (isDeltaDebug) { +- os << " delta_debug(" << isDeltaDebug.value() << ")"; +- } + } + + bool Relation::equal(const Node& node) const { +@@ -79,7 +68,7 @@ bool Relation::equal(const Node& node) const { + return name == other.name && equal_targets(attributes, other.attributes) && + qualifiers == other.qualifiers && + equal_targets(functionalDependencies, other.functionalDependencies) && +- representation == other.representation && isDeltaDebug == other.isDeltaDebug; ++ representation == other.representation; + } + + Relation* Relation::cloning() const { +@@ -88,12 +77,7 @@ Relation* Relation::cloning() const { + res->qualifiers = qualifiers; + res->functionalDependencies = clone(functionalDependencies); + res->representation = representation; +- res->isDeltaDebug = isDeltaDebug; + return res; + } + +-RelationSet orderedRelationSet(const UnorderedRelationSet& cont) { +- return RelationSet(cont.cbegin(), cont.cend()); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Relation.h b/src/ast/Relation.h +index 978e2b3..9a0ae21 100644 +--- a/src/ast/Relation.h ++++ b/src/ast/Relation.h +@@ -35,7 +35,7 @@ namespace souffle::ast { + */ + class Relation : public Node { + public: +- Relation(SrcLocation loc = {}); ++ Relation() = default; + Relation(QualifiedName name, SrcLocation loc = {}); + + /** Get qualified relation name */ +@@ -54,15 +54,6 @@ public: + return attributes.size(); + } + +- /** Return the arity of this relation */ +- std::size_t getAuxiliaryArity() const { +- std::size_t arity = 0; +- for (const auto& a : attributes) { +- arity += a->getIsLattice() ? 1 : 0; +- } +- return arity; +- } +- + /** Set relation attributes */ + void setAttributes(VecOwn attrs); + +@@ -106,16 +97,6 @@ public: + + void apply(const NodeMapper& map) override; + +- void setIsDeltaDebug(QualifiedName rel) { +- isDeltaDebug = rel; +- } +- +- std::optional getIsDeltaDebug() const { +- return isDeltaDebug; +- } +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +@@ -141,8 +122,6 @@ private: + + /** Datastructure to use for this relation */ + RelationRepresentation representation{RelationRepresentation::DEFAULT}; +- +- std::optional isDeltaDebug; + }; + + /** +@@ -155,16 +134,7 @@ private: + struct NameComparison { + bool operator()(const Relation* x, const Relation* y) const { + if (x != nullptr && y != nullptr) { +- return x->getQualifiedName().lexicalLess(y->getQualifiedName()); +- } +- return y != nullptr; +- } +-}; +- +-struct UnorderedNameComparison { +- bool operator()(const Relation* x, const Relation* y) const { +- if (x != nullptr && y != nullptr) { +- return x->getQualifiedName().getIndex() < y->getQualifiedName().getIndex(); ++ return x->getQualifiedName() < y->getQualifiedName(); + } + return y != nullptr; + } +@@ -172,21 +142,5 @@ struct UnorderedNameComparison { + + /** Relation set */ + using RelationSet = std::set; +-using UnorderedRelationSet = std::set; +- +-/// Return an unordered set of relations corresponding to the given relations. +-template +-UnorderedRelationSet unorderedRelationSet(const Container& cont) { +- return UnorderedRelationSet(cont.cbegin(), cont.cend()); +-} +- +-/// Return an unordered set of relations corresponding to the given relations. +-template +-UnorderedRelationSet unorderedRelationSet(Container& cont) { +- return UnorderedRelationSet(cont.begin(), cont.end()); +-} +- +-/// Return an ordered set of relations corresponding to the given relations. +-RelationSet orderedRelationSet(const UnorderedRelationSet& cont); + + } // namespace souffle::ast +diff --git a/src/ast/StringConstant.cpp b/src/ast/StringConstant.cpp +index 4c829c8..a056286 100644 +--- a/src/ast/StringConstant.cpp ++++ b/src/ast/StringConstant.cpp +@@ -13,7 +13,7 @@ + namespace souffle::ast { + + StringConstant::StringConstant(std::string value, SrcLocation loc) +- : Constant(NK_StringConstant, std::move(value), std::move(loc)) {} ++ : Constant(std::move(value), std::move(loc)) {} + + void StringConstant::print(std::ostream& os) const { + os << "\"" << getConstant() << "\""; +@@ -23,8 +23,4 @@ StringConstant* StringConstant::cloning() const { + return new StringConstant(getConstant(), getSrcLoc()); + } + +-bool StringConstant::classof(const Node* n) { +- return n->getKind() == NK_StringConstant; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/StringConstant.h b/src/ast/StringConstant.h +index 4209c52..d62d1db 100644 +--- a/src/ast/StringConstant.h ++++ b/src/ast/StringConstant.h +@@ -30,9 +30,6 @@ namespace souffle::ast { + class StringConstant : public Constant { + public: + explicit StringConstant(std::string value, SrcLocation loc = {}); +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/SubsetType.cpp b/src/ast/SubsetType.cpp +index b74ff62..e3e6e32 100644 +--- a/src/ast/SubsetType.cpp ++++ b/src/ast/SubsetType.cpp +@@ -14,10 +14,9 @@ + namespace souffle::ast { + + SubsetType::SubsetType(QualifiedName name, QualifiedName baseTypeName, SrcLocation loc) +- : Type(NK_SubsetType, std::move(name), std::move(loc)), baseType(std::move(baseTypeName)) {} ++ : Type(std::move(name), std::move(loc)), baseType(std::move(baseTypeName)) {} + + void SubsetType::print(std::ostream& os) const { +- printAnnotations(os); + os << ".type " << getQualifiedName() << " <: " << getBaseType(); + } + +@@ -30,8 +29,4 @@ SubsetType* SubsetType::cloning() const { + return new SubsetType(getQualifiedName(), getBaseType(), getSrcLoc()); + } + +-bool SubsetType::classof(const Node* n) { +- return n->getKind() == NK_SubsetType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/SubsetType.h b/src/ast/SubsetType.h +index f1e5d8a..7a816af 100644 +--- a/src/ast/SubsetType.h ++++ b/src/ast/SubsetType.h +@@ -44,8 +44,6 @@ public: + baseType = type; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/SubsumptiveClause.cpp b/src/ast/SubsumptiveClause.cpp +index f85214d..d09bcf2 100644 +--- a/src/ast/SubsumptiveClause.cpp ++++ b/src/ast/SubsumptiveClause.cpp +@@ -19,8 +19,7 @@ namespace souffle::ast { + + SubsumptiveClause::SubsumptiveClause( + Own head, VecOwn bodyLiterals, Own plan, SrcLocation loc) +- : Clause(NK_SubsumptiveClause, std::move(head), std::move(bodyLiterals), std::move(plan), +- std::move(loc)) {} ++ : Clause(std::move(head), std::move(bodyLiterals), std::move(plan), std::move(loc)) {} + + SubsumptiveClause::SubsumptiveClause(Own head, SrcLocation loc) + : SubsumptiveClause(std::move(head), {}, {}, std::move(loc)) {} +@@ -34,7 +33,6 @@ void SubsumptiveClause::addToBodyFront(Own literal) { + } + + void SubsumptiveClause::print(std::ostream& os) const { +- printAnnotations(os); + os << *bodyLiterals[0]; + os << " <= "; + os << *bodyLiterals[1]; +@@ -52,8 +50,7 @@ void SubsumptiveClause::print(std::ostream& os) const { + } + + SubsumptiveClause* SubsumptiveClause::cloning() const { +- auto cl = new SubsumptiveClause(clone(head), clone(bodyLiterals), clone(plan), getSrcLoc()); +- return cl; ++ return new SubsumptiveClause(clone(head), clone(bodyLiterals), clone(plan), getSrcLoc()); + } + + Clause* SubsumptiveClause::cloneHead() const { +@@ -61,12 +58,7 @@ Clause* SubsumptiveClause::cloneHead() const { + if (getExecutionPlan() != nullptr) { + myClone->setExecutionPlan(clone(getExecutionPlan())); + } +- myClone->setAnnotationsFrom(*this); + return myClone; + } + +-bool SubsumptiveClause::classof(const Node* n) { +- return n->getKind() == NK_SubsumptiveClause; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/SubsumptiveClause.h b/src/ast/SubsumptiveClause.h +index 5c38f4f..06c8230 100644 +--- a/src/ast/SubsumptiveClause.h ++++ b/src/ast/SubsumptiveClause.h +@@ -49,8 +49,6 @@ public: + + Clause* cloneHead() const override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Term.cpp b/src/ast/Term.cpp +index 87abbf7..2b60396 100644 +--- a/src/ast/Term.cpp ++++ b/src/ast/Term.cpp +@@ -14,10 +14,8 @@ + + namespace souffle::ast { + +-Term::Term(NodeKind kind, VecOwn operands, SrcLocation loc) +- : Argument(kind, std::move(loc)), args(std::move(operands)) { ++Term::Term(VecOwn operands, SrcLocation loc) : Argument(std::move(loc)), args(std::move(operands)) { + assert(allValidPtrs(args)); +- assert(kind >= NK_Term && kind < NK_LastTerm); + } + + std::vector Term::getArguments() const { +@@ -45,9 +43,4 @@ bool Term::equal(const Node& node) const { + return equal_targets(args, other.args); + } + +-bool Term::classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Term && kind < NK_LastTerm); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Term.h b/src/ast/Term.h +index 25c35f6..d289605 100644 +--- a/src/ast/Term.h ++++ b/src/ast/Term.h +@@ -32,13 +32,13 @@ namespace souffle::ast { + class Term : public Argument { + protected: + template +- Term(NodeKind kind, Operands&&... operands) : Term(kind, {}, std::forward(operands)...) {} ++ Term(Operands&&... operands) : Term({}, std::forward(operands)...) {} + + template +- Term(NodeKind kind, SrcLocation loc, Operands&&... operands) +- : Term(kind, asVec(std::forward(operands)...), std::move(loc)) {} ++ Term(SrcLocation loc, Operands&&... operands) ++ : Term(asVec(std::forward(operands)...), std::move(loc)) {} + +- Term(NodeKind kind, VecOwn operands, SrcLocation loc = {}); ++ Term(VecOwn operands, SrcLocation loc = {}); + + public: + /** Get arguments */ +@@ -51,15 +51,16 @@ public: + + bool equal(const Node& node) const override; + +- static bool classof(const Node*); +- + private: + NodeVec getChildren() const override; + + template + static VecOwn asVec(Operands... ops) { ++ Own ary[] = {std::move(ops)...}; + VecOwn xs; +- (xs.emplace_back(std::move(std::forward(ops))), ...); ++ for (auto&& x : ary) { ++ xs.push_back(std::move(x)); ++ } + return xs; + } + +diff --git a/src/ast/TranslationUnit.cpp b/src/ast/TranslationUnit.cpp +index 77a3c5f..a2975b6 100644 +--- a/src/ast/TranslationUnit.cpp ++++ b/src/ast/TranslationUnit.cpp +@@ -8,7 +8,6 @@ + + #include "ast/TranslationUnit.h" + #include "Global.h" +-#include "ast/Program.h" + #include "ast/analysis/PrecedenceGraph.h" + #include "ast/analysis/SCCGraph.h" + #include "reports/DebugReport.h" +@@ -18,13 +17,14 @@ namespace souffle::ast { + + /** get analysis: analysis is generated on the fly if not present */ + void TranslationUnit::logAnalysis(Analysis& analysis) const { +- if (!global().config().has("debug-report")) return; ++ if (!Global::config().has("debug-report")) return; + + std::string name = analysis.getName(); + if (as(analysis) || as(analysis)) { +- debugReport.addSection(DebugReportSection(name, "Ast Analysis [" + name + "]", {}, toHtml(analysis))); ++ debugReport.addSection( ++ DebugReportSection(name, "Ast Analysis [" + name + "]", {}, toString(analysis))); + } else { +- debugReport.addSection(name, "Ast Analysis [" + name + "]", toHtml(analysis)); ++ debugReport.addSection(name, "Ast Analysis [" + name + "]", toString(analysis)); + } + } + +diff --git a/src/ast/TranslationUnit.h b/src/ast/TranslationUnit.h +index 21e21ca..d3aeea7 100644 +--- a/src/ast/TranslationUnit.h ++++ b/src/ast/TranslationUnit.h +@@ -16,11 +16,12 @@ + + #pragma once + +-#include "Program.h" + #include "TranslationUnitBase.h" + + namespace souffle::ast { + ++class Program; ++ + /** + * @class TranslationUnit + * @brief Translation unit class for the translation pipeline +diff --git a/src/ast/Type.cpp b/src/ast/Type.cpp +index 6ddc07a..2ec7f34 100644 +--- a/src/ast/Type.cpp ++++ b/src/ast/Type.cpp +@@ -11,18 +11,10 @@ + + namespace souffle::ast { + +-Type::Type(NodeKind kind, QualifiedName name, SrcLocation loc) +- : Node(kind, std::move(loc)), name(std::move(name)) { +- assert(kind > NK_Type && kind < NK_LastType); +-} ++Type::Type(QualifiedName name, SrcLocation loc) : Node(std::move(loc)), name(std::move(name)) {} + + void Type::setQualifiedName(QualifiedName name) { + this->name = std::move(name); + } + +-bool Type::classof(const Node* n) { +- const NodeKind kind = n->getKind(); +- return (kind >= NK_Type && kind < NK_LastType); +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Type.h b/src/ast/Type.h +index 066241c..24c82a3 100644 +--- a/src/ast/Type.h ++++ b/src/ast/Type.h +@@ -28,7 +28,7 @@ namespace souffle::ast { + */ + class Type : public Node { + public: +- Type(NodeKind kind, QualifiedName name = {}, SrcLocation loc = {}); ++ Type(QualifiedName name = {}, SrcLocation loc = {}); + + /** Return type name */ + const QualifiedName& getQualifiedName() const { +@@ -38,8 +38,6 @@ public: + /** Set type name */ + void setQualifiedName(QualifiedName name); + +- static bool classof(const Node*); +- + private: + /** type name */ + QualifiedName name; +diff --git a/src/ast/TypeCast.cpp b/src/ast/TypeCast.cpp +index 9e96528..a87d248 100644 +--- a/src/ast/TypeCast.cpp ++++ b/src/ast/TypeCast.cpp +@@ -17,7 +17,7 @@ + namespace souffle::ast { + + TypeCast::TypeCast(Own value, QualifiedName type, SrcLocation loc) +- : Argument(NK_TypeCast, std::move(loc)), value(std::move(value)), type(std::move(type)) { ++ : Argument(std::move(loc)), value(std::move(value)), type(std::move(type)) { + assert(this->value != nullptr); + } + +@@ -47,9 +47,4 @@ bool TypeCast::equal(const Node& node) const { + TypeCast* TypeCast::cloning() const { + return new TypeCast(clone(value), type, getSrcLoc()); + } +- +-bool TypeCast::classof(const Node* n) { +- return n->getKind() == NK_TypeCast; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/TypeCast.h b/src/ast/TypeCast.h +index eb51699..18fe62a 100644 +--- a/src/ast/TypeCast.h ++++ b/src/ast/TypeCast.h +@@ -47,8 +47,6 @@ public: + + void apply(const NodeMapper& map) override; + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/UnionType.cpp b/src/ast/UnionType.cpp +index a57dd6a..971b90f 100644 +--- a/src/ast/UnionType.cpp ++++ b/src/ast/UnionType.cpp +@@ -14,7 +14,7 @@ + + namespace souffle::ast { + UnionType::UnionType(QualifiedName name, std::vector types, SrcLocation loc) +- : Type(NK_UnionType, std::move(name), std::move(loc)), types(std::move(types)) {} ++ : Type(std::move(name), std::move(loc)), types(std::move(types)) {} + + void UnionType::add(QualifiedName type) { + types.push_back(std::move(type)); +@@ -25,7 +25,6 @@ void UnionType::setType(std::size_t idx, QualifiedName type) { + } + + void UnionType::print(std::ostream& os) const { +- printAnnotations(os); + os << ".type " << getQualifiedName() << " = " << join(types, " | "); + } + +@@ -38,8 +37,4 @@ UnionType* UnionType::cloning() const { + return new UnionType(getQualifiedName(), types, getSrcLoc()); + } + +-bool UnionType::classof(const Node* n) { +- return n->getKind() == NK_UnionType; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/UnionType.h b/src/ast/UnionType.h +index e652eec..c19b890 100644 +--- a/src/ast/UnionType.h ++++ b/src/ast/UnionType.h +@@ -55,8 +55,6 @@ public: + /** Set type */ + void setType(std::size_t idx, QualifiedName type); + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/UnnamedVariable.cpp b/src/ast/UnnamedVariable.cpp +index 3edc2f0..d3cd866 100644 +--- a/src/ast/UnnamedVariable.cpp ++++ b/src/ast/UnnamedVariable.cpp +@@ -11,8 +11,6 @@ + + namespace souffle::ast { + +-UnnamedVariable::UnnamedVariable(SrcLocation loc) : Argument(NK_UnnamedVariable, loc) {} +- + void UnnamedVariable::print(std::ostream& os) const { + os << "_"; + } +@@ -21,12 +19,4 @@ UnnamedVariable* UnnamedVariable::cloning() const { + return new UnnamedVariable(getSrcLoc()); + } + +-bool UnnamedVariable::classof(const Node* n) { +- return n->getKind() == NK_UnnamedVariable; +-} +- +-bool UnnamedVariable::equal(const Node&) const { +- return true; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/UnnamedVariable.h b/src/ast/UnnamedVariable.h +index 5786470..c2794d1 100644 +--- a/src/ast/UnnamedVariable.h ++++ b/src/ast/UnnamedVariable.h +@@ -29,16 +29,10 @@ class UnnamedVariable : public Argument { + public: + using Argument::Argument; + +- UnnamedVariable(SrcLocation loc = {}); +- +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + + private: +- bool equal(const Node&) const override; +- + UnnamedVariable* cloning() const override; + }; + +diff --git a/src/ast/UserDefinedFunctor.cpp b/src/ast/UserDefinedFunctor.cpp +index c6b58bf..1ab3e12 100644 +--- a/src/ast/UserDefinedFunctor.cpp ++++ b/src/ast/UserDefinedFunctor.cpp +@@ -16,11 +16,10 @@ + + namespace souffle::ast { + +-UserDefinedFunctor::UserDefinedFunctor(std::string name) +- : Functor(NK_UserDefinedFunctor, {}, {}), name(std::move(name)) {} ++UserDefinedFunctor::UserDefinedFunctor(std::string name) : Functor({}, {}), name(std::move(name)){}; + + UserDefinedFunctor::UserDefinedFunctor(std::string name, VecOwn args, SrcLocation loc) +- : Functor(NK_UserDefinedFunctor, std::move(args), std::move(loc)), name(std::move(name)) {} ++ : Functor(std::move(args), std::move(loc)), name(std::move(name)) {} + + void UserDefinedFunctor::print(std::ostream& os) const { + os << '@' << name << "(" << join(args) << ")"; +@@ -35,8 +34,4 @@ UserDefinedFunctor* UserDefinedFunctor::cloning() const { + return new UserDefinedFunctor(name, clone(args), getSrcLoc()); + } + +-bool UserDefinedFunctor::classof(const Node* n) { +- return n->getKind() == NK_UserDefinedFunctor; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/UserDefinedFunctor.h b/src/ast/UserDefinedFunctor.h +index 950454a..17acbd5 100644 +--- a/src/ast/UserDefinedFunctor.h ++++ b/src/ast/UserDefinedFunctor.h +@@ -40,8 +40,6 @@ public: + return name; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/Variable.cpp b/src/ast/Variable.cpp +index 0e870c2..2e51e83 100644 +--- a/src/ast/Variable.cpp ++++ b/src/ast/Variable.cpp +@@ -12,8 +12,7 @@ + #include + + namespace souffle::ast { +-Variable::Variable(std::string name, SrcLocation loc) +- : Argument(NK_Variable, std::move(loc)), name(std::move(name)) {} ++Variable::Variable(std::string name, SrcLocation loc) : Argument(std::move(loc)), name(std::move(name)) {} + + void Variable::setName(std::string name) { + this->name = std::move(name); +@@ -31,9 +30,4 @@ bool Variable::equal(const Node& node) const { + Variable* Variable::cloning() const { + return new Variable(name, getSrcLoc()); + } +- +-bool Variable::classof(const Node* n) { +- return n->getKind() == NK_Variable; +-} +- + } // namespace souffle::ast +diff --git a/src/ast/Variable.h b/src/ast/Variable.h +index 4ef1900..6173647 100644 +--- a/src/ast/Variable.h ++++ b/src/ast/Variable.h +@@ -39,8 +39,6 @@ public: + return name; + } + +- static bool classof(const Node*); +- + protected: + void print(std::ostream& os) const override; + +diff --git a/src/ast/analysis/Aggregate.cpp b/src/ast/analysis/Aggregate.cpp +index c6741e0..ffe6b47 100644 +--- a/src/ast/analysis/Aggregate.cpp ++++ b/src/ast/analysis/Aggregate.cpp +@@ -95,7 +95,7 @@ std::set getWitnessVariables( + } + }; + +- auto aggregatorlessClause = mk(QualifiedName::fromString("*")); ++ auto aggregatorlessClause = mk("*"); + aggregatorlessClause->setBodyLiterals(clone(clause.getBodyLiterals())); + + auto negatedHead = mk(clone(clause.getHead())); +@@ -104,14 +104,14 @@ std::set getWitnessVariables( + // Replace all aggregates with variables + M update; + aggregatorlessClause->apply(update); +- auto groundingAtom = mk(QualifiedName::fromString("+grounding_atom")); ++ auto groundingAtom = mk("+grounding_atom"); + for (std::string variableName : update.getAggregatorVariables()) { + groundingAtom->addArgument(mk(variableName)); + } + aggregatorlessClause->addToBody(std::move(groundingAtom)); + // 2. Create an aggregate clause so that we can check + // that it IS this aggregate giving a grounding to the candidate variable. +- auto aggregateSubclause = mk(QualifiedName::fromString("*")); ++ auto aggregateSubclause = mk("*"); + aggregateSubclause->setBodyLiterals(clone(aggregate.getBodyLiterals())); + + std::set witnessVariables; +@@ -177,7 +177,7 @@ std::string findUniqueVariableName(const Clause& clause, std::string base) { + std::string findUniqueRelationName(const Program& program, std::string base) { + int counter = 0; + auto candidate = base; +- while (program.getRelation(QualifiedName::fromString(candidate)) != nullptr) { ++ while (program.getRelation(candidate) != nullptr) { + candidate = base + toString(counter++); + } + return candidate; +@@ -321,7 +321,7 @@ std::set getInjectedVariables( + }; + // 2. make a clone of the clause and then apply that mapper onto it + auto clauseCopy = clone(clause); +- auto tweakedClause = mk(QualifiedName::fromString("*")); ++ auto tweakedClause = mk("*"); + tweakedClause->setBodyLiterals(clone(clause.getBodyLiterals())); + + // copy in the head as a negated atom +@@ -330,7 +330,7 @@ std::set getInjectedVariables( + ReplaceAggregatesWithVariables update(std::move(ancestorAggregates), clone(aggregate)); + tweakedClause->apply(update); + // the update will now tell us which variables we need to ground! +- auto groundingAtom = mk(QualifiedName::fromString("+grounding_atom")); ++ auto groundingAtom = mk("+grounding_atom"); + for (std::string variableName : update.getAggregatorVariables()) { + groundingAtom->addArgument(mk(variableName)); + } +diff --git a/src/ast/analysis/ClauseNormalisation.cpp b/src/ast/analysis/ClauseNormalisation.cpp +index 97a886f..f597432 100644 +--- a/src/ast/analysis/ClauseNormalisation.cpp ++++ b/src/ast/analysis/ClauseNormalisation.cpp +@@ -44,7 +44,7 @@ namespace souffle::ast::analysis { + + NormalisedClause::NormalisedClause(const Clause* clause) { + // head +- QualifiedName name = QualifiedName::fromString("@min:head"); ++ QualifiedName name("@min:head"); + std::vector headVars; + for (const auto* arg : clause->getHead()->getArguments()) { + headVars.push_back(normaliseArgument(arg)); +@@ -76,7 +76,7 @@ void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Li + } else if (const auto* neg = as(lit)) { + addClauseAtom("@min:neg", scopeID, neg->getAtom()); + } else if (const auto* bc = as(lit)) { +- QualifiedName name = QualifiedName::fromString(toBinaryConstraintSymbol(bc->getBaseOperator())); ++ QualifiedName name(toBinaryConstraintSymbol(bc->getBaseOperator())); + name.prepend("@min:operator"); + std::vector vars; + vars.push_back(scopeID); +@@ -88,7 +88,7 @@ void NormalisedClause::addClauseBodyLiteral(const std::string& scopeID, const Li + fullyNormalised = false; + std::stringstream qualifier; + qualifier << "@min:unhandled:lit:" << scopeID; +- QualifiedName name = QualifiedName::fromString(toString(*lit)); ++ QualifiedName name(toString(*lit)); + name.prepend(qualifier.str()); + clauseElements.push_back({name, std::vector()}); + } +@@ -130,7 +130,7 @@ std::string NormalisedClause::normaliseArgument(const Argument* arg) { + std::vector aggrTypeSignatureComponents; + + // - the operator is fixed and cannot be changed +- aggrTypeSignature << ":" << aggr->getBaseOperatorName(); ++ aggrTypeSignature << ":" << aggr->getBaseOperator(); + + // - the scope can be remapped as a variable + aggrTypeSignatureComponents.push_back(scopeID.str()); +@@ -142,8 +142,7 @@ std::string NormalisedClause::normaliseArgument(const Argument* arg) { + } + + // Type signature is its own special atom +- clauseElements.push_back( +- {QualifiedName::fromString(aggrTypeSignature.str()), 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/ComponentLookup.cpp b/src/ast/analysis/ComponentLookup.cpp +index 665b971..8ba4b22 100644 +--- a/src/ast/analysis/ComponentLookup.cpp ++++ b/src/ast/analysis/ComponentLookup.cpp +@@ -42,10 +42,10 @@ void ComponentLookupAnalysis::run(const TranslationUnit& translationUnit) { + const Component* ComponentLookupAnalysis::getComponent( + const Component* scope, const std::string& name, const TypeBinding& activeBinding) const { + // forward according to binding (we do not do this recursively on purpose) +- const QualifiedName qn = QualifiedName::fromString(name); +- QualifiedName boundName = activeBinding.find(qn); ++ QualifiedName boundName = activeBinding.find(name); + if (boundName.empty()) { +- boundName = qn; ++ // compName is not bound to anything => just just compName ++ boundName = name; + } + + // search nested scopes bottom up +diff --git a/src/ast/analysis/ComponentLookup.h b/src/ast/analysis/ComponentLookup.h +index 6e288f3..84d1e58 100644 +--- a/src/ast/analysis/ComponentLookup.h ++++ b/src/ast/analysis/ComponentLookup.h +@@ -67,7 +67,7 @@ private: + * Key value pair. Keys are names that should be forwarded to value, + * which is the actual name. Example T->MyImplementation. + */ +- UnorderedQualifiedNameMap binding; ++ std::map binding; + }; + + class ComponentLookupAnalysis : public Analysis { +diff --git a/src/ast/analysis/Constraint.h b/src/ast/analysis/Constraint.h +index ab50319..a94ea71 100644 +--- a/src/ast/analysis/Constraint.h ++++ b/src/ast/analysis/Constraint.h +@@ -17,8 +17,6 @@ + #pragma once + + #include "ConstraintSystem.h" +-#include "ErrorAnalyzer.h" +-#include "ValueChecker.h" + #include "ast/Argument.h" + #include "ast/Clause.h" + #include "ast/Node.h" +@@ -46,12 +44,6 @@ struct ConstraintAnalysisVar : public Variable { + void print(std::ostream& out) const override { + out << "var(" << *(this->id) << ")"; + } +- +- const std::string name() const { +- std::stringstream ss; +- ss << *(this->id); +- return ss.str(); +- } + }; + + /** +@@ -63,12 +55,11 @@ struct ConstraintAnalysisVar : public Variable { + * to be utilized by this analysis. + */ + template +-class ConstraintAnalysis : public Visitor, ValueChecker { ++class ConstraintAnalysis : public Visitor { + public: + using value_type = typename AnalysisVar::property_space::value_type; + using constraint_type = std::shared_ptr>; + using solution_type = std::map; +- using error_analyzer_type = ErrorAnalyzer; + + virtual void collectConstraints(const Clause& clause) { + visit(clause, *this); +@@ -81,9 +72,7 @@ public: + * @param debug a flag enabling the printing of debug information + * @return an assignment mapping a property to each argument in the given clause + */ +- solution_type analyse(const Clause& clause, error_analyzer_type* errorAnalyzer = nullptr, +- std::ostream* debugOutput = nullptr) { +- this->errorAnalyzer = errorAnalyzer; ++ solution_type analyse(const Clause& clause, std::ostream* debugOutput = nullptr) { + collectConstraints(clause); + + assignment = constraints.solve(); +@@ -95,29 +84,9 @@ public: + *debugOutput << "Solution:\n" << assignment << "\n"; + } + +- if (errorAnalyzer) { +- std::map::unsat_core_type> unsat_cores; +- for (const auto& [arg, value] : assignment) { +- if (!this->valueIsValid(value)) { +- auto unsat_core = constraints.extractUnsatCore(arg); +- unsat_cores[arg] = unsat_core; +- } +- } +- std::map> equivalentArguments; +- visit(clause, [&](const Argument& arg) { +- errorAnalyzer->addUnsatCore(&arg, unsat_cores[getVar(arg)]); +- equivalentArguments[getVar(arg)].emplace(&arg); +- }); +- for (const auto& [_, argSet] : equivalentArguments) { +- errorAnalyzer->addEquivalentArgumentSet(argSet); +- } +- } +- + // convert assignment to result + solution_type solution; + visit(clause, [&](const Argument& arg) { solution[&arg] = assignment[getVar(arg)]; }); +- +- this->errorAnalyzer = nullptr; + return solution; + } + +@@ -162,8 +131,6 @@ protected: + + /** A map mapping variables to unique instances to facilitate the unification of variables */ + std::map variables; +- +- error_analyzer_type* errorAnalyzer = nullptr; + }; + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/ConstraintSystem.h b/src/ast/analysis/ConstraintSystem.h +index 9fa9f57..41488d4 100644 +--- a/src/ast/analysis/ConstraintSystem.h ++++ b/src/ast/analysis/ConstraintSystem.h +@@ -16,7 +16,6 @@ + + #pragma once + +-#include "ValueChecker.h" + #include "souffle/utility/StreamUtil.h" + #include + #include +@@ -65,13 +64,6 @@ struct default_meet_op { + return res; + } + }; +- +-template +-struct default_is_valid_op { +- bool operator()(const T&) { +- return true; +- } +-}; + } // namespace detail + + /** +@@ -93,13 +85,11 @@ struct default_is_valid_op { + */ + template , +- typename meet_op = typename detail::default_meet_op, +- typename is_valid_op = typename detail::default_is_valid_op> ++ typename meet_op = typename detail::default_meet_op> + struct property_space { + using value_type = T; + using meet_assign_op_type = meet_assign_op; + using meet_op_type = meet_op; +- using is_valid_op_type = is_valid_op; + using bottom_factory_type = bottom_factory; + }; + +@@ -214,10 +204,6 @@ public: + c.print(out); + return out; + } +- +- virtual std::optional customMessage() const { +- return std::nullopt; +- } + }; + + //---------------------------------------------------------------------- +@@ -377,18 +363,11 @@ public: + * @tparam Var the domain of variables handled by this problem + */ + template +-class Problem : public ValueChecker { ++class Problem { + // a few type definitions + using constraint = Constraint; + using constraint_ptr = std::shared_ptr; + +- using value_type = typename Var::property_space::value_type; +- using problem_type = Problem; +- +-public: +- using unsat_core_type = typename std::set; +- +-private: + /** The list of covered constraints */ + std::vector constraints; + +@@ -430,31 +409,6 @@ public: + return assignment; + } + +- unsat_core_type extractUnsatCore(const Var& var) { +- unsat_core_type unsat_core; +- const auto& constraints = (static_cast(this))->constraints; +- +- while (true) { +- Assignment assignment; +- for (const auto& constraint : unsat_core) { +- constraint->update(assignment); +- } +- if (!this->valueIsValid(assignment[var])) { +- break; +- } +- auto size_unsat_core = unsat_core.size(); +- for (const auto& constraint : constraints) { +- constraint->update(assignment); +- if (!this->valueIsValid(assignment[var])) { +- unsat_core.insert(constraint); +- break; +- } +- } +- if (size_unsat_core == unsat_core.size()) break; +- } +- return unsat_core; +- } +- + /** Enables a problem to be printed (debugging) */ + void print(std::ostream& out) const { + if (constraints.empty()) { +diff --git a/src/ast/analysis/Functor.cpp b/src/ast/analysis/Functor.cpp +index 78990d6..0ca9525 100644 +--- a/src/ast/analysis/Functor.cpp ++++ b/src/ast/analysis/Functor.cpp +@@ -19,7 +19,6 @@ + #include "ast/FunctorDeclaration.h" + #include "ast/IntrinsicFunctor.h" + #include "ast/TranslationUnit.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/UserDefinedFunctor.h" + #include "ast/utility/Visitor.h" + +@@ -37,10 +36,6 @@ bool FunctorAnalysis::isStatefulFunctor(const UserDefinedFunctor& functor) const + return getFunctorDeclaration(functor).isStateful(); + } + +-bool FunctorAnalysis::isStatefulFunctor(const UserDefinedAggregator& aggregator) const { +- return getFunctorDeclaration(aggregator).isStateful(); +-} +- + QualifiedName const& FunctorAnalysis::getFunctorReturnType(const UserDefinedFunctor& functor) const { + return getFunctorDeclaration(functor).getReturnType().getTypeName(); + } +@@ -53,11 +48,6 @@ FunctorDeclaration const& FunctorAnalysis::getFunctorDeclaration(const UserDefin + return *functorNameToDeclaration.at(functor.getName()); + } + +-FunctorDeclaration const& FunctorAnalysis::getFunctorDeclaration( +- const UserDefinedAggregator& aggregator) const { +- return *functorNameToDeclaration.at(aggregator.getBaseOperatorName()); +-} +- + bool FunctorAnalysis::isMultiResult(const Functor& functor) { + if (isA(functor)) { + return false; +diff --git a/src/ast/analysis/Functor.h b/src/ast/analysis/Functor.h +index 839ade4..28d8db0 100644 +--- a/src/ast/analysis/Functor.h ++++ b/src/ast/analysis/Functor.h +@@ -18,7 +18,6 @@ + + #include "ast/FunctorDeclaration.h" + #include "ast/TranslationUnit.h" +-#include "ast/UserDefinedAggregator.h" + #include "souffle/TypeAttribute.h" + #include + #include +@@ -51,9 +50,7 @@ public: + std::size_t getFunctorArity(UserDefinedFunctor const& functor) const; + QualifiedName const& getFunctorReturnType(const UserDefinedFunctor& functor) const; + bool isStatefulFunctor(const UserDefinedFunctor& functor) const; +- bool isStatefulFunctor(const UserDefinedAggregator& aggregator) const; + const FunctorDeclaration& getFunctorDeclaration(const UserDefinedFunctor& functor) const; +- const FunctorDeclaration& getFunctorDeclaration(const UserDefinedAggregator& aggregator) const; + + /** Return whether a UDF is stateful */ + bool isStateful(const UserDefinedFunctor& udf) const; +diff --git a/src/ast/analysis/Ground.cpp b/src/ast/analysis/Ground.cpp +index 4c657e8..52d7c94 100644 +--- a/src/ast/analysis/Ground.cpp ++++ b/src/ast/analysis/Ground.cpp +@@ -154,23 +154,8 @@ BoolDisjunctConstraint imply(const std::vector& vars, const Boo + struct GroundednessAnalysis : public ConstraintAnalysis { + Program& program; + std::set ignore; +- UnorderedQualifiedNameMap> latticeAttributes; +- bool isLatticeTransformerPass; +- +- GroundednessAnalysis(const TranslationUnit& tu, bool isLatticeTransformerPass) +- : program(tu.getProgram()), isLatticeTransformerPass(isLatticeTransformerPass) { +- if (isLatticeTransformerPass) { +- for (const Relation* rel : program.getRelations()) { +- const auto attributes = rel->getAttributes(); +- const auto& name = rel->getQualifiedName(); +- for (std::size_t i = 0; i < attributes.size(); i++) { +- if (attributes[i]->getIsLattice()) { +- latticeAttributes[name].insert(i); +- } +- } +- } +- } +- } ++ ++ GroundednessAnalysis(const TranslationUnit& tu) : program(tu.getProgram()) {} + + // atoms are producing grounded variables + void visit_(type_identity, const Atom& cur) override { +@@ -179,14 +164,9 @@ struct GroundednessAnalysis : public ConstraintAnalysis { + return; + } + +- // all arguments are grounded except lattice arguments +- const auto& name = cur.getQualifiedName(); +- const auto& args = cur.getArguments(); +- for (std::size_t i = 0; i < cur.getArity(); i++) { +- if (!isLatticeTransformerPass || !latticeAttributes.count(name) || +- !latticeAttributes[name].count(i)) { +- addConstraint(isTrue(getVar(args[i]))); +- } ++ // all arguments are grounded ++ for (const auto& arg : cur.getArguments()) { ++ addConstraint(isTrue(getVar(arg))); + } + } + +@@ -285,10 +265,9 @@ struct GroundednessAnalysis : public ConstraintAnalysis { + /*** + * computes for variables in the clause whether they are grounded + */ +-std::map getGroundedTerms( +- const TranslationUnit& tu, const Clause& clause, bool isLatticeTransformerPass) { ++std::map getGroundedTerms(const TranslationUnit& tu, const Clause& clause) { + // run analysis on given clause +- return GroundednessAnalysis(tu, isLatticeTransformerPass).analyse(clause); ++ return GroundednessAnalysis(tu).analyse(clause); + } + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/Ground.h b/src/ast/analysis/Ground.h +index 8ac8689..f6ca733 100644 +--- a/src/ast/analysis/Ground.h ++++ b/src/ast/analysis/Ground.h +@@ -32,7 +32,6 @@ namespace souffle::ast::analysis { + * @return a map mapping each contained argument to a boolean indicating + * whether the argument represents a grounded value or not + */ +-std::map getGroundedTerms( +- const TranslationUnit& tu, const Clause& clause, bool isLatticeTransformerPass = false); ++std::map getGroundedTerms(const TranslationUnit& tu, const Clause& clause); + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/IOType.h b/src/ast/analysis/IOType.h +index ff39612..9bf0dc4 100644 +--- a/src/ast/analysis/IOType.h ++++ b/src/ast/analysis/IOType.h +@@ -69,10 +69,10 @@ public: + } + + private: +- RelationSet inputRelations; +- RelationSet outputRelations; +- RelationSet printSizeRelations; +- RelationSet limitSizeRelations; ++ std::set inputRelations; ++ std::set outputRelations; ++ std::set printSizeRelations; ++ std::set limitSizeRelations; + std::map limitSize; + }; + +diff --git a/src/ast/analysis/PrecedenceGraph.cpp b/src/ast/analysis/PrecedenceGraph.cpp +index 64e3492..5c783cb 100644 +--- a/src/ast/analysis/PrecedenceGraph.cpp ++++ b/src/ast/analysis/PrecedenceGraph.cpp +@@ -27,7 +27,6 @@ + #include "ast/Relation.h" + #include "ast/TranslationUnit.h" + #include "ast/utility/Visitor.h" +-#include "souffle/utility/StringUtil.h" + #include + #include + #include +@@ -49,13 +48,6 @@ void PrecedenceGraphAnalysis::run(const TranslationUnit& translationUnit) { + souffle::visit(literals[i], addEdgeToR); + } + } +- +- // delta_debug relation are computed from the original relation +- if (const auto deltaDebug = r->getIsDeltaDebug()) { +- const auto* dbg = program.getRelation(deltaDebug.value()); +- backingGraph.insert(r, dbg); +- backingGraph.insert(dbg, r); +- } + } + } + +diff --git a/src/ast/analysis/PrecedenceGraph.h b/src/ast/analysis/PrecedenceGraph.h +index fda88f6..abeb366 100644 +--- a/src/ast/analysis/PrecedenceGraph.h ++++ b/src/ast/analysis/PrecedenceGraph.h +@@ -21,9 +21,7 @@ + #include "GraphUtils.h" + #include "ast/Relation.h" + #include "ast/TranslationUnit.h" +-#include "souffle/datastructure/Graph.h" +-#include +-#include ++#include + + namespace souffle::ast { + +@@ -45,16 +43,16 @@ public: + /** Output precedence graph in text format to a given stream */ + void print(std::ostream& os) const override; + +- /** Output precedence graph in image format to a given stream */ +- void printHTML(std::ostream& os) const override; ++ /** Output precedence graph in graphviz format to a given stream */ ++ void printHTML(std::ostream& os) const; + +- const Graph& graph() const { ++ const Graph& graph() const { + return backingGraph; + } + + private: + /** Adjacency list of precedence graph (determined by the dependencies of the relations) */ +- Graph backingGraph; ++ Graph backingGraph; + + /** Output precedence graph in text format to a given stringstream */ + void printRaw(std::stringstream& ss) const; +diff --git a/src/ast/analysis/ProfileUse.cpp b/src/ast/analysis/ProfileUse.cpp +index b6fc8ce..fe290ea 100644 +--- a/src/ast/analysis/ProfileUse.cpp ++++ b/src/ast/analysis/ProfileUse.cpp +@@ -17,7 +17,6 @@ + + #include "ast/analysis/ProfileUse.h" + #include "Global.h" +-#include "ast/Program.h" + #include "ast/QualifiedName.h" + #include "souffle/profile/ProgramRun.h" + #include "souffle/profile/Reader.h" +@@ -30,10 +29,10 @@ namespace souffle::ast::analysis { + /** + * Run analysis, i.e., retrieve profile information + */ +-void ProfileUseAnalysis::run(const TranslationUnit& TU) { ++void ProfileUseAnalysis::run(const TranslationUnit&) { + std::string filename; +- if (TU.global().config().has("auto-schedule")) { +- filename = TU.global().config().get("auto-schedule"); ++ if (Global::config().has("auto-schedule")) { ++ filename = Global::config().get("auto-schedule"); + } + reader = mk(filename, programRun); + reader->processFile(); +@@ -66,14 +65,14 @@ bool ProfileUseAnalysis::hasAutoSchedulerStats() const { + return reader->hasAutoSchedulerStats(); + } + +-double ProfileUseAnalysis::getNonRecursiveJoinSize( ++std::size_t ProfileUseAnalysis::getNonRecursiveUniqueKeys( + const std::string& rel, const std::string& attributes, const std::string& constants) const { +- return reader->getNonRecursiveEstimateJoinSize(rel, attributes, constants); ++ return reader->getNonRecursiveCountUniqueKeys(rel, attributes, constants); + } + +-double ProfileUseAnalysis::getRecursiveJoinSize(const std::string& rel, const std::string& attributes, ++std::size_t ProfileUseAnalysis::getRecursiveUniqueKeys(const std::string& rel, const std::string& attributes, + const std::string& constants, const std::string& iteration) const { +- return reader->getRecursiveEstimateJoinSize(rel, attributes, constants, iteration); ++ return reader->getRecursiveCountUniqueKeys(rel, attributes, constants, iteration); + } + + std::size_t ProfileUseAnalysis::getIterations(const std::string& rel) const { +diff --git a/src/ast/analysis/ProfileUse.h b/src/ast/analysis/ProfileUse.h +index 9ec7351..d23de41 100644 +--- a/src/ast/analysis/ProfileUse.h ++++ b/src/ast/analysis/ProfileUse.h +@@ -56,10 +56,10 @@ public: + + bool hasAutoSchedulerStats() const; + +- double getNonRecursiveJoinSize( ++ std::size_t getNonRecursiveUniqueKeys( + const std::string& rel, const std::string& attributes, const std::string& constants) const; + +- double getRecursiveJoinSize(const std::string& rel, const std::string& attributes, ++ std::size_t getRecursiveUniqueKeys(const std::string& rel, const std::string& attributes, + const std::string& constants, const std::string& iteration) const; + + std::size_t getIterations(const std::string& rel) const; +diff --git a/src/ast/analysis/RecursiveClauses.cpp b/src/ast/analysis/RecursiveClauses.cpp +index 0d0a5d8..870ed07 100644 +--- a/src/ast/analysis/RecursiveClauses.cpp ++++ b/src/ast/analysis/RecursiveClauses.cpp +@@ -10,10 +10,9 @@ + * + * @file RecursiveClauses.cpp + * +- * Compute the set of recursive clauses. +- * +- * A recursive clause is a clause of a rule R that depends directly or +- * transitively on that rule R. ++ * Implements method of precedence graph to build the precedence graph, ++ * compute strongly connected components of the precedence graph, and ++ * build the strongly connected component graph. + * + ***********************************************************************/ + +@@ -27,9 +26,7 @@ + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" + #include "souffle/utility/StreamUtil.h" +- + #include +-#include + #include + #include + #include +@@ -38,68 +35,66 @@ namespace souffle::ast::analysis { + + void RecursiveClausesAnalysis::run(const TranslationUnit& translationUnit) { + Program& program = translationUnit.getProgram(); ++ visit(program, [&](const Clause& clause) { ++ if (computeIsRecursive(clause, translationUnit)) { ++ recursiveClauses.insert(&clause); ++ } ++ }); ++} + +- // Mapping from a relation to the set of relations that it depends on directly. +- // +- // It is the adjacency list of the relations dependency graph. +- std::map> relationUseRelation; ++void RecursiveClausesAnalysis::print(std::ostream& os) const { ++ os << recursiveClauses << std::endl; ++} + +- // Mapping from a clause to the set of relations that it depends on directly. +- std::map> clauseUseRelation; ++bool RecursiveClausesAnalysis::computeIsRecursive( ++ const Clause& clause, const TranslationUnit& translationUnit) const { ++ const Program& program = translationUnit.getProgram(); + +- // Mapping from a clause to its relations. +- std::map clauseRelation; ++ // we want to reach the atom of the head through the body ++ const Relation* trg = program.getRelation(clause); + +- std::vector relations; ++ std::set reached; ++ std::vector worklist; + +- // gather dependencies +- for (const auto& qninfo : program.getRelationInfo()) { +- const uint32_t head = qninfo.first.getIndex(); +- relations.emplace_back(head); +- for (const auto& clause : qninfo.second.clauses) { +- clauseRelation.emplace(clause.get(), head); +- for (const auto& atom : getBodyLiterals(*clause)) { +- const uint32_t rhs = atom->getQualifiedName().getIndex(); +- relationUseRelation[head].emplace(rhs); +- clauseUseRelation[clause.get()].emplace(rhs); +- } ++ // set up start list ++ for (const auto* cur : getBodyLiterals(clause)) { ++ auto rel = program.getRelation(*cur); ++ if (rel == trg) { ++ return true; + } ++ worklist.push_back(rel); + } + +- // Mapping from a relation to the set of transitively reachable relations +- // it depends on, including itself. +- std::map> reachableRelation; ++ // process remaining elements ++ while (!worklist.empty()) { ++ // get next to process ++ const Relation* cur = worklist.back(); ++ worklist.pop_back(); + +- // called when we discoved that `head` transitively reach `reached`. +- const std::function dfs = [&](uint32_t head, uint32_t reached) { +- reachableRelation[head].emplace(reached); +- for (uint32_t rel : relationUseRelation[reached]) { +- if (reachableRelation[head].emplace(rel).second) { +- // discovered that relation `rel` is reachabel from `head` +- dfs(head, rel); +- } ++ // skip null pointers (errors in the input code) ++ if (cur == nullptr) { ++ continue; + } +- }; + +- // Compute the transitive closure of reachable (dependencies) relations from each relation. +- for (const uint32_t head : relations) { +- // include itself in the closure +- dfs(head, head); +- } ++ // check whether this one has been checked before ++ if (!reached.insert(cur).second) { ++ continue; ++ } + +- for (const auto& [clause, rel] : clauseRelation) { +- for (const uint32_t used : clauseUseRelation[clause]) { +- if (reachableRelation[used].count(rel) > 0) { +- // clause is recursive +- recursiveClauses.emplace(clause); +- break; ++ // check all atoms in the relations ++ for (auto&& cl : program.getClauses(*cur)) { ++ for (const Atom* at : getBodyLiterals(*cl)) { ++ auto rel = program.getRelation(*at); ++ if (rel == trg) { ++ return true; ++ } ++ worklist.push_back(rel); + } + } + } +-} + +-void RecursiveClausesAnalysis::print(std::ostream& os) const { +- os << recursiveClauses << std::endl; ++ // no cycles found ++ return false; + } + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/RecursiveClauses.h b/src/ast/analysis/RecursiveClauses.h +index 00fe7b9..56ae27e 100644 +--- a/src/ast/analysis/RecursiveClauses.h ++++ b/src/ast/analysis/RecursiveClauses.h +@@ -49,6 +49,9 @@ public: + + private: + std::set recursiveClauses; ++ ++ /** Determines whether the given clause is recursive within the given program */ ++ bool computeIsRecursive(const Clause& clause, const TranslationUnit& translationUnit) const; + }; + + } // namespace analysis +diff --git a/src/ast/analysis/RedundantRelations.cpp b/src/ast/analysis/RedundantRelations.cpp +index babf0eb..cb08841 100644 +--- a/src/ast/analysis/RedundantRelations.cpp ++++ b/src/ast/analysis/RedundantRelations.cpp +@@ -33,8 +33,8 @@ namespace souffle::ast::analysis { + void RedundantRelationsAnalysis::run(const TranslationUnit& translationUnit) { + precedenceGraph = &translationUnit.getAnalysis(); + +- RelationSet work; +- RelationSet notRedundant; ++ std::set work; ++ std::set notRedundant; + auto& ioType = translationUnit.getAnalysis(); + Program& program = translationUnit.getProgram(); + +diff --git a/src/ast/analysis/RedundantRelations.h b/src/ast/analysis/RedundantRelations.h +index 93518f4..9480ffc 100644 +--- a/src/ast/analysis/RedundantRelations.h ++++ b/src/ast/analysis/RedundantRelations.h +@@ -44,13 +44,13 @@ public: + + void print(std::ostream& os) const override; + +- const UnorderedQualifiedNameSet& getRedundantRelations() const { ++ const std::set& getRedundantRelations() const { + return redundantRelations; + } + + private: + PrecedenceGraphAnalysis* precedenceGraph = nullptr; +- UnorderedQualifiedNameSet redundantRelations; ++ std::set redundantRelations; + }; + + } // namespace analysis +diff --git a/src/ast/analysis/RelationSchedule.cpp b/src/ast/analysis/RelationSchedule.cpp +index 921d2d8..d2c4269 100644 +--- a/src/ast/analysis/RelationSchedule.cpp ++++ b/src/ast/analysis/RelationSchedule.cpp +@@ -53,34 +53,33 @@ void RelationScheduleAnalysisStep::print(std::ostream& os) const { + void RelationScheduleAnalysis::run(const TranslationUnit& translationUnit) { + topsortSCCGraphAnalysis = &translationUnit.getAnalysis(); + precedenceGraph = &translationUnit.getAnalysis(); +- sccGraph = &translationUnit.getAnalysis(); + +- const std::size_t numSCCs = sccGraph->getNumberOfSCCs(); +- std::vector relationExpirySchedule = computeRelationExpirySchedule(); ++ std::size_t numSCCs = translationUnit.getAnalysis().getNumberOfSCCs(); ++ std::vector> relationExpirySchedule = ++ computeRelationExpirySchedule(translationUnit); + + relationSchedule.clear(); + for (std::size_t i = 0; i < numSCCs; i++) { +- const auto scc = topsortSCCGraphAnalysis->order()[i]; +- const RelationSet computedRelations = sccGraph->getInternalRelations(scc); +- relationSchedule.emplace_back( +- computedRelations, relationExpirySchedule[i], sccGraph->isRecursive(scc)); ++ auto scc = topsortSCCGraphAnalysis->order()[i]; ++ const std::set computedRelations = ++ translationUnit.getAnalysis().getInternalRelations(scc); ++ relationSchedule.emplace_back(computedRelations, relationExpirySchedule[i], ++ translationUnit.getAnalysis().isRecursive(scc)); + } +- +- topsortSCCGraphAnalysis = nullptr; +- precedenceGraph = nullptr; +- sccGraph = nullptr; + } + +-std::vector RelationScheduleAnalysis::computeRelationExpirySchedule() { +- std::vector relationExpirySchedule; ++std::vector> RelationScheduleAnalysis::computeRelationExpirySchedule( ++ const TranslationUnit& translationUnit) { ++ std::vector> relationExpirySchedule; + /* Compute for each step in the reverse topological order + of evaluating the SCC the set of alive relations. */ + std::size_t numSCCs = topsortSCCGraphAnalysis->order().size(); + + /* Alive set for each step */ +- std::vector alive(numSCCs); ++ std::vector> alive(numSCCs); + /* Resize expired relations sets */ + relationExpirySchedule.resize(numSCCs); ++ const auto& sccGraph = translationUnit.getAnalysis(); + + /* Compute all alive relations by iterating over all steps in reverse order + determine the dependencies */ +@@ -90,7 +89,7 @@ std::vector RelationScheduleAnalysis::computeRelationExpirySchedule + + /* Add predecessors of relations computed in this step */ + auto scc = topsortSCCGraphAnalysis->order()[numSCCs - orderedSCC]; +- for (const Relation* r : sccGraph->getInternalRelations(scc)) { ++ for (const Relation* r : sccGraph.getInternalRelations(scc)) { + for (const Relation* predecessor : precedenceGraph->graph().predecessors(r)) { + alive[orderedSCC].insert(predecessor); + } +@@ -98,10 +97,10 @@ std::vector RelationScheduleAnalysis::computeRelationExpirySchedule + + /* Compute expired relations in reverse topological order using the set difference of the alive sets + between steps. */ +- std::copy_if(alive[orderedSCC].begin(), alive[orderedSCC].end(), ++ std::set_difference(alive[orderedSCC].begin(), alive[orderedSCC].end(), alive[orderedSCC - 1].begin(), ++ alive[orderedSCC - 1].end(), + std::inserter(relationExpirySchedule[numSCCs - orderedSCC], +- relationExpirySchedule[numSCCs - orderedSCC].end()), +- [&](const Relation* r) { return alive[orderedSCC - 1].count(r) == 0; }); ++ relationExpirySchedule[numSCCs - orderedSCC].end())); + } + + return relationExpirySchedule; +@@ -111,6 +110,21 @@ void RelationScheduleAnalysis::print(std::ostream& os) const { + os << "begin schedule\n"; + for (const RelationScheduleAnalysisStep& step : relationSchedule) { + os << step; ++ os << "computed: "; ++ for (const Relation* compRel : step.computed()) { ++ os << compRel->getQualifiedName() << ", "; ++ } ++ os << "\nexpired: "; ++ for (const Relation* compRel : step.expired()) { ++ os << compRel->getQualifiedName() << ", "; ++ } ++ os << "\n"; ++ if (step.recursive()) { ++ os << "recursive"; ++ } else { ++ os << "not recursive"; ++ } ++ os << "\n"; + } + os << "end schedule\n"; + } +diff --git a/src/ast/analysis/RelationSchedule.h b/src/ast/analysis/RelationSchedule.h +index 8f2fee7..1bfbd4f 100644 +--- a/src/ast/analysis/RelationSchedule.h ++++ b/src/ast/analysis/RelationSchedule.h +@@ -21,9 +21,7 @@ + #include "ast/Relation.h" + #include "ast/TranslationUnit.h" + #include "ast/analysis/PrecedenceGraph.h" +-#include "ast/analysis/SCCGraph.h" + #include "ast/analysis/TopologicallySortedSCCGraph.h" +- + #include + #include + #include +@@ -38,16 +36,16 @@ namespace souffle::ast::analysis { + */ + class RelationScheduleAnalysisStep { + public: +- RelationScheduleAnalysisStep( +- RelationSet computedRelations, RelationSet expiredRelations, const bool isRecursive) ++ RelationScheduleAnalysisStep(std::set computedRelations, ++ std::set expiredRelations, const bool isRecursive) + : computedRelations(std::move(computedRelations)), expiredRelations(std::move(expiredRelations)), + isRecursive(isRecursive) {} + +- const RelationSet& computed() const { ++ const std::set& computed() const { + return computedRelations; + } + +- const RelationSet& expired() const { ++ const std::set& expired() const { + return expiredRelations; + } + +@@ -64,8 +62,8 @@ public: + } + + private: +- RelationSet computedRelations; +- RelationSet expiredRelations; ++ std::set computedRelations; ++ std::set expiredRelations; + const bool isRecursive; + }; + +@@ -90,12 +88,12 @@ public: + private: + TopologicallySortedSCCGraphAnalysis* topsortSCCGraphAnalysis = nullptr; + PrecedenceGraphAnalysis* precedenceGraph = nullptr; +- const SCCGraphAnalysis* sccGraph = nullptr; + + /** Relations computed and expired relations at each step */ + std::vector relationSchedule; + +- std::vector computeRelationExpirySchedule(); ++ std::vector> computeRelationExpirySchedule( ++ const TranslationUnit& translationUnit); + }; + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/SCCGraph.cpp b/src/ast/analysis/SCCGraph.cpp +index b05a2b6..b9cbd9c 100644 +--- a/src/ast/analysis/SCCGraph.cpp ++++ b/src/ast/analysis/SCCGraph.cpp +@@ -36,7 +36,6 @@ namespace souffle::ast::analysis { + void SCCGraphAnalysis::run(const TranslationUnit& translationUnit) { + precedenceGraph = &translationUnit.getAnalysis(); + ioType = &translationUnit.getAnalysis(); +- programName = translationUnit.global().config().get("name"); + sccToRelation.clear(); + relationToScc.clear(); + predecessors.clear(); +@@ -116,7 +115,7 @@ void SCCGraphAnalysis::scR(const Relation* w, std::map& getInternalRelations(const std::size_t scc) const { + return sccToRelation.at(scc); + } + + /** Get all external output predecessor relations of a given SCC. */ +- RelationSet getExternalOutputPredecessorRelations(const std::size_t scc) const { +- RelationSet externOutPreds; ++ std::set getExternalOutputPredecessorRelations(const std::size_t scc) const { ++ std::set externOutPreds; + for (const auto& relation : getInternalRelations(scc)) { + for (const auto& predecessor : precedenceGraph->graph().predecessors(relation)) { + if (relationToScc.at(predecessor) != scc && ioType->isOutput(predecessor)) { +@@ -112,8 +112,8 @@ public: + } + + /** Get all external non-output predecessor relations of a given SCC. */ +- RelationSet getExternalNonOutputPredecessorRelations(const std::size_t scc) const { +- RelationSet externNonOutPreds; ++ std::set getExternalNonOutputPredecessorRelations(const std::size_t scc) const { ++ std::set externNonOutPreds; + for (const auto& relation : getInternalRelations(scc)) { + for (const auto& predecessor : precedenceGraph->graph().predecessors(relation)) { + if (relationToScc.at(predecessor) != scc && !ioType->isOutput(predecessor)) { +@@ -125,8 +125,8 @@ public: + } + + /** Get all external predecessor relations of a given SCC. */ +- RelationSet getExternalPredecessorRelations(const std::size_t scc) const { +- RelationSet externPreds; ++ std::set getExternalPredecessorRelations(const std::size_t scc) const { ++ std::set externPreds; + for (const auto& relation : getInternalRelations(scc)) { + for (const auto& predecessor : precedenceGraph->graph().predecessors(relation)) { + if (relationToScc.at(predecessor) != scc) { +@@ -138,8 +138,8 @@ public: + } + + /** Get all internal output relations of a given SCC. */ +- RelationSet getInternalOutputRelations(const std::size_t scc) const { +- RelationSet internOuts; ++ std::set getInternalOutputRelations(const std::size_t scc) const { ++ std::set internOuts; + for (const auto& relation : getInternalRelations(scc)) { + if (ioType->isOutput(relation)) { + internOuts.insert(relation); +@@ -149,8 +149,8 @@ public: + } + + /** Get all internal relations of a given SCC with external successors. */ +- RelationSet getInternalRelationsWithExternalSuccessors(const std::size_t scc) const { +- RelationSet internsWithExternSuccs; ++ std::set getInternalRelationsWithExternalSuccessors(const std::size_t scc) const { ++ std::set internsWithExternSuccs; + for (const auto& relation : getInternalRelations(scc)) { + for (const auto& successor : precedenceGraph->graph().successors(relation)) { + if (relationToScc.at(successor) != scc) { +@@ -163,8 +163,9 @@ public: + } + + /** Get all internal non-output relations of a given SCC with external successors. */ +- RelationSet getInternalNonOutputRelationsWithExternalSuccessors(const std::size_t scc) const { +- RelationSet internNonOutsWithExternSuccs; ++ std::set getInternalNonOutputRelationsWithExternalSuccessors( ++ const std::size_t scc) const { ++ std::set internNonOutsWithExternSuccs; + for (const auto& relation : getInternalRelations(scc)) { + if (!ioType->isOutput(relation)) { + for (const auto& successor : precedenceGraph->graph().successors(relation)) { +@@ -179,8 +180,8 @@ public: + } + + /** Get all internal input relations of a given SCC. */ +- RelationSet getInternalInputRelations(const std::size_t scc) const { +- RelationSet internIns; ++ std::set getInternalInputRelations(const std::size_t scc) const { ++ std::set internIns; + for (const auto& relation : getInternalRelations(scc)) { + if (ioType->isInput(relation)) { + internIns.insert(relation); +@@ -191,7 +192,7 @@ public: + + /** Return if the given SCC is recursive. */ + bool isRecursive(const std::size_t scc) const { +- const RelationSet& sccRelations = sccToRelation.at(scc); ++ const std::set& sccRelations = sccToRelation.at(scc); + if (sccRelations.size() == 1) { + const Relation* singleRelation = *sccRelations.begin(); + if (precedenceGraph->graph().predecessors(singleRelation).count(singleRelation) == 0u) { +@@ -205,7 +206,7 @@ public: + void print(std::ostream& os) const override; + + /** Print the SCC graph in HTML format. */ +- void printHTML(std::ostream& os) const override; ++ void printHTML(std::ostream& os) const; + + private: + PrecedenceGraphAnalysis* precedenceGraph = nullptr; +@@ -220,7 +221,7 @@ private: + std::vector> predecessors; + + /** Relations contained in a SCC */ +- std::vector sccToRelation; ++ std::vector> sccToRelation; + + /** Recursive scR method for computing SCC */ + void scR(const Relation* relation, std::map& preOrder, std::size_t& counter, +@@ -228,8 +229,6 @@ private: + + IOTypeAnalysis* ioType = nullptr; + +- std::string programName; +- + /** Print the SCC graph to a string. */ + void printRaw(std::stringstream& ss) const; + }; +diff --git a/src/ast/analysis/TopologicallySortedSCCGraph.cpp b/src/ast/analysis/TopologicallySortedSCCGraph.cpp +index bd0b480..23ed6a1 100644 +--- a/src/ast/analysis/TopologicallySortedSCCGraph.cpp ++++ b/src/ast/analysis/TopologicallySortedSCCGraph.cpp +@@ -28,8 +28,6 @@ + + namespace souffle::ast::analysis { + +-TopologicallySortedSCCGraphAnalysis::TopologicallySortedSCCGraphAnalysis() : Analysis(name) {} +- + int TopologicallySortedSCCGraphAnalysis::topologicalOrderingCost( + const std::vector& permutationOfSCCs) const { + // create variables to hold the cost of the current SCC and the permutation as a whole +@@ -69,73 +67,86 @@ int TopologicallySortedSCCGraphAnalysis::topologicalOrderingCost( + } + + void TopologicallySortedSCCGraphAnalysis::computeTopologicalOrdering( +- std::size_t scc, std::vector& visited) { +- if (visited[scc] >= 0) { ++ std::size_t scc, std::vector& visited) { ++ // create a flag to indicate that a successor was visited (by default it hasn't been) ++ bool found = false; ++ bool hasUnvisitedSuccessor = false; ++ bool hasUnvisitedPredecessor = false; ++ // for each successor of the input scc ++ const auto& successorsToVisit = sccGraph->getSuccessorSCCs(scc); ++ for (const auto scc_i : successorsToVisit) { ++ if (visited[scc_i]) { ++ continue; ++ } ++ hasUnvisitedPredecessor = false; ++ const auto& successorsPredecessors = sccGraph->getPredecessorSCCs(scc_i); ++ for (const auto scc_j : successorsPredecessors) { ++ if (!visited[scc_j]) { ++ hasUnvisitedPredecessor = true; ++ break; ++ } ++ } ++ if (!hasUnvisitedPredecessor) { ++ // give it a temporary marking ++ visited[scc_i] = true; ++ // add it to the permanent ordering ++ sccOrder.push_back(scc_i); ++ // and use it as a root node in a recursive call to this function ++ computeTopologicalOrdering(scc_i, visited); ++ // finally, indicate that a successor has been found for this node ++ found = true; ++ } ++ } ++ // return at once if no valid successors have been found; as either it has none or they all have a ++ // better predecessor ++ if (!found) { + return; + } +- +- int maxDist = 0; ++ hasUnvisitedPredecessor = false; + const auto& predecessors = sccGraph->getPredecessorSCCs(scc); +- for (const auto pred : predecessors) { +- if (visited[pred] < 0) { +- // has an unvisited predecessor +- return; +- } else { +- maxDist = std::max(maxDist, visited[pred] + 1); ++ for (const auto scc_j : predecessors) { ++ if (!visited[scc_j]) { ++ hasUnvisitedPredecessor = true; ++ break; + } + } +- +- visited[scc] = maxDist; +- sccDistance.emplace(scc, maxDist); +- sccOrder.emplace_back(scc); +- +- for (const auto succ : sccGraph->getSuccessorSCCs(scc)) { +- computeTopologicalOrdering(succ, visited); ++ hasUnvisitedSuccessor = false; ++ const auto& successors = sccGraph->getSuccessorSCCs(scc); ++ for (const auto scc_j : successors) { ++ if (!visited[scc_j]) { ++ hasUnvisitedSuccessor = true; ++ break; ++ } ++ } ++ // otherwise, if more white successors remain for the current scc, use it again as the root node in a ++ // recursive call to this function ++ if (hasUnvisitedSuccessor && !hasUnvisitedPredecessor) { ++ computeTopologicalOrdering(scc, visited); + } + } + + void TopologicallySortedSCCGraphAnalysis::run(const TranslationUnit& translationUnit) { + // obtain the scc graph + sccGraph = &translationUnit.getAnalysis(); +- + // clear the list of ordered sccs + sccOrder.clear(); +- sccDistance.clear(); +- +- // Compute the maximum distance from the root scc(s) to each scc. +- // +- // visited[scc] < 0 when the scc distance from the root(s) is not known yet +- // visited[scc] >= 0 is the maximum distance of the scc from a root +- // +- // this provides a partial order between sccs, several sccs may have the +- // same maximum distance from the roots. +- std::vector visited; ++ std::vector visited; + visited.resize(sccGraph->getNumberOfSCCs()); +- std::fill(visited.begin(), visited.end(), -1); +- for (std::size_t scc = 0; scc < sccGraph->getNumberOfSCCs(); ++scc) { +- computeTopologicalOrdering(scc, visited); +- } +- +- // find the least relation qualified name of each scc, using the lexicographic order. +- std::vector sccLeastQN; +- sccLeastQN.resize(sccGraph->getNumberOfSCCs()); ++ std::fill(visited.begin(), visited.end(), false); ++ // generate topological ordering using forwards algorithm (like Khan's algorithm) ++ // for each of the sccs in the graph + for (std::size_t scc = 0; scc < sccGraph->getNumberOfSCCs(); ++scc) { +- sccLeastQN[scc] = (*sccGraph->getInternalRelations(scc).begin())->getQualifiedName(); +- } +- +- // sort sccs by distance from roots and then by lexicographic order of the least +- // relation qualified name in each scc. +- // +- // this provides a deterministic total order between sccs. +- std::sort(sccOrder.begin(), sccOrder.end(), [&](std::size_t lhs, std::size_t rhs) { +- if (sccDistance[lhs] < sccDistance[rhs]) { +- return true; +- } else if (sccDistance[lhs] > sccDistance[rhs]) { +- return false; +- } else { +- return sccLeastQN[lhs].lexicalLess(sccLeastQN[rhs]); ++ // if that scc has no predecessors ++ if (sccGraph->getPredecessorSCCs(scc).empty()) { ++ // put it in the ordering ++ sccOrder.push_back(scc); ++ visited[scc] = true; ++ // if the scc has successors ++ if (!sccGraph->getSuccessorSCCs(scc).empty()) { ++ computeTopologicalOrdering(scc, visited); ++ } + } +- }); ++ } + } + + void TopologicallySortedSCCGraphAnalysis::print(std::ostream& os) const { +@@ -164,27 +175,4 @@ void TopologicallySortedSCCGraphAnalysis::print(std::ostream& os) const { + os << "cost: " << topologicalOrderingCost(sccOrder) << std::endl; + } + +-const std::vector& TopologicallySortedSCCGraphAnalysis::order() const { +- return sccOrder; +-} +- +-std::size_t TopologicallySortedSCCGraphAnalysis::sccOfIndex(const std::size_t index) const { +- return sccOrder.at(index); +-} +- +-std::size_t TopologicallySortedSCCGraphAnalysis::indexOfScc(const std::size_t scc) const { +- auto it = std::find(sccOrder.begin(), sccOrder.end(), scc); +- assert(it != sccOrder.end()); +- return (std::size_t)std::distance(sccOrder.begin(), it); +-} +- +-std::set TopologicallySortedSCCGraphAnalysis::indexOfScc( +- const std::set& sccs) const { +- std::set indices; +- for (const auto scc : sccs) { +- indices.insert(indexOfScc(scc)); +- } +- return indices; +-} +- + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/TopologicallySortedSCCGraph.h b/src/ast/analysis/TopologicallySortedSCCGraph.h +index 9f9213f..a2d4552 100644 +--- a/src/ast/analysis/TopologicallySortedSCCGraph.h ++++ b/src/ast/analysis/TopologicallySortedSCCGraph.h +@@ -42,17 +42,31 @@ class TopologicallySortedSCCGraphAnalysis : public Analysis { + public: + static constexpr const char* name = "topological-scc-graph"; + +- explicit TopologicallySortedSCCGraphAnalysis(); ++ TopologicallySortedSCCGraphAnalysis() : Analysis(name) {} + + void run(const TranslationUnit& translationUnit) override; + +- const std::vector& order() const; ++ const std::vector& order() const { ++ return sccOrder; ++ } + +- std::size_t sccOfIndex(const std::size_t index) const; ++ std::size_t sccOfIndex(const std::size_t index) const { ++ return sccOrder.at(index); ++ } + +- std::size_t indexOfScc(const std::size_t scc) const; ++ std::size_t indexOfScc(const std::size_t scc) const { ++ auto it = std::find(sccOrder.begin(), sccOrder.end(), scc); ++ assert(it != sccOrder.end()); ++ return (std::size_t)std::distance(sccOrder.begin(), it); ++ } + +- std::set indexOfScc(const std::set& sccs) const; ++ std::set indexOfScc(const std::set& sccs) const { ++ std::set indices; ++ for (const auto scc : sccs) { ++ indices.insert(indexOfScc(scc)); ++ } ++ return indices; ++ } + + /** Output topologically sorted strongly connected component graph in text format */ + void print(std::ostream& os) const override; +@@ -64,15 +78,12 @@ private: + /** The final topological ordering of the SCCs. */ + std::vector sccOrder; + +- /** The mapping from an SCC index to its maximum distance from roots */ +- std::map sccDistance; +- + /** Calculate the topological ordering cost of a permutation of as of yet unordered SCCs + using the ordered SCCs. Returns -1 if the given vector is not a valid topological ordering. */ + int topologicalOrderingCost(const std::vector& permutationOfSCCs) const; + + /** Recursive component for the forwards algorithm computing the topological ordering of the SCCs. */ +- void computeTopologicalOrdering(std::size_t scc, std::vector& visited); ++ void computeTopologicalOrdering(std::size_t scc, std::vector& visited); + }; + + } // namespace analysis +diff --git a/src/ast/analysis/UniqueKeys.cpp b/src/ast/analysis/UniqueKeys.cpp +new file mode 100644 +index 0000000..475eb6f +--- /dev/null ++++ b/src/ast/analysis/UniqueKeys.cpp +@@ -0,0 +1,581 @@ ++/* ++ * 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 UniqueKeys.cpp ++ * ++ * CountUniqueKeys are used for accumulating selectivity statistics for the auto scheduler ++ * This analysis determines which CountUniqueKeys statements to emit in the RAM ++ * ++ ***********************************************************************/ ++ ++#include "ast/analysis/UniqueKeys.h" ++#include "Global.h" ++#include "GraphUtils.h" ++#include "ast/BinaryConstraint.h" ++#include "ast/Constant.h" ++#include "ast/NilConstant.h" ++#include "ast/QualifiedName.h" ++#include "ast/Relation.h" ++#include "ast/StringConstant.h" ++#include "ast/SubsumptiveClause.h" ++#include "ast/UnnamedVariable.h" ++#include "ast2ram/utility/Utils.h" ++#include "ram/FloatConstant.h" ++#include "ram/SignedConstant.h" ++#include "ram/StringConstant.h" ++#include "ram/UnsignedConstant.h" ++#include "souffle/BinaryConstraintOps.h" ++#include "souffle/utility/ContainerUtil.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace souffle::ast::analysis { ++ ++const analysis::PowerSet& UniqueKeysAnalysis::getSubsets(std::size_t N, std::size_t K) const { ++ if (cache.count({N, K})) { ++ return cache.at({N, K}); ++ } ++ ++ // this powerset represents all possible subsets of cardinality K of the set {1,...,N} ++ analysis::PowerSet res; ++ ++ // specific combination ++ std::vector cur; ++ cur.reserve(K); ++ ++ // use bitmask for subset generation ++ std::string bitmask(K, 1); // K leading 1's ++ bitmask.resize(N, 0); // N-K trailing 0's ++ ++ // generate the next permutation of the bitmask ++ do { ++ cur.clear(); ++ ++ // construct the subset using the set bits in the bitmask ++ for (std::size_t i = 0; i < N; ++i) // [0..N-1] integers ++ { ++ if (bitmask[i]) { ++ cur.push_back(i); ++ } ++ } ++ res.push_back(cur); ++ } while (std::prev_permutation(bitmask.begin(), bitmask.end())); ++ ++ cache[std::make_pair(N, K)] = res; ++ return cache.at({N, K}); ++} ++ ++analysis::StratumUniqueKeys UniqueKeysAnalysis::computeRuleVersionStatements( ++ const std::set& scc, const ast::Clause& clause, ++ std::optional version, ast2ram::TranslationMode mode) { ++ auto* prog = program; ++ auto* poly = polyAnalysis; ++ auto sccAtoms = filter(ast::getBodyLiterals(clause), ++ [&](auto* atom) { return contains(scc, prog->getRelation(*atom)); }); ++ ++ auto translateConstant = [poly](const ast::Constant& constant) -> Own { ++ if (auto strConstant = as(constant)) { ++ return mk(strConstant->getConstant()); ++ } else if (isA(&constant)) { ++ return mk(0); ++ } else if (auto* numConstant = as(constant)) { ++ switch (poly->getInferredType(*numConstant)) { ++ case ast::NumericConstant::Type::Int: ++ return mk( ++ RamSignedFromString(numConstant->getConstant(), nullptr, 0)); ++ case ast::NumericConstant::Type::Uint: ++ return mk( ++ RamUnsignedFromString(numConstant->getConstant(), nullptr, 0)); ++ case ast::NumericConstant::Type::Float: ++ return mk(RamFloatFromString(numConstant->getConstant())); ++ } ++ } ++ fatal("unaccounted-for constant"); ++ }; ++ ++ analysis::StratumUniqueKeys statements; ++ ++ auto getClauseAtomName = [&sccAtoms, &version](const ast::Clause& clause, const ast::Atom* atom, ++ bool isRecursive, ast2ram::TranslationMode mode) { ++ using namespace souffle::ast2ram; ++ ++ if (isA(clause)) { ++ // find the dominated / dominating heads ++ const auto& body = clause.getBodyLiterals(); ++ auto dominatedHeadAtom = dynamic_cast(body[0]); ++ auto dominatingHeadAtom = dynamic_cast(body[1]); ++ ++ if (clause.getHead() == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getDeleteRelationName(atom->getQualifiedName()); ++ } ++ return getRejectRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatedHeadAtom == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatingHeadAtom == atom) { ++ switch (mode) { ++ case SubsumeRejectNewCurrent: ++ case SubsumeDeleteCurrentCurrent: ++ return getConcreteRelationName(atom->getQualifiedName()); ++ case SubsumeDeleteCurrentDelta: return getDeltaRelationName(atom->getQualifiedName()); ++ default: return getNewRelationName(atom->getQualifiedName()); ++ } ++ } ++ ++ if (isRecursive) { ++ if (sccAtoms.at(*version + 1) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ } ++ } ++ ++ if (!isRecursive) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ if (clause.getHead() == atom) { ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ if (sccAtoms.at(*version) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ return getConcreteRelationName(atom->getQualifiedName()); ++ }; ++ ++ using AtomIdx = std::size_t; ++ using AtomSet = std::set; ++ ++ AtomSet recursiveInCurrentStratum; ++ auto atoms = ast::getBodyLiterals(clause); ++ auto constraints = ast::getBodyLiterals(clause); ++ ++ for (auto* a : sccAtoms) { ++ for (AtomIdx i = 0; i < atoms.size(); ++i) { ++ if (*atoms[i] == *a) { ++ recursiveInCurrentStratum.insert(i); ++ } ++ } ++ } ++ ++ using VarName = std::string; ++ using VarSet = std::set; ++ using ArgIdx = std::size_t; ++ ++ // map variable name to constants if possible ++ std::unordered_map varToConstant; ++ ++ // map variables to necessary variables on other side of the equality ++ // i.e. x = y + z we should map x -> { y, z } ++ std::unordered_map varToOtherVars; ++ ++ // map variable name to the lower and upper bounds of the inequality ++ // i.e. EA < Addr < EA + Size we should map Addr -> { { EA }, { EA, Size } } ++ std::unordered_map> ineqToUpperLower; ++ ++ for (auto* constraint : constraints) { ++ auto* lhs = constraint->getLHS(); ++ auto* rhs = constraint->getRHS(); ++ ++ if (isIneqConstraint(constraint->getBaseOperator())) { ++ if (auto* var = as(lhs)) { ++ VarSet otherVars; ++ visit(rhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ if (isLessThan(constraint->getBaseOperator()) || isLessEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].second = otherVars; ++ } ++ if (isGreaterThan(constraint->getBaseOperator()) || ++ isGreaterEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].first = otherVars; ++ } ++ } ++ ++ if (auto* var = as(rhs)) { ++ VarSet otherVars; ++ visit(lhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ if (isLessThan(constraint->getBaseOperator()) || isLessEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].first = otherVars; ++ } ++ if (isGreaterThan(constraint->getBaseOperator()) || ++ isGreaterEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].second = otherVars; ++ } ++ } ++ } ++ ++ // only consider = constraint ++ if (!isEqConstraint(constraint->getBaseOperator())) { ++ continue; ++ } ++ ++ if (isA(lhs) && isA(rhs)) { ++ varToConstant[as(lhs)->getName()] = as(rhs); ++ continue; ++ } ++ ++ if (isA(lhs) && isA(rhs)) { ++ varToConstant[as(rhs)->getName()] = as(lhs); ++ continue; ++ } ++ ++ if (auto* var = as(lhs)) { ++ VarSet otherVars; ++ visit(rhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ varToOtherVars[var->getName()] = otherVars; ++ continue; ++ } ++ ++ if (auto* var = as(rhs)) { ++ VarSet otherVars; ++ visit(lhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ varToOtherVars[var->getName()] = otherVars; ++ continue; ++ } ++ } ++ ++ // check for bounded inequality i.e. EA < EA2 < EA + Size ++ for (auto& p : ineqToUpperLower) { ++ // consider this like an equality ++ auto& [lower, upper] = p.second; ++ if (!lower.empty() && !upper.empty() && ++ std::includes(upper.begin(), upper.end(), lower.begin(), lower.end())) { ++ varToOtherVars[p.first] = upper; ++ } ++ } ++ ++ std::unordered_map atomIdxToGroundedVars; ++ for (AtomIdx i = 0; i < atoms.size(); ++i) { ++ VarSet groundedVars; ++ visit(*atoms[i], [&](const ast::Variable& v) { groundedVars.insert(v.getName()); }); ++ atomIdxToGroundedVars[i] = groundedVars; ++ } ++ ++ std::unordered_map> atomToIdxConstants; ++ ++ VecOwn constants; ++ ++ AtomIdx atomIdx = 0; ++ for (auto* atom : atoms) { ++ bool isRecursive = recursiveInCurrentStratum.count(atomIdx) > 0; ++ std::string name = getClauseAtomName(clause, atom, isRecursive, mode); ++ std::map idxConstant; ++ ++ ArgIdx varIdx = 0; ++ for (auto* argument : atom->getArguments()) { ++ // if we have a variable and a constraint of the form x = 2 then treat x as 2 ++ if (auto* var = as(argument)) { ++ if (varToConstant.count(var->getName()) > 0) { ++ argument = varToConstant.at(var->getName()); ++ } ++ } ++ ++ if (auto* constant = as(argument)) { ++ auto ramConstant = translateConstant(*constant); ++ idxConstant[varIdx] = ramConstant.get(); ++ constants.push_back(std::move(ramConstant)); ++ } ++ ++varIdx; ++ } ++ ++ atomToIdxConstants[atomIdx] = std::move(idxConstant); ++ ++atomIdx; ++ } ++ ++ // for each element in the atom ++ for (AtomIdx i = 0; i < atoms.size(); ++i) { ++ // construct the set S \ S[i] and S[i] ++ AtomSet otherAtoms; ++ for (AtomIdx j = 0; j < atoms.size(); ++j) { ++ if (i != j) { ++ otherAtoms.insert(j); ++ } ++ } ++ ++ // construct the set of variables that can be used for an indexed scan on this atom ++ VarSet varDependencies; ++ for (const auto& arg : atoms[i]->getArguments()) { ++ if (const auto* var = as(arg)) { ++ varDependencies.insert(var->getName()); ++ auto& dependentVars = varToOtherVars[var->getName()]; ++ varDependencies.insert(dependentVars.begin(), dependentVars.end()); ++ } ++ } ++ ++ // remove atoms which don't ground any variables in the current atom ++ AtomSet toRemove; ++ for (AtomIdx atomIdx : otherAtoms) { ++ auto& varsGroundedByAtom = atomIdxToGroundedVars[atomIdx]; ++ bool requiredAtom = std::any_of(varsGroundedByAtom.begin(), varsGroundedByAtom.end(), ++ [&varDependencies](const std::string& var) { return varDependencies.count(var) > 0; }); ++ if (!requiredAtom) { ++ toRemove.insert(atomIdx); ++ } ++ } ++ ++ for (auto idx : toRemove) { ++ otherAtoms.erase(idx); ++ } ++ ++ // Next step is to remove atoms which ground the same set of variables in the current atom ++ toRemove.clear(); ++ std::set relevantGroundedVars; ++ std::unordered_map atomIdxToRelevantGroundedVars; ++ for (AtomIdx atomIdx : otherAtoms) { ++ VarSet groundedVars; ++ auto& varsGroundedByAtom = atomIdxToGroundedVars[atomIdx]; ++ for (const auto& var : varsGroundedByAtom) { ++ if (varDependencies.count(var) > 0) { ++ groundedVars.insert(var); ++ } ++ } ++ if (relevantGroundedVars.count(groundedVars) > 0) { ++ toRemove.insert(atomIdx); ++ } else { ++ relevantGroundedVars.insert(groundedVars); ++ atomIdxToRelevantGroundedVars[atomIdx] = groundedVars; ++ } ++ } ++ ++ for (auto idx : toRemove) { ++ otherAtoms.erase(idx); ++ } ++ ++ auto N = otherAtoms.size(); ++ for (AtomIdx K = 0; K <= N; ++K) { ++ for (auto& subset : getSubsets(N, K)) { ++ auto* atom = atoms[i]; ++ // do set union of the atoms ++ ++ VarSet providedVars; ++ for (auto x : subset) { ++ auto it = otherAtoms.begin(); ++ std::advance(it, x); ++ auto atomIdx = *it; ++ auto& newVars = atomIdxToRelevantGroundedVars[atomIdx]; ++ providedVars.insert(newVars.begin(), newVars.end()); ++ } ++ ++ // construct the node ++ std::vector joinColumns; ++ const auto& args = atom->getArguments(); ++ std::size_t numBound = 0; ++ for (ArgIdx argIdx = 0; argIdx < args.size(); ++argIdx) { ++ auto* arg = args[argIdx]; ++ // if we have a constant or var = constant then we ignore ++ if (atomToIdxConstants.at(i).count(argIdx) > 0) { ++ ++numBound; ++ joinColumns.push_back(argIdx); ++ continue; ++ } ++ ++ // unnamed variable i.e. _ ++ if (isA(arg)) { ++ ++numBound; ++ continue; ++ } ++ ++ if (auto* var = as(arg)) { ++ // free variable so we can't join on it ++ if (varToOtherVars.count(var->getName()) > 0) { ++ auto& dependentVars = varToOtherVars.at(var->getName()); ++ if (!dependentVars.empty() && ++ std::includes(providedVars.begin(), providedVars.end(), ++ dependentVars.begin(), dependentVars.end())) { ++ joinColumns.push_back(argIdx); ++ ++numBound; ++ continue; ++ } ++ } ++ ++ // direct match on variable ++ if (providedVars.count(var->getName()) > 0) { ++ joinColumns.push_back(argIdx); ++ ++numBound; ++ continue; ++ } ++ } ++ } ++ ++ // construct a CountUniqueKeys ram node ++ bool isRecursive = recursiveInCurrentStratum.count(i) > 0; ++ auto relation = getClauseAtomName(clause, atom, isRecursive, mode); ++ auto& constantMap = atomToIdxConstants.at(i); ++ ++ std::stringstream ss; ++ ss << relation << " " << joinColumns << " "; ++ for (auto& p : constantMap) { ++ ss << "(" << p.first << ", " << *p.second << ") "; ++ } ++ ss << isRecursive; ++ ++ if (seenNodes.count(ss.str()) == 0) { ++ auto node = mk( ++ relation, joinColumns, constantMap, isRecursive); ++ seenNodes.insert(ss.str()); ++ ++ if (!joinColumns.empty() || isRecursive) { ++ statements.push_back(std::move(node)); ++ } ++ } ++ } ++ } ++ } ++ return statements; ++} ++ ++std::vector UniqueKeysAnalysis::computeUniqueKeyStatements() { ++ auto* prog = program; ++ auto getSccAtoms = [prog](const ast::Clause* clause, const std::set& scc) { ++ const auto& sccAtoms = filter(ast::getBodyLiterals(*clause), ++ [&](const ast::Atom* atom) { return contains(scc, prog->getRelation(*atom)); }); ++ return sccAtoms; ++ }; ++ ++ const auto& sccOrdering = topsortSCCGraphAnalysis->order(); ++ ++ std::vector uniqueKeyStatements; ++ uniqueKeyStatements.resize(sccOrdering.size()); ++ ++ auto& config = Global::config(); ++ if (!config.has("index-stats")) { ++ return uniqueKeyStatements; ++ } ++ ++ // for each stratum (formed from scc ordering) ++ for (std::size_t i = 0; i < sccOrdering.size(); i++) { ++ analysis::StratumUniqueKeys stratumNodes; ++ ++ auto scc = sccOrdering[i]; ++ const std::set sccRelations = sccGraph->getInternalRelations(scc); ++ for (auto* rel : sccRelations) { ++ // Translate each recursive clasue ++ for (auto&& clause : program->getClauses(*rel)) { ++ // Assumption: no subsumption ++ assert(!isA(clause) && ++ "Error: assumed no subsumptive clauses while auto-scheduling!"); ++ auto sccAtoms = getSccAtoms(clause, sccRelations); ++ if (recursiveClauses->recursive(clause)) { ++ // for each rule version ++ for (std::size_t version = 0; version < sccAtoms.size(); version++) { ++ if (isA(clause)) { ++ using namespace souffle::ast2ram; ++ auto rejectNew = computeRuleVersionStatements( ++ sccRelations, *clause, {version}, TranslationMode::SubsumeRejectNewNew); ++ auto rejectNewCurrent = computeRuleVersionStatements(sccRelations, *clause, ++ {version}, TranslationMode::SubsumeRejectNewCurrent); ++ auto mode = (sccAtoms.size() > 1) ? TranslationMode::SubsumeDeleteCurrentCurrent ++ : TranslationMode::SubsumeDeleteCurrentDelta; ++ auto deleteCurrent = ++ computeRuleVersionStatements(sccRelations, *clause, {version}, mode); ++ ++ for (auto& s : rejectNew) { ++ stratumNodes.push_back(std::move(s)); ++ } ++ ++ for (auto& s : rejectNewCurrent) { ++ stratumNodes.push_back(std::move(s)); ++ } ++ ++ for (auto& s : deleteCurrent) { ++ stratumNodes.push_back(std::move(s)); ++ } ++ ++ } else { ++ auto res = computeRuleVersionStatements(sccRelations, *clause, {version}); ++ for (auto& s : res) { ++ stratumNodes.push_back(std::move(s)); ++ } ++ } ++ } ++ } else { ++ auto res = computeRuleVersionStatements(sccRelations, *clause, {}); ++ for (auto& s : res) { ++ stratumNodes.push_back(std::move(s)); ++ } ++ } ++ } ++ } ++ uniqueKeyStatements[scc] = std::move(stratumNodes); ++ } ++ ++ std::map relationToCompletedStratum; ++ ++ // first step is to compute the earliest stratum that a non-recursive relation completes ++ for (std::size_t i = 0; i < sccOrdering.size(); ++i) { ++ auto scc = sccOrdering[i]; ++ for (const auto& statement : uniqueKeyStatements[scc]) { ++ const auto& rel = statement->getRelation(); ++ ++ if (statement->isRecursiveRelation()) { ++ continue; ++ } ++ ++ if (relationToCompletedStratum.count(rel) == 0) { ++ assert(i > 0 && "Can't access non-recursive relation on stratum 0"); ++ relationToCompletedStratum[rel] = sccOrdering[i - 1]; ++ } ++ } ++ } ++ ++ for (std::size_t i = 0; i < sccOrdering.size(); ++i) { ++ auto scc = sccOrdering[i]; ++ for (auto& statement : uniqueKeyStatements[scc]) { ++ const auto& rel = statement->getRelation(); ++ if (statement->isRecursiveRelation()) { ++ continue; ++ } ++ // sanity check that we have an earliest stratum ++ assert(relationToCompletedStratum.count(rel) > 0 && ++ "Must have earliest stratum where relation is fully computed!"); ++ std::size_t newStratum = relationToCompletedStratum.at(rel); ++ ++ // move the node into the new stratum ++ uniqueKeyStatements[newStratum].push_back(std::move(statement)); ++ } ++ ++ // erase remove all nullptr from the vector since moved from unique_ptr are guaranteed to be nullptr ++ auto& v = uniqueKeyStatements[scc]; ++ v.erase(std::remove(v.begin(), v.end(), nullptr), v.end()); ++ } ++ return uniqueKeyStatements; ++} ++ ++void UniqueKeysAnalysis::run(const TranslationUnit& translationUnit) { ++ program = &translationUnit.getProgram(); ++ sccGraph = &translationUnit.getAnalysis(); ++ topsortSCCGraphAnalysis = &translationUnit.getAnalysis(); ++ recursiveClauses = &translationUnit.getAnalysis(); ++ polyAnalysis = &translationUnit.getAnalysis(); ++ uniqueKeyStatements = computeUniqueKeyStatements(); ++} ++ ++void UniqueKeysAnalysis::print(std::ostream& os) const { ++ os << "Begin UniqueKeyStatements\n"; ++ for (std::size_t i = 0; i < uniqueKeyStatements.size(); ++i) { ++ os << "Stratum: " << i << "\n"; ++ for (auto& s : uniqueKeyStatements[i]) { ++ os << *s << "\n"; ++ } ++ } ++ os << "End UniqueKeyStatements\n"; ++} ++ ++} // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/UniqueKeys.h b/src/ast/analysis/UniqueKeys.h +new file mode 100644 +index 0000000..c7fff08 +--- /dev/null ++++ b/src/ast/analysis/UniqueKeys.h +@@ -0,0 +1,79 @@ ++/* ++ * 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 UniqueKeys.h ++ ++ * Computes for every stratum, which CountUniqueKeys nodes to emit in the RAM ++ * This is useful for the auto-scheduler to accumulate selectivity statistics ++ * ++ ***********************************************************************/ ++ ++#pragma once ++ ++#include "ast/Program.h" ++#include "ast/Relation.h" ++#include "ast/TranslationUnit.h" ++#include "ast/analysis/RecursiveClauses.h" ++#include "ast/analysis/SCCGraph.h" ++#include "ast/analysis/TopologicallySortedSCCGraph.h" ++#include "ast/analysis/typesystem/PolymorphicObjects.h" ++#include "ast/utility/Visitor.h" ++#include "ast2ram/ClauseTranslator.h" ++#include "ram/CountUniqueKeys.h" ++#include "ram/Expression.h" ++#include ++#include ++#include ++#include ++#include ++ ++namespace souffle::ast::analysis { ++ ++/** ++ * Analysis pass computing a schedule for computing relations. ++ */ ++using PowerSet = std::vector>; ++using StratumUniqueKeys = std::vector>; ++ ++class UniqueKeysAnalysis : public Analysis { ++public: ++ static constexpr const char* name = "unique-keys"; ++ ++ UniqueKeysAnalysis() : Analysis(name) {} ++ ++ void run(const TranslationUnit& translationUnit) override; ++ ++ /** Dump this relation schedule to standard error. */ ++ void print(std::ostream& os) const override; ++ ++ const StratumUniqueKeys& getUniqueKeyStatementsInSCC(std::size_t scc) const { ++ return uniqueKeyStatements[scc]; ++ } ++ ++private: ++ std::vector uniqueKeyStatements; ++ ++ std::set seenNodes; ++ ast::Program* program = nullptr; ++ SCCGraphAnalysis* sccGraph = nullptr; ++ TopologicallySortedSCCGraphAnalysis* topsortSCCGraphAnalysis = nullptr; ++ RecursiveClausesAnalysis* recursiveClauses = nullptr; ++ PolymorphicObjectsAnalysis* polyAnalysis = nullptr; ++ ++ // for each stratum compute the CountUniqueKeys nodes to emit ++ std::vector computeUniqueKeyStatements(); ++ StratumUniqueKeys computeRuleVersionStatements(const std::set& sccRelations, ++ const ast::Clause& clause, std::optional version, ++ ast2ram::TranslationMode mode = ast2ram::TranslationMode::DEFAULT); ++ const PowerSet& getSubsets(std::size_t N, std::size_t K) const; ++ mutable std::map, PowerSet> cache; ++}; ++ ++} // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/typesystem/PolymorphicObjects.cpp b/src/ast/analysis/typesystem/PolymorphicObjects.cpp +index d13113d..7d2e07e 100644 +--- a/src/ast/analysis/typesystem/PolymorphicObjects.cpp ++++ b/src/ast/analysis/typesystem/PolymorphicObjects.cpp +@@ -46,7 +46,7 @@ BinaryConstraintOp PolymorphicObjectsAnalysis::getOverloadedOperator(const Binar + return typeAnalysis->getPolymorphicOperator(bc); + } + +-AggregateOp PolymorphicObjectsAnalysis::getOverloadedOperator(const IntrinsicAggregator& agg) const { ++AggregateOp PolymorphicObjectsAnalysis::getOverloadedOperator(const Aggregator& agg) const { + return typeAnalysis->getPolymorphicOperator(agg); + } + +diff --git a/src/ast/analysis/typesystem/PolymorphicObjects.h b/src/ast/analysis/typesystem/PolymorphicObjects.h +index 9b88b6d..b7c1319 100644 +--- a/src/ast/analysis/typesystem/PolymorphicObjects.h ++++ b/src/ast/analysis/typesystem/PolymorphicObjects.h +@@ -26,7 +26,7 @@ enum class FunctorOp; + } // namespace souffle + + namespace souffle::ast { +-class IntrinsicAggregator; ++class Aggregator; + class BinaryConstraint; + class IntrinsicFunctor; + class TranslationUnit; +@@ -57,7 +57,7 @@ public: + BinaryConstraintOp getOverloadedOperator(const BinaryConstraint& bc) const; + + // Aggregators +- AggregateOp getOverloadedOperator(const IntrinsicAggregator& agg) const; ++ AggregateOp getOverloadedOperator(const Aggregator& agg) const; + + private: + const TypeAnalysis* typeAnalysis = nullptr; +diff --git a/src/ast/analysis/typesystem/SumTypeBranches.h b/src/ast/analysis/typesystem/SumTypeBranches.h +index 8ca530d..cfd6327 100644 +--- a/src/ast/analysis/typesystem/SumTypeBranches.h ++++ b/src/ast/analysis/typesystem/SumTypeBranches.h +@@ -51,7 +51,7 @@ public: + } + + private: +- OrderedQualifiedNameMap branchToType; ++ std::map branchToType; + }; + + } // namespace analysis +diff --git a/src/ast/analysis/typesystem/Type.cpp b/src/ast/analysis/typesystem/Type.cpp +index b50ae83..fb06511 100644 +--- a/src/ast/analysis/typesystem/Type.cpp ++++ b/src/ast/analysis/typesystem/Type.cpp +@@ -23,7 +23,6 @@ + #include "ast/BinaryConstraint.h" + #include "ast/BranchInit.h" + #include "ast/Clause.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" + #include "ast/Negation.h" + #include "ast/NumericConstant.h" +@@ -31,12 +30,10 @@ + #include "ast/Relation.h" + #include "ast/TranslationUnit.h" + #include "ast/TypeCast.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/UserDefinedFunctor.h" + #include "ast/Variable.h" + #include "ast/analysis/Constraint.h" + #include "ast/analysis/ConstraintSystem.h" +-#include "ast/analysis/ErrorAnalyzer.h" + #include "ast/analysis/typesystem/SumTypeBranches.h" + #include "ast/analysis/typesystem/TypeConstrainsAnalysis.h" + #include "ast/analysis/typesystem/TypeConstraints.h" +@@ -73,12 +70,12 @@ Own TypeAnalysis::createAnnotatedClause( + Own operator()(Own node) const override { + if (auto* var = as(node)) { + std::stringstream newVarName; +- newVarName << var->getName() << "∈" << types.find(var)->second; ++ newVarName << var->getName() << "∈" << types.find(var)->second; + return mk(newVarName.str()); + } else if (auto* var = as(node)) { + std::stringstream newVarName; + newVarName << "_" +- << "∈" << types.find(var)->second; ++ << "∈" << types.find(var)->second; + return mk(newVarName.str()); + } + node->apply(*this); +@@ -125,9 +122,9 @@ Own TypeAnalysis::createAnnotatedClause( + return annotatedClause; + } + +-std::map TypeAnalysis::analyseTypes(const TranslationUnit& tu, const Clause& clause, +- TypeErrorAnalyzer* errorAnalyzer, std::ostream* logs) { +- return TypeConstraintsAnalysis(tu).analyse(clause, errorAnalyzer, logs); ++std::map TypeAnalysis::analyseTypes( ++ const TranslationUnit& tu, const Clause& clause, std::ostream* logs) { ++ return TypeConstraintsAnalysis(tu).analyse(clause, logs); + } + + void TypeAnalysis::print(std::ostream& os) const { +@@ -196,35 +193,6 @@ std::vector TypeAnalysis::getFunctorParamTypeAttributes( + return res; + } + +-TypeAttribute TypeAnalysis::getAggregatorReturnTypeAttribute(const UserDefinedAggregator& aggregator) const { +- return getTypeAttribute(getAggregatorReturnType(aggregator)); +-} +- +-Type const& TypeAnalysis::getAggregatorReturnType(const UserDefinedAggregator& aggregator) const { +- return nameToType(functorAnalysis->getFunctorDeclaration(aggregator).getReturnType().getTypeName()); +-} +- +-Type const& TypeAnalysis::getAggregatorParamType( +- const UserDefinedAggregator& aggregator, std::size_t idx) const { +- return nameToType(functorAnalysis->getFunctorDeclaration(aggregator).getParams().at(idx)->getTypeName()); +-} +- +-TypeAttribute TypeAnalysis::getAggregatorParamTypeAttribute( +- const UserDefinedAggregator& aggregator, std::size_t idx) const { +- return getTypeAttribute(getAggregatorParamType(aggregator, idx)); +-} +- +-std::vector TypeAnalysis::getAggregatorParamTypeAttributes( +- const UserDefinedAggregator& aggregator) const { +- auto const& decl = functorAnalysis->getFunctorDeclaration(aggregator); +- std::vector res; +- res.reserve(decl.getArity()); +- auto const& params = decl.getParams(); +- std::transform(params.begin(), params.end(), std::back_inserter(res), +- [this](auto const& attr) { return nameToTypeAttribute(attr->getTypeName()); }); +- return res; +-} +- + const std::map& TypeAnalysis::getNumericConstantTypes() const { + return numericConstantType; + } +@@ -307,15 +275,8 @@ bool TypeAnalysis::hasValidTypeInfo(const Argument& argument) const { + } + } else if (auto* nc = as(argument)) { + return contains(numericConstantType, nc); +- } else if (auto* agg = as(argument)) { ++ } else if (auto* agg = as(argument)) { + return contains(aggregatorType, agg); +- } else if (auto* uda = as(argument)) { +- try { +- auto const& declaration = functorAnalysis->getFunctorDeclaration(*uda); +- return hasValidTypeInfo(declaration); +- } catch (...) { // functor hasn't been declared +- return false; +- } + } + return true; + } +@@ -339,7 +300,7 @@ BinaryConstraintOp TypeAnalysis::getPolymorphicOperator(const BinaryConstraint& + return constraintType.at(&bc); + } + +-AggregateOp TypeAnalysis::getPolymorphicOperator(const IntrinsicAggregator& agg) const { ++AggregateOp TypeAnalysis::getPolymorphicOperator(const Aggregator& agg) const { + assert(hasValidTypeInfo(agg) && "aggregator operator not set"); + return aggregatorType.at(&agg); + } +@@ -420,7 +381,7 @@ bool TypeAnalysis::analyseAggregators(const TranslationUnit& translationUnit) { + bool changed = false; + const auto& program = translationUnit.getProgram(); + +- auto setAggregatorType = [&](const IntrinsicAggregator& agg, TypeAttribute attr) { ++ auto setAggregatorType = [&](const Aggregator& agg, TypeAttribute attr) { + auto overloadedType = convertOverloadedAggregator(agg.getBaseOperator(), attr); + if (contains(aggregatorType, &agg) && aggregatorType.at(&agg) == overloadedType) { + return; +@@ -429,7 +390,7 @@ bool TypeAnalysis::analyseAggregators(const TranslationUnit& translationUnit) { + aggregatorType[&agg] = overloadedType; + }; + +- visit(program, [&](const IntrinsicAggregator& agg) { ++ visit(program, [&](const Aggregator& agg) { + if (isOverloadedAggregator(agg.getBaseOperator())) { + auto* targetExpression = agg.getTargetExpression(); + if (isFloat(targetExpression)) { +@@ -511,8 +472,7 @@ bool TypeAnalysis::isSymbol(const Argument* argument) const { + void TypeAnalysis::run(const TranslationUnit& translationUnit) { + // Check if debugging information is being generated + std::ostream* debugStream = nullptr; +- if (translationUnit.global().config().has("debug-report") || +- translationUnit.global().config().has("show", "type-analysis")) { ++ if (Global::config().has("debug-report") || Global::config().has("show", "type-analysis")) { + debugStream = &analysisLogs; + } + +@@ -530,8 +490,7 @@ void TypeAnalysis::run(const TranslationUnit& translationUnit) { + + // Analyse general argument types, clause by clause. + for (const Clause* clause : program.getClauses()) { +- auto clauseArgumentTypes = +- analyseTypes(translationUnit, *clause, errorAnalyzer.get(), debugStream); ++ auto clauseArgumentTypes = analyseTypes(translationUnit, *clause, debugStream); + argumentTypes.insert(clauseArgumentTypes.begin(), clauseArgumentTypes.end()); + + if (debugStream != nullptr) { +@@ -582,8 +541,6 @@ void TypeAnnotationPrinter::branchOnArgument(const Argument* cur, const Type& ty + print_(type_identity(), *as(cur)); + } else if (isA(*cur)) { + print_(type_identity(), *as(cur)); +- } else if (isA(*cur)) { +- print_(type_identity(), *as(cur)); + } else if (isA(*cur)) { + print_(type_identity(), *as(cur)); + } else { +@@ -707,7 +664,8 @@ void TypeAnnotationPrinter::print_(type_identity, const User + auto arguments = fun.getArguments(); + os << "@" << fun.getName() << "("; + for (std::size_t i = 0; i < arguments.size(); ++i) { +- const auto& ty = typeAnalysis.getFunctorParamType(fun, i); ++ TypeAttribute argType = typeAnalysis.getFunctorParamTypeAttribute(fun, i); ++ auto& ty = typeEnv.getConstantType(argType); + branchOnArgument(arguments[i], ty); + if (i + 1 < arguments.size()) { + os << ","; +@@ -720,11 +678,6 @@ void TypeAnnotationPrinter::print_(type_identity, [[maybe_unused]] cons + os << "$∈{number}"; + } + +-void TypeAnnotationPrinter::print_( +- type_identity, [[maybe_unused]] const IterationCounter& counter) { +- os << "$∈{unsigned}"; +-} +- + void TypeAnnotationPrinter::print_(type_identity, const ast::TypeCast& typeCast) { + os << "as("; + auto& ty = typeEnv.getType(typeCast.getType()); +@@ -735,7 +688,7 @@ void TypeAnnotationPrinter::print_(type_identity, const ast::TypeCast& + void TypeAnnotationPrinter::print_( + type_identity, const RecordInit& record, const RecordType& type) { + auto arguments = record.getArguments(); +- const auto& ftypes = type.getFields(); ++ auto& ftypes = type.getFields(); + os << "["; + for (std::size_t i = 0; i < arguments.size(); ++i) { + branchOnArgument(arguments[i], *ftypes[i]); +@@ -776,7 +729,7 @@ void TypeAnnotationPrinter::print_(type_identity, const BranchInit& + } + + void TypeAnnotationPrinter::print_(type_identity, const Aggregator& agg) { +- auto baseOperator = agg.getBaseOperatorName(); ++ auto baseOperator = agg.getBaseOperator(); + auto bodyLiterals = agg.getBodyLiterals(); + os << baseOperator << " "; + auto targetExpr = agg.getTargetExpression(); +@@ -824,9 +777,4 @@ void TypeAnnotationPrinter::printAnnotatedClause(const Clause& clause) { + os << "." << std::endl; + } + +-TypeAnalysis::TypeAnalysis() : Analysis(name) { +- errorAnalyzer = std::make_shared(); +-} +-TypeAnalysis::~TypeAnalysis() = default; +- + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/typesystem/Type.h b/src/ast/analysis/typesystem/Type.h +index ac87828..a49ee89 100644 +--- a/src/ast/analysis/typesystem/Type.h ++++ b/src/ast/analysis/typesystem/Type.h +@@ -21,7 +21,6 @@ + #include "ast/Clause.h" + #include "ast/NumericConstant.h" + #include "ast/TranslationUnit.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/analysis/Functor.h" + #include "ast/analysis/typesystem/SumTypeBranches.h" + #include "ast/analysis/typesystem/TypeEnvironment.h" +@@ -37,13 +36,11 @@ + namespace souffle::ast { + class Argument; + class Aggregator; +-class IntrinsicAggregator; + class BinaryConstraint; + class Clause; + class Functor; + class FunctorDeclaration; + class IntrinsicFunctor; +-class IterationCounter; + class NumericConstant; + class Type; + class UserDefinedFunctor; +@@ -61,12 +58,9 @@ class TypeEnvironment; + + class TypeAnalysis : public Analysis { + public: +- class TypeErrorAnalyzer; +- + static constexpr const char* name = "type-analysis"; + +- TypeAnalysis(); +- ~TypeAnalysis(); ++ TypeAnalysis() : Analysis(name) {} + + void run(const TranslationUnit& translationUnit) override; + +@@ -84,8 +78,8 @@ public: + * + * @return a map mapping each contained argument to a set of types + */ +- static std::map analyseTypes(const TranslationUnit& tu, const Clause& clause, +- TypeErrorAnalyzer* errorAnalyzer = nullptr, std::ostream* logs = nullptr); ++ static std::map analyseTypes( ++ const TranslationUnit& tu, const Clause& clause, std::ostream* logs = nullptr); + + // Checks whether an argument has been assigned a valid type + bool hasValidTypeInfo(const Argument& argument) const; +@@ -102,19 +96,10 @@ public: + TypeAttribute getFunctorParamTypeAttribute(const Functor& functor, std::size_t idx) const; + std::vector getFunctorParamTypeAttributes(const UserDefinedFunctor& functor) const; + +- /** -- User-defined Aggregator-related methods -- */ +- TypeAttribute getAggregatorReturnTypeAttribute(const UserDefinedAggregator& aggregator) const; +- Type const& getAggregatorReturnType(const UserDefinedAggregator& aggregator) const; +- Type const& getAggregatorParamType(const UserDefinedAggregator& aggregator, std::size_t idx) const; +- TypeAttribute getAggregatorParamTypeAttribute( +- const UserDefinedAggregator& aggregator, std::size_t idx) const; +- std::vector getAggregatorParamTypeAttributes( +- const UserDefinedAggregator& aggregator) const; +- + /** -- Polymorphism-related methods -- */ + NumericConstant::Type getPolymorphicNumericConstantType(const NumericConstant& nc) const; + const std::map& getNumericConstantTypes() const; +- AggregateOp getPolymorphicOperator(const IntrinsicAggregator& agg) const; ++ AggregateOp getPolymorphicOperator(const Aggregator& agg) const; + BinaryConstraintOp getPolymorphicOperator(const BinaryConstraint& bc) const; + FunctorOp getPolymorphicOperator(const IntrinsicFunctor& inf) const; + +@@ -123,11 +108,6 @@ private: + TypeEnvironment const* typeEnv = nullptr; + FunctorAnalysis const* functorAnalysis = nullptr; + std::map argumentTypes; +- +-public: +- std::shared_ptr errorAnalyzer; +- +-private: + VecOwn annotatedClauses; + std::stringstream analysisLogs; + const TranslationUnit* translationUnit; +@@ -139,7 +119,7 @@ private: + // Polymorphic objects analysis + std::map functorInfo; + std::map numericConstantType; +- std::map aggregatorType; ++ std::map aggregatorType; + std::map constraintType; + + bool analyseIntrinsicFunctors(const TranslationUnit& translationUnit); +@@ -190,7 +170,6 @@ private: + void print_(type_identity, const IntrinsicFunctor& fun); + void print_(type_identity, const UserDefinedFunctor& fun); + void print_(type_identity, const Counter& counter); +- void print_(type_identity, const IterationCounter& counter); + void print_(type_identity, const ast::TypeCast& typeCast); + void print_(type_identity, const RecordInit& record, const RecordType&); + void print_(type_identity, const BranchInit& adt); +diff --git a/src/ast/analysis/typesystem/TypeConstrainsAnalysis.cpp b/src/ast/analysis/typesystem/TypeConstrainsAnalysis.cpp +index 588ebfd..f6c3c32 100644 +--- a/src/ast/analysis/typesystem/TypeConstrainsAnalysis.cpp ++++ b/src/ast/analysis/typesystem/TypeConstrainsAnalysis.cpp +@@ -13,7 +13,6 @@ + ***********************************************************************/ + + #include "ast/analysis/typesystem/TypeConstrainsAnalysis.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/analysis/typesystem/TypeConstraints.h" + + namespace souffle::ast::analysis { +@@ -39,11 +38,7 @@ void TypeConstraintsAnalysis::visit_(type_identity, const Atom& atom) { + } + + iterateOverAtom(atom, [&](const Argument& argument, const Type& attributeType) { +- auto constraint = isSubtypeOf(getVar(argument), attributeType); +- addConstraint(constraint); +- if (errorAnalyzer) { +- errorAnalyzer->localizeConstraint(constraint, atom.getSrcLoc()); +- } ++ addConstraint(isSubtypeOf(getVar(argument), attributeType)); + }); + } + +@@ -174,10 +169,6 @@ void TypeConstraintsAnalysis::visit_(type_identity, const Counter& coun + addConstraint(isSubtypeOf(getVar(counter), typeEnv.getConstantType(TypeAttribute::Signed))); + } + +-void TypeConstraintsAnalysis::visit_(type_identity, const IterationCounter& counter) { +- addConstraint(isSubtypeOf(getVar(counter), typeEnv.getConstantType(TypeAttribute::Unsigned))); +-} +- + void TypeConstraintsAnalysis::visit_(type_identity, const ast::TypeCast& typeCast) { + auto& typeName = typeCast.getType(); + if (!typeEnv.isType(typeName)) { +@@ -240,7 +231,7 @@ void TypeConstraintsAnalysis::visit_(type_identity, const BranchInit + } + } + +-void TypeConstraintsAnalysis::visit_(type_identity, const IntrinsicAggregator& agg) { ++void TypeConstraintsAnalysis::visit_(type_identity, const Aggregator& agg) { + if (agg.getBaseOperator() == AggregateOp::COUNT) { + addConstraint(isSubtypeOf(getVar(agg), typeEnv.getConstantType(TypeAttribute::Signed))); + } else if (agg.getBaseOperator() == AggregateOp::MEAN) { +@@ -256,17 +247,6 @@ void TypeConstraintsAnalysis::visit_(type_identity, const I + } + } + +-void TypeConstraintsAnalysis::visit_(type_identity, const UserDefinedAggregator& agg) { +- auto const& init = agg.getInit(); +- +- Type const& returnType = typeAnalysis.getAggregatorReturnType(agg); +- addConstraint(isSubtypeOf(getVar(init), returnType)); +- addConstraint(isSubtypeOf(getVar(agg), returnType)); +- +- Type const& argType = typeAnalysis.getAggregatorParamType(agg, 1); +- addConstraint(isSubtypeOf(getVar(agg.getTargetExpression()), argType)); +-} +- + void TypeConstraintsAnalysis::iterateOverAtom( + const Atom& atom, std::function map) { + // get relation +diff --git a/src/ast/analysis/typesystem/TypeConstrainsAnalysis.h b/src/ast/analysis/typesystem/TypeConstrainsAnalysis.h +index e55f9a7..627fe51 100644 +--- a/src/ast/analysis/typesystem/TypeConstrainsAnalysis.h ++++ b/src/ast/analysis/typesystem/TypeConstrainsAnalysis.h +@@ -60,12 +60,10 @@ private: + void visit_(type_identity, const IntrinsicFunctor& fun) override; + void visit_(type_identity, const UserDefinedFunctor& fun) override; + void visit_(type_identity, const Counter& counter) override; +- void visit_(type_identity, const IterationCounter& counter) override; + void visit_(type_identity, const ast::TypeCast& typeCast) override; + void visit_(type_identity, const RecordInit& record) override; + void visit_(type_identity, const BranchInit& adt) override; +- void visit_(type_identity, const IntrinsicAggregator& agg) override; +- void visit_(type_identity, const UserDefinedAggregator& agg) override; ++ void visit_(type_identity, const Aggregator& agg) override; + }; + + } // namespace souffle::ast::analysis +\ No newline at end of file +diff --git a/src/ast/analysis/typesystem/TypeConstraints.cpp b/src/ast/analysis/typesystem/TypeConstraints.cpp +index 5660d11..2819cb2 100644 +--- a/src/ast/analysis/typesystem/TypeConstraints.cpp ++++ b/src/ast/analysis/typesystem/TypeConstraints.cpp +@@ -30,7 +30,6 @@ TypeConstraint isSubtypeOf(const TypeVar& a, const TypeVar& b) { + * a are subtypes of type b. + */ + TypeConstraint isSubtypeOf(const TypeVar& variable, const Type& type) { +- // @here + struct C : public Constraint { + TypeVar variable; + const Type& type; +@@ -62,13 +61,6 @@ TypeConstraint isSubtypeOf(const TypeVar& variable, const Type& type) { + void print(std::ostream& out) const override { + out << variable << " <: " << type.getName(); + } +- +- std::optional customMessage() const override { +- std::stringstream ss; +- ss << "Type of variable '" << variable.name() << "' should be a subtype of '" +- << type.getPrettyName() << "'"; +- return ss.str(); +- } + }; + + return std::make_shared(variable, type); +diff --git a/src/ast/analysis/typesystem/TypeConstraints.h b/src/ast/analysis/typesystem/TypeConstraints.h +index 537335f..dedb7f3 100644 +--- a/src/ast/analysis/typesystem/TypeConstraints.h ++++ b/src/ast/analysis/typesystem/TypeConstraints.h +@@ -58,13 +58,6 @@ struct all_type_factory { + } + }; + +-template <> +-struct detail::default_is_valid_op { +- bool operator()(const TypeSet& a) { +- return !a.empty(); +- } +-}; +- + /** + * The type lattice forming the property space for the Type analysis. The + * value set is given by sets of types and the meet operator is based on the +@@ -88,6 +81,4 @@ TypeConstraint satisfiesOverload(const TypeEnvironment& typeEnv, IntrinsicFuncto + TypeConstraint isSubtypeOfComponent( + const TypeVar& elementVariable, const TypeVar& recordVariable, std::size_t index); + +-class TypeAnalysis::TypeErrorAnalyzer : public ErrorAnalyzer {}; +- + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/typesystem/TypeEnvironment.cpp b/src/ast/analysis/typesystem/TypeEnvironment.cpp +index d432728..326cafe 100644 +--- a/src/ast/analysis/typesystem/TypeEnvironment.cpp ++++ b/src/ast/analysis/typesystem/TypeEnvironment.cpp +@@ -15,6 +15,7 @@ + ***********************************************************************/ + + #include "ast/analysis/typesystem/TypeEnvironment.h" ++#include "GraphUtils.h" + #include "ast/AlgebraicDataType.h" + #include "ast/AliasType.h" + #include "ast/Attribute.h" +@@ -26,7 +27,6 @@ + #include "ast/Type.h" + #include "ast/UnionType.h" + #include "ast/analysis/typesystem/TypeSystem.h" +-#include "souffle/datastructure/Graph.h" + #include "souffle/utility/MiscUtil.h" + #include "souffle/utility/tinyformat.h" + #include +@@ -40,10 +40,8 @@ namespace souffle::ast::analysis { + + namespace { + +-using QNGraph = GraphLabeled; +- +-QNGraph createTypeDependencyGraph(const std::vector& programTypes) { +- QNGraph typeDependencyGraph; ++Graph createTypeDependencyGraph(const std::vector& programTypes) { ++ Graph typeDependencyGraph; + for (const auto* astType : programTypes) { + if (auto type = as(astType)) { + typeDependencyGraph.insert(type->getQualifiedName(), type->getBaseType()); +@@ -79,9 +77,9 @@ QNGraph createTypeDependencyGraph(const std::vector& programTypes) { + /** + * Find all the type with a cyclic definition (in terms of being a subtype/alias) + */ +-UnorderedQualifiedNameSet analyseCyclicTypes( +- const QNGraph& dependencyGraph, const std::vector& programTypes) { +- UnorderedQualifiedNameSet cyclicTypes; ++std::set analyseCyclicTypes( ++ const Graph& dependencyGraph, const std::vector& programTypes) { ++ std::set cyclicTypes; + for (const auto& astType : programTypes) { + QualifiedName typeName = astType->getQualifiedName(); + if (dependencyGraph.reaches(typeName, typeName)) { +@@ -94,10 +92,10 @@ UnorderedQualifiedNameSet analyseCyclicTypes( + /** + * Find all the primitive types that are the subtypes of the union types. + */ +-UnorderedQualifiedNameMap analysePrimitiveTypesInUnion( +- const QNGraph& dependencyGraph, const std::vector& programTypes, ++std::map> analysePrimitiveTypesInUnion( ++ const Graph& dependencyGraph, const std::vector& programTypes, + const TypeEnvironment& env) { +- UnorderedQualifiedNameMap primitiveTypesInUnions; ++ std::map> primitiveTypesInUnions; + + for (const auto& astType : programTypes) { + auto* unionType = as(astType); +@@ -131,13 +129,13 @@ void TypeEnvironmentAnalysis::run(const TranslationUnit& translationUnit) { + const Program& program = translationUnit.getProgram(); + + auto rawProgramTypes = program.getTypes(); +- QNGraph typeDependencyGraph{createTypeDependencyGraph(rawProgramTypes)}; ++ Graph typeDependencyGraph{createTypeDependencyGraph(rawProgramTypes)}; + + cyclicTypes = analyseCyclicTypes(typeDependencyGraph, rawProgramTypes); + + primitiveTypesInUnions = analysePrimitiveTypesInUnion(typeDependencyGraph, rawProgramTypes, env); + +- UnorderedQualifiedNameMap nameToType; ++ std::map nameToType; + + // Filter redefined primitive types and cyclic types. + std::vector programTypes; +@@ -155,7 +153,7 @@ void TypeEnvironmentAnalysis::run(const TranslationUnit& translationUnit) { + } + + const Type* TypeEnvironmentAnalysis::createType( +- const QualifiedName& typeName, const UnorderedQualifiedNameMap& nameToType) { ++ const QualifiedName& typeName, const std::map& nameToType) { + // base case + if (env.isType(typeName)) { + return &env.getType(typeName); +diff --git a/src/ast/analysis/typesystem/TypeEnvironment.h b/src/ast/analysis/typesystem/TypeEnvironment.h +index 867204a..6176c3f 100644 +--- a/src/ast/analysis/typesystem/TypeEnvironment.h ++++ b/src/ast/analysis/typesystem/TypeEnvironment.h +@@ -42,7 +42,7 @@ public: + return env; + } + +- const UnorderedQualifiedNameSet& getPrimitiveTypesInUnion(const QualifiedName& identifier) const { ++ const std::set& getPrimitiveTypesInUnion(const QualifiedName& identifier) const { + return primitiveTypesInUnions.at(identifier); + } + +@@ -52,15 +52,15 @@ public: + + private: + TypeEnvironment env; +- UnorderedQualifiedNameMap primitiveTypesInUnions; +- UnorderedQualifiedNameSet cyclicTypes; ++ std::map> primitiveTypesInUnions; ++ std::set cyclicTypes; + + /** + * Recursively create a type in env, that is + * first create its base types and then the type itself. + */ + const Type* createType( +- const QualifiedName& typeName, const UnorderedQualifiedNameMap& nameToType); ++ const QualifiedName& typeName, const std::map& nameToType); + }; + + } // namespace souffle::ast::analysis +diff --git a/src/ast/analysis/typesystem/TypeSystem.cpp b/src/ast/analysis/typesystem/TypeSystem.cpp +index f3ca043..c891e59 100644 +--- a/src/ast/analysis/typesystem/TypeSystem.cpp ++++ b/src/ast/analysis/typesystem/TypeSystem.cpp +@@ -45,22 +45,18 @@ void RecordType::print(std::ostream& out) const { + } + + TypeSet TypeEnvironment::initializeConstantTypes() { +- auto& signedConstant = createType( +- QualifiedName::fromString("__numberConstant"), QualifiedName::fromString("number")); +- auto& floatConstant = createType( +- QualifiedName::fromString("__floatConstant"), QualifiedName::fromString("float")); +- auto& symbolConstant = createType( +- QualifiedName::fromString("__symbolConstant"), QualifiedName::fromString("symbol")); +- auto& unsignedConstant = createType( +- QualifiedName::fromString("__unsignedConstant"), QualifiedName::fromString("unsigned")); ++ auto& signedConstant = createType("__numberConstant"); ++ auto& floatConstant = createType("__floatConstant"); ++ auto& symbolConstant = createType("__symbolConstant"); ++ auto& unsignedConstant = createType("__unsignedConstant"); + + return TypeSet(signedConstant, floatConstant, symbolConstant, unsignedConstant); + } + + TypeSet TypeEnvironment::initializePrimitiveTypes() { +-#define CREATE_PRIMITIVE(TYPE) \ +- auto& TYPE##Type = createType(QualifiedName::fromString(#TYPE), \ +- static_cast(getType(QualifiedName::fromString("__" #TYPE "Constant")))); ++#define CREATE_PRIMITIVE(TYPE) \ ++ auto& TYPE##Type = createType( \ ++ #TYPE, static_cast(getType("__" #TYPE "Constant"))); + + CREATE_PRIMITIVE(number); + CREATE_PRIMITIVE(float); +diff --git a/src/ast/analysis/typesystem/TypeSystem.h b/src/ast/analysis/typesystem/TypeSystem.h +index cba1fd8..2fdd748 100644 +--- a/src/ast/analysis/typesystem/TypeSystem.h ++++ b/src/ast/analysis/typesystem/TypeSystem.h +@@ -51,20 +51,6 @@ class TypeEnvironment; + */ + class Type { + public: +- /// LLVM-style no-RTTI dynamic cast +- // clang-format off +- enum TypeKind { +- TK_ConstantType, +- TK_SubsetType, +- TK_PrimitiveType, +- TK_LastSubsetType, +- TK_AliasType, +- TK_UnionType, +- TK_RecordType, +- TK_AlgebraicDataType +- }; +- // clang-format on +- + Type(const Type& other) = delete; + + virtual ~Type() = default; +@@ -73,11 +59,6 @@ public: + return name; + } + +- const QualifiedName& getPrettyName() const { +- if (maybePrettyName) return *maybePrettyName; +- return name; +- } +- + const TypeEnvironment& getTypeEnvironment() const { + return environment; + } +@@ -91,7 +72,7 @@ public: + } + + bool operator<(const Type& other) const { +- return name.lexicalLess(other.name); ++ return name < other.name; + } + + virtual void print(std::ostream& out) const { +@@ -102,25 +83,15 @@ public: + return t.print(out), out; + } + +- TypeKind getKind() const { +- return kind; +- } +- +-private: +- const TypeKind kind; +- + protected: +- explicit Type(TypeKind tyKind, const TypeEnvironment& environment, QualifiedName name, +- std::optional maybePrettyName = std::nullopt) +- : kind(tyKind), environment(environment), name(std::move(name)), +- maybePrettyName(std::move(maybePrettyName)) {} ++ Type(const TypeEnvironment& environment, QualifiedName name) ++ : environment(environment), name(std::move(name)) {} + + /** Type environment of type */ + const TypeEnvironment& environment; + + /** Qualified type name */ + QualifiedName name; +- std::optional maybePrettyName; + }; + + /** +@@ -133,14 +104,7 @@ protected: + * FloatConstant | SymbolConstant + */ + class ConstantType : public Type { +- ConstantType(const TypeEnvironment& environment, const QualifiedName& name, +- std::optional maybePrettyName = std::nullopt) +- : Type(TK_ConstantType, environment, name, std::move(maybePrettyName)) {} +- +-public: +- static bool classof(const Type* t) { +- return t->getKind() == TK_ConstantType; +- } ++ ConstantType(const TypeEnvironment& environment, const QualifiedName& name) : Type(environment, name) {} + + private: + friend class TypeEnvironment; +@@ -156,7 +120,7 @@ private: + * + * where T is a type. + */ +-class SubsetType : public Type { ++class SubsetType : virtual public Type { + public: + void print(std::ostream& out) const override; + +@@ -164,16 +128,9 @@ public: + return baseType; + } + +- static bool classof(const Type* t) { +- return t->getKind() >= TK_SubsetType && t->getKind() < TK_LastSubsetType; +- } +- +- SubsetType(TypeKind kind, const TypeEnvironment& environment, const QualifiedName& name, const Type& base) +- : Type(kind, environment, name), baseType(base) {} +- + protected: + SubsetType(const TypeEnvironment& environment, const QualifiedName& name, const Type& base) +- : SubsetType(TK_SubsetType, environment, name, base) {} ++ : Type(environment, name), baseType(base){}; + + private: + friend class TypeEnvironment; +@@ -200,13 +157,9 @@ public: + return aliasType; + } + +- static bool classof(const Type* t) { +- return t->getKind() == TK_AliasType; +- } +- + protected: + AliasType(const TypeEnvironment& environment, const QualifiedName& name, const Type& alias) +- : Type(TK_AliasType, environment, name), aliasType(alias){}; ++ : Type(environment, name), aliasType(alias){}; + + private: + friend class TypeEnvironment; +@@ -232,13 +185,9 @@ public: + out << name; + } + +- static bool classof(const Type* t) { +- return t->getKind() == TK_PrimitiveType; +- } +- + protected: + PrimitiveType(const TypeEnvironment& environment, const QualifiedName& name, const ConstantType& base) +- : SubsetType(TK_PrimitiveType, environment, name, base) {} ++ : Type(environment, name), SubsetType(environment, name, base) {} + + private: + friend class TypeEnvironment; +@@ -267,14 +216,10 @@ public: + + void print(std::ostream& out) const override; + +- static bool classof(const Type* t) { +- return t->getKind() == TK_UnionType; +- } +- + protected: + UnionType(const TypeEnvironment& environment, const QualifiedName& name, + std::vector elementTypes = {}) +- : Type(TK_UnionType, environment, name), elementTypes(std::move(elementTypes)) {} ++ : Type(environment, name), elementTypes(std::move(elementTypes)) {} + + private: + friend class TypeEnvironment; +@@ -305,14 +250,10 @@ public: + + void print(std::ostream& out) const override; + +- static bool classof(const Type* t) { +- return t->getKind() == TK_RecordType; +- } +- + protected: + RecordType(const TypeEnvironment& environment, const QualifiedName& name, + const std::vector fields = {}) +- : Type(TK_RecordType, environment, name), fields(fields) {} ++ : Type(environment, name), fields(fields) {} + + private: + friend class TypeEnvironment; +@@ -361,7 +302,7 @@ public: + void setBranches(std::vector bs) { + branches = std::move(bs); + std::sort(branches.begin(), branches.end(), +- [](const Branch& left, const Branch& right) { return left.name.lexicalLess(right.name); }); ++ [](const Branch& left, const Branch& right) { return left.name < right.name; }); + } + + const std::vector& getBranchTypes(const QualifiedName& name) const { +@@ -377,13 +318,8 @@ public: + return branches; + } + +- static bool classof(const Type* t) { +- return t->getKind() == TK_AlgebraicDataType; +- } +- + protected: +- AlgebraicDataType(const TypeEnvironment& env, QualifiedName name) +- : Type(TK_AlgebraicDataType, env, std::move(name)) {} ++ AlgebraicDataType(const TypeEnvironment& env, QualifiedName name) : Type(env, std::move(name)) {} + + private: + friend class TypeEnvironment; +@@ -573,12 +509,12 @@ public: + + const Type& getConstantType(TypeAttribute type) const { + switch (type) { +- case TypeAttribute::Signed: return getType(QualifiedName::fromString("__numberConstant")); +- case TypeAttribute::Unsigned: return getType(QualifiedName::fromString("__unsignedConstant")); +- case TypeAttribute::Float: return getType(QualifiedName::fromString("__floatConstant")); +- case TypeAttribute::Symbol: return getType(QualifiedName::fromString("__symbolConstant")); +- case TypeAttribute::Record: return getType(QualifiedName::fromString("__numberConstant")); +- case TypeAttribute::ADT: return getType(QualifiedName::fromString("__numberConstant")); ++ case TypeAttribute::Signed: return getType("__numberConstant"); ++ case TypeAttribute::Unsigned: return getType("__unsignedConstant"); ++ case TypeAttribute::Float: return getType("__floatConstant"); ++ case TypeAttribute::Symbol: return getType("__symbolConstant"); ++ case TypeAttribute::Record: return getType("__numberConstant"); ++ case TypeAttribute::ADT: return getType("__numberConstant"); + } + fatal("Unhandled type"); + } +@@ -626,12 +562,11 @@ private: + TypeSet initializeConstantTypes(); + + /** Map for storing named types */ +- UnorderedQualifiedNameMap> types; ++ std::map> types; + + const TypeSet constantTypes = initializeConstantTypes(); +- const TypeSet constantNumericTypes = TypeSet(getType(QualifiedName::fromString("__numberConstant")), +- getType(QualifiedName::fromString("__unsignedConstant")), +- getType(QualifiedName::fromString("__floatConstant"))); ++ const TypeSet constantNumericTypes = ++ TypeSet(getType("__numberConstant"), getType("__unsignedConstant"), getType("__floatConstant")); + + const TypeSet primitiveTypes = initializePrimitiveTypes(); + }; +diff --git a/src/ast/tests/CMakeLists.txt b/src/ast/tests/CMakeLists.txt +index 359035a..4457758 100644 +--- a/src/ast/tests/CMakeLists.txt ++++ b/src/ast/tests/CMakeLists.txt +@@ -12,4 +12,3 @@ souffle_add_binary_test(ast_transformers_test ast) + souffle_add_binary_test(ast_utils_test ast) + souffle_add_binary_test(type_system_test ast) + souffle_add_binary_test(constraints_test ast) +-souffle_add_binary_test(ast_recursive_clauses_test ast) +diff --git a/src/ast/tests/ast_print_test.cpp b/src/ast/tests/ast_print_test.cpp +index 74f964c..536a125 100644 +--- a/src/ast/tests/ast_print_test.cpp ++++ b/src/ast/tests/ast_print_test.cpp +@@ -22,7 +22,6 @@ + #include "ast/Atom.h" + #include "ast/Clause.h" + #include "ast/Counter.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/Literal.h" + #include "ast/NilConstant.h" + #include "ast/Node.h" +@@ -44,15 +43,10 @@ + + namespace souffle::ast::test { + +-QualifiedName qn(std::string_view s) { +- return QualifiedName::fromString(s); +-} +- + inline Own makeATU(std::string program = ".decl A,B,C(x:number)") { +- Global glb; + ErrorReport e; +- DebugReport d(glb); +- return ParserDriver::parseTranslationUnit(glb, program, e, d); ++ DebugReport d; ++ return ParserDriver::parseTranslationUnit(program, e, d); + } + + inline Own makePrintedATU(Own& tu) { +@@ -62,7 +56,7 @@ inline Own makePrintedATU(Own& tu) { + } + + inline Own makeClauseA(Own headArgument) { +- auto clause = mk(qn("A")); ++ auto clause = mk("A"); + auto headAtom = clause->getHead(); + headAtom->addArgument(std::move(headArgument)); + return clause; +@@ -89,12 +83,11 @@ TEST(AstPrint, NumberConstant) { + } + + TEST(AstPrint, StringConstant) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); ++ DebugReport d; + auto testArgument = mk("test string"); + +- auto tu1 = ParserDriver::parseTranslationUnit(glb, ".decl A,B,C(x:number)", e, d); ++ auto tu1 = ParserDriver::parseTranslationUnit(".decl A,B,C(x:number)", e, d); + tu1->getProgram().addClause(makeClauseA(std::move(testArgument))); + auto tu2 = makePrintedATU(tu1); + EXPECT_EQ(tu1->getProgram(), tu2->getProgram()); +@@ -128,12 +121,12 @@ TEST(AstPrint, Counter) { + } + + TEST(AstPrint, AggregatorMin) { +- auto atom = mk(qn("B")); ++ auto atom = mk("B"); + atom->addArgument(mk("x")); +- auto min = mk(AggregateOp::MIN, mk("x")); ++ auto min = mk(AggregateOp::MIN, mk("x")); + + VecOwn body; +- body.push_back(mk(qn("B"))); ++ body.push_back(mk("B")); + + min->setBodyLiterals(std::move(body)); + +@@ -145,9 +138,9 @@ TEST(AstPrint, AggregatorMin) { + } + + TEST(AstPrint, AggregatorMax) { +- auto atom = mk(qn("B")); ++ auto atom = mk("B"); + atom->addArgument(mk("x")); +- auto max = mk(AggregateOp::MAX, mk("x")); ++ auto max = mk(AggregateOp::MAX, mk("x")); + + VecOwn body; + body.push_back(std::move(atom)); +@@ -161,9 +154,9 @@ TEST(AstPrint, AggregatorMax) { + } + + TEST(AstPrint, AggregatorCount) { +- auto atom = mk(qn("B")); ++ auto atom = mk("B"); + atom->addArgument(mk("x")); +- auto count = mk(AggregateOp::COUNT); ++ auto count = mk(AggregateOp::COUNT); + + VecOwn body; + body.push_back(std::move(atom)); +@@ -177,9 +170,9 @@ TEST(AstPrint, AggregatorCount) { + } + + TEST(AstPrint, AggregatorSum) { +- auto atom = mk(qn("B")); ++ auto atom = mk("B"); + atom->addArgument(mk("x")); +- auto sum = mk(AggregateOp::SUM, mk("x")); ++ auto sum = mk(AggregateOp::SUM, mk("x")); + + VecOwn body; + body.push_back(std::move(atom)); +diff --git a/src/ast/tests/ast_program_test.cpp b/src/ast/tests/ast_program_test.cpp +index cd405e1..8a31847 100644 +--- a/src/ast/tests/ast_program_test.cpp ++++ b/src/ast/tests/ast_program_test.cpp +@@ -22,7 +22,6 @@ + #include "ast/Atom.h" + #include "ast/Attribute.h" + #include "ast/Clause.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/Literal.h" + #include "ast/Node.h" + #include "ast/Program.h" +@@ -43,31 +42,29 @@ + namespace souffle::ast::test { + + inline Own makeATU(std::string program) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); +- return ParserDriver::parseTranslationUnit(glb, program, e, d); ++ DebugReport d; ++ return ParserDriver::parseTranslationUnit(program, e, d); + } + + inline Own makeClause(std::string name, Own headArgument) { +- auto clause = mk(QualifiedName::fromString(name)); ++ auto clause = mk(name); + auto headAtom = clause->getHead(); + headAtom->addArgument(std::move(headArgument)); + return clause; + } + + TEST(Program, Parse) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); ++ DebugReport d; + // check the empty program +- Own empty = ParserDriver::parseTranslationUnit(glb, "", e, d); ++ Own empty = ParserDriver::parseTranslationUnit("", e, d); + + EXPECT_TRUE(empty->getProgram().getTypes().empty()); + EXPECT_TRUE(empty->getProgram().getRelations().empty()); + + // check something simple +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type Node <: symbol + .decl e ( a : Node , b : Node ) +@@ -84,22 +81,21 @@ TEST(Program, Parse) { + EXPECT_EQ(1, prog.getTypes().size()); + EXPECT_EQ(2, prog.getRelations().size()); + +- EXPECT_TRUE(prog.getRelation(QualifiedName::fromString("e"))); +- EXPECT_TRUE(prog.getRelation(QualifiedName::fromString("r"))); +- EXPECT_FALSE(prog.getRelation(QualifiedName::fromString("n"))); ++ EXPECT_TRUE(prog.getRelation("e")); ++ EXPECT_TRUE(prog.getRelation("r")); ++ EXPECT_FALSE(prog.getRelation("n")); + } + +-#define TESTASTCLONEANDEQUAL(SUBTYPE, DL) \ +- TEST(Ast, CloneAndEqual##SUBTYPE) { \ +- Global glb; \ +- ErrorReport e; \ +- DebugReport d(glb); \ +- Own tu = ParserDriver::parseTranslationUnit(glb, DL, e, d); \ +- Program& program = tu->getProgram(); \ +- EXPECT_EQ(program, program); \ +- Own cl(clone(program)); \ +- EXPECT_NE(cl.get(), &program); \ +- EXPECT_EQ(*cl, program); \ ++#define TESTASTCLONEANDEQUAL(SUBTYPE, DL) \ ++ TEST(Ast, CloneAndEqual##SUBTYPE) { \ ++ ErrorReport e; \ ++ DebugReport d; \ ++ Own tu = ParserDriver::parseTranslationUnit(DL, e, d); \ ++ Program& program = tu->getProgram(); \ ++ EXPECT_EQ(program, program); \ ++ Own cl(clone(program)); \ ++ EXPECT_NE(cl.get(), &program); \ ++ EXPECT_EQ(*cl, program); \ + } + + TESTASTCLONEANDEQUAL(Program, +@@ -201,9 +197,9 @@ TESTASTCLONEANDEQUAL(RelationCopies, + + // should *not* remove anything because `removeClause` operates by identity, not equality + TEST(Program, RemoveClauseByEquality) { +- auto atom = mk(QualifiedName::fromString("B")); ++ auto atom = mk("B"); + atom->addArgument(mk("x")); +- auto sum = mk(AggregateOp::SUM, mk("x")); ++ auto sum = mk(AggregateOp::SUM, mk("x")); + VecOwn body; + body.push_back(std::move(atom)); + sum->setBodyLiterals(std::move(body)); +@@ -225,7 +221,7 @@ TEST(Program, RemoveClauseByEquality) { + + TEST(Program, RemoveClauseByIdentity) { + auto tu1 = makeATU(".decl A,B(x:number) \n A(sum x : B(x))."); +- auto clauses = tu1->getProgram().getClauses(QualifiedName::fromString("A")); ++ auto clauses = tu1->getProgram().getClauses("A"); + EXPECT_EQ(1, clauses.size()); + tu1->getProgram().removeClause(*clauses[0]); + +@@ -237,8 +233,8 @@ TEST(Program, AppendRelation) { + auto tu1 = makeATU(".decl A,B,C(x:number)"); + auto& prog1 = tu1->getProgram(); + auto rel = mk(); +- rel->setQualifiedName(QualifiedName::fromString("D")); +- rel->addAttribute(mk("x", QualifiedName::fromString("number"))); ++ rel->setQualifiedName("D"); ++ rel->addAttribute(mk("x", "number")); + prog1.addRelation(std::move(rel)); + auto tu2 = makeATU(".decl A,B,C,D(x:number)"); + EXPECT_EQ(tu1->getProgram(), tu2->getProgram()); +@@ -246,7 +242,7 @@ TEST(Program, AppendRelation) { + + TEST(Program, RemoveRelation) { + auto tu1 = makeATU(".decl A,B,C(x:number)"); +- EXPECT_TRUE(tu1->getProgram().removeRelation(QualifiedName::fromString("B"))); ++ EXPECT_TRUE(tu1->getProgram().removeRelation("B")); + auto tu2 = makeATU(".decl A,C(x:number)"); + EXPECT_EQ(tu1->getProgram(), tu2->getProgram()); + } +diff --git a/src/ast/tests/ast_transformers_test.cpp b/src/ast/tests/ast_transformers_test.cpp +index bcfac10..274e02a 100644 +--- a/src/ast/tests/ast_transformers_test.cpp ++++ b/src/ast/tests/ast_transformers_test.cpp +@@ -43,16 +43,11 @@ + namespace souffle::ast::transform::test { + using namespace analysis; + +-QualifiedName qn(std::string_view s) { +- return QualifiedName::fromString(s); +-} +- + TEST(Transformers, GroundTermPropagation) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + // load some test program +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D <: symbol + .decl p(a:D,b:D) +@@ -64,7 +59,7 @@ TEST(Transformers, GroundTermPropagation) { + Program& program = tu->getProgram(); + + // check types in clauses +- auto* a = program.getClauses(qn("p"))[0]; ++ auto* a = program.getClauses("p")[0]; + + EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = [x,y],\n s = r,\n s = [w,v],\n [w,v] = [a,b].", + toString(*a)); +@@ -80,11 +75,10 @@ TEST(Transformers, GroundTermPropagation) { + } + + TEST(Transformers, GroundTermPropagation2) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + // load some test program +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D <: symbol + .decl p(a:D,b:D) +@@ -96,7 +90,7 @@ TEST(Transformers, GroundTermPropagation2) { + Program& program = tu->getProgram(); + + // check types in clauses +- auto* a = program.getClauses(qn("p"))[0]; ++ auto* a = program.getClauses("p")[0]; + + EXPECT_EQ("p(a,b) :- \n p(x,y),\n x = y,\n x = a,\n y = b.", toString(*a)); + +@@ -109,10 +103,9 @@ TEST(Transformers, GroundTermPropagation2) { + + TEST(Transformers, ResolveGroundedAliases) { + // load some test program +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ DebugReport debugReport; ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D <: symbol + .decl p(a:D,b:D) +@@ -124,19 +117,18 @@ TEST(Transformers, ResolveGroundedAliases) { + Program& program = tu->getProgram(); + + EXPECT_EQ("p(a,b) :- \n p(x,y),\n r = [x,y],\n s = r,\n s = [w,v],\n [w,v] = [a,b].", +- toString(*program.getClauses(qn("p"))[0])); ++ toString(*program.getClauses("p")[0])); + + mk()->apply(*tu); + +- EXPECT_EQ("p(x,y) :- \n p(x,y).", toString(*program.getClauses(qn("p"))[0])); ++ EXPECT_EQ("p(x,y) :- \n p(x,y).", toString(*program.getClauses("p")[0])); + } + + TEST(Transformers, ResolveAliasesWithTermsInAtoms) { + // load some test program +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ DebugReport debugReport; ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D <: symbol + .decl p(a:D,b:D) +@@ -148,12 +140,12 @@ TEST(Transformers, ResolveAliasesWithTermsInAtoms) { + Program& program = tu->getProgram(); + + EXPECT_EQ("p(x,c) :- \n p(x,b),\n p(b,c),\n c = (b+1),\n x = (c+2).", +- toString(*program.getClauses(qn("p"))[0])); ++ toString(*program.getClauses("p")[0])); + + mk()->apply(*tu); + + EXPECT_EQ("p(x,c) :- \n p(x,b),\n p(b,c),\n c = (b+1),\n x = (c+2).", +- toString(*program.getClauses(qn("p"))[0])); ++ toString(*program.getClauses("p")[0])); + } + + /** +@@ -171,11 +163,10 @@ TEST(Transformers, ResolveAliasesWithTermsInAtoms) { + */ + + TEST(Transformers, RemoveRelationCopies) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + // load some test program +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D = number + .decl a(a:D,b:D) +@@ -217,11 +208,10 @@ TEST(Transformers, RemoveRelationCopies) { + * + */ + TEST(Transformers, RemoveRelationCopiesOutput) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + // load some test program +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type D = number + .decl a(a:D,b:D) +@@ -252,11 +242,10 @@ TEST(Transformers, RemoveRelationCopiesOutput) { + * Test the equivalence (or lack of equivalence) of clauses using the MinimiseProgramTransfomer. + */ + TEST(Transformers, CheckClausalEquivalence) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .decl A(x:number, y:number) + .decl B(x:number) +@@ -278,9 +267,9 @@ TEST(Transformers, CheckClausalEquivalence) { + + // Resolve aliases to remove trivial equalities + mk()->apply(*tu); +- auto aClauses = program.getClauses(qn("A")); +- auto bClauses = program.getClauses(qn("B")); +- auto cClauses = program.getClauses(qn("C")); ++ auto aClauses = program.getClauses("A"); ++ auto bClauses = program.getClauses("B"); ++ auto cClauses = program.getClauses("C"); + + EXPECT_EQ(3, aClauses.size()); + EXPECT_EQ("A(0,0).", toString(*aClauses[0])); +@@ -320,9 +309,9 @@ TEST(Transformers, CheckClausalEquivalence) { + + // Make sure equivalent (and only equivalent) clauses are removed by the minimiser + mk()->apply(*tu); +- auto&& aMinClauses = program.getClauses(qn("A")); +- auto&& bMinClauses = program.getClauses(qn("B")); +- auto&& cMinClauses = program.getClauses(qn("C")); ++ auto&& aMinClauses = program.getClauses("A"); ++ auto&& bMinClauses = program.getClauses("B"); ++ auto&& cMinClauses = program.getClauses("C"); + + EXPECT_EQ(2, aMinClauses.size()); + EXPECT_EQ("A(0,0).", toString(*aMinClauses[0])); +@@ -344,11 +333,10 @@ TEST(Transformers, CheckClausalEquivalence) { + * Test the equivalence (or lack of equivalence) of aggregators using the MinimiseProgramTransfomer. + */ + TEST(Transformers, CheckAggregatorEquivalence) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .decl A,B,C,D(X:number) input + // first and second are equivalent +@@ -380,13 +368,13 @@ TEST(Transformers, CheckAggregatorEquivalence) { + + // A, B, C, D should still be the relations + EXPECT_EQ(4, program.getRelations().size()); +- EXPECT_NE(nullptr, program.getRelation(qn("A"))); +- EXPECT_NE(nullptr, program.getRelation(qn("B"))); +- EXPECT_NE(nullptr, program.getRelation(qn("C"))); +- EXPECT_NE(nullptr, program.getRelation(qn("D"))); ++ EXPECT_NE(nullptr, program.getRelation("A")); ++ EXPECT_NE(nullptr, program.getRelation("B")); ++ EXPECT_NE(nullptr, program.getRelation("C")); ++ EXPECT_NE(nullptr, program.getRelation("D")); + + // D should now only have the two clauses non-equivalent clauses +- auto&& dClauses = program.getClauses(qn("D")); ++ auto&& dClauses = program.getClauses("D"); + EXPECT_EQ(2, dClauses.size()); + EXPECT_EQ( + "D(X) :- \n B(X),\n X < max Y : { C(Y),B(Y),Y < 2 },\n A(Z),\n Z = sum A : { C(A),B(A),A " +@@ -408,11 +396,10 @@ TEST(Transformers, CheckAggregatorEquivalence) { + * e.g. a(x) :- a(x), x != 0. is only true if a(x) is already true + */ + TEST(Transformers, RemoveClauseRedundancies) { +- Global glb; + ErrorReport errorReport; +- DebugReport debugReport(glb); ++ DebugReport debugReport; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .decl a,b,c(X:number) + a(0). +@@ -435,8 +422,8 @@ TEST(Transformers, RemoveClauseRedundancies) { + // In particular: Replacing relation `c` with `b` should not create a clause + // `b(x) :- b(x)` from `c(x) :- b(x)`. + mk()->apply(*tu); +- EXPECT_EQ(nullptr, program.getRelation(qn("c"))); +- auto&& bIntermediateClauses = program.getClauses(qn("b")); ++ EXPECT_EQ(nullptr, program.getRelation("c")); ++ auto&& bIntermediateClauses = program.getClauses("b"); + EXPECT_EQ(1, bIntermediateClauses.size()); + EXPECT_EQ("b(1).", toString(*bIntermediateClauses[0])); + +@@ -444,16 +431,16 @@ TEST(Transformers, RemoveClauseRedundancies) { + mk()->apply(*tu); + EXPECT_EQ(3, program.getRelations().size()); + +- auto&& aClauses = program.getClauses(qn("a")); ++ auto&& aClauses = program.getClauses("a"); + EXPECT_EQ(2, aClauses.size()); + EXPECT_EQ("a(0).", toString(*aClauses[0])); + EXPECT_EQ("a(X) :- \n b(X).", toString(*aClauses[1])); + +- auto&& bClauses = program.getClauses(qn("b")); ++ auto&& bClauses = program.getClauses("b"); + EXPECT_EQ(1, bClauses.size()); + EXPECT_EQ("b(1).", toString(*bClauses[0])); + +- auto&& qClauses = program.getClauses(qn("q")); ++ auto&& qClauses = program.getClauses("q"); + EXPECT_EQ(1, qClauses.size()); + EXPECT_EQ("q(X) :- \n a(X).", toString(*qClauses[0])); + } +@@ -466,11 +453,10 @@ TEST(Transformers, RemoveClauseRedundancies) { + * (4) MagicSetTransformer + */ + TEST(Transformers, MagicSetComprehensive) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); ++ DebugReport d; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + // Stratum 0 - Base Relations + .decl BaseOne(X:number) magic +diff --git a/src/ast/tests/ast_utils_test.cpp b/src/ast/tests/ast_utils_test.cpp +index 19deb0f..6839e6b 100644 +--- a/src/ast/tests/ast_utils_test.cpp ++++ b/src/ast/tests/ast_utils_test.cpp +@@ -46,13 +46,9 @@ namespace souffle::ast { + + namespace test { + +-QualifiedName qn(std::string_view s) { +- return QualifiedName::fromString(s); +-} +- + TEST(AstUtils, Grounded) { + // create an example clause: +- auto clause = mk(qn("r")); ++ auto clause = mk("r"); + + // something like: + // r(X,Y,Z) :- a(X), X = Y, !b(Z). +@@ -64,7 +60,7 @@ TEST(AstUtils, Grounded) { + head->addArgument(Own(new Variable("Z"))); + + // a(X) +- auto* a = new Atom(qn("a")); ++ auto* a = new Atom("a"); + a->addArgument(Own(new Variable("X"))); + clause->addToBody(Own(a)); + +@@ -74,7 +70,7 @@ TEST(AstUtils, Grounded) { + clause->addToBody(Own(e1)); + + // !b(Z) +- auto* b = new Atom(qn("b")); ++ auto* b = new Atom("b"); + b->addArgument(Own(new Variable("Z"))); + auto* neg = new Negation(Own(b)); + clause->addToBody(Own(neg)); +@@ -82,12 +78,11 @@ TEST(AstUtils, Grounded) { + // check construction + EXPECT_EQ("r(X,Y,Z) :- \n a(X),\n X = Y,\n !b(Z).", toString(*clause)); + +- Global glb; + auto program = mk(); + program->addClause(std::move(clause)); +- DebugReport dbgReport(glb); ++ DebugReport dbgReport; + ErrorReport errReport; +- TranslationUnit tu{glb, std::move(program), errReport, dbgReport}; ++ TranslationUnit tu{std::move(program), errReport, dbgReport}; + + // obtain groundness + auto isGrounded = analysis::getGroundedTerms(tu, *tu.getProgram().getClauses()[0]); +@@ -100,10 +95,9 @@ TEST(AstUtils, Grounded) { + } + + TEST(AstUtils, GroundedRecords) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ DebugReport d; ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .type N <: symbol + .type R = [ a : N, B : N ] +@@ -118,7 +112,7 @@ TEST(AstUtils, GroundedRecords) { + + Program& program = tu->getProgram(); + +- auto* clause = program.getClauses(qn("s"))[0]; ++ auto* clause = program.getClauses("s")[0]; + + // check construction + EXPECT_EQ("s(x) :- \n r([x,y]).", toString(*clause)); +@@ -138,11 +132,10 @@ TEST(AstUtils, GroundedRecords) { + } + + TEST(AstUtils, ReorderClauseAtoms) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); ++ DebugReport d; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .decl a,b,c,d,e(x:number) + a(x) :- b(x), c(x), 1 != 2, d(y), !e(z), c(z), e(x). +@@ -153,8 +146,8 @@ TEST(AstUtils, ReorderClauseAtoms) { + Program& program = tu->getProgram(); + EXPECT_EQ(5, program.getRelations().size()); + +- EXPECT_NE(program.getRelation(qn("a")), nullptr); +- auto&& clauses = program.getClauses(qn("a")); ++ EXPECT_NE(program.getRelation("a"), nullptr); ++ auto&& clauses = program.getClauses("a"); + EXPECT_EQ(1, clauses.size()); + + auto&& clause = clauses[0]; +@@ -175,11 +168,10 @@ TEST(AstUtils, ReorderClauseAtoms) { + } + + TEST(AstUtils, RemoveEquivalentClauses) { +- Global glb; + ErrorReport e; +- DebugReport d(glb); ++ DebugReport d; + +- Own tu = ParserDriver::parseTranslationUnit(glb, ++ Own tu = ParserDriver::parseTranslationUnit( + R"( + .decl a() + a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). a(). +@@ -189,11 +181,11 @@ TEST(AstUtils, RemoveEquivalentClauses) { + Program& program = tu->getProgram(); + EXPECT_EQ(1, program.getRelations().size()); + +- EXPECT_NE(program.getRelation(qn("a")), nullptr); +- EXPECT_EQ(15, program.getClauses(qn("a")).size()); ++ EXPECT_NE(program.getRelation("a"), nullptr); ++ EXPECT_EQ(15, program.getClauses("a").size()); + +- program.removeRelation(qn("a")); +- EXPECT_EQ(0, program.getClauses(qn("a")).size()); ++ program.removeRelation("a"); ++ EXPECT_EQ(0, program.getClauses("a").size()); + } + + } // namespace test +diff --git a/src/ast/tests/type_system_test.cpp b/src/ast/tests/type_system_test.cpp +index c3f49eb..cfeab10 100644 +--- a/src/ast/tests/type_system_test.cpp ++++ b/src/ast/tests/type_system_test.cpp +@@ -28,19 +28,15 @@ + namespace souffle::ast::test { + using namespace analysis; + +-QualifiedName qn(std::string_view s) { +- return QualifiedName::fromString(s); +-} +- + TEST(TypeSystem, Basic) { + TypeEnvironment env; + +- const analysis::Type& A = env.createType(qn("A"), env.getType(qn("number"))); +- const analysis::Type& B = env.createType(qn("B"), env.getType(qn("symbol"))); ++ const analysis::Type& A = env.createType("A", env.getType("number")); ++ const analysis::Type& B = env.createType("B", env.getType("symbol")); + +- auto& U = env.createType(qn("U"), toVector(&A, &B)); ++ auto& U = env.createType("U", toVector(&A, &B)); + +- auto& R = env.createType(qn("R")); ++ auto& R = env.createType("R"); + R.setFields(toVector(&A, &B)); + + EXPECT_EQ("A <: number", toString(A)); +@@ -53,12 +49,12 @@ TEST(TypeSystem, Basic) { + TEST(TypeSystem, isNumberType) { + TypeEnvironment env; + +- const analysis::Type& N = env.getType(qn("number")); ++ const analysis::Type& N = env.getType("number"); + +- const analysis::Type& A = env.createType(qn("A"), env.getType(qn("number"))); +- const analysis::Type& B = env.createType(qn("B"), env.getType(qn("number"))); ++ const analysis::Type& A = env.createType("A", env.getType("number")); ++ const analysis::Type& B = env.createType("B", env.getType("number")); + +- const analysis::Type& C = env.createType(qn("C"), env.getType(qn("symbol"))); ++ const analysis::Type& C = env.createType("C", env.getType("symbol")); + + EXPECT_TRUE(isOfKind(N, TypeAttribute::Signed)); + EXPECT_TRUE(isOfKind(A, TypeAttribute::Signed)); +@@ -72,15 +68,15 @@ TEST(TypeSystem, isNumberType) { + + // check the union type + { +- const analysis::Type& U = env.createType(qn("U"), toVector(&A, &B)); ++ const analysis::Type& U = env.createType("U", toVector(&A, &B)); + EXPECT_TRUE(isOfKind(U, TypeAttribute::Signed)); + EXPECT_FALSE(isOfKind(U, TypeAttribute::Symbol)); +- const analysis::Type& U2 = env.createType(qn("U2"), toVector(&A, &B, &C)); ++ const analysis::Type& U2 = env.createType("U2", toVector(&A, &B, &C)); + EXPECT_FALSE(isOfKind(U2, TypeAttribute::Signed)); + EXPECT_FALSE(isOfKind(U2, TypeAttribute::Symbol)); + } + { +- const analysis::Type& U3 = env.createType(qn("U3"), toVector(&A)); ++ const analysis::Type& U3 = env.createType("U3", toVector(&A)); + EXPECT_TRUE(isOfKind(U3, TypeAttribute::Signed)); + } + } +@@ -94,8 +90,8 @@ TEST(TypeSystem, isSubtypeOf_Basic) { + + // start with the two predefined types + +- const analysis::Type& N = env.getType(qn("number")); +- const analysis::Type& S = env.getType(qn("symbol")); ++ const analysis::Type& N = env.getType("number"); ++ const analysis::Type& S = env.getType("symbol"); + + EXPECT_PRED2(isSubtypeOf, N, N); + EXPECT_PRED2(isSubtypeOf, S, S); +@@ -105,8 +101,8 @@ TEST(TypeSystem, isSubtypeOf_Basic) { + + // check primitive type + +- const analysis::Type& A = env.createType(qn("A"), env.getType(qn("number"))); +- const analysis::Type& B = env.createType(qn("B"), env.getType(qn("number"))); ++ const analysis::Type& A = env.createType("A", env.getType("number")); ++ const analysis::Type& B = env.createType("B", env.getType("number")); + + EXPECT_PRED2(isSubtypeOf, A, A); + EXPECT_PRED2(isSubtypeOf, B, B); +@@ -122,7 +118,7 @@ TEST(TypeSystem, isSubtypeOf_Basic) { + + // check union types + +- const analysis::Type& U = env.createType(qn("U"), toVector(&A, &B)); ++ const analysis::Type& U = env.createType("U", toVector(&A, &B)); + + EXPECT_PRED2(isSubtypeOf, U, U); + EXPECT_PRED2(isSubtypeOf, A, U); +@@ -133,7 +129,7 @@ TEST(TypeSystem, isSubtypeOf_Basic) { + EXPECT_PRED2(isNotSubtypeOf, U, B); + EXPECT_PRED2(isNotSubtypeOf, N, U); + +- auto& V = env.createType(qn("V"), toVector(&A, &B, &U)); ++ auto& V = env.createType("V", toVector(&A, &B, &U)); + + EXPECT_PRED2(isSubtypeOf, V, U); + EXPECT_PRED2(isSubtypeOf, U, V); +@@ -142,11 +138,11 @@ TEST(TypeSystem, isSubtypeOf_Basic) { + TEST(TypeSystem, isSubtypeOf_Records) { + TypeEnvironment env; + +- const analysis::Type& A = env.createType(qn("A"), env.getType(qn("number"))); +- const analysis::Type& B = env.createType(qn("B"), env.getType(qn("number"))); ++ const analysis::Type& A = env.createType("A", env.getType("number")); ++ const analysis::Type& B = env.createType("B", env.getType("number")); + +- auto& R1 = env.createType(qn("R1")); +- auto& R2 = env.createType(qn("R2")); ++ auto& R1 = env.createType("R1"); ++ auto& R2 = env.createType("R2"); + + EXPECT_FALSE(isSubtypeOf(R1, R2)); + EXPECT_FALSE(isSubtypeOf(R2, R1)); +@@ -161,11 +157,11 @@ TEST(TypeSystem, isSubtypeOf_Records) { + TEST(TypeSystem, GreatestCommonSubtype) { + TypeEnvironment env; + +- const analysis::Type& N = env.getType(qn("number")); ++ const analysis::Type& N = env.getType("number"); + +- const analysis::Type& A = env.createType(qn("A"), env.getType(qn("number"))); +- const analysis::Type& B = env.createType(qn("B"), env.getType(qn("number"))); +- const analysis::Type& C = env.createType(qn("C"), env.getType(qn("symbol"))); ++ const analysis::Type& A = env.createType("A", env.getType("number")); ++ const analysis::Type& B = env.createType("B", env.getType("number")); ++ const analysis::Type& C = env.createType("C", env.getType("symbol")); + + EXPECT_EQ("{number}", toString(getGreatestCommonSubtypes(N, N))); + +@@ -190,8 +186,8 @@ TEST(TypeSystem, GreatestCommonSubtype) { + + // // bring in unions + +- auto& U = env.createType(qn("U")); +- auto& S = env.createType(qn("S")); ++ auto& U = env.createType("U"); ++ auto& S = env.createType("S"); + + U.setElements(toVector(&A)); + EXPECT_EQ("{S}", toString(getGreatestCommonSubtypes(U, S))); +@@ -208,7 +204,7 @@ TEST(TypeSystem, GreatestCommonSubtype) { + EXPECT_EQ("{U}", toString(getGreatestCommonSubtypes(U, S, N))); + + // bring in a union of unions +- auto& R = env.createType(qn("R")); ++ auto& R = env.createType("R"); + + EXPECT_EQ("{R}", toString(getGreatestCommonSubtypes(U, R))); + EXPECT_EQ("{R}", toString(getGreatestCommonSubtypes(S, R))); +@@ -236,18 +232,18 @@ TEST(TypeSystem, GreatestCommonSubtype) { + TEST(TypeSystem, complexSubsetTypes) { + TypeEnvironment env; + +- auto& A = env.createType(qn("A"), env.getType(qn("number"))); +- auto& BfromA = env.createType(qn("B"), A); +- auto& CfromA = env.createType(qn("C"), A); ++ auto& A = env.createType("A", env.getType("number")); ++ auto& BfromA = env.createType("B", A); ++ auto& CfromA = env.createType("C", A); + + EXPECT_EQ("{B}", toString(getGreatestCommonSubtypes(A, BfromA))); + EXPECT_EQ("{C}", toString(getGreatestCommonSubtypes(A, CfromA))); + EXPECT_EQ("{}", toString(getGreatestCommonSubtypes(A, BfromA, CfromA))); + EXPECT_EQ("{}", toString(getGreatestCommonSubtypes(BfromA, CfromA))); + +- auto* base = &env.createType(qn("B0"), BfromA); ++ auto* base = &env.createType("B0", BfromA); + for (std::size_t i = 1; i <= 10; ++i) { +- base = &env.createType(qn("B" + std::to_string(i)), *base); ++ base = &env.createType("B" + std::to_string(i), *base); + EXPECT_PRED2(isSubtypeOf, *base, A); + } + } +@@ -255,9 +251,9 @@ TEST(TypeSystem, complexSubsetTypes) { + TEST(TypeSystem, RecordSubsets) { + TypeEnvironment env; + +- auto& R = env.createType(qn("R")); ++ auto& R = env.createType("R"); + +- auto& A = env.createType(qn("A"), R); ++ auto& A = env.createType("A", R); + EXPECT_PRED2(isSubtypeOf, A, R); + + EXPECT_EQ("{A}", toString(getGreatestCommonSubtypes(A, R))); +@@ -266,8 +262,8 @@ TEST(TypeSystem, RecordSubsets) { + TEST(TypeSystem, EquivTypes) { + TypeEnvironment env; + +- auto& A = env.createType(qn("A"), env.getType(qn("number"))); +- auto& U = env.createType(qn("U"), toVector(dynamic_cast(&A))); ++ auto& A = env.createType("A", env.getType("number")); ++ auto& U = env.createType("U", toVector(dynamic_cast(&A))); + + EXPECT_TRUE(areEquivalentTypes(A, U)); + } +@@ -275,7 +271,7 @@ TEST(TypeSystem, EquivTypes) { + TEST(TypeSystem, AlgebraicDataType) { + TypeEnvironment env; + +- auto& A = env.createType(qn("A")); ++ auto& A = env.createType("A"); + + EXPECT_TRUE(isSubtypeOf(A, A)); + EXPECT_EQ("{A}", toString(getGreatestCommonSubtypes(A, A))); +diff --git a/src/ast/transform/AddNullariesToAtomlessAggregates.cpp b/src/ast/transform/AddNullariesToAtomlessAggregates.cpp +index 62914ca..932a43c 100644 +--- a/src/ast/transform/AddNullariesToAtomlessAggregates.cpp ++++ b/src/ast/transform/AddNullariesToAtomlessAggregates.cpp +@@ -32,9 +32,6 @@ + namespace souffle::ast::transform { + + bool AddNullariesToAtomlessAggregatesTransformer::transform(TranslationUnit& translationUnit) { +- // +Tautology() +- const QualifiedName relName = QualifiedName::fromString("+Tautology"); +- + bool changed{false}; + Program& program = translationUnit.getProgram(); + visit(program, [&](Aggregator& agg) { +@@ -49,6 +46,8 @@ bool AddNullariesToAtomlessAggregatesTransformer::transform(TranslationUnit& tra + } + // We will add in the Tautology atom to the body of this aggregate now + changed = true; ++ // +Tautology() ++ std::string const relName = "+Tautology"; + + if (program.getRelation(relName) == nullptr) { + // +Tautology(). +diff --git a/src/ast/transform/ComponentChecker.cpp b/src/ast/transform/ComponentChecker.cpp +index 9127b5c..9515c0b 100644 +--- a/src/ast/transform/ComponentChecker.cpp ++++ b/src/ast/transform/ComponentChecker.cpp +@@ -50,7 +50,7 @@ bool ComponentChecker::transform(TranslationUnit& translationUnit) { + const Component* ComponentChecker::checkComponentNameReference(ErrorReport& report, + const Component* enclosingComponent, const ComponentLookupAnalysis& componentLookup, + const std::string& name, const SrcLocation& loc, const TypeBinding& binding) { +- const QualifiedName& forwarded = binding.find(QualifiedName::fromString(name)); ++ const QualifiedName& forwarded = binding.find(name); + if (!forwarded.empty()) { + // for forwarded types we do not check anything, because we do not know + // what the actual type will be +@@ -111,8 +111,7 @@ void ComponentChecker::checkComponent(ErrorReport& report, const Component* encl + // Type parameter for us here is unknown type that will be bound at the template + // instantiation time. + auto parentTypeParameters = component.getComponentType()->getTypeParameters(); +- std::vector actualParams( +- parentTypeParameters.size(), QualifiedName::fromString("")); ++ std::vector actualParams(parentTypeParameters.size(), ""); + TypeBinding activeBinding = binding.extend(parentTypeParameters, actualParams); + + // check parents of component +@@ -192,7 +191,7 @@ void ComponentChecker::checkComponents( + checkComponent(report, nullptr, componentLookup, *cur, TypeBinding()); + } + +- for (ComponentInit* cur : program.getInstantiations()) { ++ for (ComponentInit* cur : program.getComponentInstantiations()) { + checkComponentInit(report, nullptr, componentLookup, *cur, TypeBinding()); + } + } +@@ -228,7 +227,7 @@ void ComponentChecker::checkComponentNamespaces(ErrorReport& report, const Progr + } + } + +- for (const auto& inst : program.getInstantiations()) { ++ for (const auto& inst : program.getComponentInstantiations()) { + const std::string name = toString(inst->getInstanceName()); + if (names.count(name) != 0u) { + report.addError("Name clash on instantiation " + name, inst->getSrcLoc()); +diff --git a/src/ast/transform/ComponentInstantiation.cpp b/src/ast/transform/ComponentInstantiation.cpp +index 39be1f8..a17f6ea 100644 +--- a/src/ast/transform/ComponentInstantiation.cpp ++++ b/src/ast/transform/ComponentInstantiation.cpp +@@ -23,7 +23,6 @@ + #include "ast/ComponentInit.h" + #include "ast/ComponentType.h" + #include "ast/Directive.h" +-#include "ast/Lattice.h" + #include "ast/Node.h" + #include "ast/Program.h" + #include "ast/QualifiedName.h" +@@ -59,7 +58,6 @@ static const unsigned int MAX_INSTANTIATION_DEPTH = 100; + */ + struct ComponentContent { + VecOwn types; +- VecOwn lattices; + VecOwn relations; + VecOwn directives; + VecOwn clauses; +@@ -79,22 +77,6 @@ struct ComponentContent { + types.push_back(std::move(type)); + } + +- void add(Own& lattice, ErrorReport& report) { +- // add to result content (check existence first) +- auto foundItem = +- std::find_if(lattices.begin(), lattices.end(), [&](const Own& element) { +- return (element->getQualifiedName() == lattice->getQualifiedName()); +- }); +- if (foundItem != lattices.end()) { +- Diagnostic err(Diagnostic::Type::ERROR, +- DiagnosticMessage("Redefinition of lattice " + toString(lattice->getQualifiedName()), +- lattice->getSrcLoc()), +- {DiagnosticMessage("Previous definition", (*foundItem)->getSrcLoc())}); +- report.addDiagnostic(err); +- } +- lattices.push_back(std::move(lattice)); +- } +- + void add(Own& rel, ErrorReport& report) { + // add to result content (check existence first) + auto foundItem = std::find_if(relations.begin(), relations.end(), [&](const Own& element) { +@@ -182,11 +164,6 @@ void collectContent(Program& program, const Component& component, const TypeBind + res.add(type, report); + } + +- // process lattices +- for (auto& lattice : content.lattices) { +- res.add(lattice, report); +- } +- + // process relations + for (auto& rel : content.relations) { + res.add(rel, report); +@@ -279,17 +256,6 @@ void collectContent(Program& program, const Component& component, const TypeBind + res.add(type, report); + } + +- for (const auto& cur : component.getLattices()) { +- // create a clone +- Own lattice(clone(cur)); +- +- auto&& newName = binding.find(lattice->getQualifiedName()); +- if (!newName.empty()) { +- lattice->setQualifiedName(newName); +- } +- res.add(lattice, report); +- } +- + // and the local relations + // (replacing formal parameters with actual parameters) + for (const auto& cur : component.getRelations()) { +@@ -317,7 +283,7 @@ void collectContent(Program& program, const Component& component, const TypeBind + } + + // index the available relations +- UnorderedQualifiedNameMap index; ++ std::map index; + for (const auto& cur : res.relations) { + index[cur->getQualifiedName()] = cur.get(); + } +@@ -370,8 +336,6 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c + + if (component == nullptr) { + // this component is not defined => will trigger a semantic error +- report.addError("Component " + componentInit.getComponentType()->getName() + " not found", +- componentInit.getComponentType()->getSrcLoc()); + return res; + } + +@@ -391,11 +355,6 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c + res.add(type, report); + } + +- // add types +- for (auto& lattice : nestedContent.lattices) { +- res.add(lattice, report); +- } +- + // add relations + for (auto& rel : nestedContent.relations) { + res.add(rel, report); +@@ -417,7 +376,7 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c + componentLookup, res, orphans, overridden, report, maxDepth); + + // update user-defined type names +- UnorderedQualifiedNameMap typeNameMapping; ++ std::map typeNameMapping; + for (const auto& cur : res.types) { + auto newName = componentInit.getInstanceName() + cur->getQualifiedName(); + typeNameMapping[cur->getQualifiedName()] = newName; +@@ -430,13 +389,8 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c + }); + } + +- for (const auto& cur : res.lattices) { +- auto newName = componentInit.getInstanceName() + cur->getQualifiedName(); +- cur->setQualifiedName(newName); +- } +- + // update relation names +- UnorderedQualifiedNameMap relationNameMapping; ++ std::map relationNameMapping; + for (const auto& cur : res.relations) { + auto newName = componentInit.getInstanceName() + cur->getQualifiedName(); + relationNameMapping[cur->getQualifiedName()] = newName; +@@ -461,15 +415,6 @@ ComponentContent getInstantiatedContent(Program& program, const ComponentInit& c + } + }); + +- visit(node, [&](Relation& rel) { +- if (rel.getIsDeltaDebug()) { +- auto pos = relationNameMapping.find(rel.getIsDeltaDebug().value()); +- if (pos != relationNameMapping.end()) { +- rel.setIsDeltaDebug(pos->second); +- } +- } +- }); +- + // rename directives + visit(node, [&](Directive& directive) { + auto pos = relationNameMapping.find(directive.getQualifiedName()); +@@ -587,7 +532,7 @@ bool ComponentInstantiationTransformer::transform(TranslationUnit& translationUn + + auto& componentLookup = translationUnit.getAnalysis(); + +- for (const auto* cur : program.getInstantiations()) { ++ for (const auto* cur : program.getComponentInstantiations()) { + VecOwn orphans; + const std::set overridden; + +@@ -598,9 +543,6 @@ bool ComponentInstantiationTransformer::transform(TranslationUnit& translationUn + for (auto& type : content.types) { + program.addType(std::move(type)); + } +- for (auto& lattice : content.lattices) { +- program.addLattice(std::move(lattice)); +- } + for (auto& rel : content.relations) { + program.addRelation(std::move(rel)); + } +diff --git a/src/ast/transform/DebugReporter.cpp b/src/ast/transform/DebugReporter.cpp +index 8153be1..97b1c1f 100644 +--- a/src/ast/transform/DebugReporter.cpp ++++ b/src/ast/transform/DebugReporter.cpp +@@ -19,27 +19,24 @@ + #include "ast/TranslationUnit.h" + #include "ast/utility/Utils.h" + #include "reports/DebugReport.h" +-#include "souffle/utility/MiscUtil.h" +- +-#include ++#include + + namespace souffle::ast::transform { + + bool DebugReporter::transform(TranslationUnit& translationUnit) { + translationUnit.getDebugReport().startSection(); + auto datalogSpecOriginal = pprint(translationUnit.getProgram()); +- auto start = now(); ++ auto start = std::chrono::high_resolution_clock::now(); + bool changed = applySubtransformer(translationUnit, wrappedTransformer.get()); +- auto end = now(); ++ auto end = std::chrono::high_resolution_clock::now(); + + if (changed) { + generateDebugReport(translationUnit, datalogSpecOriginal); + } + +- const auto elapsed = duration_in_us(start, end); ++ auto elapsed = std::to_string(std::chrono::duration(end - start).count()); + translationUnit.getDebugReport().endSection(wrappedTransformer->getName(), +- wrappedTransformer->getName() + " (" + std::to_string(elapsed / 1000000.0) + "s)" + +- (changed ? "" : " (unchanged)")); ++ wrappedTransformer->getName() + " (" + elapsed + "s)" + (changed ? "" : " (unchanged)")); + return changed; + } + +diff --git a/src/ast/transform/ExecutionPlanChecker.cpp b/src/ast/transform/ExecutionPlanChecker.cpp +index 255a6c2..b3254a6 100644 +--- a/src/ast/transform/ExecutionPlanChecker.cpp ++++ b/src/ast/transform/ExecutionPlanChecker.cpp +@@ -43,21 +43,18 @@ bool ExecutionPlanChecker::transform(TranslationUnit& translationUnit) { + + Program& program = translationUnit.getProgram(); + for (const analysis::RelationScheduleAnalysisStep& step : relationSchedule.schedule()) { +- const RelationSet& scc = step.computed(); ++ const std::set& scc = step.computed(); + for (const Relation* rel : scc) { + for (auto&& clause : program.getClauses(*rel)) { + if (!recursiveClauses.recursive(clause)) { +- if (clause->getExecutionPlan() != nullptr) { +- auto order = clause->getExecutionPlan()->getOrders().begin()->second; +- report.addError( +- "Ignored execution plan for non-recursive clause", order->getSrcLoc()); +- } + continue; + } + if (clause->getExecutionPlan() == nullptr) { + continue; + } +- ++ if (isA(clause)) { ++ continue; ++ } + std::size_t version = 0; + for (const auto* atom : getBodyLiterals(*clause)) { + if (scc.count(program.getRelation(*atom)) != 0u) { +@@ -77,14 +74,8 @@ bool ExecutionPlanChecker::transform(TranslationUnit& translationUnit) { + } + } + auto numAtoms = getBodyLiterals(*clause).size(); +- if (order.size() != numAtoms) { +- report.addError("Invalid execution order in plan (expected " + +- std::to_string(numAtoms) + " atoms, not " + +- std::to_string(order.size()) + ")", +- cur.second->getSrcLoc()); +- } else if (!isComplete) { +- report.addError( +- "Invalid execution order in plan (incomplete)", cur.second->getSrcLoc()); ++ if (order.size() != numAtoms || !isComplete) { ++ report.addError("Invalid execution order in plan", cur.second->getSrcLoc()); + } + } + +diff --git a/src/ast/transform/IOAttributes.h b/src/ast/transform/IOAttributes.h +index 79af600..ab3ccf0 100644 +--- a/src/ast/transform/IOAttributes.h ++++ b/src/ast/transform/IOAttributes.h +@@ -158,8 +158,8 @@ private: + return changed; + } + +- const std::string& getRelationName(const Directive* node) { +- return node->getQualifiedName().toString(); ++ std::string getRelationName(const Directive* node) { ++ return toString(join(node->getQualifiedName().getQualifiers(), ".")); + } + + /** +diff --git a/src/ast/transform/IODefaults.h b/src/ast/transform/IODefaults.h +index 592631e..5ce2563 100644 +--- a/src/ast/transform/IODefaults.h ++++ b/src/ast/transform/IODefaults.h +@@ -68,7 +68,6 @@ private: + bool setDefaults(TranslationUnit& translationUnit) { + bool changed = false; + Program& program = translationUnit.getProgram(); +- const Global& glb = translationUnit.global(); + for (Directive* io : program.getDirectives()) { + // Don't do anything for a directive which + // is not an I/O directive +@@ -92,19 +91,19 @@ private: + io->addParameter("operation", "input"); + changed = true; + // Configure input directory +- if (glb.config().has("fact-dir")) { +- io->addParameter("fact-dir", glb.config().get("fact-dir")); ++ if (Global::config().has("fact-dir")) { ++ io->addParameter("fact-dir", Global::config().get("fact-dir")); + } + } else if (io->getType() == ast::DirectiveType::output) { + io->addParameter("operation", "output"); + changed = true; + // Configure output directory +- if (glb.config().has("output-dir")) { +- if (glb.config().has("output-dir", "-")) { ++ if (Global::config().has("output-dir")) { ++ if (Global::config().has("output-dir", "-")) { + io->addParameter("IO", "stdout"); + io->addParameter("headers", "true"); + } else { +- io->addParameter("output-dir", glb.config().get("output-dir")); ++ io->addParameter("output-dir", Global::config().get("output-dir")); + } + } + } else if (io->getType() == ast::DirectiveType::printsize) { +@@ -123,8 +122,8 @@ private: + * + * @return Valid relation name from the concatenated qualified name. + */ +- const std::string& getRelationName(const Directive* node) { +- return node->getQualifiedName().toString(); ++ std::string getRelationName(const Directive* node) { ++ return toString(join(node->getQualifiedName().getQualifiers(), ".")); + } + }; + +diff --git a/src/ast/transform/InlineRelations.cpp b/src/ast/transform/InlineRelations.cpp +index 15401d7..12eb123 100644 +--- a/src/ast/transform/InlineRelations.cpp ++++ b/src/ast/transform/InlineRelations.cpp +@@ -27,7 +27,6 @@ + #include "ast/Constant.h" + #include "ast/Constraint.h" + #include "ast/Functor.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" + #include "ast/Literal.h" + #include "ast/Negation.h" +@@ -42,9 +41,7 @@ + #include "ast/UnnamedVariable.h" + #include "ast/UserDefinedFunctor.h" + #include "ast/Variable.h" +-#include "ast/analysis/Ground.h" + #include "ast/analysis/typesystem/PolymorphicObjects.h" +-#include "ast/analysis/typesystem/TypeEnvironment.h" + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" + #include "souffle/BinaryConstraintOps.h" +@@ -153,10 +150,16 @@ bool normaliseInlinedHeads(Program& program) { + bool nameInlinedUnderscores(Program& program) { + struct M : public NodeMapper { + mutable bool changed = false; +- const UnorderedQualifiedNameSet inlinedRelations; ++ const std::set inlinedRelations; + bool replaceUnderscores; + +- M(UnorderedQualifiedNameSet inlinedRelations, bool replaceUnderscores) ++ void clear() { ++ this->changed = false; ++ const_cast&>(this->inlinedRelations).clear(); ++ this->replaceUnderscores = false; ++ } ++ ++ M(std::set inlinedRelations, bool replaceUnderscores) + : inlinedRelations(std::move(inlinedRelations)), replaceUnderscores(replaceUnderscores) {} + + Own operator()(Own node) const override { +@@ -168,9 +171,10 @@ bool nameInlinedUnderscores(Program& program) { + if (inlinedRelations.find(atom->getQualifiedName()) != inlinedRelations.end()) { + // Atom associated with an inlined relation, so replace the underscores + // in all of its subnodes with named variables. +- M replace(inlinedRelations, true); ++ static M replace(inlinedRelations, true); // TODO: thread safe + node->apply(replace); + changed |= replace.changed; ++ replace.clear(); + return node; + } + } +@@ -178,7 +182,8 @@ bool nameInlinedUnderscores(Program& program) { + // Give a unique name to the underscored variable + // TODO (azreika): need a more consistent way of handling internally generated variables in + // general +- std::stringstream newVarName; ++ static std::stringstream newVarName; // TODO: thread safe ++ newVarName.clear(); + newVarName << ""; + changed = true; + return mk(newVarName.str()); +@@ -190,7 +195,7 @@ bool nameInlinedUnderscores(Program& program) { + }; + + // Store the names of all relations to be inlined +- UnorderedQualifiedNameSet inlinedRelations; ++ std::set inlinedRelations; + for (Relation* rel : program.getRelations()) { + if (rel->hasQualifier(RelationQualifier::INLINE)) { + inlinedRelations.insert(rel->getQualifiedName()); +@@ -565,15 +570,7 @@ NullableVector getInlinedArgument(Program& program, const Argument* a + + // Create a new aggregator per version of the target expression + for (Argument* newArg : argumentVersions.getVector()) { +- auto* newAggr = [&]() -> Aggregator* { +- if (const auto* aggr = as(arg)) { +- return new IntrinsicAggregator(aggr->getBaseOperator(), Own(newArg)); +- } else { +- // TODO +- assert(false && "TODO"); +- return nullptr; +- } +- }(); ++ auto* newAggr = new Aggregator(aggr->getBaseOperator(), Own(newArg)); + VecOwn newBody; + for (Literal* lit : aggr->getBodyLiterals()) { + newBody.push_back(clone(lit)); +@@ -597,6 +594,8 @@ NullableVector getInlinedArgument(Program& program, const Argument* a + // Literal can be inlined! + changed = true; + ++ AggregateOp op = aggr->getBaseOperator(); ++ + // Create an aggregator (with the same operation) for each possible body + std::vector aggrVersions; + for (std::vector inlineVersions : literalVersions.getVector()) { +@@ -604,15 +603,7 @@ NullableVector getInlinedArgument(Program& program, const Argument* a + if (aggr->getTargetExpression() != nullptr) { + target = clone(aggr->getTargetExpression()); + } +- auto* newAggr = [&]() -> Aggregator* { +- if (const auto* aggr = as(arg)) { +- return new IntrinsicAggregator(aggr->getBaseOperator(), std::move(target)); +- } else { +- // TODO +- assert(false && "TODO"); +- return nullptr; +- } +- }(); ++ auto* newAggr = new Aggregator(aggr->getBaseOperator(), std::move(target)); + + VecOwn newBody; + // Add in everything except the current literal being replaced +@@ -651,12 +642,8 @@ NullableVector getInlinedArgument(Program& program, const Argument* a + }; + // Create the actual overall aggregator that ties the replacement aggregators together. + // example: min x : { a(x) }. <=> min ( min x : { a1(x) }, min x : { a2(x) }, ... ) +- if (const auto* intrinsicAggr = as(aggr)) { +- // TODO not complete +- AggregateOp op = intrinsicAggr->getBaseOperator(); +- if (op != AggregateOp::MEAN) { +- versions.push_back(combineAggregators(aggrVersions, aggregateToFunctor(op))); +- } ++ if (op != AggregateOp::MEAN) { ++ versions.push_back(combineAggregators(aggrVersions, aggregateToFunctor(op))); + } + } + +@@ -1029,11 +1016,11 @@ std::vector getInlinedClause(Program& program, const Clause& clause) { + } + } + +-ExcludedRelations InlineRelationsTransformer::excluded(Global& glb) { ++ExcludedRelations InlineRelationsTransformer::excluded() { + ExcludedRelations xs; + auto addAll = [&](const std::string& name) { +- for (auto&& r : splitString(glb.config().get(name), ',')) +- xs.insert(QualifiedName::fromString(r)); ++ for (auto&& r : splitString(Global::config().get(name), ',')) ++ xs.insert(QualifiedName(r)); + }; + + addAll("inline-exclude"); +@@ -1094,7 +1081,7 @@ bool InlineUnmarkExcludedTransform::transform(TranslationUnit& translationUnit) + bool changed = false; + Program& program = translationUnit.getProgram(); + +- auto&& excluded = InlineRelationsTransformer::excluded(translationUnit.global()); ++ auto&& excluded = InlineRelationsTransformer::excluded(); + + for (Relation* rel : program.getRelations()) { + // no-magic implies no inlining +diff --git a/src/ast/transform/InlineRelations.h b/src/ast/transform/InlineRelations.h +index 77aba18..6c7466e 100644 +--- a/src/ast/transform/InlineRelations.h ++++ b/src/ast/transform/InlineRelations.h +@@ -29,8 +29,8 @@ namespace souffle::ast::transform { + */ + class InlineRelationsTransformer : public Transformer { + public: +- using ExcludedRelations = UnorderedQualifiedNameSet; +- static ExcludedRelations excluded(Global& glb); ++ using ExcludedRelations = std::set; ++ static ExcludedRelations excluded(); + + std::string getName() const override { + return "InlineRelationsTransformer"; +diff --git a/src/ast/transform/MagicSet.cpp b/src/ast/transform/MagicSet.cpp +index c5fa4c2..bad6ba5 100644 +--- a/src/ast/transform/MagicSet.cpp ++++ b/src/ast/transform/MagicSet.cpp +@@ -31,7 +31,6 @@ + #include "ast/StringConstant.h" + #include "ast/TranslationUnit.h" + #include "ast/UnnamedVariable.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/analysis/IOType.h" + #include "ast/analysis/PrecedenceGraph.h" + #include "ast/analysis/SCCGraph.h" +@@ -61,10 +60,10 @@ using NegativeLabellingTransformer = + using PositiveLabellingTransformer = + MagicSetTransformer::LabelDatabaseTransformer::PositiveLabellingTransformer; + +-UnorderedQualifiedNameSet MagicSetTransformer::getTriviallyIgnoredRelations(const TranslationUnit& tu) { ++std::set MagicSetTransformer::getTriviallyIgnoredRelations(const TranslationUnit& tu) { + const auto& program = tu.getProgram(); + const auto& ioTypes = tu.getAnalysis(); +- UnorderedQualifiedNameSet triviallyIgnoredRelations; ++ std::set triviallyIgnoredRelations; + + // - Any relations known in constant time (IDB relations) + for (auto* rel : program.getRelations()) { +@@ -87,21 +86,23 @@ UnorderedQualifiedNameSet MagicSetTransformer::getTriviallyIgnoredRelations(cons + return triviallyIgnoredRelations; + } + +-UnorderedQualifiedNameSet MagicSetTransformer::getWeaklyIgnoredRelations(const TranslationUnit& tu) { ++std::set MagicSetTransformer::getWeaklyIgnoredRelations(const TranslationUnit& tu) { + const auto& program = tu.getProgram(); + const auto& precedenceGraph = tu.getAnalysis().graph(); + const auto& polyAnalysis = tu.getAnalysis(); +- UnorderedQualifiedNameSet weaklyIgnoredRelations; ++ std::set weaklyIgnoredRelations; + + // Add magic-transform-exclude relations to the weakly ignored set +- for (const auto& relStr : splitString(tu.global().config().get("magic-transform-exclude"), ',')) { +- weaklyIgnoredRelations.insert(QualifiedName::fromString(relStr)); ++ for (const auto& relStr : splitString(Global::config().get("magic-transform-exclude"), ',')) { ++ std::vector qualifiers = splitString(relStr, '.'); ++ weaklyIgnoredRelations.insert(QualifiedName(qualifiers)); + } + + // Pick up specified relations from config +- UnorderedQualifiedNameSet specifiedRelations; +- for (const auto& relStr : splitString(tu.global().config().get("magic-transform"), ',')) { +- specifiedRelations.insert(QualifiedName::fromString(relStr)); ++ std::set specifiedRelations; ++ for (const auto& relStr : splitString(Global::config().get("magic-transform"), ',')) { ++ std::vector qualifiers = splitString(relStr, '.'); ++ specifiedRelations.insert(QualifiedName(qualifiers)); + } + + // Pick up specified relations and ignored relations from relation tags +@@ -114,11 +115,8 @@ UnorderedQualifiedNameSet MagicSetTransformer::getWeaklyIgnoredRelations(const T + } + + // Get the complement if not everything is magic'd +- UnorderedQualifiedNameSet includedRelations; +- std::copy_if(specifiedRelations.begin(), specifiedRelations.end(), +- std::inserter(includedRelations, includedRelations.end()), +- [&](const QualifiedName& qn) { return weaklyIgnoredRelations.count(qn) == 0; }); +- if (!contains(includedRelations, QualifiedName::fromString("*"))) { ++ std::set includedRelations = specifiedRelations - weaklyIgnoredRelations; ++ if (!contains(includedRelations, "*")) { + for (const Relation* rel : program.getRelations()) { + if (!contains(specifiedRelations, rel->getQualifiedName())) { + weaklyIgnoredRelations.insert(rel->getQualifiedName()); +@@ -216,10 +214,10 @@ UnorderedQualifiedNameSet MagicSetTransformer::getWeaklyIgnoredRelations(const T + return weaklyIgnoredRelations; + } + +-UnorderedQualifiedNameSet MagicSetTransformer::getStronglyIgnoredRelations(const TranslationUnit& tu) { ++std::set MagicSetTransformer::getStronglyIgnoredRelations(const TranslationUnit& tu) { + const auto& program = tu.getProgram(); + const auto& precedenceGraph = tu.getAnalysis().graph(); +- UnorderedQualifiedNameSet stronglyIgnoredRelations; ++ std::set stronglyIgnoredRelations; + + // - Any atom appearing at the head of a clause containing a counter + for (const auto* clause : program.getClauses()) { +@@ -232,7 +230,7 @@ UnorderedQualifiedNameSet MagicSetTransformer::getStronglyIgnoredRelations(const + while (!fixpointReached) { + fixpointReached = true; + // - To prevent poslabelling issues, all dependent strata should also be strongly ignored +- UnorderedQualifiedNameSet dependentRelations; ++ std::set dependentRelations; + for (const auto& relName : stronglyIgnoredRelations) { + precedenceGraph.visit(program.getRelation(relName), [&](const auto* dependentRel) { + dependentRelations.insert(dependentRel->getQualifiedName()); +@@ -246,7 +244,7 @@ UnorderedQualifiedNameSet MagicSetTransformer::getStronglyIgnoredRelations(const + } + + // - Since we can't duplicate the rules, nothing should be labelled in the bodies as well +- UnorderedQualifiedNameSet bodyRelations; ++ std::set bodyRelations; + for (const auto& relName : stronglyIgnoredRelations) { + visit(program.getClauses(relName), + [&](Atom& atom) { bodyRelations.insert(atom.getQualifiedName()); }); +@@ -262,8 +260,8 @@ UnorderedQualifiedNameSet MagicSetTransformer::getStronglyIgnoredRelations(const + return stronglyIgnoredRelations; + } + +-UnorderedQualifiedNameSet MagicSetTransformer::getRelationsToNotLabel(const TranslationUnit& tu) { +- UnorderedQualifiedNameSet result; ++std::set MagicSetTransformer::getRelationsToNotLabel(const TranslationUnit& tu) { ++ std::set result; + for (const auto& name : getTriviallyIgnoredRelations(tu)) { + result.insert(name); + } +@@ -275,7 +273,7 @@ UnorderedQualifiedNameSet MagicSetTransformer::getRelationsToNotLabel(const Tran + + bool MagicSetTransformer::shouldRun(const TranslationUnit& tu) { + const Program& program = tu.getProgram(); +- if (tu.global().config().has("magic-transform")) return true; ++ if (Global::config().has("magic-transform")) return true; + for (const auto* rel : program.getRelations()) { + if (rel->hasQualifier(RelationQualifier::MAGIC)) return true; + } +@@ -309,7 +307,7 @@ bool NormaliseDatabaseTransformer::partitionIO(TranslationUnit& translationUnit) + const auto& ioTypes = translationUnit.getAnalysis(); + + // Get all relations that are both input and output +- UnorderedQualifiedNameSet relationsToSplit; ++ std::set relationsToSplit; + for (auto* rel : program.getRelations()) { + if (ioTypes.isInput(rel) && (ioTypes.isOutput(rel) || ioTypes.isPrintSize(rel))) { + relationsToSplit.insert(rel->getQualifiedName()); +@@ -385,7 +383,7 @@ bool NormaliseDatabaseTransformer::extractIDB(TranslationUnit& translationUnit) + }; + + // Get all input relations that also have IDB rules attached +- UnorderedQualifiedNameSet inputRelationNames; ++ std::set inputRelationNames; + for (auto* rel : program.getRelations()) { + if (ioTypes.isInput(rel) && !isStrictlyEDB(rel)) { + assert(!ioTypes.isOutput(rel) && !ioTypes.isPrintSize(rel) && +@@ -396,7 +394,7 @@ bool NormaliseDatabaseTransformer::extractIDB(TranslationUnit& translationUnit) + + // Add a new intermediate non-input relation for each + // These will cover relation appearances in IDB rules +- UnorderedQualifiedNameMap inputToIntermediate; ++ std::map inputToIntermediate; + for (const auto& inputRelationName : inputRelationNames) { + // Give it a unique name + QualifiedName intermediateName(inputRelationName); +@@ -461,7 +459,7 @@ bool NormaliseDatabaseTransformer::querifyOutputRelations(TranslationUnit& trans + + // Get all output relations that need to be normalised + const auto& ioTypes = translationUnit.getAnalysis(); +- UnorderedQualifiedNameSet outputRelationNames; ++ std::set outputRelationNames; + for (auto* rel : program.getRelations()) { + if ((ioTypes.isOutput(rel) || ioTypes.isPrintSize(rel)) && !isStrictlyOutput(rel)) { + assert(!ioTypes.isInput(rel) && "output relations should not be input at this stage"); +@@ -471,7 +469,7 @@ bool NormaliseDatabaseTransformer::querifyOutputRelations(TranslationUnit& trans + + // Add a new intermediate non-output relation for each + // These will cover relation appearances in intermediate rules +- UnorderedQualifiedNameMap outputToIntermediate; ++ std::map outputToIntermediate; + for (const auto& outputRelationName : outputRelationNames) { + // Give it a unique name + QualifiedName intermediateName(outputRelationName); +@@ -536,20 +534,10 @@ bool NormaliseDatabaseTransformer::normaliseArguments(TranslationUnit& translati + append(newBodyLiterals, cloneRange(subConstraints)); + + // Update the node to reflect normalised aggregator +- node = [&]() -> Own { +- if (auto* intrinsicAggr = as(aggr)) { +- return mk(intrinsicAggr->getBaseOperator(), +- (aggr->getTargetExpression() != nullptr ? clone(aggr->getTargetExpression()) +- : nullptr), +- std::move(newBodyLiterals)); +- } else { +- auto* uda = as(aggr); +- return mk(uda->getBaseOperatorName(), clone(uda->getInit()), +- (aggr->getTargetExpression() != nullptr ? clone(aggr->getTargetExpression()) +- : nullptr), +- std::move(newBodyLiterals)); +- } +- }(); ++ node = aggr->getTargetExpression() != nullptr ++ ? mk(aggr->getBaseOperator(), clone(aggr->getTargetExpression()), ++ std::move(newBodyLiterals)) ++ : mk(aggr->getBaseOperator(), nullptr, std::move(newBodyLiterals)); + } else { + // Otherwise, just normalise children as usual. + node->apply(*this); +@@ -793,7 +781,7 @@ bool NegativeLabellingTransformer::transform(TranslationUnit& translationUnit) { + const auto& sccGraph = translationUnit.getAnalysis(); + Program& program = translationUnit.getProgram(); + +- UnorderedQualifiedNameSet relationsToLabel; ++ std::set relationsToLabel; + VecOwn clausesToAdd; + const auto& relationsToNotLabel = getRelationsToNotLabel(translationUnit); + +@@ -820,7 +808,7 @@ bool NegativeLabellingTransformer::transform(TranslationUnit& translationUnit) { + for (std::size_t stratum = 0; stratum < sccGraph.getNumberOfSCCs(); stratum++) { + // Check which relations to label in this stratum + const auto& stratumRels = sccGraph.getInternalRelations(stratum); +- UnorderedQualifiedNameMap newSccFriendNames; ++ std::map newSccFriendNames; + for (const auto* rel : stratumRels) { + auto relName = rel->getQualifiedName(); + if (contains(relationsToNotLabel, relName)) continue; +@@ -910,7 +898,7 @@ bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) { + for (const auto* rel : sccGraph.getInternalRelations(stratum)) { + assert(isNegativelyLabelled(rel->getQualifiedName()) && + "should only be looking at neglabelled strata"); +- UnorderedQualifiedNameSet relsToCopy; ++ std::set relsToCopy; + + // Get the unignored unlabelled relations appearing in the rules + for (auto&& clause : program.getClauses(*rel)) { +@@ -922,7 +910,7 @@ bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) { + }); + } + +- UnorderedQualifiedNameMap labelledNames; ++ std::map labelledNames; + for (const auto& relName : relsToCopy) { + std::size_t relStratum = sccGraph.getSCC(program.getRelation(relName)); + std::size_t copyCount = originalStrataCopyCount.at(relStratum) + 1; +@@ -944,7 +932,7 @@ bool PositiveLabellingTransformer::transform(TranslationUnit& translationUnit) { + + for (auto&& clause : program.getClauses(*rel)) { + // Grab the new names for all unignored unlabelled positive atoms +- UnorderedQualifiedNameMap labelledNames; ++ std::map labelledNames; + visit(*clause, [&](const Atom& atom) { + const auto& relName = atom.getQualifiedName(); + if (contains(relationsToNotLabel, relName) || isNegativelyLabelled(relName)) return; +diff --git a/src/ast/transform/MagicSet.h b/src/ast/transform/MagicSet.h +index 54a6442..babee2d 100644 +--- a/src/ast/transform/MagicSet.h ++++ b/src/ast/transform/MagicSet.h +@@ -30,7 +30,6 @@ + #include + #include + #include +-#include + #include + #include + +@@ -81,26 +80,26 @@ private: + * Gets the set of relations that are trivially computable, + * and so should not be magic-set. + */ +- static UnorderedQualifiedNameSet getTriviallyIgnoredRelations(const TranslationUnit& tu); ++ static std::set getTriviallyIgnoredRelations(const TranslationUnit& tu); + + /** + * Gets the set of relations to weakly ignore during the MST process. + * Weakly-ignored relations cannot be adorned/magic'd. + * Superset of strongly-ignored relations. + */ +- static UnorderedQualifiedNameSet getWeaklyIgnoredRelations(const TranslationUnit& tu); ++ static std::set getWeaklyIgnoredRelations(const TranslationUnit& tu); + + /** + * Gets the set of relations to strongly ignore during the MST process. + * Strongly-ignored relations cannot be safely duplicated without affecting semantics. + */ +- static UnorderedQualifiedNameSet getStronglyIgnoredRelations(const TranslationUnit& tu); ++ static std::set getStronglyIgnoredRelations(const TranslationUnit& tu); + + /** + * Gets the set of relations to not label. + * The union of strongly and trivially ignored. + */ +- static UnorderedQualifiedNameSet getRelationsToNotLabel(const TranslationUnit& tu); ++ static std::set getRelationsToNotLabel(const TranslationUnit& tu); + }; + + /** +@@ -241,19 +240,9 @@ private: + + using adorned_predicate = std::pair; + +- struct AdornedPredicateHash { +- std::size_t operator()(const adorned_predicate& pred) const { +- std::size_t seed = qnhasher(pred.first); +- seed ^= strhasher(pred.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +- return seed; +- } +- std::hash qnhasher; +- std::hash strhasher; +- }; +- +- std::unordered_set headAdornmentsToDo; +- UnorderedQualifiedNameSet headAdornmentsSeen; +- UnorderedQualifiedNameSet weaklyIgnoredRelations; ++ std::set headAdornmentsToDo; ++ std::set headAdornmentsSeen; ++ std::set weaklyIgnoredRelations; + + bool transform(TranslationUnit& translationUnit) override; + +diff --git a/src/ast/transform/MaterializeAggregationQueries.cpp b/src/ast/transform/MaterializeAggregationQueries.cpp +index 6dba5c5..3c5f7b2 100644 +--- a/src/ast/transform/MaterializeAggregationQueries.cpp ++++ b/src/ast/transform/MaterializeAggregationQueries.cpp +@@ -14,13 +14,11 @@ + + #include "ast/transform/MaterializeAggregationQueries.h" + #include "AggregateOp.h" +-#include "FunctorOps.h" + #include "ast/Aggregator.h" + #include "ast/Argument.h" + #include "ast/Atom.h" + #include "ast/Attribute.h" + #include "ast/Clause.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/Literal.h" + #include "ast/Node.h" + #include "ast/Program.h" +@@ -35,7 +33,6 @@ + #include "ast/analysis/typesystem/TypeSystem.h" + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" +-#include "ram/IntrinsicOperator.h" + #include "souffle/TypeAttribute.h" + #include "souffle/utility/MiscUtil.h" + #include "souffle/utility/NodeMapper.h" +@@ -139,7 +136,7 @@ void MaterializeAggregationQueriesTransformer::groundInjectedParameters( + }; + + auto aggClauseInnerAggregatesMasked = clone(aggClause); +- aggClauseInnerAggregatesMasked->setHead(mk(QualifiedName::fromString("*"))); ++ aggClauseInnerAggregatesMasked->setHead(mk("*")); + NegateAggregateAtoms update; + aggClauseInnerAggregatesMasked->apply(update); + +@@ -193,7 +190,7 @@ void MaterializeAggregationQueriesTransformer::groundInjectedParameters( + continue; + } + // 2. Variable must be grounded by this literal. +- auto singleLiteralClause = mk(QualifiedName::fromString("*")); ++ auto singleLiteralClause = mk("*"); + singleLiteralClause->addToBody(clone(lit)); + bool variableGroundedByLiteral = false; + for (const auto& argPair : analysis::getGroundedTerms(translationUnit, *singleLiteralClause)) { +@@ -273,15 +270,8 @@ bool MaterializeAggregationQueriesTransformer::materializeAggregationQueries( + return true; + }); + +- // keep literals alive while we use the type analysis, issue (#1896). +- VecOwn oldBodyLiterals; +- + for (auto&& cl : program.getClauses()) { + auto& clause = *cl; +- +- // the mapping from the Arguments in the original clause to their type(s) +- std::optional> clauseArgTypes; +- + visit(clause, [&](Aggregator& agg) { + if (!needsMaterializedRelation(agg)) { + return; +@@ -290,21 +280,13 @@ bool MaterializeAggregationQueriesTransformer::materializeAggregationQueries( + if (innerAggregates.find(&agg) != innerAggregates.end()) { + return; + } +- +- // compute types before the clause gets modified +- if (!clauseArgTypes) { +- clauseArgTypes = analysis::TypeAnalysis::analyseTypes(translationUnit, clause); +- } +- + // begin materialisation process + auto aggregateBodyRelationName = analysis::findUniqueRelationName(program, "__agg_subclause"); + // quickly copy in all the literals from the aggregate body +- auto aggClause = mk(QualifiedName::fromString(aggregateBodyRelationName)); ++ auto aggClause = mk(aggregateBodyRelationName); + aggClause->setBodyLiterals(clone(agg.getBodyLiterals())); +- if (const auto* intrinsicAgg = as(agg)) { +- if (intrinsicAgg->getBaseOperator() == AggregateOp::COUNT) { +- instantiateUnnamedVariables(*aggClause); +- } ++ if (agg.getBaseOperator() == AggregateOp::COUNT) { ++ instantiateUnnamedVariables(*aggClause); + } + // pull in any necessary grounding atoms + groundInjectedParameters(translationUnit, *aggClause, clause, agg); +@@ -317,28 +299,17 @@ bool MaterializeAggregationQueriesTransformer::materializeAggregationQueries( + for (const auto& variableName : headArguments) { + aggClauseHead->addArgument(mk(variableName)); + } +- +- // add them to the relation as well +- auto aggRel = mk(QualifiedName::fromString(aggregateBodyRelationName)); ++ // add them to the relation as well (need to do a bit of type analysis to make this work) ++ auto aggRel = mk(aggregateBodyRelationName); ++ std::map argTypes = ++ analysis::TypeAnalysis::analyseTypes(translationUnit, *aggClause); + + for (const auto& cur : aggClauseHead->getArguments()) { +- // Find type of argument variable in original clause +- auto it = std::find_if(clauseArgTypes->cbegin(), clauseArgTypes->cend(), +- [&](const std::pair& pair) -> bool { +- if (const Variable* var = as(pair.first)) { +- // use type from first variable matching the name +- return (var->getName() == toString(*cur)); +- } +- return false; +- }); +- assert(it != clauseArgTypes->cend() && "unexpected unknown argument"); +- +- if (it != clauseArgTypes->cend()) { +- auto const curArgType = it->second; +- assert(!curArgType.empty() && "unexpected empty typeset"); +- assert(curArgType.size() == 1 && "expected fully resolved type"); +- aggRel->addAttribute(mk(toString(*cur), curArgType.begin()->getName())); +- } ++ // cur will point us to a particular argument ++ // that is found in the aggClause ++ auto const curArgType = argTypes[cur]; ++ assert(!curArgType.empty() && "unexpected empty typeset"); ++ aggRel->addAttribute(mk(toString(*cur), curArgType.begin()->getName())); + } + + // Set up the aggregate body atom that will represent the materialised relation we just created +@@ -370,9 +341,7 @@ bool MaterializeAggregationQueriesTransformer::materializeAggregationQueries( + + VecOwn newBody; + newBody.push_back(std::move(aggAtom)); +- VecOwn oldBody = agg.setBodyLiterals(std::move(newBody)); +- oldBodyLiterals.insert(oldBodyLiterals.end(), std::make_move_iterator(oldBody.begin()), +- std::make_move_iterator(oldBody.end())); ++ agg.setBodyLiterals(std::move(newBody)); + // Now we can just add these new things (relation and its single clause) to the program + program.addClause(std::move(aggClause)); + program.addRelation(std::move(aggRel)); +@@ -410,19 +379,6 @@ bool MaterializeAggregationQueriesTransformer::needsMaterializedRelation(const A + return true; + } + +- // If we have a multi-result intrinsic functor within this aggregate => materialize +- bool seenMultiresultIntrinsicFunctor = false; +- visit(agg, [&](const IntrinsicFunctor& intFunc) { +- auto candidates = functorBuiltIn(intFunc.getBaseFunctionOp()); +- seenMultiresultIntrinsicFunctor |= std::any_of(candidates.cbegin(), candidates.cend(), +- [](const std::reference_wrapper& info) -> bool { +- return isFunctorMultiResult(info.get().op); +- }); +- }); +- if (seenMultiresultIntrinsicFunctor) { +- return true; +- } +- + // If the same variable occurs several times => materialize + bool duplicates = false; + std::set vars; +diff --git a/src/ast/transform/MaterializeSingletonAggregation.cpp b/src/ast/transform/MaterializeSingletonAggregation.cpp +index 0853f5a..28ca724 100644 +--- a/src/ast/transform/MaterializeSingletonAggregation.cpp ++++ b/src/ast/transform/MaterializeSingletonAggregation.cpp +@@ -83,8 +83,8 @@ bool MaterializeSingletonAggregationTransformer::transform(TranslationUnit& tran + // synthesise an aggregate relation + // __agg_rel_0() + std::string aggRelName = analysis::findUniqueRelationName(program, "__agg_single"); +- auto aggRel = mk(QualifiedName::fromString(aggRelName)); +- auto aggClause = mk(QualifiedName::fromString(aggRelName)); ++ auto aggRel = mk(aggRelName); ++ auto aggClause = mk(aggRelName); + auto* aggHead = aggClause->getHead(); + + // create a synthesised variable to replace the aggregate term! +diff --git a/src/ast/transform/Meta.cpp b/src/ast/transform/Meta.cpp +index 894e9da..065550a 100644 +--- a/src/ast/transform/Meta.cpp ++++ b/src/ast/transform/Meta.cpp +@@ -16,20 +16,20 @@ + + #include "ast/transform/Meta.h" + #include "souffle/utility/MiscUtil.h" ++#include + #include + + namespace souffle::ast::transform { + + bool MetaTransformer::applySubtransformer(TranslationUnit& translationUnit, Transformer* transformer) { +- auto start = now(); ++ auto start = std::chrono::high_resolution_clock::now(); + bool changed = transformer->apply(translationUnit); +- auto end = now(); ++ auto end = std::chrono::high_resolution_clock::now(); + + if (verbose && (!isA(transformer))) { + std::string changedString = changed ? "changed" : "unchanged"; +- const auto elapsed = duration_in_us(start, end); +- std::cout << transformer->getName() << " time: " << std::to_string(elapsed / 1000000.0) << "s [" +- << changedString << "]" << std::endl; ++ std::cout << transformer->getName() << " time: " << std::chrono::duration(end - start).count() ++ << "sec [" << changedString << "]" << std::endl; + } + + return changed; +diff --git a/src/ast/transform/MinimiseProgram.cpp b/src/ast/transform/MinimiseProgram.cpp +index 9e8f699..c56c88d 100644 +--- a/src/ast/transform/MinimiseProgram.cpp ++++ b/src/ast/transform/MinimiseProgram.cpp +@@ -314,7 +314,7 @@ bool MinimiseProgramTransformer::reduceSingletonRelations(TranslationUnit& trans + } + + // Keep track of canonical relation name for each redundant clause +- UnorderedQualifiedNameMap canonicalName; ++ std::map canonicalName; + + // Check pairwise equivalence of each singleton relation + for (std::size_t i = 0; i < singletonRelationClauses.size(); i++) { +diff --git a/src/ast/transform/PartitionBodyLiterals.cpp b/src/ast/transform/PartitionBodyLiterals.cpp +index 88ecb8e..9eb5411 100644 +--- a/src/ast/transform/PartitionBodyLiterals.cpp ++++ b/src/ast/transform/PartitionBodyLiterals.cpp +@@ -13,6 +13,7 @@ + ***********************************************************************/ + + #include "ast/transform/PartitionBodyLiterals.h" ++#include "GraphUtils.h" + #include "ast/Atom.h" + #include "ast/Clause.h" + #include "ast/Literal.h" +@@ -22,7 +23,6 @@ + #include "ast/TranslationUnit.h" + #include "ast/Variable.h" + #include "ast/utility/Visitor.h" +-#include "souffle/datastructure/Graph.h" + #include "souffle/utility/MiscUtil.h" + #include + #include +@@ -150,7 +150,7 @@ bool PartitionBodyLiteralsTransformer::transform(TranslationUnit& translationUni + static int disconnectedCount = 0; + std::stringstream nextName; + nextName << "+disconnected" << disconnectedCount; +- QualifiedName newRelationName = QualifiedName::fromString(nextName.str()); ++ QualifiedName newRelationName = nextName.str(); + disconnectedCount++; + + // Create the extracted relation and clause for the component +diff --git a/src/ast/transform/PragmaChecker.cpp b/src/ast/transform/PragmaChecker.cpp +index a4a1776..efd0610 100644 +--- a/src/ast/transform/PragmaChecker.cpp ++++ b/src/ast/transform/PragmaChecker.cpp +@@ -26,8 +26,8 @@ + + namespace souffle::ast::transform { + +-PragmaChecker::Merger::Merger(Global& g) : glb(g) { +- auto& config = glb.config(); ++PragmaChecker::Merger::Merger() { ++ auto& config = Global::config(); + + for (auto&& [k, v] : config.data()) { + if (config.state(k) == MainConfig::State::set) { +@@ -40,7 +40,7 @@ bool PragmaChecker::Merger::operator()(std::string_view k, std::string_view v) { + // Command line options take precedence, even if the param allows multiple + if (contains(locked_keys, k)) return false; + +- auto& config = glb.config(); ++ auto& config = Global::config(); + if (config.allowsMultiple(k)) + config.append(k, std::string(v)); + else +@@ -50,10 +50,9 @@ bool PragmaChecker::Merger::operator()(std::string_view k, std::string_view v) { + } + + bool PragmaChecker::transform(TranslationUnit& translationUnit) { +- Merger merger(translationUnit.global()); ++ Merger merger; + + auto& program = translationUnit.getProgram(); +- auto& glb = translationUnit.global(); + auto& error = translationUnit.getErrorReport(); + bool changed = false; + std::map previous_pragma; +@@ -63,7 +62,7 @@ bool PragmaChecker::transform(TranslationUnit& translationUnit) { + auto&& [k, v] = pragma->getkvp(); + + // warn if subsequent pragmas override one another +- if (!glb.config().allowsMultiple(k)) { ++ if (!Global::config().allowsMultiple(k)) { + auto it = previous_pragma.find(k); + if (it != previous_pragma.end()) { + error.addDiagnostic({Diagnostic::Type::WARNING, +diff --git a/src/ast/transform/PragmaChecker.h b/src/ast/transform/PragmaChecker.h +index d975da1..3f6b775 100644 +--- a/src/ast/transform/PragmaChecker.h ++++ b/src/ast/transform/PragmaChecker.h +@@ -32,13 +32,10 @@ public: + + // This helper is used to implement both `--pragma` cmd ln args and `.pragma` statements. + struct Merger { +- Merger(Global&); ++ Merger(); + bool operator()(std::string_view key, std::string_view value); + + std::set> locked_keys; +- +- private: +- Global& glb; + }; + + private: +diff --git a/src/ast/transform/ReduceExistentials.cpp b/src/ast/transform/ReduceExistentials.cpp +index 02daf99..fe79b1d 100644 +--- a/src/ast/transform/ReduceExistentials.cpp ++++ b/src/ast/transform/ReduceExistentials.cpp +@@ -13,6 +13,7 @@ + ***********************************************************************/ + + #include "ast/transform/ReduceExistentials.h" ++#include "GraphUtils.h" + #include "RelationTag.h" + #include "ast/Aggregator.h" + #include "ast/Argument.h" +@@ -29,7 +30,6 @@ + #include "ast/analysis/IOType.h" + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" +-#include "souffle/datastructure/Graph.h" + #include "souffle/utility/MiscUtil.h" + #include + #include +@@ -58,8 +58,7 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + // - An edge (a,b) exists iff a uses b "non-existentially" in one of its *recursive* clauses + // This way, a relation can be transformed into an existential form + // if and only if all its predecessors can also be transformed. +- using QNGraph = GraphLabeled; +- QNGraph relationGraph; ++ Graph relationGraph = Graph(); + + // Add in the nodes + for (Relation* relation : program.getRelations()) { +@@ -67,7 +66,7 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + } + + // Keep track of all relations that cannot be transformed +- UnorderedQualifiedNameSet minimalIrreducibleRelations; ++ std::set minimalIrreducibleRelations; + + auto& ioType = translationUnit.getAnalysis(); + +@@ -106,14 +105,14 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + // Run a DFS from each 'bad' source + // A node is reachable in a DFS from an irreducible node if and only if it is + // also an irreducible node +- UnorderedQualifiedNameSet irreducibleRelations; ++ std::set irreducibleRelations; + for (QualifiedName relationName : minimalIrreducibleRelations) { + relationGraph.visit( + relationName, [&](const QualifiedName& subRel) { irreducibleRelations.insert(subRel); }); + } + + // All other relations are necessarily existential +- UnorderedQualifiedNameSet existentialRelations; ++ std::set existentialRelations; + for (Relation* relation : program.getRelations()) { + if (!program.getClauses(*relation).empty() && relation->getArity() != 0 && + irreducibleRelations.find(relation->getQualifiedName()) == irreducibleRelations.end()) { +@@ -127,9 +126,8 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + + std::stringstream newRelationName; + newRelationName << "+?exists_" << relationName; +- const QualifiedName newRelationQName = QualifiedName::fromString(newRelationName.str()); + +- auto newRelation = mk(newRelationQName, originalRelation->getSrcLoc()); ++ auto newRelation = mk(newRelationName.str(), originalRelation->getSrcLoc()); + + // EqRel relations require two arguments, so remove it from the qualifier + if (newRelation->getRepresentation() == RelationRepresentation::EQREL) { +@@ -139,7 +137,7 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + // Keep all non-recursive clauses + for (auto&& clause : program.getClauses(*originalRelation)) { + if (!isRecursiveClause(*clause)) { +- auto newClause = mk(mk(newRelationQName), clone(clause->getBodyLiterals()), ++ auto newClause = mk(mk(newRelationName.str()), clone(clause->getBodyLiterals()), + // clone handles nullptr gracefully + clone(clause->getExecutionPlan()), clause->getSrcLoc()); + program.addClause(std::move(newClause)); +@@ -152,9 +150,9 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + // Mapper that renames the occurrences of marked relations with their existential + // counterparts + struct renameExistentials : public NodeMapper { +- const UnorderedQualifiedNameSet& relations; ++ const std::set& relations; + +- renameExistentials(UnorderedQualifiedNameSet& relations) : relations(relations) {} ++ renameExistentials(std::set& relations) : relations(relations) {} + + Own operator()(Own node) const override { + if (auto* clause = as(node)) { +@@ -167,7 +165,7 @@ bool ReduceExistentialsTransformer::transform(TranslationUnit& translationUnit) + // Relation is now existential, so rename it + std::stringstream newName; + newName << "+?exists_" << atom->getQualifiedName(); +- return mk(QualifiedName::fromString(newName.str())); ++ return mk(newName.str()); + } + } + node->apply(*this); +diff --git a/src/ast/transform/RemoveEmptyRelations.cpp b/src/ast/transform/RemoveEmptyRelations.cpp +index e231de5..6b6b985 100644 +--- a/src/ast/transform/RemoveEmptyRelations.cpp ++++ b/src/ast/transform/RemoveEmptyRelations.cpp +@@ -37,18 +37,17 @@ bool RemoveEmptyRelationsTransformer::removeEmptyRelations(TranslationUnit& tran + Program& program = translationUnit.getProgram(); + auto& ioTypes = translationUnit.getAnalysis(); + +- UnorderedQualifiedNameSet atoms_in_aggs; ++ std::set atoms_in_aggs; + visitFrontier(program, [&](Aggregator& agg) { + visit(agg, [&](Atom& atom) { atoms_in_aggs.insert(atom.getQualifiedName()); }); + return true; + }); + +- UnorderedQualifiedNameSet emptyRelations; ++ std::set emptyRelations; + bool changed = false; + for (auto rel : program.getRelations()) { + if (ioTypes.isInput(rel)) continue; + if (!program.getClauses(*rel).empty()) continue; +- if (rel->getIsDeltaDebug()) continue; + + emptyRelations.insert(rel->getQualifiedName()); + +diff --git a/src/ast/transform/RemoveRedundantSums.cpp b/src/ast/transform/RemoveRedundantSums.cpp +index 29cdecb..e6205dd 100644 +--- a/src/ast/transform/RemoveRedundantSums.cpp ++++ b/src/ast/transform/RemoveRedundantSums.cpp +@@ -14,8 +14,8 @@ + + #include "ast/transform/RemoveRedundantSums.h" + #include "AggregateOp.h" ++#include "ast/Aggregator.h" + #include "ast/Argument.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" + #include "ast/Literal.h" + #include "ast/Node.h" +@@ -37,12 +37,12 @@ bool RemoveRedundantSumsTransformer::transform(TranslationUnit& translationUnit) + Own operator()(Own node) const override { + // Apply to all aggregates of the form + // sum k : { .. } where k is a constant +- if (auto* agg = as(node)) { ++ if (auto* agg = as(node)) { + if (agg->getBaseOperator() == AggregateOp::SUM) { + if (const auto* constant = as(agg->getTargetExpression())) { + changed = true; + // Then construct the new thing to replace it with +- auto count = mk(AggregateOp::COUNT); ++ auto count = mk(AggregateOp::COUNT); + // Duplicate the body of the aggregate + VecOwn newBody; + for (const auto& lit : agg->getBodyLiterals()) { +diff --git a/src/ast/transform/RemoveRelationCopies.cpp b/src/ast/transform/RemoveRelationCopies.cpp +index cba325b..715a987 100644 +--- a/src/ast/transform/RemoveRelationCopies.cpp ++++ b/src/ast/transform/RemoveRelationCopies.cpp +@@ -36,7 +36,7 @@ + namespace souffle::ast::transform { + + bool RemoveRelationCopiesTransformer::removeRelationCopies(TranslationUnit& translationUnit) { +- using alias_map = UnorderedQualifiedNameMap; ++ using alias_map = std::map; + + // collect aliases + alias_map isDirectAliasOf; +@@ -107,12 +107,12 @@ bool RemoveRelationCopiesTransformer::removeRelationCopies(TranslationUnit& tran + alias_map isAliasOf; + + // track any copy cycles; cyclic rules are effectively empty +- UnorderedQualifiedNameSet cycle_reps; ++ std::set cycle_reps; + + for (std::pair cur : isDirectAliasOf) { + // compute replacement + +- UnorderedQualifiedNameSet visited; ++ std::set visited; + visited.insert(cur.first); + visited.insert(cur.second); + +diff --git a/src/ast/transform/ResolveAliases.cpp b/src/ast/transform/ResolveAliases.cpp +index c205070..427d21b 100644 +--- a/src/ast/transform/ResolveAliases.cpp ++++ b/src/ast/transform/ResolveAliases.cpp +@@ -34,7 +34,6 @@ + #include "souffle/BinaryConstraintOps.h" + #include "souffle/utility/FunctionalUtil.h" + #include "souffle/utility/MiscUtil.h" +-#include "souffle/utility/NodeMapper.h" + #include "souffle/utility/StreamUtil.h" + #include "souffle/utility/StringUtil.h" + #include +@@ -201,27 +200,6 @@ public: + } + }; + +-bool nameUnnamedInit(Clause& clause) { +- int varid = 0; +- bool changed = false; +- +- auto namer = nodeMapper([&](auto&& self, Own node) -> Own { +- if (const auto* unnamed = as(node)) { +- changed = true; +- varid += 1; +- return mk("_", unnamed->getSrcLoc()); +- } else { +- node->apply(self); +- return node; +- } +- }); +- +- visit(clause, [&](RecordInit& rec) { rec.apply(namer); }); +- visit(clause, [&](BranchInit& adt) { adt.apply(namer); }); +- +- return changed; +-} +- + } // namespace + + Own ResolveAliasesTransformer::resolveAliases(const Clause& clause) { +@@ -520,7 +498,7 @@ bool ResolveAliasesTransformer::transform(TranslationUnit& translationUnit) { + Program& program = translationUnit.getProgram(); + + // get all clauses +- std::vector clauses; ++ std::vector clauses; + visit(program, [&](const Relation& rel) { + const auto& qualifiers = rel.getQualifiers(); + // Don't resolve clauses of inlined relations +@@ -532,12 +510,7 @@ bool ResolveAliasesTransformer::transform(TranslationUnit& translationUnit) { + }); + + // clean all clauses +- for (Clause* clause : clauses) { +- // -- Step 0 -- +- // Name unnamed variables in record and branch inits (souffle-lang/souffle#2482) +- // This is fine as long as this transformer runs after the semantics checker +- changed |= nameUnnamedInit(*clause); +- ++ for (const Clause* clause : clauses) { + // -- Step 1 -- + // get rid of aliases + Own noAlias = resolveAliases(*clause); +diff --git a/src/ast/transform/SemanticChecker.cpp b/src/ast/transform/SemanticChecker.cpp +index f3f7540..91f7fa5 100644 +--- a/src/ast/transform/SemanticChecker.cpp ++++ b/src/ast/transform/SemanticChecker.cpp +@@ -35,10 +35,7 @@ + #include "ast/ExecutionOrder.h" + #include "ast/ExecutionPlan.h" + #include "ast/Functor.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" +-#include "ast/IterationCounter.h" +-#include "ast/Lattice.h" + #include "ast/Literal.h" + #include "ast/Negation.h" + #include "ast/NilConstant.h" +@@ -112,11 +109,11 @@ private: + const Program& program = tu.getProgram(); + ErrorReport& report = tu.getErrorReport(); + +- void checkAtom(const Clause& parent, const Atom& atom); +- void checkLiteral(const Clause& parent, const Literal& literal); +- void checkAggregator(const Clause& parent, const Aggregator& aggregator); ++ void checkAtom(const Atom& atom); ++ void checkLiteral(const Literal& literal); ++ void checkAggregator(const Aggregator& aggregator); + bool isDependent(const Clause& agg1, const Clause& agg2); +- void checkArgument(const Clause& parent, const Argument& arg); ++ void checkArgument(const Argument& arg); + void checkConstant(const Argument& argument); + void checkFact(const Clause& fact); + void checkClause(const Clause& clause); +@@ -125,7 +122,6 @@ private: + void checkRelationFunctionalDependencies(const Relation& relation); + void checkRelation(const Relation& relation); + void checkType(ast::Attribute const& attr, std::string const& name = {}); +- void checkLatticeDeclaration(const Lattice& lattice); + void checkFunctorDeclaration(const FunctorDeclaration& decl); + + void checkNamespaces(); +@@ -141,9 +137,9 @@ bool SemanticChecker::transform(TranslationUnit& translationUnit) { + + SemanticCheckerImpl::SemanticCheckerImpl(TranslationUnit& tu) : tu(tu) { + // suppress warnings for given relations +- if (tu.global().config().has("suppress-warnings")) { ++ if (Global::config().has("suppress-warnings")) { + std::vector suppressedRelations = +- splitString(tu.global().config().get("suppress-warnings"), ','); ++ splitString(Global::config().get("suppress-warnings"), ','); + + if (std::find(suppressedRelations.begin(), suppressedRelations.end(), "*") != + suppressedRelations.end()) { +@@ -154,9 +150,13 @@ SemanticCheckerImpl::SemanticCheckerImpl(TranslationUnit& tu) : tu(tu) { + } else { + // mute only the given relations (if they exist) + for (auto& relname : suppressedRelations) { +- if (!relname.empty()) { ++ const std::vector comps = splitString(relname, '.'); ++ if (!comps.empty()) { + // generate the relation identifier +- QualifiedName relid = QualifiedName::fromString(relname); ++ QualifiedName relid(comps[0]); ++ for (std::size_t i = 1; i < comps.size(); i++) { ++ relid.append(comps[i]); ++ } + + // update suppressed qualifier if the relation is found + if (Relation* rel = program.getRelation(relid)) { +@@ -171,9 +171,6 @@ SemanticCheckerImpl::SemanticCheckerImpl(TranslationUnit& tu) : tu(tu) { + for (auto* rel : program.getRelations()) { + checkRelation(*rel); + } +- for (auto* lattice : program.getLattices()) { +- checkLatticeDeclaration(*lattice); +- } + for (auto* clause : program.getClauses()) { + checkClause(*clause); + } +@@ -208,19 +205,19 @@ SemanticCheckerImpl::SemanticCheckerImpl(TranslationUnit& tu) : tu(tu) { + + // - stratification -- + // check for cyclic dependencies +- for (const Relation* cur : program.getRelations()) { ++ for (Relation* cur : program.getRelations()) { + std::size_t scc = sccGraph.getSCC(cur); + if (sccGraph.isRecursive(scc)) { +- const RelationSet& relSet = sccGraph.getInternalRelations(scc); +- for (const Relation* cyclicRelation : relSet) { ++ for (const Relation* cyclicRelation : sccGraph.getInternalRelations(scc)) { + // Negations and aggregations need to be stratified + const Literal* foundLiteral = nullptr; + bool hasNegation = hasClauseWithNegatedRelation(cyclicRelation, cur, &program, foundLiteral); +- bool hasAggregate = +- hasClauseWithAggregatedRelation(cyclicRelation, cur, &program, foundLiteral); +- if (hasNegation || hasAggregate) { ++ if (hasNegation || ++ hasClauseWithAggregatedRelation(cyclicRelation, cur, &program, foundLiteral)) { ++ auto const& relSet = sccGraph.getInternalRelations(scc); ++ std::set sortedRelSet(relSet.begin(), relSet.end()); + // Negations and aggregations need to be stratified +- std::string relationsListStr = toString(join(relSet, ",", ++ std::string relationsListStr = toString(join(sortedRelSet, ",", + [](std::ostream& out, const Relation* r) { out << r->getQualifiedName(); })); + std::vector messages; + messages.push_back(DiagnosticMessage( +@@ -238,22 +235,22 @@ SemanticCheckerImpl::SemanticCheckerImpl(TranslationUnit& tu) : tu(tu) { + } + } + +-void SemanticCheckerImpl::checkAtom(const Clause& parent, const Atom& atom) { ++void SemanticCheckerImpl::checkAtom(const Atom& atom) { + // check existence of relation + auto* r = program.getRelation(atom); + if (r == nullptr) { + report.addError("Undefined relation " + toString(atom.getQualifiedName()), atom.getSrcLoc()); + return; + } +- std::size_t arity = r->getArity(); +- if (arity != atom.getArity()) { ++ ++ if (r->getArity() != atom.getArity()) { + report.addError("Mismatching arity of relation " + toString(atom.getQualifiedName()) + " (expected " + +- toString(arity) + ", got " + toString(atom.getArity()) + ")", ++ toString(r->getArity()) + ", got " + toString(atom.getArity()) + ")", + atom.getSrcLoc()); + } + + for (const Argument* arg : atom.getArguments()) { +- checkArgument(parent, *arg); ++ checkArgument(*arg); + } + } + +@@ -280,19 +277,19 @@ std::set getUnnamedVariables(const Node& node) { + + } // namespace + +-void SemanticCheckerImpl::checkLiteral(const Clause& parent, const Literal& literal) { ++void SemanticCheckerImpl::checkLiteral(const Literal& literal) { + // check potential nested atom + if (const auto* atom = as(literal)) { +- checkAtom(parent, *atom); ++ checkAtom(*atom); + } + + if (const auto* neg = as(literal)) { +- checkAtom(parent, *neg->getAtom()); ++ checkAtom(*neg->getAtom()); + } + + if (const auto* constraint = as(literal)) { +- checkArgument(parent, *constraint->getLHS()); +- checkArgument(parent, *constraint->getRHS()); ++ checkArgument(*constraint->getLHS()); ++ checkArgument(*constraint->getRHS()); + + std::set unnamedInRecord; + visit(*constraint, [&](const RecordInit& record) { +@@ -302,13 +299,6 @@ void SemanticCheckerImpl::checkLiteral(const Clause& parent, const Literal& lite + } + } + }); +- visit(*constraint, [&](const BranchInit& init) { +- for (auto* arg : init.getArguments()) { +- if (auto* unnamed = as(arg)) { +- unnamedInRecord.insert(unnamed); +- } +- } +- }); + + // Don't worry about underscores if either side is an aggregate (because of witness exporting) + if (isA(*constraint->getLHS()) || isA(*constraint->getRHS())) { +@@ -349,12 +339,12 @@ bool SemanticCheckerImpl::isDependent(const Clause& agg1, const Clause& agg2) { + return dependent; + } + +-void SemanticCheckerImpl::checkAggregator(const Clause& parent, const Aggregator& aggregator) { ++void SemanticCheckerImpl::checkAggregator(const Aggregator& aggregator) { + auto& report = tu.getErrorReport(); +- const QualifiedName dummyQN = QualifiedName::fromString("dummy"); +- Clause dummyClauseAggregator(dummyQN); ++ const Program& program = tu.getProgram(); ++ Clause dummyClauseAggregator("dummy"); + +- visit(parent, [&](const Literal& parentLiteral) { ++ visit(program, [&](const Literal& parentLiteral) { + visit(parentLiteral, [&](const Aggregator& candidateAggregate) { + if (candidateAggregate != aggregator) { + return; +@@ -365,10 +355,10 @@ void SemanticCheckerImpl::checkAggregator(const Clause& parent, const Aggregator + }); + }); + +- visit(parent, [&](const Literal& parentLiteral) { ++ visit(program, [&](const Literal& parentLiteral) { + visit(parentLiteral, [&](const Aggregator& /* otherAggregate */) { + // Create the other aggregate's dummy clause +- Clause dummyClauseOther(dummyQN); ++ Clause dummyClauseOther("dummy"); + dummyClauseOther.addToBody(clone(parentLiteral)); + // Check dependency between the aggregator and this one + if (isDependent(dummyClauseAggregator, dummyClauseOther) && +@@ -379,16 +369,16 @@ void SemanticCheckerImpl::checkAggregator(const Clause& parent, const Aggregator + }); + + for (Literal* literal : aggregator.getBodyLiterals()) { +- checkLiteral(parent, *literal); ++ checkLiteral(*literal); + } + } + +-void SemanticCheckerImpl::checkArgument(const Clause& parent, const Argument& arg) { ++void SemanticCheckerImpl::checkArgument(const Argument& arg) { + if (const auto* agg = as(arg)) { +- checkAggregator(parent, *agg); ++ checkAggregator(*agg); + } else if (const auto* func = as(arg)) { + for (auto arg : func->getArguments()) { +- checkArgument(parent, *arg); ++ checkArgument(*arg); + } + + if (auto const* udFunc = as(func)) { +@@ -417,8 +407,6 @@ bool isConstantArgument(const Argument* arg) { + // if all argument of functor are constant, then + // assume functor returned value is constant. + return all_of(udf->getArguments(), isConstantArgument); +- } else if (isA(arg)) { +- return false; + } else if (isA(arg)) { + return false; + } else if (auto* typeCast = as(arg)) { +@@ -459,7 +447,7 @@ void SemanticCheckerImpl::checkFact(const Clause& fact) { + + void SemanticCheckerImpl::checkClause(const Clause& clause) { + // check head atom +- checkAtom(clause, *clause.getHead()); ++ checkAtom(*clause.getHead()); + + // Check for absence of underscores in head + for (auto* unnamed : getUnnamedVariables(*clause.getHead())) { +@@ -468,7 +456,7 @@ void SemanticCheckerImpl::checkClause(const Clause& clause) { + + // check body literals + for (Literal* lit : clause.getBodyLiterals()) { +- checkLiteral(clause, *lit); ++ checkLiteral(*lit); + } + + // check facts +@@ -503,8 +491,7 @@ void SemanticCheckerImpl::checkClause(const Clause& clause) { + if (varName[0] == '_') { + assert(varName.size() > 1 && "named variable should not be a single underscore"); + if (numAppearances > 1) { +- report.addWarning(WarnType::VarAppearsOnce, +- "Variable " + varName + " marked as singleton but occurs more than once", ++ report.addWarning("Variable " + varName + " marked as singleton but occurs more than once", + varLocation); + } + } +@@ -519,113 +506,43 @@ void SemanticCheckerImpl::checkClause(const Clause& clause) { + } + + void SemanticCheckerImpl::checkComplexRule(const std::set& multiRule) { +- // variable v occurs only once if: +- // - it occurs at most once in each clause body of the (possibly) multi-clause rule. +- // - and it never occurs in any clause head of the (possibly) multi-clause rule. +- // +- // note that ungrounded variables are already detected by another check so +- // we don't report them here (ungrounded variables warning, argument in +- // fact is not constant warning). +- +- /// variables that occurs in some clause head, these variables are not +- /// candidate to "occurs only once" warning. +- std::set varOccursInSomeHead; +- +- /// variables that occurs more than once in some clause body, these +- /// variables are not candidate to "occurs only once" warning. +- std::set varOccursMoreThanOnceInSomeBody; +- +- /// variables that never occur in clause head, and at most once in +- /// each clause body, these variables "occurs only once". +- std::set varOccursAtMostOnce; +- +- struct VarsCounter : public Visitor { +- // map from variable name to the occurrence count +- std::map occurrences; +- +- void visit_(type_identity, const ast::Variable& var) override { +- const auto& name = var.getName(); +- if (name[0] == '_') { +- // do not count variables starting with underscore +- return; +- } +- auto it = occurrences.find(name); +- if (it == occurrences.end()) { +- occurrences[name] = 1; +- } else { +- it->second += 1; ++ 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; + } + } + }; + +- { // find variables that occurs in some clause head +- VarsCounter vc; +- for (const Clause* cl : multiRule) { +- // count occurrences in clause head +- visit(cl->getHead(), vc); +- } +- for (const auto& entry : vc.occurrences) { +- varOccursInSomeHead.emplace(entry.first); +- } ++ // 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, count_var); + } + +- for (const Clause* cl : multiRule) { +- // TODO (b-scholz): for negation / disjunction this is not quite +- // right; we would need more semantic information here. +- VarsCounter vc; +- for (auto lit : cl->getBodyLiterals()) { +- visit(*lit, vc); +- } +- +- for (const auto& entry : vc.occurrences) { +- const std::string name = entry.first; +- if (varOccursInSomeHead.count(name) > 0) { +- // variable occurs in some head => not a candidate for +- // "variable occurs only once" +- continue; +- } +- +- const int occurences = entry.second; +- if (occurences == 1 && varOccursMoreThanOnceInSomeBody.count(name) == 0) { +- varOccursAtMostOnce.emplace(name); +- } else { +- // variable occurs more than once in some body => not a +- // candidate for "variable occurs only once" +- varOccursMoreThanOnceInSomeBody.emplace(name); +- varOccursAtMostOnce.erase(name); +- } +- } ++ // Count variable occurrence for each head separately ++ for (auto clause : multiRule) { ++ visit(*(clause->getHead()), count_var); + } + +- /// Find the least source location of a variable. +- struct VarFinder : public Visitor { +- const std::string& name; +- bool seen = false; +- SrcLocation leastLoc = {}; +- +- explicit VarFinder(const std::string& varName) : name(varName) {} +- +- void visit_(type_identity, const ast::Variable& var) override { +- if (var.getName() == name) { +- if (!seen) { +- leastLoc = var.getSrcLoc(); +- seen = true; +- } else if (var.getSrcLoc() < leastLoc) { +- leastLoc = var.getSrcLoc(); +- } +- } +- } +- }; +- +- for (const auto& name : varOccursAtMostOnce) { +- VarFinder vf(name); +- // find the least source location of the variable to give a warning location +- for (const Clause* cl : multiRule) { +- for (auto lit : cl->getBodyLiterals()) { +- visit(*lit, vf); +- } ++ // Check that a variables occurs more than once ++ for (const auto& cur : var_count) { ++ int numAppearances = cur.second; ++ const auto& varName = cur.first; ++ const auto& varLocation = var_pos[varName]->getSrcLoc(); ++ if (varName[0] != '_' && numAppearances == 1) { ++ report.addWarning("Variable " + varName + " only occurs once", varLocation); + } +- report.addWarning(WarnType::VarAppearsOnce, "Variable " + name + " only occurs once", vf.leastLoc); + } + } + +@@ -660,43 +577,11 @@ void SemanticCheckerImpl::checkFunctorDeclaration(const FunctorDeclaration& decl + } + } + +-void SemanticCheckerImpl::checkLatticeDeclaration(const Lattice& lattice) { +- const auto& name = lattice.getQualifiedName(); +- auto* existingType = getIf( +- program.getTypes(), [&](const ast::Type* type) { return type->getQualifiedName() == name; }); +- if (!existingType) { +- report.addError(tfm::format("Undefined type %s", name), lattice.getSrcLoc()); +- } +- if (lattice.hasLub()) { +- if (!isA(lattice.getLub())) { +- report.addError( +- tfm::format("Lattice operator Lub must be a user-defined functor"), lattice.getSrcLoc()); +- } +- } else { +- report.addError(tfm::format("Lattice %s<> does not define Lub", name), lattice.getSrcLoc()); +- } +- if (lattice.hasGlb()) { +- if (!isA(lattice.getGlb())) { +- report.addError( +- tfm::format("Lattice operator Glb must be a user-defined functor"), lattice.getSrcLoc()); +- } +- } else { +- report.addWarning(WarnType::LatticeMissingOperator, +- tfm::format("Lattice %s<> does not define Glb", name), lattice.getSrcLoc()); +- } +- if (!lattice.hasBottom()) { +- report.addWarning(WarnType::LatticeMissingOperator, +- tfm::format("Lattice %s<> does not define Bottom", name), lattice.getSrcLoc()); +- } +-} +- + void SemanticCheckerImpl::checkRelationDeclaration(const Relation& relation) { + const auto& attributes = relation.getAttributes(); +- const std::size_t arity = relation.getArity(); +- std::size_t firstAuxiliary = arity - relation.getAuxiliaryArity(); ++ assert(attributes.size() == relation.getArity() && "mismatching attribute size and arity"); + +- assert(attributes.size() == arity && "mismatching attribute size and arity"); +- for (std::size_t i = 0; i < arity; i++) { ++ for (std::size_t i = 0; i < relation.getArity(); i++) { + Attribute* attr = attributes[i]; + checkType(*attr); + +@@ -706,25 +591,6 @@ void SemanticCheckerImpl::checkRelationDeclaration(const Relation& relation) { + report.addError(tfm::format("Doubly defined attribute name %s", *attr), attr->getSrcLoc()); + } + } +- +- /* check that lattice elements are always the last */ +- if (i < firstAuxiliary && attr->getIsLattice()) { +- report.addError( +- tfm::format( +- "Lattice attribute %s should be placed after all non-lattice attributes", *attr), +- attr->getSrcLoc()); +- } +- +- /* check that lattice attributes have a correct lattice definition */ +- if (attr->getIsLattice()) { +- const auto& typeName = attr->getTypeName(); +- auto* existingType = getIf(program.getLattices(), +- [&](const ast::Lattice* lattice) { return lattice->getQualifiedName() == typeName; }); +- if (!existingType) { +- report.addError( +- tfm::format("Missing lattice definition for type %s", typeName), attr->getSrcLoc()); +- } +- } + } + } + +@@ -770,8 +636,7 @@ void SemanticCheckerImpl::checkRelation(const Relation& relation) { + return sClause.getHead()->getQualifiedName() == relation.getQualifiedName(); + }); + if (relation.getRepresentation() == RelationRepresentation::BTREE_DELETE && !hasSubsumptiveRule) { +- report.addWarning(WarnType::NoSubsumptiveRule, +- "No subsumptive rule for relation " + toString(relation.getQualifiedName()), ++ report.addWarning("No subsumptive rule for relation " + toString(relation.getQualifiedName()), + relation.getSrcLoc()); + } else if (relation.getRepresentation() != RelationRepresentation::BTREE_DELETE && hasSubsumptiveRule) { + report.addError("Relation \"" + toString(relation.getQualifiedName()) + +@@ -781,21 +646,7 @@ void SemanticCheckerImpl::checkRelation(const Relation& relation) { + } + if (relation.getRepresentation() == RelationRepresentation::BTREE_DELETE && relation.getArity() == 0) { + report.addError("Subsumptive relation \"" + toString(relation.getQualifiedName()) + +- "\" must not be a nullary relation", +- relation.getSrcLoc()); +- } +- +- if (hasSubsumptiveRule && relation.getAuxiliaryArity()) { +- report.addError("Relation \"" + toString(relation.getQualifiedName()) + +- "\" must not have both subsumptive rules and lattice arguments", +- relation.getSrcLoc()); +- } +- +- if (relation.getAuxiliaryArity() && +- (relation.getRepresentation() != RelationRepresentation::BTREE && +- relation.getRepresentation() != RelationRepresentation::DEFAULT)) { +- report.addError( +- "Relation \"" + toString(relation.getQualifiedName()) + "\" must have a btree representation", ++ "\" must not be a nullary relation", + relation.getSrcLoc()); + } + +@@ -807,28 +658,10 @@ void SemanticCheckerImpl::checkRelation(const Relation& relation) { + + // check whether this relation is empty + if (program.getClauses(relation).empty() && !ioTypes.isInput(&relation) && +- !relation.getIsDeltaDebug().has_value() && + !relation.hasQualifier(RelationQualifier::SUPPRESSED)) { +- report.addWarning(WarnType::NoRulesNorFacts, +- "No rules/facts defined for relation " + toString(relation.getQualifiedName()), ++ report.addWarning("No rules/facts defined for relation " + toString(relation.getQualifiedName()), + relation.getSrcLoc()); + } +- +- // if the relation is a delta_debug, make sure if has no clause +- if (relation.getIsDeltaDebug().has_value()) { +- if (!program.getClauses(relation).empty() || ioTypes.isInput(&relation)) { +- report.addError("Unexpected rules/facts for delta_debug relation " + +- toString(relation.getQualifiedName()), +- relation.getSrcLoc()); +- } +- const auto orig = relation.getIsDeltaDebug().value(); +- if (!program.getRelation(orig)) { +- report.addError("Could not find relation " + toString(orig) + +- " referred to by the delta_debug relation " + +- toString(relation.getQualifiedName()), +- relation.getSrcLoc()); +- } +- } + } + + void SemanticCheckerImpl::checkIO() { +@@ -855,14 +688,14 @@ void SemanticCheckerImpl::checkIO() { + * + **/ + static const std::vector usesInvalidWitness( +- TranslationUnit& tu, const Clause& clause, const IntrinsicAggregator& aggregate) { ++ TranslationUnit& tu, const Clause& clause, const Aggregator& aggregate) { + std::vector invalidWitnessLocations; + + if (aggregate.getBaseOperator() == AggregateOp::MIN || aggregate.getBaseOperator() == AggregateOp::MAX) { + return invalidWitnessLocations; // ie empty result + } + +- auto aggregateSubclause = mk(QualifiedName::fromString("*")); ++ auto aggregateSubclause = mk("*"); + aggregateSubclause->setBodyLiterals(clone(aggregate.getBodyLiterals())); + + struct InnerAggregateMasker : public NodeMapper { +@@ -902,7 +735,7 @@ void SemanticCheckerImpl::checkWitnessProblem() { + // an aggregate where it doesn't make sense to use it, i.e. + // count, sum, mean + visit(program, [&](const Clause& clause) { +- visit(clause, [&](const IntrinsicAggregator& agg) { ++ visit(clause, [&](const Aggregator& agg) { + for (auto&& invalidArgument : usesInvalidWitness(tu, clause, agg)) { + report.addError( + "Witness problem: argument grounded by an aggregator's inner scope is used " +@@ -953,7 +786,7 @@ std::vector findInlineCycle(const PrecedenceGraphAnalysis& preced + } + + // Check neighbours +- const RelationSet& successors = orderedRelationSet(precedenceGraph.graph().successors(current)); ++ const RelationSet& successors = precedenceGraph.graph().successors(current); + for (const Relation* successor : successors) { + // Only care about inlined neighbours in the graph + if (successor->hasQualifier(RelationQualifier::INLINE)) { +diff --git a/src/ast/transform/SimplifyAggregateTargetExpression.cpp b/src/ast/transform/SimplifyAggregateTargetExpression.cpp +index 10a6209..269a031 100644 +--- a/src/ast/transform/SimplifyAggregateTargetExpression.cpp ++++ b/src/ast/transform/SimplifyAggregateTargetExpression.cpp +@@ -81,13 +81,7 @@ Aggregator* SimplifyAggregateTargetExpressionTransformer::simplifyTargetExpressi + }); + + // Create the new simplified aggregator +- if (auto* intrinsicAgg = as(aggregator)) { +- return new IntrinsicAggregator( +- intrinsicAgg->getBaseOperator(), std::move(newTargetExpression), std::move(newBody)); +- } else { +- assert(false && "todo"); +- return nullptr; +- } ++ return new Aggregator(aggregator.getBaseOperator(), std::move(newTargetExpression), std::move(newBody)); + } + + bool SimplifyAggregateTargetExpressionTransformer::transform(TranslationUnit& translationUnit) { +diff --git a/src/ast/transform/TypeChecker.cpp b/src/ast/transform/TypeChecker.cpp +index a85ce33..30b3cbf 100644 +--- a/src/ast/transform/TypeChecker.cpp ++++ b/src/ast/transform/TypeChecker.cpp +@@ -34,7 +34,6 @@ + #include "ast/analysis/typesystem/PolymorphicObjects.h" + #include "ast/analysis/typesystem/SumTypeBranches.h" + #include "ast/analysis/typesystem/Type.h" +-#include "ast/analysis/typesystem/TypeConstraints.h" + #include "ast/analysis/typesystem/TypeEnvironment.h" + #include "ast/analysis/typesystem/TypeSystem.h" + #include "ast/utility/Utils.h" +@@ -125,7 +124,7 @@ private: + /* Type checks */ + /** Check if declared types of the relation match deduced types. */ + void visit_(type_identity, const Atom& atom) override; +- void visit_(type_identity, const souffle::ast::Variable& var) override; ++ void visit_(type_identity, const Variable& var) override; + void visit_(type_identity, const StringConstant& constant) override; + void visit_(type_identity, const NumericConstant& constant) override; + void visit_(type_identity, const NilConstant& constant) override; +@@ -135,8 +134,7 @@ private: + void visit_(type_identity, const IntrinsicFunctor& fun) override; + void visit_(type_identity, const UserDefinedFunctor& fun) override; + void visit_(type_identity, const BinaryConstraint& constraint) override; +- void visit_(type_identity, const IntrinsicAggregator& aggregator) override; +- void visit_(type_identity, const UserDefinedAggregator& aggregator) override; ++ void visit_(type_identity, const Aggregator& aggregator) override; + }; // namespace souffle::ast::transform + + void TypeChecker::verify(TranslationUnit& tu) { +@@ -153,7 +151,7 @@ void TypeChecker::verify(TranslationUnit& tu) { + + void TypeDeclarationChecker::checkUnionType(const ast::UnionType& type) { + // check presence of all the element types and that all element types are based off a primitive +- for (const QualifiedName& sub : orderedQualifiedNameSet(type.getTypes())) { ++ for (const QualifiedName& sub : type.getTypes()) { + if (typeEnv.isPrimitiveType(sub)) { + continue; + } +@@ -186,8 +184,7 @@ void TypeDeclarationChecker::checkUnionType(const ast::UnionType& type) { + + const auto& name = type->getQualifiedName(); + +- const auto predefinedTypesInUnion = +- orderedQualifiedNameSet(typeEnvAnalysis.getPrimitiveTypesInUnion(name)); ++ const auto& predefinedTypesInUnion = typeEnvAnalysis.getPrimitiveTypesInUnion(name); + + // Report error (if size == 0, then the union is cyclic) + if (predefinedTypesInUnion.size() > 1) { +@@ -305,7 +302,7 @@ void TypeDeclarationChecker::run() { + } + + // Check if all the branch names are unique in sum types. +- UnorderedQualifiedNameMap> branchToLocation; ++ std::map> branchToLocation; + visit(program.getTypes(), [&](const ast::AlgebraicDataType& type) { + for (auto* branch : type.getBranches()) { + branchToLocation[branch->getBranchName()].push_back(branch->getSrcLoc()); +@@ -369,7 +366,7 @@ void TypeCheckerImpl::visit_(type_identity, const Atom& atom) { + return isA(type) && !isA(type); + }); + +- if (!validAttribute && !tu.global().config().has("legacy")) { ++ if (!validAttribute && !Global::config().has("legacy")) { + auto primaryDiagnostic = + DiagnosticMessage("Atom's argument type is not a subtype of its declared type", + arguments[i]->getSrcLoc()); +@@ -385,12 +382,10 @@ void TypeCheckerImpl::visit_(type_identity, const Atom& atom) { + // Declared attribute and deduced type agree if: + // They are the same type, or + // They are derived from the same constant type. +- // They are equivalent types. + bool validAttribute = all_of(argTypes, [&](const analysis::Type& type) { +- return type == attributeType || areEquivalentTypes(type, attributeType) || +- any_of(typeEnv.getConstantTypes(), [&](auto& constantType) { +- return isSubtypeOf(attributeType, constantType) && isSubtypeOf(type, constantType); +- }); ++ return type == attributeType || any_of(typeEnv.getConstantTypes(), [&](auto& constantType) { ++ return isSubtypeOf(attributeType, constantType) && isSubtypeOf(type, constantType); ++ }); + }); + + if (!validAttribute) { +@@ -407,14 +402,9 @@ void TypeCheckerImpl::visit_(type_identity, const Atom& atom) { + } + } + +-void TypeCheckerImpl::visit_(type_identity, const ast::Variable& var) { ++void TypeCheckerImpl::visit_(type_identity, const ast::Variable& var) { + if (typeAnalysis.getTypes(&var).empty()) { +- if (typeAnalysis.errorAnalyzer) { +- typeAnalysis.errorAnalyzer->explain( +- report, &var, "Unable to deduce type for variable " + var.getName()); +- } else { +- report.addError("Unable to deduce type for variable " + var.getName(), var.getSrcLoc()); +- } ++ report.addError("Unable to deduce type for variable " + var.getName(), var.getSrcLoc()); + } + } + +@@ -675,7 +665,7 @@ void TypeCheckerImpl::visit_(type_identity, const BinaryConstr + } + } + +-void TypeCheckerImpl::visit_(type_identity, const IntrinsicAggregator& aggregator) { ++void TypeCheckerImpl::visit_(type_identity, const Aggregator& aggregator) { + auto op = polyAnalysis.getOverloadedOperator(aggregator); + + auto aggregatorType = typeAnalysis.getTypes(&aggregator); +@@ -688,11 +678,6 @@ void TypeCheckerImpl::visit_(type_identity, const Intrinsic + } + } + +-void TypeCheckerImpl::visit_(type_identity, const UserDefinedAggregator& aggregator) { +- // TODO +- /*const TypeSet& resultTypes =*/typeAnalysis.getTypes(&aggregator); +-} +- + void TypeCheckerImpl::visit_(type_identity, const Negation& neg) { + negatedAtoms.insert(neg.getAtom()); + } +diff --git a/src/ast/utility/BindingStore.cpp b/src/ast/utility/BindingStore.cpp +index 478a0d4..af49e0d 100644 +--- a/src/ast/utility/BindingStore.cpp ++++ b/src/ast/utility/BindingStore.cpp +@@ -165,4 +165,5 @@ std::size_t BindingStore::numBoundArguments(const Atom* atom) const { + } + return count; + } ++ + } // namespace souffle::ast +diff --git a/src/ast/utility/SipsMetric.cpp b/src/ast/utility/SipsMetric.cpp +new file mode 100644 +index 0000000..5f98a04 +--- /dev/null ++++ b/src/ast/utility/SipsMetric.cpp +@@ -0,0 +1,797 @@ ++/* ++ * Souffle - A Datalog Compiler ++ * Copyright (c) 2020, 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 SipsMetric.cpp ++ * ++ * Defines the SipsMetric class, which specifies cost functions for atom orderings in a clause. ++ * ++ ***********************************************************************/ ++ ++#include "ast/utility/SipsMetric.h" ++#include "Global.h" ++#include "ast/Clause.h" ++#include "ast/TranslationUnit.h" ++#include "ast/Variable.h" ++#include "ast/analysis/IOType.h" ++#include "ast/analysis/ProfileUse.h" ++#include "ast/analysis/SCCGraph.h" ++#include "ast/analysis/typesystem/PolymorphicObjects.h" ++#include "ast/utility/BindingStore.h" ++#include "ast/utility/Utils.h" ++#include "ast/utility/Visitor.h" ++#include "ast2ram/utility/Utils.h" ++#include "ram/Expression.h" ++#include "ram/FloatConstant.h" ++#include "ram/SignedConstant.h" ++#include "ram/StringConstant.h" ++#include "ram/UnsignedConstant.h" ++#include ++#include ++#include ++#include ++#include ++ ++namespace souffle::ast { ++ ++SipsMetric::SipsMetric(const TranslationUnit& tu) : program(tu.getProgram()) { ++ sccGraph = &tu.getAnalysis(); ++} ++ ++std::vector StaticSipsMetric::getReordering( ++ const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const { ++ std::size_t relStratum = sccGraph->getSCC(program.getRelation(*clause)); ++ auto sccRelations = sccGraph->getInternalRelations(relStratum); ++ ++ auto sccAtoms = filter(ast::getBodyLiterals(*clause), ++ [&](auto* atom) { return contains(sccRelations, program.getRelation(*atom)); }); ++ ++ BindingStore bindingStore(clause); ++ auto atoms = getBodyLiterals(*clause); ++ 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(clause, sccAtoms, atoms, bindingStore, version, mode); ++ assert(atoms.size() == costs.size() && "each atom should have exactly one cost"); ++ 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"); ++ ++ // set all arguments that are variables as bound ++ for (const auto* arg : nextAtom->getArguments()) { ++ if (const auto* var = as(arg)) { ++ bindingStore.bindVariableStrongly(var->getName()); ++ } ++ } ++ ++ newOrder[numAdded] = minIdx; // add to the ordering ++ atoms[minIdx] = nullptr; // mark as done ++ numAdded++; // move on ++ } ++ ++ return newOrder; ++} ++ ++SelingerProfileSipsMetric::SelingerProfileSipsMetric(const TranslationUnit& tu) : SipsMetric(tu) { ++ profileUseAnalysis = &tu.getAnalysis(); ++ polyAnalysis = &tu.getAnalysis(); ++} ++ ++std::vector SelingerProfileSipsMetric::getReordering( ++ const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const { ++ auto atoms = ast::getBodyLiterals(*clause); ++ ++ // remember to exit for single atom bodies ++ if (atoms.size() <= 1) { ++ std::vector res; ++ res.resize(atoms.size()); ++ std::iota(res.begin(), res.end(), 0); ++ return res; ++ } ++ ++ auto constraints = ast::getBodyLiterals(*clause); ++ std::size_t relStratum = sccGraph->getSCC(program.getRelation(*clause)); ++ auto sccRelations = sccGraph->getInternalRelations(relStratum); ++ auto sccAtoms = filter(ast::getBodyLiterals(*clause), ++ [&](auto* atom) { return contains(sccRelations, program.getRelation(*atom)); }); ++ ++ assert(profileUseAnalysis->hasAutoSchedulerStats() && "Must have stats in order to auto-schedule!"); ++ ++ auto* prof = profileUseAnalysis; ++ auto getRelationSize = [&prof](bool isRecursive, const ast::QualifiedName& rel, ++ const std::vector& joinColumns, ++ const std::map& constantsMap, ++ const std::string& iteration) { ++ std::set joinKeys(joinColumns.begin(), joinColumns.end()); ++ for (auto& [k, _] : constantsMap) { ++ joinKeys.insert(k); ++ } ++ ++ if (joinKeys.empty() && !isRecursive) { ++ return prof->getRelationSize(rel); ++ } ++ ++ std::stringstream ss; ++ ss << joinKeys; ++ std::string attributes = ss.str(); ++ attributes[0] = '['; ++ attributes[attributes.size() - 1] = ']'; ++ ++ std::stringstream cc; ++ cc << constantsMap; ++ std::string constants = cc.str(); ++ constants[0] = '['; ++ constants[constants.size() - 1] = ']'; ++ ++ if (isRecursive) { ++ return prof->getRecursiveUniqueKeys(rel.toString(), attributes, constants, iteration); ++ } ++ ++ return prof->getNonRecursiveUniqueKeys(rel.toString(), attributes, constants); ++ }; ++ ++ using AtomIdx = std::size_t; ++ using AtomSet = std::set; ++ ++ AtomSet recursiveInCurrentStratum; ++ ++ for (auto* a : sccAtoms) { ++ for (AtomIdx i = 0; i < atoms.size(); ++i) { ++ if (*atoms[i] == *a) { ++ recursiveInCurrentStratum.insert(i); ++ } ++ } ++ } ++ ++ using VarName = std::string; ++ using VarSet = std::set; ++ using ArgIdx = std::size_t; ++ ++ // map variable name to constants if possible ++ std::unordered_map varToConstant; ++ ++ // map variables to necessary variables on other side of the equality ++ // i.e. x = y + z we should map x -> { y, z } ++ std::unordered_map varToOtherVars; ++ ++ // map variable name to the lower and upper bounds of the inequality ++ // i.e. EA < Addr < EA + Size we should map Addr -> { { EA }, { EA, Size } } ++ std::unordered_map> ineqToUpperLower; ++ ++ for (auto* constraint : constraints) { ++ auto* lhs = constraint->getLHS(); ++ auto* rhs = constraint->getRHS(); ++ ++ if (isIneqConstraint(constraint->getBaseOperator())) { ++ if (auto* var = as(lhs)) { ++ VarSet otherVars; ++ visit(rhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ if (isLessThan(constraint->getBaseOperator()) || isLessEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].second = otherVars; ++ } ++ if (isGreaterThan(constraint->getBaseOperator()) || ++ isGreaterEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].first = otherVars; ++ } ++ } ++ ++ if (auto* var = as(rhs)) { ++ VarSet otherVars; ++ visit(lhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ if (isLessThan(constraint->getBaseOperator()) || isLessEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].first = otherVars; ++ } ++ if (isGreaterThan(constraint->getBaseOperator()) || ++ isGreaterEqual(constraint->getBaseOperator())) { ++ ineqToUpperLower[var->getName()].second = otherVars; ++ } ++ } ++ } ++ ++ // only consider = constraint ++ if (!isEqConstraint(constraint->getBaseOperator())) { ++ continue; ++ } ++ ++ if (isA(lhs) && isA(rhs)) { ++ varToConstant[as(lhs)->getName()] = as(rhs); ++ continue; ++ } ++ ++ if (isA(lhs) && isA(rhs)) { ++ varToConstant[as(rhs)->getName()] = as(lhs); ++ continue; ++ } ++ ++ if (auto* var = as(lhs)) { ++ VarSet otherVars; ++ visit(rhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ varToOtherVars[var->getName()] = otherVars; ++ continue; ++ } ++ ++ if (auto* var = as(rhs)) { ++ VarSet otherVars; ++ visit(lhs, [&](const ast::Variable& v) { otherVars.insert(v.getName()); }); ++ varToOtherVars[var->getName()] = otherVars; ++ continue; ++ } ++ } ++ ++ // check for bounded inequality i.e. EA < EA2 < EA + Size ++ for (auto& p : ineqToUpperLower) { ++ // consider this like an equality ++ auto& [lower, upper] = p.second; ++ if (!lower.empty() && !upper.empty() && ++ std::includes(upper.begin(), upper.end(), lower.begin(), lower.end())) { ++ varToOtherVars[p.first] = upper; ++ } ++ } ++ ++ std::unordered_map atomIdxToGroundedVars; ++ for (AtomIdx i = 0; i < atoms.size(); ++i) { ++ VarSet groundedVars; ++ visit(*atoms[i], [&](const ast::Variable& v) { groundedVars.insert(v.getName()); }); ++ atomIdxToGroundedVars[i] = groundedVars; ++ } ++ ++ // #atoms -> variables to join -> plan, cost ++ std::map> cache; ++ ++ std::unordered_map> atomToIdxConstants; ++ ++ std::size_t iterations = 1; ++ for (std::size_t i = 0; i < atoms.size(); ++i) { ++ auto* atom = atoms[i]; ++ std::string name = getClauseAtomName(*clause, atom, sccAtoms, version, mode); ++ bool isRecursive = recursiveInCurrentStratum.count(i) > 0; ++ if (isRecursive) { ++ iterations = prof->getIterations(name); ++ break; ++ } ++ } ++ ++ AtomIdx atomIdx = 0; ++ for (auto* atom : atoms) { ++ std::string name = getClauseAtomName(*clause, atom, sccAtoms, version, mode); ++ std::map idxConstant; ++ ++ ArgIdx varIdx = 0; ++ for (auto* argument : atom->getArguments()) { ++ // if we have a variable and a constraint of the form x = 2 then treat x as 2 ++ if (auto* var = as(argument)) { ++ if (varToConstant.count(var->getName())) { ++ argument = varToConstant[var->getName()]; ++ } ++ } ++ ++ if (auto* constant = as(argument)) { ++ std::stringstream ss; ++ ss << *translateConstant(*constant); ++ std::string constantValue = ss.str(); ++ idxConstant[varIdx] = constantValue; ++ } ++ ++varIdx; ++ } ++ ++ atomToIdxConstants[atomIdx] = idxConstant; ++ ++ // start by storing the access cost for each individual relation ++ std::vector empty; ++ bool isRecursive = recursiveInCurrentStratum.count(atomIdx) > 0; ++ AtomSet singleton = {atomIdx}; ++ std::vector plan = {atomIdx}; ++ PlanTuplesCost p; ++ p.plan = plan; ++ for (std::size_t iter = 0; iter < iterations; ++iter) { ++ std::size_t tuples = getRelationSize(isRecursive, name, empty, idxConstant, std::to_string(iter)); ++ double cost = static_cast(tuples * atom->getArity()); ++ p.tuplesPerIteration.push_back(tuples); ++ p.costsPerIteration.push_back(cost); ++ } ++ cache[1].insert(std::make_pair(singleton, p)); ++ ++atomIdx; ++ } ++ ++ // do selinger's algorithm ++ auto N = atoms.size(); ++ for (std::size_t K = 2; K <= N; ++K) { ++ // for each K sized subset ++ for (auto& subset : getSubsets(N, K)) { ++ // remove an entry from the subset ++ for (AtomIdx i = 0; i < subset.size(); ++i) { ++ // construct the set S \ S[i] ++ AtomSet smallerSubset; ++ for (AtomIdx j = 0; j < subset.size(); ++j) { ++ if (i == j) { ++ continue; ++ } ++ smallerSubset.insert(subset[j]); ++ } ++ ++ // compute the grounded variables from the subset ++ VarSet groundedVariablesFromSubset; ++ for (auto idx : smallerSubset) { ++ auto& varsGroundedByAtom = atomIdxToGroundedVars[idx]; ++ groundedVariablesFromSubset.insert(varsGroundedByAtom.begin(), varsGroundedByAtom.end()); ++ } ++ ++ // compute new cost ++ AtomIdx atomIdx = subset[i]; ++ auto* atom = atoms[atomIdx]; ++ std::vector joinColumns; ++ const auto& args = atom->getArguments(); ++ std::size_t numBound = 0; ++ for (ArgIdx argIdx = 0; argIdx < args.size(); ++argIdx) { ++ auto* arg = args[argIdx]; ++ // if we have a constant or var = constant then we ignore ++ if (atomToIdxConstants[atomIdx].count(argIdx) > 0) { ++ ++numBound; ++ continue; ++ } ++ ++ // unnamed variable i.e. _ ++ if (isA(arg)) { ++ ++numBound; ++ continue; ++ } ++ ++ if (auto* var = as(arg)) { ++ // free variable so we can't join on it ++ if (varToOtherVars.count(var->getName()) > 0) { ++ auto& dependentVars = varToOtherVars.at(var->getName()); ++ if (std::includes(groundedVariablesFromSubset.begin(), ++ groundedVariablesFromSubset.end(), dependentVars.begin(), ++ dependentVars.end())) { ++ joinColumns.push_back(argIdx); ++ ++numBound; ++ continue; ++ } ++ } ++ ++ // direct match on variable ++ if (groundedVariablesFromSubset.count(var->getName()) > 0) { ++ joinColumns.push_back(argIdx); ++ ++numBound; ++ continue; ++ } ++ } ++ } ++ ++ // lookup the cost in the cache ++ auto& planTuplesCost = cache[K - 1].at(smallerSubset); ++ auto& oldPlan = planTuplesCost.plan; ++ auto oldTuples = planTuplesCost.tuplesPerIteration; ++ auto oldCost = planTuplesCost.costsPerIteration; ++ ++ PlanTuplesCost p; ++ bool isRecursive = recursiveInCurrentStratum.count(atomIdx) > 0; ++ std::vector empty; ++ double expectedTuples = 0; ++ double newTotalCost = 0.0; ++ for (std::size_t iter = 0; iter < iterations; ++iter) { ++ if (numBound == atom->getArity()) { ++ expectedTuples = 1; ++ } else { ++ auto relSizeWithConstants = getRelationSize(isRecursive, ++ getClauseAtomName(*clause, atom, sccAtoms, version, mode), empty, ++ atomToIdxConstants[atomIdx], std::to_string(iter)); ++ ++ if (joinColumns.empty()) { ++ expectedTuples = static_cast(relSizeWithConstants); ++ } else { ++ auto uniqueKeys = getRelationSize(isRecursive, ++ getClauseAtomName(*clause, atom, sccAtoms, version, mode), joinColumns, ++ atomToIdxConstants[atomIdx], std::to_string(iter)); ++ ++ bool normalize = (uniqueKeys > 0); ++ expectedTuples = ++ static_cast(relSizeWithConstants) / (normalize ? uniqueKeys : 1); ++ } ++ } ++ ++ // calculate new number of tuples ++ std::size_t newTuples = static_cast(oldTuples[iter] * expectedTuples); ++ ++ // calculate new cost ++ double newCost = oldCost[iter] + newTuples * atom->getArity(); ++ ++ // add to vector of costs/tuples ++ p.tuplesPerIteration.push_back(newTuples); ++ p.costsPerIteration.push_back(newCost); ++ newTotalCost += newCost; ++ } ++ ++ // calculate new plan ++ std::vector newPlan(oldPlan.begin(), oldPlan.end()); ++ newPlan.push_back(atomIdx); ++ p.plan = newPlan; ++ ++ // if no plan then insert it ++ AtomSet currentSet(subset.begin(), subset.end()); ++ if (cache[K].count(currentSet) == 0) { ++ cache[K].insert(std::make_pair(currentSet, p)); ++ } else { ++ // if we have a lower cost ++ auto& costVector = cache[K].at(currentSet).costsPerIteration; ++ double oldTotalCost = std::accumulate(costVector.begin(), costVector.end(), 0.0); ++ if (oldTotalCost >= newTotalCost) { ++ cache[K].erase(currentSet); ++ cache[K].insert(std::make_pair(currentSet, p)); ++ } ++ } ++ } ++ } ++ } ++ ++ std::vector newOrder; ++ assert(cache[N].size() == 1); ++ auto& bestPlanTuplesCost = cache[N].begin()->second; ++ auto& bestPlan = bestPlanTuplesCost.plan; ++ for (AtomIdx elem : bestPlan) { ++ newOrder.push_back(elem); ++ } ++ ++ return newOrder; ++} ++ ++Own SelingerProfileSipsMetric::translateConstant(const ast::Constant& constant) const { ++ if (auto strConstant = as(constant)) { ++ return mk(strConstant->getConstant()); ++ } else if (isA(&constant)) { ++ return mk(0); ++ } else if (auto* numConstant = as(constant)) { ++ switch (polyAnalysis->getInferredType(*numConstant)) { ++ case ast::NumericConstant::Type::Int: ++ return mk(RamSignedFromString(numConstant->getConstant(), nullptr, 0)); ++ case ast::NumericConstant::Type::Uint: ++ return mk( ++ RamUnsignedFromString(numConstant->getConstant(), nullptr, 0)); ++ case ast::NumericConstant::Type::Float: ++ return mk(RamFloatFromString(numConstant->getConstant())); ++ } ++ } ++ fatal("unaccounted-for constant"); ++} ++ ++std::string SipsMetric::getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom, ++ const std::vector& sccAtoms, std::size_t version, ast2ram::TranslationMode mode) const { ++ using namespace souffle::ast2ram; ++ ++ bool isRecursive = !sccAtoms.empty(); ++ ++ if (isA(clause)) { ++ // find the dominated / dominating heads ++ const auto& body = clause.getBodyLiterals(); ++ auto dominatedHeadAtom = dynamic_cast(body[0]); ++ auto dominatingHeadAtom = dynamic_cast(body[1]); ++ ++ if (clause.getHead() == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getDeleteRelationName(atom->getQualifiedName()); ++ } ++ return getRejectRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatedHeadAtom == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatingHeadAtom == atom) { ++ switch (mode) { ++ case SubsumeRejectNewCurrent: ++ case SubsumeDeleteCurrentCurrent: return getConcreteRelationName(atom->getQualifiedName()); ++ case SubsumeDeleteCurrentDelta: return getDeltaRelationName(atom->getQualifiedName()); ++ default: return getNewRelationName(atom->getQualifiedName()); ++ } ++ } ++ ++ if (isRecursive) { ++ if (sccAtoms.at(version + 1) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ } ++ } ++ ++ if (!isRecursive) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ if (clause.getHead() == atom) { ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ if (sccAtoms.at(version) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ return getConcreteRelationName(atom->getQualifiedName()); ++} ++ ++const ast::PowerSet& SelingerProfileSipsMetric::getSubsets(std::size_t N, std::size_t K) const { ++ if (cache.count({N, K})) { ++ return cache.at({N, K}); ++ } ++ // this powerset represents all possible subsets of cardinality K of the set {1,...,N} ++ ast::PowerSet res; ++ ++ // generate the next permutation of the bitmask ++ std::vector cur; ++ cur.reserve(K); ++ ++ // use bitmask for subset generation ++ std::string bitmask(K, 1); // K leading 1's ++ bitmask.resize(N, 0); // N-K trailing 0's ++ ++ // generate the combination while there are combinations to go ++ do { ++ cur.clear(); ++ ++ // construct the subset using the set bits in the bitmask ++ for (std::size_t i = 0; i < N; ++i) // [0..N-1] integers ++ { ++ if (bitmask[i]) { ++ cur.push_back(i); ++ } ++ } ++ res.push_back(cur); ++ } while (std::prev_permutation(bitmask.begin(), bitmask.end())); ++ ++ cache[std::make_pair(N, K)] = res; ++ return cache.at({N, K}); ++} ++ ++/** Create a SIPS metric based on a given heuristic. */ ++std::unique_ptr SipsMetric::create(const std::string& heuristic, const TranslationUnit& tu) { ++ if (Global::config().has("auto-schedule")) { ++ return mk(tu); ++ } else if (heuristic == "strict") ++ return mk(tu); ++ else if (heuristic == "all-bound") ++ return mk(tu); ++ else if (heuristic == "naive") ++ return mk(tu); ++ else if (heuristic == "max-bound") ++ return mk(tu); ++ else if (heuristic == "delta-max-bound") ++ return mk(tu); ++ else if (heuristic == "max-ratio") ++ return mk(tu); ++ else if (heuristic == "least-free") ++ return mk(tu); ++ else if (heuristic == "least-free-vars") ++ return mk(tu); ++ else if (heuristic == "input") ++ return mk(tu); ++ ++ // default is all-bound ++ return create("all-bound", tu); ++} ++ ++std::vector StrictSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& /* bindingStore */, std::size_t /*version*/, ++ ast2ram::TranslationMode /*mode*/) const { ++ // Goal: Always choose the left-most atom ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ cost.push_back(atom == nullptr ? std::numeric_limits::max() : 0); ++ } ++ assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); ++ return cost; ++} ++ ++std::vector AllBoundSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: Prioritise atoms with all arguments bound ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ 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"); ++ return cost; ++} ++ ++std::vector NaiveSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: Prioritise (1) all bound, then (2) atoms with at least one bound argument, then (3) left-most ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ std::size_t arity = atom->getArity(); ++ std::size_t numBound = bindingStore.numBoundArguments(atom); ++ if (arity == numBound) { ++ cost.push_back(0); ++ } else if (numBound >= 1) { ++ cost.push_back(1); ++ } else { ++ cost.push_back(2); ++ } ++ } ++ assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); ++ return cost; ++} ++ ++std::vector MaxBoundSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: prioritise (1) all-bound, then (2) max number of bound vars, then (3) left-most ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ 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); ++ } else if (numBound == 0) { ++ // Always worse than any number of bound vars ++ cost.push_back(2); ++ } else { ++ // Between 0 and 1, decreasing with more num bound ++ cost.push_back(1.0 / numBound); ++ } ++ } ++ assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); ++ return cost; ++} ++ ++std::vector MaxRatioSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: prioritise max ratio of bound args ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ 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); ++ } else if (numBound == 0) { ++ // Always worse than anything else ++ cost.push_back(2); ++ } else { ++ // Between 0 and 1, decreasing as the ratio increases ++ cost.push_back(1.0 - numBound / arity); ++ } ++ } ++ assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); ++ return cost; ++} ++ ++std::vector LeastFreeSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: choose the atom with the least number of unbound arguments ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ cost.push_back((double)(atom->getArity() - bindingStore.numBoundArguments(atom))); ++ } ++ return cost; ++} ++ ++std::vector LeastFreeVarsSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: choose the atom with the least amount of unbound variables ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ // use a set to hold all free variables to avoid double-counting ++ std::set freeVars; ++ visit(*atom, [&](const Variable& var) { ++ if (bindingStore.isBound(var.getName())) { ++ freeVars.insert(var.getName()); ++ } ++ }); ++ cost.push_back((double)freeVars.size()); ++ } ++ return cost; ++} ++ ++InputSips::InputSips(const TranslationUnit& tu) ++ : StaticSipsMetric(tu), ioTypes(tu.getAnalysis()) {} ++ ++std::vector InputSips::evaluateCosts(const Clause* /*clause*/, ++ const std::vector& /*sccAtoms*/, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t /*version*/, ast2ram::TranslationMode /*mode*/) const { ++ // Goal: prioritise (1) all-bound, (2) input, then (3) rest ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ const auto& relName = atom->getQualifiedName(); ++ std::size_t arity = atom->getArity(); ++ std::size_t numBound = bindingStore.numBoundArguments(atom); ++ if (arity == numBound) { ++ // prioritise all-bound ++ cost.push_back(0); ++ } else if (ioTypes.isInput(program.getRelation(relName))) { ++ // then input ++ cost.push_back(1); ++ } else { ++ cost.push_back(2); ++ } ++ } ++ return cost; ++} ++ ++std::vector DeltaMaxBoundSips::evaluateCosts(const Clause* clause, ++ const std::vector& sccAtoms, const std::vector atoms, ++ const BindingStore& bindingStore, std::size_t version, ast2ram::TranslationMode mode) const { ++ auto isDeltaRelation = [&](const Atom* atom) { ++ std::string name = getClauseAtomName(*clause, atom, sccAtoms, version, mode); ++ return isPrefix("@delta_", name); ++ }; ++ ++ std::vector cost; ++ for (const auto* atom : atoms) { ++ if (atom == nullptr) { ++ cost.push_back(std::numeric_limits::max()); ++ continue; ++ } ++ ++ 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.0); ++ } else if (isDeltaRelation(atom)) { ++ // Better than any other atom that is not fully bounded ++ cost.push_back(1.0); ++ } else if (numBound == 0) { ++ // Always worse than any number of bound vars ++ cost.push_back(4.0); ++ } else { ++ // Between 2 and 3, decreasing with more num bound ++ cost.push_back(2.0 + (1.0 / (double)numBound)); ++ } ++ } ++ assert(atoms.size() == cost.size() && "each atom should have exactly one cost"); ++ return cost; ++} ++ ++} // namespace souffle::ast +diff --git a/src/ast/utility/SipsMetric.h b/src/ast/utility/SipsMetric.h +new file mode 100644 +index 0000000..c9946e6 +--- /dev/null ++++ b/src/ast/utility/SipsMetric.h +@@ -0,0 +1,221 @@ ++/* ++ * Souffle - A Datalog Compiler ++ * Copyright (c) 2020, 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 SipsMetric.h ++ * ++ * Defines the SipsMetric class, which specifies cost functions for atom orderings in a clause. ++ * ++ ***********************************************************************/ ++ ++#pragma once ++ ++#include "ast2ram/ClauseTranslator.h" ++#include "souffle/utility/Types.h" ++#include ++#include ++#include ++#include ++ ++namespace souffle::ram { ++class Expression; ++} // namespace souffle::ram ++ ++namespace souffle::ast::analysis { ++class IOTypeAnalysis; ++class ProfileUseAnalysis; ++class PolymorphicObjectsAnalysis; ++class SCCGraphAnalysis; ++} // namespace souffle::ast::analysis ++namespace souffle::ast { ++ ++class Atom; ++class BindingStore; ++class Clause; ++class Constant; ++class Program; ++class TranslationUnit; ++ ++using PowerSet = std::vector>; ++ ++/** ++ * Class for SIPS cost-metric functions ++ * Each subclass represents a different heuristic used for evaluating ++ * the cost of choosing an atom next in the schedule. ++ */ ++class SipsMetric { ++public: ++ SipsMetric(const TranslationUnit& tu); ++ ++ virtual ~SipsMetric() = default; ++ ++ /** ++ * Determines the new ordering of a clause after the SIPS is applied. ++ * @param clause clause to reorder ++ * @return the vector of new positions; v[i] = j iff atom j moves to pos i ++ */ ++ virtual std::vector getReordering( ++ const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const = 0; ++ ++ /** Create a SIPS metric based on a given heuristic. */ ++ static std::unique_ptr create(const std::string& heuristic, const TranslationUnit& tu); ++ ++ std::string getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom, ++ const std::vector& sccAtoms, std::size_t version, ++ ast2ram::TranslationMode mode) const; ++ ++protected: ++ const ast::Program& program; ++ const ast::analysis::SCCGraphAnalysis* sccGraph = nullptr; ++}; ++ ++class SelingerProfileSipsMetric : public SipsMetric { ++public: ++ SelingerProfileSipsMetric(const TranslationUnit& tu); ++ std::vector getReordering( ++ const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const override; ++ ++private: ++ /* helper struct for Selinger */ ++ struct PlanTuplesCost { ++ std::vector plan; ++ std::vector tuplesPerIteration; ++ std::vector costsPerIteration; ++ }; ++ ++ const PowerSet& getSubsets(std::size_t N, std::size_t K) const; ++ ++ Own translateConstant(const ast::Constant& constant) const; ++ ++ const ast::analysis::PolymorphicObjectsAnalysis* polyAnalysis = nullptr; ++ const ast::analysis::ProfileUseAnalysis* profileUseAnalysis = nullptr; ++ mutable std::map, PowerSet> cache; ++}; ++ ++class StaticSipsMetric : public SipsMetric { ++public: ++ StaticSipsMetric(const TranslationUnit& tu) : SipsMetric(tu) {} ++ ++ std::vector getReordering( ++ const Clause* clause, std::size_t version, ast2ram::TranslationMode mode) const override; ++ ++protected: ++ /** ++ * Evaluates the cost of choosing each atom next in the current schedule ++ * @param atoms atoms to choose from; may be nullptr ++ * @param bindingStore the variables already bound to a value ++ */ ++ virtual std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const = 0; ++}; ++ ++/** Goal: Always choose the left-most atom */ ++class StrictSips : public StaticSipsMetric { ++public: ++ StrictSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: Prioritise atoms with all arguments bound */ ++class AllBoundSips : public StaticSipsMetric { ++public: ++ AllBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: Prioritise (1) all bound, then (2) atoms with at least one bound argument, then (3) left-most */ ++class NaiveSips : public StaticSipsMetric { ++public: ++ NaiveSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: prioritise (1) all-bound, then (2) max number of bound vars, then (3) left-most */ ++class MaxBoundSips : public StaticSipsMetric { ++public: ++ MaxBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: prioritise (1) delta, (2) all-bound, then (3) max number of bound vars, then (4) left-most */ ++class DeltaMaxBoundSips : public StaticSipsMetric { ++public: ++ DeltaMaxBoundSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: prioritise max ratio of bound args */ ++class MaxRatioSips : public StaticSipsMetric { ++public: ++ MaxRatioSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: choose the atom with the least number of unbound arguments */ ++class LeastFreeSips : public StaticSipsMetric { ++public: ++ LeastFreeSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: choose the atom with the least amount of unbound variables */ ++class LeastFreeVarsSips : public StaticSipsMetric { ++public: ++ LeastFreeVarsSips(const TranslationUnit& tu) : StaticSipsMetric(tu) {} ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++}; ++ ++/** Goal: prioritise (1) all-bound, then (2) input, and then (3) left-most */ ++class InputSips : public StaticSipsMetric { ++public: ++ InputSips(const TranslationUnit& tu); ++ ++protected: ++ std::vector evaluateCosts(const Clause* clause, const std::vector& sccAtoms, ++ const std::vector atoms, const BindingStore& bindingStore, std::size_t version, ++ ast2ram::TranslationMode mode) const override; ++ ++private: ++ const analysis::IOTypeAnalysis& ioTypes; ++}; ++ ++} // namespace souffle::ast +diff --git a/src/ast/utility/Utils.cpp b/src/ast/utility/Utils.cpp +index 68a709f..e173ff8 100644 +--- a/src/ast/utility/Utils.cpp ++++ b/src/ast/utility/Utils.cpp +@@ -210,7 +210,7 @@ void negateConstraintInPlace(Constraint& constraint) { + } + } + +-bool renameAtoms(Own& clause, const UnorderedQualifiedNameMap& oldToNew) { ++bool renameAtoms(Own& clause, const std::map& oldToNew) { + bool changed = false; + visit(clause, [&](Atom& atom) { + auto it = oldToNew.find(atom.getQualifiedName()); +@@ -222,7 +222,7 @@ bool renameAtoms(Own& clause, const UnorderedQualifiedNameMap& oldToNew) { ++bool renameAtoms(Program& program, const std::map& oldToNew) { + bool changed = false; + + // Can't rename clause head atoms while the clause is attached w/o invalidating by-name lookup tables. +@@ -253,7 +253,7 @@ bool renameAtoms(Program& program, const UnorderedQualifiedNameMap& oldToNew) { ++ const std::map& oldToNew) { + auto info = program.getRelationInfo(relation); + if (!info) return false; + +diff --git a/src/ast/utility/Utils.h b/src/ast/utility/Utils.h +index c7c4cdf..29b7b69 100644 +--- a/src/ast/utility/Utils.h ++++ b/src/ast/utility/Utils.h +@@ -174,7 +174,7 @@ IntrinsicFunctors validOverloads(const analysis::TypeAnalysis&, const IntrinsicF + * @param oldToNew map from old atom names to new atom names + * @return true if the node was changed + */ +-bool renameAtoms(Own& clause, const UnorderedQualifiedNameMap& oldToNew); ++bool renameAtoms(Own& clause, const std::map& oldToNew); + + /** + * Rename all atoms in the program to a given name, including clause heads. +@@ -182,7 +182,7 @@ bool renameAtoms(Own& clause, const UnorderedQualifiedNameMap& oldToNew); ++bool renameAtoms(Program& program, const std::map& oldToNew); + + /** + * Rename all atoms that appear in a given relation's clauses (including the clause's head). +@@ -192,6 +192,6 @@ bool renameAtoms(Program& program, const UnorderedQualifiedNameMap& oldToNew); ++ const std::map& oldToNew); + + } // namespace souffle::ast +diff --git a/src/ast/utility/Visitor.h b/src/ast/utility/Visitor.h +index beb0864..ebdc85f 100644 +--- a/src/ast/utility/Visitor.h ++++ b/src/ast/utility/Visitor.h +@@ -36,10 +36,7 @@ + #include "ast/FunctionalConstraint.h" + #include "ast/Functor.h" + #include "ast/FunctorDeclaration.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" +-#include "ast/IterationCounter.h" +-#include "ast/Lattice.h" + #include "ast/Literal.h" + #include "ast/Negation.h" + #include "ast/NilConstant.h" +@@ -58,7 +55,6 @@ + #include "ast/TypeCast.h" + #include "ast/UnionType.h" + #include "ast/UnnamedVariable.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/UserDefinedFunctor.h" + #include "ast/Variable.h" + #include "souffle/utility/FunctionalUtil.h" +@@ -90,7 +86,6 @@ struct Visitor : souffle::detail::VisitorBase { + SOUFFLE_VISITOR_FORWARD(Variable) + SOUFFLE_VISITOR_FORWARD(UnnamedVariable) + SOUFFLE_VISITOR_FORWARD(IntrinsicFunctor) +- SOUFFLE_VISITOR_FORWARD(IterationCounter) + SOUFFLE_VISITOR_FORWARD(UserDefinedFunctor) + SOUFFLE_VISITOR_FORWARD(Counter) + SOUFFLE_VISITOR_FORWARD(NumericConstant) +@@ -99,8 +94,7 @@ struct Visitor : souffle::detail::VisitorBase { + SOUFFLE_VISITOR_FORWARD(TypeCast) + SOUFFLE_VISITOR_FORWARD(RecordInit) + SOUFFLE_VISITOR_FORWARD(BranchInit) +- SOUFFLE_VISITOR_FORWARD(IntrinsicAggregator) +- SOUFFLE_VISITOR_FORWARD(UserDefinedAggregator) ++ SOUFFLE_VISITOR_FORWARD(Aggregator) + + // literals + SOUFFLE_VISITOR_FORWARD(Atom) +@@ -140,7 +134,6 @@ struct Visitor : souffle::detail::VisitorBase { + SOUFFLE_VISITOR_LINK(Variable, Argument) + SOUFFLE_VISITOR_LINK(UnnamedVariable, Argument) + SOUFFLE_VISITOR_LINK(Counter, Argument) +- SOUFFLE_VISITOR_LINK(IterationCounter, Argument) + SOUFFLE_VISITOR_LINK(TypeCast, Argument) + SOUFFLE_VISITOR_LINK(BranchInit, Argument) + +@@ -158,8 +151,6 @@ struct Visitor : souffle::detail::VisitorBase { + SOUFFLE_VISITOR_LINK(Term, Argument) + + SOUFFLE_VISITOR_LINK(Aggregator, Argument) +- SOUFFLE_VISITOR_LINK(IntrinsicAggregator, Aggregator) +- SOUFFLE_VISITOR_LINK(UserDefinedAggregator, Aggregator) + + SOUFFLE_VISITOR_LINK(Argument, Node); + +@@ -186,7 +177,6 @@ struct Visitor : souffle::detail::VisitorBase { + SOUFFLE_VISITOR_LINK(Relation, Node); + SOUFFLE_VISITOR_LINK(Pragma, Node); + SOUFFLE_VISITOR_LINK(FunctorDeclaration, Node); +- SOUFFLE_VISITOR_LINK(ast::Lattice, Node); + }; + } // namespace souffle::ast + +diff --git a/src/ast2ram/ClauseTranslator.h b/src/ast2ram/ClauseTranslator.h +index 7e399dc..f2c5642 100644 +--- a/src/ast2ram/ClauseTranslator.h ++++ b/src/ast2ram/ClauseTranslator.h +@@ -19,12 +19,9 @@ + + #include "souffle/utility/ContainerUtil.h" + +-#include "ast/Relation.h" +- + namespace souffle::ast { + class Clause; + class Relation; +- + } // namespace souffle::ast + + namespace souffle::ram { +@@ -39,8 +36,6 @@ class TranslatorContext; + enum TranslationMode { + DEFAULT, + +- Auxiliary, +- + // Subsumptive clauses + // + // R(x0) <= R(x1) :- body. +@@ -78,7 +73,7 @@ public: + + /** Translate a recursive clause */ + virtual Own translateRecursiveClause( +- const ast::Clause& clause, const ast::RelationSet& scc, std::size_t version) = 0; ++ const ast::Clause& clause, const std::set& scc, std::size_t version) = 0; + + protected: + /** Translation context */ +diff --git a/src/ast2ram/provenance/ClauseTranslator.cpp b/src/ast2ram/provenance/ClauseTranslator.cpp +index 0cd0e20..c695fdd 100644 +--- a/src/ast2ram/provenance/ClauseTranslator.cpp ++++ b/src/ast2ram/provenance/ClauseTranslator.cpp +@@ -133,7 +133,7 @@ Own ClauseTranslator::addAtomScan(Own op, const + + // add a scan level + std::stringstream ss; +- if (context.getGlobal()->config().has("profile")) { ++ if (Global::config().has("profile")) { + ss << "@frequency-atom" << ';'; + ss << clause.getHead()->getQualifiedName() << ';'; + ss << version << ';'; +diff --git a/src/ast2ram/provenance/UnitTranslator.cpp b/src/ast2ram/provenance/UnitTranslator.cpp +index 1a9abb2..c6fac4a 100644 +--- a/src/ast2ram/provenance/UnitTranslator.cpp ++++ b/src/ast2ram/provenance/UnitTranslator.cpp +@@ -24,14 +24,12 @@ + #include "ast2ram/utility/TranslatorContext.h" + #include "ast2ram/utility/Utils.h" + #include "ast2ram/utility/ValueIndex.h" +-#include "ram/AbstractOperator.h" + #include "ram/Call.h" + #include "ram/DebugInfo.h" + #include "ram/ExistenceCheck.h" + #include "ram/Expression.h" + #include "ram/Filter.h" + #include "ram/Insert.h" +-#include "ram/IntrinsicOperator.h" + #include "ram/LogRelationTimer.h" + #include "ram/MergeExtend.h" + #include "ram/Negation.h" +@@ -52,8 +50,6 @@ + namespace souffle::ast2ram::provenance { + + Own UnitTranslator::generateProgram(const ast::TranslationUnit& translationUnit) { +- glb = &translationUnit.global(); +- + // Do the regular translation + auto ramProgram = seminaive::UnitTranslator::generateProgram(translationUnit); + +@@ -71,12 +67,18 @@ Own UnitTranslator::generateProgram(const ast::TranslationUnit& t + + Own UnitTranslator::createRamRelation( + const ast::Relation* baseRelation, std::string ramRelationName) const { +- auto relation = seminaive::UnitTranslator::createRamRelation(baseRelation, ramRelationName); ++ auto arity = baseRelation->getArity(); ++ ++ // All relations in a provenance program should have a provenance data structure ++ auto representation = RelationRepresentation::PROVENANCE; + +- std::size_t arity = relation->getArity(); +- std::size_t auxiliaryArity = relation->getAuxiliaryArity(); +- std::vector attributeNames = relation->getAttributeNames(); +- std::vector attributeTypeQualifiers = relation->getAttributeTypes(); ++ // Add in base relation information ++ std::vector attributeNames; ++ std::vector attributeTypeQualifiers; ++ for (const auto& attribute : baseRelation->getAttributes()) { ++ attributeNames.push_back(attribute->getName()); ++ attributeTypeQualifiers.push_back(context->getAttributeTypeQualifier(attribute->getTypeName())); ++ } + + // Add in provenance information + attributeNames.push_back("@rule_number"); +@@ -85,8 +87,8 @@ Own UnitTranslator::createRamRelation( + attributeNames.push_back("@level_number"); + attributeTypeQualifiers.push_back("i:number"); + +- return mk(ramRelationName, arity + 2, auxiliaryArity + 2, attributeNames, +- attributeTypeQualifiers, relation->getRepresentation()); ++ return mk( ++ ramRelationName, arity + 2, 2, attributeNames, attributeTypeQualifiers, representation); + } + + std::string UnitTranslator::getInfoRelationName(const ast::Clause* clause) const { +@@ -147,7 +149,7 @@ void UnitTranslator::addAuxiliaryArity( + } + + Own UnitTranslator::generateClearExpiredRelations( +- const ast::RelationSet& /* expiredRelations */) const { ++ const std::set& /* expiredRelations */) const { + // Relations should be preserved if provenance is enabled + return mk(); + } +@@ -285,7 +287,7 @@ Own UnitTranslator::generateInfoClauses(const ast::Program* progr + auto infoClause = mk(std::move(factInsertion)); + + // Add logging +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + std::stringstream clauseText; + clauseText << "@info.clause[" << stringify(toString(*clause)) << "]"; + const std::string logTimerStatement = +@@ -296,18 +298,17 @@ Own UnitTranslator::generateInfoClauses(const ast::Program* progr + // Add debug info + std::ostringstream ds; + ds << "@info.clause["; +- clause->printForDebugInfo(ds); +- ds << "]" ++ ds << toString(*clause) << "]" + << "\nin file "; + ds << clause->getSrcLoc(); + infoClause = mk(std::move(infoClause), ds.str()); + + // Add the subroutine to the program +- std::string stratumID = toString(stratumCount++); ++ std::string stratumID = "stratum_" + toString(stratumCount++); + addRamSubroutine(stratumID, std::move(infoClause)); + + // Push up the subroutine call +- infoClauseCalls.push_back(mk("stratum_" + stratumID)); ++ infoClauseCalls.push_back(mk(stratumID)); + } + + return mk(std::move(infoClauseCalls)); +@@ -438,6 +439,7 @@ Own UnitTranslator::makeNegationSubproofSubroutine(const ast::Cl + + // Create the search sequence + VecOwn searchSequence; ++ std::size_t litNumber = 0; + for (const auto* lit : lits) { + if (const auto* atom = as(lit)) { + auto existenceCheck = makeRamAtomExistenceCheck(atom, idToVarName, *dummyValueIndex); +@@ -465,6 +467,8 @@ Own UnitTranslator::makeNegationSubproofSubroutine(const ast::Cl + appendStmt(searchSequence, std::move(ifStatement)); + } + } ++ ++ litNumber++; + } + + return mk(std::move(searchSequence)); +diff --git a/src/ast2ram/provenance/UnitTranslator.h b/src/ast2ram/provenance/UnitTranslator.h +index d110fdd..c0fc6af 100644 +--- a/src/ast2ram/provenance/UnitTranslator.h ++++ b/src/ast2ram/provenance/UnitTranslator.h +@@ -44,7 +44,7 @@ public: + protected: + Own generateProgram(const ast::TranslationUnit& translationUnit) override; + Own generateClearExpiredRelations( +- const ast::RelationSet& expiredRelations) const override; ++ const std::set& expiredRelations) const override; + Own createRamRelation( + const ast::Relation* baseRelation, std::string ramRelationName) const override; + VecOwn createRamRelations(const std::vector& sccOrdering) const override; +@@ -74,8 +74,6 @@ private: + ram::Node* node, const std::map& idToVar) const; + Own makeIfStatement( + Own condition, Own trueOp, Own falseOp) const; +- +- Global* glb; + }; + + } // namespace souffle::ast2ram::provenance +diff --git a/src/ast2ram/seminaive/ClauseTranslator.cpp b/src/ast2ram/seminaive/ClauseTranslator.cpp +index e527b17..c2e81e8 100644 +--- a/src/ast2ram/seminaive/ClauseTranslator.cpp ++++ b/src/ast2ram/seminaive/ClauseTranslator.cpp +@@ -21,7 +21,6 @@ + #include "ast/BranchInit.h" + #include "ast/Clause.h" + #include "ast/Constant.h" +-#include "ast/IntrinsicAggregator.h" + #include "ast/IntrinsicFunctor.h" + #include "ast/NilConstant.h" + #include "ast/NumericConstant.h" +@@ -31,25 +30,24 @@ + #include "ast/SubsumptiveClause.h" + #include "ast/UnnamedVariable.h" + #include "ast/analysis/Functor.h" ++#include "ast/utility/SipsMetric.h" + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" + #include "ast2ram/utility/Location.h" +-#include "ast2ram/utility/SipsMetric.h" + #include "ast2ram/utility/TranslatorContext.h" + #include "ast2ram/utility/Utils.h" + #include "ast2ram/utility/ValueIndex.h" + #include "ram/Aggregate.h" + #include "ram/Break.h" + #include "ram/Constraint.h" ++#include "ram/CountUniqueKeys.h" + #include "ram/DebugInfo.h" + #include "ram/EmptinessCheck.h" +-#include "ram/EstimateJoinSize.h" + #include "ram/ExistenceCheck.h" + #include "ram/Filter.h" + #include "ram/FloatConstant.h" + #include "ram/GuardedInsert.h" + #include "ram/Insert.h" +-#include "ram/IntrinsicAggregator.h" + #include "ram/LogRelationTimer.h" + #include "ram/Negation.h" + #include "ram/NestedIntrinsicOperator.h" +@@ -61,9 +59,7 @@ + #include "ram/TupleElement.h" + #include "ram/UnpackRecord.h" + #include "ram/UnsignedConstant.h" +-#include "ram/UserDefinedAggregator.h" + #include "ram/utility/Utils.h" +-#include "souffle/TypeAttribute.h" + #include "souffle/utility/StringUtil.h" + #include + #include +@@ -84,8 +80,7 @@ std::string ClauseTranslator::getClauseString(const ast::Clause& clause) const { + auto renamedClone = clone(clause); + + // Update the head atom +- renamedClone->getHead()->setQualifiedName( +- ast::QualifiedName::fromString(getClauseAtomName(clause, clause.getHead()))); ++ renamedClone->getHead()->setQualifiedName(getClauseAtomName(clause, clause.getHead())); + + // Update the body atoms + const auto& cloneAtoms = ast::getBodyLiterals(*renamedClone); +@@ -96,18 +91,14 @@ std::string ClauseTranslator::getClauseString(const ast::Clause& clause) const { + const auto* originalAtom = originalAtoms.at(i); + assert(originalAtom->getQualifiedName() == cloneAtom->getQualifiedName() && + "atom sequence in clone should match"); +- cloneAtom->setQualifiedName(ast::QualifiedName::fromString(getClauseAtomName(clause, originalAtom))); ++ cloneAtom->setQualifiedName(getClauseAtomName(clause, originalAtom)); + } + + return toString(*renamedClone); + } + +-std::string ClauseTranslator::getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom) const { +- return getAtomName(clause, atom, sccAtoms, version, isRecursive(), mode); +-} +- + Own ClauseTranslator::translateRecursiveClause( +- const ast::Clause& clause, const ast::RelationSet& scc, std::size_t version) { ++ const ast::Clause& clause, const std::set& scc, std::size_t version) { + // Update version config + sccAtoms = filter(ast::getBodyLiterals(clause), + [&](auto* atom) { return contains(scc, context.getProgram()->getRelation(*atom)); }); +@@ -117,7 +108,7 @@ Own ClauseTranslator::translateRecursiveClause( + Own rule = translateNonRecursiveClause(clause); + + // Add logging +- if (context.getGlobal()->config().has("profile")) { ++ if (Global::config().has("profile")) { + const std::string& relationName = getConcreteRelationName(clause.getHead()->getQualifiedName()); + const auto& srcLocation = clause.getSrcLoc(); + const std::string clauseText = stringify(toString(clause)); +@@ -131,8 +122,7 @@ Own ClauseTranslator::translateRecursiveClause( + + // Add debug info + std::ostringstream ds; +- clause.printForDebugInfo(ds); +- ds << "\nin file "; ++ ds << toString(clause) << "\nin file "; + ds << clause.getSrcLoc(); + rule = mk(std::move(rule), ds.str()); + +@@ -148,6 +138,55 @@ Own ClauseTranslator::translateNonRecursiveClause(const ast::Cla + return createRamRuleQuery(clause); + } + ++std::string ClauseTranslator::getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom) const { ++ if (isA(clause)) { ++ // find the dominated / dominating heads ++ const auto& body = clause.getBodyLiterals(); ++ auto dominatedHeadAtom = dynamic_cast(body[0]); ++ auto dominatingHeadAtom = dynamic_cast(body[1]); ++ ++ if (clause.getHead() == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getDeleteRelationName(atom->getQualifiedName()); ++ } ++ return getRejectRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatedHeadAtom == atom) { ++ if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ ++ if (dominatingHeadAtom == atom) { ++ switch (mode) { ++ case SubsumeRejectNewCurrent: ++ case SubsumeDeleteCurrentCurrent: return getConcreteRelationName(atom->getQualifiedName()); ++ case SubsumeDeleteCurrentDelta: return getDeltaRelationName(atom->getQualifiedName()); ++ default: return getNewRelationName(atom->getQualifiedName()); ++ } ++ } ++ ++ if (isRecursive()) { ++ if (sccAtoms.at(version + 1) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ } ++ } ++ ++ if (!isRecursive()) { ++ return getConcreteRelationName(atom->getQualifiedName()); ++ } ++ if (clause.getHead() == atom) { ++ return getNewRelationName(atom->getQualifiedName()); ++ } ++ if (sccAtoms.at(version) == atom) { ++ return getDeltaRelationName(atom->getQualifiedName()); ++ } ++ return getConcreteRelationName(atom->getQualifiedName()); ++} ++ + Own ClauseTranslator::createRamFactQuery(const ast::Clause& clause) const { + assert(isFact(clause) && "clause should be fact"); + assert(!isRecursive() && "recursive clauses cannot have facts"); +@@ -240,7 +279,7 @@ Own ClauseTranslator::addAtomScan(Own op, const + } + + std::stringstream ss; +- if (context.getGlobal()->config().has("profile")) { ++ if (Global::config().has("profile")) { + ss << "@frequency-atom" << ';'; + ss << clause.getHead()->getQualifiedName() << ';'; + ss << version << ';'; +@@ -387,23 +426,10 @@ Own ClauseTranslator::instantiateAggregator(Own + const auto* aggExpr = agg->getTargetExpression(); + auto expr = aggExpr ? context.translateValue(*valueIndex, aggExpr) : nullptr; + +- auto aggregator = [&]() -> Own { +- if (const auto* ia = as(agg)) { +- return mk(context.getOverloadedAggregatorOperator(*ia)); +- } else if (const auto* uda = as(agg)) { +- return mk(uda->getBaseOperatorName(), +- context.translateValue(*valueIndex, uda->getInit()), +- context.getFunctorParamTypeAtributes(*uda), context.getFunctorReturnTypeAttribute(*uda), +- context.isStatefulFunctor(*uda)); +- } else { +- fatal("Unhandled aggregate operation"); +- } +- }(); +- + // add Ram-Aggregation layer +- return mk(std::move(op), std::move(aggregator), getClauseAtomName(clause, aggAtom), +- expr ? std::move(expr) : mk(), aggCond ? std::move(aggCond) : mk(), +- curLevel); ++ return mk(std::move(op), context.getOverloadedAggregatorOperator(*agg), ++ getClauseAtomName(clause, aggAtom), expr ? std::move(expr) : mk(), ++ aggCond ? std::move(aggCond) : mk(), curLevel); + } + + Own ClauseTranslator::instantiateMultiResultFunctor( +@@ -680,11 +706,7 @@ std::vector ClauseTranslator::getAtomOrdering(const ast::Clause& cla + } + } + +- std::vector atomNames; +- for (auto* atom : atoms) { +- atomNames.push_back(getClauseAtomName(clause, atom)); +- } +- auto newOrder = context.getSipsMetric()->getReordering(&clause, atomNames); ++ auto newOrder = context.getSipsMetric()->getReordering(&clause, version, mode); + return reorderAtoms(atoms, newOrder); + } + +@@ -775,6 +797,9 @@ void ClauseTranslator::indexAggregatorBody(const ast::Aggregator& agg) { + } + + void ClauseTranslator::indexAggregators(const ast::Clause& clause) { ++ // Add each aggregator as an internal generator ++ visit(clause, [&](const ast::Aggregator& agg) { indexGenerator(agg); }); ++ + // Index aggregator bodies + visit(clause, [&](const ast::Aggregator& agg) { indexAggregatorBody(agg); }); + +@@ -789,6 +814,13 @@ void ClauseTranslator::indexAggregators(const ast::Clause& clause) { + } + + void ClauseTranslator::indexMultiResultFunctors(const ast::Clause& clause) { ++ // Add each multi-result functor as an internal generator ++ visit(clause, [&](const ast::IntrinsicFunctor& func) { ++ if (ast::analysis::FunctorAnalysis::isMultiResult(func)) { ++ indexGenerator(func); ++ } ++ }); ++ + // Add multi-result functor value introductions + visit(clause, [&](const ast::BinaryConstraint& bc) { + if (!isEqConstraint(bc.getBaseOperator())) return; +@@ -800,94 +832,8 @@ void ClauseTranslator::indexMultiResultFunctors(const ast::Clause& clause) { + }); + } + +-void ClauseTranslator::indexGenerators(const ast::Clause& clause) { +- // generators must be indexed in topological order according to +- // dependencies between them. +- // see issue #2416 +- +- std::map varGenerator; +- +- visit(clause, [&](const ast::BinaryConstraint& bc) { +- if (!isEqConstraint(bc.getBaseOperator())) return; +- const auto* lhs = as(bc.getLHS()); +- const ast::Argument* rhs = as(bc.getRHS()); +- if (rhs == nullptr) { +- rhs = as(bc.getRHS()); +- } else { +- if (!ast::analysis::FunctorAnalysis::isMultiResult(*as(rhs))) return; +- } +- if (lhs == nullptr || rhs == nullptr) return; +- varGenerator[lhs->getName()] = rhs; +- }); +- +- // all the generators in the clause +- std::vector generators; +- +- // 'predecessor' mapping from a generator to the generators that must +- // evaluate before. +- std::multimap dependencies; +- +- // harvest generators and dependencies +- visit(clause, [&](const ast::Argument& arg) { +- if (const ast::IntrinsicFunctor* func = as(arg)) { +- if (ast::analysis::FunctorAnalysis::isMultiResult(*func)) { +- generators.emplace_back(func); +- visit(func, [&](const ast::Variable& use) { +- if (varGenerator.count(use.getName()) > 0) { +- dependencies.emplace(func, varGenerator.at(use.getName())); +- } +- }); +- } +- } else if (const ast::Aggregator* agg = as(arg)) { +- generators.emplace_back(agg); +- visit(agg, [&](const ast::Variable& use) { +- if (varGenerator.count(use.getName()) > 0) { +- dependencies.emplace(agg, varGenerator.at(use.getName())); +- } +- }); +- } +- }); +- +- // the set of already indexed generators +- std::set indexed; +- // the recursion stack to detect a cycle in the depth-first traversal +- std::set recStack; +- +- // recursive depth-first traversal, perform a post-order indexing of genertors. +- const std::function dfs = [&](const ast::Argument* reached) { +- if (indexed.count(reached)) { +- return; +- } +- +- if (!recStack.emplace(reached).second) { +- // cycle detected +- fatal("cyclic dependency"); +- } +- +- auto range = dependencies.equal_range(reached); +- for (auto it = range.first; it != range.second; ++it) { +- if (it->second == reached) { +- continue; // ignore self-dependency +- } +- dfs(it->second); +- } +- +- // index this generator +- indexGenerator(*reached); +- +- indexed.insert(reached); +- recStack.erase(reached); +- }; +- +- // topological sorting by depth-first search +- for (const ast::Argument* root : generators) { +- dfs(root); +- } +-} +- + void ClauseTranslator::indexClause(const ast::Clause& clause) { + indexAtoms(clause); +- indexGenerators(clause); + indexAggregators(clause); + indexMultiResultFunctors(clause); + } +diff --git a/src/ast2ram/seminaive/ClauseTranslator.h b/src/ast2ram/seminaive/ClauseTranslator.h +index 5d3bcb2..d2901e4 100644 +--- a/src/ast2ram/seminaive/ClauseTranslator.h ++++ b/src/ast2ram/seminaive/ClauseTranslator.h +@@ -57,7 +57,7 @@ public: + /** Entry points */ + Own translateNonRecursiveClause(const ast::Clause& clause); + Own translateRecursiveClause( +- const ast::Clause& clause, const ast::RelationSet& scc, std::size_t version); ++ const ast::Clause& clause, const std::set& scc, std::size_t version); + + protected: + std::size_t version{0}; +@@ -66,6 +66,7 @@ protected: + bool isRecursive() const; + + std::string getClauseString(const ast::Clause& clause) const; ++ + std::string getClauseAtomName(const ast::Clause& clause, const ast::Atom* atom) const; + + virtual Own addNegatedAtom( +@@ -87,7 +88,6 @@ protected: + + /** Indexing */ + void indexClause(const ast::Clause& clause); +- void indexGenerators(const ast::Clause& clause); + virtual void indexAtoms(const ast::Clause& clause); + void indexAggregators(const ast::Clause& clause); + void indexMultiResultFunctors(const ast::Clause& clause); +diff --git a/src/ast2ram/seminaive/UnitTranslator.cpp b/src/ast2ram/seminaive/UnitTranslator.cpp +index 47d486e..d6137ef 100644 +--- a/src/ast2ram/seminaive/UnitTranslator.cpp ++++ b/src/ast2ram/seminaive/UnitTranslator.cpp +@@ -20,15 +20,12 @@ + #include "ast/Relation.h" + #include "ast/SubsumptiveClause.h" + #include "ast/TranslationUnit.h" +-#include "ast/UserDefinedFunctor.h" + #include "ast/analysis/TopologicallySortedSCCGraph.h" + #include "ast/utility/Utils.h" + #include "ast/utility/Visitor.h" + #include "ast2ram/ClauseTranslator.h" + #include "ast2ram/utility/TranslatorContext.h" + #include "ast2ram/utility/Utils.h" +-#include "ram/Aggregate.h" +-#include "ram/Assign.h" + #include "ram/Call.h" + #include "ram/Clear.h" + #include "ram/Condition.h" +@@ -43,7 +40,6 @@ + #include "ram/Filter.h" + #include "ram/IO.h" + #include "ram/Insert.h" +-#include "ram/IntrinsicOperator.h" + #include "ram/LogRelationTimer.h" + #include "ram/LogSize.h" + #include "ram/LogTimer.h" +@@ -62,11 +58,6 @@ + #include "ram/Swap.h" + #include "ram/TranslationUnit.h" + #include "ram/TupleElement.h" +-#include "ram/UndefValue.h" +-#include "ram/UnsignedConstant.h" +-#include "ram/UserDefinedAggregator.h" +-#include "ram/UserDefinedOperator.h" +-#include "ram/Variable.h" + #include "ram/utility/Utils.h" + #include "reports/DebugReport.h" + #include "reports/ErrorReport.h" +@@ -80,7 +71,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -118,11 +108,10 @@ Own UnitTranslator::generateNonRecursiveRelation(const ast::Rela + } + + // Translate clause +- TranslationMode mode = rel.getAuxiliaryArity() > 0 ? Auxiliary : DEFAULT; +- Own rule = context->translateNonRecursiveClause(*clause, mode); ++ Own rule = context->translateNonRecursiveClause(*clause); + + // Add logging +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + const std::string& relationName = toString(rel.getQualifiedName()); + const auto& srcLocation = clause->getSrcLoc(); + const std::string clauseText = stringify(toString(*clause)); +@@ -133,8 +122,7 @@ Own UnitTranslator::generateNonRecursiveRelation(const ast::Rela + + // Add debug info + std::ostringstream ds; +- clause->printForDebugInfo(ds); +- ds << "\nin file "; ++ ds << toString(*clause) << "\nin file "; + ds << clause->getSrcLoc(); + rule = mk(std::move(rule), ds.str()); + +@@ -143,7 +131,7 @@ Own UnitTranslator::generateNonRecursiveRelation(const ast::Rela + } + + // Add logging for entire relation +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + const std::string& relationName = toString(rel.getQualifiedName()); + const auto& srcLocation = rel.getSrcLoc(); + const std::string logSizeStatement = LogStatement::nNonrecursiveRelation(relationName, srcLocation); +@@ -183,24 +171,14 @@ Own UnitTranslator::generateStratum(std::size_t scc) const { + const auto* rel = *sccRelations.begin(); + appendStmt(current, generateNonRecursiveRelation(*rel)); + +- // lub auxiliary arities using the @lub relation +- if (rel->getAuxiliaryArity() > 0) { +- std::string mainRelation = getConcreteRelationName(rel->getQualifiedName()); +- std::string newRelation = getNewRelationName(rel->getQualifiedName()); +- std::string deltaRelation = getDeltaRelationName(rel->getQualifiedName()); +- appendStmt(current, generateStratumLubSequence(*rel, false)); +- std::map directives; +- appendStmt(current, mk(newRelation)); +- } +- + // issue delete sequence for non-recursive subsumptions +- appendStmt(current, generateNonRecursiveDelete(*rel)); ++ appendStmt(current, generateNonRecursiveDelete(sccRelations)); + } + + // Get all non-recursive relation statements +- auto nonRecursiveJoinSizeStatements = context->getNonRecursiveJoinSizeStatementsInSCC(scc); +- auto joinSizeSequence = mk(std::move(nonRecursiveJoinSizeStatements)); +- appendStmt(current, std::move(joinSizeSequence)); ++ auto nonRecursiveUniqueKeyStatements = context->getNonRecursiveUniqueKeyStatementsInSCC(scc); ++ auto uniqueKeySequence = mk(std::move(nonRecursiveUniqueKeyStatements)); ++ appendStmt(current, std::move(uniqueKeySequence)); + + // Store all internal output relations to the output dir with a .csv extension + for (const auto& relation : context->getOutputRelationsInSCC(scc)) { +@@ -211,7 +189,7 @@ Own UnitTranslator::generateStratum(std::size_t scc) const { + } + + Own UnitTranslator::generateClearExpiredRelations( +- const ast::RelationSet& expiredRelations) const { ++ const std::set& expiredRelations) const { + VecOwn stmts; + for (const auto& relation : expiredRelations) { + appendStmt(stmts, generateClearRelation(relation)); +@@ -282,31 +260,8 @@ Own UnitTranslator::generateMergeRelations( + return stmt; + } + +-Own UnitTranslator::generateDebugRelation(const ast::Relation* rel, +- const std::string& destRelation, const std::string& srcRelation, +- Own iteration) const { +- VecOwn values; +- +- for (std::size_t i = 0; i < rel->getArity(); i++) { +- values.push_back(mk(0, i)); +- } +- +- values.push_back(std::move(iteration)); +- +- // Proposition - insert if not empty +- if (rel->getArity() == 0) { +- auto insertion = mk(destRelation, std::move(values)); +- return mk(mk( +- mk(mk(srcRelation)), std::move(insertion))); +- } +- +- auto insertion = mk(destRelation, std::move(values)); +- auto stmt = mk(mk(srcRelation, 0, std::move(insertion))); +- return stmt; +-} +- + Own UnitTranslator::translateRecursiveClauses( +- const ast::RelationSet& scc, const ast::Relation* rel) const { ++ const std::set& scc, const ast::Relation* rel) const { + assert(contains(scc, rel) && "relation should belong to scc"); + VecOwn code; + +@@ -328,7 +283,7 @@ Own UnitTranslator::translateRecursiveClauses( + } + + Own UnitTranslator::translateSubsumptiveRecursiveClauses( +- const ast::RelationSet& scc, const ast::Relation* rel) const { ++ const std::set& scc, const ast::Relation* rel) const { + assert(contains(scc, rel) && "relation should belong to scc"); + + VecOwn code; +@@ -352,11 +307,15 @@ Own UnitTranslator::translateSubsumptiveRecursiveClauses( + continue; + } + +- const std::size_t version = 0; +- // find dominated tuples in the newR by tuples in newR and store them in rejectR +- appendStmt(code, context->translateRecursiveClause(*clause, scc, version, SubsumeRejectNewNew)); +- // find dominated tuples in the newR by tuples in R and store them in rejectR +- appendStmt(code, context->translateRecursiveClause(*clause, scc, version, SubsumeRejectNewCurrent)); ++ const auto& sccAtoms = getSccAtoms(clause, scc); ++ for (std::size_t version = 0; version < sccAtoms.size(); version++) { ++ // find dominated tuples in the newR by tuples in newR and store them in rejectR ++ appendStmt(code, context->translateRecursiveClause(*clause, scc, version, SubsumeRejectNewNew)); ++ ++ // find dominated tuples in the newR by tuples in R and store them in rejectR ++ appendStmt( ++ code, context->translateRecursiveClause(*clause, scc, version, SubsumeRejectNewCurrent)); ++ } + } + + // compute new delta set, i.e., deltaR = newR \ rejectR +@@ -374,9 +333,8 @@ Own UnitTranslator::translateSubsumptiveRecursiveClauses( + const auto& sccAtoms = getSccAtoms(clause, scc); + std::size_t sz = sccAtoms.size(); + for (std::size_t version = 0; version < sz; version++) { +- appendStmt( +- code, context->translateRecursiveClause(*clause, scc, version, +- (version >= 1) ? SubsumeDeleteCurrentCurrent : SubsumeDeleteCurrentDelta)); ++ appendStmt(code, context->translateRecursiveClause(*clause, scc, version, ++ (sz > 1) ? SubsumeDeleteCurrentCurrent : SubsumeDeleteCurrentDelta)); + } + appendStmt(code, generateEraseTuples(rel, mainRelation, deleteRelation)); + appendStmt(code, mk(deleteRelation)); +@@ -386,7 +344,7 @@ Own UnitTranslator::translateSubsumptiveRecursiveClauses( + } + + std::vector UnitTranslator::getSccAtoms( +- const ast::Clause* clause, const ast::RelationSet& scc) const { ++ const ast::Clause* clause, const std::set& scc) const { + const auto& sccAtoms = filter(ast::getBodyLiterals(*clause), [&](const ast::Atom* atom) { + if (isA(clause)) { + const auto& body = clause->getBodyLiterals(); +@@ -400,7 +358,7 @@ std::vector UnitTranslator::getSccAtoms( + } + + VecOwn UnitTranslator::generateClauseVersions( +- const ast::Clause* clause, const ast::RelationSet& scc) const { ++ const ast::Clause* clause, const std::set& scc) const { + const auto& sccAtoms = getSccAtoms(clause, scc); + + // Create each version +@@ -421,51 +379,55 @@ VecOwn UnitTranslator::generateClauseVersions( + return clauseVersions; + } + +-Own UnitTranslator::generateNonRecursiveDelete(const ast::Relation& rel) const { ++Own UnitTranslator::generateNonRecursiveDelete( ++ const std::set& scc) const { + VecOwn code; + + // Generate code for non-recursive subsumption +- if (!context->hasSubsumptiveClause(rel.getQualifiedName())) { +- return mk(std::move(code)); +- } +- +- std::string mainRelation = getConcreteRelationName(rel.getQualifiedName()); +- std::string deleteRelation = getDeleteRelationName(rel.getQualifiedName()); +- +- // Compute subsumptive deletions for non-recursive rules +- for (auto clause : context->getProgram()->getClauses(rel)) { +- if (!isA(clause)) { ++ for (const ast::Relation* rel : scc) { ++ if (!context->hasSubsumptiveClause(rel->getQualifiedName())) { + continue; + } + +- // Translate subsumptive clause +- Own rule = context->translateNonRecursiveClause(*clause, SubsumeDeleteCurrentCurrent); ++ std::string mainRelation = getConcreteRelationName(rel->getQualifiedName()); ++ std::string deleteRelation = getDeleteRelationName(rel->getQualifiedName()); + +- // Add logging for subsumptive clause +- if (glb->config().has("profile")) { +- const std::string& relationName = toString(rel.getQualifiedName()); +- const auto& srcLocation = clause->getSrcLoc(); +- const std::string clauseText = stringify(toString(*clause)); +- const std::string logTimerStatement = +- LogStatement::tNonrecursiveRule(relationName, srcLocation, clauseText); +- rule = mk(std::move(rule), logTimerStatement, mainRelation); +- } ++ // Compute subsumptive deletions for non-recursive rules ++ for (auto clause : context->getProgram()->getClauses(*rel)) { ++ if (!isA(clause)) { ++ continue; ++ } + +- // Add debug info for subsumptive clause +- std::ostringstream ds; +- ds << toString(*clause) << "\nin file "; +- ds << clause->getSrcLoc(); +- rule = mk(std::move(rule), ds.str()); ++ // Translate subsumptive clause ++ Own rule = ++ context->translateNonRecursiveClause(*clause, SubsumeDeleteCurrentCurrent); ++ ++ // Add logging for subsumptive clause ++ if (Global::config().has("profile")) { ++ const std::string& relationName = toString(rel->getQualifiedName()); ++ const auto& srcLocation = clause->getSrcLoc(); ++ const std::string clauseText = stringify(toString(*clause)); ++ const std::string logTimerStatement = ++ LogStatement::tNonrecursiveRule(relationName, srcLocation, clauseText); ++ rule = mk(std::move(rule), logTimerStatement, mainRelation); ++ } ++ ++ // Add debug info for subsumptive clause ++ std::ostringstream ds; ++ ds << toString(*clause) << "\nin file "; ++ ds << clause->getSrcLoc(); ++ rule = mk(std::move(rule), ds.str()); + +- // Add subsumptive rule to result +- appendStmt(code, std::move(rule)); ++ // Add subsumptive rule to result ++ appendStmt(code, std::move(rule)); ++ } ++ appendStmt(code, mk(generateEraseTuples(rel, mainRelation, deleteRelation), ++ mk(deleteRelation))); + } +- appendStmt(code, mk(generateEraseTuples(&rel, mainRelation, deleteRelation), +- mk(deleteRelation))); + return mk(std::move(code)); + } + +-Own UnitTranslator::generateStratumPreamble(const ast::RelationSet& scc) const { ++Own UnitTranslator::generateStratumPreamble(const std::set& scc) const { + VecOwn preamble; + + // Generate code for non-recursive rules +@@ -473,34 +435,22 @@ Own UnitTranslator::generateStratumPreamble(const ast::RelationS + std::string deltaRelation = getDeltaRelationName(rel->getQualifiedName()); + std::string mainRelation = getConcreteRelationName(rel->getQualifiedName()); + appendStmt(preamble, generateNonRecursiveRelation(*rel)); +- // lub tuples using the @lub relation +- if (rel->getAuxiliaryArity() > 0) { +- std::string newRelation = getNewRelationName(rel->getQualifiedName()); +- appendStmt(preamble, generateStratumLubSequence(*rel, false)); +- } +- // Generate non recursive delete sequences for subsumptive rules +- appendStmt(preamble, generateNonRecursiveDelete(*rel)); + } + ++ // Generate non recursive delete sequences for subsumptive rules ++ appendStmt(preamble, generateNonRecursiveDelete(scc)); ++ + // Generate code for priming relation + for (const ast::Relation* rel : scc) { + std::string deltaRelation = getDeltaRelationName(rel->getQualifiedName()); + std::string mainRelation = getConcreteRelationName(rel->getQualifiedName()); + appendStmt(preamble, generateMergeRelations(rel, deltaRelation, mainRelation)); + } +- +- for (const ast::Relation* rel : scc) { +- if (const auto* debugRel = context->getDeltaDebugRelation(rel)) { +- const std::string debugRelation = getConcreteRelationName(debugRel->getQualifiedName()); +- std::string deltaRelation = getDeltaRelationName(rel->getQualifiedName()); +- appendStmt(preamble, +- generateDebugRelation(rel, debugRelation, deltaRelation, mk(0))); +- } +- } + return mk(std::move(preamble)); + } + +-Own UnitTranslator::generateStratumPostamble(const ast::RelationSet& scc) const { ++Own UnitTranslator::generateStratumPostamble( ++ const std::set& scc) const { + VecOwn postamble; + for (const ast::Relation* rel : scc) { + // Drop temporary tables after recursion +@@ -510,7 +460,8 @@ Own UnitTranslator::generateStratumPostamble(const ast::Relation + return mk(std::move(postamble)); + } + +-Own UnitTranslator::generateStratumTableUpdates(const ast::RelationSet& scc) const { ++Own UnitTranslator::generateStratumTableUpdates( ++ const std::set& scc) const { + VecOwn updateTable; + + for (const ast::Relation* rel : scc) { +@@ -521,11 +472,7 @@ Own UnitTranslator::generateStratumTableUpdates(const ast::Relat + + // swap new and and delta relation and clear new relation afterwards (if not a subsumptive relation) + Own updateRelTable; +- if (rel->getAuxiliaryArity() > 0) { +- updateRelTable = +- mk(mk(deltaRelation), generateStratumLubSequence(*rel, true), +- generateMergeRelations(rel, mainRelation, deltaRelation)); +- } else if (!context->hasSubsumptiveClause(rel->getQualifiedName())) { ++ if (!context->hasSubsumptiveClause(rel->getQualifiedName())) { + updateRelTable = mk(generateMergeRelations(rel, mainRelation, newRelation), + mk(deltaRelation, newRelation), mk(newRelation)); + } else { +@@ -533,30 +480,22 @@ Own UnitTranslator::generateStratumTableUpdates(const ast::Relat + } + + // Measure update time +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + updateRelTable = mk(std::move(updateRelTable), + LogStatement::cRecursiveRelation(toString(rel->getQualifiedName()), rel->getSrcLoc()), + newRelation); + } + + appendStmt(updateTable, std::move(updateRelTable)); +- if (const auto* debugRel = context->getDeltaDebugRelation(rel)) { +- const std::string debugRelation = getConcreteRelationName(debugRel->getQualifiedName()); +- appendStmt(updateTable, generateDebugRelation(rel, debugRelation, deltaRelation, +- mk("loop_counter"))); +- } + } + return mk(std::move(updateTable)); + } + +-Own UnitTranslator::generateStratumLoopBody(const ast::RelationSet& scc) const { ++Own UnitTranslator::generateStratumLoopBody(const std::set& scc) const { + VecOwn loopBody; + +- const bool hasProfile = glb->config().has("profile"); +- +- auto addProfiling = [hasProfile]( +- const ast::Relation* rel, Own stmt) -> Own { +- if (hasProfile) { ++ auto addProfiling = [](const ast::Relation* rel, Own stmt) -> Own { ++ if (Global::config().has("profile")) { + const std::string& relationName = toString(rel->getQualifiedName()); + const auto& srcLocation = rel->getSrcLoc(); + const std::string logTimerStatement = LogStatement::tRecursiveRelation(relationName, srcLocation); +@@ -585,135 +524,8 @@ Own UnitTranslator::generateStratumLoopBody(const ast::RelationS + return mk(std::move(loopBody)); + } + +-/// assuming the @new() relation is populated with new tuples, generate RAM code +-/// to populate the @delta() relation with the lubbed elements from @new() +-Own UnitTranslator::generateStratumLubSequence( +- const ast::Relation& rel, bool inRecursiveLoop) const { +- VecOwn stmts; +- assert(rel.getAuxiliaryArity() > 0); +- +- auto attributes = rel.getAttributes(); +- std::string name = getConcreteRelationName(rel.getQualifiedName()); +- std::string lubName = getLubRelationName(rel.getQualifiedName()); +- std::string newName = getNewRelationName(rel.getQualifiedName()); +- +- const std::size_t arity = rel.getArity(); +- const std::size_t auxiliaryArity = rel.getAuxiliaryArity(); +- +- // Step 1 : populate @lub() from @new() +- VecOwn values; +- +- // index of the first auxiliary element of the relation +- std::size_t firstAuxiliary = arity - auxiliaryArity; +- +- for (std::size_t i = 0; i < arity; i++) { +- if (i >= firstAuxiliary) { +- values.push_back(mk(i - firstAuxiliary + 1, 0)); +- } else { +- values.push_back(mk(0, i)); +- } +- } +- Own op = mk(lubName, std::move(values)); +- +- for (std::size_t i = arity; i >= firstAuxiliary + 1; i--) { +- const auto type = attributes[i - 1]->getTypeName(); +- std::size_t level = i - firstAuxiliary; +- auto aggregator = context->getLatticeTypeLubAggregator(type, mk(0, i - 1)); +- Own condition = mk( +- BinaryConstraintOp::NE, mk(level, i - 1), mk(0, i - 1)); +- for (std::size_t j = 0; j < attributes.size(); j++) { +- if (attributes[j]->getIsLattice()) break; +- condition = mk(std::move(condition), +- mk(BinaryConstraintOp::EQ, mk(level, j), +- mk(0, j))); +- } +- op = mk(std::move(op), std::move(aggregator), newName, +- mk(level, i - 1), std::move(condition), level); +- } +- +- op = mk(newName, 0, std::move(op)); +- appendStmt(stmts, mk(std::move(op))); +- +- // clear @new() now that we no longer need it +- appendStmt(stmts, mk(newName)); +- +- if (inRecursiveLoop) { +- // Step 2 : populate @delta() from @lub() for tuples that have to be lubbed with @concrete +- std::string deltaName = getDeltaRelationName(rel.getQualifiedName()); +- +- Own condition; +- for (std::size_t i = 0; i < arity; i++) { +- if (i < firstAuxiliary) { +- values.push_back(mk(0, i)); +- } else { +- assert(attributes[i]->getIsLattice()); +- const auto type = attributes[i]->getTypeName(); +- VecOwn args; +- args.push_back(mk(0, i)); +- args.push_back(mk(1, i)); +- auto lub = context->getLatticeTypeLubFunctor(type, std::move(args)); +- auto cst = +- mk(BinaryConstraintOp::EQ, mk(1, i), clone(lub)); +- if (condition) { +- condition = mk(std::move(condition), std::move(cst)); +- } else { +- condition = std::move(cst); +- } +- values.push_back(std::move(lub)); +- } +- } +- op = mk(deltaName, std::move(values)); +- op = mk(mk(std::move(condition)), std::move(op)); +- +- for (std::size_t i = 0; i < arity - auxiliaryArity; i++) { +- auto cst = mk( +- BinaryConstraintOp::EQ, mk(0, i), mk(1, i)); +- if (condition) { +- condition = mk(std::move(condition), std::move(cst)); +- } else { +- condition = std::move(cst); +- } +- } +- if (condition) { +- op = mk(std::move(condition), std::move(op)); +- } +- op = mk(name, 1, std::move(op)); +- op = mk(lubName, 0, std::move(op)); +- appendStmt(stmts, mk(std::move(op))); +- +- // Step 3 : populate @delta() from @lub() for tuples that have nothing to lub in @concrete +- for (std::size_t i = 0; i < arity; i++) { +- values.push_back(mk(0, i)); +- } +- op = mk(deltaName, std::move(values)); +- for (std::size_t i = 0; i < arity; i++) { +- if (i < firstAuxiliary) { +- values.push_back(mk(0, i)); +- } else { +- values.push_back(mk()); +- } +- } +- op = mk( +- mk(mk(name, std::move(values))), std::move(op)); +- op = mk(lubName, 0, std::move(op)); +- appendStmt(stmts, mk(std::move(op))); +- } else { +- // we are not in the recursive loop, so the concrete relation is empty for now +- // we can just insert the content of the @lub relation into the concrete one +- for (std::size_t i = 0; i < arity; i++) { +- values.push_back(mk(0, i)); +- } +- op = mk(name, std::move(values)); +- op = mk(lubName, 0, std::move(op)); +- appendStmt(stmts, mk(std::move(op))); +- } +- +- appendStmt(stmts, mk(lubName)); +- +- return mk(std::move(stmts)); +-} +- +-Own UnitTranslator::generateStratumExitSequence(const ast::RelationSet& scc) const { ++Own UnitTranslator::generateStratumExitSequence( ++ const std::set& scc) const { + // Helper function to add a new term to a conjunctive condition + auto addCondition = [&](Own& cond, Own term) { + cond = (cond == nullptr) ? std::move(term) : mk(std::move(cond), std::move(term)); +@@ -749,7 +561,7 @@ Own UnitTranslator::generateStratumExitSequence(const ast::Relat + + /** generate RAM code for recursive relations in a strongly-connected component */ + Own UnitTranslator::generateRecursiveStratum( +- const ast::RelationSet& scc, std::size_t sccNumber) const { ++ const std::set& scc, std::size_t sccNumber) const { + assert(!scc.empty() && "scc set should not be empty"); + VecOwn result; + +@@ -757,23 +569,15 @@ Own UnitTranslator::generateRecursiveStratum( + appendStmt(result, generateStratumPreamble(scc)); + + // Get all recursive relation statements +- auto recursiveJoinSizeStatements = context->getRecursiveJoinSizeStatementsInSCC(sccNumber); +- auto joinSizeSequence = mk(std::move(recursiveJoinSizeStatements)); +- +- const std::string loop_counter = "loop_counter"; +- VecOwn inc; +- inc.push_back(mk(loop_counter)); +- inc.push_back(mk(1)); +- auto increment_counter = mk(mk(loop_counter), +- mk(FunctorOp::UADD, std::move(inc)), false); ++ auto recursiveUniqueKeyStatements = context->getRecursiveUniqueKeyStatementsInSCC(sccNumber); ++ auto uniqueKeySequence = mk(std::move(recursiveUniqueKeyStatements)); ++ + // Add in the main fixpoint loop + auto loopBody = generateStratumLoopBody(scc); + auto exitSequence = generateStratumExitSequence(scc); + auto updateSequence = generateStratumTableUpdates(scc); +- auto fixpointLoop = mk(mk(std::move(loopBody), std::move(joinSizeSequence), +- std::move(exitSequence), std::move(updateSequence), std::move(increment_counter))); +- +- appendStmt(result, mk(mk(loop_counter), mk(1), true)); ++ auto fixpointLoop = mk(mk(std::move(loopBody), std::move(uniqueKeySequence), ++ std::move(exitSequence), std::move(updateSequence))); + appendStmt(result, std::move(fixpointLoop)); + + // Add in the postamble +@@ -794,7 +598,7 @@ Own UnitTranslator::generateLoadRelation(const ast::Relation* re + for (const auto& [key, value] : load->getParameters()) { + directives.insert(std::make_pair(key, unescape(value))); + } +- if (glb->config().has("no-warn")) { ++ if (Global::config().has("no-warn")) { + directives.insert(std::make_pair("no-warn", "true")); + } + addAuxiliaryArity(relation, directives); +@@ -802,7 +606,7 @@ Own UnitTranslator::generateLoadRelation(const ast::Relation* re + // Create the resultant load statement, with profile information + std::string ramRelationName = getConcreteRelationName(relation->getQualifiedName()); + Own loadStmt = mk(ramRelationName, directives); +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + const std::string logTimerStatement = + LogStatement::tRelationLoadTime(ramRelationName, relation->getSrcLoc()); + loadStmt = mk(std::move(loadStmt), logTimerStatement, ramRelationName); +@@ -825,7 +629,7 @@ Own UnitTranslator::generateStoreRelation(const ast::Relation* r + // Create the resultant store statement, with profile information + std::string ramRelationName = getConcreteRelationName(relation->getQualifiedName()); + Own storeStmt = mk(ramRelationName, directives); +- if (glb->config().has("profile")) { ++ if (Global::config().has("profile")) { + const std::string logTimerStatement = + LogStatement::tRelationSaveTime(ramRelationName, relation->getSrcLoc()); + storeStmt = mk(std::move(storeStmt), logTimerStatement, ramRelationName); +@@ -838,10 +642,6 @@ Own UnitTranslator::generateStoreRelation(const ast::Relation* r + Own UnitTranslator::createRamRelation( + const ast::Relation* baseRelation, std::string ramRelationName) const { + auto arity = baseRelation->getArity(); +- +- bool mergeAuxiliary = (ramRelationName != getNewRelationName(baseRelation->getQualifiedName())); +- +- auto auxArity = mergeAuxiliary ? baseRelation->getAuxiliaryArity() : 0; + auto representation = baseRelation->getRepresentation(); + if (representation == RelationRepresentation::BTREE_DELETE && ramRelationName[0] == '@') { + representation = RelationRepresentation::DEFAULT; +@@ -855,7 +655,7 @@ Own UnitTranslator::createRamRelation( + } + + return mk( +- ramRelationName, arity, auxArity, attributeNames, attributeTypeQualifiers, representation); ++ ramRelationName, arity, 0, attributeNames, attributeTypeQualifiers, representation); + } + + VecOwn UnitTranslator::createRamRelations(const std::vector& sccOrdering) const { +@@ -867,24 +667,16 @@ VecOwn UnitTranslator::createRamRelations(const std::vectorgetQualifiedName()); + ramRelations.push_back(createRamRelation(rel, mainName)); + +- if (rel->getAuxiliaryArity() > 0) { +- // Add lub relation +- std::string lubName = getLubRelationName(rel->getQualifiedName()); +- ramRelations.push_back(createRamRelation(rel, lubName)); +- } +- +- if (isRecursive || rel->getAuxiliaryArity() > 0) { +- // Add new relation +- std::string newName = getNewRelationName(rel->getQualifiedName()); +- ramRelations.push_back(createRamRelation(rel, newName)); +- } +- + // Recursive relations also require @delta and @new variants, with the same signature + if (isRecursive) { + // Add delta relation + std::string deltaName = getDeltaRelationName(rel->getQualifiedName()); + ramRelations.push_back(createRamRelation(rel, deltaName)); + ++ // Add new relation ++ std::string newName = getNewRelationName(rel->getQualifiedName()); ++ ramRelations.push_back(createRamRelation(rel, newName)); ++ + // Add auxiliary relation for subsumption + if (context->hasSubsumptiveClause(rel->getQualifiedName())) { + // Add reject relation +@@ -912,7 +704,6 @@ Own UnitTranslator::generateProgram(const ast::TranslationUnit& t + } + const auto& sccOrdering = + translationUnit.getAnalysis().order(); +- VecOwn res; + + // Create subroutines for each SCC according to topological order + for (std::size_t i = 0; i < sccOrdering.size(); i++) { +@@ -924,17 +715,18 @@ Own UnitTranslator::generateProgram(const ast::TranslationUnit& t + stratum = mk(std::move(stratum), generateClearExpiredRelations(expiredRelations)); + + // Add the subroutine +- const ast::Relation* rel = *context->getRelationsInSCC(sccOrdering.at(i)).begin(); +- +- std::string stratumID = rel->getQualifiedName().toString(); ++ std::string stratumID = "stratum_" + toString(i); + addRamSubroutine(stratumID, std::move(stratum)); ++ } + +- // invoke the strata +- appendStmt(res, mk("stratum_" + stratumID)); ++ // Invoke all strata ++ VecOwn res; ++ for (std::size_t i = 0; i < sccOrdering.size(); i++) { ++ appendStmt(res, mk("stratum_" + toString(i))); + } + + // Add main timer if profiling +- if (!res.empty() && glb->config().has("profile")) { ++ if (!res.empty() && Global::config().has("profile")) { + auto newStmt = mk(mk(std::move(res)), LogStatement::runtime()); + res.clear(); + appendStmt(res, std::move(newStmt)); +@@ -945,8 +737,6 @@ Own UnitTranslator::generateProgram(const ast::TranslationUnit& t + } + + Own UnitTranslator::translateUnit(ast::TranslationUnit& tu) { +- glb = &tu.global(); +- + /* -- Set-up -- */ + auto ram_start = std::chrono::high_resolution_clock::now(); + context = mk(tu); +@@ -966,7 +756,7 @@ Own UnitTranslator::translateUnit(ast::TranslationUnit& tu + mk(std::move(ramRelations), std::move(ramMain), std::move(ramSubroutines)); + + // Add the translated program to the debug report +- if (glb->config().has("debug-report")) { ++ if (Global::config().has("debug-report")) { + auto ram_end = std::chrono::high_resolution_clock::now(); + std::string runtimeStr = + "(" + std::to_string(std::chrono::duration(ram_end - ram_start).count()) + "s)"; +@@ -976,7 +766,7 @@ Own UnitTranslator::translateUnit(ast::TranslationUnit& tu + } + + // Wrap the program into a translation unit +- return mk(tu.global(), std::move(ramProgram), errReport, debugReport); ++ return mk(std::move(ramProgram), errReport, debugReport); + } + + } // namespace souffle::ast2ram::seminaive +diff --git a/src/ast2ram/seminaive/UnitTranslator.h b/src/ast2ram/seminaive/UnitTranslator.h +index 4be81c5..19f8d7e 100644 +--- a/src/ast2ram/seminaive/UnitTranslator.h ++++ b/src/ast2ram/seminaive/UnitTranslator.h +@@ -14,19 +14,13 @@ + + #pragma once + +-#include "ast/Relation.h" + #include "ast2ram/UnitTranslator.h" +-#include "ram/Expression.h" + #include "souffle/utility/ContainerUtil.h" + #include + #include + #include + #include + +-namespace souffle { +-class Global; +-} +- + namespace souffle::ast { + class Clause; + class Relation; +@@ -56,12 +50,13 @@ protected: + const ast::Relation* baseRelation, std::string ramRelationName) const; + virtual VecOwn createRamRelations(const std::vector& sccOrdering) const; + Own translateRecursiveClauses( +- const ast::RelationSet& scc, const ast::Relation* rel) const; ++ const std::set& scc, const ast::Relation* rel) const; + Own translateSubsumptiveRecursiveClauses( +- const ast::RelationSet& scc, const ast::Relation* rel) const; ++ const std::set& scc, const ast::Relation* rel) const; + VecOwn generateClauseVersions( +- const ast::Clause* clause, const ast::RelationSet& scc) const; +- std::vector getSccAtoms(const ast::Clause* clause, const ast::RelationSet& scc) const; ++ const ast::Clause* clause, const std::set& scc) const; ++ std::vector getSccAtoms( ++ const ast::Clause* clause, const std::set& scc) const; + + virtual void addAuxiliaryArity( + const ast::Relation* relation, std::map& directives) const; +@@ -71,7 +66,8 @@ protected: + /** High-level relation translation */ + virtual Own generateProgram(const ast::TranslationUnit& translationUnit); + Own generateNonRecursiveRelation(const ast::Relation& rel) const; +- Own generateRecursiveStratum(const ast::RelationSet& scc, std::size_t sccNum) const; ++ Own generateRecursiveStratum( ++ const std::set& scc, std::size_t sccNum) const; + + /** IO translation */ + Own generateStoreRelation(const ast::Relation* relation) const; +@@ -79,16 +75,16 @@ protected: + + /** Low-level stratum translation */ + Own generateStratum(std::size_t scc) const; +- Own generateStratumPreamble(const ast::RelationSet& scc) const; +- Own generateNonRecursiveDelete(const ast::Relation& rel) const; +- Own generateStratumPostamble(const ast::RelationSet& scc) const; +- Own generateStratumLoopBody(const ast::RelationSet& scc) const; +- Own generateStratumTableUpdates(const ast::RelationSet& scc) const; +- Own generateStratumExitSequence(const ast::RelationSet& scc) const; +- Own generateStratumLubSequence(const ast::Relation& rel, bool inRecursiveLoop) const; ++ Own generateStratumPreamble(const std::set& scc) const; ++ Own generateNonRecursiveDelete(const std::set& scc) const; ++ Own generateStratumPostamble(const std::set& scc) const; ++ Own generateStratumLoopBody(const std::set& scc) const; ++ Own generateStratumTableUpdates(const std::set& scc) const; ++ Own generateStratumExitSequence(const std::set& scc) const; + + /** Other helper generations */ +- virtual Own generateClearExpiredRelations(const ast::RelationSet& expiredRelations) const; ++ virtual Own generateClearExpiredRelations( ++ const std::set& expiredRelations) const; + Own generateClearRelation(const ast::Relation* relation) const; + virtual Own generateMergeRelations( + const ast::Relation* rel, const std::string& destRelation, const std::string& srcRelation) const; +@@ -97,14 +93,9 @@ protected: + const std::string& filterRelation) const; + virtual Own generateEraseTuples( + const ast::Relation* rel, const std::string& destRelation, const std::string& srcRelation) const; +- virtual Own generateDebugRelation(const ast::Relation* rel, +- const std::string& destRelation, const std::string& srcRelation, +- Own iteration) const; + + private: + std::map> ramSubroutines; +- +- Global* glb; + }; + + } // namespace souffle::ast2ram::seminaive +diff --git a/src/ast2ram/seminaive/ValueTranslator.cpp b/src/ast2ram/seminaive/ValueTranslator.cpp +index 0f150f9..4dbfffd 100644 +--- a/src/ast2ram/seminaive/ValueTranslator.cpp ++++ b/src/ast2ram/seminaive/ValueTranslator.cpp +@@ -33,7 +33,6 @@ + #include "ram/UndefValue.h" + #include "ram/UnsignedConstant.h" + #include "ram/UserDefinedOperator.h" +-#include "ram/Variable.h" + #include "ram/utility/Utils.h" + #include "souffle/utility/StringUtil.h" + +@@ -111,11 +110,6 @@ Own ValueTranslator::visit_(type_identity, const + return mk(); + } + +-Own ValueTranslator::visit_( +- type_identity, const ast::IterationCounter&) { +- return mk("loop_counter"); +-} +- + Own ValueTranslator::visit_(type_identity, const ast::RecordInit& init) { + VecOwn values; + for (const auto& cur : init.getArguments()) { +diff --git a/src/ast2ram/seminaive/ValueTranslator.h b/src/ast2ram/seminaive/ValueTranslator.h +index 8e99120..a4774bc 100644 +--- a/src/ast2ram/seminaive/ValueTranslator.h ++++ b/src/ast2ram/seminaive/ValueTranslator.h +@@ -62,8 +62,6 @@ public: + Own visit_( + type_identity, const ast::UserDefinedFunctor& udf) override; + Own visit_(type_identity, const ast::Counter& ctr) override; +- Own visit_( +- type_identity, const ast::IterationCounter& ctr) override; + Own visit_(type_identity, const ast::RecordInit& init) override; + Own visit_(type_identity, const ast::BranchInit& init) override; + Own visit_(type_identity, const ast::Aggregator& agg) override; +diff --git a/src/ast2ram/utility/TranslatorContext.cpp b/src/ast2ram/utility/TranslatorContext.cpp +index 9bbf576..2cb0bdc 100644 +--- a/src/ast2ram/utility/TranslatorContext.cpp ++++ b/src/ast2ram/utility/TranslatorContext.cpp +@@ -14,53 +14,41 @@ + + #include "ast2ram/utility/TranslatorContext.h" + #include "Global.h" +-#include "ast/Aggregator.h" + #include "ast/Atom.h" + #include "ast/BranchInit.h" + #include "ast/Directive.h" +-#include "ast/Functor.h" +-#include "ast/IntrinsicFunctor.h" + #include "ast/QualifiedName.h" + #include "ast/SubsumptiveClause.h" + #include "ast/TranslationUnit.h" +-#include "ast/UserDefinedAggregator.h" + #include "ast/analysis/Functor.h" + #include "ast/analysis/IOType.h" +-#include "ast/analysis/JoinSize.h" + #include "ast/analysis/RecursiveClauses.h" + #include "ast/analysis/RelationSchedule.h" + #include "ast/analysis/SCCGraph.h" ++#include "ast/analysis/UniqueKeys.h" + #include "ast/analysis/typesystem/PolymorphicObjects.h" + #include "ast/analysis/typesystem/SumTypeBranches.h" + #include "ast/analysis/typesystem/Type.h" + #include "ast/analysis/typesystem/TypeEnvironment.h" + #include "ast/analysis/typesystem/TypeSystem.h" ++#include "ast/utility/SipsMetric.h" + #include "ast/utility/Utils.h" + #include "ast2ram/ClauseTranslator.h" + #include "ast2ram/ConstraintTranslator.h" + #include "ast2ram/ValueTranslator.h" + #include "ast2ram/provenance/TranslationStrategy.h" + #include "ast2ram/seminaive/TranslationStrategy.h" +-#include "ast2ram/utility/SipsMetric.h" +-#include "ram/AbstractOperator.h" + #include "ram/Condition.h" + #include "ram/Expression.h" +-#include "ram/IntrinsicAggregator.h" +-#include "ram/IntrinsicOperator.h" + #include "ram/Statement.h" +-#include "ram/UndefValue.h" +-#include "ram/UserDefinedAggregator.h" +-#include "ram/UserDefinedOperator.h" + #include "souffle/utility/FunctionalUtil.h" + #include "souffle/utility/StringUtil.h" +-#include + #include + + namespace souffle::ast2ram { + + TranslatorContext::TranslatorContext(const ast::TranslationUnit& tu) { + program = &tu.getProgram(); +- global = &tu.global(); + + // Set up analyses + functorAnalysis = &tu.getAnalysis(); +@@ -72,7 +60,7 @@ TranslatorContext::TranslatorContext(const ast::TranslationUnit& tu) { + typeEnv = &tu.getAnalysis().getTypeEnvironment(); + sumTypeBranches = &tu.getAnalysis(); + polyAnalysis = &tu.getAnalysis(); +- joinSizeAnalysis = &tu.getAnalysis(); ++ uniqueKeysAnalysis = &tu.getAnalysis(); + + // Set up clause nums + for (const ast::Relation* rel : program->getRelations()) { +@@ -87,30 +75,17 @@ TranslatorContext::TranslatorContext(const ast::TranslationUnit& tu) { + + // Set up SIPS metric + std::string sipsChosen = "all-bound"; +- if (global->config().has("RamSIPS")) { +- sipsChosen = global->config().get("RamSIPS"); ++ if (Global::config().has("RamSIPS")) { ++ sipsChosen = Global::config().get("RamSIPS"); + } + sipsMetric = ast::SipsMetric::create(sipsChosen, tu); + + // Set up the correct strategy +- if (global->config().has("provenance")) { ++ if (Global::config().has("provenance")) { + translationStrategy = mk(); + } else { + translationStrategy = mk(); + } +- +- // populates deltaRel +- for (const ast::Relation* rel : program->getRelations()) { +- const auto delta = rel->getIsDeltaDebug(); +- if (delta.has_value()) { +- deltaRel[program->getRelation(delta.value())] = rel; +- } +- } +- +- // populates map type name -> lattice +- for (const ast::Lattice* lattice : program->getLattices()) { +- lattices.emplace(lattice->getQualifiedName(), lattice); +- } + } + + TranslatorContext::~TranslatorContext() = default; +@@ -128,40 +103,6 @@ std::string TranslatorContext::getAttributeTypeQualifier(const ast::QualifiedNam + return getTypeQualifier(typeEnv->getType(name)); + } + +-Own TranslatorContext::getLatticeTypeLubFunctor( +- const ast::QualifiedName& typeName, VecOwn args) const { +- const ast::Lattice* lattice = lattices.at(typeName); +- if (const auto* lub = as(lattice->getLub())) { +- const auto typeAttributes = getFunctorParamTypeAtributes(*lub); +- const auto returnAttribute = getFunctorReturnTypeAttribute(*lub); +- bool stateful = isStatefulFunctor(*lub); +- return mk( +- lub->getName(), typeAttributes, returnAttribute, stateful, std::move(args)); +- } else if (const auto* lub = as(lattice->getLub())) { +- assert(false && lub && "intrinsic functors not yet supported in lattice"); +- // return mk(getOverloadedFunctorOp(lub->getBaseFunctionOp()), +- // std::move(args)); +- } +- assert(false); +- return {}; +-} +- +-Own TranslatorContext::getLatticeTypeLubAggregator( +- const ast::QualifiedName& typeName, Own init) const { +- const ast::Lattice* lattice = lattices.at(typeName); +- if (const auto* lub = as(lattice->getLub())) { +- const auto typeAttributes = getFunctorParamTypeAtributes(*lub); +- const auto returnAttribute = getFunctorReturnTypeAttribute(*lub); +- bool stateful = isStatefulFunctor(*lub); +- return mk( +- lub->getName(), std::move(init), typeAttributes, returnAttribute, stateful); +- } else if (const auto* lub = as(lattice->getLub())) { +- assert(false && lub && "intrinsic aggregators not yet supported in lattice"); +- } +- assert(false); +- return {}; +-} +- + std::size_t TranslatorContext::getNumberOfSCCs() const { + return sccGraph->getNumberOfSCCs(); + } +@@ -170,14 +111,6 @@ bool TranslatorContext::isRecursiveSCC(std::size_t scc) const { + return sccGraph->isRecursive(scc); + } + +-const ast::Relation* TranslatorContext::getDeltaDebugRelation(const ast::Relation* rel) const { +- const auto res = deltaRel.find(rel); +- if (res != deltaRel.end()) { +- return res->second; +- } +- return nullptr; +-} +- + std::vector TranslatorContext::getStoreDirectives(const ast::QualifiedName& name) const { + return filter(program->getDirectives(name), [&](const ast::Directive* dir) { + return dir->getType() == ast::DirectiveType::printsize || +@@ -199,21 +132,21 @@ std::size_t TranslatorContext::getSizeLimit(const ast::Relation* relation) const + return ioType->getLimitSize(relation); + } + +-ast::RelationSet TranslatorContext::getRelationsInSCC(std::size_t scc) const { ++std::set TranslatorContext::getRelationsInSCC(std::size_t scc) const { + return sccGraph->getInternalRelations(scc); + } + +-ast::RelationSet TranslatorContext::getInputRelationsInSCC(std::size_t scc) const { ++std::set TranslatorContext::getInputRelationsInSCC(std::size_t scc) const { + return sccGraph->getInternalInputRelations(scc); + } + +-ast::RelationSet TranslatorContext::getOutputRelationsInSCC(std::size_t scc) const { ++std::set TranslatorContext::getOutputRelationsInSCC(std::size_t scc) const { + return sccGraph->getInternalOutputRelations(scc); + } + +-VecOwn TranslatorContext::getRecursiveJoinSizeStatementsInSCC(std::size_t scc) const { ++VecOwn TranslatorContext::getRecursiveUniqueKeyStatementsInSCC(std::size_t scc) const { + VecOwn res; +- for (auto&& s : joinSizeAnalysis->getJoinSizeStatementsInSCC(scc)) { ++ for (auto&& s : uniqueKeysAnalysis->getUniqueKeyStatementsInSCC(scc)) { + if (s->isRecursiveRelation()) { + res.push_back(clone(s)); + } +@@ -221,9 +154,9 @@ VecOwn TranslatorContext::getRecursiveJoinSizeStatementsInSCC(st + return res; + } + +-VecOwn TranslatorContext::getNonRecursiveJoinSizeStatementsInSCC(std::size_t scc) const { ++VecOwn TranslatorContext::getNonRecursiveUniqueKeyStatementsInSCC(std::size_t scc) const { + VecOwn res; +- for (auto&& s : joinSizeAnalysis->getJoinSizeStatementsInSCC(scc)) { ++ for (auto&& s : uniqueKeysAnalysis->getUniqueKeyStatementsInSCC(scc)) { + if (!s->isRecursiveRelation()) { + res.push_back(clone(s)); + } +@@ -231,7 +164,7 @@ VecOwn TranslatorContext::getNonRecursiveJoinSizeStatementsInSCC + return res; + } + +-ast::RelationSet TranslatorContext::getExpiredRelations(std::size_t scc) const { ++std::set TranslatorContext::getExpiredRelations(std::size_t scc) const { + return relationSchedule->schedule().at(scc).expired(); + } + +@@ -262,30 +195,12 @@ bool TranslatorContext::isStatefulFunctor(const ast::UserDefinedFunctor& udf) co + return functorAnalysis->isStatefulFunctor(udf); + } + +-TypeAttribute TranslatorContext::getFunctorReturnTypeAttribute(const ast::UserDefinedAggregator& uda) const { +- return typeAnalysis->getAggregatorReturnTypeAttribute(uda); +-} +- +-TypeAttribute TranslatorContext::getFunctorParamTypeAtribute( +- const ast::UserDefinedAggregator& uda, std::size_t idx) const { +- return typeAnalysis->getAggregatorParamTypeAttribute(uda, idx); +-} +- +-std::vector TranslatorContext::getFunctorParamTypeAtributes( +- const ast::UserDefinedAggregator& uda) const { +- return typeAnalysis->getAggregatorParamTypeAttributes(uda); +-} +- +-bool TranslatorContext::isStatefulFunctor(const ast::UserDefinedAggregator& uda) const { +- return functorAnalysis->isStatefulFunctor(uda); +-} +- + ast::NumericConstant::Type TranslatorContext::getInferredNumericConstantType( + const ast::NumericConstant& nc) const { + return polyAnalysis->getInferredType(nc); + } + +-AggregateOp TranslatorContext::getOverloadedAggregatorOperator(const ast::IntrinsicAggregator& aggr) const { ++AggregateOp TranslatorContext::getOverloadedAggregatorOperator(const ast::Aggregator& aggr) const { + return polyAnalysis->getOverloadedOperator(aggr); + } + +@@ -309,7 +224,7 @@ int TranslatorContext::getADTBranchId(const ast::BranchInit* adt) const { + auto iterToBranch = std::lower_bound(branches.begin(), branches.end(), searchDummy, + [](const ast::analysis::AlgebraicDataType::Branch& left, + const ast::analysis::AlgebraicDataType::Branch& right) { +- return left.name.lexicalLess(right.name); ++ return left.name < right.name; + }); + return static_cast(std::distance(std::begin(branches), iterToBranch)); + } +@@ -326,7 +241,7 @@ Own TranslatorContext::translateNonRecursiveClause( + } + + Own TranslatorContext::translateRecursiveClause(const ast::Clause& clause, +- const ast::RelationSet& scc, std::size_t version, TranslationMode mode) const { ++ const std::set& scc, std::size_t version, TranslationMode mode) const { + auto clauseTranslator = Own(translationStrategy->createClauseTranslator(*this, mode)); + return clauseTranslator->translateRecursiveClause(clause, scc, version); + } +diff --git a/src/ast2ram/utility/TranslatorContext.h b/src/ast2ram/utility/TranslatorContext.h +index 50b718c..1f5c404 100644 +--- a/src/ast2ram/utility/TranslatorContext.h ++++ b/src/ast2ram/utility/TranslatorContext.h +@@ -18,12 +18,10 @@ + #include "FunctorOps.h" + #include "ast/NumericConstant.h" + #include "ast/QualifiedName.h" +-#include "ast/UserDefinedAggregator.h" +-#include "ast/analysis/JoinSize.h" + #include "ast/analysis/ProfileUse.h" ++#include "ast/analysis/UniqueKeys.h" + #include "ast/analysis/typesystem/Type.h" + #include "ast2ram/ClauseTranslator.h" +-#include "ram/Aggregator.h" + #include "souffle/BinaryConstraintOps.h" + #include "souffle/TypeAttribute.h" + #include "souffle/utility/ContainerUtil.h" +@@ -31,10 +29,6 @@ + #include + #include + +-namespace souffle { +-class Global; +-} +- + namespace souffle::ast { + class Aggregator; + class Atom; +@@ -69,7 +63,7 @@ class RelationScheduleAnalysis; + class SumTypeBranchesAnalysis; + class SCCGraphAnalysis; + class TypeEnvironment; +-class JoinSizeAnalysis; ++class UniqueKeysAnalysis; + } // namespace souffle::ast::analysis + + namespace souffle::ast2ram { +@@ -82,10 +76,6 @@ public: + TranslatorContext(const ast::TranslationUnit& tu); + ~TranslatorContext(); + +- const Global* getGlobal() const { +- return global; +- } +- + const ast::Program* getProgram() const { + return program; + } +@@ -97,14 +87,6 @@ public: + bool hasSizeLimit(const ast::Relation* relation) const; + std::size_t getSizeLimit(const ast::Relation* relation) const; + +- Own getLatticeTypeLubAggregator( +- const ast::QualifiedName& typeName, Own init) const; +- Own getLatticeTypeLubFunctor( +- const ast::QualifiedName& typeName, VecOwn args) const; +- +- /** Associates a relation with its delta_debug relation if present */ +- const ast::Relation* getDeltaDebugRelation(const ast::Relation* rel) const; +- + /** Clause methods */ + bool hasSubsumptiveClause(const ast::QualifiedName& name) const; + bool isRecursiveClause(const ast::Clause* clause) const; +@@ -113,14 +95,14 @@ public: + /** SCC methods */ + std::size_t getNumberOfSCCs() const; + bool isRecursiveSCC(std::size_t scc) const; +- ast::RelationSet getExpiredRelations(std::size_t scc) const; +- ast::RelationSet getRelationsInSCC(std::size_t scc) const; +- ast::RelationSet getInputRelationsInSCC(std::size_t scc) const; +- ast::RelationSet getOutputRelationsInSCC(std::size_t scc) const; ++ std::set getExpiredRelations(std::size_t scc) const; ++ std::set getRelationsInSCC(std::size_t scc) const; ++ std::set getInputRelationsInSCC(std::size_t scc) const; ++ std::set getOutputRelationsInSCC(std::size_t scc) const; + +- /** JoinSize methods */ +- VecOwn getRecursiveJoinSizeStatementsInSCC(std::size_t scc) const; +- VecOwn getNonRecursiveJoinSizeStatementsInSCC(std::size_t scc) const; ++ /** UniqueKeys methods */ ++ VecOwn getRecursiveUniqueKeyStatementsInSCC(std::size_t scc) const; ++ VecOwn getNonRecursiveUniqueKeyStatementsInSCC(std::size_t scc) const; + + /** Functor methods */ + TypeAttribute getFunctorReturnTypeAttribute(const ast::Functor& functor) const; +@@ -128,13 +110,6 @@ public: + std::vector getFunctorParamTypeAtributes(const ast::UserDefinedFunctor& udf) const; + bool isStatefulFunctor(const ast::UserDefinedFunctor& functor) const; + +- /** Functor methods */ +- TypeAttribute getFunctorReturnTypeAttribute(const ast::UserDefinedAggregator& aggregator) const; +- TypeAttribute getFunctorParamTypeAtribute( +- const ast::UserDefinedAggregator& aggregator, std::size_t idx) const; +- std::vector getFunctorParamTypeAtributes(const ast::UserDefinedAggregator& udf) const; +- bool isStatefulFunctor(const ast::UserDefinedAggregator& aggregator) const; +- + /** ADT methods */ + bool isADTEnum(const ast::BranchInit* adt) const; + int getADTBranchId(const ast::BranchInit* adt) const; +@@ -142,7 +117,7 @@ public: + + /** Polymorphic objects methods */ + ast::NumericConstant::Type getInferredNumericConstantType(const ast::NumericConstant& nc) const; +- AggregateOp getOverloadedAggregatorOperator(const ast::IntrinsicAggregator& aggr) const; ++ AggregateOp getOverloadedAggregatorOperator(const ast::Aggregator& aggr) const; + BinaryConstraintOp getOverloadedBinaryConstraintOperator(const ast::BinaryConstraint& bc) const; + FunctorOp getOverloadedFunctorOp(const ast::IntrinsicFunctor& inf) const; + +@@ -154,8 +129,9 @@ public: + /** Translation strategy */ + Own translateNonRecursiveClause( + const ast::Clause& clause, TranslationMode mode = DEFAULT) const; +- Own translateRecursiveClause(const ast::Clause& clause, const ast::RelationSet& scc, +- std::size_t version, TranslationMode mode = DEFAULT) const; ++ Own translateRecursiveClause(const ast::Clause& clause, ++ const std::set& scc, std::size_t version, ++ TranslationMode mode = DEFAULT) const; + + Own translateConstraint(const ValueIndex& index, const ast::Literal* lit) const; + +@@ -163,7 +139,6 @@ public: + + private: + const ast::Program* program; +- const Global* global; + const ast::analysis::RecursiveClausesAnalysis* recursiveClauses; + const ast::analysis::RelationScheduleAnalysis* relationSchedule; + const ast::analysis::SCCGraphAnalysis* sccGraph; +@@ -173,12 +148,10 @@ private: + const ast::analysis::TypeEnvironment* typeEnv; + const ast::analysis::SumTypeBranchesAnalysis* sumTypeBranches; + const ast::analysis::PolymorphicObjectsAnalysis* polyAnalysis; +- const ast::analysis::JoinSizeAnalysis* joinSizeAnalysis; ++ const ast::analysis::UniqueKeysAnalysis* uniqueKeysAnalysis; + std::map clauseNums; + Own sipsMetric; + Own translationStrategy; +- std::map deltaRel; +- ast::UnorderedQualifiedNameMap lattices; + }; + + } // namespace souffle::ast2ram +diff --git a/src/ast2ram/utility/Utils.cpp b/src/ast2ram/utility/Utils.cpp +index 24afdb6..8a554c5 100644 +--- a/src/ast2ram/utility/Utils.cpp ++++ b/src/ast2ram/utility/Utils.cpp +@@ -19,11 +19,10 @@ + #include "ast/Clause.h" + #include "ast/QualifiedName.h" + #include "ast/Relation.h" +-#include "ast/SubsumptiveClause.h" + #include "ast/UnnamedVariable.h" + #include "ast/Variable.h" ++ + #include "ast/utility/Utils.h" +-#include "ast2ram/ClauseTranslator.h" + #include "ast2ram/utility/Location.h" + #include "ram/Clear.h" + #include "ram/Condition.h" +@@ -36,62 +35,6 @@ + + namespace souffle::ast2ram { + +-std::string getAtomName(const ast::Clause& clause, const ast::Atom* atom, +- const std::vector& sccAtoms, std::size_t version, bool isRecursive, +- TranslationMode mode) { +- if (isA(clause)) { +- // find the dominated / dominating heads +- const auto& body = clause.getBodyLiterals(); +- auto dominatedHeadAtom = dynamic_cast(body[0]); +- auto dominatingHeadAtom = dynamic_cast(body[1]); +- +- if (clause.getHead() == atom) { +- if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { +- return getDeleteRelationName(atom->getQualifiedName()); +- } +- return getRejectRelationName(atom->getQualifiedName()); +- } +- +- if (dominatedHeadAtom == atom) { +- if (mode == SubsumeDeleteCurrentDelta || mode == SubsumeDeleteCurrentCurrent) { +- return getConcreteRelationName(atom->getQualifiedName()); +- } +- return getNewRelationName(atom->getQualifiedName()); +- } +- +- if (dominatingHeadAtom == atom) { +- switch (mode) { +- case SubsumeRejectNewCurrent: +- case SubsumeDeleteCurrentCurrent: return getConcreteRelationName(atom->getQualifiedName()); +- case SubsumeDeleteCurrentDelta: return getDeltaRelationName(atom->getQualifiedName()); +- default: return getNewRelationName(atom->getQualifiedName()); +- } +- } +- +- if (isRecursive) { +- if (sccAtoms.at(version + 1) == atom) { +- return getDeltaRelationName(atom->getQualifiedName()); +- } +- } +- +- return getConcreteRelationName(atom->getQualifiedName()); +- } +- +- if (!isRecursive) { +- if (mode == Auxiliary && clause.getHead() == atom) { +- return getNewRelationName(atom->getQualifiedName()); +- } +- return getConcreteRelationName(atom->getQualifiedName()); +- } +- if (clause.getHead() == atom) { +- return getNewRelationName(atom->getQualifiedName()); +- } +- if (sccAtoms.at(version) == atom) { +- return getDeltaRelationName(atom->getQualifiedName()); +- } +- return getConcreteRelationName(atom->getQualifiedName()); +-} +- + std::string getConcreteRelationName(const ast::QualifiedName& name, const std::string prefix) { + return prefix + getRelationName(name); + } +@@ -104,10 +47,6 @@ std::string getNewRelationName(const ast::QualifiedName& name) { + return getConcreteRelationName(name, "@new_"); + } + +-std::string getLubRelationName(const ast::QualifiedName& name) { +- return getConcreteRelationName(name, "@lub_"); +-} +- + std::string getRejectRelationName(const ast::QualifiedName& name) { + return getConcreteRelationName(name, "@reject_"); + } +@@ -116,8 +55,8 @@ std::string getDeleteRelationName(const ast::QualifiedName& name) { + return getConcreteRelationName(name, "@delete_"); + } + +-const std::string& getRelationName(const ast::QualifiedName& name) { +- return name.toString(); ++std::string getRelationName(const ast::QualifiedName& name) { ++ return toString(join(name.getQualifiers(), ".")); + } + + std::string getBaseRelationName(const ast::QualifiedName& name) { +diff --git a/src/ast2ram/utility/Utils.h b/src/ast2ram/utility/Utils.h +index 46e4e9b..0738b68 100644 +--- a/src/ast2ram/utility/Utils.h ++++ b/src/ast2ram/utility/Utils.h +@@ -16,7 +16,6 @@ + + #pragma once + +-#include "ast2ram/seminaive/ClauseTranslator.h" + #include "souffle/utility/ContainerUtil.h" + #include + +@@ -38,15 +37,11 @@ namespace souffle::ast2ram { + + struct Location; + +-/** Get the corresponding atom name given the clause and other state */ +-std::string getAtomName(const ast::Clause& clause, const ast::Atom* atom, +- const std::vector& sccAtoms, std::size_t version, bool isRecursive, TranslationMode mode); +- + /** Get the corresponding concretised RAM relation name for the relation */ + std::string getConcreteRelationName(const ast::QualifiedName& name, const std::string prefix = ""); + + /** converts the given relation identifier into a relation name */ +-const std::string& getRelationName(const ast::QualifiedName& name); ++std::string getRelationName(const ast::QualifiedName& name); + + /** Get the corresponding RAM delta relation name for the relation */ + std::string getDeltaRelationName(const ast::QualifiedName& name); +@@ -54,9 +49,6 @@ std::string getDeltaRelationName(const ast::QualifiedName& name); + /** Get the corresponding RAM 'new' relation name for the relation */ + std::string getNewRelationName(const ast::QualifiedName& name); + +-/** Get the corresponding RAM 'lub' relation name for the relation */ +-std::string getLubRelationName(const ast::QualifiedName& name); +- + /** Get the corresponding RAM 'reject' relation name for the relation */ + std::string getRejectRelationName(const ast::QualifiedName& name); + +diff --git a/src/include/souffle/CompiledOptions.h b/src/include/souffle/CompiledOptions.h +index 9de6c86..ea007e7 100644 +--- a/src/include/souffle/CompiledOptions.h ++++ b/src/include/souffle/CompiledOptions.h +@@ -151,7 +151,7 @@ public: + break; + /* Output directory for resulting .csv files */ + case 'D': +- if (*optarg && !existDir(optarg) && !dirIsStdout(optarg)) { ++ if (*optarg && !existDir(optarg)) { + printf("Output directory %s does not exists!\n", optarg); + ok = false; + } +@@ -222,11 +222,6 @@ private: + #endif + std::cerr << " -h -- prints this help page.\n"; + std::cerr << "--------------------------------------------------------------------\n"; +-#ifdef SOUFFLE_GENERATOR_VERSION +- std::cerr << " Version: " << SOUFFLE_GENERATOR_VERSION << std::endl; +-#endif +- std::cerr << " Word size: " << RAM_DOMAIN_SIZE << " bits" << std::endl; +- std::cerr << "--------------------------------------------------------------------\n"; + std::cerr << " Copyright (c) 2016-22 The Souffle Developers." << std::endl; + std::cerr << " Copyright (c) 2013-16 Oracle and/or its affiliates." << std::endl; + std::cerr << " All rights reserved.\n"; +@@ -258,13 +253,6 @@ private: + } + return false; + } +- +- /** +- * Check whether the output is "-", for which the output should be stdout +- */ +- bool dirIsStdout(const std::string& name) const { +- return name == "-"; +- } + }; + + } // end of namespace souffle +diff --git a/src/include/souffle/CompiledSouffle.h b/src/include/souffle/CompiledSouffle.h +index 9183e98..01008a7 100644 +--- a/src/include/souffle/CompiledSouffle.h ++++ b/src/include/souffle/CompiledSouffle.h +@@ -23,16 +23,16 @@ + #include "souffle/SymbolTable.h" + #include "souffle/datastructure/BTreeDelete.h" + #include "souffle/datastructure/Brie.h" +-#include "souffle/datastructure/ConcurrentCache.h" +-#include "souffle/datastructure/EqRel.h" +-#include "souffle/datastructure/Info.h" +-#include "souffle/datastructure/Nullaries.h" ++#include "souffle/datastructure/EquivalenceRelation.h" + #include "souffle/datastructure/RecordTableImpl.h" + #include "souffle/datastructure/SymbolTableImpl.h" + #include "souffle/datastructure/Table.h" + #include "souffle/io/IOSystem.h" + #include "souffle/io/WriteStream.h" + #include "souffle/utility/EvaluatorUtil.h" ++#ifndef __EMBEDDED_SOUFFLE__ ++#include "souffle/CompiledOptions.h" ++#endif + + #if defined(_OPENMP) + #include +@@ -155,4 +155,328 @@ public: + } + }; + ++/** Nullary relations */ ++class t_nullaries { ++private: ++ std::atomic data{false}; ++ ++public: ++ static constexpr Relation::arity_type Arity = 0; ++ ++ t_nullaries() = default; ++ using t_tuple = Tuple; ++ struct context {}; ++ context createContext() { ++ return context(); ++ } ++ class iterator { ++ bool value; ++ ++ public: ++ using iterator_category = std::forward_iterator_tag; ++ using value_type = RamDomain*; ++ using difference_type = ptrdiff_t; ++ using pointer = value_type*; ++ using reference = value_type&; ++ ++ iterator(bool v = false) : value(v) {} ++ ++ const RamDomain* operator*() { ++ return nullptr; ++ } ++ ++ bool operator==(const iterator& other) const { ++ return other.value == value; ++ } ++ ++ bool operator!=(const iterator& other) const { ++ return other.value != value; ++ } ++ ++ iterator& operator++() { ++ if (value) { ++ value = false; ++ } ++ return *this; ++ } ++ }; ++ iterator begin() const { ++ return iterator(data); ++ } ++ iterator end() const { ++ return iterator(); ++ } ++ void insert(const t_tuple& /* t */) { ++ data = true; ++ } ++ void insert(const t_tuple& /* t */, context& /* ctxt */) { ++ data = true; ++ } ++ void insert(const RamDomain* /* ramDomain */) { ++ data = true; ++ } ++ bool insert() { ++ bool result = data; ++ data = true; ++ return !result; ++ } ++ bool contains(const t_tuple& /* t */) const { ++ return data; ++ } ++ bool contains(const t_tuple& /* t */, context& /* ctxt */) const { ++ return data; ++ } ++ std::size_t size() const { ++ return data ? 1 : 0; ++ } ++ bool empty() const { ++ return !data; ++ } ++ void purge() { ++ data = false; ++ } ++ void printStatistics(std::ostream& /* o */) const {} ++}; ++ ++/** Info relations */ ++template ++class t_info { ++public: ++ static constexpr Relation::arity_type Arity = Arity_; ++ ++ t_info() = default; ++ using t_tuple = Tuple; ++ struct context {}; ++ context createContext() { ++ return context(); ++ } ++ class iterator : public std::iterator> { ++ typename std::vector>::const_iterator it; ++ ++ public: ++ iterator(const typename std::vector::const_iterator& o) : it(o) {} ++ ++ const t_tuple operator*() { ++ return *it; ++ } ++ ++ bool operator==(const iterator& other) const { ++ return other.it == it; ++ } ++ ++ bool operator!=(const iterator& other) const { ++ return !(*this == other); ++ } ++ ++ iterator& operator++() { ++ it++; ++ return *this; ++ } ++ }; ++ iterator begin() const { ++ return iterator(data.begin()); ++ } ++ iterator end() const { ++ return iterator(data.end()); ++ } ++ void insert(const t_tuple& t) { ++ insert_lock.lock(); ++ if (!contains(t)) { ++ data.push_back(t); ++ } ++ insert_lock.unlock(); ++ } ++ void insert(const t_tuple& t, context& /* ctxt */) { ++ insert(t); ++ } ++ void insert(const RamDomain* ramDomain) { ++ insert_lock.lock(); ++ t_tuple t; ++ for (std::size_t i = 0; i < Arity; ++i) { ++ t.data[i] = ramDomain[i]; ++ } ++ data.push_back(t); ++ insert_lock.unlock(); ++ } ++ bool contains(const t_tuple& t) const { ++ for (const auto& o : data) { ++ if (t == o) { ++ return true; ++ } ++ } ++ return false; ++ } ++ bool contains(const t_tuple& t, context& /* ctxt */) const { ++ return contains(t); ++ } ++ std::size_t size() const { ++ return data.size(); ++ } ++ bool empty() const { ++ return data.size() == 0; ++ } ++ void purge() { ++ data.clear(); ++ } ++ void printStatistics(std::ostream& /* o */) const {} ++ ++private: ++ std::vector> data; ++ Lock insert_lock; ++}; ++ ++/** Equivalence relations */ ++struct t_eqrel { ++ static constexpr Relation::arity_type Arity = 2; ++ using t_tuple = Tuple; ++ using t_ind = EquivalenceRelation; ++ t_ind ind; ++ class iterator_0 : public std::iterator { ++ using nested_iterator = typename t_ind::iterator; ++ nested_iterator nested; ++ t_tuple value; ++ ++ public: ++ iterator_0(const nested_iterator& iter) : nested(iter), value(*iter) {} ++ iterator_0(const iterator_0& other) = default; ++ iterator_0& operator=(const iterator_0& other) = default; ++ bool operator==(const iterator_0& other) const { ++ return nested == other.nested; ++ } ++ bool operator!=(const iterator_0& other) const { ++ return !(*this == other); ++ } ++ const t_tuple& operator*() const { ++ return value; ++ } ++ const t_tuple* operator->() const { ++ return &value; ++ } ++ iterator_0& operator++() { ++ ++nested; ++ value = *nested; ++ return *this; ++ } ++ }; ++ class iterator_1 : public std::iterator { ++ using nested_iterator = typename t_ind::iterator; ++ nested_iterator nested; ++ t_tuple value; ++ ++ public: ++ iterator_1(const nested_iterator& iter) : nested(iter), value(reorder(*iter)) {} ++ iterator_1(const iterator_1& other) = default; ++ iterator_1& operator=(const iterator_1& other) = default; ++ bool operator==(const iterator_1& other) const { ++ return nested == other.nested; ++ } ++ bool operator!=(const iterator_1& other) const { ++ return !(*this == other); ++ } ++ const t_tuple& operator*() const { ++ return value; ++ } ++ const t_tuple* operator->() const { ++ return &value; ++ } ++ iterator_1& operator++() { ++ ++nested; ++ value = reorder(*nested); ++ return *this; ++ } ++ }; ++ using iterator = iterator_0; ++ struct context { ++ t_ind::operation_hints hints; ++ }; ++ context createContext() { ++ return context(); ++ } ++ bool insert(const t_tuple& t) { ++ return ind.insert(t[0], t[1]); ++ } ++ bool insert(const t_tuple& t, context& h) { ++ return ind.insert(t[0], t[1], h.hints); ++ } ++ bool insert(const RamDomain* ramDomain) { ++ RamDomain data[2]; ++ std::copy(ramDomain, ramDomain + 2, data); ++ auto& tuple = reinterpret_cast(data); ++ context h; ++ return insert(tuple, h); ++ } ++ bool insert(RamDomain a1, RamDomain a2) { ++ RamDomain data[2] = {a1, a2}; ++ return insert(data); ++ } ++ void extendAndInsert(t_eqrel& other) { ++ ind.extendAndInsert(other.ind); ++ } ++ bool contains(const t_tuple& t) const { ++ return ind.contains(t[0], t[1]); ++ } ++ bool contains(const t_tuple& t, context&) const { ++ return ind.contains(t[0], t[1]); ++ } ++ std::size_t size() const { ++ return ind.size(); ++ } ++ iterator find(const t_tuple& t) const { ++ return ind.find(t); ++ } ++ iterator find(const t_tuple& t, context&) const { ++ return ind.find(t); ++ } ++ range lowerUpperRange_10(const t_tuple& lower, const t_tuple& /*upper*/, context& h) const { ++ auto r = ind.template getBoundaries<1>((lower), h.hints); ++ return make_range(iterator(r.begin()), iterator(r.end())); ++ } ++ range lowerUpperRange_10(const t_tuple& lower, const t_tuple& upper) const { ++ context h; ++ return lowerUpperRange_10(lower, upper, h); ++ } ++ range lowerUpperRange_01(const t_tuple& lower, const t_tuple& /*upper*/, context& h) const { ++ auto r = ind.template getBoundaries<1>(reorder(lower), h.hints); ++ return make_range(iterator_1(r.begin()), iterator_1(r.end())); ++ } ++ range lowerUpperRange_01(const t_tuple& lower, const t_tuple& upper) const { ++ context h; ++ return lowerUpperRange_01(lower, upper, h); ++ } ++ range lowerUpperRange_11(const t_tuple& lower, const t_tuple& /*upper*/, context& h) const { ++ auto r = ind.template getBoundaries<2>((lower), h.hints); ++ return make_range(iterator(r.begin()), iterator(r.end())); ++ } ++ range lowerUpperRange_11(const t_tuple& lower, const t_tuple& upper) const { ++ context h; ++ return lowerUpperRange_11(lower, upper, h); ++ } ++ bool empty() const { ++ return ind.size() == 0; ++ } ++ std::vector> partition() const { ++ std::vector> res; ++ for (const auto& cur : ind.partition(10000)) { ++ res.push_back(make_range(iterator(cur.begin()), iterator(cur.end()))); ++ } ++ return res; ++ } ++ void purge() { ++ ind.clear(); ++ } ++ iterator begin() const { ++ return iterator(ind.begin()); ++ } ++ iterator end() const { ++ return iterator(ind.end()); ++ } ++ static t_tuple reorder(const t_tuple& t) { ++ t_tuple res; ++ res[0] = t[1]; ++ res[1] = t[0]; ++ return res; ++ } ++ void printStatistics(std::ostream& /* o */) const {} ++}; ++ + } // namespace souffle +diff --git a/src/include/souffle/RamTypes.h b/src/include/souffle/RamTypes.h +index d46063c..76c5145 100644 +--- a/src/include/souffle/RamTypes.h ++++ b/src/include/souffle/RamTypes.h +@@ -95,7 +95,4 @@ constexpr RamUnsigned MAX_RAM_UNSIGNED = std::numeric_limits::max() + + constexpr RamFloat MIN_RAM_FLOAT = std::numeric_limits::lowest(); + constexpr RamFloat MAX_RAM_FLOAT = std::numeric_limits::max(); +- +-constexpr RamDomain RAM_BIT_SHIFT_MASK = RAM_DOMAIN_SIZE - 1; +- + } // end of namespace souffle +diff --git a/src/include/souffle/RecordTable.h b/src/include/souffle/RecordTable.h +index 2a6c1dd..5c173cb 100644 +--- a/src/include/souffle/RecordTable.h ++++ b/src/include/souffle/RecordTable.h +@@ -19,8 +19,6 @@ + + #include "souffle/RamTypes.h" + #include "souffle/utility/span.h" +- +-#include + #include + + namespace souffle { +@@ -37,10 +35,6 @@ public: + virtual RamDomain pack(const std::initializer_list& List) = 0; + + virtual const RamDomain* unpack(const RamDomain Ref, const std::size_t Arity) const = 0; +- +- /// Enumerate each record. +- virtual void enumerate(const std::function& Callback) const = 0; + }; + + /** @brief helper to convert tuple to record reference for the synthesiser */ +diff --git a/src/include/souffle/SignalHandler.h b/src/include/souffle/SignalHandler.h +index d42897f..3e9beec 100644 +--- a/src/include/souffle/SignalHandler.h ++++ b/src/include/souffle/SignalHandler.h +@@ -16,8 +16,6 @@ + + #pragma once + +-#include "souffle/profile/ProfileEvent.h" +- + #include + #include + #include +@@ -54,15 +52,6 @@ public: + void enableLogging() { + logMessages = true; + } +- +- void enableProfiling() { +- profileEnabled = true; +- } +- +- bool profilingEnabled() const { +- return profileEnabled; +- } +- + // set signal message + void setMsg(const char* m) { + if (logMessages && m != nullptr) { +@@ -115,9 +104,7 @@ public: + } + + /*** +- * Reset signal handlers. +- * +- * Disable profiling. ++ * reset signal handlers + */ + void reset() { + if (isSet) { +@@ -138,7 +125,6 @@ public: + } + isSet = false; + } +- profileEnabled = false; + } + + /*** +@@ -164,8 +150,6 @@ private: + + bool logMessages = false; + +- bool profileEnabled = false; +- + // previous signal handler routines + void (*prevFpeHandler)(int) = nullptr; + void (*prevIntHandler)(int) = nullptr; +@@ -212,12 +196,6 @@ private: + else + write({error, " signal.\n"}); + +- if (instance()->profilingEnabled()) { +- write({error, "dumping profiling data...\n"}); +- ProfileEventSingleton::instance().stopTimer(); +- ProfileEventSingleton::instance().dump(); +- } +- + std::_Exit(EXIT_FAILURE); + } + +diff --git a/src/include/souffle/SouffleInterface.h b/src/include/souffle/SouffleInterface.h +index be9a3f5..2087b38 100644 +--- a/src/include/souffle/SouffleInterface.h ++++ b/src/include/souffle/SouffleInterface.h +@@ -19,7 +19,6 @@ + #include "souffle/RamTypes.h" + #include "souffle/RecordTable.h" + #include "souffle/SymbolTable.h" +-#include "souffle/datastructure/ConcurrentCache.h" + #include "souffle/utility/MiscUtil.h" + #include + #include +@@ -30,7 +29,6 @@ + #include + #include + #include +-#include + #include + #include + #include +diff --git a/src/include/souffle/datastructure/BTree.h b/src/include/souffle/datastructure/BTree.h +index 5adc0b5..47d2bc8 100644 +--- a/src/include/souffle/datastructure/BTree.h ++++ b/src/include/souffle/datastructure/BTree.h +@@ -91,8 +91,8 @@ protected: + /* -------------- updater utilities ------------- */ + + mutable Updater upd; +- bool update(Key& old_k, const Key& new_k) { +- return upd.update(old_k, new_k); ++ void update(Key& old_k, const Key& new_k) { ++ upd.update(old_k, new_k); + } + + /* -------------- the node type ----------------- */ +@@ -1225,14 +1225,14 @@ public: + } + + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *pos)) { + if (!cur->lock.try_upgrade_to_write(cur_lease)) { + // start again + return insert(k, hints); + } +- bool updated = update(*pos, k); ++ update(*pos, k); + cur->lock.end_write(); +- return updated; ++ return true; + } + + // we found the element => no check of lock necessary +@@ -1280,14 +1280,14 @@ public: + } + + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *(pos - 1))) { + if (!cur->lock.try_upgrade_to_write(cur_lease)) { + // start again + return insert(k, hints); + } +- bool updated = update(*(pos - 1), k); ++ update(*(pos - 1), k); + cur->lock.end_write(); +- return updated; ++ return true; + } + + // we found the element => done +@@ -1432,8 +1432,9 @@ public: + // early exit for sets + if (isSet && pos != b && weak_equal(*pos, k)) { + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { +- return update(*pos, k); ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *pos)) { ++ update(*pos, k); ++ return true; + } + + return false; +@@ -1457,8 +1458,9 @@ public: + // early exit for sets + if (isSet && pos != a && weak_equal(*(pos - 1), k)) { + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { +- return update(*(pos - 1), k); ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *(pos - 1))) { ++ update(*(pos - 1), k); ++ return true; + } + + return false; +@@ -1857,11 +1859,9 @@ public: + out << " Size of inner node: " << sizeof(inner_node) << "\n"; + out << " Size of leaf node: " << sizeof(leaf_node) << "\n"; + out << " Size of Key: " << sizeof(Key) << "\n"; +- out << " max keys / node: " << node::maxKeys << "\n"; +- out << " avg keys / node: " << (nodes == 0 ? 0 : ((double)size() / (double)nodes)) << "\n"; +- out << " avg filling rate: " +- << (nodes == 0 ? 0 : (((double)size() / (double)nodes) / node::maxKeys)) << "\n"; +- out << " Memory usage: " << (getMemoryUsage() / 1'000'000) << "MB\n"; ++ out << " max keys / node: " << node::maxKeys << "\n"; ++ out << " avg keys / node: " << (size() / (double)nodes) << "\n"; ++ out << " avg filling rate: " << ((size() / (double)nodes) / node::maxKeys) << "\n"; + out << " ---------------------------------\n"; + out << " insert-hint (hits/misses/total): " << hint_stats.inserts.getHits() << "/" + << hint_stats.inserts.getMisses() << "/" << hint_stats.inserts.getAccesses() << "\n"; +diff --git a/src/include/souffle/datastructure/BTreeDelete.h b/src/include/souffle/datastructure/BTreeDelete.h +index 13bdfad..be970ad 100644 +--- a/src/include/souffle/datastructure/BTreeDelete.h ++++ b/src/include/souffle/datastructure/BTreeDelete.h +@@ -92,8 +92,8 @@ protected: + /* -------------- updater utilities ------------- */ + + mutable Updater upd; +- bool update(Key& old_k, const Key& new_k) { +- return upd.update(old_k, new_k); ++ void update(Key& old_k, const Key& new_k) { ++ upd.update(old_k, new_k); + } + + /* -------------- the node type ----------------- */ +@@ -1278,14 +1278,14 @@ public: + } + + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *pos)) { + if (!cur->lock.try_upgrade_to_write(cur_lease)) { + // start again + return insert(k, hints); + } +- bool updated = update(*pos, k); ++ update(*pos, k); + cur->lock.end_write(); +- return updated; ++ return true; + } + + // we found the element => no check of lock necessary +@@ -1333,14 +1333,14 @@ public: + } + + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *(pos - 1))) { + if (!cur->lock.try_upgrade_to_write(cur_lease)) { + // start again + return insert(k, hints); + } +- bool updated = update(*(pos - 1), k); ++ update(*(pos - 1), k); + cur->lock.end_write(); +- return updated; ++ return true; + } + + // we found the element => done +@@ -1485,8 +1485,9 @@ public: + // early exit for sets + if (isSet && pos != b && weak_equal(*pos, k)) { + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { +- return update(*pos, k); ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *pos)) { ++ update(*pos, k); ++ return true; + } + + return false; +@@ -1510,8 +1511,9 @@ public: + // early exit for sets + if (isSet && pos != a && weak_equal(*(pos - 1), k)) { + // update provenance information +- if (typeid(Comparator) != typeid(WeakComparator)) { +- return update(*(pos - 1), k); ++ if (typeid(Comparator) != typeid(WeakComparator) && less(k, *(pos - 1))) { ++ update(*(pos - 1), k); ++ return true; + } + + return false; +@@ -1658,16 +1660,12 @@ public: + leftmost = nullptr; + res.cur = nullptr; + res.pos = 0; +- delete static_cast(iter.cur); + } else { + // Whole tree now contained in child at position 0 + root = iter.cur->getChild(0); + root->parent = nullptr; +- for (unsigned i = 0; i <= iter.cur->asInnerNode().numElements; ++i) { +- iter.cur->asInnerNode().children[i] = nullptr; +- } +- delete static_cast(iter.cur); + } ++ delete iter.cur; + } + break; + } +@@ -1915,14 +1913,7 @@ private: + left->numElements += right->getNumElements() + 1; + + // Delete the right node +- if (right->isLeaf()) { +- delete static_cast(right); +- } else { +- for (unsigned i = 0; i <= right->asInnerNode().numElements; ++i) { +- right->asInnerNode().children[i] = nullptr; +- } +- delete static_cast(right); +- } ++ delete right; + } + + /** +@@ -2418,11 +2409,9 @@ public: + out << " Size of inner node: " << sizeof(inner_node) << "\n"; + out << " Size of leaf node: " << sizeof(leaf_node) << "\n"; + out << " Size of Key: " << sizeof(Key) << "\n"; +- out << " max keys / node: " << node::maxKeys << "\n"; +- out << " avg keys / node: " << (nodes == 0 ? 0 : ((double)size() / (double)nodes)) << "\n"; +- out << " avg filling rate: " +- << (nodes == 0 ? 0 : (((double)size() / (double)nodes) / node::maxKeys)) << "\n"; +- out << " Memory usage: " << (getMemoryUsage() / 1'000'000) << "MB\n"; ++ out << " max keys / node: " << node::maxKeys << "\n"; ++ out << " avg keys / node: " << (size() / (double)nodes) << "\n"; ++ out << " avg filling rate: " << ((size() / (double)nodes) / node::maxKeys) << "\n"; + out << " ---------------------------------\n"; + out << " insert-hint (hits/misses/total): " << hint_stats.inserts.getHits() << "/" + << hint_stats.inserts.getMisses() << "/" << hint_stats.inserts.getAccesses() << "\n"; +diff --git a/src/include/souffle/datastructure/BTreeUtil.h b/src/include/souffle/datastructure/BTreeUtil.h +index 1640060..421149c 100644 +--- a/src/include/souffle/datastructure/BTreeUtil.h ++++ b/src/include/souffle/datastructure/BTreeUtil.h +@@ -16,8 +16,6 @@ + + #pragma once + +-#include +- + namespace souffle { + + namespace detail { +@@ -217,9 +215,7 @@ struct default_strategy> : public linear {}; + */ + template + struct updater { +- bool update(T& /* old_t */, const T& /* new_t */) { +- return false; +- } ++ void update(T& /* old_t */, const T& /* new_t */) {} + }; + + } // end of namespace detail +diff --git a/src/include/souffle/datastructure/ConcurrentFlyweight.h b/src/include/souffle/datastructure/ConcurrentFlyweight.h +index afba416..ed41d11 100644 +--- a/src/include/souffle/datastructure/ConcurrentFlyweight.h ++++ b/src/include/souffle/datastructure/ConcurrentFlyweight.h +@@ -190,15 +190,10 @@ public: + static_assert(NONE == std::numeric_limits::max(), + "required for wrap around to 0 for begin-iterator-scan"); + static_assert(NONE + 1 == 0, "required for wrap around to 0 for begin-iterator-scan"); +- + while (Slot != END) { + assert(Slot + 1 < SLOT_MAX); +- + if (Slot + 1 < NextMaybeUnassignedSlot) { // next unassigned slot not reached + Slot = Slot + 1; +- if (Slot == 0 && This->FirstSlotIsReserved) { +- continue; +- } + return true; + } + +@@ -231,11 +226,10 @@ public: + const bool ReserveFirst, const Hash& hash = Hash(), const KeyEqual& key_equal = KeyEqual(), + const KeyFactory& key_factory = KeyFactory()) + : Lanes(LaneCount), HandleCount(LaneCount), +- Mapping(LaneCount, InitialCapacity, hash, key_equal, key_factory), +- FirstSlotIsReserved(ReserveFirst) { ++ Mapping(LaneCount, InitialCapacity, hash, key_equal, key_factory) { + Slots = std::make_unique(InitialCapacity); + Handles = std::make_unique(HandleCount); +- NextSlot = (FirstSlotIsReserved ? 1 : 0); ++ NextSlot = (ReserveFirst ? 1 : 0); + SlotCount = InitialCapacity; + } + +@@ -400,9 +394,6 @@ private: + // Number of slots. + std::atomic SlotCount; + +- /// If true, the first slot (index 0) is not a valid entry. +- const bool FirstSlotIsReserved; +- + /// Grow the datastructure if needed. + bool tryGrow(const lane_id H) { + // This call may release and re-acquire the lane to +diff --git a/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h b/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h +index ed51ef6..d9f0746 100644 +--- a/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h ++++ b/src/include/souffle/datastructure/ConcurrentInsertOnlyHashMap.h +@@ -38,7 +38,7 @@ static constexpr uint64_t LargestPrime64 = 18446744073709551557UL; + + // Return a prime greater or equal to the lower bound. + // Return 0 if the next prime would not fit in 64 bits. +-inline static uint64_t GreaterOrEqualPrime(const uint64_t LowerBound) { ++static uint64_t GreaterOrEqualPrime(const uint64_t LowerBound) { + if (LowerBound > LargestPrime64) { + return 0; + } +@@ -177,15 +177,15 @@ public: + return static_cast(BL); + } + +- /** +- * @brief Lookup a value associated with a key. ++ /** @brief Checks if the map contains an element with the given key. + * + * The search is done concurrently with possible insertion of the +- * searched key. If the a nullpointer is returned, then the key +- * was not associated with a value when the search began. ++ * searched key. If return true, then there is definitely an element ++ * with the specified key, if return false then there was no such ++ * element when the search began. + */ + template +- const value_type* weakFind(const lane_id H, const K& X) const { ++ bool weakContains(const lane_id H, const K& X) const { + const size_t HashValue = Hasher(X); + const auto Guard = Lanes.guard(H); + const size_t Bucket = HashValue % BucketCount; +@@ -194,23 +194,11 @@ public: + while (L != nullptr) { + if (EqualTo(L->Value.first, X)) { + // found the key +- return &L->Value; ++ return true; + } + L = L->Next; + } +- return nullptr; +- } +- +- /** @brief Checks if the map contains an element with the given key. +- * +- * The search is done concurrently with possible insertion of the +- * searched key. If return true, then there is definitely an element +- * with the specified key, if return false then there was no such +- * element when the search began. +- */ +- template +- inline bool weakContains(const lane_id H, const K& X) const { +- return weakFind(H, X) != nullptr; ++ return false; + } + + /** +diff --git a/src/include/souffle/datastructure/EquivalenceRelation.h b/src/include/souffle/datastructure/EquivalenceRelation.h +index 07651c1..1eddfe5 100644 +--- a/src/include/souffle/datastructure/EquivalenceRelation.h ++++ b/src/include/souffle/datastructure/EquivalenceRelation.h +@@ -54,7 +54,7 @@ class EquivalenceRelation { + public: + using element_type = TupleType; + +- EquivalenceRelation() : statesMapStale(false) {} ++ EquivalenceRelation() : statesMapStale(false){}; + ~EquivalenceRelation() { + emptyPartition(); + } +@@ -79,7 +79,7 @@ public: + bool insert(value_type x, value_type y) { + operation_hints z; + return insert(x, y, z); +- } ++ }; + + /** + * Insert the tuple symbolically. +@@ -89,7 +89,7 @@ public: + bool insert(const TupleType& tuple) { + operation_hints hints; + return insert(tuple[0], tuple[1], hints); +- } ++ }; + + /** + * Insert the two values symbolically as a binary relation +@@ -208,11 +208,11 @@ public: + */ + bool contains(const TupleType& tuple, operation_hints&) const { + return contains(tuple[0], tuple[1]); +- } ++ }; + + bool contains(const TupleType& tuple) const { + return contains(tuple[0], tuple[1]); +- } ++ }; + + void emptyPartition() const { + // delete the beautiful values inside (they're raw ptrs, so they need to be.) +@@ -270,7 +270,7 @@ public: + + // one iterator for signalling the end (simplifies) + explicit iterator(const EquivalenceRelation* br, bool /* signalIsEndIterator */) +- : br(br), isEndVal(true) {} ++ : br(br), isEndVal(true){}; + + explicit iterator(const EquivalenceRelation* br) + : br(br), ityp(IterType::ALL), djSetMapListIt(br->equivalencePartition.begin()), +@@ -695,7 +695,7 @@ public: + for (const auto& itp : equivalencePartition) { + const std::size_t s = itp.second->size(); + if (s * s > perchunk) { +- for (const auto& i : *itp.second) { ++ for (auto i : *itp.second) { + ret.push_back(souffle::make_range(anteriorIt(i), end())); + } + } else { +@@ -716,8 +716,6 @@ public: + return find(t, context); + } + +- void printStats(std::ostream& /* o */) const {} +- + protected: + bool containsElement(value_type e) const { + return this->sds.nodeExists(e); +diff --git a/src/include/souffle/datastructure/PiggyList.h b/src/include/souffle/datastructure/PiggyList.h +index 88c4e5c..91efd5b 100644 +--- a/src/include/souffle/datastructure/PiggyList.h ++++ b/src/include/souffle/datastructure/PiggyList.h +@@ -16,11 +16,7 @@ + */ + #if defined(_MSC_VER) + int __inline __builtin_clzll(unsigned long long value) { +-#if _WIN64 + return static_cast(__lzcnt64(value)); +-#else +- return static_cast(__lzcnt(value)); +-#endif + } + #endif // _MSC_VER + #endif // _WIN32 +diff --git a/src/include/souffle/datastructure/RecordTableImpl.h b/src/include/souffle/datastructure/RecordTableImpl.h +index c96fda9..2e7b965 100644 +--- a/src/include/souffle/datastructure/RecordTableImpl.h ++++ b/src/include/souffle/datastructure/RecordTableImpl.h +@@ -344,8 +344,6 @@ public: + virtual RamDomain pack(const RamDomain* Tuple) = 0; + virtual RamDomain pack(const std::initializer_list& List) = 0; + virtual const RamDomain* unpack(RamDomain index) const = 0; +- virtual void enumerate(const std::function& Callback) const = 0; + }; + + /** @brief Bidirectional mappping between records and record references, for any record arity. */ +@@ -390,16 +388,6 @@ public: + const RamDomain* unpack(RamDomain Index) const override { + return fetch(Index).data(); + } +- +- void enumerate(const std::function& Callback) const override { +- const auto End = end(); +- for (auto It = begin(); It != End; ++It) { +- RamDomain key = It->second; +- const std::vector& tuple = It->first; +- Callback(tuple.data(), Arity, key); +- } +- } + }; + + /** @brief Bidirectional mappping between records and record references, specialized for a record arity. */ +@@ -449,16 +437,6 @@ public: + const RamDomain* unpack(RamDomain Index) const override { + return Base::fetch(Index).data(); + } +- +- void enumerate(const std::function& Callback) const override { +- const auto End = Base::end(); +- for (auto It = Base::begin(); It != End; ++It) { +- RamDomain key = It->second; +- const auto& tuple = It->first; +- Callback(tuple.data(), Arity, key); +- } +- } + }; + + /** Record map specialized for arity 0 */ +@@ -501,9 +479,6 @@ public: + assert(Index == EmptyRecordIndex); + return EmptyRecordData; + } +- +- void enumerate(const std::function&) const override {} + }; + + /** A concurrent Record Table with some specialized record maps. */ +@@ -577,17 +552,6 @@ public: + return lookupMap(Arity).unpack(Ref); + } + +- void enumerate(const std::function& Callback) const override { +- auto Guard = Lanes.guard(); +- for (std::size_t Arity = 0; Arity < Maps.size(); ++Arity) { +- const RecordMap* Map = Maps.at(Arity); +- if (Map != nullptr) { +- Map->enumerate(Callback); +- } +- } +- } +- + private: + /** @brief lookup RecordMap for a given arity; the map for that arity must exist. */ + RecordMap& lookupMap(const std::size_t Arity) const { +diff --git a/src/include/souffle/io/IOSystem.h b/src/include/souffle/io/IOSystem.h +index d3d2a86..79649aa 100644 +--- a/src/include/souffle/io/IOSystem.h ++++ b/src/include/souffle/io/IOSystem.h +@@ -88,6 +88,7 @@ private: + registerWriteStreamFactory(std::make_shared()); + #ifdef USE_SQLITE + registerReadStreamFactory(std::make_shared()); ++ registerReadStreamFactory(std::make_shared()); + registerWriteStreamFactory(std::make_shared()); + #endif + }; +diff --git a/src/include/souffle/io/ReadStream.h b/src/include/souffle/io/ReadStream.h +index 576a5c0..4ec4261 100644 +--- a/src/include/souffle/io/ReadStream.h ++++ b/src/include/souffle/io/ReadStream.h +@@ -149,7 +149,7 @@ protected: + + // Consume initial character + consumeChar(source, '$', pos); +- std::string constructor = readQualifiedName(source, pos); ++ std::string constructor = readIdentifier(source, pos); + + json11::Json branchInfo = [&]() -> json11::Json { + for (auto branch : branches.array_items()) { +@@ -250,7 +250,7 @@ protected: + * Consume preceding whitespace. + * TODO (darth_tytus): use std::string_view? + */ +- std::string readQualifiedName(const std::string& source, std::size_t& pos) { ++ std::string readIdentifier(const std::string& source, std::size_t& pos) { + consumeWhiteSpace(source, pos); + if (pos >= source.length()) { + throw std::invalid_argument("Unexpected end of input"); +@@ -259,7 +259,7 @@ protected: + const std::size_t bgn = pos; + while (pos < source.length()) { + unsigned char ch = static_cast(source[pos]); +- bool valid = std::isalnum(ch) || ch == '_' || ch == '?' || ch == '.'; ++ bool valid = std::isalnum(ch) || ch == '_' || ch == '?'; + if (!valid) break; + ++pos; + } +diff --git a/src/include/souffle/io/ReadStreamCSV.h b/src/include/souffle/io/ReadStreamCSV.h +index a90d63e..5d96b75 100644 +--- a/src/include/souffle/io/ReadStreamCSV.h ++++ b/src/include/souffle/io/ReadStreamCSV.h +@@ -63,19 +63,6 @@ public: + } + + protected: +- bool readNextLine(std::string& line, bool& isCRLF) { +- if (!getline(file, line)) { +- return false; +- } +- // Handle Windows line endings on non-Windows systems +- isCRLF = !line.empty() && line.back() == '\r'; +- if (isCRLF) { +- line.pop_back(); +- } +- ++lineNumber; +- return true; +- } +- + /** + * Read and return the next tuple. + * +@@ -88,16 +75,21 @@ protected: + } + std::string line; + Own tuple = mk(typeAttributes.size()); +- bool wasCRLF = false; +- if (!readNextLine(line, wasCRLF)) { ++ ++ if (!getline(file, line)) { + return nullptr; + } ++ // Handle Windows line endings on non-Windows systems ++ if (!line.empty() && line.back() == '\r') { ++ line = line.substr(0, line.length() - 1); ++ } ++ ++lineNumber; + + std::size_t start = 0; + std::size_t columnsFilled = 0; + for (uint32_t column = 0; columnsFilled < arity; column++) { + std::size_t charactersRead = 0; +- std::string element = nextElement(line, start, wasCRLF); ++ std::string element = nextElement(line, start); + if (inputMap.count(column) == 0) { + continue; + } +@@ -170,43 +162,16 @@ protected: + return value; + } + +- std::string nextElement(std::string& line, std::size_t& start, bool& wasCRLF) { ++ std::string nextElement(const std::string& line, std::size_t& start) { + std::string element; + + if (rfc4180) { + if (line[start] == '"') { + // quoted field +- std::size_t end = line.length(); ++ const std::size_t end = line.length(); + std::size_t pos = start + 1; + bool foundEndQuote = false; +- while (!foundEndQuote) { +- if (pos == end) { +- bool newWasCRLF = false; +- if (!readNextLine(line, newWasCRLF)) { +- break; +- } +- // account for \r\n or \n that we had previously +- // read and thrown out. +- // since we're in a quote, we should restore +- // what the user provided +- if (wasCRLF) { +- element.push_back('\r'); +- } +- element.push_back('\n'); +- +- // remember if we just read a CRLF sequence +- wasCRLF = newWasCRLF; +- +- // start over +- pos = 0; +- end = line.length(); +- } +- if (pos == end) { +- // this means we've got a blank line and we need to read +- // more +- continue; +- } +- ++ while (pos < end) { + char c = line[pos++]; + if (c == '"' && (pos < end) && line[pos] == '"') { + // two double-quote => one double-quote +@@ -214,6 +179,7 @@ protected: + ++pos; + } else if (c == '"') { + foundEndQuote = true; ++ break; + } else { + element.push_back(c); + } +diff --git a/src/include/souffle/io/ReadStreamSQLite.h b/src/include/souffle/io/ReadStreamSQLite.h +index 9cac3e3..875ab68 100644 +--- a/src/include/souffle/io/ReadStreamSQLite.h ++++ b/src/include/souffle/io/ReadStreamSQLite.h +@@ -35,9 +35,10 @@ namespace souffle { + class ReadStreamSQLite : public ReadStream { + public: + ReadStreamSQLite(const std::map& rwOperation, SymbolTable& symbolTable, +- RecordTable& recordTable) ++ RecordTable& recordTable, bool needEmptyStringTransform) + : ReadStream(rwOperation, symbolTable, recordTable), dbFilename(getFileName(rwOperation)), +- relationName(rwOperation.at("name")) { ++ relationName(rwOperation.at("name")), ++ flagNeedEmptyStringTransform(needEmptyStringTransform) { + openDB(); + checkTableExists(); + prepareSelectStatement(); +@@ -66,12 +67,12 @@ protected: + for (column = 0; column < arity; column++) { + std::string element; + if (0 == sqlite3_column_bytes(selectStatement, column)) { +- element = ""; ++ element = flagNeedEmptyStringTransform? "n/a":""; + } else { + element = reinterpret_cast(sqlite3_column_text(selectStatement, column)); + + if (element.empty()) { +- element = ""; ++ element = flagNeedEmptyStringTransform? "n/a":""; + } + } + +@@ -186,13 +187,14 @@ protected: + const std::string relationName; + sqlite3_stmt* selectStatement = nullptr; + sqlite3* db = nullptr; ++ bool flagNeedEmptyStringTransform = true; + }; + + class ReadSQLiteFactory : public ReadStreamFactory { + public: + Own getReader(const std::map& rwOperation, SymbolTable& symbolTable, + RecordTable& recordTable) override { +- return mk(rwOperation, symbolTable, recordTable); ++ return mk(rwOperation, symbolTable, recordTable, true); + } + + const std::string& getName() const override { +@@ -202,4 +204,18 @@ public: + ~ReadSQLiteFactory() override = default; + }; + ++class ReadSQLiteCacheFactory : public ReadStreamFactory { ++public: ++ Own getReader(const std::map& rwOperation, SymbolTable& symbolTable, ++ RecordTable& recordTable) override { ++ return mk(rwOperation, symbolTable, recordTable, false); ++ } ++ ++ const std::string& getName() const override { ++ static const std::string name = "sqlite-cache"; ++ return name; ++ } ++ ~ReadSQLiteCacheFactory() override = default; ++}; ++ + } /* namespace souffle */ +diff --git a/src/include/souffle/io/SerialisationStream.h b/src/include/souffle/io/SerialisationStream.h +index d13d06c..6a609ea 100644 +--- a/src/include/souffle/io/SerialisationStream.h ++++ b/src/include/souffle/io/SerialisationStream.h +@@ -17,7 +17,7 @@ + #pragma once + + #include "souffle/RamTypes.h" +-#include "souffle/utility/ContainerUtil.h" ++ + #include "souffle/utility/StringUtil.h" + #include "souffle/utility/json11.h" + #include +diff --git a/src/include/souffle/io/WriteStream.h b/src/include/souffle/io/WriteStream.h +index 555ce8a..06449cd 100644 +--- a/src/include/souffle/io/WriteStream.h ++++ b/src/include/souffle/io/WriteStream.h +@@ -50,7 +50,7 @@ public: + } + return; + } +- for (const auto& current : relation) { ++ for (auto current : relation) { + writeNext(current); + } + } +diff --git a/src/include/souffle/io/WriteStreamSQLite.h b/src/include/souffle/io/WriteStreamSQLite.h +index 240b6da..09f9218 100644 +--- a/src/include/souffle/io/WriteStreamSQLite.h ++++ b/src/include/souffle/io/WriteStreamSQLite.h +@@ -58,20 +58,30 @@ protected: + void writeNextTuple(const RamDomain* tuple) override { + for (std::size_t i = 0; i < arity; i++) { + RamDomain value = 0; // Silence warning ++ const char* symvalue = nullptr; // Directly insert string into db + + switch (typeAttributes.at(i)[0]) { +- case 's': value = getSymbolTableID(tuple[i]); break; ++ case 's': ++ value = getSymbolTableID(tuple[i]); ++ symvalue = symbolTable.decode(tuple[i]).c_str(); ++ break; + default: value = tuple[i]; break; + } +- ++ if (symvalue) { ++ if (sqlite3_bind_text(insertStatement, static_cast(i + 1), symvalue, -1, SQLITE_TRANSIENT) != ++ SQLITE_OK) { ++ throwError("SQLite error in sqlite3_bind_text: "); ++ } ++ } else { + #if RAM_DOMAIN_SIZE == 64 +- if (sqlite3_bind_int64(insertStatement, static_cast(i + 1), +- static_cast(value)) != SQLITE_OK) { ++ if (sqlite3_bind_int64(insertStatement, static_cast(i + 1), ++ static_cast(value)) != SQLITE_OK) { + #else +- if (sqlite3_bind_int(insertStatement, static_cast(i + 1), static_cast(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: "); ++ throwError("SQLite error in sqlite3_bind_text: "); ++ } + } + } + if (sqlite3_step(insertStatement) != SQLITE_DONE) { +@@ -192,18 +202,18 @@ private: + + void createTables() { + createRelationTable(); +- createRelationView(); + createSymbolTable(); ++ createRelationView(); + } + + void createRelationTable() { + std::stringstream createTableText; + createTableText << "CREATE TABLE IF NOT EXISTS '_" << relationName << "' ("; + if (arity > 0) { +- createTableText << "'0' INTEGER"; ++ createTableText << "'0' " << (typeAttributes.at(0)[0] == 's'? "TEXT":"INTEGER"); + for (unsigned int i = 1; i < arity; i++) { + createTableText << ",'" << std::to_string(i) << "' "; +- createTableText << "INTEGER"; ++ createTableText << (typeAttributes.at(i)[0] == 's'? "TEXT":"INTEGER"); + } + } + createTableText << ");"; +diff --git a/src/include/souffle/profile/Cell.h b/src/include/souffle/profile/Cell.h +index 0ebd83e..a759495 100644 +--- a/src/include/souffle/profile/Cell.h ++++ b/src/include/souffle/profile/Cell.h +@@ -37,7 +37,7 @@ public: + double getDoubleVal() const override { + return value.count() / 1000000.0; + } +- int64_t getLongVal() const override { ++ long getLongVal() const override { + std::cerr << "getting long on time cell\n"; + throw this; + } +@@ -62,7 +62,7 @@ public: + double getDoubleVal() const override { + return value; + } +- int64_t getLongVal() const override { ++ long getLongVal() const override { + std::cerr << "getting long on double cell\n"; + throw this; + } +@@ -89,7 +89,7 @@ public: + std::cerr << "getting double on string cell\n"; + throw this; + } +- int64_t getLongVal() const override { ++ long getLongVal() const override { + std::cerr << "getting long on string cell\n"; + throw this; + } +@@ -106,11 +106,11 @@ public: + }; + + template <> +-class Cell : public CellInterface { +- const int64_t value; ++class Cell : public CellInterface { ++ const long value; + + public: +- Cell(int64_t value) : value(value){}; ++ Cell(long value) : value(value){}; + double getDoubleVal() const override { + std::cerr << "getting double on long cell\n"; + throw this; +@@ -119,7 +119,7 @@ public: + std::cerr << "getting string on long cell\n"; + throw this; + } +- int64_t getLongVal() const override { ++ long getLongVal() const override { + return value; + } + std::chrono::microseconds getTimeVal() const override { +@@ -139,7 +139,7 @@ public: + std::cerr << "getting double on void cell"; + throw this; + } +- int64_t getLongVal() const override { ++ long getLongVal() const override { + std::cerr << "getting long on void cell"; + throw this; + } +diff --git a/src/include/souffle/profile/CellInterface.h b/src/include/souffle/profile/CellInterface.h +index 8194b77..38cc8eb 100644 +--- a/src/include/souffle/profile/CellInterface.h ++++ b/src/include/souffle/profile/CellInterface.h +@@ -20,7 +20,7 @@ public: + + virtual double getDoubleVal() const = 0; + +- virtual int64_t getLongVal() const = 0; ++ virtual long getLongVal() const = 0; + + virtual std::string getStringVal() const = 0; + +diff --git a/src/include/souffle/profile/Cli.h b/src/include/souffle/profile/Cli.h +index 0558324..45a5c1e 100644 +--- a/src/include/souffle/profile/Cli.h ++++ b/src/include/souffle/profile/Cli.h +@@ -15,12 +15,7 @@ + #include + #include + #include +- +-#ifdef USE_CUSTOM_GETOPTLONG +-#include "souffle/utility/GetOptLongImpl.h" +-#else + #include +-#endif + + namespace souffle { + namespace profile { +@@ -57,10 +52,10 @@ public: + } + } + +- int parse() { ++ void parse() { + if (args.size() == 0) { + std::cout << "No arguments provided.\nTry souffleprof -h for help.\n"; +- return (EXIT_FAILURE); ++ exit(EXIT_FAILURE); + } + + if (args.count('h') != 0 || args.count('f') == 0) { +@@ -75,7 +70,7 @@ public: + << std::endl + << " Default filename is profiler_html/[num].html" << std::endl + << "-h Print this help message." << std::endl; +- return (0); ++ exit(0); + } + std::string filename = args['f']; + +@@ -86,15 +81,13 @@ public: + } + } else if (args.count('j') != 0) { + if (args['j'] == "j") { +- return Tui(filename, false, true).outputHtml(); ++ Tui(filename, false, true).outputHtml(); + } else { +- return Tui(filename, false, true).outputHtml(args['j']); ++ Tui(filename, false, true).outputHtml(args['j']); + } + } else { + Tui(filename, true, false).runProf(); + } +- +- return 0; + } + }; + +diff --git a/src/include/souffle/profile/EventProcessor.h b/src/include/souffle/profile/EventProcessor.h +index 468a6e5..72fa546 100644 +--- a/src/include/souffle/profile/EventProcessor.h ++++ b/src/include/souffle/profile/EventProcessor.h +@@ -224,45 +224,45 @@ public: + } nonRecursiveRuleNumberProcessor; + + /** +- * Non-Recursive Estimate Join Size Profile Event Processor ++ * Non-Recursive Count Unique Keys Profile Event Processor + */ +-const class NonRecursiveEstimateJoinSizeProcessor : public EventProcessor { ++const class NonRecursiveCountUniqueKeysProcessor : public EventProcessor { + public: +- NonRecursiveEstimateJoinSizeProcessor() { +- EventProcessorSingleton::instance().registerEventProcessor("@non-recursive-estimate-join-size", this); ++ NonRecursiveCountUniqueKeysProcessor() { ++ EventProcessorSingleton::instance().registerEventProcessor("@non-recursive-count-unique-keys", this); + } + /** process event input */ + void process(ProfileDatabase& db, const std::vector& signature, va_list& args) override { + const std::string& relation = signature[1]; + const std::string& attributes = signature[2]; + const std::string& constants = signature[3]; +- std::string joinSize = std::to_string(va_arg(args, double)); +- db.addTextEntry({"program", "statistics", "relation", relation, "attributes", attributes, "constants", ++ std::size_t uniqueKeys = va_arg(args, std::size_t); ++ db.addSizeEntry({"program", "statistics", "relation", relation, "attributes", attributes, "constants", + constants}, +- joinSize); ++ uniqueKeys); + } +-} nonRecursiveEstimateJoinSizeProcessor; ++} nonRecursiveCountUniqueKeysProcessor; + + /** +- * Recursive Estimate Join Size Profile Event Processor ++ * Recursive Count Unique Keys Profile Event Processor + */ +-const class RecursiveEstimateJoinSizeProcessor : public EventProcessor { ++const class RecursiveCountUniqueKeysProcessor : public EventProcessor { + public: +- RecursiveEstimateJoinSizeProcessor() { +- EventProcessorSingleton::instance().registerEventProcessor("@recursive-estimate-join-size", this); ++ RecursiveCountUniqueKeysProcessor() { ++ EventProcessorSingleton::instance().registerEventProcessor("@recursive-count-unique-keys", this); + } + /** process event input */ + void process(ProfileDatabase& db, const std::vector& signature, va_list& args) override { + const std::string& relation = signature[1]; + const std::string& attributes = signature[2]; + const std::string& constants = signature[3]; +- std::string joinSize = std::to_string(va_arg(args, double)); ++ std::size_t uniqueKeys = va_arg(args, std::size_t); + std::string iteration = std::to_string(va_arg(args, std::size_t)); +- db.addTextEntry({"program", "statistics", "relation", relation, "iteration", iteration, "attributes", ++ db.addSizeEntry({"program", "statistics", "relation", relation, "iteration", iteration, "attributes", + attributes, "constants", constants}, +- joinSize); ++ uniqueKeys); + } +-} recursiveEstimateJoinSizeProcessor; ++} recursiveCountUniqueKeysProcessor; + + /** + * Recursive Rule Timing Profile Event Processor +diff --git a/src/include/souffle/profile/OutputProcessor.h b/src/include/souffle/profile/OutputProcessor.h +index 09a1c6d..745a3d7 100644 +--- a/src/include/souffle/profile/OutputProcessor.h ++++ b/src/include/souffle/profile/OutputProcessor.h +@@ -86,20 +86,19 @@ Table inline OutputProcessor::getRelTable() const { + row[1] = std::make_shared>(r->getNonRecTime()); + row[2] = std::make_shared>(r->getRecTime()); + row[3] = std::make_shared>(r->getCopyTime()); +- row[4] = std::make_shared>(static_cast(r->size())); ++ row[4] = std::make_shared>(r->size()); + row[5] = std::make_shared>(r->getName()); + row[6] = std::make_shared>(r->getId()); + row[7] = std::make_shared>(r->getLocator()); + if (total_time.count() != 0) { +- row[8] = std::make_shared>(static_cast( +- static_cast(r->size()) / (static_cast(total_time.count()) / 1000000.0))); ++ row[8] = std::make_shared>(r->size() / (total_time.count() / 1000000.0)); + } else { +- row[8] = std::make_shared>(static_cast(r->size())); ++ row[8] = std::make_shared>(r->size()); + } + row[9] = std::make_shared>(r->getLoadtime()); + row[10] = std::make_shared>(r->getSavetime()); +- row[11] = std::make_shared>(static_cast(r->getMaxRSSDiff())); +- row[12] = std::make_shared>(static_cast(r->getReads())); ++ row[11] = std::make_shared>(r->getMaxRSSDiff()); ++ row[12] = std::make_shared>(r->getReads()); + + table.addRow(std::make_shared(row)); + } +@@ -132,11 +131,11 @@ Table inline OutputProcessor::getRulTable() const { + row[1] = std::make_shared>(rule->getRuntime()); + row[2] = std::make_shared>(std::chrono::microseconds(0)); + row[3] = std::make_shared>(std::chrono::microseconds(0)); +- row[4] = std::make_shared>(static_cast(rule->size())); ++ row[4] = std::make_shared>(rule->size()); + row[5] = std::make_shared>(rule->getName()); + row[6] = std::make_shared>(rule->getId()); + row[7] = std::make_shared>(rel.second->getName()); +- row[8] = std::make_shared>(0); ++ row[8] = std::make_shared>(0); + row[10] = std::make_shared>(rule->getLocator()); + ruleMap.emplace(rule->getName(), std::make_shared(row)); + } +@@ -147,8 +146,7 @@ Table inline OutputProcessor::getRulTable() const { + Row row = *ruleMap[rule->getName()]; + row[2] = std::make_shared>( + row[2]->getTimeVal() + rule->getRuntime()); +- row[4] = std::make_shared>( +- row[4]->getLongVal() + static_cast(rule->size())); ++ row[4] = std::make_shared>(row[4]->getLongVal() + rule->size()); + row[0] = std::make_shared>( + row[0]->getTimeVal() + rule->getRuntime()); + ruleMap[rule->getName()] = std::make_shared(row); +@@ -158,11 +156,11 @@ Table inline OutputProcessor::getRulTable() const { + row[1] = std::make_shared>(std::chrono::microseconds(0)); + row[2] = std::make_shared>(rule->getRuntime()); + row[3] = std::make_shared>(std::chrono::microseconds(0)); +- row[4] = std::make_shared>(static_cast(rule->size())); ++ row[4] = std::make_shared>(rule->size()); + row[5] = std::make_shared>(rule->getName()); + row[6] = std::make_shared>(rule->getId()); + row[7] = std::make_shared>(rel.second->getName()); +- row[8] = std::make_shared>(rule->getVersion()); ++ row[8] = std::make_shared>(rule->getVersion()); + row[10] = std::make_shared>(rule->getLocator()); + ruleMap[rule->getName()] = std::make_shared(row); + } +@@ -219,8 +217,8 @@ Table inline OutputProcessor::getAtomTable(std::string strRel, std::string strRu + Row row(4); + row[0] = std::make_shared>(atom.rule); + row[1] = std::make_shared>(atom.identifier); +- row[2] = std::make_shared>(static_cast(atom.level)); +- row[3] = std::make_shared>(static_cast(atom.frequency)); ++ row[2] = std::make_shared>(atom.level); ++ row[3] = std::make_shared>(atom.frequency); + + table.addRow(std::make_shared(row)); + } +@@ -303,8 +301,7 @@ Table inline OutputProcessor::getVersions(std::string strRel, std::string strRul + Row row = *ruleMap[strTemp]; + row[2] = std::make_shared>( + row[2]->getTimeVal() + rule->getRuntime()); +- row[4] = std::make_shared>( +- row[4]->getLongVal() + static_cast(rule->size())); ++ row[4] = std::make_shared>(row[4]->getLongVal() + rule->size()); + row[0] = std::make_shared>(rule->getRuntime()); + ruleMap[strTemp] = std::make_shared(row); + } else { +@@ -312,11 +309,11 @@ Table inline OutputProcessor::getVersions(std::string strRel, std::string strRul + row[1] = std::make_shared>(std::chrono::microseconds(0)); + row[2] = std::make_shared>(rule->getRuntime()); + row[3] = std::make_shared>(std::chrono::microseconds(0)); +- row[4] = std::make_shared>(static_cast(rule->size())); ++ row[4] = std::make_shared>(rule->size()); + row[5] = std::make_shared>(rule->getName()); + row[6] = std::make_shared>(rule->getId()); + row[7] = std::make_shared>(rel->getName()); +- row[8] = std::make_shared>(rule->getVersion()); ++ row[8] = std::make_shared>(rule->getVersion()); + row[9] = std::make_shared>(rule->getLocator()); + row[0] = std::make_shared>(rule->getRuntime()); + ruleMap[strTemp] = std::make_shared(row); +@@ -369,8 +366,8 @@ Table inline OutputProcessor::getVersionAtoms(std::string strRel, std::string sr + Row row(4); + row[0] = std::make_shared>(atom.rule); + row[1] = std::make_shared>(atom.identifier); +- row[2] = std::make_shared>(static_cast(atom.level)); +- row[3] = std::make_shared>(static_cast(atom.frequency)); ++ row[2] = std::make_shared>(atom.level); ++ row[3] = std::make_shared>(atom.frequency); + table.addRow(std::make_shared(row)); + } + } +diff --git a/src/include/souffle/profile/ProfileEvent.h b/src/include/souffle/profile/ProfileEvent.h +index 4106680..59ff34c 100644 +--- a/src/include/souffle/profile/ProfileEvent.h ++++ b/src/include/souffle/profile/ProfileEvent.h +@@ -85,12 +85,12 @@ public: + profile::EventProcessorSingleton::instance().process(database, txt.c_str(), number, iteration); + } + +- void makeNonRecursiveCountEvent(const std::string& txt, double joinSize) { +- profile::EventProcessorSingleton::instance().process(database, txt.c_str(), joinSize); ++ void makeNonRecursiveCountEvent(const std::string& txt, std::size_t uniqueKeys) { ++ profile::EventProcessorSingleton::instance().process(database, txt.c_str(), uniqueKeys); + } + +- void makeRecursiveCountEvent(const std::string& txt, double joinSize, std::size_t iteration) { +- profile::EventProcessorSingleton::instance().process(database, txt.c_str(), joinSize, iteration); ++ void makeRecursiveCountEvent(const std::string& txt, std::size_t uniqueKeys, std::size_t iteration) { ++ profile::EventProcessorSingleton::instance().process(database, txt.c_str(), uniqueKeys, iteration); + } + + /** create utilisation event */ +diff --git a/src/include/souffle/profile/ProgramRun.h b/src/include/souffle/profile/ProgramRun.h +index b4422a9..a2e2e7f 100644 +--- a/src/include/souffle/profile/ProgramRun.h ++++ b/src/include/souffle/profile/ProgramRun.h +@@ -138,8 +138,6 @@ public: + for (auto& cur : relationMap) { + if (cur.second->getStarttime() <= end && cur.second->getEndtime() >= start) { + result.insert(cur.second); +- } else if (cur.second->getLoadStarttime() <= end && cur.second->getLoadEndtime() >= start) { +- result.insert(cur.second); + } + } + return result; +@@ -149,7 +147,7 @@ public: + return Tools::formatTime(runtime); + } + +- inline std::string formatNum(int precision, int64_t number) const { ++ inline std::string formatNum(int precision, long number) const { + return Tools::formatNum(precision, number); + } + +diff --git a/src/include/souffle/profile/Reader.h b/src/include/souffle/profile/Reader.h +index 5d8cc36..0dbbf35 100644 +--- a/src/include/souffle/profile/Reader.h ++++ b/src/include/souffle/profile/Reader.h +@@ -230,7 +230,8 @@ public: + RelationVisitor(Relation& relation) : DSNVisitor(relation) {} + void visit(DurationEntry& duration) override { + if (duration.getKey() == "loadtime") { +- base.setLoadtime(duration.getStart(), duration.getEnd()); ++ auto loadtime = (duration.getEnd() - duration.getStart()); ++ base.setLoadtime(loadtime); + } else if (duration.getKey() == "savetime") { + auto savetime = (duration.getEnd() - duration.getStart()); + base.setSavetime(savetime); +@@ -277,8 +278,9 @@ private: + bool online{true}; + + std::unordered_map> relationMap{}; +- std::unordered_map> countRecursiveJoinSizeMap{}; +- std::unordered_map countNonRecursiveJoinSizeMap{}; ++ std::unordered_map> ++ countRecursiveUniqueKeysMap{}; ++ std::unordered_map countNonRecursiveUniqueKeysMap{}; + int rel_id{0}; + + public: +@@ -327,12 +329,12 @@ public: + continue; + } + for (const auto& constants : prefixWithAttributes->getKeys()) { +- auto fullKey = as(db.lookupEntry({"program", "statistics", "relation", ++ auto fullKey = as(db.lookupEntry({"program", "statistics", "relation", + rel, "attributes", attributes, "constants", constants})); + if (fullKey != nullptr) { +- double joinSize = std::stod(fullKey->getText()); ++ std::size_t uniqueKeys = fullKey->getSize(); + std::string key = rel + " " + attributes + " " + constants; +- countNonRecursiveJoinSizeMap[key] = joinSize; ++ countNonRecursiveUniqueKeysMap[key] = uniqueKeys; + } + } + } +@@ -356,13 +358,13 @@ public: + continue; + } + for (const auto& constants : prefixWithAttributes->getKeys()) { +- auto fullKey = as(db.lookupEntry( ++ auto fullKey = as(db.lookupEntry( + {"program", "statistics", "relation", rel, "iteration", iteration, + "attributes", attributes, "constants", constants})); +- double joinSize = std::stod(fullKey->getText()); ++ std::size_t uniqueKeys = fullKey->getSize(); + if (fullKey != nullptr) { + std::string key = rel + " " + attributes + " " + constants; +- countRecursiveJoinSizeMap[key][iteration] = joinSize; ++ countRecursiveUniqueKeysMap[key][iteration] = uniqueKeys; + } + } + } +@@ -417,17 +419,17 @@ public: + } + + bool hasAutoSchedulerStats() { +- return !countNonRecursiveJoinSizeMap.empty() || !countRecursiveJoinSizeMap.empty(); ++ return !countNonRecursiveUniqueKeysMap.empty() || !countRecursiveUniqueKeysMap.empty(); + } + +- double getNonRecursiveEstimateJoinSize( ++ std::size_t getNonRecursiveCountUniqueKeys( + const std::string& rel, const std::string& attributes, const std::string& constants) { + auto key = rel + " " + attributes + " " + constants; +- return countNonRecursiveJoinSizeMap.at(key); ++ return countNonRecursiveUniqueKeysMap.at(key); + } + + std::size_t getIterations(const std::string& rel) { +- for (auto& [key, m] : countRecursiveJoinSizeMap) { ++ for (auto& [key, m] : countRecursiveUniqueKeysMap) { + std::string token = key.substr(0, key.find(" ")); + if (token == rel) { + return m.size(); +@@ -437,11 +439,11 @@ public: + return 0; + } + +- double getRecursiveEstimateJoinSize(const std::string& rel, const std::string& attributes, ++ std::size_t getRecursiveCountUniqueKeys(const std::string& rel, const std::string& attributes, + const std::string& constants, const std::string& iteration) { + auto key = rel + " " + attributes + " " + constants; +- auto& m = countRecursiveJoinSizeMap.at(key); +- return m.at(iteration); ++ auto& m = countRecursiveUniqueKeysMap.at(key); ++ return static_cast(m.at(iteration)); + } + + void addRelation(const DirectoryEntry& relation) { +diff --git a/src/include/souffle/profile/Relation.h b/src/include/souffle/profile/Relation.h +index 0a16d96..b0bffd1 100644 +--- a/src/include/souffle/profile/Relation.h ++++ b/src/include/souffle/profile/Relation.h +@@ -31,8 +31,7 @@ private: + const std::string name; + std::chrono::microseconds starttime{}; + std::chrono::microseconds endtime{}; +- std::chrono::microseconds loadstarttime{}; +- std::chrono::microseconds loadendtime{}; ++ std::chrono::microseconds loadtime{}; + std::chrono::microseconds savetime{}; + std::size_t nonRecTuples = 0; + std::size_t preMaxRSS = 0; +@@ -71,15 +70,7 @@ public: + } + + std::chrono::microseconds getLoadtime() const { +- return loadendtime - loadstarttime; +- } +- +- std::chrono::microseconds getLoadStarttime() const { +- return loadstarttime; +- } +- +- std::chrono::microseconds getLoadEndtime() const { +- return loadendtime; ++ return loadtime; + } + + std::chrono::microseconds getSavetime() const { +@@ -136,9 +127,8 @@ public: + return result; + } + +- void setLoadtime(std::chrono::microseconds loadstarttime, std::chrono::microseconds loadendtime) { +- this->loadstarttime = loadstarttime; +- this->loadendtime = loadendtime; ++ void setLoadtime(std::chrono::microseconds loadtime) { ++ this->loadtime = loadtime; + } + + void setSavetime(std::chrono::microseconds savetime) { +diff --git a/src/include/souffle/profile/Tui.h b/src/include/souffle/profile/Tui.h +index 6de9147..8ef1210 100644 +--- a/src/include/souffle/profile/Tui.h ++++ b/src/include/souffle/profile/Tui.h +@@ -28,7 +28,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -40,11 +39,9 @@ + #include + #include + #include +-#ifndef _MSC_VER + #include + #include + #include +-#endif + + namespace souffle { + namespace profile { +@@ -216,11 +213,10 @@ public: + } + } + +- /// Return an exit status equal to 0 on success. +- int runProf() { ++ void runProf() { + if (!loaded && !f_name.empty()) { + std::cout << "Error: File cannot be loaded\n"; +- return 1; ++ return; + } + if (loaded) { + std::cout << "SouffleProf\n"; +@@ -257,8 +253,6 @@ public: + runCommand(c); + } + } +- +- return 0; + } + + std::stringstream& genJsonTop(std::stringstream& ss) { +@@ -639,50 +633,47 @@ public: + return ss.str(); + } + +- /// Return an exit status equal to 0 on success. +- int outputHtml(std::string filename = "profiler_html/") { ++ void outputHtml(std::string filename = "profiler_html/") { + std::cout << "SouffleProf\n"; + std::cout << "Generating HTML files...\n"; + +- std::filesystem::path filepath(filename); +- if (filepath.has_parent_path()) { +- std::error_code ec; +- std::filesystem::create_directories(filepath.parent_path(), ec); +- if (ec != std::error_code{}) { +- std::cerr << "directory " << filepath.parent_path() +- << " could not be created. Please create it and try again.\n"; +- return 2; +- } +- } +- +- if (!filepath.has_filename()) { +- // create a fresh filename +- bool notfound = true; +- unsigned i = 1; +- while (i < 1000) { +- std::filesystem::path freshPath = filepath; +- freshPath /= std::to_string(i); +- freshPath.replace_extension(".html"); +- if (!std::filesystem::exists(freshPath)) { +- filepath = freshPath; +- notfound = false; +- break; +- } +- ++i; ++ DIR* dir; ++ bool exists = false; ++ ++ if (filename.find('/') != std::string::npos) { ++ std::string path = filename.substr(0, filename.find('/')); ++ if ((dir = opendir(path.c_str())) != nullptr) { ++ exists = true; ++ closedir(dir); + } +- if (notfound) { +- std::cerr << "Could not generate a fresh file name (1000 tested).\n"; +- return 2; ++ if (!exists) { ++ mode_t nMode = 0733; // UNIX style permissions ++ int nError = 0; ++ nError = mkdir(path.c_str(), nMode); ++ if (nError != 0) { ++ std::cerr << "directory " << path ++ << " could not be created. Please create it and try again."; ++ exit(2); ++ } + } + } ++ std::string filetype = ".html"; ++ std::string newFile = filename; + +- std::ofstream outfile(filepath); ++ if (filename.size() <= filetype.size() || ++ !std::equal(filetype.rbegin(), filetype.rend(), filename.rbegin())) { ++ int i = 0; ++ do { ++ ++i; ++ newFile = filename + std::to_string(i) + ".html"; ++ } while (Tools::file_exists(newFile)); ++ } + +- outfile << HtmlGenerator::getHtml(genJson()); ++ std::ofstream outfile(newFile); + +- std::cout << "file output to: " << filepath << std::endl; ++ outfile << HtmlGenerator::getHtml(genJson()); + +- return 0; ++ std::cout << "file output to: " << newFile << std::endl; + } + + void quit() { +@@ -892,13 +883,11 @@ public: + std::printf("%11s\n", Tools::formatTime(usages.rbegin()->usertime).c_str()); + + // Add columns to the graph +- // char grid[height][width]; +- std::vector> grid; +- grid.reserve(height); +- grid.resize(height); ++ char grid[height][width]; + for (uint32_t i = 0; i < height; ++i) { +- grid[i].reserve(width); +- grid[i].resize(width, ' '); ++ for (uint32_t j = 0; j < width; ++j) { ++ grid[i][j] = ' '; ++ } + } + + previousUsage = {{}, 0, {}, {}}; +@@ -955,13 +944,11 @@ public: + uint64_t maxMaxRSS = 0; + + std::set usages = getUsageStats(width); +- // char grid[height][width]; +- std::vector> grid; +- grid.reserve(height); +- grid.resize(height); ++ char grid[height][width]; + for (uint32_t i = 0; i < height; ++i) { +- grid[i].reserve(width); +- grid[i].resize(width, ' '); ++ for (uint32_t j = 0; j < width; ++j) { ++ grid[i][j] = ' '; ++ } + } + + for (auto& usage : usages) { +@@ -1484,14 +1471,10 @@ protected: + } + + uint32_t getTermWidth() { +-#ifdef _MSC_VER +- return 80; +-#else + struct winsize w {}; + ioctl(0, TIOCGWINSZ, &w); + uint32_t width = w.ws_col > 0 ? w.ws_col : 80; + return width; +-#endif + } + }; + +diff --git a/src/include/souffle/profile/UserInputReader.h b/src/include/souffle/profile/UserInputReader.h +index 767f612..0130e25 100644 +--- a/src/include/souffle/profile/UserInputReader.h ++++ b/src/include/souffle/profile/UserInputReader.h +@@ -12,11 +12,8 @@ + #include // for end, begin + #include + #include +- +-#ifndef _MSC_VER + #include // for termios, tcsetattr, tcgetattr, ECHO, ICANON, cc_t + #include // for read +-#endif + + namespace souffle { + namespace profile { +@@ -57,7 +54,6 @@ public: + } + + void readchar() { +-#ifndef _MSC_VER + char buf = 0; + struct termios old = {}; + if (tcgetattr(0, &old) < 0) { +@@ -80,13 +76,6 @@ public: + } + + current_char = buf; +-#else +- char buf = 0; +- if (_read(0, &buf, 1) < 0) { +- perror("read()"); +- } +- current_char = buf; +-#endif + } + std::string getInput() { + output = ""; +@@ -150,7 +139,6 @@ public: + + return output; + } +- + void setPrompt(std::string prompt) { + this->prompt = prompt; + } +diff --git a/src/include/souffle/profile/htmlJsMain.h b/src/include/souffle/profile/htmlJsMain.h +index e1ce7b7..0f43d2e 100644 +--- a/src/include/souffle/profile/htmlJsMain.h ++++ b/src/include/souffle/profile/htmlJsMain.h +@@ -25,14 +25,6 @@ function changeSelectedRul(id) { + genAtomVer(); + } + +-function goBack() { +- if (came_from==="rel") { +- document.getElementById("rel_tab").click(); +- } else if (came_from==="rul") { +- document.getElementById("rul_tab").click(); +- } +-} +- + function highlightRow() { + var i; + for (i=0;i& a, const std::vector& b) { + * A function testing whether two maps of unique pointers are referencing to equivalent + * targets. + */ +-template +-bool equal_targets(const std::map, Cmp>& a, const std::map, Cmp>& b) { ++template ++bool equal_targets(const std::map>& a, const std::map>& b) { + auto comp = comp_deref>(); + return equal_targets( + a, b, [&comp](auto& a, auto& b) { return a.first == b.first && comp(a.second, b.second); }); +@@ -247,8 +247,8 @@ bool equal_targets(const std::map, Cmp>& a, const std::map +-bool equal_targets_map(const std::map& a, const std::map& b, F&& comp) { ++template ++bool equal_targets_map(const std::map& a, const std::map& b, F&& comp) { + return equal_targets( + a, b, [&](auto& a, auto& b) { return a.first == b.first && comp(a.second, b.second); }); + } +diff --git a/src/include/souffle/utility/DynamicCasting.h b/src/include/souffle/utility/DynamicCasting.h +index 50506dd..61fc020 100644 +--- a/src/include/souffle/utility/DynamicCasting.h ++++ b/src/include/souffle/utility/DynamicCasting.h +@@ -30,114 +30,50 @@ namespace souffle { + class AllowCrossCast {}; + + namespace detail { +- +-/// Tels if A is a valid cross-cast option + template + constexpr bool is_valid_cross_cast_option = std::is_same_v || std::is_same_v; +- +-/// Tells if there is a function `To::classof(From*)` +-template +-struct has_classof : std::false_type {}; +- +-template +-struct has_classof::classof(std::declval()))>> +- : std::true_type {}; +-} // namespace detail +- +-/// Takes a non-null pointer and return whether it is pointing to a derived class of `To`. +-template >> +-inline bool isA(From* p) noexcept { +- if constexpr (detail::has_classof::value) { +- // use classof when available +- return remove_cvref_t::classof(p); +- } else { +- // fallback to dynamic_cast +- return dynamic_cast>>(p) != nullptr; +- } +-} +- +-/// forward isA when From is not a pointer +-template >, +- typename = std::enable_if_t || std::is_base_of_v>, +- typename = std::enable_if_t && !is_pointer_like>> +-inline bool isA(From& p) noexcept { +- return isA(&p); + } + +-/// forward isA when From is supposed to be a unique or shared pointer +-template >> +-inline bool isA(const From& p) noexcept { +- return isA(p.get()); +-} +- +-/// Takes a non-null pointer and dynamic-cast to `To`. +-/// +-/// Leverage `To::classof` when available to avoid costly `dynamic_cast`. +-template (p)` instead of `as(p)`. ++ */ ++template >> +-inline auto as(From* p) noexcept { +- using ToClass = remove_cvref_t; +- using FromClass = remove_cvref_t; +- if constexpr (std::is_base_of_v) { +- // trivial conversion from pointer to derived class to pointer to base class +- return static_cast>>(p); +- } else if constexpr (std::is_base_of_v && +- can_static_cast::value) { +- // cast using isA when converting from pointer to non-virtual base class to pointer to derived class +- using ResultType = remove_cvref_t; +- return isA(p) ? static_cast>>(p) : nullptr; +- } else if constexpr (std::is_same_v || +- !can_static_cast::value) { +- // dynamic cast when converting across type hierarchies or +- // converting from pointer to virtual base class to pointer to derived class +- return dynamic_cast>>(p); +- } else { +- // cross-hierarchy dynamic cast not allowed unless CastType = AllowCrossCast +- static_assert(std::is_base_of_v, ++auto as(A* x) { ++ if constexpr (!std::is_same_v && ++ !std::is_base_of_v, std::remove_const_t>) { ++ static_assert(std::is_base_of_v, std::remove_const_t>, + "`as` does not allow cross-type dyn casts. " + "(i.e. `as` where `B <: A` is not true.) " + "Such a cast is likely a mistake or typo."); + } ++ return dynamic_cast*>(x); + } + +-/// Takes a possibly null pointer and dynamic-cast to `To`. +-template >> +-inline auto as_or_null(From* p) noexcept { +- using ToClass = remove_cvref_t; +- if (p == nullptr) { +- return static_cast>>(nullptr); +- } +- return as(p); +-} +- +-template >, +- typename = std::enable_if_t || std::is_base_of_v>, +- typename = std::enable_if_t && !is_pointer_like>> +-inline auto as(From& x) { +- return as(&x); ++template || std::is_base_of_v>, ++ typename = std::enable_if_t && !is_pointer_like>> ++auto as(A& x) { ++ return as(&x); + } + +-template +-inline auto as(const std::unique_ptr& x) { +- return as(x.get()); ++template >> ++auto as(const A& x) { ++ return as(x.get()); + } + +-template +-inline auto as(const std::reference_wrapper& x) { +- return as(x.get()); ++template ++auto as(const std::reference_wrapper& x) { ++ return as(x.get()); + } + + /** + * Down-casts and checks the cast has succeeded + */ +-template +-auto& asAssert(From&& a) { +- auto* cast = as(std::forward(a)); ++template ++auto& asAssert(A&& a) { ++ auto* cast = as(std::forward(a)); + assert(cast && "Invalid cast"); + return *cast; + } +@@ -156,13 +92,13 @@ Own UNSAFE_cast(Own x) { + } + } + +-///** +-// * Checks if the object of type Source can be casted to type Destination. +-// */ +-// template +-//// [[deprecated("Use `as` and implicit boolean conversion instead.")]] +-// bool isA(A&& src) { +-// return as(std::forward(src)); +-// } ++/** ++ * Checks if the object of type Source can be casted to type Destination. ++ */ ++template ++// [[deprecated("Use `as` and implicit boolean conversion instead.")]] ++bool isA(A&& src) { ++ return as(std::forward(src)); ++} + + } // namespace souffle +diff --git a/src/include/souffle/utility/EvaluatorUtil.h b/src/include/souffle/utility/EvaluatorUtil.h +index 5cbb2a8..7d21a9f 100644 +--- a/src/include/souffle/utility/EvaluatorUtil.h ++++ b/src/include/souffle/utility/EvaluatorUtil.h +@@ -70,9 +70,9 @@ A symbol2numeric(const std::string& src) { + if constexpr (std::is_same_v) { + return RamFloatFromString(src); + } else if constexpr (std::is_same_v) { +- return RamSignedFromString(src, nullptr, 0); ++ return RamSignedFromString(src); + } else if constexpr (std::is_same_v) { +- return RamUnsignedFromString(src, nullptr, 0); ++ return RamUnsignedFromString(src); + } else { + static_assert(sizeof(A) == 0, "Invalid type specified for symbol2Numeric"); + } +diff --git a/src/include/souffle/utility/FileUtil.h b/src/include/souffle/utility/FileUtil.h +index 24b40b9..fbb3497 100644 +--- a/src/include/souffle/utility/FileUtil.h ++++ b/src/include/souffle/utility/FileUtil.h +@@ -17,7 +17,6 @@ + #pragma once + + #include +-#include + #include + #include + #include +@@ -323,20 +322,19 @@ inline std::string tempFile() { + } + + inline std::stringstream execStdOut(char const* cmd) { ++ FILE* in = popen(cmd, "r"); + std::stringstream data; +- std::shared_ptr command_pipe(popen(cmd, "r"), pclose); + +- if (command_pipe.get() == nullptr) { ++ if (in == nullptr) { + return data; + } + +- std::array buffer; +- while (!feof(command_pipe.get())) { +- if (fgets(buffer.data(), 256, command_pipe.get()) != nullptr) { +- data << buffer.data(); +- } ++ while (!feof(in)) { ++ int c = fgetc(in); ++ data << static_cast(c); + } + ++ pclose(in); + return data; + } + +diff --git a/src/include/souffle/utility/FunctionalUtil.h b/src/include/souffle/utility/FunctionalUtil.h +index 047e29d..64d0450 100644 +--- a/src/include/souffle/utility/FunctionalUtil.h ++++ b/src/include/souffle/utility/FunctionalUtil.h +@@ -386,7 +386,7 @@ void append(std::vector& xs, B&& y) { + // ------------------------------------------------------------------------------- + + template +-std::set operator&(const std::set>& lhs, const std::set>& rhs) { ++std::set operator&(const std::set& lhs, const std::set& rhs) { + std::set result; + std::set_intersection( + lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin())); +@@ -394,14 +394,14 @@ std::set operator&(const std::set>& lhs, const std::set +-std::set operator|(const std::set>& lhs, const std::set>& rhs) { ++std::set operator|(const std::set& lhs, const std::set& rhs) { + std::set result; + std::set_union(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin())); + return result; + } + + template +-std::set operator-(const std::set>& lhs, const std::set>& rhs) { ++std::set operator-(const std::set& lhs, const std::set& rhs) { + std::set result; + std::set_difference( + lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), std::inserter(result, result.begin())); +diff --git a/src/include/souffle/utility/MiscUtil.h b/src/include/souffle/utility/MiscUtil.h +index 15b1efc..c405e65 100644 +--- a/src/include/souffle/utility/MiscUtil.h ++++ b/src/include/souffle/utility/MiscUtil.h +@@ -27,7 +27,6 @@ + #include + #include + #include +-#include + #include + + #ifdef _WIN32 +@@ -47,11 +46,7 @@ + * windows equivalents. However ctz is used in a constexpr context, and we can't + * use BitScanForward, so we implement it ourselves. + */ +-#if _WIN64 + #define __builtin_popcountll __popcnt64 +-#else +-#define __builtin_popcountll __popcnt +-#endif + + #if defined(_MSC_VER) + // return the number of trailing zeroes in value, or 32 if value is zero. +@@ -78,11 +73,7 @@ inline constexpr int __builtin_ctzll_constexpr(unsigned long long value) { + inline int __builtin_ctzll(unsigned long long value) { + unsigned long trailing_zeroes = 0; + +-#if _WIN64 + if (_BitScanForward64(&trailing_zeroes, value)) { +-#else +- if (_BitScanForward(&trailing_zeroes, value)) { +-#endif + return static_cast(trailing_zeroes); + } else { + return 64; // return 64 like GCC would when value == 0 +@@ -97,34 +88,23 @@ inline int __builtin_ctzll(unsigned long long value) { + + namespace souffle { + +-/// select the most precise and steady clock to measure durations +-using steady_clock = std::conditional::type; +- +-static_assert(steady_clock::is_steady, "clock is not monotonically-increasing"); +- + // a type def for a time point +-using time_point = steady_clock::time_point; +-using microseconds = std::chrono::microseconds; ++using time_point = std::chrono::high_resolution_clock::time_point; ++using std::chrono::microseconds; + + // a shortcut for taking the current time + inline time_point now() { +- return steady_clock::now(); ++ return std::chrono::high_resolution_clock::now(); + } + + // a shortcut for obtaining the time difference in milliseconds +-inline int64_t duration_in_ms(const time_point& start, const time_point& end) { +- return static_cast(std::chrono::duration_cast(end - start).count()); +-} +- +-// a shortcut for obtaining the time difference in microseconds +-inline int64_t duration_in_us(const time_point& start, const time_point& end) { +- return static_cast(std::chrono::duration_cast(end - start).count()); ++inline long duration_in_us(const time_point& start, const time_point& end) { ++ return static_cast(std::chrono::duration_cast(end - start).count()); + } + + // a shortcut for obtaining the time difference in nanoseconds +-inline int64_t duration_in_ns(const time_point& start, const time_point& end) { +- return static_cast(std::chrono::duration_cast(end - start).count()); ++inline long duration_in_ns(const time_point& start, const time_point& end) { ++ return static_cast(std::chrono::duration_cast(end - start).count()); + } + + // ------------------------------------------------------------------------------- +@@ -167,17 +147,9 @@ Own clone(const Own& node) { + return clone(node.get()); + } + +-template +-auto clone(const std::map& xs) { +- std::map())), C> ys; +- for (auto&& [k, v] : xs) +- ys.insert({k, clone(v)}); +- return ys; +-} +- +-template +-auto clone(const std::unordered_map& xs) { +- std::unordered_map())), H> ys; ++template ++auto clone(const std::map& xs) { ++ std::map()))> ys; + for (auto&& [k, v] : xs) + ys.insert({k, clone(v)}); + return ys; +diff --git a/src/include/souffle/utility/StreamUtil.h b/src/include/souffle/utility/StreamUtil.h +index 8c899ab..e2eafb0 100644 +--- a/src/include/souffle/utility/StreamUtil.h ++++ b/src/include/souffle/utility/StreamUtil.h +@@ -22,8 +22,6 @@ + #include + #include + #include +-#include +-#include + #include + #include + +@@ -300,18 +298,6 @@ ostream& operator<<(ostream& out, const map& m) { + }) << "}"; + } + +-template +-ostream& operator<<(ostream& out, const unordered_set& s) { +- return out << "{" << souffle::join(s) << "}"; +-} +- +-template +-ostream& operator<<(ostream& out, const unordered_map& m) { +- return out << "{" << souffle::join(m, ",", [](ostream& out, const pair& cur) { +- out << cur.first << "->" << cur.second; +- }) << "}"; +-} +- + } // end namespace std + + #endif +diff --git a/src/include/souffle/utility/StringUtil.h b/src/include/souffle/utility/StringUtil.h +index 7e87d44..0c1483b 100644 +--- a/src/include/souffle/utility/StringUtil.h ++++ b/src/include/souffle/utility/StringUtil.h +@@ -247,15 +247,6 @@ template + struct is_printable() << std::declval()), void>::type> + : public std::true_type {}; +- +-template +-struct is_html_printable : public std::false_type {}; +- +-template +-struct is_html_printable().printHTML(std::declval())), +- void>::type> : public std::true_type {}; +- + } // namespace detail + + /** +@@ -286,20 +277,6 @@ typename std::enable_if::value, std::string>::type toSt + return ss.str(); + } + +-template +-auto toHtml(const T& obj) -> typename std::enable_if::value, std::string>::type { +- std::stringstream out; +- obj.printHTML(out); +- return out.str(); +-} +- +-/** Fallback to `toString` */ +-template +-auto toHtml(const T& obj) -> +- typename std::enable_if::value, std::string>::type { +- return toString(obj); +-} +- + // ------------------------------------------------------------------------------- + // String Utils + // ------------------------------------------------------------------------------- +diff --git a/src/include/souffle/utility/SubProcess.h b/src/include/souffle/utility/SubProcess.h +index 93b4eb7..7c17548 100644 +--- a/src/include/souffle/utility/SubProcess.h ++++ b/src/include/souffle/utility/SubProcess.h +@@ -70,7 +70,7 @@ std::optional execute( + + auto pid = ::fork(); + switch (pid) { +- case -1: return std::nullopt; // unable to fork. likely hit a resource limit of some kind. ++ case -1: return {}; // unable to fork. likely hit a resource limit of some kind. + + case 0: { // child + // thankfully we're a fork. we can trash this proc's `::environ` w/o reprocussions +@@ -100,14 +100,14 @@ std::optional execute( + // check that the fork child successfully `exec`'d + if (WIFEXITED(status)) { + switch (WEXITSTATUS(status)) { +- default: return WEXITSTATUS(status); ++ default: break; + +- case EC::cannot_execute: // FALL THRU: command_not_found +- case EC::command_not_found: return std::nullopt; // fork couldn't execute the program ++ case EC::cannot_execute: // FALL THRU: command_not_found ++ case EC::command_not_found: return {}; // fork couldn't execute the program + } + } +- // what should be returned on signal? Treat as error +- return EXIT_FAILURE; ++ ++ return status; + } + } + #else +@@ -128,7 +128,7 @@ std::optional execute( + int64_t Found = (int64_t)FindExecutableW(program_w.c_str(), nullptr, FoundPath); + if (Found <= 32) { + std::cerr << "Cannot find executable '" << program << "'.\n"; +- return std::nullopt; ++ return {}; + } + + std::wstringstream args_w; +@@ -152,13 +152,13 @@ std::optional execute( + + if (!CreateProcessW(FoundPath, args_w.str().data(), NULL, NULL, FALSE, 0, /*envir.data()*/ nullptr, NULL, + &si, &pi)) { +- return std::nullopt; ++ return {}; + } + + WaitForSingleObject(pi.hProcess, INFINITE); + + if (!GetExitCodeProcess(pi.hProcess, &exit_code)) { +- return std::nullopt; ++ return {}; + } + + CloseHandle(pi.hProcess); +diff --git a/src/include/souffle/utility/Types.h b/src/include/souffle/utility/Types.h +index 01d7841..dd09242 100644 +--- a/src/include/souffle/utility/Types.h ++++ b/src/include/souffle/utility/Types.h +@@ -152,19 +152,4 @@ constexpr bool is_set = detail::is_set::value; + template + constexpr bool unhandled_dispatch_type = !std::is_same_v; + +-// Tells if we can static_cast +-template +-struct can_static_cast : std::false_type {}; +- +-template +-struct can_static_cast(std::declval()))>> +- : std::true_type {}; +- +-// A virtual base is first and foremost a base, +-// that, however, cannot be static_casted to its derived class. +-template +-struct is_virtual_base_of +- : std::conjunction, std::negation>> { +-}; +- + } // namespace souffle +diff --git a/src/include/souffle/utility/Visitor.h b/src/include/souffle/utility/Visitor.h +index f51d9fb..80b9543 100644 +--- a/src/include/souffle/utility/Visitor.h ++++ b/src/include/souffle/utility/Visitor.h +@@ -113,12 +113,7 @@ struct VisitorBase : visitor_tag { + + /** The base case for all visitors -- if no more specific overload was defined */ + virtual R visit_(type_identity, NodeType& /*node*/, Params const&... /*args*/) { +- if constexpr (std::is_same_v) { +- return; +- } else { +- R res{}; +- return res; +- } ++ return R(); + } + }; + +diff --git a/src/include/souffle/utility/json11.h b/src/include/souffle/utility/json11.h +index 98aa33a..bc335c1 100644 +--- a/src/include/souffle/utility/json11.h ++++ b/src/include/souffle/utility/json11.h +@@ -66,7 +66,6 @@ + #include + + #ifdef _MSC_VER +-#pragma warning(push) + #pragma warning(disable : 4244) + #if _MSC_VER <= 1800 // VS 2013 + #ifndef noexcept +@@ -79,11 +78,6 @@ + #endif + #endif + +-#if defined(__GNUC__) && (__GNUC__ >= 7) +-#pragma GCC diagnostic push +-#pragma GCC diagnostic ignored "-Woverloaded-virtual" +-#endif +- + namespace json11 { + + enum JsonParse { STANDARD, COMMENTS }; +@@ -1112,11 +1106,7 @@ inline std::vector parse_multi(const std::string& in, std::string::size_ty + } + + #ifdef _MSC_VER +-#pragma warning(pop) ++#pragma warning(default : 4244) + #endif // _MSC_VER + + } // namespace json11 +- +-#if defined(__GNUC__) && (__GNUC__ >= 7) +-#pragma GCC diagnostic pop +-#endif +\ No newline at end of file +diff --git a/src/interpreter/BTreeDeleteIndex.cpp b/src/interpreter/BTreeDeleteIndex.cpp +index 7275e37..9a108ae 100644 +--- a/src/interpreter/BTreeDeleteIndex.cpp ++++ b/src/interpreter/BTreeDeleteIndex.cpp +@@ -21,15 +21,23 @@ + + namespace souffle::interpreter { + +-#define CREATE_BTREE_DELETE_REL(Structure, Arity, AuxiliaryArity, ...) \ +- if (id.getArity() == Arity && id.getAuxiliaryArity() == AuxiliaryArity) { \ +- return mk>(id.getName(), indexSelection); \ ++#define CREATE_BTREE_DELETE_REL(Structure, Arity, ...) \ ++ case (Arity): { \ ++ return mk>(id.getAuxiliaryArity(), id.getName(), indexSelection); \ + } + + Own createBTreeDeleteRelation( + const ram::Relation& id, const ram::analysis::IndexCluster& indexSelection) { +- FOR_EACH_BTREE_DELETE(CREATE_BTREE_DELETE_REL); +- fatal("Requested arity not yet supported. Feel free to add it."); ++ switch (id.getArity()) { ++ FOR_EACH_BTREE_DELETE(CREATE_BTREE_DELETE_REL); ++ ++ default: ++ fatal(( ++ "createBTreeDeleteRelation: Requested arity " + ++ std::to_string(id.getArity()) + ++ " not yet supported. Feel free to add it." ++ ).c_str()); ++ } + } + + } // namespace souffle::interpreter +diff --git a/src/interpreter/BTreeIndex.cpp b/src/interpreter/BTreeIndex.cpp +index 5a42aa6..7c5af5f 100644 +--- a/src/interpreter/BTreeIndex.cpp ++++ b/src/interpreter/BTreeIndex.cpp +@@ -21,15 +21,24 @@ + + namespace souffle::interpreter { + +-#define CREATE_BTREE_REL(Structure, Arity, AuxiliaryArity, ...) \ +- if (id.getArity() == Arity && id.getAuxiliaryArity() == AuxiliaryArity) { \ +- return mk>(id.getName(), indexSelection); \ ++#define CREATE_BTREE_REL(Structure, Arity, ...) \ ++ case (Arity): { \ ++ return mk>( \ ++ id.getAuxiliaryArity(), id.getName(), indexSelection); \ + } + + Own createBTreeRelation( + const ram::Relation& id, const ram::analysis::IndexCluster& indexSelection) { +- FOR_EACH_BTREE(CREATE_BTREE_REL); +- fatal("Requested arity not yet supported. Feel free to add it."); ++ switch (id.getArity()) { ++ FOR_EACH_BTREE(CREATE_BTREE_REL); ++ ++ default: ++ fatal(( ++ "createBTreeRelation: Requested arity " + ++ std::to_string(id.getArity()) + ++ " not yet supported. Feel free to add it." ++ ).c_str()); ++ } + } + + } // namespace souffle::interpreter +diff --git a/src/interpreter/Context.h b/src/interpreter/Context.h +index 6b9a0fd..efba119 100644 +--- a/src/interpreter/Context.h ++++ b/src/interpreter/Context.h +@@ -109,14 +109,6 @@ public: + return views[id].get(); + } + +- RamDomain getVariable(const std::string& name) { +- return variables[name]; +- } +- +- void setVariable(const std::string& name, RamDomain value) { +- variables[name] = value; +- } +- + private: + /** @brief Run-time value */ + std::vector data; +@@ -128,7 +120,6 @@ private: + VecOwn allocatedDataContainer; + /** @brief Views */ + VecOwn views; +- std::map variables; + }; + + } // namespace souffle::interpreter +diff --git a/src/interpreter/Engine.cpp b/src/interpreter/Engine.cpp +index 41683d3..cb38b9f 100644 +--- a/src/interpreter/Engine.cpp ++++ b/src/interpreter/Engine.cpp +@@ -23,18 +23,16 @@ + #include "interpreter/Relation.h" + #include "interpreter/ViewContext.h" + #include "ram/Aggregate.h" +-#include "ram/Aggregator.h" +-#include "ram/Assign.h" + #include "ram/AutoIncrement.h" + #include "ram/Break.h" + #include "ram/Call.h" + #include "ram/Clear.h" + #include "ram/Conjunction.h" + #include "ram/Constraint.h" ++#include "ram/CountUniqueKeys.h" + #include "ram/DebugInfo.h" + #include "ram/EmptinessCheck.h" + #include "ram/Erase.h" +-#include "ram/EstimateJoinSize.h" + #include "ram/ExistenceCheck.h" + #include "ram/Exit.h" + #include "ram/False.h" +@@ -45,7 +43,6 @@ + #include "ram/IndexIfExists.h" + #include "ram/IndexScan.h" + #include "ram/Insert.h" +-#include "ram/IntrinsicAggregator.h" + #include "ram/IntrinsicOperator.h" + #include "ram/LogRelationTimer.h" + #include "ram/LogSize.h" +@@ -80,9 +77,7 @@ + #include "ram/TupleElement.h" + #include "ram/TupleOperation.h" + #include "ram/UnpackRecord.h" +-#include "ram/UserDefinedAggregator.h" + #include "ram/UserDefinedOperator.h" +-#include "ram/Variable.h" + #include "ram/utility/Visitor.h" + #include "souffle/BinaryConstraintOps.h" + #include "souffle/RamTypes.h" +@@ -90,7 +85,6 @@ + #include "souffle/SignalHandler.h" + #include "souffle/SymbolTable.h" + #include "souffle/TypeAttribute.h" +-#include "souffle/datastructure/RecordTableImpl.h" + #include "souffle/datastructure/SymbolTableImpl.h" + #include "souffle/io/IOSystem.h" + #include "souffle/io/ReadStream.h" +@@ -98,9 +92,9 @@ + #include "souffle/profile/Logger.h" + #include "souffle/profile/ProfileEvent.h" + #include "souffle/utility/EvaluatorUtil.h" ++#include "souffle/utility/MiscUtil.h" + #include "souffle/utility/ParallelUtil.h" + #include "souffle/utility/StringUtil.h" +- + #include + #include + #include +@@ -194,17 +188,6 @@ RamDomain callStateful(ExecuteFn&& execute, Context& ctxt, Shadow& shadow, void + return callWithTuple(userFunctor, argsTuple); + } + +-/** Call a stateful aggregate functor. */ +-template +-RamDomain callStatefulAggregate(AnyFunctor&& userFunctor, souffle::SymbolTable* symbolTable, +- souffle::RecordTable* recordTable, souffle::RamDomain arg1, souffle::RamDomain arg2) { +- std::array args; +- args[0] = arg1; +- args[1] = arg2; +- auto argsTuple = statefulCallTuple(symbolTable, recordTable, args, std::make_index_sequence<2>{}); +- return callWithTuple(std::forward(userFunctor), argsTuple); +-} +- + /** + * Governs the maximum supported arity for stateless functors. + * +@@ -302,12 +285,12 @@ RamDomain callStateless(ExecuteFn&& execute, Context& ctxt, Shadow& shadow, souf + + } // namespace + +-Engine::Engine(ram::TranslationUnit& tUnit, const std::size_t numberOfThreadsOrZero) +- : tUnit(tUnit), global(tUnit.global()), profileEnabled(global.config().has("profile")), +- frequencyCounterEnabled(global.config().has("profile-frequency")), +- numOfThreads(number_of_threads(numberOfThreadsOrZero)), ++Engine::Engine(ram::TranslationUnit& tUnit) ++ : profileEnabled(Global::config().has("profile")), ++ frequencyCounterEnabled(Global::config().has("profile-frequency")), ++ numOfThreads(number_of_threads(std::stoi(Global::config().get("jobs")))), tUnit(tUnit), + isa(tUnit.getAnalysis()), recordTable(numOfThreads), +- symbolTable(numOfThreads), regexCache(numOfThreads) {} ++ symbolTable(numOfThreads) {} + + Engine::RelationHandle& Engine::getRelationHandle(const std::size_t idx) { + return *relations[idx]; +@@ -323,14 +306,6 @@ RamDomain Engine::incCounter() { + return counter++; + } + +-Global& Engine::getGlobal() { +- return global; +-} +- +-SymbolTable& Engine::getSymbolTable() { +- return symbolTable; +-} +- + RecordTable& Engine::getRecordTable() { + return recordTable; + } +@@ -339,6 +314,37 @@ ram::TranslationUnit& Engine::getTranslationUnit() { + return tUnit; + } + ++// TODO: Given from Gödel ++static souffle::RamDomain get_field_by_index(souffle::SymbolTable* symbolTable, souffle::RecordTable* recordTable, souffle::RamDomain arg, souffle::RamDomain total, souffle::RamDomain index) { ++ assert(symbolTable && "NULL symbol table"); ++ assert(recordTable && "NULL record table"); ++ assert(arg != 0); ++ const souffle::RamDomain* myTuple = recordTable->unpack(arg, total); ++ return myTuple[index]; ++} ++ ++static const char* godel_lang_builtin_string_getMatchResult(const char *self, const char *pattern, int index) { ++ static std::unordered_map mapper; ++ ++ std::string str(self); ++ if (!mapper.count(pattern)) { ++ mapper[pattern] = std::regex( ++ pattern, ++ std::regex_constants::ECMAScript|std::regex_constants::optimize ++ ); ++ } ++ const std::regex& re = mapper.at(pattern); ++ std::smatch sm; ++ auto r = std::regex_match(str, sm, re); ++ if (r && sm.size() > static_cast(index)) { ++ const auto& x = sm[index].str(); ++ auto buffer = new char[x.size() + 1](); ++ std::strncpy(buffer, x.data(), x.size()); ++ return buffer; ++ } ++ return ""; ++} ++ + void* Engine::getMethodHandle(const std::string& method) { + for (void* libHandle : dll) { + auto* methodHandle = dlsym(libHandle, method.c_str()); +@@ -346,6 +352,12 @@ void* Engine::getMethodHandle(const std::string& method) { + return methodHandle; + } + } ++ // TODO: Given from Gödel ++ if (method == "get_field_by_index") { ++ return reinterpret_cast(get_field_by_index); ++ } else if (method == "godel_lang_builtin_string_getMatchResult") { ++ return reinterpret_cast(godel_lang_builtin_string_getMatchResult); ++ } + return nullptr; + } + +@@ -359,13 +371,13 @@ void Engine::createRelation(const ram::Relation& id, const std::size_t idx) { + } + + RelationHandle res; +- bool hasProvenance = id.getArity() > 0 && id.getAttributeNames().back() == "@level_number"; +- if (hasProvenance) { +- res = createProvenanceRelation(id, isa.getIndexSelection(id.getName())); +- } else if (id.getRepresentation() == RelationRepresentation::EQREL) { ++ ++ if (id.getRepresentation() == RelationRepresentation::EQREL) { + res = createEqrelRelation(id, isa.getIndexSelection(id.getName())); + } else if (id.getRepresentation() == RelationRepresentation::BTREE_DELETE) { + res = createBTreeDeleteRelation(id, isa.getIndexSelection(id.getName())); ++ } else if (id.getRepresentation() == RelationRepresentation::PROVENANCE) { ++ res = createProvenanceRelation(id, isa.getIndexSelection(id.getName())); + } else { + res = createBTreeRelation(id, isa.getIndexSelection(id.getName())); + } +@@ -377,19 +389,19 @@ const std::vector& Engine::loadDLL() { + return dll; + } + +- if (!global.config().has("libraries")) { +- global.config().set("libraries", "functors"); ++ if (!Global::config().has("libraries")) { ++ Global::config().set("libraries", "functors"); + } +- if (!global.config().has("library-dir")) { +- global.config().set("library-dir", "."); ++ if (!Global::config().has("library-dir")) { ++ Global::config().set("library-dir", "."); + } + +- for (auto&& library : global.config().getMany("libraries")) { ++ for (auto&& library : Global::config().getMany("libraries")) { + // The library may be blank + if (library.empty()) { + continue; + } +- auto paths = global.config().getMany("library-dir"); ++ auto paths = Global::config().getMany("library-dir"); + // Set up our paths to have a library appended + for (std::string& path : paths) { + if (path.back() != pathSeparator) { +@@ -406,11 +418,7 @@ const std::vector& Engine::loadDLL() { + void* tmp = nullptr; + for (const std::string& path : paths) { + std::string fullpath = path + "lib" + library + dynamicLibSuffix; +-#ifndef EMSCRIPTEN + tmp = dlopen(fullpath.c_str(), RTLD_LAZY); +-#else +- tmp = nullptr; +-#endif + if (tmp != nullptr) { + dll.push_back(tmp); + break; +@@ -424,18 +432,16 @@ const std::vector& Engine::loadDLL() { + std::size_t Engine::getIterationNumber() const { + return iteration; + } +- + void Engine::incIterationNumber() { + ++iteration; + } +- + void Engine::resetIterationNumber() { + iteration = 0; + } + + void Engine::executeMain() { + SignalHandler::instance()->set(); +- if (global.config().has("verbose")) { ++ if (Global::config().has("verbose")) { + SignalHandler::instance()->enableLogging(); + } + +@@ -450,7 +456,7 @@ void Engine::executeMain() { + Context ctxt; + execute(main.get(), ctxt); + } else { +- ProfileEventSingleton::instance().setOutputFile(global.config().get("profile")); ++ ProfileEventSingleton::instance().setOutputFile(Global::config().get("profile")); + // Prepare the frequency table for threaded use + const ram::Program& program = tUnit.getProgram(); + visit(program, [&](const ram::TupleOperation& node) { +@@ -463,7 +469,7 @@ void Engine::executeMain() { + ProfileEventSingleton::instance().startTimer(); + ProfileEventSingleton::instance().makeTimeEvent("@time;starttime"); + // Store configuration +- for (auto&& [k, vs] : global.config().data()) ++ for (auto&& [k, vs] : Global::config().data()) + for (auto&& v : vs) + ProfileEventSingleton::instance().makeConfigRecord(k, v); + +@@ -482,8 +488,6 @@ void Engine::executeMain() { + visit(program, [&](const ram::Query&) { ++ruleCount; }); + ProfileEventSingleton::instance().makeConfigRecord("ruleCount", std::to_string(ruleCount)); + +- SignalHandler::instance()->enableProfiling(); +- + Context ctxt; + execute(main.get(), ctxt); + ProfileEventSingleton::instance().stopTimer(); +@@ -506,7 +510,7 @@ void Engine::generateIR() { + NodeGenerator generator(*this); + if (subroutine.empty()) { + for (const auto& sub : program.getSubroutines()) { +- subroutine.emplace(std::make_pair("stratum_" + sub.first, generator.generateTree(*sub.second))); ++ subroutine.push_back(generator.generateTree(*sub.second)); + } + } + if (main == nullptr) { +@@ -520,7 +524,10 @@ void Engine::executeSubroutine( + ctxt.setReturnValues(ret); + ctxt.setArguments(args); + generateIR(); +- execute(subroutine["stratum_" + name].get(), ctxt); ++ const ram::Program& program = tUnit.getProgram(); ++ auto subs = program.getSubroutines(); ++ std::size_t i = distance(subs.begin(), subs.find(name)); ++ execute(subroutine[i].get(), ctxt); + } + + RamDomain Engine::execute(const Node* node, Context& ctxt) { +@@ -531,9 +538,9 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + + // Overload CASE based on number of arguments. + // CASE(Kind) -> BASE_CASE(Kind) +-// CASE(Kind, Structure, Arity, AuxiliaryArity) -> EXTEND_CASE(Kind, Structure, Arity, AuxiliaryArity) +-#define GET_MACRO(_1, _2, _3, _4, NAME, ...) NAME +-#define CASE(...) GET_MACRO(__VA_ARGS__, EXTEND_CASE, _Dummy, _Dummy2, BASE_CASE)(__VA_ARGS__) ++// CASE(Kind, Structure, Arity) -> EXTEND_CASE(Kind, Structure, Arity) ++#define GET_MACRO(_1, _2, _3, NAME, ...) NAME ++#define CASE(...) GET_MACRO(__VA_ARGS__, EXTEND_CASE, _Dummy, BASE_CASE)(__VA_ARGS__) + + #define BASE_CASE(Kind) \ + case (I_##Kind): { \ +@@ -541,12 +548,12 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + [[maybe_unused]] const auto& shadow = *static_cast(node); \ + [[maybe_unused]] const auto& cur = *static_cast(node->getShadow()); + // EXTEND_CASE also defer the relation type +-#define EXTEND_CASE(Kind, Structure, Arity, AuxiliaryArity) \ +- case (I_##Kind##_##Structure##_##Arity##_##AuxiliaryArity): { \ ++#define EXTEND_CASE(Kind, Structure, Arity) \ ++ case (I_##Kind##_##Structure##_##Arity): { \ + return [&]() -> RamDomain { \ + [[maybe_unused]] const auto& shadow = *static_cast(node); \ + [[maybe_unused]] const auto& cur = *static_cast(node->getShadow());\ +- using RelType = Relation; ++ using RelType = Relation; + #define ESAC(Kind) \ + } \ + (); \ +@@ -580,10 +587,6 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return cur.getConstant(); + ESAC(NumericConstant) + +- CASE(Variable) +- return ctxt.getVariable(cur.getName()); +- ESAC(Variable) +- + CASE(StringConstant) + return shadow.getConstant(); + ESAC(StringConstant) +@@ -597,7 +600,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + ESAC(AutoIncrement) + + CASE(IntrinsicOperator) +- // clang-format off ++// clang-format off + #define BINARY_OP_TYPED(ty, op) return ramBitCast(static_cast(EVAL_CHILD(ty, 0) op EVAL_CHILD(ty, 1))) + + #define BINARY_OP_LOGICAL(opcode, op) BINARY_OP_INTEGRAL(opcode, op) +@@ -618,7 +621,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + { \ + auto result = EVAL_CHILD(RamDomain, 0); \ + auto* result_val = &getSymbolTable().decode(result); \ +- for (std::size_t i = 1; i < numArgs; i++) { \ ++ for (std::size_t i = 1; i < args.size(); i++) { \ + auto alt = EVAL_CHILD(RamDomain, i); \ + if (alt == result) continue; \ + \ +@@ -633,7 +636,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + #define MINMAX_OP(ty, op) \ + { \ + auto result = EVAL_CHILD(ty, 0); \ +- for (std::size_t i = 1; i < numArgs; i++) { \ ++ for (std::size_t i = 1; i < args.size(); i++) { \ + result = op(result, EVAL_CHILD(ty, i)); \ + } \ + return ramBitCast(result); \ +@@ -655,7 +658,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + getSymbolTable().decode(EVAL_CHILD(RamDomain, 0)))); + // clang-format on + +- const auto numArgs = cur.getNumArgs(); ++ const auto& args = cur.getArguments(); + switch (cur.getOperator()) { + /** Unary Functor Operators */ + case FunctorOp::ORD: return execute(shadow.getChild(0), ctxt); +@@ -763,7 +766,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + + case FunctorOp::CAT: { + std::stringstream ss; +- for (std::size_t i = 0; i < numArgs; i++) { ++ for (std::size_t i = 0; i < args.size(); i++) { + ss << getSymbolTable().decode(execute(shadow.getChild(i), ctxt)); + } + return getSymbolTable().encode(ss.str()); +@@ -777,7 +780,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + std::string sub_str; + try { + sub_str = str.substr(idx, len); +- } catch (std::out_of_range&) { ++ } catch (...) { + std::cerr << "warning: wrong index position provided by substr(\""; + std::cerr << str << "\"," << (int32_t)idx << "," << (int32_t)len << ") functor.\n"; + } +@@ -788,17 +791,9 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + case FunctorOp::URANGE: + case FunctorOp::FRANGE: + fatal("ICE: functor `%s` must map onto `NestedIntrinsicOperator`", cur.getOperator()); +- +- case FunctorOp::SSADD: { +- auto sleft = execute(shadow.getChild(0), ctxt); +- auto sright = execute(shadow.getChild(1), ctxt); +- const std::string& strleft = getSymbolTable().decode(sleft); +- const std::string& strright = getSymbolTable().decode(sright); +- return getSymbolTable().encode(strleft + strright); +- } + } + +- {UNREACHABLE_BAD_CASE_ANALYSIS} ++ { UNREACHABLE_BAD_CASE_ANALYSIS } + + #undef BINARY_OP_LOGICAL + #undef BINARY_OP_INTEGRAL +@@ -814,8 +809,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + ESAC(IntrinsicOperator) + + CASE(NestedIntrinsicOperator) +- const auto numArgs = cur.getNumArgs(); +- const auto runNested = [&](auto&& tuple) { ++ auto numArgs = cur.getArguments().size(); ++ auto runNested = [&](auto&& tuple) { + ctxt[cur.getTupleId()] = tuple.data(); + execute(shadow.getChild(numArgs), ctxt); + }; +@@ -832,7 +827,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + case ram::NestedIntrinsicOp::FRANGE: return RUN_RANGE(RamFloat); + } + +- {UNREACHABLE_BAD_CASE_ANALYSIS} ++ { UNREACHABLE_BAD_CASE_ANALYSIS } + #undef RUN_RANGE + ESAC(NestedIntrinsicOperator) + +@@ -841,7 +836,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + + auto userFunctor = reinterpret_cast(shadow.getFunctionPointer()); + if (userFunctor == nullptr) fatal("cannot find user-defined operator `%s`", name); +- std::size_t arity = cur.getNumArgs(); ++ std::size_t arity = cur.getArguments().size(); + + if (cur.isStateful()) { + auto exec = std::bind(&Engine::execute, this, std::placeholders::_1, std::placeholders::_2); +@@ -961,7 +956,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + ESAC(UserDefinedOperator) + + CASE(PackRecord) +- const std::size_t arity = cur.getNumArgs(); ++ auto values = cur.getArguments(); ++ std::size_t arity = values.size(); + std::unique_ptr data = std::make_unique(arity); + for (std::size_t i = 0; i < arity; ++i) { + data[i] = execute(shadow.getChild(i), ctxt); +@@ -989,8 +985,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return !execute(shadow.getChild(), ctxt); + ESAC(Negation) + +-#define EMPTINESS_CHECK(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(EmptinessCheck, Structure, Arity, AuxiliaryArity) \ ++#define EMPTINESS_CHECK(Structure, Arity, ...) \ ++ CASE(EmptinessCheck, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return rel.empty(); \ + ESAC(EmptinessCheck) +@@ -998,8 +994,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(EMPTINESS_CHECK) + #undef EMPTINESS_CHECK + +-#define RELATION_SIZE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(RelationSize, Structure, Arity, AuxiliaryArity) \ ++#define RELATION_SIZE(Structure, Arity, ...) \ ++ CASE(RelationSize, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return rel.size(); \ + ESAC(RelationSize) +@@ -1007,17 +1003,17 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(RELATION_SIZE) + #undef RELATION_SIZE + +-#define EXISTENCE_CHECK(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ExistenceCheck, Structure, Arity, AuxiliaryArity) \ +- return evalExistenceCheck(shadow, ctxt); \ ++#define EXISTENCE_CHECK(Structure, Arity, ...) \ ++ CASE(ExistenceCheck, Structure, Arity) \ ++ return evalExistenceCheck(shadow, ctxt); \ + ESAC(ExistenceCheck) + + FOR_EACH(EXISTENCE_CHECK) + #undef EXISTENCE_CHECK + +-#define PROVENANCE_EXISTENCE_CHECK(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ProvenanceExistenceCheck, Structure, Arity, AuxiliaryArity) \ +- return evalProvenanceExistenceCheck(shadow, ctxt); \ ++#define PROVENANCE_EXISTENCE_CHECK(Structure, Arity, ...) \ ++ CASE(ProvenanceExistenceCheck, Structure, Arity) \ ++ return evalProvenanceExistenceCheck(shadow, ctxt); \ + ESAC(ProvenanceExistenceCheck) + + FOR_EACH_PROVENANCE(PROVENANCE_EXISTENCE_CHECK) +@@ -1049,53 +1045,30 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + COMPARE(GE, >=) + + case BinaryConstraintOp::MATCH: { +- bool result = false; ++ RamDomain left = execute(shadow.getLhs(), ctxt); + RamDomain right = execute(shadow.getRhs(), ctxt); ++ const std::string& pattern = getSymbolTable().decode(left); + const std::string& text = getSymbolTable().decode(right); +- +- const Node* patternNode = shadow.getLhs(); +- if (const RegexConstant* regexNode = dynamic_cast(patternNode); +- regexNode) { +- const auto& regex = regexNode->getRegex(); +- if (regex) { +- result = std::regex_match(text, *regex); +- } +- } else { +- RamDomain left = execute(patternNode, ctxt); +- const std::string& pattern = getSymbolTable().decode(left); +- try { +- const std::regex& regex = regexCache.getOrCreate(pattern); +- result = std::regex_match(text, regex); +- } catch (...) { +- std::cerr << "warning: wrong pattern provided for match(\"" << pattern << "\",\"" +- << text << "\").\n"; +- } ++ bool result = false; ++ try { ++ result = std::regex_match(text, std::regex(pattern)); ++ } catch (...) { ++ std::cerr << "warning: wrong pattern provided for match(\"" << pattern << "\",\"" ++ << text << "\").\n"; + } +- + return result; + } + case BinaryConstraintOp::NOT_MATCH: { +- bool result = false; ++ RamDomain left = execute(shadow.getLhs(), ctxt); + RamDomain right = execute(shadow.getRhs(), ctxt); ++ const std::string& pattern = getSymbolTable().decode(left); + const std::string& text = getSymbolTable().decode(right); +- +- const Node* patternNode = shadow.getLhs(); +- if (const RegexConstant* regexNode = dynamic_cast(patternNode); +- regexNode) { +- const auto& regex = regexNode->getRegex(); +- if (regex) { +- result = !std::regex_match(text, *regex); +- } +- } else { +- RamDomain left = execute(patternNode, ctxt); +- const std::string& pattern = getSymbolTable().decode(left); +- try { +- const std::regex& regex = regexCache.getOrCreate(pattern); +- result = !std::regex_match(text, regex); +- } catch (...) { +- std::cerr << "warning: wrong pattern provided for !match(\"" << pattern << "\",\"" +- << text << "\").\n"; +- } ++ bool result = false; ++ try { ++ result = !std::regex_match(text, std::regex(pattern)); ++ } catch (...) { ++ std::cerr << "warning: wrong pattern provided for !match(\"" << pattern << "\",\"" ++ << text << "\").\n"; + } + return result; + } +@@ -1115,7 +1088,7 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + } + } + +- {UNREACHABLE_BAD_CASE_ANALYSIS} ++ { UNREACHABLE_BAD_CASE_ANALYSIS } + + #undef COMPARE_NUMERIC + #undef COMPARE_STRING +@@ -1138,8 +1111,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return result; + ESAC(TupleOperation) + +-#define SCAN(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(Scan, Structure, Arity, AuxiliaryArity) \ ++#define SCAN(Structure, Arity, ...) \ ++ CASE(Scan, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalScan(rel, cur, shadow, ctxt); \ + ESAC(Scan) +@@ -1147,24 +1120,24 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(SCAN) + #undef SCAN + +-#define PARALLEL_SCAN(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelScan, Structure, Arity, AuxiliaryArity) \ ++#define PARALLEL_SCAN(Structure, Arity, ...) \ ++ CASE(ParallelScan, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalParallelScan(rel, cur, shadow, ctxt); \ + ESAC(ParallelScan) + FOR_EACH(PARALLEL_SCAN) + #undef PARALLEL_SCAN + +-#define INDEX_SCAN(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(IndexScan, Structure, Arity, AuxiliaryArity) \ ++#define INDEX_SCAN(Structure, Arity, ...) \ ++ CASE(IndexScan, Structure, Arity) \ + return evalIndexScan(cur, shadow, ctxt); \ + ESAC(IndexScan) + + FOR_EACH(INDEX_SCAN) + #undef INDEX_SCAN + +-#define PARALLEL_INDEX_SCAN(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelIndexScan, Structure, Arity, AuxiliaryArity) \ ++#define PARALLEL_INDEX_SCAN(Structure, Arity, ...) \ ++ CASE(ParallelIndexScan, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalParallelIndexScan(rel, cur, shadow, ctxt); \ + ESAC(ParallelIndexScan) +@@ -1172,8 +1145,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(PARALLEL_INDEX_SCAN) + #undef PARALLEL_INDEX_SCAN + +-#define IFEXISTS(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(IfExists, Structure, Arity, AuxiliaryArity) \ ++#define IFEXISTS(Structure, Arity, ...) \ ++ CASE(IfExists, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalIfExists(rel, cur, shadow, ctxt); \ + ESAC(IfExists) +@@ -1181,8 +1154,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(IFEXISTS) + #undef IFEXISTS + +-#define PARALLEL_IFEXISTS(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelIfExists, Structure, Arity, AuxiliaryArity) \ ++#define PARALLEL_IFEXISTS(Structure, Arity, ...) \ ++ CASE(ParallelIfExists, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalParallelIfExists(rel, cur, shadow, ctxt); \ + ESAC(ParallelIfExists) +@@ -1190,16 +1163,16 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(PARALLEL_IFEXISTS) + #undef PARALLEL_IFEXISTS + +-#define INDEX_IFEXISTS(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(IndexIfExists, Structure, Arity, AuxiliaryArity) \ ++#define INDEX_IFEXISTS(Structure, Arity, ...) \ ++ CASE(IndexIfExists, Structure, Arity) \ + return evalIndexIfExists(cur, shadow, ctxt); \ + ESAC(IndexIfExists) + + FOR_EACH(INDEX_IFEXISTS) + #undef INDEX_IFEXISTS + +-#define PARALLEL_INDEX_IFEXISTS(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelIndexIfExists, Structure, Arity, AuxiliaryArity) \ ++#define PARALLEL_INDEX_IFEXISTS(Structure, Arity, ...) \ ++ CASE(ParallelIndexIfExists, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalParallelIndexIfExists(rel, cur, shadow, ctxt); \ + ESAC(ParallelIndexIfExists) +@@ -1226,8 +1199,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return execute(shadow.getNestedOperation(), ctxt); + ESAC(UnpackRecord) + +-#define PARALLEL_AGGREGATE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelAggregate, Structure, Arity, AuxiliaryArity) \ ++#define PARALLEL_AGGREGATE(Structure, Arity, ...) \ ++ CASE(ParallelAggregate, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ + return evalParallelAggregate(rel, cur, shadow, ctxt); \ + ESAC(ParallelAggregate) +@@ -1235,25 +1208,26 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(PARALLEL_AGGREGATE) + #undef PARALLEL_AGGREGATE + +-#define AGGREGATE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(Aggregate, Structure, Arity, AuxiliaryArity) \ +- const auto& rel = *static_cast(shadow.getRelation()); \ +- return evalAggregate(cur, shadow, rel.scan(), ctxt); \ ++#define AGGREGATE(Structure, Arity, ...) \ ++ CASE(Aggregate, Structure, Arity) \ ++ const auto& rel = *static_cast(shadow.getRelation()); \ ++ return evalAggregate(cur, *shadow.getCondition(), shadow.getExpr(), *shadow.getNestedOperation(), \ ++ rel.scan(), ctxt); \ + ESAC(Aggregate) + + FOR_EACH(AGGREGATE) + #undef AGGREGATE + +-#define PARALLEL_INDEX_AGGREGATE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(ParallelIndexAggregate, Structure, Arity, AuxiliaryArity) \ +- return evalParallelIndexAggregate(cur, shadow, ctxt); \ ++#define PARALLEL_INDEX_AGGREGATE(Structure, Arity, ...) \ ++ CASE(ParallelIndexAggregate, Structure, Arity) \ ++ return evalParallelIndexAggregate(cur, shadow, ctxt); \ + ESAC(ParallelIndexAggregate) + + FOR_EACH(PARALLEL_INDEX_AGGREGATE) + #undef PARALLEL_INDEX_AGGREGATE + +-#define INDEX_AGGREGATE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(IndexAggregate, Structure, Arity, AuxiliaryArity) \ ++#define INDEX_AGGREGATE(Structure, Arity, ...) \ ++ CASE(IndexAggregate, Structure, Arity) \ + return evalIndexAggregate(cur, shadow, ctxt); \ + ESAC(IndexAggregate) + +@@ -1286,8 +1260,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return result; + ESAC(Filter) + +-#define GUARDED_INSERT(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(GuardedInsert, Structure, Arity, AuxiliaryArity) \ ++#define GUARDED_INSERT(Structure, Arity, ...) \ ++ CASE(GuardedInsert, Structure, Arity) \ + auto& rel = *static_cast(shadow.getRelation()); \ + return evalGuardedInsert(rel, shadow, ctxt); \ + ESAC(GuardedInsert) +@@ -1295,8 +1269,8 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(GUARDED_INSERT) + #undef GUARDED_INSERT + +-#define INSERT(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(Insert, Structure, Arity, AuxiliaryArity) \ ++#define INSERT(Structure, Arity, ...) \ ++ CASE(Insert, Structure, Arity) \ + auto& rel = *static_cast(shadow.getRelation()); \ + return evalInsert(rel, shadow, ctxt); \ + ESAC(Insert) +@@ -1304,18 +1278,18 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + FOR_EACH(INSERT) + #undef INSERT + +-#define ERASE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(Erase, Structure, Arity, AuxiliaryArity) \ +- void(static_cast(shadow.getRelation())); \ +- auto& rel = *static_cast*>(shadow.getRelation()); \ +- return evalErase(rel, shadow, ctxt); \ ++#define ERASE(Structure, Arity, ...) \ ++ CASE(Erase, Structure, Arity) \ ++ void(static_cast(shadow.getRelation())); \ ++ auto& rel = *static_cast*>(shadow.getRelation()); \ ++ return evalErase(rel, shadow, ctxt); \ + ESAC(Erase) + + FOR_EACH_BTREE_DELETE(ERASE) + #undef ERASE + + CASE(SubroutineReturn) +- for (std::size_t i = 0; i < cur.getNumValues(); ++i) { ++ for (std::size_t i = 0; i < cur.getValues().size(); ++i) { + if (shadow.getChild(i) == nullptr) { + ctxt.addReturnValue(0); + } else { +@@ -1345,11 +1319,9 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + + CASE(Loop) + resetIterationNumber(); +- + while (execute(shadow.getChild(), ctxt)) { + incIterationNumber(); + } +- + resetIterationNumber(); + return true; + ESAC(Loop) +@@ -1374,23 +1346,27 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + return execute(shadow.getChild(), ctxt); + ESAC(DebugInfo) + +- CASE(Clear) +- auto* rel = shadow.getRelation(); +- rel->purge(); +- return true; +- ESAC(Clear) ++#define CLEAR(Structure, Arity, ...) \ ++ CASE(Clear, Structure, Arity) \ ++ auto& rel = *static_cast(shadow.getRelation()); \ ++ rel.__purge(); \ ++ return true; \ ++ ESAC(Clear) + +-#define ESTIMATEJOINSIZE(Structure, Arity, AuxiliaryArity, ...) \ +- CASE(EstimateJoinSize, Structure, Arity, AuxiliaryArity) \ ++ FOR_EACH(CLEAR) ++#undef CLEAR ++ ++#define COUNTUNIQUEKEYS(Structure, Arity, ...) \ ++ CASE(CountUniqueKeys, Structure, Arity) \ + const auto& rel = *static_cast(shadow.getRelation()); \ +- return evalEstimateJoinSize(rel, cur, shadow, ctxt); \ +- ESAC(EstimateJoinSize) ++ return evalCountUniqueKeys(rel, cur, shadow, ctxt); \ ++ ESAC(CountUniqueKeys) + +- FOR_EACH(ESTIMATEJOINSIZE) +-#undef ESTIMATEJOINSIZE ++ FOR_EACH(COUNTUNIQUEKEYS) ++#undef COUNTUNIQUEKEYS + + CASE(Call) +- execute(subroutine[shadow.getSubroutineName()].get(), ctxt); ++ execute(subroutine[shadow.getSubroutineId()].get(), ctxt); + return true; + ESAC(Call) + +@@ -1413,7 +1389,6 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + ->readAll(rel); + } catch (std::exception& e) { + std::cerr << "Error loading " << rel.getName() << " data: " << e.what() << "\n"; +- exit(EXIT_FAILURE); + } + return true; + } else if (op == "output" || op == "printsize") { +@@ -1481,13 +1456,6 @@ RamDomain Engine::execute(const Node* node, Context& ctxt) { + swapRelation(shadow.getSourceId(), shadow.getTargetId()); + return true; + ESAC(Swap) +- +- CASE(Assign) +- const std::string& name = cur.getVariable().getName(); +- const RamDomain val = execute(shadow.getRhs(), ctxt); +- ctxt.setVariable(name, val); +- return true; +- ESAC(Assign) + } + + UNREACHABLE_BAD_CASE_ANALYSIS +@@ -1602,7 +1570,7 @@ RamDomain Engine::evalParallelScan( + const Rel& rel, const ram::ParallelScan& cur, const ParallelScan& shadow, Context& ctxt) { + auto viewContext = shadow.getViewContext(); + +- auto pStream = rel.partitionScan(numOfThreads * 20); ++ auto pStream = rel.partitionScan(numOfThreads); + + PARALLEL_START + Context newCtxt(ctxt); +@@ -1630,8 +1598,8 @@ RamDomain Engine::evalParallelScan( + } + + template +-RamDomain Engine::evalEstimateJoinSize( +- const Rel& rel, const ram::EstimateJoinSize& cur, const EstimateJoinSize& shadow, Context& ctxt) { ++RamDomain Engine::evalCountUniqueKeys( ++ const Rel& rel, const ram::CountUniqueKeys& cur, const CountUniqueKeys& shadow, Context& ctxt) { + (void)ctxt; + constexpr std::size_t Arity = Rel::Arity; + bool onlyConstants = true; +@@ -1681,8 +1649,8 @@ RamDomain Engine::evalEstimateJoinSize( + // ensure range is non-empty + auto* index = rel.getIndex(indexPos); + // initial values +- double total = 0; +- double duplicates = 0; ++ std::size_t total = 0; ++ std::size_t duplicates = 0; + + if (!index->scan().empty()) { + // assign first tuple as prev as a dummy +@@ -1710,14 +1678,14 @@ RamDomain Engine::evalEstimateJoinSize( + ++total; + } + } +- double joinSize = (onlyConstants ? total : total / std::max(1.0, (total - duplicates))); ++ std::size_t uniqueKeys = (onlyConstants ? total : total - duplicates); + + std::stringstream columnsStream; + columnsStream << cur.getKeyColumns(); + std::string columns = columnsStream.str(); + + std::stringstream constantsStream; +- constantsStream << "{"; ++ constantsStream << "["; + bool first = true; + for (auto& [k, constant] : cur.getConstantsMap()) { + if (first) { +@@ -1727,18 +1695,18 @@ RamDomain Engine::evalEstimateJoinSize( + } + constantsStream << k << "->" << *constant; + } +- constantsStream << "}"; ++ constantsStream << "]"; + + std::string constants = stringify(constantsStream.str()); + + if (cur.isRecursiveRelation()) { + std::string txt = +- "@recursive-estimate-join-size;" + cur.getRelation() + ";" + columns + ";" + constants; +- ProfileEventSingleton::instance().makeRecursiveCountEvent(txt, joinSize, getIterationNumber()); ++ "@recursive-count-unique-keys;" + cur.getRelation() + ";" + columns + ";" + constants; ++ ProfileEventSingleton::instance().makeRecursiveCountEvent(txt, uniqueKeys, getIterationNumber()); + } else { + std::string txt = +- "@non-recursive-estimate-join-size;" + cur.getRelation() + ";" + columns + ";" + constants; +- ProfileEventSingleton::instance().makeNonRecursiveCountEvent(txt, joinSize); ++ "@non-recursive-count-unique-keys;" + cur.getRelation() + ";" + columns + ";" + constants; ++ ProfileEventSingleton::instance().makeNonRecursiveCountEvent(txt, uniqueKeys); + } + return true; + } +@@ -1777,7 +1745,7 @@ RamDomain Engine::evalParallelIndexScan( + CAL_SEARCH_BOUND(superInfo, low, high); + + std::size_t indexPos = shadow.getViewId(); +- auto pStream = rel.partitionRange(indexPos, low, high, numOfThreads * 20); ++ auto pStream = rel.partitionRange(indexPos, low, high, numOfThreads); + PARALLEL_START + Context newCtxt(ctxt); + auto viewInfo = viewContext->getViewInfoForNested(); +@@ -1822,7 +1790,7 @@ RamDomain Engine::evalParallelIfExists( + const Rel& rel, const ram::ParallelIfExists& cur, const ParallelIfExists& shadow, Context& ctxt) { + auto viewContext = shadow.getViewContext(); + +- auto pStream = rel.partitionScan(numOfThreads * 20); ++ auto pStream = rel.partitionScan(numOfThreads); + auto viewInfo = viewContext->getViewInfoForNested(); + PARALLEL_START + Context newCtxt(ctxt); +@@ -1886,7 +1854,7 @@ RamDomain Engine::evalParallelIndexIfExists(const Rel& rel, const ram::ParallelI + CAL_SEARCH_BOUND(superInfo, low, high); + + std::size_t indexPos = shadow.getViewId(); +- auto pStream = rel.partitionRange(indexPos, low, high, numOfThreads * 20); ++ auto pStream = rel.partitionRange(indexPos, low, high, numOfThreads); + + PARALLEL_START + Context newCtxt(ctxt); +@@ -1914,68 +1882,49 @@ RamDomain Engine::evalParallelIndexIfExists(const Rel& rel, const ram::ParallelI + return true; + } + +-template +-RamDomain Engine::initValue(const ram::Aggregator& aggregator, const Shadow& shadow, Context& ctxt) { +- if (const auto* ia = as(aggregator)) { +- switch (ia->getFunction()) { +- case AggregateOp::MIN: return ramBitCast(MAX_RAM_SIGNED); +- case AggregateOp::UMIN: return ramBitCast(MAX_RAM_UNSIGNED); +- case AggregateOp::FMIN: return ramBitCast(MAX_RAM_FLOAT); +- case AggregateOp::MAX: return ramBitCast(MIN_RAM_SIGNED); +- case AggregateOp::UMAX: return ramBitCast(MIN_RAM_UNSIGNED); +- case AggregateOp::FMAX: return ramBitCast(MIN_RAM_FLOAT); +- case AggregateOp::SUM: return ramBitCast(static_cast(0)); +- case AggregateOp::USUM: return ramBitCast(static_cast(0)); +- case AggregateOp::FSUM: return ramBitCast(static_cast(0)); +- case AggregateOp::MEAN: return 0; +- case AggregateOp::COUNT: return 0; +- } +- } else if (isA(aggregator)) { +- return execute(shadow.getInit(), ctxt); +- } +- fatal("Unhandled aggregator"); +-} +- +-bool runNested(const ram::Aggregator& aggregator) { +- if (const auto* ia = as(aggregator)) { +- switch (ia->getFunction()) { +- case AggregateOp::COUNT: +- case AggregateOp::FSUM: +- case AggregateOp::USUM: +- case AggregateOp::SUM: return true; +- default: return false; +- } +- } else if (isA(aggregator)) { +- return true; +- } +- return false; +-} +- +-void ifIntrinsic(const ram::Aggregator& aggregator, AggregateOp op, std::function fn) { +- if (const auto* ia = as(aggregator)) { +- if (ia->getFunction() == op) { +- fn(); +- }; +- } +-} +- +-template +-RamDomain Engine::evalAggregate( +- const Aggregate& aggregate, const Shadow& shadow, const Iter& ranges, Context& ctxt) { ++template ++RamDomain Engine::evalAggregate(const Aggregate& aggregate, const Node& filter, const Node* expression, ++ const Node& nestedOperation, const Iter& ranges, Context& ctxt) { + bool shouldRunNested = false; + +- const Node& filter = *shadow.getCondition(); +- const Node* expression = shadow.getExpr(); +- const Node& nestedOperation = *shadow.getNestedOperation(); + // initialize result + RamDomain res = 0; + + // Use for calculating mean. +- std::pair accumulateMean = {0, 0}; ++ std::pair accumulateMean; ++ ++ switch (aggregate.getFunction()) { ++ case AggregateOp::MIN: res = ramBitCast(MAX_RAM_SIGNED); break; ++ case AggregateOp::UMIN: res = ramBitCast(MAX_RAM_UNSIGNED); break; ++ case AggregateOp::FMIN: res = ramBitCast(MAX_RAM_FLOAT); break; ++ ++ case AggregateOp::MAX: res = ramBitCast(MIN_RAM_SIGNED); break; ++ case AggregateOp::UMAX: res = ramBitCast(MIN_RAM_UNSIGNED); break; ++ case AggregateOp::FMAX: res = ramBitCast(MIN_RAM_FLOAT); break; ++ ++ case AggregateOp::SUM: ++ res = ramBitCast(static_cast(0)); ++ shouldRunNested = true; ++ break; ++ case AggregateOp::USUM: ++ res = ramBitCast(static_cast(0)); ++ shouldRunNested = true; ++ break; ++ case AggregateOp::FSUM: ++ res = ramBitCast(static_cast(0)); ++ shouldRunNested = true; ++ break; + +- const ram::Aggregator& aggregator = aggregate.getAggregator(); +- res = initValue(aggregator, shadow, ctxt); +- shouldRunNested = runNested(aggregator); ++ case AggregateOp::MEAN: ++ res = 0; ++ accumulateMean = {0, 0}; ++ break; ++ ++ case AggregateOp::COUNT: ++ res = 0; ++ shouldRunNested = true; ++ break; ++ } + + for (const auto& tuple : ranges) { + ctxt[aggregate.getTupleId()] = tuple.data(); +@@ -1986,11 +1935,8 @@ RamDomain Engine::evalAggregate( + + shouldRunNested = true; + +- bool isCount = false; +- ifIntrinsic(aggregator, AggregateOp::COUNT, [&]() { isCount = true; }); +- + // count is a special case. +- if (isCount) { ++ if (aggregate.getFunction() == AggregateOp::COUNT) { + ++res; + continue; + } +@@ -1999,56 +1945,43 @@ RamDomain Engine::evalAggregate( + assert(expression); // only case where this is null is `COUNT` + RamDomain val = execute(expression, ctxt); + +- if (const auto* ia = as(aggregator)) { +- switch (ia->getFunction()) { +- case AggregateOp::MIN: res = std::min(res, val); break; +- case AggregateOp::FMIN: +- res = ramBitCast(std::min(ramBitCast(res), ramBitCast(val))); +- break; +- case AggregateOp::UMIN: +- res = ramBitCast(std::min(ramBitCast(res), ramBitCast(val))); +- break; ++ switch (aggregate.getFunction()) { ++ case AggregateOp::MIN: res = std::min(res, val); break; ++ case AggregateOp::FMIN: ++ res = ramBitCast(std::min(ramBitCast(res), ramBitCast(val))); ++ break; ++ case AggregateOp::UMIN: ++ res = ramBitCast(std::min(ramBitCast(res), ramBitCast(val))); ++ break; + +- case AggregateOp::MAX: res = std::max(res, val); break; +- case AggregateOp::FMAX: +- res = ramBitCast(std::max(ramBitCast(res), ramBitCast(val))); +- break; +- case AggregateOp::UMAX: +- res = ramBitCast(std::max(ramBitCast(res), ramBitCast(val))); +- break; ++ case AggregateOp::MAX: res = std::max(res, val); break; ++ case AggregateOp::FMAX: ++ res = ramBitCast(std::max(ramBitCast(res), ramBitCast(val))); ++ break; ++ case AggregateOp::UMAX: ++ res = ramBitCast(std::max(ramBitCast(res), ramBitCast(val))); ++ break; + +- case AggregateOp::SUM: res += val; break; +- case AggregateOp::FSUM: +- res = ramBitCast(ramBitCast(res) + ramBitCast(val)); +- break; +- case AggregateOp::USUM: +- res = ramBitCast(ramBitCast(res) + ramBitCast(val)); +- break; ++ case AggregateOp::SUM: res += val; break; ++ case AggregateOp::FSUM: ++ res = ramBitCast(ramBitCast(res) + ramBitCast(val)); ++ break; ++ case AggregateOp::USUM: ++ res = ramBitCast(ramBitCast(res) + ramBitCast(val)); ++ break; + +- case AggregateOp::MEAN: +- accumulateMean.first += ramBitCast(val); +- accumulateMean.second++; +- break; ++ case AggregateOp::MEAN: ++ accumulateMean.first += ramBitCast(val); ++ accumulateMean.second++; ++ break; + +- case AggregateOp::COUNT: fatal("This should never be executed"); +- } +- } else if (const auto* uda = as(aggregator)) { +- auto userFunctorPtr = reinterpret_cast(shadow.getFunctionPointer()); +- if (uda->isStateful() && userFunctorPtr) { +- res = callStatefulAggregate(userFunctorPtr, &getSymbolTable(), &getRecordTable(), res, val); +- } else { +- fatal("stateless functors not supported in user-defined aggregates"); +- } +- } else { +- fatal("Unhandled aggregator"); ++ case AggregateOp::COUNT: fatal("This should never be executed"); + } + } + +- ifIntrinsic(aggregator, AggregateOp::MEAN, [&]() { +- if (accumulateMean.second != 0) { +- res = ramBitCast(accumulateMean.first / accumulateMean.second); +- } +- }); ++ if (aggregate.getFunction() == AggregateOp::MEAN && accumulateMean.second != 0) { ++ res = ramBitCast(accumulateMean.first / accumulateMean.second); ++ } + + // write result to environment + souffle::Tuple tuple; +@@ -2072,7 +2005,8 @@ RamDomain Engine::evalParallelAggregate( + for (const auto& info : viewInfo) { + newCtxt.createView(*getRelationHandle(info[0]), info[1], info[2]); + } +- return evalAggregate(cur, shadow, rel.scan(), newCtxt); ++ return evalAggregate( ++ cur, *shadow.getCondition(), shadow.getExpr(), *shadow.getNestedOperation(), rel.scan(), newCtxt); + } + + template +@@ -2097,7 +2031,8 @@ RamDomain Engine::evalParallelIndexAggregate( + std::size_t viewId = shadow.getViewId(); + auto view = Rel::castView(newCtxt.getView(viewId)); + +- return evalAggregate(cur, shadow, view->range(low, high), newCtxt); ++ return evalAggregate(cur, *shadow.getCondition(), shadow.getExpr(), *shadow.getNestedOperation(), ++ view->range(low, high), newCtxt); + } + + template +@@ -2113,7 +2048,8 @@ RamDomain Engine::evalIndexAggregate( + std::size_t viewId = shadow.getViewId(); + auto view = Rel::castView(ctxt.getView(viewId)); + +- return evalAggregate(cur, shadow, view->range(low, high), ctxt); ++ return evalAggregate(cur, *shadow.getCondition(), shadow.getExpr(), *shadow.getNestedOperation(), ++ view->range(low, high), ctxt); + } + + template +diff --git a/src/interpreter/Engine.h b/src/interpreter/Engine.h +index 22293f1..1e3ab30 100644 +--- a/src/interpreter/Engine.h ++++ b/src/interpreter/Engine.h +@@ -27,7 +27,6 @@ + #include "souffle/RamTypes.h" + #include "souffle/RecordTable.h" + #include "souffle/SymbolTable.h" +-#include "souffle/datastructure/ConcurrentCache.h" + #include "souffle/datastructure/RecordTableImpl.h" + #include "souffle/datastructure/SymbolTableImpl.h" + #include "souffle/utility/ContainerUtil.h" +@@ -36,7 +35,6 @@ + #include + #include + #include +-#include + #include + #include + #ifdef _OPENMP +@@ -57,24 +55,14 @@ class Engine { + friend NodeGenerator; + + public: +- Engine(ram::TranslationUnit& tUnit, const std::size_t numThreads); ++ Engine(ram::TranslationUnit& tUnit); + + /** @brief Execute the main program */ + void executeMain(); +- + /** @brief Execute the subroutine program */ + void executeSubroutine( + const std::string& name, const std::vector& args, std::vector& ret); + +- /** @brief Return the global object this engine uses */ +- Global& getGlobal(); +- +- /** @brief Return the string symbol table */ +- SymbolTable& getSymbolTable(); +- +- /** @brief Return the record table */ +- RecordTable& getRecordTable(); +- + private: + /** @brief Generate intermediate representation from RAM */ + void generateIR(); +@@ -84,9 +72,15 @@ private: + void swapRelation(const std::size_t ramRel1, const std::size_t ramRel2); + /** @brief Return a reference to the relation on the given index */ + RelationHandle& getRelationHandle(const std::size_t idx); ++ /** @brief Return the string symbol table */ ++ SymbolTable& getSymbolTable() { ++ return symbolTable; ++ } ++ /** @brief Return the record table */ ++ RecordTable& getRecordTable(); + /** @brief Return the ram::TranslationUnit */ + ram::TranslationUnit& getTranslationUnit(); +- /** @brief Execute a specific node program */ ++ /** @brief Execute the program */ + RamDomain execute(const Node*, Context&); + /** @brief Return method handler */ + void* getMethodHandle(const std::string& method); +@@ -120,8 +114,8 @@ private: + const Rel& rel, const ram::ParallelScan& cur, const ParallelScan& shadow, Context& ctxt); + + template +- RamDomain evalEstimateJoinSize( +- const Rel& rel, const ram::EstimateJoinSize& cur, const EstimateJoinSize& shadow, Context& ctxt); ++ RamDomain evalCountUniqueKeys( ++ const Rel& rel, const ram::CountUniqueKeys& cur, const CountUniqueKeys& shadow, Context& ctxt); + + template + RamDomain evalIndexScan(const ram::IndexScan& cur, const IndexScan& shadow, Context& ctxt); +@@ -144,12 +138,9 @@ private: + RamDomain evalParallelIndexIfExists(const Rel& rel, const ram::ParallelIndexIfExists& cur, + const ParallelIndexIfExists& shadow, Context& ctxt); + +- template +- RamDomain initValue(const ram::Aggregator& aggregator, const Shadow& shadow, Context& ctxt); +- +- template +- RamDomain evalAggregate( +- const Aggregate& aggregate, const Shadow& shadow, const Iter& ranges, Context& ctxt); ++ template ++ RamDomain evalAggregate(const Aggregate& aggregate, const Node& filter, const Node* expression, ++ const Node& nestedOperation, const Iter& ranges, Context& ctxt); + + template + RamDomain evalParallelAggregate(const Rel& rel, const ram::ParallelAggregate& cur, +@@ -171,15 +162,11 @@ private: + template + RamDomain evalErase(Rel& rel, const Erase& shadow, Context& ctxt); + +- /** Program */ +- ram::TranslationUnit& tUnit; +- /** Global */ +- Global& global; + /** If profile is enable in this program */ + const bool profileEnabled; + const bool frequencyCounterEnabled; + /** subroutines */ +- std::map> subroutine; ++ VecOwn subroutine; + /** main program */ + Own main; + /** Number of threads enabled for this program */ +@@ -194,6 +181,8 @@ private: + std::map> reads; + /** DLL */ + std::vector dll; ++ /** Program */ ++ ram::TranslationUnit& tUnit; + /** IndexAnalysis */ + ram::analysis::IndexAnalysis& isa; + /** Record Table Implementation*/ +@@ -202,8 +191,6 @@ private: + VecOwn relations; + /** Symbol table */ + SymbolTableImpl symbolTable; +- /** A cache for regexes */ +- ConcurrentCache regexCache; + }; + + } // namespace souffle::interpreter +diff --git a/src/interpreter/EqrelIndex.cpp b/src/interpreter/EqrelIndex.cpp +index b6aeef1..7a368c1 100644 +--- a/src/interpreter/EqrelIndex.cpp ++++ b/src/interpreter/EqrelIndex.cpp +@@ -23,8 +23,7 @@ namespace souffle::interpreter { + Own createEqrelRelation( + const ram::Relation& id, const ram::analysis::IndexCluster& indexSelection) { + assert(id.getArity() == 2 && "Eqivalence relation must have arity size 2."); +- assert(id.getAuxiliaryArity() == 0 && "Equivalence relation must have auxiliary arity size 0."); +- return mk(id.getName(), indexSelection); ++ return mk(id.getAuxiliaryArity(), id.getName(), indexSelection); + } + + } // namespace souffle::interpreter +diff --git a/src/interpreter/Generator.cpp b/src/interpreter/Generator.cpp +index 07e134a..4dbbdd7 100644 +--- a/src/interpreter/Generator.cpp ++++ b/src/interpreter/Generator.cpp +@@ -15,7 +15,6 @@ + + #include "interpreter/Generator.h" + #include "interpreter/Engine.h" +-#include "ram/UserDefinedAggregator.h" + + namespace souffle::interpreter { + +@@ -23,7 +22,7 @@ using NodePtr = Own; + using NodePtrVec = std::vector; + using RelationHandle = Own; + +-NodeGenerator::NodeGenerator(Engine& engine) : engine(engine), global(engine.getGlobal()) { ++NodeGenerator::NodeGenerator(Engine& engine) : engine(engine) { + visit(engine.tUnit.getProgram(), [&](const ram::Relation& relation) { + assert(relationMap.find(relation.getName()) == relationMap.end() && "double-naming of relations"); + relationMap[relation.getName()] = &relation; +@@ -36,9 +35,9 @@ NodePtr NodeGenerator::generateTree(const ram::Node& root) { + if (isA(&node)) { + newQueryBlock(); + } +- if (const auto* estimateJoinSize = as(node)) { +- encodeIndexPos(*estimateJoinSize); +- encodeView(estimateJoinSize); ++ if (const auto* countUniqueKeys = as(node)) { ++ encodeIndexPos(*countUniqueKeys); ++ encodeView(countUniqueKeys); + } else if (const auto* indexSearch = as(node)) { + encodeIndexPos(*indexSearch); + encodeView(indexSearch); +@@ -63,10 +62,6 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Nu + return mk(I_NumericConstant, &num); + } + +-NodePtr NodeGenerator::visit_(type_identity, const ram::Variable& var) { +- return mk(I_Variable, &var); +-} +- + NodePtr NodeGenerator::visit_(type_identity, const ram::TupleElement& access) { + auto tupleId = access.getTupleId(); + auto elementId = access.getElement(); +@@ -210,14 +205,14 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Negation& + NodePtr NodeGenerator::visit_(type_identity, const ram::EmptinessCheck& emptiness) { + std::size_t relId = encodeRelation(emptiness.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "EmptinessCheck", lookup(emptiness.getRelation())); ++ NodeType type = constructNodeType("EmptinessCheck", lookup(emptiness.getRelation())); + return mk(type, &emptiness, rel); + } + + NodePtr NodeGenerator::visit_(type_identity, const ram::RelationSize& size) { + std::size_t relId = encodeRelation(size.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "RelationSize", lookup(size.getRelation())); ++ NodeType type = constructNodeType("RelationSize", lookup(size.getRelation())); + return mk(type, &size, rel); + } + +@@ -231,7 +226,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Exi + } + } + const auto& ramRelation = lookup(exists.getRelation()); +- NodeType type = constructNodeType(global, "ExistenceCheck", ramRelation); ++ NodeType type = constructNodeType("ExistenceCheck", ramRelation); + return mk(type, &exists, isTotal, encodeView(&exists), std::move(superOp), + ramRelation.isTemp(), ramRelation.getName()); + } +@@ -239,35 +234,13 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Exi + NodePtr NodeGenerator::visit_( + type_identity, const ram::ProvenanceExistenceCheck& provExists) { + SuperInstruction superOp = getExistenceSuperInstInfo(provExists); +- NodeType type = constructNodeType(global, "ProvenanceExistenceCheck", lookup(provExists.getRelation())); ++ NodeType type = constructNodeType("ProvenanceExistenceCheck", lookup(provExists.getRelation())); + return mk(type, &provExists, dispatch(*(--provExists.getChildNodes().end())), + encodeView(&provExists), std::move(superOp)); + } + + NodePtr NodeGenerator::visit_(type_identity, const ram::Constraint& relOp) { +- auto left = dispatch(relOp.getLHS()); +- auto right = dispatch(relOp.getRHS()); +- switch (relOp.getOperator()) { +- case BinaryConstraintOp::MATCH: +- case BinaryConstraintOp::NOT_MATCH: +- if (const StringConstant* str = dynamic_cast(left.get()); str) { +- const std::string& pattern = engine.getSymbolTable().unsafeDecode(str->getConstant()); +- try { +- std::regex regex(pattern); +- // treat the string constant as a regex +- left = mk(*str, std::move(regex)); +- } catch (const std::exception&) { +- std::cerr << "warning: wrong pattern provided \"" << pattern << "\"\n"; +- +- // we could not compile the pattern +- left = mk(*str, std::nullopt); +- } +- } +- break; +- default: break; +- } +- +- return mk(I_Constraint, &relOp, std::move(left), std::move(right)); ++ return mk(I_Constraint, &relOp, dispatch(relOp.getLHS()), dispatch(relOp.getRHS())); + } + + NodePtr NodeGenerator::visit_(type_identity, const ram::NestedOperation& nested) { +@@ -285,7 +258,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Scan& scan) { + orderingContext.addTupleWithDefaultOrder(scan.getTupleId(), scan); + std::size_t relId = encodeRelation(scan.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "Scan", lookup(scan.getRelation())); ++ NodeType type = constructNodeType("Scan", lookup(scan.getRelation())); + return mk(type, &scan, rel, visit_(type_identity(), scan)); + } + +@@ -293,7 +266,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Paral + orderingContext.addTupleWithDefaultOrder(pScan.getTupleId(), pScan); + std::size_t relId = encodeRelation(pScan.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelScan", lookup(pScan.getRelation())); ++ NodeType type = constructNodeType("ParallelScan", lookup(pScan.getRelation())); + auto res = mk(type, &pScan, rel, visit_(type_identity(), pScan)); + res->setViewContext(parentQueryViewContext); + return res; +@@ -302,7 +275,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Paral + NodePtr NodeGenerator::visit_(type_identity, const ram::IndexScan& iScan) { + orderingContext.addTupleWithIndexOrder(iScan.getTupleId(), iScan); + SuperInstruction indexOperation = getIndexSuperInstInfo(iScan); +- NodeType type = constructNodeType(global, "IndexScan", lookup(iScan.getRelation())); ++ NodeType type = constructNodeType("IndexScan", lookup(iScan.getRelation())); + return mk(type, &iScan, nullptr, visit_(type_identity(), iScan), + encodeView(&iScan), std::move(indexOperation)); + } +@@ -312,7 +285,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram:: + SuperInstruction indexOperation = getIndexSuperInstInfo(piscan); + std::size_t relId = encodeRelation(piscan.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelIndexScan", lookup(piscan.getRelation())); ++ NodeType type = constructNodeType("ParallelIndexScan", lookup(piscan.getRelation())); + auto res = mk(type, &piscan, rel, visit_(type_identity(), piscan), + encodeIndexPos(piscan), std::move(indexOperation)); + res->setViewContext(parentQueryViewContext); +@@ -323,7 +296,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::IfExists& + orderingContext.addTupleWithDefaultOrder(ifexists.getTupleId(), ifexists); + std::size_t relId = encodeRelation(ifexists.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "IfExists", lookup(ifexists.getRelation())); ++ NodeType type = constructNodeType("IfExists", lookup(ifexists.getRelation())); + return mk(type, &ifexists, rel, dispatch(ifexists.getCondition()), + visit_(type_identity(), ifexists)); + } +@@ -332,7 +305,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::P + orderingContext.addTupleWithDefaultOrder(pIfExists.getTupleId(), pIfExists); + std::size_t relId = encodeRelation(pIfExists.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelIfExists", lookup(pIfExists.getRelation())); ++ NodeType type = constructNodeType("ParallelIfExists", lookup(pIfExists.getRelation())); + auto res = mk(type, &pIfExists, rel, dispatch(pIfExists.getCondition()), + visit_(type_identity(), pIfExists)); + res->setViewContext(parentQueryViewContext); +@@ -342,7 +315,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::P + NodePtr NodeGenerator::visit_(type_identity, const ram::IndexIfExists& iIfExists) { + orderingContext.addTupleWithIndexOrder(iIfExists.getTupleId(), iIfExists); + SuperInstruction indexOperation = getIndexSuperInstInfo(iIfExists); +- NodeType type = constructNodeType(global, "IndexIfExists", lookup(iIfExists.getRelation())); ++ NodeType type = constructNodeType("IndexIfExists", lookup(iIfExists.getRelation())); + return mk(type, &iIfExists, nullptr, dispatch(iIfExists.getCondition()), + visit_(type_identity(), iIfExists), encodeView(&iIfExists), + std::move(indexOperation)); +@@ -354,7 +327,7 @@ NodePtr NodeGenerator::visit_( + SuperInstruction indexOperation = getIndexSuperInstInfo(piIfExists); + std::size_t relId = encodeRelation(piIfExists.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelIndexIfExists", lookup(piIfExists.getRelation())); ++ NodeType type = constructNodeType("ParallelIndexIfExists", lookup(piIfExists.getRelation())); + auto res = mk(type, &piIfExists, rel, dispatch(piIfExists.getCondition()), + dispatch(piIfExists.getOperation()), encodeIndexPos(piIfExists), std::move(indexOperation)); + res->setViewContext(parentQueryViewContext); +@@ -368,65 +341,33 @@ NodePtr NodeGenerator::visit_( + visit_(type_identity(), unpack)); + } + +-NodePtr NodeGenerator::mkInit(const ram::AbstractAggregate& aggregate) { +- const ram::Aggregator& aggregator = aggregate.getAggregator(); +- if (const auto* uda = as(aggregator)) { +- return dispatch(*uda->getInitValue()); +- } else { +- return nullptr; +- } +-}; +- +-void* NodeGenerator::resolveFunctionPointers(const ram::AbstractAggregate& aggregate) { +- const ram::Aggregator& aggregator = aggregate.getAggregator(); +- if (const auto* uda = as(aggregator)) { +- void* functionPtr = engine.getMethodHandle(uda->getName()); +- if (functionPtr == nullptr) { +- fatal("cannot find user-defined operator `%s`", uda->getName()); +- } +- return functionPtr; +- } else { +- // Intrinsic aggregates do not need function pointers. +- return nullptr; +- } +-} +- + NodePtr NodeGenerator::visit_(type_identity, const ram::Aggregate& aggregate) { + // Notice: Aggregate is sensitive to the visiting order of the subexprs in order to make + // orderCtxt consistent. The order of visiting should be the same as the order of execution during + // runtime. + orderingContext.addTupleWithDefaultOrder(aggregate.getTupleId(), aggregate); +- NodePtr init = mkInit(aggregate); + NodePtr expr = dispatch(aggregate.getExpression()); + NodePtr cond = dispatch(aggregate.getCondition()); + orderingContext.addNewTuple(aggregate.getTupleId(), 1); + NodePtr nested = visit_(type_identity(), aggregate); + std::size_t relId = encodeRelation(aggregate.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "Aggregate", lookup(aggregate.getRelation())); +- +- /* Resolve functor to actual function pointer now */ +- void* functionPtr = resolveFunctionPointers(aggregate); +- +- return mk(type, &aggregate, rel, std::move(expr), std::move(cond), std::move(nested), +- std::move(init), functionPtr); ++ NodeType type = constructNodeType("Aggregate", lookup(aggregate.getRelation())); ++ return mk(type, &aggregate, rel, std::move(expr), std::move(cond), std::move(nested)); + } + + NodePtr NodeGenerator::visit_( + type_identity, const ram::ParallelAggregate& pAggregate) { + orderingContext.addTupleWithDefaultOrder(pAggregate.getTupleId(), pAggregate); +- NodePtr init = mkInit(pAggregate); + NodePtr expr = dispatch(pAggregate.getExpression()); + NodePtr cond = dispatch(pAggregate.getCondition()); + orderingContext.addNewTuple(pAggregate.getTupleId(), 1); + NodePtr nested = visit_(type_identity(), pAggregate); + std::size_t relId = encodeRelation(pAggregate.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelAggregate", lookup(pAggregate.getRelation())); +- /* Resolve functor to actual function pointer now */ +- void* functionPtr = resolveFunctionPointers(pAggregate); +- auto res = mk(type, &pAggregate, rel, std::move(expr), std::move(cond), +- std::move(nested), std::move(init), functionPtr); ++ NodeType type = constructNodeType("ParallelAggregate", lookup(pAggregate.getRelation())); ++ auto res = mk( ++ type, &pAggregate, rel, std::move(expr), std::move(cond), std::move(nested)); + res->setViewContext(parentQueryViewContext); + + return res; +@@ -435,37 +376,30 @@ NodePtr NodeGenerator::visit_( + NodePtr NodeGenerator::visit_(type_identity, const ram::IndexAggregate& iAggregate) { + orderingContext.addTupleWithIndexOrder(iAggregate.getTupleId(), iAggregate); + SuperInstruction indexOperation = getIndexSuperInstInfo(iAggregate); +- NodePtr init = mkInit(iAggregate); + NodePtr expr = dispatch(iAggregate.getExpression()); + NodePtr cond = dispatch(iAggregate.getCondition()); + orderingContext.addNewTuple(iAggregate.getTupleId(), 1); + NodePtr nested = visit_(type_identity(), iAggregate); + std::size_t relId = encodeRelation(iAggregate.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "IndexAggregate", lookup(iAggregate.getRelation())); +- /* Resolve functor to actual function pointer now */ +- void* functionPtr = resolveFunctionPointers(iAggregate); ++ NodeType type = constructNodeType("IndexAggregate", lookup(iAggregate.getRelation())); + return mk(type, &iAggregate, rel, std::move(expr), std::move(cond), std::move(nested), +- std::move(init), functionPtr, encodeView(&iAggregate), std::move(indexOperation)); ++ encodeView(&iAggregate), std::move(indexOperation)); + } + + NodePtr NodeGenerator::visit_( + type_identity, const ram::ParallelIndexAggregate& piAggregate) { + orderingContext.addTupleWithIndexOrder(piAggregate.getTupleId(), piAggregate); + SuperInstruction indexOperation = getIndexSuperInstInfo(piAggregate); +- NodePtr init = mkInit(piAggregate); + NodePtr expr = dispatch(piAggregate.getExpression()); + NodePtr cond = dispatch(piAggregate.getCondition()); + orderingContext.addNewTuple(piAggregate.getTupleId(), 1); + NodePtr nested = visit_(type_identity(), piAggregate); + std::size_t relId = encodeRelation(piAggregate.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "ParallelIndexAggregate", lookup(piAggregate.getRelation())); +- /* Resolve functor to actual function pointer now */ +- void* functionPtr = resolveFunctionPointers(piAggregate); ++ NodeType type = constructNodeType("ParallelIndexAggregate", lookup(piAggregate.getRelation())); + auto res = mk(type, &piAggregate, rel, std::move(expr), std::move(cond), +- std::move(nested), std::move(init), functionPtr, encodeView(&piAggregate), +- std::move(indexOperation)); ++ std::move(nested), encodeView(&piAggregate), std::move(indexOperation)); + res->setViewContext(parentQueryViewContext); + return res; + } +@@ -482,7 +416,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Guar + SuperInstruction superOp = getInsertSuperInstInfo(guardedInsert); + std::size_t relId = encodeRelation(guardedInsert.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "GuardedInsert", lookup(guardedInsert.getRelation())); ++ NodeType type = constructNodeType("GuardedInsert", lookup(guardedInsert.getRelation())); + auto condition = guardedInsert.getCondition(); + return mk(type, &guardedInsert, rel, std::move(superOp), dispatch(*condition)); + } +@@ -491,7 +425,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Insert& ins + SuperInstruction superOp = getInsertSuperInstInfo(insert); + std::size_t relId = encodeRelation(insert.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "Insert", lookup(insert.getRelation())); ++ NodeType type = constructNodeType("Insert", lookup(insert.getRelation())); + return mk(type, &insert, rel, std::move(superOp)); + } + +@@ -499,7 +433,7 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Erase& erase + SuperInstruction superOp = getEraseSuperInstInfo(erase); + std::size_t relId = encodeRelation(erase.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "Erase", lookup(erase.getRelation())); ++ NodeType type = constructNodeType("Erase", lookup(erase.getRelation())); + return mk(type, &erase, rel, std::move(superOp)); + } + +@@ -542,7 +476,9 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Call& call) { + // in the interpreter. The index is stored in the + // data array of the Node as the first + // entry. +- return mk(I_Call, &call, call.getName()); ++ auto subs = engine.tUnit.getProgram().getSubroutines(); ++ std::size_t subroutineId = distance(subs.begin(), subs.find(call.getName())); ++ return mk(I_Call, &call, subroutineId); + } + + NodePtr NodeGenerator::visit_(type_identity, const ram::LogRelationTimer& timer) { +@@ -562,15 +498,15 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::DebugInf + NodePtr NodeGenerator::visit_(type_identity, const ram::Clear& clear) { + std::size_t relId = encodeRelation(clear.getRelation()); + auto rel = getRelationHandle(relId); +- return mk(I_Clear, &clear, rel); ++ NodeType type = constructNodeType("Clear", lookup(clear.getRelation())); ++ return mk(type, &clear, rel); + } + +-NodePtr NodeGenerator::visit_( +- type_identity, const ram::EstimateJoinSize& estimateJoinSize) { +- std::size_t relId = encodeRelation(estimateJoinSize.getRelation()); ++NodePtr NodeGenerator::visit_(type_identity, const ram::CountUniqueKeys& count) { ++ std::size_t relId = encodeRelation(count.getRelation()); + auto rel = getRelationHandle(relId); +- NodeType type = constructNodeType(global, "EstimateJoinSize", lookup(estimateJoinSize.getRelation())); +- return mk(type, &estimateJoinSize, rel, encodeIndexPos(estimateJoinSize)); ++ NodeType type = constructNodeType("CountUniqueKeys", lookup(count.getRelation())); ++ return mk(type, &count, rel, encodeIndexPos(count)); + } + + NodePtr NodeGenerator::visit_(type_identity, const ram::LogSize& size) { +@@ -644,10 +580,6 @@ NodePtr NodeGenerator::visit_(type_identity, const ram::Swap& swap) { + return mk(I_Swap, &swap, src, target); + } + +-NodePtr NodeGenerator::visit_(type_identity, const ram::Assign& assign) { +- return mk(I_Assign, &assign, dispatch(assign.getVariable()), dispatch(assign.getValue())); +-} +- + NodePtr NodeGenerator::visit_(type_identity, const ram::UndefValue&) { + return nullptr; + } +diff --git a/src/interpreter/Generator.h b/src/interpreter/Generator.h +index 731d645..9850629 100644 +--- a/src/interpreter/Generator.h ++++ b/src/interpreter/Generator.h +@@ -32,9 +32,9 @@ + #include "ram/Condition.h" + #include "ram/Conjunction.h" + #include "ram/Constraint.h" ++#include "ram/CountUniqueKeys.h" + #include "ram/DebugInfo.h" + #include "ram/EmptinessCheck.h" +-#include "ram/EstimateJoinSize.h" + #include "ram/ExistenceCheck.h" + #include "ram/Exit.h" + #include "ram/Expression.h" +@@ -128,8 +128,6 @@ public: + + NodePtr visit_(type_identity, const ram::NumericConstant& num) override; + +- NodePtr visit_(type_identity, const ram::Variable& var) override; +- + NodePtr visit_(type_identity, const ram::StringConstant& num) override; + + NodePtr visit_(type_identity, const ram::TupleElement& access) override; +@@ -228,8 +226,7 @@ public: + + NodePtr visit_(type_identity, const ram::Clear& clear) override; + +- NodePtr visit_( +- type_identity, const ram::EstimateJoinSize& estimateJoinSize) override; ++ NodePtr visit_(type_identity, const ram::CountUniqueKeys& count) override; + + NodePtr visit_(type_identity, const ram::LogSize& size) override; + +@@ -241,8 +238,6 @@ public: + + NodePtr visit_(type_identity, const ram::Swap& swap) override; + +- NodePtr visit_(type_identity, const ram::Assign& assign) override; +- + NodePtr visit_(type_identity, const ram::UndefValue&) override; + + NodePtr visit_(type_identity, const ram::Node& node) override; +@@ -347,9 +342,6 @@ private: + SuperInstruction getInsertSuperInstInfo(const ram::Insert& exist); + SuperInstruction getEraseSuperInstInfo(const ram::Erase& exist); + +- NodePtr mkInit(const ram::AbstractAggregate& aggregate); +- void* resolveFunctionPointers(const ram::AbstractAggregate& aggregate); +- + /** Environment encoding, store a mapping from ram::Node to its operation index id. */ + std::unordered_map indexTable; + /** Points to the current viewContext during the generation. +@@ -370,7 +362,5 @@ private: + OrderingContext orderingContext = OrderingContext(*this); + /** Reference to the engine instance */ + Engine& engine; +- /** Reference to global */ +- Global& global; + }; + } // namespace souffle::interpreter +diff --git a/src/interpreter/Index.h b/src/interpreter/Index.h +index 734a9e4..3fc693d 100644 +--- a/src/interpreter/Index.h ++++ b/src/interpreter/Index.h +@@ -142,13 +142,11 @@ struct ViewWrapper { + /** + * An index is an abstraction of a data structure + */ +-template typename Structure> ++template typename Structure> + class Index { + public: + static constexpr std::size_t Arity = _Arity; +- static constexpr std::size_t AuxiliaryArity = _AuxiliaryArity; +- using Data = Structure; ++ using Data = Structure; + using Tuple = typename souffle::Tuple; + using iterator = typename Data::iterator; + using Hints = typename Data::operation_hints; +@@ -241,7 +239,7 @@ public: + /** + * Inserts all elements of the given index. + */ +- void insert(const Index& src) { ++ void insert(const Index& src) { + for (const auto& tuple : src) { + this->insert(tuple); + } +@@ -312,18 +310,14 @@ public: + void clear() { + data.clear(); + } +- +- void printStats(std::ostream& o) const { +- data.printStats(o); +- } + }; + + /** + * A partial specialize template for nullary indexes. + * No complex data structure is required. + */ +-template