├── .circleci └── config.yml ├── .clang-format ├── .gitignore ├── .ocamlformat ├── BUILD ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── REPO_OVERVIEW.md ├── WORKSPACE ├── benchmarks ├── AsyncFibonacci.java ├── Fibonacci.java ├── asyncFib.bolt ├── fib.bolt ├── fib.s └── fibByteCode.txt ├── bolt.opam ├── dune-project ├── examples ├── binarySearchTree.bolt ├── binarySearchTree.java ├── factorial-optimised.ll ├── factorial-unoptimised.ll ├── factorial.bolt ├── factorial.ir ├── foo.bolt ├── generics.bolt ├── inheritance.bolt ├── linkedlist.bolt ├── linkedlist.java ├── overloading.bolt ├── tree.bolt └── vtable │ ├── foo.bolt │ └── foo.ll ├── hooks └── pre-commit ├── llvm.bzl ├── scripts ├── benchmarks.sh ├── ci_install_deps.sh ├── compile_program.sh ├── run_e2e_tests.sh ├── run_frontend_integration_tests.sh └── run_program.sh ├── src ├── BUILD ├── frontend │ ├── README.md │ ├── ast │ │ ├── README.md │ │ ├── ast_types.ml │ │ ├── ast_types.mli │ │ ├── dune │ │ ├── pprint_ast.ml │ │ └── pprint_ast.mli │ ├── compile_program_ir.ml │ ├── compile_program_ir.mli │ ├── data_race_checker │ │ ├── aggregate_capability_accesses.ml │ │ ├── aggregate_capability_accesses.mli │ │ ├── data_race_checker_env.ml │ │ ├── data_race_checker_env.mli │ │ ├── dune │ │ ├── type_alias_liveness.ml │ │ ├── type_alias_liveness.mli │ │ ├── type_async_capabilities.ml │ │ ├── type_async_capabilities.mli │ │ ├── type_borrowing.ml │ │ ├── type_borrowing.mli │ │ ├── type_capability_annotations.ml │ │ ├── type_capability_annotations.mli │ │ ├── type_capability_constraints.ml │ │ ├── type_capability_constraints.mli │ │ ├── type_concurrent_capability_access.ml │ │ ├── type_concurrent_capability_access.mli │ │ ├── type_consume_expr.ml │ │ ├── type_consume_expr.mli │ │ ├── type_data_races_classes.ml │ │ ├── type_data_races_classes.mli │ │ ├── type_data_races_expr.ml │ │ ├── type_data_races_expr.mli │ │ ├── type_data_races_functions.ml │ │ ├── type_data_races_functions.mli │ │ ├── type_data_races_program.ml │ │ ├── type_data_races_program.mli │ │ ├── type_linear_capabilities.ml │ │ ├── type_linear_capabilities.mli │ │ ├── type_read_capabilities.ml │ │ ├── type_read_capabilities.mli │ │ ├── type_subord_capabilities.ml │ │ ├── type_subord_capabilities.mli │ │ ├── type_subtyping.ml │ │ ├── type_subtyping.mli │ │ ├── update_identifier_capabilities.ml │ │ └── update_identifier_capabilities.mli │ ├── desugaring │ │ ├── count_generics_instantiations.ml │ │ ├── count_generics_instantiations.mli │ │ ├── desugar_class_and_function_defns.ml │ │ ├── desugar_class_and_function_defns.mli │ │ ├── desugar_env.ml │ │ ├── desugar_env.mli │ │ ├── desugar_expr.ml │ │ ├── desugar_expr.mli │ │ ├── desugar_generics.ml │ │ ├── desugar_generics.mli │ │ ├── desugar_overloading.ml │ │ ├── desugar_overloading.mli │ │ ├── desugar_program.ml │ │ ├── desugar_program.mli │ │ ├── desugared_ast.ml │ │ ├── desugared_ast.mli │ │ ├── dune │ │ ├── free_obj_vars_expr.ml │ │ ├── free_obj_vars_expr.mli │ │ ├── name_mangle_generics.ml │ │ ├── name_mangle_generics.mli │ │ ├── pprint_dast.ml │ │ ├── pprint_dast.mli │ │ ├── remove_variable_shadowing.ml │ │ ├── remove_variable_shadowing.mli │ │ ├── replace_generic_with_instantiated_class_defns.ml │ │ └── replace_generic_with_instantiated_class_defns.mli │ ├── dune │ ├── ir_gen │ │ ├── dune │ │ ├── frontend_ir.ml │ │ ├── frontend_ir.mli │ │ ├── ir_gen_class_and_function_defns.ml │ │ ├── ir_gen_class_and_function_defns.mli │ │ ├── ir_gen_env.ml │ │ ├── ir_gen_env.mli │ │ ├── ir_gen_expr.ml │ │ ├── ir_gen_expr.mli │ │ ├── ir_gen_operators.ml │ │ ├── ir_gen_operators.mli │ │ ├── ir_gen_program.ml │ │ ├── ir_gen_program.mli │ │ ├── ir_gen_protobuf.ml │ │ ├── ir_gen_protobuf.mli │ │ ├── pprint_fir.ml │ │ └── pprint_fir.mli │ ├── main.ml │ ├── main.mli │ ├── parsing │ │ ├── README.md │ │ ├── dune │ │ ├── lex_and_parse.ml │ │ ├── lex_and_parse.mli │ │ ├── lexer.mll │ │ ├── parsed_ast.ml │ │ ├── parsed_ast.mli │ │ ├── parser.mly │ │ ├── pprint_parser_tokens.ml │ │ ├── pprint_parser_tokens.mli │ │ ├── pprint_past.ml │ │ └── pprint_past.mli │ └── typing │ │ ├── README.md │ │ ├── dune │ │ ├── pprint_tast.ml │ │ ├── pprint_tast.mli │ │ ├── type_classes.ml │ │ ├── type_classes.mli │ │ ├── type_env.ml │ │ ├── type_env.mli │ │ ├── type_expr.ml │ │ ├── type_expr.mli │ │ ├── type_functions.ml │ │ ├── type_functions.mli │ │ ├── type_generics.ml │ │ ├── type_generics.mli │ │ ├── type_inheritance.ml │ │ ├── type_inheritance.mli │ │ ├── type_overloading.ml │ │ ├── type_overloading.mli │ │ ├── type_program.ml │ │ ├── type_program.mli │ │ ├── typed_ast.ml │ │ └── typed_ast.mli ├── frontend_ir.proto └── llvm-backend │ ├── BUILD │ ├── deserialise_ir │ ├── BUILD │ ├── class_ir.cc │ ├── class_ir.h │ ├── deserialise_protobuf.cc │ ├── deserialise_protobuf.h │ ├── expr_ir.cc │ ├── expr_ir.h │ ├── function_ir.cc │ ├── function_ir.h │ ├── ir_visitor.h │ ├── program_ir.cc │ ├── program_ir.h │ ├── type_ir.cc │ └── type_ir.h │ ├── llvm_ir_codegen │ ├── BUILD │ ├── class_codegen.cc │ ├── expr_codegen.cc │ ├── extern_functions_codegen.cc │ ├── function_codegen.cc │ ├── ir_codegen_visitor.cc │ ├── ir_codegen_visitor.h │ ├── lock_codegen.cc │ ├── pthread_codegen.cc │ └── type_codegen.cc │ └── main.cc └── tests ├── e2e ├── for_loop.bolt ├── for_loop.ll.expected ├── for_loop.out.expected ├── heap_objects.bolt ├── heap_objects.ll.expected ├── heap_objects.out.expected ├── independent_threads.bolt ├── independent_threads.ll.expected ├── independent_threads.out.expected ├── is_pthread_blocking.bolt ├── is_pthread_blocking.ll.expected ├── is_pthread_blocking.out.expected ├── recursive_method.bolt ├── recursive_method.ll.expected ├── recursive_method.out.expected ├── subtype_passed_to_fn.bolt ├── subtype_passed_to_fn.ll.expected ├── subtype_passed_to_fn.out.expected ├── vtable.bolt ├── vtable.ll.expected └── vtable.out.expected ├── frontend ├── alcotest │ ├── desugaring │ │ └── test_remove_variable_shadowing.ml │ ├── parsing │ │ ├── dune │ │ └── test_lexer.ml │ └── typing │ │ ├── dune │ │ └── test_type_env.ml ├── expect │ ├── data_race_checker │ │ ├── bad_assignment.ml │ │ ├── bad_concurrent_regions.ml │ │ ├── bad_consume_variable.ml │ │ ├── bad_finish_async_expr.ml │ │ ├── bad_function_borrowing.ml │ │ ├── bad_function_call.ml │ │ ├── bad_method_call.ml │ │ ├── bad_read_region_access.ml │ │ ├── bad_region_annotations.ml │ │ ├── bad_subord_access.ml │ │ ├── dune │ │ ├── good_block_exprs.ml │ │ ├── good_capability_annotations.ml │ │ ├── good_class_defn.ml │ │ ├── good_comments.ml │ │ ├── good_conditional_expr.ml │ │ ├── good_constructor.ml │ │ ├── good_consume_variable.ml │ │ ├── good_function_application.ml │ │ ├── good_immutable_refs_in_multiple_threads.ml │ │ ├── good_inheritance.ml │ │ ├── good_operators.ml │ │ ├── good_shadowing_variables.ml │ │ ├── good_simple_capability_classes.ml │ │ ├── print_data_race_checker_ast.ml │ │ └── print_data_race_checker_ast.mli │ ├── desugaring │ │ ├── dune │ │ ├── good_generics.ml │ │ ├── good_overloading.ml │ │ ├── good_variable_shadowing.ml │ │ ├── print_desugared_ast.ml │ │ └── print_desugared_ast.mli │ ├── ir_gen │ │ ├── dune │ │ ├── good_block_exprs.ml │ │ ├── good_class_defn.ml │ │ ├── good_comments.ml │ │ ├── good_conditional_expr.ml │ │ ├── good_constructor.ml │ │ ├── good_consume_variable.ml │ │ ├── good_function_application.ml │ │ ├── good_immutable_refs_in_multiple_threads.ml │ │ ├── good_inheritance.ml │ │ ├── good_operators.ml │ │ ├── good_region_annotations.ml │ │ ├── good_shadowing_variables.ml │ │ ├── good_simple_capability_classes.ml │ │ ├── print_frontend_ir.ml │ │ └── print_frontend_ir.mli │ ├── parsing │ │ ├── bad_consume_expr.ml │ │ ├── bad_expressions_not_terminated.ml │ │ ├── bad_function_expr.ml │ │ ├── dune │ │ ├── good_block_exprs.ml │ │ ├── good_comments.ml │ │ ├── good_conditional_expr.ml │ │ ├── good_constructor.ml │ │ ├── good_consume_variable.ml │ │ ├── good_function_application.ml │ │ ├── good_immutable_refs_in_multiple_threads.ml │ │ ├── good_operators.ml │ │ ├── good_shadowing_variables.ml │ │ ├── good_simple_capability_classes.ml │ │ ├── print_parsed_ast.ml │ │ └── print_parsed_ast.mli │ └── typing │ │ ├── bad_access.ml │ │ ├── bad_assignment.ml │ │ ├── bad_class_defn.ml │ │ ├── bad_conditional_expr.ml │ │ ├── bad_constructor.ml │ │ ├── bad_consume_variable.ml │ │ ├── bad_function_application.ml │ │ ├── bad_function_defn.ml │ │ ├── bad_generics.ml │ │ ├── bad_inheritance.ml │ │ ├── bad_let_declaration.ml │ │ ├── bad_method_call.ml │ │ ├── bad_operators.ml │ │ ├── bad_overloading.ml │ │ ├── bad_variable_shadowing.ml │ │ ├── dune │ │ ├── good_block_exprs.ml │ │ ├── good_class_defn.ml │ │ ├── good_comments.ml │ │ ├── good_conditional_expr.ml │ │ ├── good_constructor.ml │ │ ├── good_consume_variable.ml │ │ ├── good_function_application.ml │ │ ├── good_function_defn.ml │ │ ├── good_generics.ml │ │ ├── good_immutable_refs_in_multiple_threads.ml │ │ ├── good_inheritance.ml │ │ ├── good_let_declaration.ml │ │ ├── good_operators.ml │ │ ├── good_overloading.ml │ │ ├── good_shadowing_variables.ml │ │ ├── good_simple_capability_classes.ml │ │ ├── print_typed_ast.ml │ │ └── print_typed_ast.mli └── integration │ ├── example-generics-print-data-race-ast.out.expected │ ├── example-generics-print-desugared-ast.out.expected │ ├── example-generics-print-frontend-ir.out.expected │ ├── example-generics-print-parsed-ast.out.expected │ ├── example-generics-print-typed-ast.out.expected │ ├── example-generics.bolt │ ├── independent_threads-print-data-race-ast.out.expected │ ├── independent_threads-print-desugared-ast.out.expected │ ├── independent_threads-print-frontend-ir.out.expected │ ├── independent_threads-print-parsed-ast.out.expected │ ├── independent_threads-print-typed-ast.out.expected │ ├── independent_threads.bolt │ └── invalid_programs │ ├── README.md │ ├── README.out.expected │ ├── invalid_programs.out.expected │ ├── not_a_program │ ├── not_a_program.out.expected │ ├── wrong_extension.out.expected │ └── wrong_extension.txt └── llvm-backend ├── BUILD ├── gtest.BUILD └── llvm_ir_codegen ├── BUILD ├── ir_codegen_test_visitor.cc ├── ir_codegen_test_visitor.h └── test_ir_codegen.cc /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | test: 4 | docker: 5 | - image: ocaml/opam2:debian-10-ocaml-4.08 6 | environment: 7 | - TERM: dumb 8 | steps: 9 | - checkout 10 | - restore_cache: 11 | key: cache-{{ checksum "bolt.opam" }} 12 | - run: 13 | name: "Install deps" 14 | command: scripts/ci_install_deps.sh 15 | - save_cache: 16 | key: cache-{{ checksum "bolt.opam" }} 17 | paths: 18 | - ~/.opam 19 | - run: 20 | name: "Lint" 21 | command: opam exec -- make lint 22 | - run: 23 | name: "Run tests and calculate test coverage" 24 | command: opam exec -- make coverage 25 | - run: 26 | name: "Send Coveralls.io coverage report" 27 | command: opam exec -- bisect-ppx-report send-to Coveralls 28 | - run: 29 | name: "Make docs" 30 | command: opam exec -- make doc 31 | - persist_to_workspace: 32 | root: ./ 33 | paths: docs 34 | 35 | docs-deploy: 36 | docker: 37 | - image: node:8.10.0 38 | steps: 39 | - checkout 40 | - attach_workspace: 41 | at: ./ 42 | - run: 43 | name: Install and configure dependencies 44 | command: | 45 | npm install -g --silent gh-pages@2.0.1 46 | git config user.email "mukul.rathi@gmail.com" 47 | git config user.name "mukul-rathi" 48 | - run: 49 | name: Deploy docs to gh-pages branch 50 | command: gh-pages --dist docs/ --message "[skip ci] Update docs" 51 | 52 | workflows: 53 | version: 2 54 | build: 55 | jobs: 56 | - "test" 57 | - docs-deploy: 58 | requires: 59 | - test 60 | filters: 61 | branches: 62 | only: master 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | * 3 | 4 | # Unignore all with extensions 5 | !*.* 6 | 7 | # Unignore all dirs 8 | !*/ 9 | !dune 10 | !Dockerfile 11 | 12 | .vscode 13 | .DS_Store 14 | 15 | *.annot 16 | *.cmo 17 | *.cma 18 | *.cmi 19 | *.a 20 | *.o 21 | *.cmx 22 | *.cmxs 23 | *.cmxa 24 | *.install 25 | 26 | # ocamlbuild working directory 27 | _build/ 28 | 29 | # ocamlbuild targets 30 | *.byte 31 | *.native 32 | 33 | # oasis generated files 34 | setup.data 35 | setup.log 36 | 37 | # Merlin configuring file for Vim and Emacs 38 | .merlin 39 | 40 | # Coveralls.io configuration file 41 | .coveralls.yml 42 | 43 | #coverage build files 44 | _coverage 45 | *.coverage 46 | 47 | # Ignore (corrected) output for tests 48 | *.out 49 | *.ll 50 | *.ir 51 | *.out.corrected 52 | 53 | # Ignore other opam files 54 | ast.opam 55 | parsing.opam 56 | typing.opam 57 | data_race_checker.opam 58 | desugaring.opam 59 | ir_gen.opam 60 | 61 | #ignore generated proto files 62 | *.pb.* 63 | 64 | # Ignore all bazel-* generated files 65 | /bazel-* 66 | 67 | # ignore Java output 68 | *.class 69 | 70 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile = compact 2 | margin = 90 3 | parse-docstrings = true 4 | wrap-comments = true 5 | align-cases = true 6 | align-constructors-decl = true 7 | align-variants-decl = true 8 | break-string-literals=never 9 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//:__pkg__"]) 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ocaml/opam2:ubuntu-18.04-opam 2 | USER root 3 | 4 | RUN sudo apt-get -y upgrade 5 | RUN sudo apt-get update 6 | 7 | # install Bazel 8 | RUN sudo apt-get install -y m4 curl wget default-jdk 9 | RUN curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add - 10 | RUN echo "deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list 11 | RUN sudo apt update -y && sudo apt install -y bazel 12 | RUN sudo apt update -y && sudo apt install -y bazel-2.2.0 13 | 14 | # Set up workspace 15 | RUN mkdir -p /usr/src/app 16 | ENV WORKSPACE /usr/src/app 17 | WORKDIR /usr/src/app 18 | COPY . . 19 | 20 | #set up opam 21 | RUN opam init --disable-sandboxing --compiler=4.08.0 -y 22 | RUN eval $(opam env) 23 | RUN echo "test -r /home/opam/.opam/opam-init/init.sh && . /home/opam/.opam/opam-init/init.sh > /dev/null 2> /dev/null || true" >> ~/.profile 24 | 25 | # install clang 26 | RUN wget https://releases.llvm.org/9.0.0/clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz 27 | RUN tar xf clang* 28 | RUN sudo cp -r clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-18.04/* /usr/local/ 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mukul Rathi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: 2 | opam update 3 | opam install . --deps-only 4 | make build 5 | 6 | .PHONY: build 7 | build: 8 | make pre-build 9 | dune build 10 | bazel build //src/llvm-backend:main 11 | 12 | install: 13 | eval $(opam config env) 14 | opam update 15 | opam install --yes . --deps-only 16 | eval $(opam env) 17 | 18 | lint: 19 | make clean 20 | make pre-build 21 | dune build @lint 22 | dune build @fmt 23 | 24 | test: 25 | make pre-build 26 | dune runtest 27 | scripts/run_frontend_integration_tests.sh 28 | bazel test tests/llvm-backend/llvm_ir_codegen:test_llvm_ir_codegen 29 | scripts/run_e2e_tests.sh 30 | 31 | 32 | .SILENT: clean 33 | clean: 34 | dune clean 35 | @git clean -dfX 36 | rm -rf docs/ 37 | 38 | doc: 39 | make clean 40 | make pre-build 41 | dune build @doc 42 | mkdir docs/ 43 | cp -r ./_build/default/_doc/_html/* docs/ 44 | 45 | format: 46 | make pre-build 47 | dune build @fmt --auto-promote 48 | find **/llvm-backend/** -name "*.h" -o -name "*.cc" | xargs clang-format -i --style=file 49 | 50 | hook: 51 | cp ./hooks/* .git/hooks 52 | 53 | coverage: 54 | make clean 55 | make pre-build 56 | BISECT_ENABLE=yes dune build 57 | dune runtest 58 | scripts/run_frontend_integration_tests.sh 59 | bisect-ppx-report html 60 | bisect-ppx-report summary 61 | 62 | .SILENT: pre-build 63 | pre-build: 64 | # hack: create opam files so libraries can be exposed publicly 65 | cp bolt.opam ast.opam 66 | cp bolt.opam parsing.opam 67 | cp bolt.opam typing.opam 68 | cp bolt.opam desugaring.opam 69 | cp bolt.opam data_race_checker.opam 70 | cp bolt.opam ir_gen.opam 71 | rm -rf bazel-bolt -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 2 | 3 | http_archive( 4 | name = "gtest", 5 | url = "https://github.com/google/googletest/archive/release-1.7.0.zip", 6 | sha256 = "b58cb7547a28b2c718d1e38aee18a3659c9e3ff52440297e965f5edffe34b6d0", 7 | build_file = "//tests/llvm-backend:gtest.BUILD", 8 | strip_prefix = "googletest-release-1.7.0", 9 | ) 10 | 11 | # rules_cc defines rules for generating C++ code from Protocol Buffers. 12 | http_archive( 13 | name = "rules_cc", 14 | sha256 = "35f2fb4ea0b3e61ad64a369de284e4fbbdcdba71836a5555abb5e194cf119509", 15 | strip_prefix = "rules_cc-624b5d59dfb45672d4239422fa1e3de1822ee110", 16 | urls = [ 17 | "https://github.com/bazelbuild/rules_cc/archive/624b5d59dfb45672d4239422fa1e3de1822ee110.tar.gz", 18 | ], 19 | ) 20 | 21 | # rules_proto defines abstract rules for building Protocol Buffers. 22 | http_archive( 23 | name = "rules_proto", 24 | sha256 = "57001a3b33ec690a175cdf0698243431ef27233017b9bed23f96d44b9c98242f", 25 | strip_prefix = "rules_proto-9cd4f8f1ede19d81c6d48910429fe96776e567b1", 26 | urls = [ 27 | "https://github.com/bazelbuild/rules_proto/archive/9cd4f8f1ede19d81c6d48910429fe96776e567b1.tar.gz", 28 | ], 29 | ) 30 | 31 | 32 | load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies") 33 | rules_cc_dependencies() 34 | 35 | load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") 36 | rules_proto_dependencies() 37 | rules_proto_toolchains() 38 | 39 | # Load the LLVM repository. 40 | load("//:llvm.bzl", "llvm") 41 | llvm( 42 | name = "llvm", 43 | workspace_name = "bolt", 44 | ) -------------------------------------------------------------------------------- /benchmarks/AsyncFibonacci.java: -------------------------------------------------------------------------------- 1 | public class AsyncFibonacci{ 2 | int left; 3 | int right; 4 | int n; 5 | AsyncFibonacci(int n){ 6 | this.n = n; 7 | } 8 | static int fib(int n){ 9 | if (n ==0 || n==1){ 10 | return 1; 11 | } 12 | else return fib(n-1)+fib(n-2); 13 | } 14 | 15 | 16 | static int asyncFib(int n){ 17 | if (n < 35){ // for small cases use synchronous version 18 | return fib(n); 19 | } 20 | AsyncFibonacci x = new AsyncFibonacci(n); 21 | Thread t = new Thread() { 22 | @Override 23 | public void run() { 24 | x.left = asyncFib(x.n - 1); 25 | } 26 | }; 27 | t.start(); 28 | x.right = asyncFib(x.n-2); 29 | try{ 30 | t.join(); 31 | } catch(InterruptedException e){}// do nothing as shouldn't be thrown 32 | return x.left + x.right; 33 | } 34 | 35 | 36 | public static void main(String[] args){ 37 | System.out.print("Fib of 46 is: "); 38 | System.out.println(asyncFib(46)); 39 | } 40 | 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /benchmarks/Fibonacci.java: -------------------------------------------------------------------------------- 1 | public class Fibonacci{ 2 | 3 | static int fib(int n){ 4 | if (n ==0 || n==1){ 5 | return 1; 6 | } 7 | else return fib(n-1)+fib(n-2); 8 | } 9 | 10 | public static void main(String[] args){ 11 | System.out.print("Fib of 46 is: "); 12 | System.out.println(fib(46)); 13 | } 14 | 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /benchmarks/asyncFib.bolt: -------------------------------------------------------------------------------- 1 | class FibHelper{ 2 | capability linear left, linear right, read view; 3 | var int left : left; 4 | var int right : right; 5 | const int n : view; 6 | } 7 | 8 | function int fib(int n){ 9 | if ((n==0) || (n==1)){ 10 | 1 11 | } else{ 12 | (fib(n - 1)) + (fib(n - 2)) 13 | } 14 | } 15 | 16 | 17 | function int asyncFib(int n){ 18 | if(n <35) { // for small cases use synchronous version 19 | fib(n) 20 | } 21 | else{ 22 | let x = new FibHelper(n : n); 23 | finish{ 24 | async{ 25 | x.left := asyncFib(x.n - 1) 26 | } 27 | x.right := asyncFib(x.n - 2) 28 | }; 29 | x.left + x.right 30 | } 31 | } 32 | 33 | void main(){ 34 | printf("Fib of 46 is: %d", asyncFib(46) ) 35 | } -------------------------------------------------------------------------------- /benchmarks/fib.bolt: -------------------------------------------------------------------------------- 1 | function int fib(int n){ 2 | if ((n==0) || (n==1)){ 3 | 1 4 | } else{ 5 | (fib(n - 1)) + (fib(n - 2)) 6 | } 7 | } 8 | 9 | void main(){ 10 | printf("Fib of 46 is: %d", fib(46) ) 11 | } -------------------------------------------------------------------------------- /benchmarks/fib.s: -------------------------------------------------------------------------------- 1 | .section __TEXT,__text,regular,pure_instructions 2 | .macosx_version_min 10, 14 3 | .globl _fib ## -- Begin function fib 4 | .p2align 4, 0x90 5 | _fib: ## @fib 6 | ## %bb.0: ## %entry 7 | pushq %rbp 8 | pushq %rbx 9 | pushq %rax 10 | movl $1, %ebp 11 | cmpl $2, %edi 12 | jb LBB0_3 13 | ## %bb.1: ## %else.preheader 14 | movl %edi, %ebx 15 | movl $1, %ebp 16 | .p2align 4, 0x90 17 | LBB0_2: ## %else 18 | ## =>This Inner Loop Header: Depth=1 19 | leal -1(%rbx), %edi 20 | callq _fib 21 | addl $-2, %ebx 22 | addl %eax, %ebp 23 | cmpl $1, %ebx 24 | ja LBB0_2 25 | LBB0_3: ## %ifcont 26 | movl %ebp, %eax 27 | addq $8, %rsp 28 | popq %rbx 29 | popq %rbp 30 | retq 31 | ## -- End function 32 | .globl _main ## -- Begin function main 33 | .p2align 4, 0x90 34 | _main: ## @main 35 | ## %bb.0: ## %entry 36 | pushq %rax 37 | movl $46, %edi 38 | callq _fib 39 | leaq L___unnamed_1(%rip), %rdi 40 | movl %eax, %esi 41 | xorl %eax, %eax 42 | callq _printf 43 | xorl %eax, %eax 44 | popq %rcx 45 | retq 46 | ## -- End function 47 | .section __TEXT,__cstring,cstring_literals 48 | L___unnamed_1: ## @0 49 | .asciz "Fib Bolt of 46 is: %d\n" 50 | 51 | 52 | .subsections_via_symbols 53 | -------------------------------------------------------------------------------- /benchmarks/fibByteCode.txt: -------------------------------------------------------------------------------- 1 | Compiled from "Fibonacci.java" 2 | public class Fibonacci { 3 | public Fibonacci(); 4 | Code: 5 | 0: aload_0 6 | 1: invokespecial #1 // Method java/lang/Object."":()V 7 | 4: return 8 | 9 | static int fib(int); 10 | Code: 11 | 0: iload_0 12 | 1: ifeq 9 13 | 4: iload_0 14 | 5: iconst_1 15 | 6: if_icmpne 11 16 | 9: iconst_1 17 | 10: ireturn 18 | 11: iload_0 19 | 12: iconst_1 20 | 13: isub 21 | 14: invokestatic #7 // Method fib:(I)I 22 | 17: iload_0 23 | 18: iconst_2 24 | 19: isub 25 | 20: invokestatic #7 // Method fib:(I)I 26 | 23: iadd 27 | 24: ireturn 28 | 29 | public static void main(java.lang.String[]); 30 | Code: 31 | 0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream; 32 | 3: ldc #19 // String Fib of 46 is: 33 | 5: invokevirtual #21 // Method java/io/PrintStream.print:(Ljava/lang/String;)V 34 | 8: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream; 35 | 11: bipush 46 36 | 13: invokestatic #7 // Method fib:(I)I 37 | 16: invokevirtual #27 // Method java/io/PrintStream.println:(I)V 38 | 19: return 39 | } 40 | -------------------------------------------------------------------------------- /bolt.opam: -------------------------------------------------------------------------------- 1 | opam-version: "2.0" 2 | synopsis: "A type system to prevent data races" 3 | version: "1.5" 4 | maintainer: "msr45@cam.ac.uk" 5 | authors: ["Mukul Rathi"] 6 | homepage: "https://github.com/mukul-rathi/bolt" 7 | bug-reports: "https://github.com/mukul-rathi/bolt/issues" 8 | dev-repo: "git+https://github.com/mukul-rathi/bolt.git" 9 | build: [ 10 | ["dune" "build" "-p" name "-j" jobs] 11 | ] 12 | depends: [ 13 | "dune" {>= "2.0.0"} 14 | "qcheck" {= "0.12"} 15 | "qcheck-alcotest" {= "0.12"} 16 | "alcotest" {= "0.8.5"} 17 | "odoc" {= "1.4.2"} 18 | "core" {= "v0.12.4"} 19 | "ocamlformat" {="0.12"} 20 | "bisect_ppx" { = "2.2.0"} 21 | "ppx_tools_versioned" { = "5.3.0"} 22 | "ppx_jane"{= "v0.12.0"} 23 | "menhir"{= "20190924"} 24 | "fmt" { ="0.8.8"} 25 | "ppx_deriving_protobuf" {= "2.7"} 26 | ] 27 | 28 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.0) 2 | (name bolt) 3 | (using menhir 2.0) 4 | -------------------------------------------------------------------------------- /examples/factorial-optimised.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | declare i32 @printf(i8*, ...) 6 | 7 | declare i8* @GC_malloc(i64) 8 | 9 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 10 | 11 | declare i32 @pthread_join(i8*, i8**) 12 | 13 | declare i32 @pthread_equal(i8*, i8*) 14 | 15 | declare i8* @pthread_self() 16 | 17 | define i32 @factorial(i32) { 18 | entry: 19 | %eq = icmp eq i32 %0, 0 20 | br i1 %eq, label %ifcont, label %else 21 | 22 | else: ; preds = %entry 23 | %sub = add i32 %0, -1 24 | %1 = call i32 @factorial(i32 %sub) 25 | %mult = mul i32 %1, %0 26 | br label %ifcont 27 | 28 | ifcont: ; preds = %entry, %else 29 | %iftmp = phi i32 [ %mult, %else ], [ 1, %entry ] 30 | ret i32 %iftmp 31 | } 32 | 33 | define i32 @main() { 34 | entry: 35 | %0 = call i32 @factorial(i32 5) 36 | ret i32 0 37 | } 38 | -------------------------------------------------------------------------------- /examples/factorial-unoptimised.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | declare i32 @printf(i8*, ...) 6 | 7 | declare i8* @GC_malloc(i64) 8 | 9 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 10 | 11 | declare i32 @pthread_join(i8*, i8**) 12 | 13 | declare i32 @pthread_equal(i8*, i8*) 14 | 15 | declare i8* @pthread_self() 16 | 17 | define i32 @factorial(i32) { 18 | entry: 19 | %n = alloca i32 20 | store i32 %0, i32* %n 21 | %1 = load i32, i32* %n 22 | %eq = icmp eq i32 %1, 0 23 | br i1 %eq, label %then, label %else 24 | 25 | then: ; preds = %entry 26 | br label %ifcont 27 | 28 | else: ; preds = %entry 29 | %2 = load i32, i32* %n 30 | %3 = load i32, i32* %n 31 | %sub = sub i32 %3, 1 32 | %4 = call i32 @factorial(i32 %sub) 33 | %mult = mul i32 %2, %4 34 | br label %ifcont 35 | 36 | ifcont: ; preds = %else, %then 37 | %iftmp = phi i32 [ 1, %then ], [ %mult, %else ] 38 | ret i32 %iftmp 39 | } 40 | 41 | define i32 @main() { 42 | entry: 43 | %0 = call i32 @factorial(i32 5) 44 | ret i32 0 45 | } 46 | -------------------------------------------------------------------------------- /examples/factorial.bolt: -------------------------------------------------------------------------------- 1 | function int factorial(int n){ 2 | if (n==0) { 3 | 1 4 | } 5 | else { 6 | n * factorial(n - 1) 7 | 8 | } 9 | 10 | } 11 | 12 | void main(){ 13 | factorial(5) 14 | } 15 | -------------------------------------------------------------------------------- /examples/factorial.ir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/bolt/a81627f95af6577cd465df09290a51c9d469c667/examples/factorial.ir -------------------------------------------------------------------------------- /examples/foo.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability read Bar, linear Baz; 3 | const int f : Bar; 4 | var int g : Baz; 5 | void setg(int x) : Baz{ 6 | this.g := x 7 | } 8 | } 9 | void main(){ 10 | let x = new Foo(f:100); 11 | let y = new Foo(); 12 | finish { 13 | async{ 14 | printf("Value of x.f: %d", x.f) 15 | } 16 | y.setg(10); 17 | printf("Value of y.g: %d", y.g) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/generics.bolt: -------------------------------------------------------------------------------- 1 | class Foo{ 2 | capability linear Bar; 3 | var T f : Bar; 4 | void copy(Foo x): Bar{ 5 | this.f := x.f 6 | } 7 | void setF(T f) : Bar { 8 | this.f := f 9 | } 10 | T getF() : Bar { 11 | this.f 12 | } 13 | } 14 | 15 | 16 | 17 | void main() { 18 | let x = new Foo(f:5); 19 | let y = new Foo(); 20 | y.copy(consume x); 21 | finish{ 22 | async{ 23 | let z = new Foo(f:true); 24 | for (let i=0; i < 6; i:=i+1){ 25 | z.setF((i%2) == 0); 26 | printf("Value of z: %d", z.getF() ) 27 | } 28 | } 29 | let y = new Foo(f:0); 30 | for (let i=0; i < 100; i:=i+1){ 31 | y.setF((y.getF()) + i) 32 | }; 33 | printf("Value of y: %d", y.getF() ) 34 | } 35 | } -------------------------------------------------------------------------------- /examples/inheritance.bolt: -------------------------------------------------------------------------------- 1 | class Vehicle{ 2 | capability linear UpdateModel, linear UpdateDriveTrain, read ViewModel; 3 | var int model : UpdateModel; 4 | var int engine : UpdateDriveTrain; 5 | void setModel(int model) : UpdateModel { 6 | this.model := model 7 | } 8 | void printModel(): ViewModel { 9 | printf("This is a vehicle.") 10 | } 11 | } 12 | 13 | class Car extends Vehicle { 14 | capability linear UpdateMake; 15 | var int carMake : UpdateMake; 16 | void setModel(int model) : UpdateModel, UpdateMake { 17 | this.model := model; 18 | if (model==0){ 19 | this.carMake := 1 20 | } 21 | else{ 22 | this.carMake := 2 23 | } 24 | } 25 | 26 | void printModel(): ViewModel { 27 | printf("This is a car.") 28 | } 29 | } 30 | 31 | class Truck extends Vehicle { 32 | // trucks can have 6 or 8 wheels depending on model and drivetrain 33 | var int wheels : UpdateModel, UpdateDriveTrain; 34 | void setModel(int model) : UpdateModel , UpdateDriveTrain { 35 | if (model==0){ 36 | this.wheels := 6 37 | } 38 | else{ 39 | this.wheels := 8 40 | }; 41 | this.model := model 42 | } 43 | void printModel(): ViewModel{ 44 | printf("This is a truck.") 45 | } 46 | } 47 | 48 | function void example(Vehicle{ViewModel} v){ 49 | v.printModel() 50 | } 51 | 52 | void main(){ 53 | let x = new Car(); 54 | example(consume x) 55 | } -------------------------------------------------------------------------------- /examples/overloading.bolt: -------------------------------------------------------------------------------- 1 | class Foo{ 2 | capability read readCap; 3 | var int f : readCap; 4 | int test(int x) : readCap { 5 | this.f + x 6 | } 7 | int test(bool b) : readCap { 8 | if b { this.f + 1 } else { this.f } 9 | } 10 | } 11 | 12 | void main() { 13 | let x = new Foo(f:0); 14 | printf("First overloaded method %d", x.test(12)); 15 | printf("Second overloaded method %d",x.test(true)) 16 | } -------------------------------------------------------------------------------- /examples/tree.bolt: -------------------------------------------------------------------------------- 1 | class Tree { 2 | capability linear Left, linear Val, linear Right, read ViewChild, read ViewElem; 3 | var Tree leftChild : Left, ViewChild; 4 | var int value : Val, ViewElem; 5 | var Tree rightChild: Right, ViewChild; 6 | 7 | void setLeftChildVal(int val) : Left { 8 | let y = consume this.leftChild; 9 | y.value := val; 10 | this.leftChild := consume y 11 | 12 | } 13 | void setRightChildVal(int val) : Right { 14 | let y = consume this.rightChild; 15 | y.value := val; 16 | this.rightChild := consume y 17 | } 18 | } 19 | 20 | 21 | 22 | void main(){ 23 | let left = new Tree(); 24 | let right = new Tree(); 25 | let x = new Tree(value: 10, leftChild: left, rightChild: right); 26 | 27 | /* Using disjoint linear capabilities concurrently */ 28 | finish{ 29 | async{ 30 | x.setLeftChildVal(5) 31 | } 32 | async{ 33 | x.setRightChildVal(50) 34 | } 35 | x.value := 20 36 | }; 37 | let lc = x.leftChild; 38 | let rc = x.rightChild; 39 | finish{ 40 | /* Using the View capability for read-only access MRSW */ 41 | async{ 42 | printf("I can print out the left child in one thread %d\n" ,lc.value) 43 | } 44 | async{ 45 | printf("I can print out the left child in another thread %d\n" ,lc.value) 46 | } 47 | async{ 48 | printf("I can print out the right child in yet another thread %d\n", rc.value) 49 | } } 50 | } 51 | -------------------------------------------------------------------------------- /examples/vtable/foo.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability read Bar, linear Baz; 3 | const int f : Bar; 4 | var int g : Baz; 5 | void setg(int x) : Baz{ 6 | this.g := x ; 7 | printf("This works!") 8 | } 9 | } 10 | void main(){ 11 | let y = new Foo(); 12 | y.setg(10) // this should call the vtable 13 | } 14 | -------------------------------------------------------------------------------- /examples/vtable/foo.ll: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | %_VtableFoo = type { void (%Foo*, i32)* } 6 | %Foo = type { %_VtableFoo*, %pthread_t*, i32, i32, i32, i32 } 7 | %pthread_t = type opaque 8 | 9 | @_VtableFoo = global %_VtableFoo { void (%Foo*, i32)* @_Foo__setgi } 10 | @0 = private unnamed_addr constant [12 x i8] c"This works!\00", align 1 11 | 12 | declare i32 @printf(i8*, ...) 13 | 14 | declare i8* @malloc(i64) 15 | 16 | declare i32 @pthread_create(%pthread_t**, i8*, i8* (i8*)*, i8*) 17 | 18 | declare i32 @pthread_join(%pthread_t*, i8**) 19 | 20 | declare i32 @pthread_equal(%pthread_t*, %pthread_t*) 21 | 22 | declare %pthread_t* @pthread_self() 23 | 24 | define void @_Foo__setgi(%Foo*, i32) { 25 | entry: 26 | %this = alloca %Foo* 27 | store %Foo* %0, %Foo** %this 28 | %x = alloca i32 29 | store i32 %1, i32* %x 30 | %2 = load i32, i32* %x 31 | %3 = load %Foo*, %Foo** %this 32 | %4 = getelementptr inbounds %Foo, %Foo* %3, i32 0, i32 5 33 | store i32 %2, i32* %4 34 | %5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @0, i32 0, i32 0)) 35 | ret void 36 | } 37 | 38 | define i32 @main() { 39 | entry: 40 | %_var_y0 = alloca %Foo* 41 | %0 = call i8* @malloc(i64 ptrtoint (%Foo* getelementptr (%Foo, %Foo* null, i64 1) to i64)) 42 | %1 = bitcast i8* %0 to %Foo* 43 | %2 = getelementptr inbounds %Foo, %Foo* %1, i32 0, i32 0 44 | store %_VtableFoo* @_VtableFoo, %_VtableFoo** %2 45 | %3 = getelementptr inbounds %Foo, %Foo* %1, i32 0, i32 2 46 | store i32 0, i32* %3 47 | %4 = getelementptr inbounds %Foo, %Foo* %1, i32 0, i32 3 48 | store i32 0, i32* %4 49 | store %Foo* %1, %Foo** %_var_y0 50 | %5 = load %Foo*, %Foo** %_var_y0 51 | %6 = getelementptr inbounds %Foo, %Foo* %5, i32 0, i32 0 52 | %7 = load %_VtableFoo*, %_VtableFoo** %6 53 | %8 = getelementptr inbounds %_VtableFoo, %_VtableFoo* %7, i32 0, i32 0 54 | %9 = load void (%Foo*, i32)*, void (%Foo*, i32)** %8 55 | call void %9(%Foo* %5, i32 10) 56 | ret i32 0 57 | } 58 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | make format 3 | make lint 4 | make clean -------------------------------------------------------------------------------- /llvm.bzl: -------------------------------------------------------------------------------- 1 | """ 2 | Exposes the LLVM binary as a cc_library 3 | """ 4 | 5 | def _impl(ctx): 6 | # Download the pre-built LLVM binary. 7 | version = "9.0.0" 8 | name = "clang+llvm-%s-x86_64-darwin-apple" % version 9 | ctx.download_and_extract( 10 | url = "https://releases.llvm.org/%s/%s.tar.xz" % (version, name), 11 | ) 12 | 13 | # Create a BUILD file containing the cc_library declaration 14 | ctx.file("BUILD", """ 15 | package(default_visibility = ["//visibility:public"]) 16 | 17 | cc_library( 18 | name = "llvm", 19 | srcs = glob(["%s/lib/*.a"]), 20 | hdrs = glob([ 21 | "%s/include/**/*.h", 22 | "%s/include/**/*.inc", 23 | "%s/include/**/*.def", 24 | "%s/include/**/*.gen", 25 | ]), 26 | includes = ["%s/include/"], 27 | linkopts = [ 28 | "-lpthread", 29 | "-lcurses", 30 | "-ldl", 31 | ], 32 | ) 33 | """ % (name, name, name, name, name, name)) 34 | 35 | # Create an empty WORKSPACE file. 36 | ctx.file("WORKSPACE", "") 37 | 38 | llvm = repository_rule( 39 | implementation = _impl, 40 | attrs = { 41 | "workspace_name": attr.string(mandatory = True), 42 | }, 43 | ) 44 | -------------------------------------------------------------------------------- /scripts/benchmarks.sh: -------------------------------------------------------------------------------- 1 | 2 | case "$1" in 3 | --sync) 4 | scripts/run_program.sh benchmarks/fib.bolt 5 | cd benchmarks 6 | javac Fibonacci.java 7 | echo "Bolt" 8 | for i in {1..10} 9 | do 10 | time ./fib 11 | done 12 | echo "Java" 13 | 14 | for i in {1..3} # warmup JIT 15 | do 16 | java Fibonacci >nul 17 | done 18 | for i in {1..10} 19 | do 20 | time java Fibonacci 21 | done 22 | ;; 23 | --async) 24 | scripts/run_program.sh benchmarks/asyncFib.bolt 25 | cd benchmarks 26 | javac AsyncFibonacci.java 27 | echo "Bolt" 28 | for i in {1..10} 29 | do 30 | time ./asyncFib 31 | done 32 | echo "Java" 33 | for i in {1..3} 34 | do 35 | java AsyncFibonacci >nul 36 | done 37 | for i in {1..10} 38 | do 39 | time java AsyncFibonacci 40 | done 41 | ;; 42 | esac -------------------------------------------------------------------------------- /scripts/ci_install_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo apt-get install -y m4 curl 3 | # install opam dependencies 4 | cd /home/opam/opam-repository && git pull 5 | opam update 6 | cd - 7 | eval $(opam env) 8 | make install 9 | -------------------------------------------------------------------------------- /scripts/compile_program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | make pre-build 3 | rm -rf "${1%.bolt}.ir" 4 | if [ $# -eq 0 ]; then 5 | dune exec -- src/frontend/main.exe -help 6 | else 7 | (dune exec -- src/frontend/main.exe $*) 8 | if [ -f "${1%.bolt}.ir" ]; then 9 | echo -e "Frontend type-checking completed \033[0;32msuccessfully". 10 | echo -e "\033[0m" 11 | bazel-bin/src/llvm-backend/main "${1%.bolt}.ir" > "${1%.bolt}.ll" && echo -e "LLVM IR Codegen completed \033[0;32msuccessfully". 12 | echo -e "\033[0m" 13 | clang -O3 -pthread -I/usr/local/include/gc/ "${1%.bolt}.ll" /usr/local/lib/libgc.a -o "${1%.bolt}" && echo -e "Native binary compiled \033[0;32msuccessfully". 14 | echo -e "\033[0m" 15 | fi 16 | fi 17 | -------------------------------------------------------------------------------- /scripts/run_e2e_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | make build 4 | # normal output 5 | 6 | for f in $(find ./tests/e2e -name '*.bolt'); do # run through program test_suite 7 | PROGRAM_FILE=$(basename $f) # get file name from path 8 | OUT_FILES=("${f%.bolt}.ll" "${f%.bolt}.out") #get output file path 9 | scripts/run_program.sh $f > "${f%.bolt}.out" 10 | 11 | for((i=0; i<${#OUT_FILES[@]}; i++)); do 12 | OUT_FILE=${OUT_FILES[$i]} 13 | if [ -f "${OUT_FILE}.expected" ]; then # if we have expected output already 14 | diff "${OUT_FILE}" "${OUT_FILE}.expected" # compare output against expected output 15 | is_diff=$? 16 | if [ $is_diff -eq 1 ]; then 17 | if [ "$1" == "--save" ]; then # if we want to save this output as the expected one for regression tests 18 | mv "${OUT_FILE}" "${OUT_FILE}.expected" 19 | else 20 | echo "Regression tests failed." 21 | echo $f 22 | exit 1 #test failed 23 | fi 24 | fi 25 | else 26 | # create expected output for regression tests in future 27 | cp "${OUT_FILE}" "${OUT_FILE}.expected" 28 | fi 29 | done 30 | done -------------------------------------------------------------------------------- /scripts/run_program.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | scripts/compile_program.sh $* && if [ -f "${1%.bolt}.ir" ]; then ./${1%.bolt} 3 | fi 4 | -------------------------------------------------------------------------------- /src/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | # Convention: 4 | # A cc_proto_library that wraps a proto_library named foo_proto 5 | # should be called foo_cc_proto. 6 | cc_proto_library( 7 | name = "frontend_ir_cc_proto", 8 | deps = [":frontend_ir_proto"], 9 | visibility = ["//src/llvm-backend/deserialise_ir:__pkg__"], 10 | 11 | ) 12 | 13 | # Conventions: 14 | # 1. One proto_library rule per .proto file. 15 | # 2. A file named foo.proto will be in a rule named foo_proto. 16 | proto_library( 17 | name = "frontend_ir_proto", 18 | srcs = ["frontend_ir.proto"], 19 | ) -------------------------------------------------------------------------------- /src/frontend/README.md: -------------------------------------------------------------------------------- 1 | ## Frontend 2 | 3 | This contains the code for the compiler frontend, written in OCaml. 4 | 5 | The pipeline is as follows: 6 | 7 | parsing -> typing -> desugaring -> data_race_checker -> ir_gen 8 | 9 | This is specified in `compile_program_ir.ml`, which takes in a buffer to read the program from, and writes the serialised IR output by the frontend. 10 | 11 | The `main.ml` file reads the Bolt file from the command line, along with other optional command-line arguments. 12 | -------------------------------------------------------------------------------- /src/frontend/ast/README.md: -------------------------------------------------------------------------------- 1 | ## AST library 2 | 3 | This folder contains types and helper functions that are shared by the ASTs (Abstract Syntax Trees) across the frontend compiler. 4 | -------------------------------------------------------------------------------- /src/frontend/ast/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name ast) 3 | (public_name ast) 4 | (libraries core fmt) 5 | (preprocess 6 | (pps bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 9 | -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /src/frontend/ast/pprint_ast.ml: -------------------------------------------------------------------------------- 1 | open Ast_types 2 | open Core 3 | 4 | let indent_space = " " 5 | 6 | let pprint_modifier ppf ~indent modifier = 7 | Fmt.pf ppf "%sModifier: %s@." indent (string_of_modifier modifier) 8 | 9 | let pprint_type_expr ppf ~indent type_expr = 10 | Fmt.pf ppf "%sType expr: %s@." indent (string_of_type type_expr) 11 | 12 | let pprint_capability ppf ~indent (TCapability (mode, capability_name)) = 13 | Fmt.pf ppf "%sCapability: %s %s@." indent (string_of_mode mode) 14 | (Capability_name.to_string capability_name) 15 | 16 | let pprint_capabilities ppf ~indent capabilities = 17 | Fmt.pf ppf "%sCapabilities: @." indent ; 18 | let new_indent = indent_space ^ indent in 19 | List.iter ~f:(pprint_capability ppf ~indent:new_indent) capabilities 20 | 21 | let pprint_capability_names ppf ~indent capability_names = 22 | Fmt.pf ppf "%sCapabilities: %s@." indent 23 | (String.concat ~sep:"," (List.map ~f:Capability_name.to_string capability_names)) 24 | 25 | let pprint_field_defn ppf ~indent 26 | (TField (modifier, type_field, field_name, capability_names)) = 27 | Fmt.pf ppf "%sField Defn: %s@." indent (Field_name.to_string field_name) ; 28 | let new_indent = indent_space ^ indent in 29 | pprint_modifier ppf ~indent:new_indent modifier ; 30 | pprint_type_expr ppf ~indent:new_indent type_field ; 31 | pprint_capability_names ppf ~indent:new_indent capability_names 32 | 33 | let pprint_param ppf ~indent = function 34 | | TParam (type_expr, param_name, maybe_capabilities_restricted, maybe_borrowed) -> ( 35 | Fmt.pf ppf "%s%sParam: %s@." indent 36 | (string_of_maybe_borrowed_ref maybe_borrowed) 37 | (Var_name.to_string param_name) ; 38 | let new_indent = indent_space ^ indent in 39 | pprint_type_expr ppf ~indent:new_indent type_expr ; 40 | match maybe_capabilities_restricted with 41 | | Some allowed_capability_names -> 42 | pprint_capability_names ppf ~indent:new_indent allowed_capability_names 43 | | None -> () ) 44 | 45 | let pprint_params ppf ~indent = function 46 | | [] -> Fmt.pf ppf "%sParam: %s@." indent (string_of_type TEVoid) 47 | | params -> List.iter ~f:(pprint_param ppf ~indent) params 48 | -------------------------------------------------------------------------------- /src/frontend/ast/pprint_ast.mli: -------------------------------------------------------------------------------- 1 | (** This module defines a set of pretty printing functions for the parts of the AST that 2 | are common across the parsed and typed ASTs - to be used by pretty printing functions 3 | of the two ASTs. 4 | 5 | They all take in a formatter which specifies output channel e.g. a string formatter or 6 | a stdout formatter. (Think of this as a generalisation of printf) 7 | 8 | The second argument for all of these is an indent - this corresponds to nesting depth 9 | within the AST *) 10 | 11 | open Ast_types 12 | 13 | val pprint_capabilities : Format.formatter -> indent:string -> capability list -> unit 14 | 15 | val pprint_capability_names : 16 | Format.formatter -> indent:string -> Capability_name.t list -> unit 17 | 18 | val pprint_modifier : Format.formatter -> indent:string -> modifier -> unit 19 | val pprint_field_defn : Format.formatter -> indent:string -> field_defn -> unit 20 | val pprint_type_expr : Format.formatter -> indent:string -> type_expr -> unit 21 | val pprint_params : Format.formatter -> indent:string -> param list -> unit 22 | -------------------------------------------------------------------------------- /src/frontend/compile_program_ir.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Parsing.Lex_and_parse 3 | open Typing.Type_program 4 | open Desugaring.Desugar_program 5 | open Data_race_checker.Type_data_races_program 6 | open Ir_gen.Ir_gen_program 7 | open Ir_gen.Ir_gen_protobuf 8 | 9 | let maybe_pprint_ast should_pprint_ast pprintfun ast = 10 | if should_pprint_ast then ( 11 | pprintfun Fmt.stdout ast ; 12 | Error (Error.of_string "") 13 | (* This ends the program (preserving existing regression tests if subsequent pipeline 14 | changes) *) ) 15 | else Ok ast 16 | 17 | let compile_program_ir ?(should_pprint_past = false) ?(should_pprint_tast = false) 18 | ?(should_pprint_dast = false) ?(should_pprint_drast = false) 19 | ?(should_pprint_fir = false) ?(ignore_data_races = false) ?compile_out_file lexbuf = 20 | let open Result in 21 | parse_program lexbuf 22 | >>= maybe_pprint_ast should_pprint_past pprint_parsed_ast 23 | >>= type_program 24 | >>= maybe_pprint_ast should_pprint_tast pprint_typed_ast 25 | >>= desugar_program 26 | >>= maybe_pprint_ast should_pprint_dast pprint_desugared_ast 27 | >>= type_data_races_program ~ignore_data_races 28 | >>= maybe_pprint_ast should_pprint_drast pprint_data_race_checker_ast 29 | >>| ir_gen_program 30 | >>= maybe_pprint_ast should_pprint_fir pprint_frontend_ir 31 | |> function 32 | | Ok program -> ( 33 | match compile_out_file with 34 | | Some file_name -> 35 | Out_channel.with_file file_name ~f:(fun file_oc -> 36 | ir_gen_protobuf program file_oc) 37 | | None -> ir_gen_protobuf program stdout ) 38 | | Error e -> eprintf "%s" (Error.to_string_hum e) 39 | -------------------------------------------------------------------------------- /src/frontend/compile_program_ir.mli: -------------------------------------------------------------------------------- 1 | (** This is the entry point for execution of Bolt programs. *) 2 | 3 | val compile_program_ir : 4 | ?should_pprint_past:bool (** whether to print parsed AST *) 5 | -> ?should_pprint_tast:bool (** whether to print typed AST *) 6 | -> ?should_pprint_dast:bool (** whether to print desugared AST *) 7 | -> ?should_pprint_drast:bool (** whether to print data-race type-checked AST *) 8 | -> ?should_pprint_fir:bool (** whether to print serialisable frontend IR *) 9 | -> ?ignore_data_races:bool (** whether to ignore data races (execute program anyways) *) 10 | -> ?compile_out_file:string (** output serialised protobuf to a file *) 11 | -> Lexing.lexbuf (* Lex buffer to read the program from *) 12 | -> unit 13 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/aggregate_capability_accesses.mli: -------------------------------------------------------------------------------- 1 | (** This module aggregates all capability accesses for objects in a given thread, and, if 2 | an identifier has multiple capability options, we choose which capability to use and 3 | update the identifier accordingly. *) 4 | 5 | open Desugaring.Desugared_ast 6 | 7 | val aggregate_capability_accesses_expr : 8 | class_defn list -> function_defn list -> expr -> expr * obj_var_and_capabilities list 9 | 10 | val aggregate_capability_accesses_block_expr : 11 | class_defn list 12 | -> function_defn list 13 | -> block_expr 14 | -> block_expr * obj_var_and_capabilities list 15 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name data_race_checker) 3 | (public_name data_race_checker) 4 | (libraries core fmt desugaring) 5 | (preprocess 6 | (pps bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 9 | -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_alias_liveness.mli: -------------------------------------------------------------------------------- 1 | (** This module checks the liveness of aliases for a object with linear mode , and updates 2 | updates the linear object reference to no longer have the linear mode when aliases are 3 | live. 4 | 5 | (Same concept as Non-Lexical-Lifetimes in Rust) *) 6 | 7 | open Desugaring.Desugared_ast 8 | open Ast.Ast_types 9 | 10 | val type_alias_liveness_block_expr : 11 | Var_name.t 12 | -> Var_name.t list 13 | -> (capability list -> capability -> bool) 14 | -> Var_name.t list 15 | -> block_expr 16 | -> block_expr * Var_name.t list 17 | (** Takes in as args the [Var_name.t] name of the aliased object, the list of possible 18 | aliases, a capability filter function that filters out capabilities needing linear 19 | modes, a list of the current live aliases and the block expression. 20 | 21 | Returns the updated block expression along with the live variables at the start of the 22 | block. *) 23 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_async_capabilities.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks potential accesses to capabilities of objects in async 2 | expressions *) 3 | 4 | open Desugaring.Desugared_ast 5 | 6 | val type_async_capabilities_expr : class_defn list -> expr -> expr 7 | val type_async_capabilities_block_expr : class_defn list -> block_expr -> block_expr 8 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_borrowing.mli: -------------------------------------------------------------------------------- 1 | open Core 2 | open Desugaring.Desugared_ast 3 | open Ast.Ast_types 4 | 5 | val type_function_forward_borrowing_expr : 6 | class_defn list -> function_defn list -> expr -> unit Or_error.t 7 | (** check an identifier isn't borrowed multiple times when passed as args to function 8 | calls (forward borrowing) *) 9 | 10 | val type_function_forward_borrowing_block_expr : 11 | class_defn list -> function_defn list -> block_expr -> unit Or_error.t 12 | 13 | val type_function_reverse_borrowing : 14 | class_defn list 15 | -> string 16 | -> type_expr 17 | -> borrowed_ref option 18 | -> block_expr 19 | -> unit Or_error.t 20 | (** Checks the function return type [type_expr] to determine if linear reference, whether 21 | it returns borrowed refernce [borrowed_ref option] and if the [block_expr] body 22 | expression returns a non-consumed id (not allowed if no reverse borrowing in function 23 | signature). [string] is the error prefix *) 24 | 25 | val type_assign_borrowed_expr : 26 | class_defn list -> function_defn list -> expr -> borrowed_ref option Or_error.t 27 | (** Checks that borrowed values aren't assigned to objects (so don't outlive their 28 | borrowed scope.) Returns whether the current expr / block reduces to a borrowed value. *) 29 | 30 | val type_assign_borrowed_block_expr : 31 | class_defn list -> function_defn list -> block_expr -> borrowed_ref option Or_error.t 32 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_capability_annotations.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Ast.Ast_types 3 | open Data_race_checker_env 4 | 5 | let check_capability_in_class_capabilities class_name class_capabilities capability_name = 6 | match 7 | List.filter 8 | ~f:(fun (TCapability (_, name)) -> capability_name = name) 9 | class_capabilities 10 | with 11 | | [] -> 12 | Error 13 | (Error.of_string 14 | (Fmt.str "Error: capability %s is not present in %s@." 15 | (Capability_name.to_string capability_name) 16 | (Class_name.to_string class_name))) 17 | | capability :: _ -> Ok capability 18 | 19 | let type_field_capability_annotations class_name class_capabilities capability_names = 20 | Result.all 21 | (List.map 22 | ~f:(check_capability_in_class_capabilities class_name class_capabilities) 23 | capability_names) 24 | 25 | let type_param_capability_annotations class_defns = function 26 | | TParam (param_type, _, optional_capability_guards, _) -> ( 27 | let open Result in 28 | match param_type with 29 | | TEClass (obj_class, _) -> ( 30 | let open Result in 31 | match optional_capability_guards with 32 | | Some capability_guards -> 33 | get_class_capabilities obj_class class_defns 34 | |> fun class_capabilities -> 35 | Result.all 36 | (List.map 37 | ~f: 38 | (check_capability_in_class_capabilities obj_class class_capabilities) 39 | capability_guards) 40 | >>| fun _ -> () 41 | | None -> Ok () ) 42 | | TEInt | TEBool | TEVoid -> Ok () 43 | | TEGeneric -> 44 | (* shouldn't occur as desugared earlier - we throw exn to avoid tainting type *) 45 | raise (Ast.Ast_types.NotDesugaredGenericType " param capability annotations") ) 46 | 47 | let type_params_capability_annotations class_defns params = 48 | Result.all_unit (List.map ~f:(type_param_capability_annotations class_defns) params) 49 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_capability_annotations.mli: -------------------------------------------------------------------------------- 1 | (** This module checks that any capability annotations to function/method are valid 2 | capabilities *) 3 | 4 | open Core 5 | open Ast.Ast_types 6 | open Desugaring.Desugared_ast 7 | 8 | val type_params_capability_annotations : class_defn list -> param list -> unit Or_error.t 9 | (** This checks if the guards specified in function/method parameters are actually valid 10 | capabilities *) 11 | 12 | val type_field_capability_annotations : 13 | Class_name.t -> capability list -> Capability_name.t list -> capability list Or_error.t 14 | (** This checks if the capabilities specified in fields are actually valid capabilities of 15 | the class *) 16 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_capability_constraints.mli: -------------------------------------------------------------------------------- 1 | open Core 2 | open Desugaring.Desugared_ast 3 | 4 | val type_param_capability_constraints : 5 | obj_var_and_capabilities list -> block_expr -> block_expr 6 | (** Enforce the constraints placed function params and method effects. *) 7 | 8 | val type_capabilities_constraints_expr : 9 | class_defn list -> function_defn list -> expr -> unit Or_error.t 10 | 11 | val type_capabilities_constraints_block_expr : 12 | class_defn list -> function_defn list -> block_expr -> unit Or_error.t 13 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_concurrent_capability_access.mli: -------------------------------------------------------------------------------- 1 | (** This module types whether capabilities can be concurrently accessed *) 2 | 3 | open Ast.Ast_types 4 | open Desugaring.Desugared_ast 5 | open Core 6 | 7 | val can_concurrently_access_capabilities : 8 | Class_name.t -> class_defn list -> capability -> capability -> bool 9 | (** Checks if the two capabilities of the object of the given class can be used 10 | concurrently *) 11 | 12 | val type_concurrent_capability_constraints_vars : 13 | class_defn list -> obj_var_and_capabilities list -> loc -> unit Or_error.t 14 | (** This function takes a list of free variables and types concurrent accesses of 15 | variables (it is expected that each occurrence of a given free variable corresponds to 16 | a separate thread). *) 17 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_consume_expr.mli: -------------------------------------------------------------------------------- 1 | (** Type checks the consume operation - ensures you can't consume or access already 2 | consumed variables *) 3 | 4 | open Core 5 | open Desugaring.Desugared_ast 6 | 7 | val type_consume_expr : 8 | class_defn list -> expr -> identifier list -> identifier list Or_error.t 9 | (** Takes as arguments the list of consumed variables before this expression, and returns 10 | the list of consumed variables after this expression is abstractly interpreted. *) 11 | 12 | val type_consume_block_expr : 13 | class_defn list -> block_expr -> identifier list -> identifier list Or_error.t 14 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_classes.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for checking the the desugared AST class definitions for 2 | data races *) 3 | 4 | open Core 5 | open Desugaring.Desugared_ast 6 | 7 | val type_data_races_class_defn : 8 | class_defn list 9 | -> function_defn list 10 | -> ignore_data_races:bool 11 | -> class_defn 12 | -> class_defn Or_error.t 13 | (** If ignore_data_races flag set, will check capabilities but won't enforce constraints. *) 14 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Type_consume_expr 3 | open Type_capability_constraints 4 | open Type_read_capabilities 5 | open Type_async_capabilities 6 | open Type_subord_capabilities 7 | open Type_linear_capabilities 8 | open Type_borrowing 9 | open Aggregate_capability_accesses 10 | 11 | let type_data_races_block_expr class_defns function_defns ~ignore_data_races block_expr 12 | obj_vars_and_capabilities = 13 | let open Result in 14 | type_read_capabilities_block_expr block_expr 15 | |> type_subord_capabilities_block_expr class_defns obj_vars_and_capabilities 16 | |> type_async_capabilities_block_expr class_defns 17 | |> type_linear_capabilities_block_expr class_defns 18 | >>= fun typed_block_expr -> 19 | Result.ignore_m (type_consume_block_expr class_defns typed_block_expr []) 20 | >>= fun () -> 21 | type_function_forward_borrowing_block_expr class_defns function_defns typed_block_expr 22 | >>= fun () -> 23 | Result.ignore_m 24 | (type_assign_borrowed_block_expr class_defns function_defns typed_block_expr) 25 | >>= fun () -> 26 | aggregate_capability_accesses_block_expr class_defns function_defns typed_block_expr 27 | |> fun (capability_access_aggregated_block_expr, _) -> 28 | ( if ignore_data_races then Ok () 29 | else 30 | type_capabilities_constraints_block_expr class_defns function_defns 31 | capability_access_aggregated_block_expr ) 32 | >>| fun () -> capability_access_aggregated_block_expr 33 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_expr.mli: -------------------------------------------------------------------------------- 1 | (** This module checks a expression for potential data races *) 2 | 3 | open Core 4 | open Desugaring.Desugared_ast 5 | 6 | val type_data_races_block_expr : 7 | class_defn list 8 | -> function_defn list 9 | -> ignore_data_races:bool 10 | -> block_expr 11 | -> obj_var_and_capabilities list 12 | -> block_expr Or_error.t 13 | (** If ignore_data_races flag set, will check capabilities but won't enforce constraints. *) 14 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_functions.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Ast.Ast_types 3 | open Type_data_races_expr 4 | open Desugaring.Desugared_ast 5 | open Type_capability_annotations 6 | open Type_capability_constraints 7 | open Data_race_checker_env 8 | open Type_borrowing 9 | 10 | let type_data_races_function_defn class_defns function_defns ~ignore_data_races 11 | (TFunction (func_name, maybe_borrowed_ref_ret, ret_type, params, body_expr)) = 12 | let open Result in 13 | type_params_capability_annotations class_defns params 14 | >>= fun () -> 15 | let error_prefix = 16 | Fmt.str "Potential data race in function %s " (Function_name.to_string func_name) 17 | in 18 | let param_obj_var_capabilities = 19 | params_to_obj_vars_and_capabilities class_defns params in 20 | type_function_reverse_borrowing class_defns error_prefix ret_type maybe_borrowed_ref_ret 21 | body_expr 22 | >>= fun () -> 23 | type_param_capability_constraints param_obj_var_capabilities body_expr 24 | |> fun param_constrained_body_expr -> 25 | type_data_races_block_expr class_defns function_defns param_constrained_body_expr 26 | param_obj_var_capabilities ~ignore_data_races 27 | >>| fun data_race_checked_body_expr -> 28 | TFunction 29 | (func_name, maybe_borrowed_ref_ret, ret_type, params, data_race_checked_body_expr) 30 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_functions.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for checking the the desugared AST functions for data races *) 2 | 3 | open Core 4 | open Desugaring.Desugared_ast 5 | 6 | val type_data_races_function_defn : 7 | class_defn list 8 | -> function_defn list 9 | -> ignore_data_races:bool 10 | -> function_defn 11 | -> function_defn Or_error.t 12 | (** If ignore_data_races flag set, will check capabilities but won't enforce constraints. *) 13 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_program.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Type_data_races_expr 3 | open Type_data_races_classes 4 | open Type_data_races_functions 5 | open Desugaring 6 | open Desugared_ast 7 | 8 | let type_data_races_program ~ignore_data_races 9 | (Prog (class_defns, function_defns, main_expr)) = 10 | let open Result in 11 | Result.all 12 | (List.map 13 | ~f:(type_data_races_class_defn class_defns function_defns ~ignore_data_races) 14 | class_defns) 15 | >>= fun data_race_checked_class_defns -> 16 | Result.all 17 | (List.map 18 | ~f:(type_data_races_function_defn class_defns function_defns ~ignore_data_races) 19 | function_defns) 20 | >>= fun data_race_checked_function_defns -> 21 | type_data_races_block_expr class_defns function_defns ~ignore_data_races main_expr [] 22 | >>| fun data_race_checked_main_expr -> 23 | Prog 24 | ( data_race_checked_class_defns 25 | , data_race_checked_function_defns 26 | , data_race_checked_main_expr ) 27 | 28 | let pprint_data_race_checker_ast ppf prog = Pprint_dast.pprint_program ppf prog 29 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_data_races_program.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for checking the the desugared AST for data races *) 2 | 3 | open Core 4 | open Desugaring.Desugared_ast 5 | 6 | val type_data_races_program : ignore_data_races:bool -> program -> program Or_error.t 7 | (** If ignore_data_races flag set, will check capabilities but won't enforce constraints. *) 8 | 9 | val pprint_data_race_checker_ast : Format.formatter -> program -> unit 10 | (** Given a formatter and desugared AST, pretty-print the AST - useful for debugging *) 11 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_linear_capabilities.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks potential accesses to linear capabilities of objects *) 2 | 3 | open Core 4 | open Desugaring.Desugared_ast 5 | open Ast.Ast_types 6 | 7 | val type_linear_obj_method_args : 8 | class_defn list 9 | -> Var_name.t 10 | -> Class_name.t 11 | -> identifier list 12 | -> loc 13 | -> unit Or_error.t 14 | (** Checks that linear objects are not passed as arguments to their methods. *) 15 | 16 | val type_linear_args : class_defn list -> identifier list -> loc -> unit Or_error.t 17 | (** Checks that linear arguments are not duplicated in function calls. *) 18 | 19 | val type_linear_capabilities_block_expr : 20 | class_defn list -> block_expr -> block_expr Or_error.t 21 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_read_capabilities.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks potential accesses to read capabilities of objects *) 2 | 3 | open Desugaring.Desugared_ast 4 | 5 | val type_read_capabilities_expr : expr -> expr 6 | val type_read_capabilities_block_expr : block_expr -> block_expr 7 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_subord_capabilities.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks potential accesses to subordinate capabilities of objects *) 2 | 3 | open Desugaring.Desugared_ast 4 | open Ast.Ast_types 5 | open Core 6 | 7 | val type_subord_capabilities_block_expr : 8 | class_defn list -> obj_var_and_capabilities list -> block_expr -> block_expr 9 | 10 | val type_subord_capabilities_method_prototype : 11 | class_defn list 12 | -> Class_name.t 13 | -> Method_name.t 14 | -> type_expr 15 | -> obj_var_and_capabilities list 16 | -> unit Or_error.t 17 | (** type check any potential subordinate state passed into 18 | ([obj_var_and_capabilities list]) or returned from a method ([type_expr]) given the 19 | class the method belongs to. *) 20 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_subtyping.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Desugaring.Desugared_ast 3 | open Ast.Ast_types 4 | open Type_concurrent_capability_access 5 | open Data_race_checker_env 6 | 7 | let type_subtyping_capabilities class_defns class_name superclass = 8 | get_class_capabilities superclass class_defns 9 | |> fun superclass_capabilities -> 10 | Result.all_unit 11 | (List.map 12 | ~f:(fun superclass_cap1 -> 13 | Result.all_unit 14 | (List.map 15 | ~f:(fun superclass_cap2 -> 16 | if 17 | (* if capabilities can be used concurrently in the superclass they must 18 | also be able to be used concurrently in subclasses *) 19 | (not 20 | (can_concurrently_access_capabilities superclass class_defns 21 | superclass_cap1 superclass_cap2)) 22 | || can_concurrently_access_capabilities class_name class_defns 23 | superclass_cap1 superclass_cap2 24 | then Ok () 25 | else 26 | Error 27 | (Error.of_string 28 | (Fmt.str 29 | "Type error: %s and %s can be used concurrently in %s but not in subclass %s@." 30 | (string_of_cap superclass_cap1) 31 | (string_of_cap superclass_cap2) 32 | (Class_name.to_string superclass) 33 | (Class_name.to_string class_name)))) 34 | superclass_capabilities)) 35 | superclass_capabilities) 36 | 37 | let type_subtyping class_defns (TClass (class_name, maybe_superclass, _, _, _)) = 38 | match maybe_superclass with 39 | | None -> Ok () 40 | | Some superclass -> type_subtyping_capabilities class_defns class_name superclass 41 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/type_subtyping.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks behavioural subtyping - that a class preserves the capability 2 | behaviour of its superclass. *) 3 | 4 | open Core 5 | open Desugaring.Desugared_ast 6 | 7 | val type_subtyping : class_defn list -> class_defn -> unit Or_error.t 8 | -------------------------------------------------------------------------------- /src/frontend/data_race_checker/update_identifier_capabilities.mli: -------------------------------------------------------------------------------- 1 | open Ast.Ast_types 2 | open Desugaring.Desugared_ast 3 | 4 | (** Update identifiers that match the given names, filtering by 5 | [capability list -> capability -> bool] - a function that given a list of all the 6 | capabilities, decides whether a capability should be in the identifier. *) 7 | 8 | val update_matching_identifier_caps_block_expr : 9 | Var_name.t list -> (capability list -> capability -> bool) -> block_expr -> block_expr 10 | -------------------------------------------------------------------------------- /src/frontend/desugaring/count_generics_instantiations.mli: -------------------------------------------------------------------------------- 1 | (** This module is used to count the number of different concrete type parameters generic 2 | classes are instantiated with. *) 3 | 4 | open Typing 5 | open Ast.Ast_types 6 | 7 | val count_generics_instantiations_program : 8 | Typed_ast.program -> (Class_name.t * type_expr list) list 9 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_class_and_function_defns.ml: -------------------------------------------------------------------------------- 1 | open Desugar_expr 2 | open Desugar_overloading 3 | open Ast.Ast_types 4 | open Typing 5 | open Core 6 | 7 | let borrowed_param_vars params = 8 | List.filter_map 9 | ~f:(fun (TParam (_, param_name, _, maybeBorrowed)) -> 10 | match maybeBorrowed with Some Borrowed -> Some param_name | None -> None) 11 | params 12 | 13 | let get_param_types params = 14 | List.map ~f:(fun (TParam (param_type, _, _, _)) -> param_type) params 15 | 16 | let desugar_function_defn class_defns function_defns 17 | (Typed_ast.TFunction (func_name, maybe_borrowed_ref_ret, ret_type, params, body_expr)) 18 | = 19 | desugar_block_expr class_defns function_defns (borrowed_param_vars params) body_expr 20 | |> fun desugared_body_expr -> 21 | Desugared_ast.TFunction 22 | ( name_mangle_if_overloaded_function function_defns func_name (get_param_types params) 23 | , maybe_borrowed_ref_ret 24 | , ret_type 25 | , params 26 | , desugared_body_expr ) 27 | 28 | let desugar_method_defn class_defns function_defns 29 | (Typed_ast.TMethod 30 | (method_name, maybe_borrowed_ref_ret, ret_type, params, capabilities_used, body_expr)) 31 | = 32 | desugar_block_expr class_defns function_defns (borrowed_param_vars params) body_expr 33 | |> fun desugared_body_expr -> 34 | Desugared_ast.TMethod 35 | ( name_mangle_overloaded_method method_name (get_param_types params) 36 | , maybe_borrowed_ref_ret 37 | , ret_type 38 | , params 39 | , capabilities_used 40 | , desugared_body_expr ) 41 | 42 | let desugar_class_defn class_defns function_defns 43 | (* Generics have been desugared earlier in this stage so we ignore whether a class is 44 | generic or not. *) 45 | (Typed_ast.TClass 46 | (class_name, _, maybe_superclass, capabilities, fields, method_defns)) = 47 | List.map ~f:(desugar_method_defn class_defns function_defns) method_defns 48 | |> fun desugared_method_defns -> 49 | Desugared_ast.TClass 50 | (class_name, maybe_superclass, capabilities, fields, desugared_method_defns) 51 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_class_and_function_defns.mli: -------------------------------------------------------------------------------- 1 | (** This module desugars the typed AST's function and class defns *) 2 | 3 | open Typing 4 | 5 | val desugar_function_defn : 6 | Typed_ast.class_defn list 7 | -> Typed_ast.function_defn list 8 | -> Typed_ast.function_defn 9 | -> Desugared_ast.function_defn 10 | 11 | val desugar_class_defn : 12 | Typed_ast.class_defn list 13 | -> Typed_ast.function_defn list 14 | -> Typed_ast.class_defn 15 | -> Desugared_ast.class_defn 16 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_env.mli: -------------------------------------------------------------------------------- 1 | (** This module contains helper functions used in the desugaring stage *) 2 | 3 | open Ast.Ast_types 4 | open Typing 5 | 6 | val get_class_method_defns : 7 | Class_name.t -> Typed_ast.class_defn list -> Typed_ast.method_defn list 8 | 9 | val get_class_capabilities : Class_name.t -> Typed_ast.class_defn list -> capability list 10 | 11 | val get_class_field_capabilities : 12 | Class_name.t -> Field_name.t -> Typed_ast.class_defn list -> capability list 13 | 14 | val maybe_get_superclass : 15 | Class_name.t -> Typed_ast.class_defn list -> Class_name.t option 16 | 17 | val elem_in_list : 'a -> 'a list -> bool 18 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_expr.mli: -------------------------------------------------------------------------------- 1 | (** This module desugars the typed AST's exprs *) 2 | 3 | open Ast.Ast_types 4 | open Typing 5 | 6 | val desugar_expr : 7 | Typed_ast.class_defn list 8 | -> Typed_ast.function_defn list 9 | -> Var_name.t list (** list of borrowed variables *) 10 | -> Typed_ast.expr 11 | -> Desugared_ast.expr 12 | 13 | val desugar_block_expr : 14 | Typed_ast.class_defn list 15 | -> Typed_ast.function_defn list 16 | -> Var_name.t list (** list of borrowed variables *) 17 | -> Typed_ast.block_expr 18 | -> Desugared_ast.block_expr 19 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_generics.ml: -------------------------------------------------------------------------------- 1 | open Count_generics_instantiations 2 | open Name_mangle_generics 3 | open Replace_generic_with_instantiated_class_defns 4 | open Typing 5 | 6 | (* First, count the number of concrete type parameters a generic class has been 7 | instantiated with *) 8 | 9 | (* NEXT, instantiate classes with concrete types *) 10 | 11 | (* Name mangle types so pointing to correct concrete class instantiation *) 12 | 13 | let desugar_generics_program 14 | (Typed_ast.Prog (class_defns, function_defns, main_expr) as prog) = 15 | count_generics_instantiations_program prog 16 | |> fun class_insts -> 17 | replace_generic_with_instantiated_class_defns class_defns class_insts 18 | |> fun instantiated_class_defns -> 19 | name_mangle_generics_usage_program 20 | (Typed_ast.Prog (instantiated_class_defns, function_defns, main_expr)) 21 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_generics.mli: -------------------------------------------------------------------------------- 1 | (** This module desugars generic classes into normal classes, before any further 2 | desugaring occurs *) 3 | 4 | open Typing 5 | 6 | val desugar_generics_program : Typed_ast.program -> Typed_ast.program 7 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_overloading.ml: -------------------------------------------------------------------------------- 1 | open Ast.Ast_types 2 | open Typing 3 | open Core 4 | 5 | let name_mangle_param_types param_types = 6 | String.concat 7 | (List.map 8 | ~f:(function 9 | | TEGeneric -> raise 10 | (NotDesugaredGenericType "Desugaring - overloading") 11 | | TEVoid -> "v" 12 | | TEInt -> "i" 13 | | TEBool -> "b" 14 | | TEClass (class_name, _) -> 15 | let class_name_str = Class_name.to_string class_name in 16 | Fmt.str "%d%s" (String.length class_name_str) class_name_str) 17 | param_types) 18 | 19 | let name_mangle_overloaded_method meth_name param_types = 20 | Method_name.of_string 21 | (Fmt.str "_%s%s" 22 | (Method_name.to_string meth_name) 23 | (name_mangle_param_types param_types)) 24 | 25 | let name_mangle_if_overloaded_function function_defns func_name param_types = 26 | List.filter 27 | ~f:(fun (Typed_ast.TFunction (name, _, _, _, _)) -> name = func_name) 28 | function_defns 29 | |> fun matching_function_defns -> 30 | if List.length matching_function_defns > 1 then 31 | (* Function is overloaded so name mangle *) 32 | Function_name.of_string 33 | (Fmt.str "_%s%s" 34 | (Function_name.to_string func_name) 35 | (name_mangle_param_types param_types)) 36 | else func_name 37 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_overloading.mli: -------------------------------------------------------------------------------- 1 | (** Desugar overloaded functions and methods *) 2 | 3 | open Ast.Ast_types 4 | open Typing 5 | 6 | val name_mangle_overloaded_method : Method_name.t -> type_expr list -> Method_name.t 7 | 8 | val name_mangle_if_overloaded_function : 9 | Typed_ast.function_defn list -> Function_name.t -> type_expr list -> Function_name.t 10 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_program.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Typing 3 | open Desugar_expr 4 | open Desugar_class_and_function_defns 5 | open Remove_variable_shadowing 6 | open Pprint_dast 7 | open Desugar_generics 8 | 9 | let desugar_program prog = 10 | desugar_generics_program prog 11 | |> fun (Typed_ast.Prog (class_defns, function_defns, main_expr)) -> 12 | List.map ~f:(desugar_class_defn class_defns function_defns) class_defns 13 | |> fun desugared_class_defns -> 14 | List.map ~f:(desugar_function_defn class_defns function_defns) function_defns 15 | |> fun desugared_function_defns -> 16 | desugar_block_expr class_defns function_defns [] main_expr 17 | |> fun desugared_main_expr -> 18 | let desugared_program = 19 | Desugared_ast.Prog 20 | (desugared_class_defns, desugared_function_defns, desugared_main_expr) in 21 | remove_var_shadowing_program desugared_program 22 | 23 | let pprint_desugared_ast ppf prog = pprint_program ppf prog 24 | -------------------------------------------------------------------------------- /src/frontend/desugaring/desugar_program.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for desugared the typed AST. The desugared AST simplifies 2 | the expressions in the typed ASTs It provides the invariant that there is no variable 3 | shadowing in the desugared AST *) 4 | 5 | open Core 6 | open Typing 7 | 8 | val desugar_program : Typed_ast.program -> Desugared_ast.program Or_error.t 9 | 10 | val pprint_desugared_ast : Format.formatter -> Desugared_ast.program -> unit 11 | (** Given a formatter and desugared AST, pretty-print the AST - useful for debugging *) 12 | -------------------------------------------------------------------------------- /src/frontend/desugaring/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name desugaring) 3 | (public_name desugaring) 4 | (libraries core fmt typing) 5 | (preprocess 6 | (pps bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 9 | -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /src/frontend/desugaring/free_obj_vars_expr.mli: -------------------------------------------------------------------------------- 1 | open Ast.Ast_types 2 | open Typing 3 | 4 | val free_obj_vars_expr : 5 | Typed_ast.class_defn list 6 | -> Typed_ast.expr 7 | -> (Var_name.t * Class_name.t * capability list) list 8 | (** Return a list of the object free variables in an expr and their associated classes and 9 | capabilities *) 10 | 11 | val free_obj_vars_block_expr : 12 | Typed_ast.class_defn list 13 | -> Typed_ast.block_expr 14 | -> (Var_name.t * Class_name.t * capability list) list 15 | (** Return a list of the object free variables in a block expr and their associated 16 | classes and capabilities *) 17 | -------------------------------------------------------------------------------- /src/frontend/desugaring/name_mangle_generics.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for name-mangling generics, so Foo -> _Fooint *) 2 | 3 | open Ast.Ast_types 4 | open Typing 5 | 6 | val name_mangle_generic_class : Class_name.t -> type_expr -> Class_name.t 7 | val name_mangle_if_generic_class : Class_name.t -> type_expr option -> Class_name.t 8 | val name_mangle_generics_usage_program : Typed_ast.program -> Typed_ast.program 9 | -------------------------------------------------------------------------------- /src/frontend/desugaring/pprint_dast.mli: -------------------------------------------------------------------------------- 1 | (** This module pretty prints the desugared AST of a Bolt program *) 2 | 3 | val pprint_program : Format.formatter -> Desugared_ast.program -> unit 4 | -------------------------------------------------------------------------------- /src/frontend/desugaring/remove_variable_shadowing.mli: -------------------------------------------------------------------------------- 1 | open Core 2 | open Ast.Ast_types 3 | 4 | (** This module takes in a desugared AST and renames variable names to ensure there is no 5 | variable shadowing *) 6 | 7 | type var_name_map = (Var_name.t * Var_name.t) list 8 | 9 | val remove_var_shadowing_program : 10 | Desugared_ast.program -> Desugared_ast.program Or_error.t 11 | 12 | val remove_var_shadowing_expr : 13 | Desugared_ast.expr -> var_name_map -> (Desugared_ast.expr * var_name_map) Or_error.t 14 | -------------------------------------------------------------------------------- /src/frontend/desugaring/replace_generic_with_instantiated_class_defns.mli: -------------------------------------------------------------------------------- 1 | (** This module replaces the generic class definitions with their instantiations (one for 2 | each concrete type parameter they're instantiated with) *) 3 | 4 | open Ast.Ast_types 5 | open Typing 6 | 7 | val replace_generic_with_instantiated_class_defns : 8 | Typed_ast.class_defn list 9 | -> (Class_name.t * type_expr list) list 10 | -> Typed_ast.class_defn list 11 | -------------------------------------------------------------------------------- /src/frontend/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name main) 3 | (modules main) 4 | (libraries core fmt compile_program_ir) 5 | (preprocess 6 | (pps ppx_jane bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -annotated-ignores -styler -pretty -dated-deprecation))) 9 | 10 | (library 11 | (public_name bolt) 12 | (name compile_program_ir) 13 | (modules compile_program_ir) 14 | (libraries core fmt parsing typing data_race_checker ir_gen) 15 | (preprocess 16 | (pps ppx_jane bisect_ppx --conditional)) 17 | (lint 18 | (pps ppx_js_style -annotated-ignores -styler -pretty -dated-deprecation))) 19 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name ir_gen) 3 | (public_name ir_gen) 4 | (libraries core fmt desugaring ast) 5 | (flags 6 | (:standard -w -39)) 7 | (preprocess 8 | (pps ppx_deriving_protobuf bisect_ppx --conditional)) 9 | (lint 10 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 11 | -dated-deprecation))) 12 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_class_and_function_defns.mli: -------------------------------------------------------------------------------- 1 | (** Generates serialisable IR representations of the desugared class and function 2 | definitions *) 3 | 4 | open Desugaring 5 | 6 | val ir_gen_class_defns : Desugared_ast.class_defn list -> Frontend_ir.class_defn list 7 | 8 | val ir_gen_function_defns : 9 | Desugared_ast.class_defn list 10 | -> Desugared_ast.function_defn list 11 | -> Frontend_ir.function_defn list 12 | (** Generates IR function defns from the function defns and each class's method defns *) 13 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_env.mli: -------------------------------------------------------------------------------- 1 | (** This module contains helper functions used in the frontend IR generation stage *) 2 | 3 | open Ast.Ast_types 4 | open Desugaring.Desugared_ast 5 | 6 | val name_mangle_method_name : Method_name.t -> Class_name.t -> string 7 | (** Name mangling of method names - takes as input the method name and the class of the 8 | object calling it and returns the mangled name *) 9 | 10 | val ir_gen_field_index : Field_name.t -> Class_name.t -> class_defn list -> int 11 | (** Given a field and the type of the object to which it belongs, and a list of class 12 | defns, get the field index within the list of field defns in the corresponding class 13 | defn *) 14 | 15 | val get_class_fields : Class_name.t -> class_defn list -> field_defn list 16 | 17 | (** VTable methods *) 18 | 19 | val ir_gen_vtable : Class_name.t -> class_defn list -> string list 20 | val ir_gen_vtable_method_index : Method_name.t -> Class_name.t -> class_defn list -> int 21 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_expr.mli: -------------------------------------------------------------------------------- 1 | open Desugaring 2 | 3 | val ir_gen_expr : Desugared_ast.class_defn list -> Desugared_ast.expr -> Frontend_ir.expr 4 | (** Generates the serialisable IR from a desugared expr *) 5 | 6 | val ir_gen_block_expr : 7 | Desugared_ast.class_defn list -> Desugared_ast.block_expr -> Frontend_ir.expr list 8 | (** Generates the serialisable IR from a desugared block expr *) 9 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_operators.ml: -------------------------------------------------------------------------------- 1 | let ir_gen_un_op = function 2 | | Ast.Ast_types.UnOpNot -> Frontend_ir.UnOpNot 3 | | Ast.Ast_types.UnOpNeg -> Frontend_ir.UnOpNeg 4 | 5 | let ir_gen_bin_op = function 6 | | Ast.Ast_types.BinOpPlus -> Frontend_ir.BinOpPlus 7 | | Ast.Ast_types.BinOpMinus -> Frontend_ir.BinOpMinus 8 | | Ast.Ast_types.BinOpMult -> Frontend_ir.BinOpMult 9 | | Ast.Ast_types.BinOpIntDiv -> Frontend_ir.BinOpIntDiv 10 | | Ast.Ast_types.BinOpRem -> Frontend_ir.BinOpRem 11 | | Ast.Ast_types.BinOpLessThan -> Frontend_ir.BinOpLessThan 12 | | Ast.Ast_types.BinOpLessThanEq -> Frontend_ir.BinOpLessThanEq 13 | | Ast.Ast_types.BinOpGreaterThan -> Frontend_ir.BinOpGreaterThan 14 | | Ast.Ast_types.BinOpGreaterThanEq -> Frontend_ir.BinOpGreaterThanEq 15 | | Ast.Ast_types.BinOpAnd -> Frontend_ir.BinOpAnd 16 | | Ast.Ast_types.BinOpOr -> Frontend_ir.BinOpOr 17 | | Ast.Ast_types.BinOpEq -> Frontend_ir.BinOpEq 18 | | Ast.Ast_types.BinOpNotEq -> Frontend_ir.BinOpNotEq 19 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_operators.mli: -------------------------------------------------------------------------------- 1 | (** Generates the serialisable IR representation of the operators *) 2 | 3 | val ir_gen_un_op : Ast.Ast_types.un_op -> Frontend_ir.un_op 4 | val ir_gen_bin_op : Ast.Ast_types.bin_op -> Frontend_ir.bin_op 5 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_program.ml: -------------------------------------------------------------------------------- 1 | open Desugaring 2 | open Ir_gen_expr 3 | open Ir_gen_class_and_function_defns 4 | open Pprint_fir 5 | 6 | let ir_gen_program (Desugared_ast.Prog (class_defns, function_defns, main_expr)) = 7 | ir_gen_class_defns class_defns 8 | |> fun ir_class_defns -> 9 | ir_gen_function_defns class_defns function_defns 10 | |> fun ir_function_defns -> 11 | ir_gen_block_expr class_defns main_expr 12 | |> fun ir_main_exprs -> 13 | Frontend_ir.Prog (ir_class_defns, ir_function_defns, ir_main_exprs) 14 | 15 | let pprint_frontend_ir ppf (prog : Frontend_ir.program) = pprint_program ppf prog 16 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_program.mli: -------------------------------------------------------------------------------- 1 | (** Given the desugared AST of a bolt program, convert it to serialisable IR (to be passed 2 | to the compiler middle/backend). *) 3 | 4 | open Desugaring 5 | 6 | val ir_gen_program : Desugared_ast.program -> Frontend_ir.program 7 | 8 | val pprint_frontend_ir : Format.formatter -> Frontend_ir.program -> unit 9 | (** Given a formatter and the generated IR, pretty-print the IR - useful for debugging *) 10 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_protobuf.ml: -------------------------------------------------------------------------------- 1 | open Frontend_ir 2 | 3 | let ir_gen_protobuf program out_chan = 4 | let protobuf_message = Protobuf.Encoder.encode_exn program_to_protobuf program in 5 | output_bytes out_chan protobuf_message 6 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/ir_gen_protobuf.mli: -------------------------------------------------------------------------------- 1 | val ir_gen_protobuf : Frontend_ir.program -> out_channel -> unit 2 | (** Given the IR from a program, write the serialised protobuf bytes output to the 3 | [out_channel] *) 4 | -------------------------------------------------------------------------------- /src/frontend/ir_gen/pprint_fir.mli: -------------------------------------------------------------------------------- 1 | (** This module pretty prints the serialisable frontend IR of a Bolt program *) 2 | 3 | val pprint_program : Format.formatter -> Frontend_ir.program -> unit 4 | -------------------------------------------------------------------------------- /src/frontend/main.mli: -------------------------------------------------------------------------------- 1 | (** This is the entry point for command-line frontend IR compilation of Bolt programs. 2 | 3 | Execution options are as follows: 4 | 5 | main.exe [FILENAME] 6 | 7 | A list of execution options 8 | 9 | === flags === 10 | 11 | [-check-data-races] Check programs for potential data-races 12 | 13 | [-print-parsed-ast] Pretty print the parsed AST of the program 14 | 15 | [-print-typed-ast] Pretty print the typed AST of the program 16 | 17 | [-print-desugared-ast] Pretty print the desugared AST of the program 18 | 19 | [-print-data-race-ast] Pretty print the AST after the program is type-checked for data 20 | races 21 | 22 | [-print-frontend-ir] Pretty print the generated IR of the program 23 | 24 | [-ignore-data-races] Don't enforce data race type-checking constraints. 25 | 26 | [-build-info] print info about this build and exit 27 | 28 | [-version] print the version of this build and exit 29 | 30 | [-help] print this help text and exit (alias: -?) 31 | 32 | ======= 33 | 34 | If no flags are provided, then the serialised IR is output to stdout *) 35 | -------------------------------------------------------------------------------- /src/frontend/parsing/README.md: -------------------------------------------------------------------------------- 1 | ## Parsing 2 | 3 | This folder contains the code for the parsing stage of the compiler. 4 | 5 | There is an [accompanying tutorial](https://mukulrathi.netlify.app/create-your-own-programming-language/parsing-ocamllex-menhir/). 6 | 7 | `lexer.mll` and `parser.mly` are the specifications for the lexer (which uses OCamllex) and parser (which uses Menhir) respectively. 8 | 9 | The `parsed_ast.mli` file contains the type definition for the parsed Abstract Syntax Tree. 10 | 11 | There are a couple of modules to pretty print tokens and the parsed Abstract Syntax Tree. 12 | 13 | Finally, the main interface for this parsing library is `lex_and_parse.mli` - this exposes a function that takes in a buffer that the lexer reads from, and returns the parsed AST. It also exposes a pretty-print function to view the output parsed AST. 14 | -------------------------------------------------------------------------------- /src/frontend/parsing/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name parsing) 3 | (public_name parsing) 4 | (libraries core fmt ast) 5 | (preprocess 6 | (pps bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -annotated-ignores -styler -pretty -dated-deprecation))) 9 | 10 | (ocamllex lexer) 11 | 12 | (menhir 13 | (modules parser)) 14 | -------------------------------------------------------------------------------- /src/frontend/parsing/lex_and_parse.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Lexer 3 | open Lexing 4 | 5 | (* Prints the line number and character number where the error occurred.*) 6 | let print_error_position lexbuf = 7 | let pos = lexbuf.lex_curr_p in 8 | Fmt.str "Line:%d Position:%d" pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1) 9 | 10 | let parse_program lexbuf = 11 | try Ok (Parser.program Lexer.read_token lexbuf) with 12 | (* Unfortunately the lexer and parser throw exceptions - so here we swallow the exn into 13 | the Result monad*) 14 | | SyntaxError msg -> 15 | let error_msg = Fmt.str "%s: %s@." (print_error_position lexbuf) msg in 16 | Error (Error.of_string error_msg) 17 | | Parser.Error -> 18 | let error_msg = Fmt.str "%s: syntax error@." (print_error_position lexbuf) in 19 | Error (Error.of_string error_msg) 20 | 21 | let pprint_parsed_ast ppf (prog : Parsed_ast.program) = 22 | Pprint_past.pprint_program ppf prog 23 | -------------------------------------------------------------------------------- /src/frontend/parsing/lex_and_parse.mli: -------------------------------------------------------------------------------- 1 | (** This module executes the lexer and parser. It acts as an interface between the parsing 2 | code and the main method (abstracting away the underlying implementation) *) 3 | 4 | open Core 5 | 6 | val parse_program : Lexing.lexbuf -> Parsed_ast.program Or_error.t 7 | (** Given a lex buffer to read a bolt program from, parse the program and return the AST 8 | if successful*) 9 | 10 | val pprint_parsed_ast : Format.formatter -> Parsed_ast.program -> unit 11 | (** Given a formatter and parsed AST, pretty-print the AST - useful for debugging *) 12 | -------------------------------------------------------------------------------- /src/frontend/parsing/pprint_parser_tokens.mli: -------------------------------------------------------------------------------- 1 | (** This module is responsible for pretty-printing the tokens output by the lexer. It is 2 | used in the Alcotest lexer unit tests*) 3 | 4 | val pprint_tokens : Parser.token Fmt.t 5 | -------------------------------------------------------------------------------- /src/frontend/parsing/pprint_past.mli: -------------------------------------------------------------------------------- 1 | (** This module pretty prints the parsed AST of a Bolt program *) 2 | 3 | val pprint_program : Format.formatter -> Parsed_ast.program -> unit 4 | -------------------------------------------------------------------------------- /src/frontend/typing/README.md: -------------------------------------------------------------------------------- 1 | ## Typing 2 | 3 | The stage type-checks the core language (this is the "traditional" form of type-checking found in most compilers - the data-race type-checker is a separate stage). 4 | 5 | This stage occurs after the lexing+parsing - `type_program.mli` acts as the interface to the library - this takes in the program's parsed AST and outputs the Typed AST. It also has a pretty-print function to print out the typed AST. 6 | 7 | The `typed_ast.mli` contains the type definition for the typed AST. 8 | 9 | The rest of this folder type-checks specific language constructs. The main file is `type_expr.mli` which type-checks a given Bolt expression. This is used by the `type_classes.mli` and `type_functions.mli` to check function and method bodies. 10 | 11 | Function/method overloading, inheritance and generics are language constructs are type-checked in their own files. 12 | 13 | Finally `type_env.ml` contains a bunch of helper functions used in the stage. 14 | -------------------------------------------------------------------------------- /src/frontend/typing/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name typing) 3 | (public_name typing) 4 | (libraries core fmt parsing) 5 | (preprocess 6 | (pps bisect_ppx --conditional)) 7 | (lint 8 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 9 | -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /src/frontend/typing/pprint_tast.mli: -------------------------------------------------------------------------------- 1 | (** This module pretty prints the typed AST of a Bolt program *) 2 | 3 | val pprint_program : Format.formatter -> Typed_ast.program -> unit 4 | -------------------------------------------------------------------------------- /src/frontend/typing/type_classes.mli: -------------------------------------------------------------------------------- 1 | (** Check class definitions are well-formed (using the function definitions to check the 2 | function calls in methods) *) 3 | 4 | open Core 5 | open Parsing 6 | 7 | val type_class_defns : 8 | Parsed_ast.class_defn list 9 | -> Parsed_ast.function_defn list 10 | -> Typed_ast.class_defn list Or_error.t 11 | -------------------------------------------------------------------------------- /src/frontend/typing/type_env.mli: -------------------------------------------------------------------------------- 1 | (** This module consists of helper functions for manipulating the type environment during 2 | the core language type-checking phase. *) 3 | 4 | open Ast.Ast_types 5 | open Core 6 | open Parsing 7 | 8 | type type_binding = Var_name.t * type_expr 9 | type type_env = type_binding list 10 | 11 | (** A bunch of getter methods used in type-checking the core language *) 12 | 13 | val get_var_type : Var_name.t -> type_env -> loc -> type_expr Or_error.t 14 | 15 | val get_class_field : 16 | Field_name.t 17 | -> Parsed_ast.class_defn list 18 | -> Parsed_ast.class_defn 19 | -> type_expr option 20 | -> loc 21 | -> field_defn Or_error.t 22 | 23 | val get_class_methods : 24 | Parsed_ast.class_defn list 25 | -> Parsed_ast.class_defn 26 | -> type_expr option 27 | -> loc 28 | -> Parsed_ast.method_defn list Or_error.t 29 | 30 | val get_obj_class_defn : 31 | Var_name.t 32 | -> type_env 33 | -> Parsed_ast.class_defn list 34 | -> loc 35 | -> (Parsed_ast.class_defn * type_expr option) Or_error.t 36 | (** returns class, with any type parameters instantiated *) 37 | 38 | val get_class_defn : 39 | Class_name.t -> Parsed_ast.class_defn list -> loc -> Parsed_ast.class_defn Or_error.t 40 | 41 | val get_instantiated_class_defn : 42 | Class_name.t 43 | -> Parsed_ast.class_defn list 44 | -> type_expr option 45 | -> loc 46 | -> Parsed_ast.class_defn Or_error.t 47 | 48 | val get_class_capabilities : 49 | Class_name.t -> Parsed_ast.class_defn list -> capability list Or_error.t 50 | 51 | val get_method_capability_annotations : 52 | Class_name.t -> capability list -> Capability_name.t list -> capability list Or_error.t 53 | 54 | (** Checker methods - check invariants *) 55 | 56 | val check_type_valid : 57 | type_expr -> Parsed_ast.class_defn sexp_list -> string -> unit Or_error.t 58 | 59 | val check_no_duplicate_var_declarations_in_block : 60 | Parsed_ast.expr list -> loc -> unit Or_error.t 61 | 62 | val check_identifier_assignable : 63 | Parsed_ast.class_defn list 64 | -> Parsed_ast.identifier 65 | -> type_env 66 | -> loc 67 | -> unit Or_error.t 68 | 69 | val check_identifier_consumable : 70 | Parsed_ast.class_defn list 71 | -> Parsed_ast.identifier 72 | -> type_env 73 | -> loc 74 | -> unit Or_error.t 75 | 76 | val check_variable_declarable : Var_name.t -> loc -> unit Or_error.t 77 | -------------------------------------------------------------------------------- /src/frontend/typing/type_expr.mli: -------------------------------------------------------------------------------- 1 | (** Infer type parsed expressions given the class and function definitions - return the 2 | expression annotated with types if type-checking succeeds. *) 3 | 4 | open Ast.Ast_types 5 | open Parsing 6 | open Core 7 | open Type_env 8 | 9 | val type_expr : 10 | Parsed_ast.class_defn list 11 | -> Parsed_ast.function_defn list 12 | -> Parsed_ast.expr 13 | -> type_env 14 | -> (Typed_ast.expr * type_expr) Or_error.t 15 | 16 | val type_block_expr : 17 | Parsed_ast.class_defn list 18 | -> Parsed_ast.function_defn list 19 | -> Parsed_ast.block_expr 20 | -> type_env 21 | -> (Typed_ast.block_expr * type_expr) Or_error.t 22 | -------------------------------------------------------------------------------- /src/frontend/typing/type_functions.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Ast.Ast_types 3 | open Parsing 4 | open Type_expr 5 | open Type_overloading 6 | open Type_generics 7 | open Type_inheritance 8 | open Type_env 9 | 10 | let init_env_from_params params = 11 | List.map 12 | ~f:(function TParam (type_expr, param_name, _, _) -> (param_name, type_expr)) 13 | params 14 | 15 | let type_function_type_sig class_defns func_name params return_type = 16 | let function_error_prefix = 17 | Fmt.str "Type error for function %s" (Function_name.to_string func_name) in 18 | let return_type_error_prefix = Fmt.str "%s return type" function_error_prefix in 19 | let open Result in 20 | check_type_valid return_type class_defns return_type_error_prefix 21 | >>= fun () -> 22 | Result.all_unit 23 | (List.map 24 | ~f:(fun (TParam (param_type, param_name, _, _)) -> 25 | let param_error_prefix = 26 | Fmt.str "%s param %s" function_error_prefix (Var_name.to_string param_name) 27 | in 28 | check_type_valid param_type class_defns param_error_prefix) 29 | params) 30 | 31 | let type_function_defn class_defns function_defns 32 | ( Parsed_ast.TFunction 33 | (func_name, maybe_borrowed_ret_ref, return_type, params, body_expr) as 34 | curr_function_defn ) = 35 | let open Result in 36 | type_function_type_sig class_defns func_name params return_type 37 | >>= fun () -> 38 | type_generics_usage_function_defn curr_function_defn 39 | >>= fun () -> 40 | type_block_expr class_defns function_defns body_expr (init_env_from_params params) 41 | >>= fun (typed_body_expr, body_return_type) -> 42 | (* We throw away returned expr if return type is void *) 43 | if return_type = TEVoid || is_subtype_of class_defns body_return_type return_type then 44 | Ok 45 | (Typed_ast.TFunction 46 | (func_name, maybe_borrowed_ret_ref, return_type, params, typed_body_expr)) 47 | else 48 | Error 49 | (Error.of_string 50 | (Fmt.str 51 | "Type Error for function %s: expected return type of %s but got %s instead" 52 | (Function_name.to_string func_name) 53 | (string_of_type return_type) 54 | (string_of_type body_return_type))) 55 | 56 | let type_function_defns class_defns function_defns = 57 | let open Result in 58 | type_overloaded_function_defns function_defns 59 | >>= fun () -> 60 | Result.all (List.map ~f:(type_function_defn class_defns function_defns) function_defns) 61 | -------------------------------------------------------------------------------- /src/frontend/typing/type_functions.mli: -------------------------------------------------------------------------------- 1 | open Core 2 | open Parsing 3 | 4 | val type_function_defns : 5 | Parsed_ast.class_defn list 6 | -> Parsed_ast.function_defn list 7 | -> Typed_ast.function_defn list Or_error.t 8 | (** Type-check function defns, using types defined in the class defns, and return the 9 | typed function defn augmented with type information *) 10 | -------------------------------------------------------------------------------- /src/frontend/typing/type_generics.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks the usage of generics. *) 2 | 3 | open Ast.Ast_types 4 | open Core 5 | open Parsing 6 | 7 | (** Type usage of generic types *) 8 | 9 | val type_generics_usage_main_expr : Parsed_ast.block_expr -> unit Or_error.t 10 | val type_generics_usage_function_defn : Parsed_ast.function_defn -> unit Or_error.t 11 | val type_generics_usage_class_defn : Parsed_ast.class_defn -> unit Or_error.t 12 | 13 | (** Instantiate generic types *) 14 | 15 | val instantiate_maybe_generic_this : Parsed_ast.class_defn -> Type_env.type_binding 16 | (** Instantiate "this" - to be used when type-checking a class *) 17 | 18 | val instantiate_maybe_generic_class_defn : 19 | type_expr option (** maybe instantiated with type param *) 20 | -> Parsed_ast.class_defn 21 | -> loc 22 | -> Parsed_ast.class_defn Or_error.t 23 | (** Returns the class definition replacing any generic type parameters with the concrete 24 | type passed in. *) 25 | -------------------------------------------------------------------------------- /src/frontend/typing/type_inheritance.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks class inheritance *) 2 | 3 | open Core 4 | open Ast.Ast_types 5 | open Parsing 6 | 7 | val is_subtype_of : Parsed_ast.class_defn list -> type_expr -> type_expr -> bool 8 | 9 | val are_subtypes_of : 10 | Parsed_ast.class_defn list -> type_expr list -> type_expr list -> bool 11 | 12 | val type_class_inheritance : 13 | Parsed_ast.class_defn -> Parsed_ast.class_defn list -> unit Or_error.t 14 | -------------------------------------------------------------------------------- /src/frontend/typing/type_overloading.mli: -------------------------------------------------------------------------------- 1 | (** This module type-checks overloading in functions and methods. *) 2 | 3 | open Core 4 | open Ast.Ast_types 5 | open Parsing 6 | 7 | val type_overloaded_function_defns : Parsed_ast.function_defn list -> unit Or_error.t 8 | (** Check functions are overloaded correctly (i.e. each has a different number/type of 9 | args) *) 10 | 11 | val type_overloaded_method_defns : Parsed_ast.method_defn list -> unit Or_error.t 12 | 13 | val get_matching_function_type : 14 | Parsed_ast.class_defn list 15 | -> Function_name.t 16 | -> type_expr list 17 | -> Parsed_ast.function_defn list 18 | -> loc 19 | -> (type_expr list * type_expr) Or_error.t 20 | (** We pass in the args types to find the matching overloaded function/method defn. *) 21 | 22 | val get_matching_method_type : 23 | Parsed_ast.class_defn list 24 | -> Method_name.t 25 | -> type_expr list 26 | -> Parsed_ast.class_defn 27 | -> type_expr option 28 | -> loc 29 | -> (type_expr sexp_list * type_expr) Or_error.t 30 | -------------------------------------------------------------------------------- /src/frontend/typing/type_program.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Type_generics 3 | open Parsing 4 | 5 | let type_program (Parsed_ast.Prog (class_defns, function_defns, main_expr)) = 6 | let open Result in 7 | (* Check if class defns well-formed *) 8 | Type_classes.type_class_defns class_defns function_defns 9 | >>= fun typed_class_defns -> 10 | Type_functions.type_function_defns class_defns function_defns 11 | >>= fun typed_function_defns -> 12 | type_generics_usage_main_expr main_expr 13 | >>= fun () -> 14 | (* Type check the expression *) 15 | Type_expr.type_block_expr class_defns function_defns main_expr [] 16 | >>| fun (typed_main_expr, _) -> 17 | Typed_ast.Prog (typed_class_defns, typed_function_defns, typed_main_expr) 18 | 19 | let pprint_typed_ast ppf (prog : Typed_ast.program) = Pprint_tast.pprint_program ppf prog 20 | -------------------------------------------------------------------------------- /src/frontend/typing/type_program.mli: -------------------------------------------------------------------------------- 1 | (** Given the parsed AST of a bolt program, type-check it and if type-checking succeeds 2 | return the AST with type annotations. 3 | 4 | NB: We are not checking for data-race freedom in this stage of the type-checker. *) 5 | 6 | open Core 7 | open Parsing 8 | 9 | val type_program : Parsed_ast.program -> Typed_ast.program Or_error.t 10 | 11 | val pprint_typed_ast : Format.formatter -> Typed_ast.program -> unit 12 | (** Given a formatter and typed AST, pretty-print the AST - useful for debugging *) 13 | -------------------------------------------------------------------------------- /src/llvm-backend/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_binary") 2 | 3 | cc_binary( 4 | name = "main", 5 | srcs = ["main.cc"], 6 | deps = [ 7 | "//src/llvm-backend/deserialise_ir:deserialise_ir", "//src/llvm-backend/llvm_ir_codegen:llvm_ir_codegen", "@llvm" 8 | ], 9 | ) 10 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "deserialise_ir", 5 | srcs = glob(["*.cc"]), 6 | hdrs = glob(["*.h"]), 7 | visibility = ["//src/llvm-backend:__pkg__", "//src/llvm-backend/llvm_ir_codegen:__pkg__", "//tests/llvm-backend/deserialise_ir:__pkg__"], 8 | deps = ["//src:frontend_ir_cc_proto", "@llvm"] 9 | ) 10 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/class_ir.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "src/llvm-backend/deserialise_ir/class_ir.h" 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "src/frontend_ir.pb.h" 10 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 11 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 12 | 13 | ClassIR::ClassIR(const Frontend_ir::class_defn &classDefn) { 14 | className = classDefn.tclass()._0(); 15 | for (int i = 0; i < classDefn.tclass()._1_size(); i++) { 16 | fields.push_back(deserialiseType(classDefn.tclass()._1(i))); 17 | } 18 | for (int i = 0; i < classDefn.tclass()._2_size(); i++) { 19 | vtable.push_back(classDefn.tclass()._2(i)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/class_ir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "src/frontend_ir.pb.h" 8 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 9 | 10 | struct ClassIR { 11 | std::string className; 12 | std::vector> fields; 13 | std::vector vtable; 14 | ClassIR(const Frontend_ir::class_defn &classDefn); 15 | }; 16 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/deserialise_protobuf.cc: -------------------------------------------------------------------------------- 1 | #include "src/llvm-backend/deserialise_ir/deserialise_protobuf.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "src/llvm-backend/deserialise_ir/program_ir.h" 9 | 10 | Frontend_ir::program deserialiseProtobufFile(std::string &filePath) { 11 | Frontend_ir::program program; 12 | std::fstream fileIn(filePath, std::ios::in | std::ios::binary); 13 | if (!fileIn) { 14 | throw DeserialiseProtobufException("File not found."); 15 | } 16 | if (!program.ParseFromIstream(&fileIn)) { 17 | throw DeserialiseProtobufException("Protobuf not deserialised from file."); 18 | } 19 | return program; 20 | } 21 | 22 | std::unique_ptr protobufToIR(const Frontend_ir::program &program) { 23 | return std::unique_ptr(new ProgramIR(program)); 24 | } 25 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/deserialise_protobuf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "src/llvm-backend/deserialise_ir/program_ir.h" 8 | 9 | Frontend_ir::program deserialiseProtobufFile(std::string &filePath); 10 | std::unique_ptr protobufToIR(const Frontend_ir::program &program); 11 | 12 | class DeserialiseProtobufException : public std::exception { 13 | std::string errorMessage; 14 | 15 | public: 16 | DeserialiseProtobufException(const char msg[]) { 17 | std::string errorMessage(msg); 18 | } 19 | const char *what() const throw() { return errorMessage.c_str(); } 20 | }; 21 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/function_ir.cc: -------------------------------------------------------------------------------- 1 | #include "src/llvm-backend/deserialise_ir/function_ir.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "src/frontend_ir.pb.h" 9 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 10 | #include "src/llvm-backend/deserialise_ir/ir_visitor.h" 11 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 12 | 13 | ParameterIR::ParameterIR(const Frontend_ir::param::_TParam ¶m) { 14 | paramType = deserialiseType(param._0()); 15 | paramName = param._1(); 16 | } 17 | 18 | FunctionIR::FunctionIR(const Frontend_ir::function_defn &functionDefn) { 19 | functionName = functionDefn.tfunction()._0(); 20 | returnType = deserialiseType(functionDefn.tfunction()._1()); 21 | 22 | for (int i = 0; i < functionDefn.tfunction()._2_size(); i++) { 23 | params.push_back(std::unique_ptr( 24 | new ParameterIR(functionDefn.tfunction()._2(i).tparam()))); 25 | } 26 | 27 | for (int i = 0; i < functionDefn.tfunction()._3_size(); i++) { 28 | bodyExpr.push_back(deserialiseExpr(functionDefn.tfunction()._3(i))); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/function_ir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "src/frontend_ir.pb.h" 8 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 9 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 10 | /* Visitor class declarations */ 11 | class IRVisitor; 12 | 13 | struct ParameterIR { 14 | std::unique_ptr paramType; 15 | std::string paramName; 16 | ParameterIR(const Frontend_ir::param::_TParam ¶m); 17 | }; 18 | 19 | struct FunctionIR { 20 | std::string functionName; 21 | std::unique_ptr returnType; 22 | std::vector> params; 23 | std::vector> bodyExpr; 24 | 25 | FunctionIR(const Frontend_ir::function_defn &functionDefn); 26 | }; 27 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/ir_visitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | 6 | #include "llvm/IR/Type.h" 7 | #include "llvm/IR/Value.h" 8 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 9 | #include "src/llvm-backend/deserialise_ir/function_ir.h" 10 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 11 | 12 | class IRVisitor { 13 | public: 14 | virtual llvm::Value *codegen(const IdentifierVarIR &var) = 0; 15 | virtual llvm::Value *codegen(const IdentifierObjFieldIR &objField) = 0; 16 | 17 | virtual llvm::Value *codegen(const ExprIntegerIR &expr) = 0; 18 | virtual llvm::Value *codegen(const ExprBooleanIR &expr) = 0; 19 | virtual llvm::Value *codegen(const ExprIdentifierIR &expr) = 0; 20 | virtual llvm::Value *codegen(const ExprConstructorIR &expr) = 0; 21 | virtual llvm::Value *codegen(const ExprLetIR &expr) = 0; 22 | virtual llvm::Value *codegen(const ExprAssignIR &expr) = 0; 23 | virtual llvm::Value *codegen(const ExprConsumeIR &expr) = 0; 24 | virtual llvm::Value *codegen(const ExprFunctionAppIR &expr) = 0; 25 | virtual llvm::Value *codegen(const ExprMethodAppIR &expr) = 0; 26 | virtual llvm::Value *codegen(const ExprFinishAsyncIR &expr) = 0; 27 | virtual llvm::Value *codegen(const ExprIfElseIR &expr) = 0; 28 | virtual llvm::Value *codegen(const ExprWhileLoopIR &expr) = 0; 29 | virtual llvm::Value *codegen(const ExprBinOpIR &expr) = 0; 30 | virtual llvm::Value *codegen(const ExprUnOpIR &expr) = 0; 31 | virtual llvm::Value *codegen(const ExprPrintfIR &expr) = 0; 32 | virtual llvm::Value *codegen(const ExprBlockIR &expr) = 0; 33 | virtual llvm::Value *codegen(const ExprLockIR &expr) = 0; 34 | virtual llvm::Value *codegen(const ExprUnlockIR &expr) = 0; 35 | 36 | virtual llvm::Type *codegen(const TypeIntIR &typeIR) = 0; 37 | virtual llvm::Type *codegen(const TypeClassIR &typeIR) = 0; 38 | virtual llvm::Type *codegen(const TypeVoidIR &typeIR) = 0; 39 | virtual llvm::Type *codegen(const TypeBoolIR &typeIR) = 0; 40 | }; 41 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/program_ir.cc: -------------------------------------------------------------------------------- 1 | #include "src/llvm-backend/deserialise_ir/program_ir.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "src/frontend_ir.pb.h" 9 | #include "src/llvm-backend/deserialise_ir/class_ir.h" 10 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 11 | #include "src/llvm-backend/deserialise_ir/function_ir.h" 12 | 13 | ProgramIR::ProgramIR(const Frontend_ir::program &program) { 14 | for (int i = 0; i < program.prog()._0_size(); i++) { 15 | Frontend_ir::class_defn cls = program.prog()._0(i); 16 | classDefns.push_back(std::unique_ptr(new ClassIR(cls))); 17 | } 18 | for (int i = 0; i < program.prog()._1_size(); i++) { 19 | Frontend_ir::function_defn fn = program.prog()._1(i); 20 | functionDefns.push_back(std::unique_ptr(new FunctionIR(fn))); 21 | } 22 | for (int i = 0; i < program.prog()._2_size(); i++) { 23 | Frontend_ir::expr expr = program.prog()._2(i); 24 | mainExpr.push_back(deserialiseExpr(expr)); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/program_ir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include "src/frontend_ir.pb.h" 8 | #include "src/llvm-backend/deserialise_ir/class_ir.h" 9 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 10 | #include "src/llvm-backend/deserialise_ir/function_ir.h" 11 | 12 | struct ProgramIR { 13 | std::vector> classDefns; 14 | std::vector> functionDefns; 15 | std::vector> mainExpr; 16 | 17 | ProgramIR(const Frontend_ir::program &program); 18 | ProgramIR(){}; 19 | }; 20 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/type_ir.cc: -------------------------------------------------------------------------------- 1 | #include "src/llvm-backend/deserialise_ir/type_ir.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "llvm/IR/Type.h" 9 | #include "src/frontend_ir.pb.h" 10 | #include "src/llvm-backend/deserialise_ir/expr_ir.h" 11 | #include "src/llvm-backend/deserialise_ir/ir_visitor.h" 12 | 13 | std::unique_ptr deserialiseType( 14 | const Frontend_ir::type_expr &typeExpr) { 15 | switch (typeExpr.tag()) { 16 | case Frontend_ir::type_expr__tag_TEInt_tag: 17 | return std::unique_ptr(new TypeIntIR()); 18 | case Frontend_ir::type_expr__tag_TEClass_tag: 19 | return std::unique_ptr(new TypeClassIR(typeExpr.teclass())); 20 | case Frontend_ir::type_expr__tag_TEVoid_tag: 21 | return std::unique_ptr(new TypeVoidIR()); 22 | 23 | case Frontend_ir::type_expr__tag_TEBool_tag: 24 | return std::unique_ptr(new TypeBoolIR()); 25 | } 26 | } 27 | 28 | llvm::Type *TypeIntIR::codegen(IRVisitor &visitor) { 29 | return visitor.codegen(*this); 30 | } 31 | llvm::Type *TypeClassIR::codegen(IRVisitor &visitor) { 32 | return visitor.codegen(*this); 33 | } 34 | llvm::Type *TypeVoidIR::codegen(IRVisitor &visitor) { 35 | return visitor.codegen(*this); 36 | } 37 | llvm::Type *TypeBoolIR::codegen(IRVisitor &visitor) { 38 | return visitor.codegen(*this); 39 | } 40 | -------------------------------------------------------------------------------- /src/llvm-backend/deserialise_ir/type_ir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "llvm/IR/Type.h" 6 | #include "src/frontend_ir.pb.h" 7 | 8 | /* Visitor class declarations */ 9 | class IRVisitor; 10 | 11 | struct TypeIR { 12 | virtual ~TypeIR() = default; 13 | virtual llvm::Type *codegen(IRVisitor &visitor) = 0; 14 | }; 15 | 16 | std::unique_ptr deserialiseType(const Frontend_ir::type_expr &typeExpr); 17 | 18 | struct TypeIntIR : public TypeIR { 19 | virtual llvm::Type *codegen(IRVisitor &visitor) override; 20 | }; 21 | 22 | struct TypeClassIR : public TypeIR { 23 | std::string className; 24 | 25 | TypeClassIR(const std::string &name) : className(name) {} 26 | virtual llvm::Type *codegen(IRVisitor &visitor) override; 27 | }; 28 | 29 | struct TypeVoidIR : public TypeIR { 30 | virtual llvm::Type *codegen(IRVisitor &visitor) override; 31 | }; 32 | 33 | struct TypeBoolIR : public TypeIR { 34 | virtual llvm::Type *codegen(IRVisitor &visitor) override; 35 | }; 36 | -------------------------------------------------------------------------------- /src/llvm-backend/llvm_ir_codegen/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "llvm_ir_codegen", 5 | srcs = glob(["*.cc"]), 6 | hdrs = glob(["*.h"]), 7 | visibility = ["//src/llvm-backend:__pkg__", "//tests/llvm-backend/llvm_ir_codegen:__pkg__"], 8 | deps = [ "//src/llvm-backend/deserialise_ir:deserialise_ir","@llvm"] 9 | 10 | ) 11 | -------------------------------------------------------------------------------- /src/llvm-backend/llvm_ir_codegen/type_codegen.cc: -------------------------------------------------------------------------------- 1 | #include "llvm/ADT/StringRef.h" 2 | #include "llvm/IR/Constants.h" 3 | #include "llvm/IR/Type.h" 4 | #include "src/llvm-backend/llvm_ir_codegen/ir_codegen_visitor.h" 5 | 6 | llvm::Type *IRCodegenVisitor::codegen(const TypeIntIR &typeIR) { 7 | return llvm::Type::getInt32Ty(*context); 8 | }; 9 | 10 | llvm::Type *IRCodegenVisitor::codegen(const TypeClassIR &typeIR) { 11 | return module->getTypeByName(llvm::StringRef(typeIR.className)) 12 | ->getPointerTo(); 13 | ; 14 | }; 15 | 16 | llvm::Type *IRCodegenVisitor::codegen(const TypeVoidIR &typeIR) { 17 | return llvm::Type::getVoidTy(*context); 18 | }; 19 | 20 | llvm::Type *IRCodegenVisitor::codegen(const TypeBoolIR &typeIR) { 21 | return llvm::Type::getInt1Ty(*context); // bools are 1-bit integers 22 | }; 23 | -------------------------------------------------------------------------------- /src/llvm-backend/main.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include "llvm/IR/Function.h" 5 | #include "llvm/IR/IRBuilder.h" 6 | #include "llvm/IR/LLVMContext.h" 7 | #include "llvm/IR/Module.h" 8 | #include "llvm/IR/Value.h" 9 | #include "llvm/IR/Verifier.h" 10 | #include "src/llvm-backend/deserialise_ir/deserialise_protobuf.h" 11 | #include "src/llvm-backend/llvm_ir_codegen/ir_codegen_visitor.h" 12 | 13 | int main(int argc, char **argv) { 14 | std::string filePath(argv[1]); 15 | std::unique_ptr programIR = 16 | protobufToIR(deserialiseProtobufFile(filePath)); 17 | try { 18 | IRCodegenVisitor codeGen; 19 | codeGen.codegenProgram(*programIR); 20 | codeGen.configureTarget(); 21 | codeGen.dumpLLVMIR(); 22 | } catch (IRCodegenException *ex) { 23 | std::cerr << ex->what(); 24 | return 1; 25 | } 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/e2e/for_loop.bolt: -------------------------------------------------------------------------------- 1 | void main(){ 2 | for(let i = 0; i< 10 ; i:=i+1){ 3 | printf("i = %d;\n", i) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/e2e/for_loop.ll.expected: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | @0 = private unnamed_addr constant [9 x i8] c"i = %d;\0A\00", align 1 6 | 7 | declare i32 @printf(i8*, ...) 8 | 9 | declare i8* @GC_malloc(i64) 10 | 11 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 12 | 13 | declare i32 @pthread_join(i8*, i8**) 14 | 15 | declare i32 @pthread_equal(i8*, i8*) 16 | 17 | declare i8* @pthread_self() 18 | 19 | define i32 @main() { 20 | entry: 21 | br label %loop 22 | 23 | loop: ; preds = %entry, %loop 24 | %_i0.0 = phi i32 [ 0, %entry ], [ %add, %loop ] 25 | %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @0, i64 0, i64 0), i32 %_i0.0) 26 | %add = add i32 %_i0.0, 1 27 | %lt1 = icmp slt i32 %add, 10 28 | br i1 %lt1, label %loop, label %loopend 29 | 30 | loopend: ; preds = %loop 31 | ret i32 0 32 | } 33 | -------------------------------------------------------------------------------- /tests/e2e/for_loop.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | i = 0; 8 | i = 1; 9 | i = 2; 10 | i = 3; 11 | i = 4; 12 | i = 5; 13 | i = 6; 14 | i = 7; 15 | i = 8; 16 | i = 9; 17 | -------------------------------------------------------------------------------- /tests/e2e/heap_objects.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability linear Bar; 3 | var int f : Bar; 4 | } 5 | void main(){ 6 | let x = new Foo(f:4); 7 | finish { 8 | async{ 9 | x.f := 6 10 | } 11 | }; 12 | printf("Value of x.f: %d\n", x.f) // Should print 6 if value of x updated globally 13 | } 14 | -------------------------------------------------------------------------------- /tests/e2e/heap_objects.ll.expected: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | %_VtableFoo = type {} 6 | %structPtrArgType = type { %Foo* } 7 | %Foo = type { %_VtableFoo*, i8*, i32, i32, i32 } 8 | 9 | @_VtableFoo = global %_VtableFoo zeroinitializer 10 | @0 = private unnamed_addr constant [18 x i8] c"Value of x.f: %d\0A\00", align 1 11 | 12 | declare i32 @printf(i8*, ...) 13 | 14 | declare i8* @GC_malloc(i64) 15 | 16 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 17 | 18 | declare i32 @pthread_join(i8*, i8**) 19 | 20 | declare i32 @pthread_equal(i8*, i8*) 21 | 22 | declare i8* @pthread_self() 23 | 24 | define i32 @main() { 25 | entry: 26 | %0 = call i8* @GC_malloc(i64 32) 27 | %1 = bitcast i8* %0 to %_VtableFoo** 28 | store %_VtableFoo* @_VtableFoo, %_VtableFoo** %1, align 8 29 | %2 = getelementptr inbounds i8, i8* %0, i64 16 30 | %3 = bitcast i8* %2 to i32* 31 | store i32 0, i32* %3, align 4 32 | %4 = getelementptr inbounds i8, i8* %0, i64 20 33 | %5 = bitcast i8* %4 to i32* 34 | store i32 0, i32* %5, align 4 35 | %6 = getelementptr inbounds i8, i8* %0, i64 24 36 | %7 = bitcast i8* %6 to i32* 37 | store i32 4, i32* %7, align 4 38 | %pthread = alloca i8*, align 8 39 | %8 = alloca %structPtrArgType, align 8 40 | %9 = bitcast %structPtrArgType* %8 to i8** 41 | store i8* %0, i8** %9, align 8 42 | %10 = bitcast %structPtrArgType* %8 to i8* 43 | %11 = call i32 @pthread_create(i8** nonnull %pthread, i8* null, i8* (i8*)* nonnull @_async0, i8* nonnull %10) 44 | %12 = load i8*, i8** %pthread, align 8 45 | %13 = call i32 @pthread_join(i8* %12, i8** null) 46 | %14 = load i32, i32* %7, align 4 47 | %15 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @0, i64 0, i64 0), i32 %14) 48 | ret i32 0 49 | } 50 | 51 | define i8* @_async0(i8*) { 52 | entry: 53 | %1 = bitcast i8* %0 to %structPtrArgType* 54 | %2 = getelementptr inbounds %structPtrArgType, %structPtrArgType* %1, i32 0, i32 0 55 | %3 = load %Foo*, %Foo** %2 56 | %_x0 = alloca %Foo* 57 | store %Foo* %3, %Foo** %_x0 58 | %4 = load %Foo*, %Foo** %_x0 59 | %5 = getelementptr inbounds %Foo, %Foo* %4, i32 0, i32 4 60 | store i32 6, i32* %5 61 | ret i8* null 62 | } 63 | -------------------------------------------------------------------------------- /tests/e2e/heap_objects.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | Value of x.f: 6 8 | -------------------------------------------------------------------------------- /tests/e2e/independent_threads.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability linear Bar; 3 | var int f : Bar; 4 | const int g : Bar; 5 | const int h : Bar; 6 | 7 | } 8 | function int f (int x ){ x} 9 | void main(){ 10 | let x = new Foo(f:5); 11 | finish { 12 | async{ 13 | f(5) 14 | } 15 | async{ 16 | let w = new Foo(g:5); 17 | w.f := 5 18 | } 19 | printf("Value of x.f: %d\n", x.f) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/e2e/independent_threads.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | Value of x.f: 5 8 | -------------------------------------------------------------------------------- /tests/e2e/is_pthread_blocking.bolt: -------------------------------------------------------------------------------- 1 | void main(){ 2 | finish { 3 | async{ 4 | let x = 6; 5 | printf("Value of x: %d\n", x) // Should print if blocking 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/e2e/is_pthread_blocking.ll.expected: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | %structPtrArgType = type {} 6 | 7 | @0 = private unnamed_addr constant [16 x i8] c"Value of x: %d\0A\00", align 1 8 | 9 | declare i32 @printf(i8*, ...) 10 | 11 | declare i8* @GC_malloc(i64) 12 | 13 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 14 | 15 | declare i32 @pthread_join(i8*, i8**) 16 | 17 | declare i32 @pthread_equal(i8*, i8*) 18 | 19 | declare i8* @pthread_self() 20 | 21 | define i32 @main() { 22 | entry: 23 | %0 = alloca %structPtrArgType, align 8 24 | %pthread = alloca i8*, align 8 25 | %1 = bitcast %structPtrArgType* %0 to i8* 26 | %2 = call i32 @pthread_create(i8** nonnull %pthread, i8* null, i8* (i8*)* nonnull @_async0, i8* nonnull %1) 27 | %3 = load i8*, i8** %pthread, align 8 28 | %4 = call i32 @pthread_join(i8* %3, i8** null) 29 | ret i32 0 30 | } 31 | 32 | define i8* @_async0(i8*) { 33 | entry: 34 | %_x0 = alloca i32 35 | %1 = bitcast i8* %0 to %structPtrArgType* 36 | store i32 6, i32* %_x0 37 | %2 = load i32, i32* %_x0 38 | %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @0, i32 0, i32 0), i32 %2) 39 | ret i8* null 40 | } 41 | -------------------------------------------------------------------------------- /tests/e2e/is_pthread_blocking.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | Value of x: 6 8 | -------------------------------------------------------------------------------- /tests/e2e/recursive_method.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability read Bar, locked Baz; 3 | const int f : Bar; 4 | var int g : Baz; 5 | int setg(int x) : Baz{ 6 | if(x>0){ 7 | this.setg(-x) 8 | } 9 | else{ 10 | this.g := x 11 | } 12 | } 13 | } 14 | void main(){ 15 | let x = new Foo(f:100); 16 | let y = new Foo(); 17 | finish { 18 | async{ 19 | printf("Value of x.f: %d\n", x.f) 20 | } 21 | y.setg(10); 22 | printf("Value of y.g: %d\n", y.g) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/e2e/recursive_method.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | Value of y.g: -10 8 | Value of x.f: 100 9 | -------------------------------------------------------------------------------- /tests/e2e/subtype_passed_to_fn.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability linear Bar; 3 | var int f : Bar; 4 | } 5 | class Baz extends Foo { 6 | capability linear Boo; 7 | var int g : Boo; 8 | } 9 | function int test(borrowed Foo x){ 10 | x.f 11 | } 12 | void main() { 13 | let x = new Baz(); 14 | test(x) 15 | } -------------------------------------------------------------------------------- /tests/e2e/subtype_passed_to_fn.ll.expected: -------------------------------------------------------------------------------- 1 | ; ModuleID = 'Module' 2 | source_filename = "Module" 3 | target triple = "x86_64-apple-darwin18.7.0" 4 | 5 | %_VtableFoo = type {} 6 | %_VtableBaz = type {} 7 | %Foo = type { %_VtableFoo*, i8*, i32, i32, i32 } 8 | 9 | @_VtableFoo = global %_VtableFoo zeroinitializer 10 | @_VtableBaz = global %_VtableBaz zeroinitializer 11 | 12 | declare i32 @printf(i8*, ...) 13 | 14 | declare i8* @GC_malloc(i64) 15 | 16 | declare i32 @pthread_create(i8**, i8*, i8* (i8*)*, i8*) 17 | 18 | declare i32 @pthread_join(i8*, i8**) 19 | 20 | declare i32 @pthread_equal(i8*, i8*) 21 | 22 | declare i8* @pthread_self() 23 | 24 | define i32 @test(%Foo*) { 25 | entry: 26 | %1 = getelementptr inbounds %Foo, %Foo* %0, i64 0, i32 4 27 | %2 = load i32, i32* %1, align 4 28 | ret i32 %2 29 | } 30 | 31 | define i32 @main() { 32 | entry: 33 | %0 = call i8* @GC_malloc(i64 32) 34 | %1 = bitcast i8* %0 to %_VtableBaz** 35 | store %_VtableBaz* @_VtableBaz, %_VtableBaz** %1, align 8 36 | %2 = getelementptr inbounds i8, i8* %0, i64 16 37 | %3 = bitcast i8* %2 to i32* 38 | store i32 0, i32* %3, align 4 39 | %4 = getelementptr inbounds i8, i8* %0, i64 20 40 | %5 = bitcast i8* %4 to i32* 41 | store i32 0, i32* %5, align 4 42 | %6 = bitcast i8* %0 to %Foo* 43 | %7 = call i32 @test(%Foo* %6) 44 | ret i32 0 45 | } 46 | -------------------------------------------------------------------------------- /tests/e2e/subtype_passed_to_fn.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | -------------------------------------------------------------------------------- /tests/e2e/vtable.bolt: -------------------------------------------------------------------------------- 1 | // dummy classes 2 | 3 | class Species{ 4 | capability local Acap; 5 | var int someVal : Acap; 6 | } 7 | 8 | class Breed{ 9 | capability local Acap; 10 | var int someVal : Acap; 11 | } 12 | 13 | 14 | class Animal { 15 | capability local Acap, local Bcap; 16 | var int age : Acap; 17 | var Species species: Bcap; 18 | int getAge() : Acap { 19 | this.age 20 | } 21 | void printSpecies() : Bcap { printf("Something") } 22 | } 23 | class Dog extends Animal { 24 | capability local Ccap; 25 | var Breed breed: Ccap; 26 | int getAge() : Acap{ 27 | 7*this.age // dog years! 28 | } 29 | void printBreed() : Ccap{ printf("Something else") } 30 | } 31 | 32 | function void printAge(Animal a){ 33 | printf("I'm %d years old!\n", a.getAge()) 34 | } 35 | 36 | void main() { 37 | let animal = new Animal(age: 2); 38 | let dog = new Dog(age: 2); 39 | 40 | printAge(animal); // print 2 41 | printAge(dog) // print 14 42 | } -------------------------------------------------------------------------------- /tests/e2e/vtable.out.expected: -------------------------------------------------------------------------------- 1 | Frontend type-checking completed successfully. 2 |  3 | LLVM IR Codegen completed successfully. 4 |  5 | Native binary compiled successfully. 6 |  7 | I'm 2 years old! 8 | I'm 14 years old! 9 | -------------------------------------------------------------------------------- /tests/frontend/alcotest/desugaring/test_remove_variable_shadowing.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Desugaring.Remove_variable_shadowing 3 | open Ast.Ast_types 4 | open Desugaring.Desugared_ast 5 | 6 | let print_error_string = function Ok _ -> "" | Error e -> Error.to_string_hum e 7 | 8 | let test_error_if_var_not_in_var_map () = 9 | let expected_error = 10 | Fmt.str "Error: no unique var name for (potentially) shadowed variable foo@." in 11 | let result = 12 | remove_var_shadowing_expr 13 | (Identifier (Lexing.dummy_pos, Variable (TEVoid, Var_name.of_string "foo", []))) 14 | [] in 15 | Alcotest.(check string) "same error string" expected_error (print_error_string result) 16 | 17 | let () = 18 | let open Alcotest in 19 | run "Remove Variable Shadowing" 20 | [("Errors", [test_case "Var not in var map" `Quick test_error_if_var_not_in_var_map])] 21 | -------------------------------------------------------------------------------- /tests/frontend/alcotest/parsing/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test_lexer) 3 | (libraries alcotest core qcheck qcheck-alcotest parsing) 4 | (lint 5 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 6 | -dated-deprecation))) 7 | -------------------------------------------------------------------------------- /tests/frontend/alcotest/typing/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test_type_env) 3 | (libraries alcotest core qcheck qcheck-alcotest typing parsing) 4 | (lint 5 | (pps ppx_js_style -check-doc-comments -annotated-ignores -styler -pretty 6 | -dated-deprecation))) 7 | -------------------------------------------------------------------------------- /tests/frontend/alcotest/typing/test_type_env.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Result 3 | open Ast.Ast_types 4 | open Typing.Type_env 5 | 6 | let print_error_string = function Ok _ -> "" | Error e -> Error.to_string_hum e 7 | 8 | let test_error_if_duplicate_class_defns () = 9 | let expected_error = 10 | Fmt.str 11 | "Line:0 Position:0 Type error - Class Foo has duplicate definitions in environment@." 12 | in 13 | let example_class = 14 | Parsing.Parsed_ast.TClass (Class_name.of_string "Foo", None, None, [], [], []) in 15 | let result = 16 | get_instantiated_class_defn (Class_name.of_string "Foo") 17 | [example_class; example_class] 18 | None Lexing.dummy_pos in 19 | Alcotest.(check string) "same error string" expected_error (print_error_string result) 20 | 21 | let test_error_if_duplicate_class_fields () = 22 | let expected_error = 23 | Fmt.str 24 | "Line:0 Position:0 Type error - Field Baz has duplicate definitions in environment@." 25 | in 26 | let example_field = TField (MConst, TEInt, Field_name.of_string "Baz", []) in 27 | let example_class = 28 | Parsing.Parsed_ast.TClass 29 | (Class_name.of_string "Foo", None, None, [], [example_field; example_field], []) 30 | in 31 | let result = 32 | get_class_field (Field_name.of_string "Baz") [example_class] example_class None 33 | Lexing.dummy_pos in 34 | Alcotest.(check string) "same error string" expected_error (print_error_string result) 35 | 36 | let test_error_if_get_field_of_non_object () = 37 | let expected_error = 38 | Fmt.str 39 | "Line:0 Position:0 Type error - x should be an object, instead is of type Int@." 40 | in 41 | let result = 42 | get_obj_class_defn (Var_name.of_string "x") 43 | [(Var_name.of_string "x", TEInt)] 44 | [] Lexing.dummy_pos in 45 | Alcotest.(check string) "same error string" expected_error (print_error_string result) 46 | 47 | let () = 48 | let open Alcotest in 49 | run "Core Lang Type Env" 50 | [ ( "Errors" 51 | , [ test_case "Duplicate Class Definitions" `Quick 52 | test_error_if_duplicate_class_defns 53 | ; test_case "Duplicate Class Fields" `Quick test_error_if_duplicate_class_fields 54 | ; test_case "Access Field of Non-object" `Quick 55 | test_error_if_get_field_of_non_object ] ) ] 56 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_assignment.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Assign expr to object without all required capabilities present" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability read Bar, local Baz; 9 | var int f : Bar, Baz; 10 | } 11 | class Something { 12 | capability linear Else; 13 | var Foo f : Else; 14 | 15 | void set_f(Foo{Bar} x ) : Else { 16 | this.f := x 17 | } 18 | } 19 | function void test(Foo x){} 20 | void main(){ 21 | let x = new Foo(); 22 | let y = new Something(); 23 | y.set_f(x) 24 | } 25 | " ; 26 | [%expect 27 | {| Line:11 Position:9 Assigned expression doesn't have all capabilities available |}] 28 | 29 | let%expect_test "Assign linear object that hasn't been consumed to field" = 30 | print_data_race_checker_ast 31 | " 32 | class Foo { 33 | capability local Bar; 34 | var Baz f : Bar; 35 | } 36 | class Baz { 37 | capability linear Fa; 38 | var int g : Fa; 39 | } 40 | void main(){ 41 | let x = new Foo(); 42 | let z = new Baz(); 43 | x.f := z // Error: should consume the field 44 | 45 | } 46 | " ; 47 | [%expect 48 | {| 49 | Line:13 Position:9 Error: Can only assign a linear variable if it has been consumed |}] 50 | 51 | let%expect_test "Assign borrowed reference" = 52 | print_data_race_checker_ast 53 | " 54 | class Foo { 55 | capability linear Fa; 56 | var int g : Fa; 57 | } 58 | function borrowed Foo f(){ 59 | let x = new Foo(); 60 | x 61 | } 62 | function void test(){ 63 | let y = new Foo(); 64 | y := f() // shouldn't be able to assign a borrowed expression. 65 | } 66 | void main(){ 67 | } 68 | " ; 69 | [%expect {| 70 | Line:12 Position:7 Type error: Trying to assign a borrowed value |}] 71 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_concurrent_regions.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Access linear capability in multiple locals" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | int id (int x): Bar { x} 11 | } 12 | void main(){ 13 | let x = new Foo(); 14 | finish{ 15 | async{ 16 | x.f // error - as accessing linear capability in multiple locals 17 | } 18 | x.f 19 | } 20 | 21 | } 22 | " ; 23 | [%expect 24 | {| 25 | Potential data race: Line:9 Position:6 Can't access capabilities Bar and Bar of object _x0 concurrently |}] 26 | 27 | let%expect_test "Access capabilities concurrently that don't share safe state" = 28 | print_data_race_checker_ast 29 | " 30 | class Foo { 31 | capability linear Bar, linear Baz; 32 | var int f : Bar, Baz; // since Baz and Bar aren't both safe(), we can't access this field concurrently 33 | int get(): Baz { this.f } 34 | } 35 | void main(){ 36 | let x = new Foo(); 37 | finish{ 38 | async{ 39 | x.f 40 | } 41 | x.get() 42 | } 43 | 44 | } 45 | " ; 46 | [%expect 47 | {| 48 | Potential data race: Line:9 Position:6 Can't access capabilities Baz and Bar of object _x0 concurrently |}] 49 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_finish_async_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Access local variable from multiple locals" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability local Bar; 9 | var int f : Bar; 10 | } 11 | void main(){ 12 | let x = new Foo(); 13 | finish { 14 | async{ 15 | x.f := 1 16 | } 17 | let y = x; 18 | y.f 19 | } 20 | 21 | } 22 | " ; 23 | [%expect 24 | {| 25 | Line:10 Position:11 Potential data race: no allowed capabilities for Objfield: (Class: Foo) _x0.f |}] 26 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_function_borrowing.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Pass in linear arguments twice to function" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | } 11 | function void test2(Foo x, Foo y){ 12 | } 13 | void main() { 14 | let x = new Foo (); 15 | test2(x,x) // not allowed! 16 | } 17 | " ; 18 | [%expect {| 19 | Line:10 Position:7 Linear arguments are duplicated |}] 20 | 21 | let%expect_test "Pass in linear object to own method" = 22 | print_data_race_checker_ast 23 | " 24 | class Foo { 25 | capability linear Bar; 26 | var int f : Bar; 27 | void test(Foo y) : Bar { 28 | } 29 | } 30 | void main() { 31 | let x = new Foo (); 32 | x.test(x) // not allowed! 33 | } 34 | " ; 35 | [%expect 36 | {| 37 | Line:10 Position:7 One of linear object _x0's method's arguments aliases it |}] 38 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_function_call.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Call function on object without all required capabilities present" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability subordinate Bar, local Baz; 9 | var int f : Bar, Baz; 10 | } 11 | function void test(Foo x){} 12 | void main(){ 13 | let x = new Foo(); 14 | test(x) 15 | } 16 | " ; 17 | [%expect 18 | {| 19 | Line:9 Position:7 Potential data race: Function test's argument capability constraints not satisfied. |}] 20 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_method_call.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Access method without all required capabilities" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability linear Bar, local Baz; 9 | var int f : Bar, Baz; 10 | int id (int x): Bar { x} 11 | } 12 | void main(){ 13 | let x = new Foo(); 14 | let y = x; 15 | y.f:= x.id(5) 16 | } 17 | " ; 18 | [%expect 19 | {| Line:10 Position:13 Potential data race: _x0's method _idi's capability constraints not satisfied. |}] 20 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_read_region_access.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Try to assign to a read capability" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | var int f : Bar; 10 | } 11 | void main(){ 12 | let x = new Foo(); 13 | x.f := 1 14 | } 15 | " ; 16 | [%expect 17 | {| 18 | Line:8 Position:7 Potential data race: no allowed capabilities for Objfield: (Class: Foo) _x0.f |}] 19 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/bad_subord_access.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Access subordinate variable from outside class" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability subordinate Bar; 9 | var int f : Bar; 10 | } 11 | void main(){ 12 | let x = new Foo(); 13 | x.f 14 | } 15 | " ; 16 | [%expect 17 | {| 18 | Line:8 Position:7 Potential data race: no allowed capabilities for Objfield: (Class: Foo) _x0.f |}] 19 | 20 | let%expect_test "Access subordinate variable from outside class" = 21 | print_data_race_checker_ast 22 | " 23 | class Foo { 24 | capability subordinate Bar; 25 | var int f : Bar; 26 | void print_field() : Bar{ 27 | finish{ 28 | async{ 29 | this.f // can't access subordinate state in another local 30 | } 31 | 32 | } 33 | } 34 | } 35 | void main(){ 36 | } 37 | " ; 38 | [%expect 39 | {| Line:8 Position:13 Potential data race: no allowed capabilities for Objfield: (Class: Foo) this.f |}] 40 | 41 | let%expect_test "Return subordinate state from non-encapsulated method" = 42 | print_data_race_checker_ast 43 | " 44 | class Foo { 45 | capability subordinate Bar, locked Baz; 46 | var int f : Bar; 47 | var Foo g : Baz; 48 | Foo return_g() : Baz { 49 | this.g 50 | } 51 | } 52 | void main(){ 53 | } 54 | " ; 55 | [%expect 56 | {| Potential Data Race in Foo's method _return_g: Subordinate state returned by non-encapsulated method |}] 57 | 58 | let%expect_test "Pass subordinate state to non-encapsulated method" = 59 | print_data_race_checker_ast 60 | " 61 | class Foo { 62 | capability subordinate Bar, locked Baz; 63 | var int f : Bar; 64 | var Foo g : Baz; 65 | void return_g(Foo x) : Baz { 66 | this.g := x 67 | } 68 | } 69 | void main(){ 70 | } 71 | " ; 72 | [%expect 73 | {| Potential Data Race in Foo's method _return_g3Foo: Subordinate arguments passed into non-encapsulated method: x |}] 74 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name test_data_race_checker) 3 | (libraries core compile_program_ir) 4 | (inline_tests) 5 | (preprocess 6 | (pps ppx_jane)) 7 | (lint 8 | (pps ppx_jane ppx_js_style -check-doc-comments -annotated-ignores -styler 9 | -pretty -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/good_block_exprs.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Block of exprs" = 5 | print_data_race_checker_ast 6 | " 7 | function int f (int x){x} 8 | void main(){ 9 | f(4); 10 | f(5); 11 | f(6) 12 | } 13 | " ; 14 | [%expect 15 | {| 16 | Program 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Type expr: Int 23 | └──Expr: Variable: x 24 | └──Type expr: Int 25 | └──Main block 26 | └──Type expr: Int 27 | └──Expr: Function App 28 | └──Type expr: Int 29 | └──Function: f 30 | └──Expr: Int:4 31 | └──Expr: Function App 32 | └──Type expr: Int 33 | └──Function: f 34 | └──Expr: Int:5 35 | └──Expr: Function App 36 | └──Type expr: Int 37 | └──Function: f 38 | └──Expr: Int:6 |}] 39 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/good_comments.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Comments interspersed with code" = 5 | print_data_race_checker_ast 6 | " 7 | void main(){ 8 | /* This is a comment - it should not be parsed */ 9 | let x = 4;// Can occur after a line 10 | let y /*Or even midway*/ = 5; 11 | /* Or before */ x 12 | /* 13 | Comments 14 | Can 15 | Span 16 | Multiple 17 | Lines 18 | */ 19 | } 20 | " ; 21 | [%expect 22 | {| 23 | Program 24 | └──Main block 25 | └──Type expr: Int 26 | └──Expr: Let var: _x0 27 | └──Type expr: Int 28 | └──Expr: Int:4 29 | └──Expr: Let var: _y0 30 | └──Type expr: Int 31 | └──Expr: Int:5 32 | └──Expr: Variable: _x0 33 | └──Type expr: Int |}] 34 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/good_constructor.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Constructor with multiple args" = 5 | print_data_race_checker_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | const int g : Bar; 11 | const int h : Bar; 12 | } 13 | void main(){ 14 | let x = new Foo(f:4, g:5, h:6); 15 | x 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──Capabilities: 23 | └──Capability: Linear Bar 24 | └──Field Defn: f 25 | └──Modifier: Const 26 | └──Type expr: Int 27 | └──Capabilities: Bar 28 | └──Field Defn: g 29 | └──Modifier: Const 30 | └──Type expr: Int 31 | └──Capabilities: Bar 32 | └──Field Defn: h 33 | └──Modifier: Const 34 | └──Type expr: Int 35 | └──Capabilities: Bar 36 | └──Main block 37 | └──Type expr: Foo 38 | └──Expr: Let var: _x0 39 | └──Type expr: Foo 40 | └──Expr: Constructor for: Foo 41 | └──Type expr: Foo 42 | └── Field: f 43 | └──Type expr: Int 44 | └──Expr: Int:4 45 | └── Field: g 46 | └──Type expr: Int 47 | └──Expr: Int:5 48 | └── Field: h 49 | └──Type expr: Int 50 | └──Expr: Int:6 51 | └──Expr: Variable: _x0 52 | └──Type expr: Foo 53 | └── Possible Capabilities: 54 | └── Possible Capability: Linear Bar |}] 55 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/good_function_application.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_data_race_checker_ast 3 | 4 | let%expect_test "Function application" = 5 | print_data_race_checker_ast 6 | " 7 | function int f (int x ){ x} 8 | void main(){ 9 | f(4) 10 | } 11 | " ; 12 | [%expect 13 | {| 14 | Program 15 | └── Function: f 16 | └── Return type: Int 17 | └──Param: x 18 | └──Type expr: Int 19 | └──Body block 20 | └──Type expr: Int 21 | └──Expr: Variable: x 22 | └──Type expr: Int 23 | └──Main block 24 | └──Type expr: Int 25 | └──Expr: Function App 26 | └──Type expr: Int 27 | └──Function: f 28 | └──Expr: Int:4 |}] 29 | 30 | let%expect_test "Function application with multiple args " = 31 | print_data_race_checker_ast 32 | " 33 | function int f (int x, int y){ x} 34 | void main(){ 35 | f (3, 4) 36 | } 37 | " ; 38 | [%expect 39 | {| 40 | Program 41 | └── Function: f 42 | └── Return type: Int 43 | └──Param: x 44 | └──Type expr: Int 45 | └──Param: y 46 | └──Type expr: Int 47 | └──Body block 48 | └──Type expr: Int 49 | └──Expr: Variable: x 50 | └──Type expr: Int 51 | └──Main block 52 | └──Type expr: Int 53 | └──Expr: Function App 54 | └──Type expr: Int 55 | └──Function: f 56 | └──Expr: Int:3 57 | └──Expr: Int:4 |}] 58 | 59 | let%expect_test "Function application with no args " = 60 | print_data_race_checker_ast 61 | " 62 | function int f ( ){ 4} 63 | void main(){ 64 | f() 65 | } 66 | " ; 67 | [%expect 68 | {| 69 | Program 70 | └── Function: f 71 | └── Return type: Int 72 | └──Param: Void 73 | └──Body block 74 | └──Type expr: Int 75 | └──Expr: Int:4 76 | └──Main block 77 | └──Type expr: Int 78 | └──Expr: Function App 79 | └──Type expr: Int 80 | └──Function: f 81 | └──() |}] 82 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/print_data_race_checker_ast.ml: -------------------------------------------------------------------------------- 1 | open Compile_program_ir 2 | 3 | let print_data_race_checker_ast input_str = 4 | compile_program_ir (Lexing.from_string input_str) ~should_pprint_drast:true 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/data_race_checker/print_data_race_checker_ast.mli: -------------------------------------------------------------------------------- 1 | (** This module is used by all core language type-checker expect tests *) 2 | 3 | val print_data_race_checker_ast : string -> unit 4 | (** Given a bolt program as a string, print out its typed AST *) 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/desugaring/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name test_desugaring) 3 | (libraries core compile_program_ir) 4 | (inline_tests) 5 | (preprocess 6 | (pps ppx_jane)) 7 | (lint 8 | (pps ppx_jane ppx_js_style -check-doc-comments -annotated-ignores -styler 9 | -pretty -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/desugaring/good_variable_shadowing.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_desugared_ast 3 | 4 | let%expect_test "Variable nested shadowing" = 5 | print_desugared_ast 6 | " 7 | 8 | void main() { 9 | let x = 5; 10 | while(x <4){ 11 | printf(\"Value of x %d\", x); // this is outer x 12 | let x = 1; 13 | printf(\"Value of x %d\", x) // this is nested x 14 | } 15 | } 16 | 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Main block 22 | └──Type expr: Void 23 | └──Expr: Let var: _x0 24 | └──Type expr: Int 25 | └──Expr: Int:5 26 | └──Expr: While 27 | └──Type expr: Void 28 | └──Expr: Bin Op: < 29 | └──Type expr: Bool 30 | └──Expr: Variable: _x0 31 | └──Type expr: Int 32 | └──Expr: Int:4 33 | └──Body block 34 | └──Type expr: Void 35 | └──Expr: Printf 36 | └──Value of x %d 37 | └──Expr: Variable: _x0 38 | └──Type expr: Int 39 | └──Expr: Let var: _x1 40 | └──Type expr: Int 41 | └──Expr: Int:1 42 | └──Expr: Printf 43 | └──Value of x %d 44 | └──Expr: Variable: _x1 45 | └──Type expr: Int |}] 46 | -------------------------------------------------------------------------------- /tests/frontend/expect/desugaring/print_desugared_ast.ml: -------------------------------------------------------------------------------- 1 | open Compile_program_ir 2 | 3 | let print_desugared_ast input_str = 4 | compile_program_ir (Lexing.from_string input_str) ~should_pprint_dast:true 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/desugaring/print_desugared_ast.mli: -------------------------------------------------------------------------------- 1 | (** This module is used by all desugared stage expect tests *) 2 | 3 | val print_desugared_ast : string -> unit 4 | (** Given a bolt program as a string, print out its desugared AST *) 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name test_ir_gen) 3 | (libraries core compile_program_ir) 4 | (inline_tests) 5 | (preprocess 6 | (pps ppx_jane)) 7 | (lint 8 | (pps ppx_jane ppx_js_style -check-doc-comments -annotated-ignores -styler 9 | -pretty -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_block_exprs.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Block of exprs" = 5 | print_frontend_ir 6 | " 7 | function int f (int x){x} 8 | void main(){ 9 | f(4); 10 | f(5); 11 | f(6) 12 | } 13 | " ; 14 | [%expect 15 | {| 16 | Program 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: Int x 20 | └──Body block 21 | └──Expr: Variable: x 22 | └──Main expr 23 | └──Expr: Function App 24 | └──Function: f 25 | └──Expr: Int:4 26 | └──Expr: Function App 27 | └──Function: f 28 | └──Expr: Int:5 29 | └──Expr: Function App 30 | └──Function: f 31 | └──Expr: Int:6 |}] 32 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_comments.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Comments interspersed with code" = 5 | print_frontend_ir 6 | " 7 | void main(){ 8 | /* This is a comment - it should not be parsed */ 9 | let x = 4; // Can occur after a line 10 | let y /*Or even midway*/ = 5; 11 | /* Or before */ x 12 | /* 13 | Comments 14 | Can 15 | Span 16 | Multiple 17 | Lines 18 | */ 19 | } 20 | " ; 21 | [%expect 22 | {| 23 | Program 24 | └──Main expr 25 | └──Expr: Let var: _x0 26 | └──Expr: Int:4 27 | └──Expr: Let var: _y0 28 | └──Expr: Int:5 29 | └──Expr: Variable: _x0 |}] 30 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_conditional_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Good if statement" = 5 | print_frontend_ir 6 | " 7 | void main(){ 8 | let x = true; 9 | if x { 10 | 0 11 | } 12 | else { 13 | 1 14 | } 15 | } 16 | " ; 17 | [%expect 18 | {| 19 | Program 20 | └──Main expr 21 | └──Expr: Let var: _x0 22 | └──Expr: Bool:true 23 | └──Expr: If 24 | └──Expr: Variable: _x0 25 | └──Then block 26 | └──Expr: Int:0 27 | └──Else block 28 | └──Expr: Int:1 |}] 29 | 30 | let%expect_test "Good while loop" = 31 | print_frontend_ir " 32 | void main(){ 33 | while (1 < 2){ 34 | let x = 5 35 | } 36 | } 37 | " ; 38 | [%expect 39 | {| 40 | Program 41 | └──Main expr 42 | └──Expr: While 43 | └──Expr: Bin Op: < 44 | └──Expr: Int:1 45 | └──Expr: Int:2 46 | └──Body block 47 | └──Expr: Let var: _x0 48 | └──Expr: Int:5 |}] 49 | 50 | let%expect_test "Good for loop" = 51 | print_frontend_ir 52 | " 53 | void main(){ 54 | for (let i=0; i < (5*5); i:= i+1) { 55 | i 56 | } 57 | } 58 | " ; 59 | [%expect 60 | {| 61 | Program 62 | └──Main expr 63 | └──Expr: Let var: _i0 64 | └──Expr: Int:0 65 | └──Expr: While 66 | └──Expr: Bin Op: < 67 | └──Expr: Variable: _i0 68 | └──Expr: Bin Op: * 69 | └──Expr: Int:5 70 | └──Expr: Int:5 71 | └──Body block 72 | └──Expr: Variable: _i0 73 | └──Expr: Assign 74 | └──Expr: Variable: _i0 75 | └──Expr: Bin Op: + 76 | └──Expr: Variable: _i0 77 | └──Expr: Int:1 |}] 78 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_constructor.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Constructor with multiple args" = 5 | print_frontend_ir 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | const int g : Bar; 11 | const int h : Bar; 12 | } 13 | void main(){ 14 | let x = new Foo(f:4, g:5, h:6); 15 | x 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──VTable [] 23 | └──Field: Int 24 | └──Field: Int 25 | └──Field: Int 26 | └──Main expr 27 | └──Expr: Let var: _x0 28 | └──Expr: Constructor for: Foo 29 | └── Field: 0 30 | └──Expr: Int:4 31 | └── Field: 1 32 | └──Expr: Int:5 33 | └── Field: 2 34 | └──Expr: Int:6 35 | └──Expr: Variable: _x0 |}] 36 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_consume_variable.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Consume linear variable" = 5 | print_frontend_ir 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | const int g : Bar ; 11 | const int h : Bar; 12 | } 13 | void main(){ 14 | let x = new Foo(f:4, g:5, h:6); 15 | let y = consume x // Consume linear variable 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──VTable [] 23 | └──Field: Int 24 | └──Field: Int 25 | └──Field: Int 26 | └──Main expr 27 | └──Expr: Let var: _x0 28 | └──Expr: Constructor for: Foo 29 | └── Field: 0 30 | └──Expr: Int:4 31 | └── Field: 1 32 | └──Expr: Int:5 33 | └── Field: 2 34 | └──Expr: Int:6 35 | └──Expr: Let var: _y0 36 | └──Expr: Consume 37 | └──Expr: Variable: _x0 |}] 38 | 39 | let%expect_test "Consume linear field of variable" = 40 | print_frontend_ir 41 | " 42 | class Foo { 43 | capability local Bar; 44 | var Baz f : Bar; 45 | } 46 | class Baz { 47 | capability linear Fa; 48 | var int g : Fa; 49 | } 50 | void main(){ 51 | let x = new Foo(); 52 | let y = consume x.f // Consume linear field of variable 53 | 54 | } 55 | " ; 56 | [%expect 57 | {| 58 | Program 59 | └──Class: Foo 60 | └──VTable [] 61 | └──Field: Class: Baz 62 | └──Class: Baz 63 | └──VTable [] 64 | └──Field: Int 65 | └──Main expr 66 | └──Expr: Let var: _x0 67 | └──Expr: Constructor for: Foo 68 | └──Expr: Let var: _y0 69 | └──Expr: Consume 70 | └──Expr: Objfield: _x0[0] |}] 71 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_function_application.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Function application" = 5 | print_frontend_ir 6 | " 7 | function int f (int x ){ x} 8 | void main(){ 9 | f(4) 10 | } 11 | " ; 12 | [%expect 13 | {| 14 | Program 15 | └── Function: f 16 | └── Return type: Int 17 | └──Param: Int x 18 | └──Body block 19 | └──Expr: Variable: x 20 | └──Main expr 21 | └──Expr: Function App 22 | └──Function: f 23 | └──Expr: Int:4 |}] 24 | 25 | let%expect_test "Function application with multiple args " = 26 | print_frontend_ir 27 | " 28 | function int f (int x, int y){ x} 29 | void main(){ 30 | f (3, 4) 31 | } 32 | " ; 33 | [%expect 34 | {| 35 | Program 36 | └── Function: f 37 | └── Return type: Int 38 | └──Param: Int x 39 | └──Param: Int y 40 | └──Body block 41 | └──Expr: Variable: x 42 | └──Main expr 43 | └──Expr: Function App 44 | └──Function: f 45 | └──Expr: Int:3 46 | └──Expr: Int:4 |}] 47 | 48 | let%expect_test "Function application with no args " = 49 | print_frontend_ir " 50 | function int f ( ){ 4} 51 | void main(){ 52 | f() 53 | } 54 | " ; 55 | [%expect 56 | {| 57 | Program 58 | └── Function: f 59 | └── Return type: Int 60 | └──Param: Void 61 | └──Body block 62 | └──Expr: Int:4 63 | └──Main expr 64 | └──Expr: Function App 65 | └──Function: f 66 | └──() |}] 67 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_immutable_refs_in_multiple_threads.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Immutable refs in multiple locals" = 5 | print_frontend_ir 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | function int test() { 12 | 5 13 | } 14 | void main() { 15 | let x = new Foo(f:5); 16 | let y = 5; 17 | finish{ 18 | // can read aliases in different locals as neither are mutable 19 | async { 20 | x; 21 | test(); 22 | y 23 | } 24 | x; 25 | y 26 | }; 27 | x.f 28 | } 29 | " ; 30 | [%expect 31 | {| 32 | Program 33 | └──Class: Foo 34 | └──VTable [] 35 | └──Field: Int 36 | └── Function: test 37 | └── Return type: Int 38 | └──Param: Void 39 | └──Body block 40 | └──Expr: Int:5 41 | └──Main expr 42 | └──Expr: Let var: _x0 43 | └──Expr: Constructor for: Foo 44 | └── Field: 0 45 | └──Expr: Int:5 46 | └──Expr: Let var: _y0 47 | └──Expr: Int:5 48 | └──Expr: Finish_async 49 | └── Async Expr Free Vars: 50 | └── (_x0) 51 | └──Async Expr block 52 | └──Expr: Variable: _x0 53 | └──Expr: Function App 54 | └──Function: test 55 | └──() 56 | └──Expr: Variable: _y0 57 | └──Current ThreadLocal Expr block 58 | └──Expr: Variable: _x0 59 | └──Expr: Variable: _y0 60 | └──Expr: Objfield: _x0[0] |}] 61 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_operators.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Arithmetic operators" = 5 | print_frontend_ir " 6 | void main(){ 7 | (((5 + (5 % 2)) - 10) * (1 / 2)) 8 | } 9 | " ; 10 | [%expect 11 | {| 12 | Program 13 | └──Main expr 14 | └──Expr: Bin Op: * 15 | └──Expr: Bin Op: - 16 | └──Expr: Bin Op: + 17 | └──Expr: Int:5 18 | └──Expr: Bin Op: % 19 | └──Expr: Int:5 20 | └──Expr: Int:2 21 | └──Expr: Int:10 22 | └──Expr: Bin Op: / 23 | └──Expr: Int:1 24 | └──Expr: Int:2 |}] 25 | 26 | let%expect_test "Comparison operators" = 27 | print_frontend_ir 28 | " 29 | void main(){ 30 | (4 < 5); 31 | let x = 4; 32 | (x <= x); 33 | (x > 3); 34 | (x >= 4); 35 | (x != 23); 36 | (x == 4) 37 | } 38 | " ; 39 | [%expect 40 | {| 41 | Program 42 | └──Main expr 43 | └──Expr: Bin Op: < 44 | └──Expr: Int:4 45 | └──Expr: Int:5 46 | └──Expr: Let var: _x0 47 | └──Expr: Int:4 48 | └──Expr: Bin Op: <= 49 | └──Expr: Variable: _x0 50 | └──Expr: Variable: _x0 51 | └──Expr: Bin Op: > 52 | └──Expr: Variable: _x0 53 | └──Expr: Int:3 54 | └──Expr: Bin Op: >= 55 | └──Expr: Variable: _x0 56 | └──Expr: Int:4 57 | └──Expr: Bin Op: != 58 | └──Expr: Variable: _x0 59 | └──Expr: Int:23 60 | └──Expr: Bin Op: == 61 | └──Expr: Variable: _x0 62 | └──Expr: Int:4 |}] 63 | 64 | let%expect_test "Boolean operators" = 65 | print_frontend_ir " 66 | void main(){ 67 | (true || false) && (!false) 68 | } 69 | " ; 70 | [%expect 71 | {| 72 | Program 73 | └──Main expr 74 | └──Expr: Bin Op: && 75 | └──Expr: Bin Op: || 76 | └──Expr: Bool:true 77 | └──Expr: Bool:false 78 | └──Expr: Unary Op: ! 79 | └──Expr: Bool:false |}] 80 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_shadowing_variables.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Variable shadowing in different blocks" = 5 | print_frontend_ir 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | void main(){ 12 | let x = 6; 13 | if true { 14 | let x = new Foo(f:5); // shadowing in an inner block is okay 15 | let y = -5; 16 | finish{ 17 | async { 18 | x; 19 | y 20 | } 21 | async{ 22 | x; 23 | y 24 | } 25 | x 26 | }; 27 | x.f 28 | } 29 | else { 30 | 5 31 | } 32 | } 33 | " ; 34 | [%expect 35 | {| 36 | Program 37 | └──Class: Foo 38 | └──VTable [] 39 | └──Field: Int 40 | └──Main expr 41 | └──Expr: Let var: _x0 42 | └──Expr: Int:6 43 | └──Expr: If 44 | └──Expr: Bool:true 45 | └──Then block 46 | └──Expr: Let var: _x1 47 | └──Expr: Constructor for: Foo 48 | └── Field: 0 49 | └──Expr: Int:5 50 | └──Expr: Let var: _y0 51 | └──Expr: Int:-5 52 | └──Expr: Finish_async 53 | └── Async Expr Free Vars: 54 | └── (_x1) 55 | └──Async Expr block 56 | └──Expr: Variable: _x1 57 | └──Expr: Variable: _y0 58 | └── Async Expr Free Vars: 59 | └── (_x1) 60 | └──Async Expr block 61 | └──Expr: Variable: _x1 62 | └──Expr: Variable: _y0 63 | └──Current ThreadLocal Expr block 64 | └──Expr: Variable: _x1 65 | └──Expr: Objfield: _x1[0] 66 | └──Else block 67 | └──Expr: Int:5 |}] 68 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/good_simple_capability_classes.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_frontend_ir 3 | 4 | let%expect_test "Simple linear class" = 5 | print_frontend_ir 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | int id (int x): Bar { x} 11 | } 12 | void main(){ 13 | let x = new Foo(); 14 | x.f := x.id(5) 15 | 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──VTable [_Foo__idi] 23 | └──Field: Int 24 | └── Function: _Foo__idi 25 | └── Return type: Int 26 | └──Param: Class: Foo this 27 | └──Param: Int x 28 | └──Body block 29 | └──Expr: Variable: x 30 | └──Main expr 31 | └──Expr: Let var: _x0 32 | └──Expr: Constructor for: Foo 33 | └──Expr: Assign 34 | └──Expr: Objfield: _x0[0] 35 | └──Expr: ObjMethod: _x0[0] 36 | └──Expr: Int:5 |}] 37 | 38 | let%expect_test "Simple local class" = 39 | print_frontend_ir 40 | " 41 | class Foo { 42 | capability local Bar; 43 | var int f : Bar; 44 | } 45 | void main(){ 46 | let x = new Foo(); 47 | x.f:= 5 48 | } 49 | " ; 50 | [%expect 51 | {| 52 | Program 53 | └──Class: Foo 54 | └──VTable [] 55 | └──Field: Int 56 | └──Main expr 57 | └──Expr: Let var: _x0 58 | └──Expr: Constructor for: Foo 59 | └──Expr: Assign 60 | └──Expr: Objfield: _x0[0] 61 | └──Expr: Int:5 |}] 62 | 63 | let%expect_test "Simple read class" = 64 | print_frontend_ir 65 | " 66 | class Foo { 67 | capability read Bar; 68 | const bool f : Bar; 69 | } 70 | void main(){ 71 | let x = new Foo(f:true); 72 | x.f 73 | } 74 | " ; 75 | [%expect 76 | {| 77 | Program 78 | └──Class: Foo 79 | └──VTable [] 80 | └──Field: Bool 81 | └──Main expr 82 | └──Expr: Let var: _x0 83 | └──Expr: Constructor for: Foo 84 | └── Field: 0 85 | └──Expr: Bool:true 86 | └──Expr: Objfield: _x0[0] |}] 87 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/print_frontend_ir.ml: -------------------------------------------------------------------------------- 1 | open Compile_program_ir 2 | 3 | let print_frontend_ir input_str = 4 | compile_program_ir (Lexing.from_string input_str) ~should_pprint_fir:true 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/ir_gen/print_frontend_ir.mli: -------------------------------------------------------------------------------- 1 | (** This module is used by all ir_gen expect tests *) 2 | 3 | val print_frontend_ir : string -> unit 4 | (** Given a bolt program as a string, print out its frontend IR *) 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/bad_consume_expr.ml: -------------------------------------------------------------------------------- 1 | open Print_parsed_ast 2 | 3 | let%expect_test "Consume non-variable" = 4 | print_parsed_ast " 5 | void main(){ 6 | let y1 = consume new Choco(f:5); 7 | } 8 | " ; 9 | [%expect {| Line:3 Position:29: syntax error |}] 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/bad_expressions_not_terminated.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Comment not terminated" = 5 | print_parsed_ast " 6 | /* This comment hasn't been terminated 7 | " ; 8 | [%expect 9 | {| Line:3 Position:3: Lexer - Unexpected EOF - please terminate your comment. |}] 10 | 11 | let%expect_test "Class defn not terminated" = 12 | print_parsed_ast 13 | " 14 | class Foo { 15 | capability linear Bar; 16 | var int f : Bar 17 | // Missing closing brace 18 | class Baz { 19 | capability linear Bar; 20 | var int f : Bar 21 | } 22 | void main() { 23 | let x = new Foo() in 24 | x.f:= 5 25 | end 26 | } 27 | " ; 28 | [%expect {| Line:6 Position:10: syntax error |}] 29 | 30 | let%expect_test "If Statement with no else branch" = 31 | print_parsed_ast " 32 | void main() { 33 | if true { 34 | 3 35 | } 36 | } 37 | " ; 38 | [%expect {| 39 | Line:6 Position:6: syntax error |}] 40 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/bad_function_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Function argument not annotated with type" = 5 | print_parsed_ast " 6 | function int f (x){ x} 7 | void main(){ 8 | f(4) 9 | } 10 | " ; 11 | [%expect {| Line:2 Position:23: syntax error |}] 12 | 13 | let%expect_test "Function not annotated with return type" = 14 | print_parsed_ast " 15 | function f (int x){ x} 16 | void main(){ 17 | f(4) 18 | } 19 | " ; 20 | [%expect {| Line:2 Position:17: syntax error |}] 21 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name test_parsing) 3 | (libraries core compile_program_ir) 4 | (inline_tests) 5 | (preprocess 6 | (pps ppx_jane)) 7 | (lint 8 | (pps ppx_jane ppx_js_style -check-doc-comments -annotated-ignores -styler 9 | -pretty -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_block_exprs.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Block of exprs" = 5 | print_parsed_ast 6 | " 7 | function int f (int x){x} 8 | void main(){ 9 | f(4); 10 | f(5); 11 | f(6) 12 | } 13 | " ; 14 | [%expect 15 | {| 16 | Program 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Expr: Variable: x 23 | └──Main block 24 | └──Expr: Function App 25 | └──Function: f 26 | └──Expr: Int:4 27 | └──Expr: Function App 28 | └──Function: f 29 | └──Expr: Int:5 30 | └──Expr: Function App 31 | └──Function: f 32 | └──Expr: Int:6 |}] 33 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_comments.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Comments interspersed with code" = 5 | print_parsed_ast 6 | " 7 | void main(){ 8 | /* This is a comment - it should not be parsed */ 9 | let x = 4;// Can occur after a line 10 | let y /*Or even midway*/ = 5; 11 | /* Or before */ x 12 | /* 13 | Comments 14 | Can 15 | Span 16 | Multiple 17 | Lines 18 | */ 19 | } 20 | " ; 21 | [%expect 22 | {| 23 | Program 24 | └──Main block 25 | └──Expr: Let var: x 26 | └──Expr: Int:4 27 | └──Expr: Let var: y 28 | └──Expr: Int:5 29 | └──Expr: Variable: x |}] 30 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_conditional_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Good if statement" = 5 | print_parsed_ast 6 | " 7 | void main() { 8 | let x = true; 9 | if x { 10 | 0 11 | } 12 | else { 13 | 1 14 | } 15 | } 16 | " ; 17 | [%expect 18 | {| 19 | Program 20 | └──Main block 21 | └──Expr: Let var: x 22 | └──Expr: Bool:true 23 | └──Expr: If 24 | └──Expr: Variable: x 25 | └──Then block 26 | └──Expr: Int:0 27 | └──Else block 28 | └──Expr: Int:1 |}] 29 | 30 | let%expect_test "Good while loop" = 31 | print_parsed_ast " 32 | void main(){ 33 | while (1 < 2){ 34 | let x = 5 35 | } 36 | } 37 | " ; 38 | [%expect 39 | {| 40 | Program 41 | └──Main block 42 | └──Expr: While 43 | └──Expr: Bin Op: < 44 | └──Expr: Int:1 45 | └──Expr: Int:2 46 | └──Body block 47 | └──Expr: Let var: x 48 | └──Expr: Int:5 |}] 49 | 50 | let%expect_test "Good for loop" = 51 | print_parsed_ast 52 | " 53 | void main(){ 54 | for (let i = 0; i < (5*5); i := i+1) { 55 | i 56 | } 57 | } 58 | " ; 59 | [%expect 60 | {| 61 | Program 62 | └──Main block 63 | └──Expr: For 64 | └──Expr: Let var: i 65 | └──Expr: Int:0 66 | └──Expr: Bin Op: < 67 | └──Expr: Variable: i 68 | └──Expr: Bin Op: * 69 | └──Expr: Int:5 70 | └──Expr: Int:5 71 | └──Expr: Assign 72 | └──Expr: Variable: i 73 | └──Expr: Bin Op: + 74 | └──Expr: Variable: i 75 | └──Expr: Int:1 76 | └──Body block 77 | └──Expr: Variable: i |}] 78 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_constructor.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Constructor with multiple args" = 5 | print_parsed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | const int g : Bar; 11 | const int h : Bar; 12 | } 13 | void main(){ 14 | let x = new Foo(f:4, g:5, h:6); 15 | x 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──Capabilities: 23 | └──Capability: Linear Bar 24 | └──Field Defn: f 25 | └──Modifier: Const 26 | └──Type expr: Int 27 | └──Capabilities: Bar 28 | └──Field Defn: g 29 | └──Modifier: Const 30 | └──Type expr: Int 31 | └──Capabilities: Bar 32 | └──Field Defn: h 33 | └──Modifier: Const 34 | └──Type expr: Int 35 | └──Capabilities: Bar 36 | └──Main block 37 | └──Expr: Let var: x 38 | └──Expr: Constructor for: Foo 39 | └── Field: f 40 | └──Expr: Int:4 41 | └── Field: g 42 | └──Expr: Int:5 43 | └── Field: h 44 | └──Expr: Int:6 45 | └──Expr: Variable: x |}] 46 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_function_application.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Function application" = 5 | print_parsed_ast 6 | " 7 | function int f (int x ){ x} 8 | void main(){ 9 | f(4) 10 | } 11 | " ; 12 | [%expect 13 | {| 14 | Program 15 | └── Function: f 16 | └── Return type: Int 17 | └──Param: x 18 | └──Type expr: Int 19 | └──Body block 20 | └──Expr: Variable: x 21 | └──Main block 22 | └──Expr: Function App 23 | └──Function: f 24 | └──Expr: Int:4 |}] 25 | 26 | let%expect_test "Function application with multiple args " = 27 | print_parsed_ast 28 | " 29 | function int f (int x, int y){ x} 30 | void main(){ 31 | f (3, 4) 32 | } 33 | " ; 34 | [%expect 35 | {| 36 | Program 37 | └── Function: f 38 | └── Return type: Int 39 | └──Param: x 40 | └──Type expr: Int 41 | └──Param: y 42 | └──Type expr: Int 43 | └──Body block 44 | └──Expr: Variable: x 45 | └──Main block 46 | └──Expr: Function App 47 | └──Function: f 48 | └──Expr: Int:3 49 | └──Expr: Int:4 |}] 50 | 51 | let%expect_test "Function application with no args " = 52 | print_parsed_ast " 53 | function int f ( ){ 4} 54 | void main(){ 55 | f() 56 | } 57 | " ; 58 | [%expect 59 | {| 60 | Program 61 | └── Function: f 62 | └── Return type: Int 63 | └──Param: Void 64 | └──Body block 65 | └──Expr: Int:4 66 | └──Main block 67 | └──Expr: Function App 68 | └──Function: f 69 | └──() |}] 70 | 71 | let%expect_test "Function application with boolean arg" = 72 | print_parsed_ast 73 | " 74 | function bool f (bool b ){ b } 75 | void main(){ 76 | f(true); 77 | f(false) 78 | } 79 | " ; 80 | [%expect 81 | {| 82 | Program 83 | └── Function: f 84 | └── Return type: Bool 85 | └──Param: b 86 | └──Type expr: Bool 87 | └──Body block 88 | └──Expr: Variable: b 89 | └──Main block 90 | └──Expr: Function App 91 | └──Function: f 92 | └──Expr: Bool:true 93 | └──Expr: Function App 94 | └──Function: f 95 | └──Expr: Bool:false |}] 96 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_immutable_refs_in_multiple_threads.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Immutable refs in multiple locals" = 5 | print_parsed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | void main() { 12 | let x = new Foo(f:5); 13 | let y = 5; 14 | finish{ 15 | // can read aliases in different locals as neither are mutable 16 | async { 17 | x; 18 | y 19 | } 20 | x; 21 | y 22 | }; 23 | x.f 24 | } 25 | " ; 26 | [%expect 27 | {| 28 | Program 29 | └──Class: Foo 30 | └──Capabilities: 31 | └──Capability: Read Bar 32 | └──Field Defn: f 33 | └──Modifier: Const 34 | └──Type expr: Int 35 | └──Capabilities: Bar 36 | └──Main block 37 | └──Expr: Let var: x 38 | └──Expr: Constructor for: Foo 39 | └── Field: f 40 | └──Expr: Int:5 41 | └──Expr: Let var: y 42 | └──Expr: Int:5 43 | └──Expr: Finish async 44 | └──Async Expr block 45 | └──Expr: Variable: x 46 | └──Expr: Variable: y 47 | └──Current thread block 48 | └──Expr: Variable: x 49 | └──Expr: Variable: y 50 | └──Expr: Objfield: x.f |}] 51 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_operators.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Arithmetic operators" = 5 | print_parsed_ast " 6 | void main(){ 7 | (((5 + (5 % 2)) - 10) * (1 / 2)) 8 | } 9 | " ; 10 | [%expect 11 | {| 12 | Program 13 | └──Main block 14 | └──Expr: Bin Op: * 15 | └──Expr: Bin Op: - 16 | └──Expr: Bin Op: + 17 | └──Expr: Int:5 18 | └──Expr: Bin Op: % 19 | └──Expr: Int:5 20 | └──Expr: Int:2 21 | └──Expr: Int:10 22 | └──Expr: Bin Op: / 23 | └──Expr: Int:1 24 | └──Expr: Int:2 |}] 25 | 26 | let%expect_test "Comparison operators" = 27 | print_parsed_ast 28 | " 29 | void main(){ 30 | (4 < 5); 31 | let x = 4; 32 | (x <= x); 33 | (x > 3); 34 | (x >= 4); 35 | (x != 23); 36 | (x == 4) 37 | } 38 | " ; 39 | [%expect 40 | {| 41 | Program 42 | └──Main block 43 | └──Expr: Bin Op: < 44 | └──Expr: Int:4 45 | └──Expr: Int:5 46 | └──Expr: Let var: x 47 | └──Expr: Int:4 48 | └──Expr: Bin Op: <= 49 | └──Expr: Variable: x 50 | └──Expr: Variable: x 51 | └──Expr: Bin Op: > 52 | └──Expr: Variable: x 53 | └──Expr: Int:3 54 | └──Expr: Bin Op: >= 55 | └──Expr: Variable: x 56 | └──Expr: Int:4 57 | └──Expr: Bin Op: != 58 | └──Expr: Variable: x 59 | └──Expr: Int:23 60 | └──Expr: Bin Op: == 61 | └──Expr: Variable: x 62 | └──Expr: Int:4 |}] 63 | 64 | let%expect_test "Boolean operators" = 65 | print_parsed_ast " 66 | void main(){ 67 | (true || false) && (!false) 68 | } 69 | " ; 70 | [%expect 71 | {| 72 | Program 73 | └──Main block 74 | └──Expr: Bin Op: && 75 | └──Expr: Bin Op: || 76 | └──Expr: Bool:true 77 | └──Expr: Bool:false 78 | └──Expr: Unary Op: ! 79 | └──Expr: Bool:false |}] 80 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/good_shadowing_variables.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_parsed_ast 3 | 4 | let%expect_test "Variable shadowing in different blocks" = 5 | print_parsed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | void main(){ 12 | let x = 6; 13 | if true { 14 | let x = new Foo(f:5); // shadowing in an inner block is okay 15 | let y = -5; 16 | finish{ 17 | async { 18 | x; 19 | y 20 | } 21 | async{ 22 | x; 23 | y 24 | } 25 | }; 26 | x.f 27 | } 28 | else { 29 | 5 30 | } 31 | } 32 | " ; 33 | [%expect 34 | {| 35 | Program 36 | └──Class: Foo 37 | └──Capabilities: 38 | └──Capability: Read Bar 39 | └──Field Defn: f 40 | └──Modifier: Const 41 | └──Type expr: Int 42 | └──Capabilities: Bar 43 | └──Main block 44 | └──Expr: Let var: x 45 | └──Expr: Int:6 46 | └──Expr: If 47 | └──Expr: Bool:true 48 | └──Then block 49 | └──Expr: Let var: x 50 | └──Expr: Constructor for: Foo 51 | └── Field: f 52 | └──Expr: Int:5 53 | └──Expr: Let var: y 54 | └──Expr: Int:-5 55 | └──Expr: Finish async 56 | └──Async Expr block 57 | └──Expr: Variable: x 58 | └──Expr: Variable: y 59 | └──Async Expr block 60 | └──Expr: Variable: x 61 | └──Expr: Variable: y 62 | └──Current thread block 63 | └──Expr: Objfield: x.f 64 | └──Else block 65 | └──Expr: Int:5 |}] 66 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/print_parsed_ast.ml: -------------------------------------------------------------------------------- 1 | open Compile_program_ir 2 | 3 | let print_parsed_ast input_str = 4 | compile_program_ir (Lexing.from_string input_str) ~should_pprint_past:true 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/parsing/print_parsed_ast.mli: -------------------------------------------------------------------------------- 1 | (** This module is used by all parser expect tests *) 2 | 3 | val print_parsed_ast : string -> unit 4 | (** Given a bolt program as a string, print out its parsed AST *) 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_access.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Accessing a free variable" = 5 | print_typed_ast " 6 | void main(){ 7 | x 8 | } 9 | " ; 10 | [%expect {| 11 | Line:3 Position:5 Type error - Variable x not defined in environment |}] 12 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_assignment.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Assign to field not in class" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | void main(){ 12 | let x = new Foo(); 13 | x.g := 5 // Can't assign to field g as not in class 14 | } 15 | " ; 16 | [%expect {| 17 | Line:8 Position:7 Type error - Field g not defined in environment |}] 18 | 19 | let%expect_test "Assign wrong type" = 20 | print_typed_ast 21 | " 22 | class Foo { 23 | capability linear Bar; 24 | var int f : Bar; 25 | } 26 | void main(){ 27 | let y = new Foo(); 28 | let x = new Foo(); 29 | x.f := y // Error - try to assign Foo to int 30 | } 31 | " ; 32 | [%expect 33 | {| 34 | Line:9 Position:11 Type error - Assigning type Foo to a field of type Int |}] 35 | 36 | let%expect_test "Assign value to const" = 37 | print_typed_ast 38 | " 39 | class Foo { 40 | capability read Bar; 41 | const int f : Bar; 42 | } 43 | void main(){ 44 | let x = new Foo(); 45 | x.f := 5 // Can't assign to const field 46 | } 47 | " ; 48 | [%expect {| 49 | Line:8 Position:7 Type error - Assigning expr to a const field. |}] 50 | 51 | let%expect_test "Assign value to this" = 52 | print_typed_ast 53 | " 54 | class Foo { 55 | capability read Bar; 56 | const int f : Bar; 57 | Foo test(Foo x) : Bar { 58 | this := x 59 | } 60 | } 61 | void main(){ 62 | let x = new Foo(); 63 | x.test(x) 64 | } 65 | " ; 66 | [%expect {| 67 | Line:6 Position:9 Type error - Assigning expr to 'this'. |}] 68 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_conditional_expr.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "If statement with non-boolean condition" = 5 | print_typed_ast 6 | " 7 | void main(){ 8 | if 1 { // 1 is not a boolean value 9 | 0 10 | } 11 | else { 12 | 1 13 | } 14 | } 15 | " ; 16 | [%expect 17 | {| 18 | Line:3 Position:6 Type error - If statement condition expression should have boolean type but instead has type Int |}] 19 | 20 | let%expect_test "If statement then and else branches' types are different " = 21 | print_typed_ast 22 | " 23 | void main(){ 24 | if true { 25 | 0 26 | } 27 | else { 28 | false 29 | } 30 | } 31 | " ; 32 | [%expect 33 | {| 34 | Line:3 Position:6 Type error - If statement branches' types' not consistent - then branch has type Int but else branch has type Bool |}] 35 | 36 | let%expect_test "While loop with non-boolean condition" = 37 | print_typed_ast 38 | " 39 | void main(){ 40 | while 1 { // 1 is not a boolean value 41 | 0 42 | }; 4 43 | } 44 | " ; 45 | [%expect 46 | {| 47 | Line:3 Position:6 Type error - Loop condition expression should have boolean type but instead has type Int |}] 48 | 49 | let%expect_test "For loop with non-boolean condition" = 50 | print_typed_ast 51 | " 52 | void main(){ 53 | for (let i = 0; i ; i := i+1 ) { 54 | i 55 | } 56 | } 57 | " ; 58 | [%expect 59 | {| 60 | Line:3 Position:4 Type error - Loop condition expression should have boolean type but instead has type Int |}] 61 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_constructor.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Class not defined" = 5 | print_typed_ast 6 | " 7 | void main() { 8 | let x = new Foo() ; // Foo not defined! 9 | x 10 | } 11 | " ; 12 | [%expect {| 13 | Line:3 Position:15 Type error - Class Foo not defined in environment |}] 14 | 15 | let%expect_test "Incorrect constructor field arg type" = 16 | print_typed_ast 17 | " 18 | class Foo { 19 | capability linear Bar; 20 | const int f : Bar; 21 | const int g : Bar ; 22 | const int h : Bar; 23 | } 24 | void main() { 25 | let y = new Foo(); 26 | let x = new Foo(f:y, g:5, h:6); //Error - try to assign Foo to int in constructor 27 | x 28 | } 29 | " ; 30 | [%expect 31 | {| 32 | Line:10 Position:15 Type mismatch - constructor expected argument of type Int, instead received type Foo |}] 33 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_consume_variable.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Consume this" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | Foo test() : Bar { 11 | consume this 12 | } 13 | 14 | } 15 | void main(){ 16 | } 17 | " ; 18 | [%expect {| 19 | Line:6 Position:10 Type error - Trying to consume 'this'. |}] 20 | 21 | let%expect_test "Consume const field" = 22 | print_typed_ast 23 | " 24 | class Foo { 25 | capability linear Bar; 26 | const int f : Bar; 27 | Foo test() : Bar { 28 | consume this.f 29 | } 30 | 31 | } 32 | void main(){ 33 | } 34 | " ; 35 | [%expect {| 36 | Line:6 Position:10 Type error - Trying to consume a const field. |}] 37 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_function_application.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Function return type incorrect" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | } 11 | function Foo f (int z, Foo y) { 12 | z 13 | } 14 | void main(){5} 15 | " ; 16 | [%expect 17 | {| Type Error for function f: expected return type of Foo but got Int instead |}] 18 | 19 | let%expect_test "Function not present" = 20 | print_typed_ast " 21 | void main(){ 22 | f(1) // No definition for function f 23 | } 24 | " ; 25 | [%expect {| Line:3 Position:5 Type error - function f is not defined in environment |}] 26 | 27 | let%expect_test "Function arg type mismatch" = 28 | print_typed_ast 29 | " 30 | function bool f (bool b ){ b } 31 | void main(){ 32 | f(5) 33 | } 34 | " ; 35 | [%expect 36 | {| 37 | Line:4 Position:7 Type error - function f expected arguments of type Bool, instead received type Int |}] 38 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_function_defn.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Function args in wrong order" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | } 11 | function int f (int z, Foo y) { 12 | z 13 | } 14 | void main(){ 15 | let y = new Foo(); 16 | f(y, 4) // Error - args in wrong order 17 | } 18 | " ; 19 | [%expect 20 | {| Line:11 Position:7 Type error - function f expected arguments of type Int * Foo, instead received type Foo * Int |}] 21 | 22 | let%expect_test "Function arg type mismatch" = 23 | print_typed_ast 24 | " 25 | class Foo { 26 | capability linear Bar; 27 | var int f : Bar; 28 | } 29 | function int f (int z) { 30 | z 31 | } 32 | void main() { 33 | let y = new Foo(); 34 | f(y) // Error - y is not an int 35 | } 36 | " ; 37 | [%expect 38 | {| Line:11 Position:7 Type error - function f expected arguments of type Int, instead received type Foo |}] 39 | 40 | let%expect_test "Function too many args" = 41 | print_typed_ast 42 | " 43 | class Foo { 44 | capability linear Bar; 45 | var int f : Bar; 46 | } 47 | function int f (int z) { 48 | z 49 | } 50 | void main() { 51 | let y = new Foo(); 52 | f(y,y) // Error - too many args 53 | } 54 | " ; 55 | [%expect 56 | {| Line:11 Position:7 Type error - function f expected arguments of type Int, instead received type Foo * Foo |}] 57 | 58 | let%expect_test "Invalid function return type" = 59 | print_typed_ast " function NonExistentClass gen(){ 60 | } 61 | void main(){ 62 | } 63 | " ; 64 | [%expect 65 | {| 66 | Type error for function gen return type - class NonExistentClass doesn't exists |}] 67 | 68 | let%expect_test "Invalid function param type" = 69 | print_typed_ast 70 | " 71 | function void gen(NonExistentClass x) { 72 | } 73 | void main(){ 74 | } 75 | " ; 76 | [%expect 77 | {| 78 | Type error for function gen param x - class NonExistentClass doesn't exists |}] 79 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_let_declaration.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Try to define 'this'" = 5 | print_typed_ast " 6 | void main(){ 7 | let this = 1 8 | } 9 | " ; 10 | [%expect {| 11 | Line:3 Position:5 Type error - Trying to declare 'this'. |}] 12 | 13 | let%expect_test "Incorrect type annotation" = 14 | print_typed_ast " 15 | void main(){ 16 | let x : bool = 1 17 | } 18 | " ; 19 | [%expect 20 | {| 21 | Line:3 Position:5 Type error - variable x annotated with Bool but actual type was Int |}] 22 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_method_call.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Trying to call an undefined method" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | 11 | } 12 | void main(){ 13 | let x = new Foo(f:5); 14 | x.gen() // No method gen() in Foo 15 | } 16 | " ; 17 | [%expect 18 | {| 19 | Line:9 Position:7 Type error - method gen is not defined in environment |}] 20 | 21 | let%expect_test "Trying to call a method with wrong args" = 22 | print_typed_ast 23 | " 24 | class Foo { 25 | capability read Bar; 26 | const int f : Bar; 27 | int id(int x) : Bar { x} 28 | } 29 | void main(){ 30 | let x = new Foo(f:5); 31 | x.id() // No args passed to x 32 | } 33 | " ; 34 | [%expect 35 | {| Line:9 Position:7 Type error - method id expected arguments of type Int, instead received type Void |}] 36 | 37 | let%expect_test "Trying to call a method with arg type_mismatch" = 38 | print_typed_ast 39 | " 40 | class Foo { 41 | capability read Bar; 42 | const int f : Bar; 43 | int id(int x) : Bar { x} 44 | } 45 | void main(){ 46 | let x = new Foo(f:5); 47 | let y = new Foo(f:6); 48 | x.id(y) // Wrong args passed to x 49 | } 50 | " ; 51 | [%expect 52 | {| Line:10 Position:7 Type error - method id expected arguments of type Int, instead received type Foo |}] 53 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/bad_variable_shadowing.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Variable shadowing in same block" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | void main() { 12 | let x = 6; 13 | let x = new Foo(f:5); // shadowing in an same block not allowed 14 | let y = 5; 15 | finish{ 16 | async { 17 | x; 18 | y 19 | } 20 | async{ 21 | x; 22 | y 23 | } 24 | }; 25 | x.f 26 | } 27 | " ; 28 | [%expect 29 | {| 30 | Line:6 Position:16 Type error: Duplicate variable declarations in same block. |}] 31 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name test_typing_core_lang) 3 | (libraries core compile_program_ir) 4 | (inline_tests) 5 | (preprocess 6 | (pps ppx_jane)) 7 | (lint 8 | (pps ppx_jane ppx_js_style -check-doc-comments -annotated-ignores -styler 9 | -pretty -dated-deprecation))) 10 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_block_exprs.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Block of exprs" = 5 | print_typed_ast 6 | " 7 | function int f (int x){x} 8 | void main(){ 9 | f(4); 10 | f(5); 11 | f(6) 12 | } 13 | " ; 14 | [%expect 15 | {| 16 | Program 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Type expr: Int 23 | └──Expr: Variable: x 24 | └──Type expr: Int 25 | └──Main block 26 | └──Type expr: Int 27 | └──Expr: Function App 28 | └──Type expr: Int 29 | └──Function: f 30 | └──Expr: Int:4 31 | └──Expr: Function App 32 | └──Type expr: Int 33 | └──Function: f 34 | └──Expr: Int:5 35 | └──Expr: Function App 36 | └──Type expr: Int 37 | └──Function: f 38 | └──Expr: Int:6 |}] 39 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_comments.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Comments interspersed with code" = 5 | print_typed_ast 6 | " 7 | void main(){ 8 | /* This is a comment - it should not be parsed */ 9 | let x = 4;// Can occur after a line 10 | let y /*Or even midway*/ = 5; 11 | /* Or before */ x 12 | /* 13 | Comments 14 | Can 15 | Span 16 | Multiple 17 | Lines 18 | */ 19 | } 20 | " ; 21 | [%expect 22 | {| 23 | Program 24 | └──Main block 25 | └──Type expr: Int 26 | └──Expr: Let var: x 27 | └──Type expr: Int 28 | └──Expr: Int:4 29 | └──Expr: Let var: y 30 | └──Type expr: Int 31 | └──Expr: Int:5 32 | └──Expr: Variable: x 33 | └──Type expr: Int |}] 34 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_constructor.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Constructor with multiple args" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | const int f : Bar; 10 | const int g : Bar; 11 | const int h : Bar; 12 | } 13 | void main(){ 14 | let x = new Foo(f:4, g:5, h:6); 15 | x 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──Capabilities: 23 | └──Capability: Linear Bar 24 | └──Field Defn: f 25 | └──Modifier: Const 26 | └──Type expr: Int 27 | └──Capabilities: Bar 28 | └──Field Defn: g 29 | └──Modifier: Const 30 | └──Type expr: Int 31 | └──Capabilities: Bar 32 | └──Field Defn: h 33 | └──Modifier: Const 34 | └──Type expr: Int 35 | └──Capabilities: Bar 36 | └──Main block 37 | └──Type expr: Foo 38 | └──Expr: Let var: x 39 | └──Type expr: Foo 40 | └──Expr: Constructor for: Foo 41 | └──Type expr: Foo 42 | └── Field: f 43 | └──Type expr: Int 44 | └──Expr: Int:4 45 | └── Field: g 46 | └──Type expr: Int 47 | └──Expr: Int:5 48 | └── Field: h 49 | └──Type expr: Int 50 | └──Expr: Int:6 51 | └──Expr: Variable: x 52 | └──Type expr: Foo |}] 53 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_function_defn.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Function return type matches" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability linear Bar; 9 | var int f : Bar; 10 | } 11 | function int f (int z) { 12 | z // good - returns an int 13 | } 14 | void main() { 15 | f(1) 16 | } 17 | " ; 18 | [%expect 19 | {| 20 | Program 21 | └──Class: Foo 22 | └──Capabilities: 23 | └──Capability: Linear Bar 24 | └──Field Defn: f 25 | └──Modifier: Var 26 | └──Type expr: Int 27 | └──Capabilities: Bar 28 | └── Function: f 29 | └── Return type: Int 30 | └──Param: z 31 | └──Type expr: Int 32 | └──Body block 33 | └──Type expr: Int 34 | └──Expr: Variable: z 35 | └──Type expr: Int 36 | └──Main block 37 | └──Type expr: Int 38 | └──Expr: Function App 39 | └──Type expr: Int 40 | └──Function: f 41 | └──Expr: Int:1 |}] 42 | 43 | let%expect_test "Function void return type matches any type " = 44 | print_typed_ast 45 | " 46 | class Foo { 47 | capability linear Bar; 48 | var int f : Bar; 49 | } 50 | function void f (int z) { 51 | z // good - throws away int return type 52 | } 53 | void main() { 54 | f(1) 55 | } 56 | " ; 57 | [%expect 58 | {| 59 | Program 60 | └──Class: Foo 61 | └──Capabilities: 62 | └──Capability: Linear Bar 63 | └──Field Defn: f 64 | └──Modifier: Var 65 | └──Type expr: Int 66 | └──Capabilities: Bar 67 | └── Function: f 68 | └── Return type: Void 69 | └──Param: z 70 | └──Type expr: Int 71 | └──Body block 72 | └──Type expr: Int 73 | └──Expr: Variable: z 74 | └──Type expr: Int 75 | └──Main block 76 | └──Type expr: Void 77 | └──Expr: Function App 78 | └──Type expr: Void 79 | └──Function: f 80 | └──Expr: Int:1 |}] 81 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_immutable_refs_in_multiple_threads.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Immutable refs in multiple locals" = 5 | print_typed_ast 6 | " 7 | class Foo { 8 | capability read Bar; 9 | const int f : Bar; 10 | } 11 | function int test() { 12 | 5 13 | } 14 | void main() { 15 | let x = new Foo(f:5); 16 | let y = 5; 17 | finish{ 18 | // can read aliases in different locals as neither are mutable 19 | async { 20 | x; 21 | test(); 22 | y 23 | } 24 | x; 25 | y 26 | }; 27 | x.f 28 | } 29 | " ; 30 | [%expect 31 | {| 32 | Program 33 | └──Class: Foo 34 | └──Capabilities: 35 | └──Capability: Read Bar 36 | └──Field Defn: f 37 | └──Modifier: Const 38 | └──Type expr: Int 39 | └──Capabilities: Bar 40 | └── Function: test 41 | └── Return type: Int 42 | └──Param: Void 43 | └──Body block 44 | └──Type expr: Int 45 | └──Expr: Int:5 46 | └──Main block 47 | └──Type expr: Int 48 | └──Expr: Let var: x 49 | └──Type expr: Foo 50 | └──Expr: Constructor for: Foo 51 | └──Type expr: Foo 52 | └── Field: f 53 | └──Type expr: Int 54 | └──Expr: Int:5 55 | └──Expr: Let var: y 56 | └──Type expr: Int 57 | └──Expr: Int:5 58 | └──Expr: Finish_async 59 | └──Type expr: Int 60 | └──Async Expr block 61 | └──Type expr: Int 62 | └──Expr: Variable: x 63 | └──Type expr: Foo 64 | └──Expr: Function App 65 | └──Type expr: Int 66 | └──Function: test 67 | └──() 68 | └──Expr: Variable: y 69 | └──Type expr: Int 70 | └──Current thread block 71 | └──Type expr: Int 72 | └──Expr: Variable: x 73 | └──Type expr: Foo 74 | └──Expr: Variable: y 75 | └──Type expr: Int 76 | └──Expr: Objfield: (Class: Foo) x.f 77 | └──Type expr: Int |}] 78 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/good_let_declaration.ml: -------------------------------------------------------------------------------- 1 | open Core 2 | open Print_typed_ast 3 | 4 | let%expect_test "Correct type annotation" = 5 | print_typed_ast " 6 | void main(){ 7 | let x : int = 1 8 | } 9 | " ; 10 | [%expect 11 | {| 12 | Program 13 | └──Main block 14 | └──Type expr: Int 15 | └──Expr: Let var: x 16 | └──Type expr: Int 17 | └──Expr: Int:1 |}] 18 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/print_typed_ast.ml: -------------------------------------------------------------------------------- 1 | open Compile_program_ir 2 | 3 | let print_typed_ast input_str = 4 | compile_program_ir (Lexing.from_string input_str) ~should_pprint_tast:true 5 | -------------------------------------------------------------------------------- /tests/frontend/expect/typing/print_typed_ast.mli: -------------------------------------------------------------------------------- 1 | (** This module is used by all core language type-checker expect tests *) 2 | 3 | val print_typed_ast : string -> unit 4 | (** Given a bolt program as a string, print out its typed AST *) 5 | -------------------------------------------------------------------------------- /tests/frontend/integration/example-generics.bolt: -------------------------------------------------------------------------------- 1 | class Foo{ 2 | capability linear Bar; 3 | var T f : Bar; 4 | void copy(Foo x): Bar{ 5 | finish{ 6 | async{ 7 | this.f := x.f 8 | } 9 | let y = new Foo(f:0); 10 | for (let i=0; i < 100; i:=i+1){ 11 | y.setF((y.getF()) + i) 12 | }; 13 | printf("Value of y: %d", y.getF() ) 14 | } 15 | } 16 | void setF(T f) : Bar { 17 | this.f := f 18 | } 19 | T getF() : Bar { 20 | this.f 21 | } 22 | 23 | void baz() : Bar{ 24 | let z = getTrueFoo(); 25 | if (!(z.f)){ 26 | z.setF(true) 27 | 28 | } 29 | else{ 30 | z.setF(false) 31 | } 32 | } 33 | } 34 | 35 | function Foo getTrueFoo(){ 36 | new Foo(f:true) 37 | } 38 | 39 | 40 | void main() { 41 | let x = new Foo(f:5); 42 | let y = new Foo(); 43 | y.copy(consume x) 44 | } -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads-print-data-race-ast.out.expected: -------------------------------------------------------------------------------- 1 | Program 2 | └──Class: Foo 3 | └──Capabilities: 4 | └──Capability: Linear Bar 5 | └──Field Defn: f 6 | └──Modifier: Var 7 | └──Type expr: Int 8 | └──Capabilities: Bar 9 | └──Field Defn: g 10 | └──Modifier: Const 11 | └──Type expr: Int 12 | └──Capabilities: Bar 13 | └──Field Defn: h 14 | └──Modifier: Const 15 | └──Type expr: Int 16 | └──Capabilities: Bar 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Type expr: Int 23 | └──Expr: Variable: x 24 | └──Type expr: Int 25 | └──Main block 26 | └──Type expr: Void 27 | └──Expr: Let var: _x0 28 | └──Type expr: Foo 29 | └──Expr: Constructor for: Foo 30 | └──Type expr: Foo 31 | └── Field: f 32 | └──Type expr: Int 33 | └──Expr: Int:5 34 | └──Expr: Finish_async 35 | └──Type expr: Void 36 | └── Async Expr Free Vars: 37 | └──Async Expr block 38 | └──Type expr: Int 39 | └──Expr: Function App 40 | └──Type expr: Int 41 | └──Function: f 42 | └──Expr: Int:5 43 | └── Async Expr Free Vars: 44 | └──Async Expr block 45 | └──Type expr: Int 46 | └──Expr: Let var: _w0 47 | └──Type expr: Foo 48 | └──Expr: Constructor for: Foo 49 | └──Type expr: Foo 50 | └── Field: g 51 | └──Type expr: Int 52 | └──Expr: Int:5 53 | └──Expr: Assign 54 | └──Type expr: Int 55 | └──Expr: Objfield: (Class: Foo) _w0.f 56 | └──Type expr: Int 57 | └──Capabilities: 58 | └──Capability: Linear Bar 59 | └──Expr: Int:5 60 | └── Current ThreadLocal Expr Free Vars: 61 | └── (Foo) _x0, Capabilities: Bar 62 | └──Current thread block 63 | └──Type expr: Void 64 | └──Expr: Printf 65 | └──Value of x.f: %d\n 66 | └──Expr: Objfield: (Class: Foo) _x0.f 67 | └──Type expr: Int 68 | └──Capabilities: 69 | └──Capability: Linear Bar 70 | -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads-print-desugared-ast.out.expected: -------------------------------------------------------------------------------- 1 | Program 2 | └──Class: Foo 3 | └──Capabilities: 4 | └──Capability: Linear Bar 5 | └──Field Defn: f 6 | └──Modifier: Var 7 | └──Type expr: Int 8 | └──Capabilities: Bar 9 | └──Field Defn: g 10 | └──Modifier: Const 11 | └──Type expr: Int 12 | └──Capabilities: Bar 13 | └──Field Defn: h 14 | └──Modifier: Const 15 | └──Type expr: Int 16 | └──Capabilities: Bar 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Type expr: Int 23 | └──Expr: Variable: x 24 | └──Type expr: Int 25 | └──Main block 26 | └──Type expr: Void 27 | └──Expr: Let var: _x0 28 | └──Type expr: Foo 29 | └──Expr: Constructor for: Foo 30 | └──Type expr: Foo 31 | └── Field: f 32 | └──Type expr: Int 33 | └──Expr: Int:5 34 | └──Expr: Finish_async 35 | └──Type expr: Void 36 | └── Async Expr Free Vars: 37 | └──Async Expr block 38 | └──Type expr: Int 39 | └──Expr: Function App 40 | └──Type expr: Int 41 | └──Function: f 42 | └──Expr: Int:5 43 | └── Async Expr Free Vars: 44 | └──Async Expr block 45 | └──Type expr: Int 46 | └──Expr: Let var: _w0 47 | └──Type expr: Foo 48 | └──Expr: Constructor for: Foo 49 | └──Type expr: Foo 50 | └── Field: g 51 | └──Type expr: Int 52 | └──Expr: Int:5 53 | └──Expr: Assign 54 | └──Type expr: Int 55 | └──Expr: Objfield: (Class: Foo) _w0.f 56 | └──Type expr: Int 57 | └──Capabilities: 58 | └──Capability: Linear Bar 59 | └──Expr: Int:5 60 | └── Current ThreadLocal Expr Free Vars: 61 | └── (Foo) _x0, Capabilities: Bar 62 | └──Current thread block 63 | └──Type expr: Void 64 | └──Expr: Printf 65 | └──Value of x.f: %d\n 66 | └──Expr: Objfield: (Class: Foo) _x0.f 67 | └──Type expr: Int 68 | └──Capabilities: 69 | └──Capability: Linear Bar 70 | -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads-print-frontend-ir.out.expected: -------------------------------------------------------------------------------- 1 | Program 2 | └──Class: Foo 3 | └──VTable [] 4 | └──Field: Int 5 | └──Field: Int 6 | └──Field: Int 7 | └── Function: f 8 | └── Return type: Int 9 | └──Param: Int x 10 | └──Body block 11 | └──Expr: Variable: x 12 | └──Main expr 13 | └──Expr: Let var: _x0 14 | └──Expr: Constructor for: Foo 15 | └── Field: 0 16 | └──Expr: Int:5 17 | └──Expr: Finish_async 18 | └── Async Expr Free Vars: 19 | └── () 20 | └──Async Expr block 21 | └──Expr: Function App 22 | └──Function: f 23 | └──Expr: Int:5 24 | └── Async Expr Free Vars: 25 | └── () 26 | └──Async Expr block 27 | └──Expr: Let var: _w0 28 | └──Expr: Constructor for: Foo 29 | └── Field: 1 30 | └──Expr: Int:5 31 | └──Expr: Assign 32 | └──Expr: Objfield: _w0[0] 33 | └──Expr: Int:5 34 | └──Current ThreadLocal Expr block 35 | └──Expr: Printf 36 | └──Value of x.f: %d\n 37 | └──Expr: Objfield: _x0[0] 38 | -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads-print-parsed-ast.out.expected: -------------------------------------------------------------------------------- 1 | Program 2 | └──Class: Foo 3 | └──Capabilities: 4 | └──Capability: Linear Bar 5 | └──Field Defn: f 6 | └──Modifier: Var 7 | └──Type expr: Int 8 | └──Capabilities: Bar 9 | └──Field Defn: g 10 | └──Modifier: Const 11 | └──Type expr: Int 12 | └──Capabilities: Bar 13 | └──Field Defn: h 14 | └──Modifier: Const 15 | └──Type expr: Int 16 | └──Capabilities: Bar 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Expr: Variable: x 23 | └──Main block 24 | └──Expr: Let var: x 25 | └──Expr: Constructor for: Foo 26 | └── Field: f 27 | └──Expr: Int:5 28 | └──Expr: Finish async 29 | └──Async Expr block 30 | └──Expr: Function App 31 | └──Function: f 32 | └──Expr: Int:5 33 | └──Async Expr block 34 | └──Expr: Let var: w 35 | └──Expr: Constructor for: Foo 36 | └── Field: g 37 | └──Expr: Int:5 38 | └──Expr: Assign 39 | └──Expr: Objfield: w.f 40 | └──Expr: Int:5 41 | └──Current thread block 42 | └──Expr: Printf 43 | └──Value of x.f: %d\n 44 | └──Expr: Objfield: x.f 45 | -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads-print-typed-ast.out.expected: -------------------------------------------------------------------------------- 1 | Program 2 | └──Class: Foo 3 | └──Capabilities: 4 | └──Capability: Linear Bar 5 | └──Field Defn: f 6 | └──Modifier: Var 7 | └──Type expr: Int 8 | └──Capabilities: Bar 9 | └──Field Defn: g 10 | └──Modifier: Const 11 | └──Type expr: Int 12 | └──Capabilities: Bar 13 | └──Field Defn: h 14 | └──Modifier: Const 15 | └──Type expr: Int 16 | └──Capabilities: Bar 17 | └── Function: f 18 | └── Return type: Int 19 | └──Param: x 20 | └──Type expr: Int 21 | └──Body block 22 | └──Type expr: Int 23 | └──Expr: Variable: x 24 | └──Type expr: Int 25 | └──Main block 26 | └──Type expr: Void 27 | └──Expr: Let var: x 28 | └──Type expr: Foo 29 | └──Expr: Constructor for: Foo 30 | └──Type expr: Foo 31 | └── Field: f 32 | └──Type expr: Int 33 | └──Expr: Int:5 34 | └──Expr: Finish_async 35 | └──Type expr: Void 36 | └──Async Expr block 37 | └──Type expr: Int 38 | └──Expr: Function App 39 | └──Type expr: Int 40 | └──Function: f 41 | └──Expr: Int:5 42 | └──Async Expr block 43 | └──Type expr: Int 44 | └──Expr: Let var: w 45 | └──Type expr: Foo 46 | └──Expr: Constructor for: Foo 47 | └──Type expr: Foo 48 | └── Field: g 49 | └──Type expr: Int 50 | └──Expr: Int:5 51 | └──Expr: Assign 52 | └──Type expr: Int 53 | └──Expr: Objfield: (Class: Foo) w.f 54 | └──Type expr: Int 55 | └──Expr: Int:5 56 | └──Current thread block 57 | └──Type expr: Void 58 | └──Expr: Printf 59 | └──Value of x.f: %d\n 60 | └──Expr: Objfield: (Class: Foo) x.f 61 | └──Type expr: Int 62 | -------------------------------------------------------------------------------- /tests/frontend/integration/independent_threads.bolt: -------------------------------------------------------------------------------- 1 | class Foo { 2 | capability linear Bar; 3 | var int f : Bar; 4 | const int g : Bar; 5 | const int h : Bar; 6 | 7 | } 8 | function int f (int x ){ x} 9 | void main(){ 10 | let x = new Foo(f:5); 11 | finish { 12 | async{ 13 | f(5) 14 | } 15 | async{ 16 | let w = new Foo(g:5); 17 | w.f := 5 18 | } 19 | printf("Value of x.f: %d\n", x.f) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/README.md: -------------------------------------------------------------------------------- 1 | # Programs Test Suite 2 | 3 | These **.bolt** programs are used for expectation tests - they're named after the behaviour they are testing. 4 | 5 | There are both positive and negative examples - the positive examples being put in good/ and then the negative examples placed in bad_parse/ bad_type_check data_races/ based on which stage of the parsing/type-checking pipeline they fail. 6 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/README.out.expected: -------------------------------------------------------------------------------- 1 | './tests/frontend/integration/invalid_programs/README.md' is not a bolt file. Hint: use the .bolt extension 2 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/invalid_programs.out.expected: -------------------------------------------------------------------------------- 1 | './tests/frontend/integration/invalid_programs' is not a bolt file. Hint: use the .bolt extension 2 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/not_a_program: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mukul-rathi/bolt/a81627f95af6577cd465df09290a51c9d469c667/tests/frontend/integration/invalid_programs/not_a_program -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/not_a_program.out.expected: -------------------------------------------------------------------------------- 1 | './tests/frontend/integration/invalid_programs/not_a_program' is not a bolt file. Hint: use the .bolt extension 2 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/wrong_extension.out.expected: -------------------------------------------------------------------------------- 1 | './tests/frontend/integration/invalid_programs/wrong_extension.txt' is not a bolt file. Hint: use the .bolt extension 2 | -------------------------------------------------------------------------------- /tests/frontend/integration/invalid_programs/wrong_extension.txt: -------------------------------------------------------------------------------- 1 | (* We only accept programs with the extension bolt, not any old .txt file *) 2 | 3 | class Foo = linear Bar { 4 | const f : int 5 | const g : int 6 | const h : int 7 | 8 | } 9 | linear trait Bar { 10 | require const f : int 11 | require const g : int 12 | require const h : int 13 | } 14 | let x = new Foo(f:4, g:5, h:6) in 15 | let y = consume x in 16 | y.h 17 | end 18 | end -------------------------------------------------------------------------------- /tests/llvm-backend/BUILD: -------------------------------------------------------------------------------- 1 | cc_test( 2 | name = "test_llvm_backend", 3 | srcs = glob(["test_*.cc"]), 4 | copts = ["-Iexternal/gtest/include"], 5 | deps = [ 6 | "@gtest//:main", 7 | ], 8 | ) -------------------------------------------------------------------------------- /tests/llvm-backend/gtest.BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "main", 3 | srcs = glob( 4 | ["src/*.cc"], 5 | exclude = ["src/gtest-all.cc"] 6 | ), 7 | hdrs = glob([ 8 | "include/**/*.h", 9 | "src/*.h" 10 | ]), 11 | copts = ["-Iexternal/gtest/include"], 12 | linkopts = ["-pthread"], 13 | visibility = ["//visibility:public"], 14 | ) -------------------------------------------------------------------------------- /tests/llvm-backend/llvm_ir_codegen/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | 3 | cc_library( 4 | name = "ir_codegen_test_visitor", 5 | srcs = ["ir_codegen_test_visitor.cc"], 6 | hdrs = ["ir_codegen_test_visitor.h"], 7 | deps = [ 8 | "//src/llvm-backend/llvm_ir_codegen:llvm_ir_codegen", "@llvm" 9 | ], 10 | ) 11 | 12 | cc_test( 13 | name = "test_llvm_ir_codegen", 14 | srcs = glob(["test_*.cc"]), 15 | copts = ["-Iexternal/gtest/include"], 16 | deps = [ 17 | "@gtest//:main", 18 | "//src/llvm-backend/llvm_ir_codegen:llvm_ir_codegen", "//tests/llvm-backend/llvm_ir_codegen:ir_codegen_test_visitor", "@llvm" 19 | ], 20 | ) -------------------------------------------------------------------------------- /tests/llvm-backend/llvm_ir_codegen/ir_codegen_test_visitor.cc: -------------------------------------------------------------------------------- 1 | #include "tests/llvm-backend/llvm_ir_codegen/ir_codegen_test_visitor.h" 2 | 3 | bool IRCodegenTestVisitor::isFunctionPresent(std::string functionName) { 4 | return (module->getFunction(functionName) != nullptr); 5 | } -------------------------------------------------------------------------------- /tests/llvm-backend/llvm_ir_codegen/ir_codegen_test_visitor.h: -------------------------------------------------------------------------------- 1 | #include "src/llvm-backend/llvm_ir_codegen/ir_codegen_visitor.h" 2 | 3 | class IRCodegenTestVisitor : public IRCodegenVisitor { 4 | public: 5 | bool isFunctionPresent(std::string functionName); 6 | }; 7 | -------------------------------------------------------------------------------- /tests/llvm-backend/llvm_ir_codegen/test_ir_codegen.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "gtest/gtest.h" 6 | #include "tests/llvm-backend/llvm_ir_codegen/ir_codegen_test_visitor.h" 7 | 8 | class IRCodegenTest : public ::testing::Test { 9 | protected: 10 | IRCodegenTestVisitor codeGenTester; 11 | }; 12 | 13 | TEST_F(IRCodegenTest, PrintfInstantiated) { 14 | codeGenTester.codegenExternFunctionDeclarations(); 15 | EXPECT_TRUE(codeGenTester.isFunctionPresent("printf")); 16 | } 17 | 18 | TEST_F(IRCodegenTest, MallocInstantiated) { 19 | codeGenTester.codegenExternFunctionDeclarations(); 20 | EXPECT_TRUE(codeGenTester.isFunctionPresent("GC_malloc")); 21 | } 22 | TEST_F(IRCodegenTest, PthreadFuncsInstantiated) { 23 | codeGenTester.codegenExternFunctionDeclarations(); 24 | EXPECT_TRUE(codeGenTester.isFunctionPresent("pthread_create")); 25 | EXPECT_TRUE(codeGenTester.isFunctionPresent("pthread_join")); 26 | EXPECT_TRUE(codeGenTester.isFunctionPresent("pthread_equal")); 27 | EXPECT_TRUE(codeGenTester.isFunctionPresent("pthread_self")); 28 | } 29 | --------------------------------------------------------------------------------